inn-2.6.0/0000755000175200017520000000000012575023702011720 5ustar iuliusiuliusinn-2.6.0/nnrpd/0000755000175200017520000000000012575023701013040 5ustar iuliusiuliusinn-2.6.0/nnrpd/post.h0000644000175200017520000000341212575023702014177 0ustar iuliusiulius/* $Id: post.h 8857 2009-12-24 12:08:00Z iulius $ ** ** NetNews Reading Protocol server. */ typedef enum _HEADERTYPE { HTobs, HTreq, HTstd } HEADERTYPE; typedef struct _HEADER { const char * Name; bool CanSet; HEADERTYPE Type; int Size; char * Value; /* Just after ':' in header. */ char * Body; /* Where actual body begins. */ int Len; /* Body length excluding trailing white spaces. */ } HEADER; #define HDR(_x) (Table[(_x)].Body) #define HDR_SET(_x, _y) \ do { \ Table[(_x)].Body = _y; \ Table[(_x)].Value = _y; \ if (_y == NULL) { \ Table[(_x)].Len = 0; \ } else { \ Table[(_x)].Len = strlen(_y); \ } \ } while (0) #define HDR_CLEAR(_x) \ do { \ Table[(_x)].Body = NULL; \ Table[(_x)].Value = NULL; \ Table[(_x)].Len = 0; \ } while (0) #define HDR__PATH 0 #define HDR__FROM 1 #define HDR__NEWSGROUPS 2 #define HDR__SUBJECT 3 #define HDR__CONTROL 4 #define HDR__FOLLOWUPTO 6 #define HDR__DATE 7 #define HDR__ORGANIZATION 8 #define HDR__LINES 9 #define HDR__SENDER 10 #define HDR__APPROVED 11 #define HDR__DISTRIBUTION 13 #define HDR__EXPIRES 14 #define HDR__MESSAGEID 15 #define HDR__INJECTION_DATE 26 #define HDR__INJECTION_INFO 27 #define HDR__CC 36 #define HDR__BCC 37 #define HDR__TO 38 inn-2.6.0/nnrpd/nnrpd.c0000644000175200017520000012354712575023702014342 0ustar iuliusiulius/* $Id: nnrpd.c 9913 2015-07-07 16:31:52Z iulius $ ** ** NNTP server for readers (NNRP) for InterNetNews. ** ** This server doesn't do any real load-limiting, except for what has ** proven empirically necessary (i.e. look at GRPscandir). */ #include "config.h" #include "portable/alloca.h" #include "clibrary.h" #include "portable/setproctitle.h" #include "portable/socket.h" #include #include #include #if HAVE_GETSPNAM # include #endif #include #include "inn/innconf.h" #include "inn/libinn.h" #include "inn/messages.h" #include "inn/network.h" #include "inn/network-innbind.h" #include "inn/newsuser.h" #include "inn/ov.h" #include "inn/version.h" #define MAINLINE #include "nnrpd.h" #include "tls.h" #ifdef HAVE_OPENSSL extern SSL *tls_conn; bool nnrpd_starttls_done = false; #endif /* ** If we have getloadavg, include the appropriate header file. Otherwise, ** just assume that we always have a load of 0. */ #if HAVE_GETLOADAVG # if HAVE_SYS_LOADAVG_H # include # endif #else static int getloadavg(double loadavg[], int nelem) { int i; for (i = 0; i < nelem && i < 3; i++) loadavg[i] = 0; return i; } #endif #define MAXPATTERNDEFINE 10 #define CMDany -1 typedef struct _CMDENT { const char * Name; void (*Function)(int, char **); bool Needauth; int Minac; int Maxac; bool Stripspaces; const char * Help; } CMDENT; char *ACTIVE = NULL; char *ACTIVETIMES = NULL; char *HISTORY = NULL; char *NEWSGROUPS = NULL; char *NNRPACCESS = NULL; static char *LocalLogFileName = NULL; static char *LocalLogDirName; struct history *History; static double STATstart; static double STATfinish; static char *PushedBack; static sig_atomic_t ChangeTrace; bool DaemonMode = false; bool ForeGroundMode = false; static const char *HostErrorStr; bool GetHostByAddr = true; /* Formerly DO_NNRP_GETHOSTBYADDR. */ const char *NNRPinstance = ""; #ifdef DO_PERL bool PerlLoaded = false; #endif #ifdef DO_PYTHON bool PY_use_dynamic = false; #endif static char CMDfetchhelp[] = "[message-ID|number]"; /* ** { command base name, function to call, need authentication, ** min args, max args, strip spaces, help string } */ static CMDENT CMDtable[] = { { "ARTICLE", CMDfetch, true, 1, 2, true, CMDfetchhelp }, /* Parse AUTHINFO in a special way so as to keep white spaces * in usernames and passwords. */ { "AUTHINFO", CMDauthinfo, false, 3, CMDany, false, "USER name|PASS password" #ifdef HAVE_SASL "|SASL mechanism [initial-response]" #endif "|GENERIC program [argument ...]" }, { "BODY", CMDfetch, true, 1, 2, true, CMDfetchhelp }, { "CAPABILITIES", CMDcapabilities,false, 1, 2, true, "[keyword]" }, { "DATE", CMDdate, false, 1, 1, true, NULL }, { "GROUP", CMDgroup, true, 2, 2, true, "newsgroup" }, { "HDR", CMDpat , true, 2, 3, true, "header [message-ID|range]" }, { "HEAD", CMDfetch, true, 1, 2, true, CMDfetchhelp }, { "HELP", CMDhelp, false, 1, 1, true, NULL }, { "IHAVE", CMDpost, true, 2, 2, true, "message-ID" }, { "LAST", CMDnextlast, true, 1, 1, true, NULL }, { "LIST", CMDlist, true, 1, 3, true, "[ACTIVE [wildmat]|ACTIVE.TIMES [wildmat]|COUNTS [wildmat]|DISTRIB.PATS" "|DISTRIBUTIONS|HEADERS [MSGID|RANGE]|MODERATORS|MOTD|NEWSGROUPS [wildmat]" "|OVERVIEW.FMT|SUBSCRIPTIONS [wildmat]]" }, { "LISTGROUP", CMDgroup, true, 1, 3, true, "[newsgroup [range]]" }, { "MODE", CMDmode, false, 2, 2, true, "READER" }, { "NEWGROUPS", CMDnewgroups, true, 3, 4, true, "[yy]yymmdd hhmmss [GMT]" }, { "NEWNEWS", CMDnewnews, true, 4, 5, true, "wildmat [yy]yymmdd hhmmss [GMT]" }, { "NEXT", CMDnextlast, true, 1, 1, true, NULL }, { "OVER", CMDover, true, 1, 2, true, "[range]" }, { "POST", CMDpost, true, 1, 1, true, NULL }, { "QUIT", CMDquit, false, 1, 1, true, NULL }, /* SLAVE (which was ill-defined in RFC 977) was removed from the NNTP * protocol in RFC 3977. */ { "SLAVE", CMD_unimp, false, 1, 1, true, NULL }, #ifdef HAVE_OPENSSL { "STARTTLS", CMDstarttls, false, 1, 1, true, NULL }, #endif { "STAT", CMDfetch, true, 1, 2, true, CMDfetchhelp }, { "XGTITLE", CMDxgtitle, true, 1, 2, true, "[wildmat]" }, { "XHDR", CMDpat, true, 2, 3, true, "header [message-ID|range]" }, { "XOVER", CMDover, true, 1, 2, true, "[range]" }, { "XPAT", CMDpat, true, 4, CMDany, true, "header message-ID|range pattern [pattern ...]" }, { NULL, CMD_unimp, false, 0, 0, true, NULL } }; static const char *const timer_name[] = { "idle", "newnews", "readart", "checkart", "nntpread", "nntpwrite", }; /* ** Log a summary status message and exit. */ void ExitWithStats(int x, bool readconf) { double usertime; double systime; line_free(&NNTPline); fflush(stdout); STATfinish = TMRnow_double(); if (GetResourceUsage(&usertime, &systime) < 0) { usertime = 0; systime = 0; } GRPreport(); if (ARTcount) syslog(L_NOTICE, "%s exit articles %ld groups %ld", Client.host, ARTcount, GRPcount); if (POSTreceived || POSTrejected) syslog(L_NOTICE, "%s posts received %ld rejected %ld", Client.host, POSTreceived, POSTrejected); syslog(L_NOTICE, "%s times user %.3f system %.3f idle %.3f elapsed %.3f", Client.host, usertime, systime, IDLEtime, STATfinish - STATstart); /* Tracking code - Make entries in the logfile(s) to show that we have * finished with this session. */ if (!readconf && PERMaccessconf && PERMaccessconf->readertrack) { syslog(L_NOTICE, "%s Tracking Disabled (%s)", Client.host, Username); if (LLOGenable) { fprintf(locallog, "%s Tracking Disabled (%s)\n", Client.host, Username); fclose(locallog); syslog(L_NOTICE,"%s Local Logging ends (%s) %s", Client.host, Username, LocalLogFileName); } } if (ARTget) syslog(L_NOTICE, "%s artstats get %ld time %ld size %ld", Client.host, ARTget, ARTgettime, ARTgetsize); if (!readconf && PERMaccessconf && PERMaccessconf->nnrpdoverstats && OVERcount) syslog(L_NOTICE, "%s overstats count %ld hit %ld miss %ld time %ld size %ld dbz %ld seek %ld get %ld artcheck %ld", Client.host, OVERcount, OVERhit, OVERmiss, OVERtime, OVERsize, OVERdbz, OVERseek, OVERget, OVERartcheck); #ifdef HAVE_OPENSSL if (tls_conn) { SSL_shutdown(tls_conn); SSL_free(tls_conn); tls_conn = NULL; } #endif #ifdef HAVE_SASL if (sasl_conn) { sasl_dispose(&sasl_conn); sasl_conn = NULL; sasl_ssf = 0; sasl_maxout = NNTP_MAXLEN_COMMAND; } sasl_done(); #endif /* HAVE_SASL */ if (DaemonMode) { shutdown(STDIN_FILENO, 2); shutdown(STDOUT_FILENO, 2); shutdown(STDERR_FILENO, 2); close(STDIN_FILENO); close(STDOUT_FILENO); close(STDERR_FILENO); } OVclose(); SMshutdown(); #ifdef DO_PYTHON PY_close_python(); #endif /* DO_PYTHON */ if (History) HISclose(History); if (innconf->timer != 0) { TMRsummary(Client.host, timer_name); TMRfree(); } if (LocalLogFileName != NULL) free(LocalLogFileName); closelog(); exit(x); } /* ** The HELP command. */ void CMDhelp(int ac UNUSED, char *av[] UNUSED) { CMDENT *cp; char *p, *q; static const char *newsmaster = NEWSMASTER; Reply("%d Legal commands\r\n", NNTP_INFO_HELP); for (cp = CMDtable; cp->Name; cp++) { if (cp->Function == CMD_unimp) continue; if (cp->Help == NULL) Printf(" %s\r\n", cp->Name); else Printf(" %s %s\r\n", cp->Name, cp->Help); } if (PERMaccessconf && (VirtualPathlen > 0)) { if (PERMaccessconf->newsmaster) { if (strchr(PERMaccessconf->newsmaster, '@') == NULL) { Printf("Report problems to <%s@%s>.\r\n", PERMaccessconf->newsmaster, PERMaccessconf->domain); } else { Printf("Report problems to <%s>.\r\n", PERMaccessconf->newsmaster); } } else { /* Sigh, pickup from newsmaster anyway. */ if ((p = strchr(newsmaster, '@')) == NULL) Printf("Report problems to <%s@%s>.\r\n", newsmaster, PERMaccessconf->domain); else { q = xstrndup(newsmaster, p - newsmaster); Printf("Report problems to <%s@%s>.\r\n", q, PERMaccessconf->domain); free(q); } } } else { if (strchr(newsmaster, '@') == NULL) Printf("Report problems to <%s@%s>.\r\n", newsmaster, innconf->fromhost); else Printf("Report problems to <%s>.\r\n", newsmaster); } Printf(".\r\n"); } /* ** The CAPABILITIES command. ** ** nnrpd does not advertise the MODE-READER capability; only innd may ** advertise it. */ void CMDcapabilities(int ac, char *av[]) { if (ac == 2 && !IsValidKeyword(av[1])) { Reply("%d Syntax error in keyword\r\n", NNTP_ERR_SYNTAX); return; } Reply("%d Capability list:\r\n", NNTP_INFO_CAPABILITIES); Printf("VERSION 2\r\n"); Printf("IMPLEMENTATION %s\r\n", INN_VERSION_STRING); #ifdef HAVE_SASL const char *mechlist = NULL; /* Check for available SASL mechanisms. * Start the string with a space for the strstr() calls afterwards. */ sasl_listmech(sasl_conn, NULL, " ", " ", "", &mechlist, NULL, NULL); #endif /* The client is not already authenticated. */ if ((!PERMauthorized || PERMneedauth || PERMcanauthenticate)) { Printf("AUTHINFO"); /* No arguments if the server does not permit any authentication commands * in its current state. */ if (PERMcanauthenticate) { #ifdef HAVE_OPENSSL if (PERMcanauthenticatewithoutSSL || nnrpd_starttls_done) { #endif /* AUTHINFO USER is advertised only if a TLS layer is active, * if compiled with TLS support. */ Printf(" USER"); #ifdef HAVE_OPENSSL } else { #ifdef HAVE_SASL /* Remove unsecure PLAIN, LOGIN and EXTERNAL SASL mechanisms, * if compiled with TLS support and a TLS layer is not active. */ if (mechlist != NULL) { char *p; if ((p = strstr(mechlist, " PLAIN")) != NULL && (p[6] == '\0' || p[6] == ' ')) { memmove(p, p+6, strlen(p)-5); } if ((p = strstr(mechlist, " LOGIN")) != NULL && (p[6] == '\0' || p[6] == ' ')) { memmove(p, p+6, strlen(p)-5); } if ((p = strstr(mechlist, " EXTERNAL")) != NULL && (p[9] == '\0' || p[9] == ' ')) { memmove(p, p+9, strlen(p)-8); } } #endif /* HAVE_SASL */ } #endif /* HAVE_OPENSSL */ #ifdef HAVE_SASL /* Check whether at least one SASL mechanism is available. */ if (mechlist != NULL && strlen(mechlist) > 2) { Printf(" SASL"); } #endif } Printf("\r\n"); } if (PERMcanread) { Printf("HDR\r\n"); } if (PERMaccessconf->allowihave && PERMcanpost) { Printf("IHAVE\r\n"); } Printf("LIST ACTIVE ACTIVE.TIMES COUNTS DISTRIB.PATS DISTRIBUTIONS" " HEADERS MODERATORS MOTD NEWSGROUPS OVERVIEW.FMT SUBSCRIPTIONS\r\n"); if (PERMaccessconf->allownewnews && PERMcanread) { Printf("NEWNEWS\r\n"); } if (PERMcanread) { Printf("OVER\r\n"); } if (PERMcanpost) { Printf("POST\r\n"); } Printf("READER\r\n"); #ifdef HAVE_SASL /* Check whether at least one SASL mechanism is available. */ if (mechlist != NULL && strlen(mechlist) > 2) { Printf("SASL%s\r\n", mechlist); } #endif #ifdef HAVE_OPENSSL /* A TLS layer is not active and the client is not already authenticated. */ if (!nnrpd_starttls_done && (!PERMauthorized || PERMneedauth || PERMcanauthenticate)) { Printf("STARTTLS\r\n"); } #endif Printf(".\r\n"); } /* ** Catch-all for unimplemented functions. */ void CMD_unimp(int ac UNUSED, char *av[]) { Reply("%d \"%s\" not implemented; try \"HELP\"\r\n", NNTP_ERR_COMMAND, av[0]); } /* ** The QUIT command. */ void CMDquit(int ac UNUSED, char *av[] UNUSED) { Reply("%d Bye!\r\n", NNTP_OK_QUIT); ExitWithStats(0, false); } /* ** Convert an address to a hostname. Don't trust the reverse lookup ** since anyone can fake reverse DNS entries. */ static bool Address2Name(struct sockaddr *sa, char *hostname, size_t size) { static const char MISMATCH[] = "reverse lookup validation failed"; struct addrinfo hints, *ai, *host; char *p; int ret; bool valid = false; /* Get the official hostname, store it away. */ ret = getnameinfo(sa, SA_LEN(sa), hostname, size, NULL, 0, NI_NAMEREQD); if (ret != 0) { HostErrorStr = gai_strerror(ret); return false; } /* Get addresses for this host. */ memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; ret = getaddrinfo(hostname, NULL, &hints, &ai); if (ret != 0) { HostErrorStr = gai_strerror(ret); return false; } /* Make sure one of those addresses is the address we got. */ for (host = ai; host != NULL; host = host->ai_next) if (network_sockaddr_equal(sa, host->ai_addr)) { valid = true; break; } freeaddrinfo(ai); /* Lowercase the returned name since wildmats are case-sensitive. */ if (valid) { for (p = hostname; *p != '\0'; p++) if (isupper((unsigned char) *p)) *p = tolower((unsigned char) *p); return true; } else { HostErrorStr = MISMATCH; return false; } } /* ** Determine access rights of the client. */ static void StartConnection(unsigned short port) { static const char *default_host_error = "unknown error"; struct sockaddr_storage ssc, sss; struct sockaddr *sac = (struct sockaddr *) &ssc; struct sockaddr *sas = (struct sockaddr *) &sss; socklen_t length; size_t size; int nodelay = 1; memset(&Client, 0, sizeof(Client)); strlcpy(Client.host, "?", sizeof(Client.host)); /* Get the peer's name. */ length = sizeof(ssc); if (getpeername(STDIN_FILENO, sac, &length) < 0) { if (!isatty(STDIN_FILENO)) { sysnotice("? can't getpeername"); Reply("%d Can't get your name. Goodbye!\r\n", NNTP_ERR_ACCESS); ExitWithStats(1, true); } strlcpy(Client.host, "localhost", sizeof(Client.host)); } else { /* Figure out client's IP address/hostname. */ HostErrorStr = default_host_error; if (!network_sockaddr_sprint(Client.ip, sizeof(Client.ip), sac)) { notice("? can't get client numeric address: %s", HostErrorStr); Reply("%d Can't get your numeric address. Goodbye!\r\n", NNTP_ERR_ACCESS); ExitWithStats(1, true); } if (GetHostByAddr) { HostErrorStr = default_host_error; if (!Address2Name(sac, Client.host, sizeof(Client.host))) { notice("? reverse lookup for %s failed: %s -- using IP" " address for access", Client.ip, HostErrorStr); strlcpy(Client.host, Client.ip, sizeof(Client.host)); } } else { strlcpy(Client.host, Client.ip, sizeof(Client.host)); } /* Figure out server's IP address/hostname. */ length = sizeof(sss); if (getsockname(STDIN_FILENO, sas, &length) < 0) { sysnotice("%s can't getsockname", Client.host); Reply("%d Can't figure out where you connected to. Goodbye!\r\n", NNTP_ERR_ACCESS); ExitWithStats(1, true); } HostErrorStr = default_host_error; size = sizeof(Client.serverip); if (!network_sockaddr_sprint(Client.serverip, size, sas)) { notice("? can't get server numeric address: %s", HostErrorStr); Reply("%d Can't get my own numeric address. Goodbye!\r\n", NNTP_ERR_ACCESS); ExitWithStats(1, true); } if (GetHostByAddr) { HostErrorStr = default_host_error; size = sizeof(Client.serverhost); if (!Address2Name(sas, Client.serverhost, size)) { notice("? reverse lookup for %s failed: %s -- using IP" " address for access", Client.serverip, HostErrorStr); strlcpy(Client.serverhost, Client.serverip, sizeof(Client.serverhost)); } } else { strlcpy(Client.serverhost, Client.serverip, sizeof(Client.serverhost)); } /* Get port numbers. */ Client.port = network_sockaddr_port(sac); Client.serverport = network_sockaddr_port(sas); } /* Setting TCP_NODELAY to nnrpd fixes a problem of slow downloading * of overviews and slow answers on some architectures (like BSD/OS). */ setsockopt(STDIN_FILENO, IPPROTO_TCP, TCP_NODELAY, &nodelay, sizeof(nodelay)); notice("%s (%s) connect - port %u", Client.host, Client.ip, port); PERMgetaccess(NNRPACCESS); PERMgetpermissions(); } /* ** Write a buffer, via SASL security layer and/or TLS if necessary. */ void write_buffer(const char *buff, ssize_t len) { const char *p; ssize_t n; TMRstart(TMR_NNTPWRITE); p = buff; while (len > 0) { const char *out; unsigned outlen; #ifdef HAVE_SASL if (sasl_conn && sasl_ssf) { int r; /* Can only encode as much as the client can handle at one time. */ n = (len > sasl_maxout) ? sasl_maxout : len; if ((r = sasl_encode(sasl_conn, p, n, &out, &outlen)) != SASL_OK) { sysnotice("sasl_encode() failed: %s", sasl_errstring(r, NULL, NULL)); return; } } else #endif /* HAVE_SASL */ { /* Output the entire unencoded string. */ n = len; out = buff; outlen = len; } len -= n; p += n; #ifdef HAVE_OPENSSL if (tls_conn) { int r; Again: r = SSL_write(tls_conn, out, outlen); switch (SSL_get_error(tls_conn, r)) { case SSL_ERROR_NONE: case SSL_ERROR_SYSCALL: break; case SSL_ERROR_WANT_WRITE: goto Again; break; case SSL_ERROR_SSL: SSL_shutdown(tls_conn); tls_conn = NULL; errno = ECONNRESET; break; case SSL_ERROR_ZERO_RETURN: break; } } else #endif /* HAVE_OPENSSL */ do { n = write(STDIN_FILENO, out, outlen); } while (n == -1 && errno == EINTR); } TMRstop(TMR_NNTPWRITE); } /* ** Send formatted output, possibly with debugging output. */ static void __attribute__((__format__(printf, 1, 0))) VPrintf(const char *fmt, va_list args, int dotrace) { char buff[2048], *p; ssize_t len; len = vsnprintf(buff, sizeof(buff), fmt, args); write_buffer(buff, len); if (dotrace && Tracing) { int oerrno = errno; /* Copy output, but strip trailing CR-LF. Note we're assuming here * that no output line can ever be longer than 2045 characters. */ p = buff + strlen(buff) - 1; while (p >= buff && (*p == '\n' || *p == '\r')) *p-- = '\0'; syslog(L_TRACE, "%s > %s", Client.host, buff); errno = oerrno; } } /* ** Send a reply, possibly with debugging output. ** Reply() is used for answers which can possibly be traced (response codes). ** Printf() is used in other cases. */ void Reply(const char *fmt, ...) { va_list args; va_start(args, fmt); VPrintf(fmt, args, 1); va_end(args); } void Printf(const char *fmt, ...) { va_list args; va_start(args, fmt); VPrintf(fmt, args, 0); va_end(args); } #ifdef HAVE_SIGACTION #define NO_SIGACTION_UNUSED UNUSED #else #define NO_SIGACTION_UNUSED #endif /* ** Got a signal; toggle tracing. */ static void ToggleTrace(int s NO_SIGACTION_UNUSED) { ChangeTrace = true; #ifndef HAVE_SIGACTION xsignal(s, ToggleTrace); #endif } /* ** Got a SIGPIPE; exit cleanly. */ static void CatchPipe(int s UNUSED) { ExitWithStats(0, false); } /* ** Got a signal; wait for children. */ static void WaitChild(int s NO_SIGACTION_UNUSED) { int pid; for (;;) { pid = waitpid(-1, NULL, WNOHANG); if (pid <= 0) break; } #ifndef HAVE_SIGACTION xsignal(s, WaitChild); #endif } static void SetupDaemon(void) { bool val; val = true; if (SMsetup(SM_PREOPEN, (void *)&val) && !SMinit()) { syslog(L_NOTICE, "can't initialize storage method, %s", SMerrorstr); Reply("%d NNTP server unavailable. Try later!\r\n", NNTP_FAIL_TERMINATING); ExitWithStats(1, true); } OVextra = overview_extra_fields(false); if (OVextra == NULL) { /* overview_extra_fields() should already have logged something * useful. */ Reply("%d NNTP server unavailable. Try later!\r\n", NNTP_FAIL_TERMINATING); ExitWithStats(1, true); } overhdr_xref = overview_index("Xref", OVextra); if (!OVopen(OV_READ)) { /* This shouldn't really happen. */ syslog(L_NOTICE, "can't open overview %m"); Reply("%d NNTP server unavailable. Try later!\r\n", NNTP_FAIL_TERMINATING); ExitWithStats(1, true); } if (!OVctl(OVCACHEKEEP, &val)) { syslog(L_NOTICE, "can't enable overview cache %m"); Reply("%d NNTP server unavailable. Try later!\r\n", NNTP_FAIL_TERMINATING); ExitWithStats(1, true); } } /* ** Print a usage message and exit. */ static void Usage(void) { fprintf(stderr, "Usage error.\n"); exit(1); } int main(int argc, char *argv[]) { const char *name; CMDENT *cp; char buff[NNTP_MAXLEN_COMMAND]; char **av; int ac; READTYPE r; int i; unsigned int j; char **v; char *Reject; int timeout; unsigned int vid=0; int count=123456789; struct timeval tv; unsigned short ListenPort = NNTP_PORT; char *ListenAddr = NULL; char *ListenAddr6 = NULL; int *lfds; fd_set lfdset, lfdsetread; unsigned int lfdcount; int lfdreadcount; bool lfdokay; int lfdmax = 0; int fd = -1; pid_t pid = -1; FILE *pidfile; int clienttimeout; char *ConfFile = NULL; char *path; bool validcommandtoolong; int respawn = 0; setproctitle_init(argc, argv); /* Parse arguments. Must xstrdup() optarg if used because setproctitle * may clobber it! */ Reject = NULL; LLOGenable = false; GRPcur = NULL; MaxBytesPerSecond = 0; strlcpy(Username, "unknown", sizeof(Username)); /* Set up the pathname, first thing, and teach our error handlers about * the name of the program. */ name = argv[0]; if (name == NULL || *name == '\0') name = "nnrpd"; else { const char *p; /* We take the last "/" in the path. */ p = strrchr(name, '/'); if (p != NULL) name = p + 1; } message_program_name = xstrdup(name); openlog(message_program_name, L_OPENLOG_FLAGS | LOG_PID, LOG_INN_PROG); message_handlers_die(1, message_log_syslog_crit); message_handlers_warn(1, message_log_syslog_warning); message_handlers_notice(1, message_log_syslog_notice); /* Initialize the character class tables for message-IDs. */ InitializeMessageIDcclass(); if (!innconf_read(NULL)) exit(1); #ifdef HAVE_SASL if (sasl_server_init(sasl_callbacks, "INN") != SASL_OK) { syslog(L_FATAL, "sasl_server_init() failed"); exit(1); } #endif /* HAVE_SASL */ #ifdef HAVE_OPENSSL while ((i = getopt(argc, argv, "4:6:b:c:Dfi:I:nop:P:r:s:St")) != EOF) #else while ((i = getopt(argc, argv, "4:6:b:c:Dfi:I:nop:P:r:s:t")) != EOF) #endif /* HAVE_OPENSSL */ switch (i) { default: Usage(); /* NOTREACHED */ case '4': /* Bind to a certain IPv4 address. */ case 'b': if (ListenAddr != NULL) die("-b and -4 are the same and both may not be given"); ListenAddr = xstrdup(optarg); break; case '6': /* Bind to a certain IPv6 address. */ ListenAddr6 = xstrdup(optarg); break; case 'c': /* Use alternate readers.conf. */ ConfFile = concatpath(innconf->pathetc, optarg); break; case 'D': /* Standalone daemon mode. */ DaemonMode = true; break; case 'P': /* Prespawn count in daemon mode. */ respawn = atoi(optarg); break; case 'f': /* Don't fork on daemon mode. */ ForeGroundMode = true; break; case 'i': /* Initial command. */ PushedBack = xstrdup(optarg); break; case 'I': /* Instance. */ NNRPinstance = xstrdup(optarg); break; case 'n': /* No DNS lookups. */ GetHostByAddr = false; break; case 'o': Offlinepost = true; /* Offline posting only. */ break; case 'p': /* TCP port for daemon mode. */ ListenPort = atoi(optarg); break; case 'r': /* Reject connection message. */ Reject = xstrdup(optarg); break; case 's': /* Unused title string. */ break; case 't': /* Tracing. */ Tracing = true; break; #ifdef HAVE_OPENSSL case 'S': /* Force SSL negotiation. */ initialSSL = true; break; #endif /* HAVE_OPENSSL */ } argc -= optind; if (argc) Usage(); /* Make other processes happier if someone is reading. This allows other * processes like overchan to keep up when there are lots of readers. * Note that this is cumulative with nicekids. */ if (innconf->nicennrpd != 0) nice(innconf->nicennrpd); HISTORY = concatpath(innconf->pathdb, INN_PATH_HISTORY); ACTIVE = concatpath(innconf->pathdb, INN_PATH_ACTIVE); ACTIVETIMES = concatpath(innconf->pathdb, INN_PATH_ACTIVETIMES); NEWSGROUPS = concatpath(innconf->pathdb, INN_PATH_NEWSGROUPS); if(ConfFile) NNRPACCESS = ConfFile; else NNRPACCESS = concatpath(innconf->pathetc,INN_PATH_NNRPACCESS); if (DaemonMode) { /* Allocate an lfds array to hold the file descriptors * for IPv4 and/or IPv6 connections. */ if (ListenAddr == NULL && ListenAddr6 == NULL) { network_innbind_all(SOCK_STREAM, ListenPort, &lfds, &lfdcount); } else { if (ListenAddr != NULL && ListenAddr6 != NULL) lfdcount = 2; else lfdcount = 1; lfds = xmalloc(lfdcount * sizeof(int)); i = 0; if (ListenAddr6 != NULL) { lfds[i++] = network_innbind_ipv6(SOCK_STREAM, ListenAddr6, ListenPort); } if (ListenAddr != NULL) { lfds[i] = network_innbind_ipv4(SOCK_STREAM, ListenAddr, ListenPort); } } /* Bail if we couldn't listen on any sockets. */ lfdokay = false; for (j = 0; j < lfdcount; j++) { if (lfds[j] < 0) continue; lfdokay = true; } if (!lfdokay) die("can't bind to any addresses"); /* If started as root, switch to news uid. Unlike other parts of INN, we * don't die if we can't become the news user. As long as we're not * running as root, everything's fine; it's okay to write the things we * write as a member of the news group. */ if (getuid() == 0) { ensure_news_user_grp(true, true); } /* Detach. */ if (!ForeGroundMode) { daemonize("/"); } if (ListenPort == NNTP_PORT) strlcpy(buff, "nnrpd.pid", sizeof(buff)); else snprintf(buff, sizeof(buff), "nnrpd-%d.pid", ListenPort); path = concatpath(innconf->pathrun, buff); pidfile = fopen(path, "w"); free(path); if (pidfile == NULL) { syslog(L_ERROR, "cannot write %s %m", buff); exit(1); } fprintf(pidfile,"%lu\n", (unsigned long) getpid()); fclose(pidfile); /* Set signal handle to care for dead children. */ if (!respawn) xsignal(SIGCHLD, WaitChild); /* Arrange to toggle tracing. */ xsignal(SIGHUP, ToggleTrace); setproctitle("accepting connections"); /* Initialize the listener file descriptors set. */ FD_ZERO(&lfdset); for (j = 0; j < lfdcount; j++) { if (listen(lfds[j], 128) < 0) { if (j != 0 && errno == EADDRINUSE) continue; syslog(L_ERROR, "can't listen to socket"); } else { /* Remember the largest descriptor number * that is to be tested by select(). */ FD_SET(lfds[j], &lfdset); if (lfdmax < lfds[j]) lfdmax = lfds[j]; } } if (respawn) { /* Pre-forked mode. */ for (;;) { if (respawn > 0) { --respawn; pid = fork(); if (pid == 0) { do { fd = -1; /* Copy the master set because lfdsetread * will be modified. */ lfdsetread = lfdset; lfdreadcount = select(lfdmax + 1, &lfdsetread, NULL, NULL, NULL); if (lfdreadcount > 0) { for (j = 0; j < lfdcount; j++) { if (FD_ISSET(lfds[j], &lfdsetread)) { fd = accept(lfds[j], NULL, NULL); /* Only handle the first match. Future * calls to select() will handle possible * other matches. */ if (fd >= 0) break; } } } } while (fd < 0); break; } } for (;;) { if (respawn == 0) pid = wait(NULL); else pid = waitpid(-1, NULL, WNOHANG); if (pid <= 0) break; ++respawn; } } } else { /* Fork on demand. */ do { fd = -1; /* Copy the master set because lfdsetread will be modified. */ lfdsetread = lfdset; lfdreadcount = select(lfdmax + 1, &lfdsetread, NULL, NULL, NULL); if (lfdreadcount > 0) { for (j = 0; j < lfdcount; j++) { if (FD_ISSET(lfds[j], &lfdsetread)) { fd = accept(lfds[j], NULL, NULL); /* Only handle the first match. Future calls * to select() will handle possible other matches. */ if (fd >= 0) break; } } } if (fd < 0) continue; for (j = 0; j <= innconf->maxforks && (pid = fork()) < 0; j++) { if (j == innconf->maxforks) { syslog(L_FATAL, "can't fork (dropping connection): %m"); continue; } syslog(L_NOTICE, "can't fork (waiting): %m"); sleep(1); } if (ChangeTrace) { Tracing = Tracing ? false : true; syslog(L_TRACE, "trace %sabled", Tracing ? "en" : "dis"); ChangeTrace = false; } if (pid != 0) close(fd); } while (pid != 0); } /* Child process starts here. */ setproctitle("connected"); for (j = 0; j < lfdcount; j++) { close(lfds[j]); } dup2(fd, 0); close(fd); dup2(0, 1); dup2(0, 2); if (innconf->timer != 0) TMRinit(TMR_MAX); STATstart = TMRnow_double(); SetupDaemon(); /* If we are a daemon, innd didn't make us nice, so be nice kids. */ if (innconf->nicekids) { if (nice(innconf->nicekids) < 0) syslog(L_ERROR, "Could not nice child to %ld: %m", innconf->nicekids); } /* Only automatically reap children in the listening process. */ xsignal(SIGCHLD, SIG_DFL); } else { if (innconf->timer != 0) TMRinit(TMR_MAX); STATstart = TMRnow_double(); SetupDaemon(); /* Arrange to toggle tracing. */ xsignal(SIGHUP, ToggleTrace); } /* DaemonMode */ #ifdef HAVE_OPENSSL if (initialSSL) { tls_init(); if (tls_start_servertls(0, 1) == -1) { Reply("%d SSL connection failed\r\n", NNTP_FAIL_TERMINATING); ExitWithStats(1, false); } nnrpd_starttls_done = true; } #endif /* HAVE_OPENSSL */ /* If requested, check the load average. */ if (innconf->nnrpdloadlimit != 0) { double load[1]; if (getloadavg(load, 1) < 0) warn("cannot obtain system load"); else { if ((unsigned long)(load[0] + 0.5) > innconf->nnrpdloadlimit) { syslog(L_NOTICE, "load %.2f > %lu", load[0], innconf->nnrpdloadlimit); Reply("%d load at %.2f, try later\r\n", NNTP_FAIL_TERMINATING, load[0]); ExitWithStats(1, true); } } } /* Catch SIGPIPE so that we can exit out of long write loops. */ xsignal(SIGPIPE, CatchPipe); /* Get permissions and see if we can talk to this client. */ StartConnection(ListenPort); if (!PERMcanread && !PERMcanpost && !PERMneedauth) { syslog(L_NOTICE, "%s no_permission", Client.host); Reply("%d You have no permission to talk. Goodbye!\r\n", NNTP_ERR_ACCESS); ExitWithStats(1, false); } /* Proceed with initialization. */ setproctitle("%s connect", Client.host); /* Were we told to reject connections? */ if (Reject) { syslog(L_NOTICE, "%s rejected %s", Client.host, Reject); Reply("%d %s\r\n", NNTP_FAIL_TERMINATING, is_valid_utf8(Reject) ? Reject : "Connection rejected"); ExitWithStats(0, false); } if (PERMaccessconf) { if (PERMaccessconf->readertrack) PERMaccessconf->readertrack = TrackClient(Client.host, Username, sizeof(Username)); } else { if (innconf->readertrack) innconf->readertrack = TrackClient(Client.host, Username, sizeof(Username)); } if ((PERMaccessconf && PERMaccessconf->readertrack) || (!PERMaccessconf && innconf->readertrack)) { int len; syslog(L_NOTICE, "%s Tracking Enabled (%s)", Client.host, Username); pid=getpid(); gettimeofday(&tv,NULL); count += pid; vid = tv.tv_sec ^ tv.tv_usec ^ pid ^ count; len = strlen("innconf->pathlog") + strlen("/tracklogs/log-") + BUFSIZ; LocalLogFileName = xmalloc(len); sprintf(LocalLogFileName, "%s/tracklogs/log-%d", innconf->pathlog, vid); if ((locallog = fopen(LocalLogFileName, "w")) == NULL) { LocalLogDirName = concatpath(innconf->pathlog, "tracklogs"); MakeDirectory(LocalLogDirName, false); free(LocalLogDirName); } if (locallog == NULL && (locallog = fopen(LocalLogFileName, "w")) == NULL) { syslog(L_ERROR, "%s Local Logging failed (%s) %s: %m", Client.host, Username, LocalLogFileName); } else { syslog(L_NOTICE, "%s Local Logging begins (%s) %s",Client.host, Username, LocalLogFileName); fprintf(locallog, "%s Tracking Enabled (%s)\n", Client.host, Username); fflush(locallog); LLOGenable = true; } } #ifdef HAVE_SASL SASLnewserver(); #endif /* HAVE_SASL */ if (PERMaccessconf) { Reply("%d %s InterNetNews NNRP server %s ready (%s)\r\n", (PERMcanpost || (PERMcanauthenticate && PERMcanpostgreeting)) ? NNTP_OK_BANNER_POST : NNTP_OK_BANNER_NOPOST, PERMaccessconf->pathhost, INN_VERSION_STRING, (!PERMneedauth && PERMcanpost) ? "posting ok" : "no posting"); clienttimeout = PERMaccessconf->clienttimeout; } else { Reply("%d %s InterNetNews NNRP server %s ready (%s)\r\n", (PERMcanpost || (PERMcanauthenticate && PERMcanpostgreeting)) ? NNTP_OK_BANNER_POST : NNTP_OK_BANNER_NOPOST, innconf->pathhost, INN_VERSION_STRING, (!PERMneedauth && PERMcanpost) ? "posting ok" : "no posting"); clienttimeout = innconf->clienttimeout; } line_init(&NNTPline); /* Main dispatch loop. */ for (timeout = innconf->initialtimeout, av = NULL, ac = 0; ; timeout = clienttimeout) { alloca_free(); TMRstart(TMR_NNTPWRITE); fflush(stdout); TMRstop(TMR_NNTPWRITE); if (ChangeTrace) { Tracing = Tracing ? false : true; syslog(L_TRACE, "trace %sabled", Tracing ? "en" : "dis"); ChangeTrace = false; } if (PushedBack) { if (PushedBack[0] == '\0') continue; if (Tracing) syslog(L_TRACE, "%s < %s", Client.host, PushedBack); ac = nArgify(PushedBack, &av, 1); r = RTok; } else { size_t len; size_t lenstripped = 0; const char *p; char *q; r = line_read(&NNTPline, timeout, &p, &len, &lenstripped); switch (r) { default: syslog(L_ERROR, "%s internal %d in main", Client.host, r); /* FALLTHROUGH */ case RTtimeout: if (timeout < clienttimeout) syslog(L_NOTICE, "%s timeout short", Client.host); else syslog(L_NOTICE, "%s timeout", Client.host); ExitWithStats(1, false); break; case RTok: /* len does not count CRLF. */ if (len + lenstripped <= sizeof(buff)) { /* line_read guarantees null termination. */ memcpy(buff, p, len + 1); /* Do some input processing, check for blank line. */ if (buff[0] != '\0') ac = nArgify(buff, &av, 1); if (Tracing) { /* Do not log passwords if AUTHINFO PASS, * AUTHINFO SASL PLAIN or AUTHINFO SASL EXTERNAL * are used. (Only one space between SASL and * PLAIN/EXTERNAL should be put; otherwise, the * whole command will be logged). * AUTHINFO SASL LOGIN does not use an initial response; * therefore, there is nothing to hide here. */ if (ac > 1 && strcasecmp(av[0], "AUTHINFO") == 0) { if (strncasecmp(av[1], "PASS", 4) == 0) syslog(L_TRACE, "%s < AUTHINFO PASS ********", Client.host); else if (strncasecmp(av[1], "SASL PLAIN", 10) == 0) syslog(L_TRACE, "%s < AUTHINFO SASL PLAIN ********", Client.host); else if (strncasecmp(av[1], "SASL EXTERNAL", 13) == 0) syslog(L_TRACE, "%s < AUTHINFO SASL EXTERNAL ********", Client.host); else syslog(L_TRACE, "%s < %s", Client.host, buff); } else { syslog(L_TRACE, "%s < %s", Client.host, buff); } } if (buff[0] == '\0') continue; break; } /* FALLTHROUGH */ case RTlong: /* The line is too long but we have to make sure that * no recognized command has been sent. */ q = (char *)p; ac = nArgify(q, &av, 1); validcommandtoolong = false; for (cp = CMDtable; cp->Name; cp++) { if ((cp->Function != CMD_unimp) && (ac > 0 && strcasecmp(cp->Name, av[0]) == 0)) { validcommandtoolong = true; break; } } Reply("%d Line too long\r\n", validcommandtoolong ? NNTP_ERR_SYNTAX : NNTP_ERR_COMMAND); continue; case RTeof: /* Handled below. */ break; } } /* Client gone? */ if (r == RTeof) break; if (ac == 0) continue; /* Find command. */ for (cp = CMDtable; cp->Name; cp++) if (strcasecmp(cp->Name, av[0]) == 0) break; /* If no command has been recognized. */ if (cp->Name == NULL) { if ((int)strlen(buff) > 40) syslog(L_NOTICE, "%s unrecognized %.40s...", Client.host, buff); else syslog(L_NOTICE, "%s unrecognized %s", Client.host, buff); if (strcasecmp(av[0], "XYZZY") == 0) { /* Acknowledge the magic word from the Colossal Cave Adventure computer game. */ Reply("%d Nothing happens\r\n", NNTP_ERR_COMMAND); } else { Reply("%d What?\r\n", NNTP_ERR_COMMAND); } continue; } /* Go on parsing the command. */ ac--; ac += reArgify(av[ac], &av[ac], cp->Stripspaces ? -1 : cp->Minac - ac - 1, cp->Stripspaces); /* Check whether all arguments do not exceed their allowed size. */ if (ac > 1) { validcommandtoolong = false; for (v = av; *v; v++) if (strlen(*v) > NNTP_MAXLEN_ARG) { validcommandtoolong = true; Reply("%d Argument too long\r\n", NNTP_ERR_SYNTAX); break; } if (validcommandtoolong) continue; } /* Check usage. */ if ((cp->Minac != CMDany && ac < cp->Minac) || (cp->Maxac != CMDany && ac > cp->Maxac)) { Reply("%d Syntax is: %s %s\r\n", NNTP_ERR_SYNTAX, cp->Name, cp->Help ? cp->Help : "(no argument allowed)"); continue; } /* Check permissions and dispatch. */ if (cp->Needauth && PERMneedauth) { Reply("%d Authentication required for command\r\n", NNTP_FAIL_AUTH_NEEDED); continue; } setproctitle("%s %s", Client.host, av[0]); (*cp->Function)(ac, av); if (PushedBack) break; if (PERMaccessconf) clienttimeout = PERMaccessconf->clienttimeout; else clienttimeout = innconf->clienttimeout; } ExitWithStats(0, false); /* NOTREACHED */ return 1; } inn-2.6.0/nnrpd/newnews.c0000644000175200017520000001717512575023702014706 0ustar iuliusiulius/* $Id: newnews.c 8903 2010-01-17 18:21:56Z iulius $ ** ** The NEWNEWS command. */ #include "config.h" #include "clibrary.h" #include "inn/innconf.h" #include "inn/messages.h" #include "inn/wire.h" #include "nnrpd.h" #include "inn/ov.h" #include "cache.h" #define GROUP_LIST_DELTA 10 static bool FindHeader(ARTHANDLE *art, const char **pp, const char **qp, const char* hdr, size_t hdrlen UNUSED) { const char *p, *p1, *q; bool Nocr = true; p = wire_findheader(art->data, art->len, hdr, true); if (p == NULL) return false; q = p; for (p1 = NULL; p < art->data + art->len; p++) { if (p1 != NULL && *p1 == '\r' && *p == '\n') { Nocr = false; break; } if (*p == '\n') { Nocr = true; break; } p1 = p; } if (p >= art->data + art->len) return false; if (!Nocr) p = p1; *pp = p; *qp = q; return true; } /* ** Get Xref: header. */ static char * GetXref(ARTHANDLE *art) { const char *p, *q; if (!FindHeader(art, &p, &q, "xref", sizeof("xref"))) return NULL; return xstrndup(q, p - q); } /* ** Split newsgroup list into array of newsgroups. Return static pointer, ** or NULL if there are no newsgroup. */ static char ** GetGroups(char *p) { static int size; static char **list; int i; char *q; static char *Xrefbuf = NULL; char *Xref = p; if (size == 0) { size = GROUP_LIST_DELTA; list = xmalloc((size + 1) * sizeof(char *)); } Xref = p; for (Xref++; *Xref == ' '; Xref++); if ((Xref = strchr(Xref, ' ')) == NULL) return NULL; for (Xref++; *Xref == ' '; Xref++); if (!Xrefbuf) Xrefbuf = xmalloc(BIG_BUFFER); strlcpy(Xrefbuf, Xref, BIG_BUFFER); if ((q = strchr(Xrefbuf, '\t')) != NULL) *q = '\0'; p = Xrefbuf; for (i = 0 ; ;i++) { while (ISWHITE(*p)) p++; if (*p == '\0' || *p == '\n') break; if (i >= size - 1) { size += GROUP_LIST_DELTA; list = xrealloc(list, (size + 1) * sizeof(char *)); } for (list[i] = p; *p && *p != '\n' && !ISWHITE(*p); p++) { if (*p == ':') *p = '\0'; } if (*p) *p++ = '\0'; } list[i] = NULL; return i ? list : NULL; } static bool HaveSeen(bool AllGroups, char *group, char **groups, char **xrefs) { char *list[2]; list[1] = NULL; for ( ; *xrefs; xrefs++) { list[0] = *xrefs; if ((!AllGroups && PERMmatch(groups, list)) && (!PERMspecified || (PERMspecified && PERMmatch(PERMreadlist, list)))) { if (!strcmp(*xrefs, group)) return false; else return true; } } return false; } static char **groups; static char xref_header[] = "Xref"; static void process_newnews(char *group, bool AllGroups, time_t date) { int low, high; char **xrefs; int count; void *handle; char *p; time_t arrived; ARTHANDLE *art = NULL; TOKEN token; char *data; int len; char *grplist[2]; time_t now; grplist[0] = group; grplist[1] = NULL; if (PERMspecified && !PERMmatch(PERMreadlist, grplist)) return; if (!AllGroups && !PERMmatch(groups, grplist)) return; if (!OVgroupstats(group, &low, &high, &count, NULL)) return; ARTlow = low; ARThigh = high; if ((handle = OVopensearch(group, ARTlow, ARThigh)) != NULL) { ARTNUM artnum; unsigned long artcount = 0; struct cvector *vector = NULL; if (innconf->nfsreader) { time(&now); /* Move the start time back nfsreaderdelay seconds * as we are an NFS reader. */ if (date >= (time_t) innconf->nfsreaderdelay) date -= innconf->nfsreaderdelay; } while (OVsearch(handle, &artnum, &data, &len, &token, &arrived)) { if (innconf->nfsreader != 0 && (time_t) (arrived + innconf->nfsreaderdelay) > now) continue; if (len == 0 || date > arrived) continue; vector = overview_split(data, len, NULL, vector); if (overhdr_xref == -1) { if ((art = SMretrieve(token, RETR_HEAD)) == NULL) continue; p = GetXref(art); SMfreearticle(art); } else { if (PERMaccessconf->nnrpdcheckart && !ARTinstorebytoken(token)) continue; /* We only care about the newsgroup list here, virtual * hosting isn't relevant. */ p = overview_get_extra_header(vector, xref_header); } if (p == NULL) continue; xrefs = GetGroups(p); free(p); if (xrefs == NULL) continue; if (HaveSeen(AllGroups, group, groups, xrefs)) continue; p = overview_get_standard_header(vector, OVERVIEW_MESSAGE_ID); if (p == NULL) continue; ++artcount; cache_add(HashMessageID(p), token); Printf("%s\r\n", p); free(p); } OVclosesearch(handle); notice("%s newnews %s %lu", Client.host, group, artcount); if (vector) cvector_free(vector); } } /* ** NEWNEWS wildmat date time [GMT] ** Return the message-ID of any articles after the specified date. */ void CMDnewnews(int ac, char *av[]) { char *p, *q; char *path; bool AllGroups; char line[BIG_BUFFER]; time_t date; QIOSTATE *qp; int i; bool local = true; TMRstart(TMR_NEWNEWS); /* Check the arguments and parse the date. */ if (ac > 4) { if (strcasecmp(av[4], "GMT") == 0) local = false; else { Reply("%d Syntax error for \"GMT\"\r\n", NNTP_ERR_SYNTAX); TMRstop(TMR_NEWNEWS); return; } } /* Parse the newsgroups. */ AllGroups = (strcmp(av[1], "*") == 0); if (!AllGroups && !NGgetlist(&groups, av[1])) { Reply("%d Bad newsgroup specifier %s\r\n", NNTP_ERR_SYNTAX, av[1]); TMRstop(TMR_NEWNEWS); return; } /* Parse the date. */ date = parsedate_nntp(av[2], av[3], local); if (date == (time_t) -1) { Reply("%d Bad date\r\n", NNTP_ERR_SYNTAX); TMRstop(TMR_NEWNEWS); return; } /* Check authorizations. */ if (!PERMaccessconf->allownewnews) { Reply("%d NEWNEWS command disabled by administrator\r\n", PERMcanauthenticate ? NNTP_FAIL_AUTH_NEEDED : NNTP_ERR_ACCESS); TMRstop(TMR_NEWNEWS); return; } if (!PERMcanread) { Reply("%d Read access denied\r\n", PERMcanauthenticate ? NNTP_FAIL_AUTH_NEEDED : NNTP_ERR_ACCESS); TMRstop(TMR_NEWNEWS); return; } snprintf(line, sizeof(line), "%s %s %s %s", av[1], av[2], av[3], local ? "local" : "GMT"); notice("%s newnews %s", Client.host, line); /* Optimization in case client asks for !* (no groups). */ if (strcmp(av[1], "!*") == 0) { Reply("%d No new news\r\n", NNTP_OK_NEWNEWS); Printf(".\r\n"); TMRstop(TMR_NEWNEWS); return; } /* Make other processes happier if someone uses NEWNEWS. */ if (innconf->nicenewnews != 0) { nice(innconf->nicenewnews); innconf->nicenewnews = 0; } if (strcspn(av[1], "\\!*[?]") == strlen(av[1])) { /* Optimise case -- don't need to scan the active file pattern * matching. */ Reply("%d New news follows\r\n", NNTP_OK_NEWNEWS); for (i = 0; groups[i]; ++i) { process_newnews(groups[i], AllGroups, date); } } else { path = concatpath(innconf->pathdb, INN_PATH_ACTIVE); qp = QIOopen(path); if (qp == NULL) { if (errno == ENOENT) { Reply("%d Can't open active\r\n", NNTP_FAIL_ACTION); } else { syswarn("%s can't fopen %s", Client.host, path); Reply("%d Can't open active\r\n", NNTP_FAIL_ACTION); } free(path); TMRstop(TMR_NEWNEWS); return; } free(path); Reply("%d New news follows\r\n", NNTP_OK_NEWNEWS); while ((p = QIOread(qp)) != NULL) { for (q = p; *q != '\0'; q++) { if (*q == ' ' || *q == '\t') { *q = '\0'; break; } } process_newnews(p, AllGroups, date); } QIOclose(qp); } Printf(".\r\n"); TMRstop(TMR_NEWNEWS); } inn-2.6.0/nnrpd/perl.c0000644000175200017520000002733512575023702014161 0ustar iuliusiulius/* $Id: perl.c 9930 2015-08-10 11:51:03Z iulius $ ** ** Embedded Perl support for INN. ** ** Originally written by Christophe Wolfhugel (although ** he wouldn't recognize it any more, so don't blame him) and modified, ** expanded, and tweaked by James Brister, Dave Hayes, Andrew Gierth, and ** Russ Allbery among others. ** ** This file should contain all innd-specific Perl linkage. Linkage ** applicable to both innd and nnrpd should go into lib/perl.c instead. ** ** We are assuming Perl 5.004 or later. */ #include "config.h" #include "clibrary.h" #include "inn/innconf.h" #include "nnrpd.h" #include "inn/nntp.h" #include "inn/paths.h" #include "post.h" /* Skip this entire file if DO_PERL (./configure --with-perl) isn't set. */ #ifdef DO_PERL #include #include #include #include "ppport.h" #include "innperl.h" extern HEADER Table[], *EndOfTable; extern char PERMuser[]; extern char **OtherHeaders; extern int OtherCount; extern bool HeadersModified; extern bool PerlLoaded; /* #define DEBUG_MODIFY only if you want to see verbose output. */ #ifdef DEBUG_MODIFY static FILE *flog; void dumpTable(char *msg); #endif /* DEBUG_MODIFY */ char * HandleHeaders(char *article) { dSP; HEADER *hp; HV *attribs; HV *hdr; SV *body; int rc; char *p, *q; static char buf[256]; int i; size_t len; char *s,*t; HE *scan; SV *modswitch; int OtherSize; char *argv[] = { NULL }; bool failure; SV *errsv; if(!PerlLoaded) { loadPerl(); } if (!PerlFilterActive) return NULL; /* Not really necessary. */ #ifdef DEBUG_MODIFY if ((flog = fopen("/var/news/log/nnrpdperlerrror", "a+")) == NULL) { syslog(L_ERROR,"Whoops. Can't open error log: %m"); } #endif /* DEBUG_MODIFY */ ENTER; SAVETMPS; /* Create the Perl attributes hash. */ attribs = perl_get_hv("attributes", true); (void) hv_store(attribs, "hostname", 8, newSVpv(Client.host, 0), 0); (void) hv_store(attribs, "ipaddress", 9, newSVpv(Client.ip, 0), 0); (void) hv_store(attribs, "port", 4, newSViv(Client.port), 0); (void) hv_store(attribs, "interface", 9, newSVpv(Client.serverhost, 0), 0); (void) hv_store(attribs, "intipaddr", 9, newSVpv(Client.serverip, 0), 0); (void) hv_store(attribs, "intport", 7, newSViv(Client.serverport), 0); /* Create the Perl header hash. */ hdr = perl_get_hv("hdr", true); for (hp = Table; hp < EndOfTable; hp++) { if (hp->Body) (void) hv_store(hdr, (char *) hp->Name, strlen(hp->Name), newSVpv(hp->Body, 0), 0); } /* Also store other headers. */ OtherSize = OtherCount; for (i = 0; i < OtherCount; i++) { p = OtherHeaders[i]; if (p == NULL) { syslog(L_ERROR, "Null header number %d copying headers for Perl", i); continue; } s = strchr(p, ':'); if (s == NULL) { syslog(L_ERROR, "Bad header copying headers for Perl: '%s'", p); continue; } s++; t = (*s == ' ' ? s + 1 : s); (void) hv_store(hdr, p, (s - p) - 1, newSVpv(t, 0), 0); } /* Store user. */ sv_setpv(perl_get_sv("user", true), PERMuser); /* Store body. */ body = perl_get_sv("body", true); sv_setpv(body, article); /* Call the filtering function. */ /* No need for PUSHMARK(SP) with call_argv(). */ rc = perl_call_argv("filter_post", G_EVAL|G_SCALAR, argv); SPAGAIN; /* Restore headers if they have just been modified by the filter. */ modswitch = perl_get_sv("modify_headers", false); HeadersModified = false; if (SvTRUE(modswitch)) { HeadersModified = true; i = 0; #ifdef DEBUG_MODIFY dumpTable("Before mod"); #endif /* DEBUG_MODIFY */ hv_iterinit(hdr); while ((scan = hv_iternext(hdr)) != NULL) { /* Get the values. We replace the known ones with these * new values. */ p = HePV(scan, len); s = SvPV(HeVAL(scan), PL_na); #ifdef DEBUG_MODIFY fprintf(flog,"Hash iter: '%s','%s'\n", p, s); #endif /* DEBUG_MODIFY */ /* See if it is a table header. */ for (hp = Table; hp < EndOfTable; hp++) { if (strcasecmp(p, hp->Name) == 0) { char *copy = xstrdup(s); HDR_SET(hp - Table, copy); hp->Len = TrimSpaces(hp->Value); for (q = hp->Value ; ISWHITE(*q) || *q == '\n' ; q++) continue; hp->Body = q; if (hp->Len == 0) { free(hp->Value); hp->Value = hp->Body = NULL; } break; } } if (hp != EndOfTable) continue; /* Add to other headers if not empty. */ if (TrimSpaces(s) > 0) { if (i >= OtherSize - 1) { OtherSize += 20; OtherHeaders = xrealloc(OtherHeaders, OtherSize * sizeof(char *)); } t = concat(p, ": ", s, (char *) 0); OtherHeaders[i++] = t; } } OtherCount = i; #ifdef DEBUG_MODIFY dumpTable("After mod"); #endif /* DEBUG_MODIFY */ } hv_undef(attribs); hv_undef(hdr); sv_setsv(body, &PL_sv_undef); buf[0] = '\0'; /* Check $@. */ errsv = ERRSV; if (SvTRUE(errsv)) { failure = true; syslog(L_ERROR, "Perl function filter_post died: %s", SvPV(errsv, PL_na)); (void)POPs; } else { failure = false; if (rc == 1) { p = POPp; if (p != NULL && *p != '\0') strlcpy(buf, p, sizeof(buf)); } } PUTBACK; FREETMPS; LEAVE; if (failure) PerlFilter(false); if (buf[0] != '\0') return buf; #ifdef DEBUG_MODIFY fclose(flog); #endif /* DEBUG_MODIFY */ return NULL; } void loadPerl(void) { char *path; path = concatpath(innconf->pathfilter, INN_PATH_PERL_FILTER_NNRPD); PERLsetup(NULL, path, "filter_post"); free(path); PerlFilter(true); PerlLoaded = true; } void perlAccess(char *user, struct vector *access_vec) { dSP; HV *attribs; SV *sv; int rc, i; char *key, *val, *buffer; SV *errsv; if (!PerlFilterActive) return; ENTER; SAVETMPS; attribs = perl_get_hv("attributes", true); (void) hv_store(attribs, "hostname", 8, newSVpv(Client.host, 0), 0); (void) hv_store(attribs, "ipaddress", 9, newSVpv(Client.ip, 0), 0); (void) hv_store(attribs, "port", 4, newSViv(Client.port), 0); (void) hv_store(attribs, "interface", 9, newSVpv(Client.serverhost, 0), 0); (void) hv_store(attribs, "intipaddr", 9, newSVpv(Client.serverip, 0), 0); (void) hv_store(attribs, "intport", 7, newSViv(Client.serverport), 0); (void) hv_store(attribs, "username", 8, newSVpv(user, 0), 0); PUSHMARK(SP); PUTBACK; if (perl_get_cv("access", 0) == NULL) { syslog(L_ERROR, "Perl function access not defined"); Reply("%d Internal error (3). Goodbye!\r\n", NNTP_FAIL_TERMINATING); ExitWithStats(1, true); } rc = perl_call_pv("access", G_EVAL|G_ARRAY); SPAGAIN; if (rc == 0) { /* Error occured, same as checking $@. */ errsv = ERRSV; syslog(L_ERROR, "Perl function access died: %s", SvPV(errsv, PL_na)); Reply("%d Internal error (1). Goodbye!\r\n", NNTP_FAIL_TERMINATING); ExitWithStats(1, true); } if ((rc % 2) != 0) { syslog(L_ERROR, "Perl function access returned an odd number of arguments: %i", rc); Reply("%d Internal error (2). Goodbye!\r\n", NNTP_FAIL_TERMINATING); ExitWithStats(1, true); } vector_resize(access_vec, (rc / 2)); buffer = xmalloc(BIG_BUFFER); for (i = (rc / 2); i >= 1; i--) { sv = POPs; val = SvPV(sv, PL_na); sv = POPs; key = SvPV(sv, PL_na); strlcpy(buffer, key, BIG_BUFFER); strlcat(buffer, ": \"", BIG_BUFFER); strlcat(buffer, val, BIG_BUFFER); strlcat(buffer, "\"\n", BIG_BUFFER); vector_add(access_vec, buffer); } free(buffer); hv_undef(attribs); PUTBACK; FREETMPS; LEAVE; } void perlAuthInit(void) { dSP; int rc; SV *errsv; if (!PerlFilterActive) return; if (perl_get_cv("auth_init", 0) == NULL) { return; } ENTER; SAVETMPS; PUSHMARK(SP); PUTBACK; rc = perl_call_pv("auth_init", G_EVAL|G_DISCARD); SPAGAIN; errsv = ERRSV; if (SvTRUE(errsv)) { /* Check $@. */ syslog(L_ERROR, "Perl function authenticate died: %s", SvPV(errsv, PL_na)); Reply("%d Internal error (1). Goodbye!\r\n", NNTP_FAIL_TERMINATING); ExitWithStats(1, true); } while (rc--) { (void)POPs; } PUTBACK; FREETMPS; LEAVE; } void perlAuthenticate(char *user, char *passwd, int *code, char *errorstring, char *newUser) { dSP; HV *attribs; int rc; char *p; SV *errsv; if (!PerlFilterActive) *code = NNTP_FAIL_AUTHINFO_BAD; if (perl_get_cv("authenticate", 0) == NULL) { syslog(L_ERROR, "Perl function authenticate not defined"); Reply("%d Internal error (3). Goodbye!\r\n", NNTP_FAIL_TERMINATING); ExitWithStats(1, true); } ENTER; SAVETMPS; attribs = perl_get_hv("attributes", true); (void) hv_store(attribs, "hostname", 8, newSVpv(Client.host, 0), 0); (void) hv_store(attribs, "ipaddress", 9, newSVpv(Client.ip, 0), 0); (void) hv_store(attribs, "port", 4, newSViv(Client.port), 0); (void) hv_store(attribs, "interface", 9, newSVpv(Client.serverhost, 0), 0); (void) hv_store(attribs, "intipaddr", 9, newSVpv(Client.serverip, 0), 0); (void) hv_store(attribs, "intport", 7, newSViv(Client.serverport), 0); (void) hv_store(attribs, "username", 8, newSVpv(user, 0), 0); (void) hv_store(attribs, "password", 8, newSVpv(passwd, 0), 0); PUSHMARK(SP); PUTBACK; rc = perl_call_pv("authenticate", G_EVAL|G_ARRAY); SPAGAIN; if (rc == 0 ) { /* Error occurred, same as checking $@. */ errsv = ERRSV; syslog(L_ERROR, "Perl function authenticate died: %s", SvPV(errsv, PL_na)); Reply("%d Internal error (1). Goodbye!\r\n", NNTP_FAIL_TERMINATING); ExitWithStats(1, false); } if ((rc != 3) && (rc != 2)) { syslog(L_ERROR, "Perl function authenticate returned wrong number of results: %d", rc); Reply("%d Internal error (2). Goodbye!\r\n", NNTP_FAIL_TERMINATING); ExitWithStats(1, false); } /* FIXME: Change the structure of the code so that we don't have to rely * on keeping these sizes in sync with the buffers that are passed into * this function. */ if (rc == 3) { p = POPp; strlcpy(newUser, p, BIG_BUFFER); } p = POPp; strlcpy(errorstring, p, BIG_BUFFER); *code = POPi; hv_undef(attribs); PUTBACK; FREETMPS; LEAVE; } #ifdef DEBUG_MODIFY void dumpTable (char *msg) { HEADER *hp; int i; fprintf(flog, "===BEGIN TABLE DUMP: %s\n", msg); for (hp = Table; hp < EndOfTable; hp++) { fprintf(flog, " Name: '%s'",hp->Name); fflush(flog); fprintf(flog, " Size: '%d'", hp->Size); fflush(flog); fprintf(flog, " Value: '%s'\n", ((hp->Value == NULL) ? "(NULL)" : hp->Value)); fflush(flog); } for (i=0; i. ** ** Author: Kenichi Okada ** Created: 2000-02-22 ** ** [RFC 2246] "The TLS Protocol Version 1.0" ** by Christopher Allen and ** Tim Dierks (1999/01) ** ** [RFC 2595] "Using TLS with IMAP, POP3 and ACAP" ** by Chris Newman (1999/06) */ #ifndef HAVE_OPENSSL_SSL_H # undef HAVE_OPENSSL #endif #ifdef HAVE_OPENSSL #ifndef TLS_H #define TLS_H #include #include #include #include #include #include #include #if !defined(OPENSSL_NO_EC) && defined(TLSEXT_ECPOINTFORMAT_uncompressed) # include # include # define HAVE_OPENSSL_ECC #endif /* Protocol support. */ #define INN_TLS_SSLv2 1 #define INN_TLS_SSLv3 2 #define INN_TLS_TLSv1 4 #define INN_TLS_TLSv1_1 8 #define INN_TLS_TLSv1_2 16 /* Init TLS engine. */ int tls_init_serverengine(int verifydepth, /* Depth to verify. */ int askcert, /* 1 = Verify client. */ int requirecert, /* 1 = Another client verify? */ char *tls_CAfile, char *tls_CApath, char *tls_cert_file, char *tls_key_file, bool prefer_server_ciphers, bool tls_compression, struct vector *tls_protocols, char *tls_ciphers, char *tls_ec_curve); /* Init TLS. */ int tls_init(void); /* Start TLS negotiation. */ int tls_start_servertls(int readfd, int writefd); ssize_t SSL_writev(SSL *ssl, const struct iovec *vector, int count); #endif /* TLS_H */ #endif /* HAVE_OPENSSL */ inn-2.6.0/nnrpd/perm.c0000644000175200017520000016627012575023702014164 0ustar iuliusiulius/* $Id: perm.c 9847 2015-05-03 15:01:03Z iulius $ ** ** How to figure out where a user comes from, and what that user can do once ** we know who sie is. */ #include "config.h" #include "clibrary.h" #include #include #include #include "conffile.h" #include "inn/network.h" #include "inn/innconf.h" #include "innperl.h" #include "nnrpd.h" /* Needed on AIX 4.1 to get fd_set and friends. */ #ifdef HAVE_SYS_SELECT_H # include #endif #ifdef HAVE_OPENSSL extern bool nnrpd_starttls_done; #endif /* HAVE_OPENSSL */ /* Data types. */ typedef struct _CONFCHAIN { CONFFILE *f; struct _CONFCHAIN *parent; } CONFCHAIN; typedef struct _METHOD { char *name; char *program; int type; /* Type of auth (perl, python or external)/ */ char *users; /* Only used for auth_methods, not for res_methods. */ char **extra_logs; } METHOD; typedef struct _AUTHGROUP { char *name; char *key; #ifdef HAVE_OPENSSL int require_ssl; #endif char *hosts; METHOD **res_methods; METHOD **auth_methods; char *default_user; char *default_domain; char *localaddress; char *access_script; int access_type; /* Type of access (Perl or Python). */ char *dynamic_script; int dynamic_type; /* Type of dynamic authorization (Python only). */ } AUTHGROUP; typedef struct _GROUP { char *name; struct _GROUP *above; AUTHGROUP *auth; ACCESSGROUP *access; } GROUP; /* Function declarations. */ static void PERMreadfile(char *filename); static void authdecl_parse(AUTHGROUP*, CONFFILE*, CONFTOKEN*); static void accessdecl_parse(ACCESSGROUP *curaccess, CONFFILE *f, CONFTOKEN *tok); static void method_parse(METHOD*, CONFFILE*, CONFTOKEN*, int); static void add_authgroup(AUTHGROUP*); static void add_accessgroup(ACCESSGROUP*); static void strip_accessgroups(void); static METHOD *copy_method(METHOD*); static void free_method(METHOD*); static AUTHGROUP *copy_authgroup(AUTHGROUP*); static void free_authgroup(AUTHGROUP*); static ACCESSGROUP *copy_accessgroup(ACCESSGROUP*); static void free_accessgroup(ACCESSGROUP*); static void CompressList(char*); static bool MatchHost(char*, char*, char*); static int MatchUser(char*, char*); static char *ResolveUser(AUTHGROUP*); static char *AuthenticateUser(AUTHGROUP*, char*, char*, int*, char*); static void GrowArray(void*, void*); static void PERMvectortoaccess(ACCESSGROUP *acc, const char *name, struct vector *acccess_vec) UNUSED; /* Global variables. */ static AUTHGROUP **auth_realms; static AUTHGROUP *success_auth; static ACCESSGROUP **access_realms; static char *ConfigBit; static int ConfigBitsize; extern bool PerlLoaded; #define PERMlbrace 1 #define PERMrbrace 2 #define PERMgroup 3 #define PERMauth 4 #define PERMaccess 5 #define PERMhost 6 #define PERMauthprog 7 #define PERMresolv 8 #define PERMresprog 9 #define PERMdefuser 10 #define PERMdefdomain 11 #define PERMusers 12 #define PERMnewsgroups 13 #define PERMread 14 #define PERMpost 15 #define PERMaccessrp 16 #define PERMalsolog 18 #define PERMprogram 19 #define PERMinclude 20 #define PERMkey 21 #define PERMlocaltime 22 #define PERMstrippath 23 #define PERMnnrpdperlfilter 24 #define PERMnnrpdpythonfilter 25 #define PERMfromhost 26 #define PERMpathhost 27 #define PERMorganization 28 #define PERMmoderatormailer 29 #define PERMdomain 30 #define PERMcomplaints 31 #define PERMspoolfirst 32 #define PERMcheckincludedtext 33 #define PERMclienttimeout 34 #define PERMlocalmaxartsize 35 #define PERMreadertrack 36 #define PERMstrippostcc 37 #define PERMaddinjectiondate 38 #define PERMaddinjectionpostingaccount 39 #define PERMaddinjectionpostinghost 40 #define PERMnnrpdposthost 41 #define PERMnnrpdpostport 42 #define PERMnnrpdoverstats 43 #define PERMbackoff_auth 44 #define PERMbackoff_db 45 #define PERMbackoff_k 46 #define PERMbackoff_postfast 47 #define PERMbackoff_postslow 48 #define PERMbackoff_trigger 49 #define PERMnnrpdcheckart 50 #define PERMnnrpdauthsender 51 #define PERMvirtualhost 52 #define PERMnewsmaster 53 #define PERMlocaladdress 54 #define PERMrejectwith 55 #define PERMmaxbytespersecond 56 #define PERMperl_auth 57 #define PERMpython_auth 58 #define PERMperl_access 59 #define PERMpython_access 60 #define PERMpython_dynamic 61 #ifdef HAVE_OPENSSL #define PERMrequire_ssl 62 #define PERMMAX 63 #else #define PERMMAX 62 #endif #define TEST_CONFIG(a, b) \ { \ int byte, offset; \ offset = a % 8; \ byte = (a - offset) / 8; \ b = ((ConfigBit[byte] & (1 << offset)) != 0) ? true : false; \ } #define SET_CONFIG(a) \ { \ int byte, offset; \ offset = a % 8; \ byte = (a - offset) / 8; \ ConfigBit[byte] |= (1 << offset); \ } #define CLEAR_CONFIG(a) \ { \ int byte, offset; \ offset = a % 8; \ byte = (a - offset) / 8; \ ConfigBit[byte] &= ~(1 << offset); \ } static CONFTOKEN PERMtoks[] = { { PERMlbrace, (char *) "{" }, { PERMrbrace, (char *) "}" }, { PERMgroup, (char *) "group" }, { PERMauth, (char *) "auth" }, { PERMaccess, (char *) "access" }, { PERMhost, (char *) "hosts:" }, { PERMauthprog, (char *) "auth:" }, { PERMresolv, (char *) "res" }, { PERMresprog, (char *) "res:" }, { PERMdefuser, (char *) "default:" }, { PERMdefdomain, (char *) "default-domain:" }, { PERMusers, (char *) "users:" }, { PERMnewsgroups, (char *) "newsgroups:" }, { PERMread, (char *) "read:" }, { PERMpost, (char *) "post:" }, { PERMaccessrp, (char *) "access:" }, { PERMalsolog, (char *) "log:" }, { PERMprogram, (char *) "program:" }, { PERMinclude, (char *) "include" }, { PERMkey, (char *) "key:" }, { PERMlocaltime, (char *) "localtime:" }, { PERMstrippath, (char *) "strippath:" }, { PERMnnrpdperlfilter, (char *) "perlfilter:" }, { PERMnnrpdpythonfilter, (char *) "pythonfilter:" }, { PERMfromhost, (char *) "fromhost:" }, { PERMpathhost, (char *) "pathhost:" }, { PERMorganization, (char *) "organization:" }, { PERMmoderatormailer, (char *) "moderatormailer:" }, { PERMdomain, (char *) "domain:" }, { PERMcomplaints, (char *) "complaints:" }, { PERMspoolfirst, (char *) "spoolfirst:" }, { PERMcheckincludedtext, (char *) "checkincludedtext:" }, { PERMclienttimeout, (char *) "clienttimeout:" }, { PERMlocalmaxartsize, (char *) "localmaxartsize:" }, { PERMreadertrack, (char *) "readertrack:" }, { PERMstrippostcc, (char *) "strippostcc:" }, { PERMaddinjectiondate, (char *) "addinjectiondate:" }, { PERMaddinjectionpostingaccount, (char *) "addinjectionpostingaccount:" }, { PERMaddinjectionpostinghost, (char *) "addinjectionpostinghost:" }, { PERMnnrpdposthost, (char *) "nnrpdposthost:" }, { PERMnnrpdpostport, (char *) "nnrpdpostport:" }, { PERMnnrpdoverstats, (char *) "nnrpdoverstats:" }, { PERMbackoff_auth, (char *) "backoff_auth:" }, { PERMbackoff_db, (char *) "backoff_db:" }, { PERMbackoff_k, (char *) "backoff_k:" }, { PERMbackoff_postfast, (char *) "backoff_postfast:" }, { PERMbackoff_postslow, (char *) "backoff_postslow:" }, { PERMbackoff_trigger, (char *) "backoff_trigger:" }, { PERMnnrpdcheckart, (char *) "nnrpdcheckart:" }, { PERMnnrpdauthsender, (char *) "nnrpdauthsender:" }, { PERMvirtualhost, (char *) "virtualhost:" }, { PERMnewsmaster, (char *) "newsmaster:" }, { PERMlocaladdress, (char *) "localaddress:" }, { PERMrejectwith, (char *) "reject_with:" }, { PERMmaxbytespersecond, (char *) "max_rate:" }, { PERMperl_auth, (char *) "perl_auth:" }, { PERMpython_auth, (char *) "python_auth:" }, { PERMperl_access, (char *) "perl_access:" }, { PERMpython_access, (char *) "python_access:" }, { PERMpython_dynamic, (char *) "python_dynamic:" }, #ifdef HAVE_OPENSSL { PERMrequire_ssl, (char *) "require_ssl:" }, #endif { 0, (char *) NULL } }; /* Function definitions. */ static void GrowArray(void *data, void *el) { int i; void ***array = data; if (*array == NULL) { *array = xmalloc(2 * sizeof(void *)); i = 0; } else { for (i = 0; (*array)[i]; i++) ; *array = xrealloc(*array, (i + 2) * sizeof(void *)); } (*array)[i++] = el; (*array)[i] = 0; } static METHOD * copy_method(METHOD *orig) { METHOD *ret; int i; ret = xmalloc(sizeof(METHOD)); memset(ConfigBit, '\0', ConfigBitsize); ret->name = xstrdup(orig->name); if (orig->program != NULL) { ret->program = xstrdup(orig->program); } else { ret->program = NULL; } if (orig->users) ret->users = xstrdup(orig->users); else ret->users = 0; ret->extra_logs = 0; if (orig->extra_logs) { for (i = 0; orig->extra_logs[i]; i++) GrowArray(&ret->extra_logs, xstrdup(orig->extra_logs[i])); } ret->type = orig->type; return(ret); } static void free_method(METHOD *del) { int j; if (del->extra_logs) { for (j = 0; del->extra_logs[j]; j++) free(del->extra_logs[j]); free(del->extra_logs); } if (del->program) free(del->program); if (del->users) free(del->users); free(del->name); free(del); } static AUTHGROUP * copy_authgroup(AUTHGROUP *orig) { AUTHGROUP *ret; int i; if (!orig) return(0); ret = xmalloc(sizeof(AUTHGROUP)); memset(ConfigBit, '\0', ConfigBitsize); if (orig->name) ret->name = xstrdup(orig->name); else ret->name = 0; if (orig->key) ret->key = xstrdup(orig->key); else ret->key = 0; if (orig->hosts) ret->hosts = xstrdup(orig->hosts); else ret->hosts = 0; #ifdef HAVE_OPENSSL ret->require_ssl = orig->require_ssl; #endif ret->res_methods = 0; if (orig->res_methods) { for (i = 0; orig->res_methods[i]; i++) GrowArray(&ret->res_methods, copy_method(orig->res_methods[i]));; } ret->auth_methods = 0; if (orig->auth_methods) { for (i = 0; orig->auth_methods[i]; i++) GrowArray(&ret->auth_methods, copy_method(orig->auth_methods[i])); } if (orig->default_user) ret->default_user = xstrdup(orig->default_user); else ret->default_user = 0; if (orig->default_domain) ret->default_domain = xstrdup(orig->default_domain); else ret->default_domain = 0; if (orig->localaddress) ret->localaddress = xstrdup(orig->localaddress); else ret->localaddress = 0; if (orig->access_script) ret->access_script = xstrdup(orig->access_script); else ret->access_script = 0; if (orig->access_type) ret->access_type = orig->access_type; else ret->access_type = 0; if (orig->dynamic_script) ret->dynamic_script = xstrdup(orig->dynamic_script); else ret->dynamic_script = 0; if (orig->dynamic_type) ret->dynamic_type = orig->dynamic_type; else ret->dynamic_type = 0; return(ret); } static ACCESSGROUP * copy_accessgroup(ACCESSGROUP *orig) { ACCESSGROUP *ret; if (!orig) return(0); ret = xmalloc(sizeof(ACCESSGROUP)); memset(ConfigBit, '\0', ConfigBitsize); /* Copy all anyway, and update for local strings. */ *ret = *orig; if (orig->name) ret->name = xstrdup(orig->name); if (orig->key) ret->key = xstrdup(orig->key); if (orig->read) ret->read = xstrdup(orig->read); if (orig->post) ret->post = xstrdup(orig->post); if (orig->users) ret->users = xstrdup(orig->users); if (orig->rejectwith) ret->rejectwith = xstrdup(orig->rejectwith); if (orig->fromhost) ret->fromhost = xstrdup(orig->fromhost); if (orig->pathhost) ret->pathhost = xstrdup(orig->pathhost); if (orig->organization) ret->organization = xstrdup(orig->organization); if (orig->moderatormailer) ret->moderatormailer = xstrdup(orig->moderatormailer); if (orig->domain) ret->domain = xstrdup(orig->domain); if (orig->complaints) ret->complaints = xstrdup(orig->complaints); if (orig->nnrpdposthost) ret->nnrpdposthost = xstrdup(orig->nnrpdposthost); if (orig->backoff_db) ret->backoff_db = xstrdup(orig->backoff_db); if (orig->newsmaster) ret->newsmaster = xstrdup(orig->newsmaster); return(ret); } static void SetDefaultAuth(AUTHGROUP *curauth UNUSED) { #ifdef HAVE_OPENSSL curauth->require_ssl = false; #endif } void SetDefaultAccess(ACCESSGROUP *curaccess) { curaccess->allownewnews = innconf->allownewnews;; curaccess->allowihave = false; curaccess->locpost = false; curaccess->allowapproved = false; curaccess->localtime = false; curaccess->strippath = false; curaccess->nnrpdperlfilter = true; curaccess->nnrpdpythonfilter = true; curaccess->fromhost = NULL; if (innconf->fromhost) curaccess->fromhost = xstrdup(innconf->fromhost); curaccess->pathhost = NULL; if (innconf->pathhost) curaccess->pathhost = xstrdup(innconf->pathhost); curaccess->organization = NULL; if (innconf->organization) curaccess->organization = xstrdup(innconf->organization); curaccess->moderatormailer = NULL; if (innconf->moderatormailer) curaccess->moderatormailer = xstrdup(innconf->moderatormailer); curaccess->domain = NULL; if (innconf->domain) curaccess->domain = xstrdup(innconf->domain); curaccess->complaints = NULL; if (innconf->complaints) curaccess->complaints = xstrdup(innconf->complaints); curaccess->spoolfirst = innconf->spoolfirst; curaccess->checkincludedtext = innconf->checkincludedtext; curaccess->clienttimeout = innconf->clienttimeout; curaccess->localmaxartsize = innconf->localmaxartsize; curaccess->readertrack = innconf->readertrack; curaccess->strippostcc = innconf->strippostcc; curaccess->addinjectiondate = innconf->addinjectiondate; curaccess->addinjectionpostingaccount = innconf->addinjectionpostingaccount; curaccess->addinjectionpostinghost = innconf->addinjectionpostinghost; curaccess->nnrpdposthost = innconf->nnrpdposthost; curaccess->nnrpdpostport = innconf->nnrpdpostport; curaccess->nnrpdoverstats = innconf->nnrpdoverstats; curaccess->backoff_auth = innconf->backoffauth; curaccess->backoff_db = NULL; if (innconf->backoffdb && *innconf->backoffdb != '\0') curaccess->backoff_db = xstrdup(innconf->backoffdb); curaccess->backoff_k = innconf->backoffk; curaccess->backoff_postfast = innconf->backoffpostfast; curaccess->backoff_postslow = innconf->backoffpostslow; curaccess->backoff_trigger = innconf->backofftrigger; curaccess->nnrpdcheckart = innconf->nnrpdcheckart; curaccess->nnrpdauthsender = innconf->nnrpdauthsender; curaccess->virtualhost = false; curaccess->newsmaster = NULL; curaccess->maxbytespersecond = 0; } static void free_authgroup(AUTHGROUP *del) { int i; if (del->name) free(del->name); if (del->key) free(del->key); if (del->hosts) free(del->hosts); if (del->res_methods) { for (i = 0; del->res_methods[i]; i++) free_method(del->res_methods[i]); free(del->res_methods); } if (del->auth_methods) { for (i = 0; del->auth_methods[i]; i++) free_method(del->auth_methods[i]); free(del->auth_methods); } if (del->default_user) free(del->default_user); if (del->default_domain) free(del->default_domain); if (del->localaddress) free(del->localaddress); if (del->access_script) free(del->access_script); if (del->dynamic_script) free(del->dynamic_script); free(del); } static void free_accessgroup(ACCESSGROUP *del) { if (del->name) free(del->name); if (del->key) free(del->key); if (del->read) free(del->read); if (del->post) free(del->post); if (del->users) free(del->users); if (del->rejectwith) free(del->rejectwith); if (del->fromhost) free(del->fromhost); if (del->pathhost) free(del->pathhost); if (del->organization) free(del->organization); if (del->moderatormailer) free(del->moderatormailer); if (del->domain) free(del->domain); if (del->complaints) free(del->complaints); if (del->nnrpdposthost) free(del->nnrpdposthost); if (del->backoff_db) free(del->backoff_db); if (del->newsmaster) free(del->newsmaster); free(del); } static void ReportError(CONFFILE *f, const char *err) { syslog(L_ERROR, "%s syntax error in %s(%d), %s", Client.host, f->filename, f->lineno, err); Reply("%d NNTP server unavailable. Try later!\r\n", NNTP_FAIL_TERMINATING); ExitWithStats(1, true); } static void method_parse(METHOD *method, CONFFILE *f, CONFTOKEN *tok, int auth) { int oldtype; oldtype = tok->type; tok = CONFgettoken(0, f); if (tok == NULL) { ReportError(f, "Expected value."); } switch (oldtype) { case PERMalsolog: GrowArray(&method->extra_logs, xstrdup(tok->name)); break; case PERMusers: if (!auth) { ReportError(f, "Unexpected users: directive in file."); } else if (method->users) { ReportError(f, "Multiple users: directive in file."); } method->users = xstrdup(tok->name); break; case PERMprogram: if (method->program) { ReportError(f, "Multiple program: directives in auth/res declaration."); } method->program = xstrdup(tok->name); break; } } static void authdecl_parse(AUTHGROUP *curauth, CONFFILE *f, CONFTOKEN *tok) { int oldtype; #ifdef HAVE_OPENSSL int boolval; #endif METHOD *m; bool bit; char buff[SMBUF], *oldname, *p; oldtype = tok->type; oldname = tok->name; tok = CONFgettoken(PERMtoks, f); if (tok == NULL) { ReportError(f, "Expected value."); } TEST_CONFIG(oldtype, bit); if (bit) { snprintf(buff, sizeof(buff), "Duplicated '%s' field in auth group.", oldname); ReportError(f, buff); } #ifdef HAVE_OPENSSL if (strcasecmp(tok->name, "on") == 0 || strcasecmp(tok->name, "true") == 0 || strcasecmp(tok->name, "yes") == 0) boolval = true; else if (strcasecmp(tok->name, "off") == 0 || strcasecmp(tok->name, "false") == 0 || strcasecmp(tok->name, "no") == 0) boolval = false; else boolval = -1; #endif switch (oldtype) { case PERMkey: curauth->key = xstrdup(tok->name); SET_CONFIG(PERMkey); break; #ifdef HAVE_OPENSSL case PERMrequire_ssl: if (boolval != -1) curauth->require_ssl = boolval; SET_CONFIG(PERMrequire_ssl); break; #endif case PERMhost: curauth->hosts = xstrdup(tok->name); CompressList(curauth->hosts); SET_CONFIG(PERMhost); /* nnrpd.c downcases the names of connecting hosts. We should * therefore also downcase the wildmat patterns to make sure there * aren't any surprises. DNS is case-insensitive. */ for (p = curauth->hosts; *p; p++) if (isupper((unsigned char) *p)) *p = tolower((unsigned char) *p); break; case PERMdefdomain: curauth->default_domain = xstrdup(tok->name); SET_CONFIG(PERMdefdomain); break; case PERMdefuser: curauth->default_user = xstrdup(tok->name); SET_CONFIG(PERMdefuser); break; case PERMresolv: case PERMresprog: m = xcalloc(1, sizeof(METHOD)); memset(ConfigBit, '\0', ConfigBitsize); GrowArray(&curauth->res_methods, m); if (oldtype == PERMresprog) m->program = xstrdup(tok->name); else { m->name = xstrdup(tok->name); tok = CONFgettoken(PERMtoks, f); if (tok == NULL || tok->type != PERMlbrace) { ReportError(f, "Expected '{' after 'res'."); } tok = CONFgettoken(PERMtoks, f); while (tok != NULL && tok->type != PERMrbrace) { method_parse(m, f, tok, 0); tok = CONFgettoken(PERMtoks, f); } if (m->program == NULL) { ReportError(f, "Missing 'program:' key."); } if (tok == NULL) { ReportError(f, "Unexpected EOF."); } } break; case PERMauth: case PERMperl_auth: case PERMpython_auth: case PERMauthprog: m = xcalloc(1, sizeof(METHOD)); memset(ConfigBit, '\0', ConfigBitsize); GrowArray(&curauth->auth_methods, m); if (oldtype == PERMauthprog) { m->type = PERMauthprog; m->program = xstrdup(tok->name); } else if (oldtype == PERMperl_auth) { #ifdef DO_PERL m->type = PERMperl_auth; m->program = xstrdup(tok->name); #else ReportError(f, "perl_auth can not be used in readers.conf: INN not compiled with Perl support enabled."); #endif } else if (oldtype == PERMpython_auth) { #ifdef DO_PYTHON m->type = PERMpython_auth; m->program = xstrdup(tok->name); #else ReportError(f, "python_auth can not be used in readers.conf: INN not compiled with Python support enabled."); #endif } else { m->name = xstrdup(tok->name); tok = CONFgettoken(PERMtoks, f); if (tok == NULL || tok->type != PERMlbrace) { ReportError(f, "Expected '{' after 'auth'."); } tok = CONFgettoken(PERMtoks, f); while (tok != NULL && tok->type != PERMrbrace) { method_parse(m, f, tok, 1); tok = CONFgettoken(PERMtoks, f); } if (tok == NULL) { ReportError(f, "Unexpected EOF."); } } break; case PERMperl_access: #ifdef DO_PERL curauth->access_script = xstrdup(tok->name); curauth->access_type = PERMperl_access; #else ReportError(f, "perl_access can not be used in readers.conf: INN not compiled with Perl support enabled."); #endif break; case PERMpython_access: #ifdef DO_PYTHON curauth->access_script = xstrdup(tok->name); curauth->access_type = PERMpython_access; #else ReportError(f, "python_access can not be used in readers.conf: INN not compiled with Python support enabled."); #endif break; case PERMpython_dynamic: #ifdef DO_PYTHON curauth->dynamic_script = xstrdup(tok->name); curauth->dynamic_type = PERMpython_dynamic; #else ReportError(f, "python_dynamic can not be used in readers.conf: INN not compiled with Python support enabled."); #endif break; case PERMlocaladdress: curauth->localaddress = xstrdup(tok->name); CompressList(curauth->localaddress); SET_CONFIG(PERMlocaladdress); break; default: snprintf(buff, sizeof(buff), "Unexpected token '%s'.", tok->name); ReportError(f, buff); break; } } static void accessdecl_parse(ACCESSGROUP *curaccess, CONFFILE *f, CONFTOKEN *tok) { int oldtype, boolval; bool bit; char buff[SMBUF], *oldname; oldtype = tok->type; oldname = tok->name; tok = CONFgettoken(0, f); if (tok == NULL) { ReportError(f, "Expected value."); } TEST_CONFIG(oldtype, bit); if (bit) { snprintf(buff, sizeof(buff), "Duplicated '%s' field in access group.", oldname); ReportError(f, buff); } if (strcasecmp(tok->name, "on") == 0 || strcasecmp(tok->name, "true") == 0 || strcasecmp(tok->name, "yes") == 0) boolval = true; else if (strcasecmp(tok->name, "off") == 0 || strcasecmp(tok->name, "false") == 0 || strcasecmp(tok->name, "no") == 0) boolval = false; else boolval = -1; switch (oldtype) { case PERMkey: curaccess->key = xstrdup(tok->name); SET_CONFIG(oldtype); break; case PERMusers: curaccess->users = xstrdup(tok->name); CompressList(curaccess->users); SET_CONFIG(oldtype); break; case PERMrejectwith: curaccess->rejectwith = xstrdup(tok->name); SET_CONFIG(oldtype); break; case PERMnewsgroups: TEST_CONFIG(PERMread, bit); if (bit) { /* Syntax error... can't set read: or post: _and_ use * newsgroups:. */ ReportError(f, "read: newsgroups already set."); } TEST_CONFIG(PERMpost, bit); if (bit) { /* Syntax error... can't set read: or post: _and_ use * newsgroups:. */ ReportError(f, "post: newsgroups already set."); } curaccess->read = xstrdup(tok->name); CompressList(curaccess->read); curaccess->post = xstrdup(tok->name); CompressList(curaccess->post); SET_CONFIG(oldtype); SET_CONFIG(PERMread); SET_CONFIG(PERMpost); break; case PERMread: curaccess->read = xstrdup(tok->name); CompressList(curaccess->read); SET_CONFIG(oldtype); break; case PERMpost: curaccess->post = xstrdup(tok->name); CompressList(curaccess->post); SET_CONFIG(oldtype); break; case PERMaccessrp: TEST_CONFIG(PERMread, bit); if (bit && strchr(tok->name, 'R') == NULL) { free(curaccess->read); curaccess->read = 0; CLEAR_CONFIG(PERMread); } TEST_CONFIG(PERMpost, bit); if (bit && strchr(tok->name, 'P') == NULL) { free(curaccess->post); curaccess->post = 0; CLEAR_CONFIG(PERMpost); } curaccess->allowapproved = (strchr(tok->name, 'A') != NULL); curaccess->allownewnews = (strchr(tok->name, 'N') != NULL); curaccess->allowihave = (strchr(tok->name, 'I') != NULL); curaccess->locpost = (strchr(tok->name, 'L') != NULL); SET_CONFIG(oldtype); break; case PERMlocaltime: if (boolval != -1) curaccess->localtime = boolval; SET_CONFIG(oldtype); break; case PERMstrippath: if (boolval != -1) curaccess->strippath = boolval; SET_CONFIG(oldtype); break; case PERMnnrpdperlfilter: if (boolval != -1) curaccess->nnrpdperlfilter = boolval; SET_CONFIG(oldtype); break; case PERMnnrpdpythonfilter: if (boolval != -1) curaccess->nnrpdpythonfilter = boolval; SET_CONFIG(oldtype); break; case PERMfromhost: if (curaccess->fromhost) free(curaccess->fromhost); curaccess->fromhost = xstrdup(tok->name); SET_CONFIG(oldtype); break; case PERMpathhost: if (curaccess->pathhost) free(curaccess->pathhost); curaccess->pathhost = xstrdup(tok->name); SET_CONFIG(oldtype); break; case PERMorganization: if (curaccess->organization) free(curaccess->organization); curaccess->organization = xstrdup(tok->name); SET_CONFIG(oldtype); break; case PERMmoderatormailer: if (curaccess->moderatormailer) free(curaccess->moderatormailer); curaccess->moderatormailer = xstrdup(tok->name); SET_CONFIG(oldtype); break; case PERMdomain: if (curaccess->domain) free(curaccess->domain); curaccess->domain = xstrdup(tok->name); SET_CONFIG(oldtype); break; case PERMcomplaints: if (curaccess->complaints) free(curaccess->complaints); curaccess->complaints = xstrdup(tok->name); SET_CONFIG(oldtype); break; case PERMspoolfirst: if (boolval != -1) curaccess->spoolfirst = boolval; SET_CONFIG(oldtype); break; case PERMcheckincludedtext: if (boolval != -1) curaccess->checkincludedtext = boolval; SET_CONFIG(oldtype); break; case PERMclienttimeout: curaccess->clienttimeout = atoi(tok->name); SET_CONFIG(oldtype); break; case PERMlocalmaxartsize: curaccess->localmaxartsize = strtoul(tok->name, NULL, 10); SET_CONFIG(oldtype); break; case PERMreadertrack: if (boolval != -1) curaccess->readertrack = boolval; SET_CONFIG(oldtype); break; case PERMstrippostcc: if (boolval != -1) curaccess->strippostcc = boolval; SET_CONFIG(oldtype); break; case PERMaddinjectiondate: if (boolval != -1) curaccess->addinjectiondate = boolval; SET_CONFIG(oldtype); break; case PERMaddinjectionpostingaccount: if (boolval != -1) curaccess->addinjectionpostingaccount = boolval; SET_CONFIG(oldtype); break; case PERMaddinjectionpostinghost: if (boolval != -1) curaccess->addinjectionpostinghost = boolval; SET_CONFIG(oldtype); break; case PERMnnrpdposthost: if (curaccess->nnrpdposthost) free(curaccess->nnrpdposthost); curaccess->nnrpdposthost = xstrdup(tok->name); SET_CONFIG(oldtype); break; case PERMnnrpdpostport: curaccess->nnrpdpostport = strtoul(tok->name, NULL, 10); SET_CONFIG(oldtype); break; case PERMnnrpdoverstats: if (boolval != -1) curaccess->nnrpdoverstats = boolval; SET_CONFIG(oldtype); break; case PERMbackoff_auth: if (boolval != -1) curaccess->backoff_auth = boolval; SET_CONFIG(oldtype); break; case PERMbackoff_db: if (curaccess->backoff_db) free(curaccess->backoff_db); curaccess->backoff_db = xstrdup(tok->name); SET_CONFIG(oldtype); break; case PERMbackoff_k: curaccess->backoff_k = strtoul(tok->name, NULL, 10); SET_CONFIG(oldtype); break; case PERMbackoff_postfast: curaccess->backoff_postfast = strtoul(tok->name, NULL, 10); SET_CONFIG(oldtype); break; case PERMbackoff_postslow: curaccess->backoff_postslow = strtoul(tok->name, NULL, 10); SET_CONFIG(oldtype); break; case PERMbackoff_trigger: curaccess->backoff_trigger = strtoul(tok->name, NULL, 10); SET_CONFIG(oldtype); break; case PERMnnrpdcheckart: if (boolval != -1) curaccess->nnrpdcheckart = boolval; SET_CONFIG(oldtype); break; case PERMnnrpdauthsender: if (boolval != -1) curaccess->nnrpdauthsender = boolval; SET_CONFIG(oldtype); break; case PERMvirtualhost: if (boolval != -1) curaccess->virtualhost = boolval; SET_CONFIG(oldtype); break; case PERMnewsmaster: if (curaccess->newsmaster) free(curaccess->newsmaster); curaccess->newsmaster = xstrdup(tok->name); SET_CONFIG(oldtype); break; case PERMmaxbytespersecond: curaccess->maxbytespersecond = atol(tok->name); SET_CONFIG(oldtype); break; default: snprintf(buff, sizeof(buff), "Unexpected token '%s'.", tok->name); ReportError(f, buff); break; } } static void PERMvectortoaccess(ACCESSGROUP *acc, const char *name, struct vector *access_vec) { CONFTOKEN *tok = NULL; CONFFILE *file; char *str; unsigned int i; file = xcalloc(1, sizeof(CONFFILE)); file->array = access_vec->strings; file->array_len = access_vec->count; memset(ConfigBit, '\0', ConfigBitsize); SetDefaultAccess(acc); str = xstrdup(name); acc->name = str; file->filename = str; for (i = 0; i <= access_vec->count; i++) { tok = CONFgettoken(PERMtoks, file); if (tok != NULL) { accessdecl_parse(acc, file, tok); } } /* No need to free str here; it is used by acc, and will be * free'd when the access block is free'd. */ free(file); return; } static void PERMreadfile(char *filename) { CONFCHAIN *cf = NULL, *hold = NULL; CONFTOKEN *tok = NULL; int inwhat; GROUP *curgroup = NULL, *newgroup = NULL; ACCESSGROUP *curaccess = NULL; AUTHGROUP *curauth = NULL; int oldtype; char *str = NULL; char *path = NULL; char buff[SMBUF]; if(filename != NULL) { syslog(L_TRACE, "Reading access from %s", filename == NULL ? "(NULL)" : filename); } cf = xmalloc(sizeof(CONFCHAIN)); if ((cf->f = CONFfopen(filename)) == NULL) { syslog(L_ERROR, "%s cannot open %s: %m", Client.host, filename); Reply("%d NNTP server unavailable. Try later!\r\n", NNTP_FAIL_TERMINATING); ExitWithStats(1, true); } cf->parent = 0; /* Are we editing an auth or access group? */ inwhat = 0; curgroup = 0; tok = CONFgettoken(PERMtoks, cf->f); while (tok != NULL) { if (inwhat == 0) { /* Top-level parser. */ switch (tok->type) { /* Include a child file. */ case PERMinclude: tok = CONFgettoken(0, cf->f); if (tok == NULL) { ReportError(cf->f, "Expected filename after 'include'."); } hold = xmalloc(sizeof(CONFCHAIN)); hold->parent = cf; /* Unless the filename's path is fully qualified, open it * relative to . */ path = concatpath(innconf->pathetc, tok->name); hold->f = CONFfopen(path); free(path); if (hold->f == NULL) { ReportError(cf->f, "Could not open 'include' filename."); } cf = hold; goto again; break; /* Nested group declaration. */ case PERMgroup: tok = CONFgettoken(PERMtoks, cf->f); if (tok == NULL) { ReportError(cf->f, "Unexpected EOF at group name."); } newgroup = xmalloc(sizeof(GROUP)); newgroup->above = curgroup; newgroup->name = xstrdup(tok->name); memset(ConfigBit, '\0', ConfigBitsize); tok = CONFgettoken(PERMtoks, cf->f); if (tok == NULL || tok->type != PERMlbrace) { ReportError(cf->f, "Expected '{' after group name."); } /* Nested group declaration. */ if (curgroup) { newgroup->auth = copy_authgroup(curgroup->auth); newgroup->access = copy_accessgroup(curgroup->access); } else { newgroup->auth = 0; newgroup->access = 0; } curgroup = newgroup; break; /* Beginning of an auth or access group declaration. */ case PERMauth: case PERMaccess: oldtype = tok->type; if ((tok = CONFgettoken(PERMtoks, cf->f)) == NULL) { ReportError(cf->f, "Expected identifier."); } str = xstrdup(tok->name); tok = CONFgettoken(PERMtoks, cf->f); if (tok == NULL || tok->type != PERMlbrace) { ReportError(cf->f, "Expected '{'."); } switch (oldtype) { case PERMauth: if (curgroup && curgroup->auth) { curauth = copy_authgroup(curgroup->auth); } else { curauth = xcalloc(1, sizeof(AUTHGROUP)); memset(ConfigBit, '\0', ConfigBitsize); SetDefaultAuth(curauth); } curauth->name = str; inwhat = 1; break; case PERMaccess: if (curgroup && curgroup->access) curaccess = copy_accessgroup(curgroup->access); else { curaccess = xcalloc(1, sizeof(ACCESSGROUP)); memset(ConfigBit, '\0', ConfigBitsize); SetDefaultAccess(curaccess); } curaccess->name = str; inwhat = 2; break; } break; /* End of a group declaration. */ case PERMrbrace: if (curgroup == NULL) { ReportError(cf->f, "Unmatched '}'."); } newgroup = curgroup; curgroup = curgroup->above; if (newgroup->auth) free_authgroup(newgroup->auth); if (newgroup->access) free_accessgroup(newgroup->access); free(newgroup->name); free(newgroup); break; /* Stuff that belongs to an auth group. */ case PERMhost: #ifdef HAVE_OPENSSL case PERMrequire_ssl: #endif case PERMauthprog: case PERMresprog: case PERMdefuser: case PERMdefdomain: if (curgroup == NULL) { curgroup = xcalloc(1, sizeof(GROUP)); memset(ConfigBit, '\0', ConfigBitsize); } if (curgroup->auth == NULL) { curgroup->auth = xcalloc(1, sizeof(AUTHGROUP)); memset(ConfigBit, '\0', ConfigBitsize); SetDefaultAuth(curgroup->auth); } authdecl_parse(curgroup->auth, cf->f, tok); break; /* Stuff that belongs to an access group. */ case PERMusers: case PERMrejectwith: case PERMnewsgroups: case PERMread: case PERMpost: case PERMaccessrp: case PERMlocaltime: case PERMstrippath: case PERMnnrpdperlfilter: case PERMnnrpdpythonfilter: case PERMfromhost: case PERMpathhost: case PERMorganization: case PERMmoderatormailer: case PERMdomain: case PERMcomplaints: case PERMspoolfirst: case PERMcheckincludedtext: case PERMclienttimeout: case PERMlocalmaxartsize: case PERMreadertrack: case PERMstrippostcc: case PERMaddinjectiondate: case PERMaddinjectionpostingaccount: case PERMaddinjectionpostinghost: case PERMnnrpdposthost: case PERMnnrpdpostport: case PERMnnrpdoverstats: case PERMbackoff_auth: case PERMbackoff_db: case PERMbackoff_k: case PERMbackoff_postfast: case PERMbackoff_postslow: case PERMbackoff_trigger: case PERMnnrpdcheckart: case PERMnnrpdauthsender: case PERMvirtualhost: case PERMnewsmaster: if (!curgroup) { curgroup = xcalloc(1, sizeof(GROUP)); memset(ConfigBit, '\0', ConfigBitsize); } if (!curgroup->access) { curgroup->access = xcalloc(1, sizeof(ACCESSGROUP)); memset(ConfigBit, '\0', ConfigBitsize); SetDefaultAccess(curgroup->access); } accessdecl_parse(curgroup->access, cf->f, tok); break; default: snprintf(buff, sizeof(buff), "Unexpected token: %s", tok->name); ReportError(cf->f, buff); break; } } else if (inwhat == 1) { /* Auth group parser. */ if (tok->type == PERMrbrace) { inwhat = 0; if (curauth->name && MatchHost(curauth->hosts, Client.host, Client.ip)) { if (!MatchHost(curauth->localaddress, Client.serverhost, Client.serverip)) { syslog(L_TRACE, "Auth strategy '%s' does not match localhost. Removing.", curauth->name == NULL ? "(NULL)" : curauth->name); free_authgroup(curauth); } else add_authgroup(curauth); } else { syslog(L_TRACE, "Auth strategy '%s' does not match client. Removing.", curauth->name == NULL ? "(NULL)" : curauth->name); free_authgroup(curauth); } curauth = NULL; goto again; } authdecl_parse(curauth, cf->f, tok); } else if (inwhat == 2) { /* Access group parser. */ if (tok->type == PERMrbrace) { inwhat = 0; if (curaccess->name) add_accessgroup(curaccess); else free_accessgroup(curaccess); curaccess = NULL; goto again; } accessdecl_parse(curaccess, cf->f, tok); } else { /* Should never happen. */ syslog(L_TRACE, "SHOULD NEVER HAPPEN!"); } again: /* Go back up the 'include' chain. */ tok = CONFgettoken(PERMtoks, cf->f); while (tok == NULL && cf) { hold = cf; cf = hold->parent; CONFfclose(hold->f); free(hold); if (cf) { tok = CONFgettoken(PERMtoks, cf->f); } } } return; } void PERMgetaccess(char *nnrpaccess) { int i; char *uname; auth_realms = NULL; access_realms = NULL; success_auth = NULL; #ifdef HAVE_SASL PERMcanauthenticate = true; #else PERMcanauthenticate = false; #endif #ifdef HAVE_OPENSSL PERMcanauthenticatewithoutSSL = false; #endif PERMgroupmadeinvalid = false; PERMcanpostgreeting = false; PERMcanread = PERMcanpost = false; PERMreadlist = PERMpostlist = false; PERMaccessconf = NULL; if (ConfigBit == NULL) { if (PERMMAX % 8 == 0) ConfigBitsize = PERMMAX/8; else ConfigBitsize = (PERMMAX - (PERMMAX % 8))/8 + 1; ConfigBit = xcalloc(ConfigBitsize, 1); } PERMreadfile(nnrpaccess); strip_accessgroups(); if (auth_realms == NULL) { /* No one can talk, empty file. */ syslog(L_NOTICE, "%s no_permission", Client.host); Reply("%d You have no permission to talk. Goodbye!\r\n", NNTP_ERR_ACCESS); ExitWithStats(1, true); } /* auth_realms are all expected to match the user. * Be careful whether SSL is required, though. */ for (i = 0; auth_realms[i]; i++) { if (auth_realms[i]->auth_methods != NULL) { PERMcanauthenticate = true; #ifdef HAVE_OPENSSL if (auth_realms[i]->require_ssl == false) PERMcanauthenticatewithoutSSL = true; #endif } /* We assume that an access or dynamic script will allow * the user to post when authenticated, so that a 200 greeting * code can be sent. */ if (auth_realms[i]->access_script != NULL || auth_realms[i]->dynamic_script != NULL) PERMcanpostgreeting = true; } uname = 0; while (!uname && i--) { #ifdef HAVE_OPENSSL /* If SSL is required, check that the connection is encrypted. */ if ((auth_realms[i]->require_ssl == true) && !nnrpd_starttls_done) continue; #endif if ((uname = ResolveUser(auth_realms[i])) != NULL) PERMauthorized = true; if (!uname && auth_realms[i]->default_user) uname = xstrdup(auth_realms[i]->default_user); } if (uname) { strlcpy(PERMuser, uname, sizeof(PERMuser)); free(uname); uname = strchr(PERMuser, '@'); if (!uname && auth_realms[i]->default_domain) { /* Append the default domain to the username. */ strlcat(PERMuser, "@", sizeof(PERMuser)); strlcat(PERMuser, auth_realms[i]->default_domain, sizeof(PERMuser)); } PERMneedauth = false; success_auth = auth_realms[i]; syslog(L_TRACE, "%s res %s", Client.host, PERMuser); } else if (!PERMcanauthenticate) { /* Couldn't resolve the user. */ syslog(L_NOTICE, "%s no_user", Client.host); Reply("%d Could not get your access name. Goodbye!\r\n", NNTP_ERR_ACCESS); ExitWithStats(1, true); } else { PERMneedauth = true; } /* Check maximum allowed permissions for any host that matches (for * the greeting string). */ for (i = 0; access_realms[i]; i++) { if (!PERMcanread) PERMcanread = (access_realms[i]->read != NULL); if (!PERMcanpost) PERMcanpost = (access_realms[i]->post != NULL); if (!PERMcanpostgreeting) PERMcanpostgreeting = (access_realms[i]->post != NULL); } if (!i) { /* No applicable access groups. Zeroing all these makes INN * return permission denied to client. */ PERMcanread = PERMcanpost = PERMneedauth = false; } } void PERMlogin(char *uname, char *pass, int *code, char *errorstr) { int i = 0; char *runame; if (ConfigBit == NULL) { if (PERMMAX % 8 == 0) ConfigBitsize = PERMMAX/8; else ConfigBitsize = (PERMMAX - (PERMMAX % 8))/8 + 1; ConfigBit = xcalloc(ConfigBitsize, 1); } /* The check in CMDauthinfo uses the value of PERMneedauth to know if * authentication succeeded or not. By default, authentication doesn't * succeed. */ PERMneedauth = true; if(auth_realms != NULL) { for (i = 0; auth_realms[i]; i++) { ; } } runame = NULL; while (runame == NULL && i--) runame = AuthenticateUser(auth_realms[i], uname, pass, code, errorstr); if (runame) { strlcpy(PERMuser, runame, sizeof(PERMuser)); free(runame); uname = strchr(PERMuser, '@'); if (!uname && auth_realms[i]->default_domain) { /* Append the default domain to the username. */ strlcat(PERMuser, "@", sizeof(PERMuser)); strlcat(PERMuser, auth_realms[i]->default_domain, sizeof(PERMuser)); } PERMneedauth = false; PERMauthorized = true; PERMcanauthenticate = false; success_auth = auth_realms[i]; } } static int MatchUser(char *pat, char *user) { char *cp, **list; char *userlist[2]; int ret; if (!pat) return(1); if (!user || !*user) return(0); cp = xstrdup(pat); list = 0; NGgetlist(&list, cp); userlist[0] = user; userlist[1] = 0; ret = PERMmatch(list, userlist); free(cp); free(list[0]); free(list); return(ret); } void PERMgetpermissions(void) { int i; char *cp, **list; char *user[2]; static ACCESSGROUP *noaccessconf; if (ConfigBit == NULL) { if (PERMMAX % 8 == 0) ConfigBitsize = PERMMAX/8; else ConfigBitsize = (PERMMAX - (PERMMAX % 8))/8 + 1; ConfigBit = xcalloc(ConfigBitsize, 1); } if (!success_auth) { /* If we haven't successfully authenticated, we can't do anything. */ syslog(L_TRACE, "%s no_success_auth", Client.host); if (!noaccessconf) noaccessconf = xmalloc(sizeof(ACCESSGROUP)); PERMaccessconf = noaccessconf; SetDefaultAccess(PERMaccessconf); return; #ifdef DO_PERL } else if ((success_auth->access_script != NULL) && (success_auth->access_type == PERMperl_access)) { char *uname; char *cpp, *script_path; char **args; struct vector *access_vec; i = 0; cpp = xstrdup(success_auth->access_script); args = 0; Argify(cpp, &args); script_path = concat(args[0], (char *) 0); if ((script_path != NULL) && (strlen(script_path) > 0)) { if(!PerlLoaded) { loadPerl(); } PERLsetup(NULL, script_path, "access"); free(script_path); uname = xstrdup(PERMuser); access_vec = vector_new(); perlAccess(uname, access_vec); free(uname); access_realms[0] = xcalloc(1, sizeof(ACCESSGROUP)); PERMvectortoaccess(access_realms[0], "perl_access-block", access_vec); vector_free(access_vec); } else { syslog(L_ERROR, "No script specified in perl_access method.\n"); Reply("%d NNTP server unavailable. Try later!\r\n", NNTP_FAIL_TERMINATING); ExitWithStats(1, true); } free(cpp); free(args); #endif /* DO_PERL */ #ifdef DO_PYTHON } else if ((success_auth->access_script != NULL) && (success_auth->access_type == PERMpython_access)) { char *uname; char *cpp, *script_path; char **args; struct vector *access_vec; i = 0; cpp = xstrdup(success_auth->access_script); args = 0; Argify(cpp, &args); script_path = concat(args[0], (char *) 0); if ((script_path != NULL) && (strlen(script_path) > 0)) { uname = xstrdup(PERMuser); access_vec = vector_new(); PY_access(script_path, access_vec, uname); free(script_path); free(uname); free(args); access_realms[0] = xcalloc(1, sizeof(ACCESSGROUP)); memset(access_realms[0], 0, sizeof(ACCESSGROUP)); PERMvectortoaccess(access_realms[0], "python_access-block", access_vec); vector_free(access_vec); } else { syslog(L_ERROR, "No script specified in python_access method.\n"); Reply("%d NNTP server unavailable. Try later!\r\n", NNTP_FAIL_TERMINATING); ExitWithStats(1, true); } free(cpp); #endif /* DO_PYTHON */ } else { for (i = 0; access_realms[i]; i++) ; user[0] = PERMuser; user[1] = 0; while (i--) { if ((!success_auth->key && !access_realms[i]->key) || (access_realms[i]->key && success_auth->key && strcmp(access_realms[i]->key, success_auth->key) == 0)) { if (!access_realms[i]->users) break; else if (!*PERMuser) continue; cp = xstrdup(access_realms[i]->users); list = 0; NGgetlist(&list, cp); if (PERMmatch(list, user)) { syslog(L_TRACE, "%s match_user %s %s", Client.host, PERMuser, access_realms[i]->users); free(cp); free(list[0]); free(list); break; } else syslog(L_TRACE, "%s no_match_user %s %s", Client.host, PERMuser, access_realms[i]->users); free(cp); free(list[0]); free(list); } } } if (i >= 0) { /* Found the right access group. */ if (access_realms[i]->rejectwith) { syslog(L_NOTICE, "%s rejected by rule (%s)", Client.host, access_realms[i]->rejectwith); Reply("%d Permission denied: %s\r\n", NNTP_FAIL_TERMINATING, access_realms[i]->rejectwith); ExitWithStats(1, true); } if (access_realms[i]->read) { cp = xstrdup(access_realms[i]->read); PERMspecified = NGgetlist(&PERMreadlist, cp); free(cp); PERMcanread = true; } else { syslog(L_TRACE, "%s no_read %s", Client.host, access_realms[i]->name); PERMcanread = false; } if (access_realms[i]->post) { cp = xstrdup(access_realms[i]->post); NGgetlist(&PERMpostlist, cp); free(cp); PERMcanpost = true; } else { syslog(L_TRACE, "%s no_post %s", Client.host, access_realms[i]->name); PERMcanpost = false; } PERMaccessconf = access_realms[i]; MaxBytesPerSecond = PERMaccessconf->maxbytespersecond; if (PERMaccessconf->virtualhost) { if (PERMaccessconf->domain == NULL) { syslog(L_ERROR, "%s virtualhost needs domain parameter (%s).", Client.host, PERMaccessconf->name); Reply("%d NNTP server unavailable. Try later!\r\n", NNTP_FAIL_TERMINATING); ExitWithStats(1, true); } if (VirtualPath) free(VirtualPath); if (strcasecmp(innconf->pathhost, PERMaccessconf->pathhost) == 0) { /* Use domain, if pathhost in access realm matches one in * inn.conf to differentiate virtual host. */ if (innconf->domain != NULL && strcasecmp(innconf->domain, PERMaccessconf->domain) == 0) { syslog(L_ERROR, "%s domain parameter (%s) in readers.conf must be different from the one in inn.conf.", Client.host, PERMaccessconf->name); Reply("%d NNTP server unavailable. Try later!\r\n", NNTP_FAIL_TERMINATING); ExitWithStats(1, true); } VirtualPath = concat(PERMaccessconf->domain, "!", (char *) 0); } else { VirtualPath = concat(PERMaccessconf->pathhost, "!", (char *) 0); } VirtualPathlen = strlen(VirtualPath); } else VirtualPathlen = 0; } else { if (!noaccessconf) noaccessconf = xmalloc(sizeof(ACCESSGROUP)); PERMaccessconf = noaccessconf; SetDefaultAccess(PERMaccessconf); syslog(L_TRACE, "%s no_access_realm", Client.host); } /* Check if dynamic access control is enabled; if so, init it. */ #ifdef DO_PYTHON if ((success_auth->dynamic_type == PERMpython_dynamic) && success_auth->dynamic_script) { PY_dynamic_init(success_auth->dynamic_script); } #endif /* DO_PYTHON */ } /* Strip blanks out of a string. */ static void CompressList(char *list) { char *cpto; bool inword = false; for (cpto = list; *list; ) { if (strchr("\n \t,", *list) != NULL) { list++; if(inword) { *cpto++ = ','; inword = false; } } else { *cpto++ = *list++; inword = true; } } *cpto = '\0'; } static bool MatchHost(char *hostlist, char *host, char *ip) { int i; char *cp, *pat, *mask; char **list = NULL; bool match = false; /* If no hostlist are specified, by default they match. */ if (hostlist == NULL) return true; cp = xstrdup(hostlist); NGgetlist(&list, cp); /* Start searching from the end of the list. The default is no access. */ for (i = 0; list[i] != NULL; i++) ; while (!match && i-- > 0) { pat = list[i]; match = uwildmat(host, pat); if (!match && ip != NULL && *ip != '\0') { match = uwildmat(ip, pat); if (match) break; mask = strchr(pat, '/'); if (mask == NULL) continue; *mask = '\0'; mask++; match = network_addr_match(ip, pat, mask); } } free(list[0]); free(list); free(cp); return match; } static void add_authgroup(AUTHGROUP *group) { int i; if (auth_realms == NULL) { i = 0; auth_realms = xmalloc(2 * sizeof(AUTHGROUP *)); } else { for (i = 0; auth_realms[i]; i++) ; auth_realms = xrealloc(auth_realms, (i + 2) * sizeof(AUTHGROUP *)); } auth_realms[i] = group; auth_realms[i+1] = 0; } static void add_accessgroup(ACCESSGROUP *group) { int i; if (access_realms == NULL) { i = 0; access_realms = xmalloc(2 * sizeof(ACCESSGROUP *)); } else { for (i = 0; access_realms[i]; i++) ; access_realms = xrealloc(access_realms, (i + 2) * sizeof(ACCESSGROUP *)); } access_realms[i] = group; access_realms[i+1] = 0; } /* ** Clean out access groups that don't apply to any of our auth groups. */ static void strip_accessgroups(void) { int i, j; /* Flag the access group as used or not. */ if(access_realms != NULL) { for (j = 0; access_realms[j] != NULL; j++) { access_realms[j]->used = 0; } } else { syslog(L_TRACE, "No access realms to check!"); return; } /* If there are auth realms to check... */ if(auth_realms != NULL) { /* ... Then for each auth realm... */ for (i = 0; auth_realms[i] != NULL; i++) { /* ... for each access realm... */ for (j = 0; access_realms[j] != NULL; j++) { /* If the access realm isn't already in use... */ if (! access_realms[j]->used) { /* Check to see if both the access_realm key and * auth_realm key are NULL... */ if (!access_realms[j]->key && !auth_realms[i]->key) { /* If so, mark the realm in use and continue on... */ access_realms[j]->used = 1; } else { /* If not, check to see if both the access_realm and auth_realm are NOT _both_ NULL, and see if they are equal... */ if (access_realms[j]->key && auth_realms[i]->key && strcmp(access_realms[j]->key, auth_realms[i]->key) == 0) { /* And if so, mark the realm in use. */ access_realms[j]->used = 1; } } } } } } else { syslog(L_TRACE, "No auth realms to check!"); } /* Strip out unused access groups. */ i = j = 0; while (access_realms[i] != NULL) { if (access_realms[i]->used) access_realms[j++] = access_realms[i]; else syslog(L_TRACE, "%s removing irrelevant access group %s", Client.host, access_realms[i]->name); i++; } access_realms[j] = 0; } /* ** Execute a series of resolvers to get the remote username. If one is ** found, return it in newly allocated space; otherwise, return NULL. */ static char * ResolveUser(AUTHGROUP *auth) { int i, j; char *command; char *user = NULL; char *resdir; char *tmp; if (auth->res_methods == NULL) return NULL; #ifdef HAVE_OPENSSL /* If SSL is required, check that the connection is encrypted. */ if ((auth->require_ssl == true) && !nnrpd_starttls_done) return NULL; #endif tmp = concatpath(innconf->pathbin, INN_PATH_AUTHDIR); resdir = concatpath(tmp, INN_PATH_AUTHDIR_NOPASS); free(tmp); for (i = 0; auth->res_methods[i]; i++) { command = auth->res_methods[i]->program; syslog(L_TRACE, "%s res starting resolver %s", Client.host, command); if (auth->res_methods[i]->extra_logs) { for (j = 0; auth->res_methods[i]->extra_logs[j]; j++) syslog(L_NOTICE, "%s res also-log: %s", Client.host, auth->res_methods[i]->extra_logs[j]); } user = auth_external(&Client, command, resdir, NULL, NULL); if (user == NULL) syslog(L_TRACE, "%s res resolver failed", Client.host); else { syslog(L_TRACE, "%s res resolver successful, user %s", Client.host, user); break; } } free(resdir); return user; } /* ** Execute a series of authenticators to get the remote username. If one is ** found, return it in newly allocated space; otherwise, return NULL. Also ** handles running the Perl and Python authentication functions. */ static char * AuthenticateUser(AUTHGROUP *auth, char *username, char *password, int *code UNUSED, char *errorstr UNUSED) { int i, j; char *command; char *user = NULL; char *resdir; char *tmp; if (auth->auth_methods == NULL) return NULL; #ifdef HAVE_OPENSSL /* If SSL is required, check that the connection is encrypted. */ if ((auth->require_ssl == true) && !nnrpd_starttls_done) return NULL; #endif tmp = concatpath(innconf->pathbin, INN_PATH_AUTHDIR); resdir = concatpath(tmp, INN_PATH_AUTHDIR_PASSWD); free(tmp); for (i = 0; auth->auth_methods[i]; i++) { if (auth->auth_methods[i]->type == PERMperl_auth) { #ifdef DO_PERL char *script_path, *cp; char **args; char newUser[BIG_BUFFER]; cp = xstrdup(auth->auth_methods[i]->program); args = 0; Argify(cp, &args); script_path = concat(args[0], (char *) 0); if ((script_path != NULL) && (strlen(script_path) > 0)) { if (!PerlLoaded) loadPerl(); PERLsetup(NULL, script_path, "authenticate"); free(script_path); perlAuthInit(); newUser[0] = '\0'; perlAuthenticate(username, password, code, errorstr, newUser); if (*code == NNTP_OK_AUTHINFO) { if (newUser[0] != '\0') user = xstrdup(newUser); else user = xstrdup(username); syslog(L_NOTICE, "%s user %s", Client.host, user); if (LLOGenable) { fprintf(locallog, "%s user %s\n", Client.host, user); fflush(locallog); } break; } else { syslog(L_NOTICE, "%s bad_auth", Client.host); } } else { syslog(L_ERROR, "no script specified in auth method"); } #endif /* DO_PERL */ } else if (auth->auth_methods[i]->type == PERMpython_auth) { #ifdef DO_PYTHON char *script_path, *cp; char **args; char newUser[BIG_BUFFER]; cp = xstrdup(auth->auth_methods[i]->program); args = 0; Argify(cp, &args); script_path = concat(args[0], (char *) 0); if ((script_path != NULL) && (strlen(script_path) > 0)) { newUser[0] = '\0'; PY_authenticate(script_path, username, password, code, errorstr, newUser); free(script_path); if (*code == NNTP_OK_AUTHINFO) { if (newUser[0] != '\0') user = xstrdup(newUser); else user = xstrdup(username); syslog(L_NOTICE, "%s user %s", Client.host, user); if (LLOGenable) { fprintf(locallog, "%s user %s\n", Client.host, user); fflush(locallog); } break; } else if (*code < 0) { syslog(L_NOTICE, "PY_authenticate(): authentication skipped due to no Python authentication method defined."); } else { syslog(L_NOTICE, "%s bad_auth", Client.host); } } else { syslog(L_ERROR, "no script specified in auth method"); } #endif /* DO_PYTHON */ } else { if (auth->auth_methods[i]->users && !MatchUser(auth->auth_methods[i]->users, username)) continue; command = auth->auth_methods[i]->program; syslog(L_TRACE, "%s res starting authenticator %s", Client.host, command); if (auth->auth_methods[i]->extra_logs) { for (j = 0; auth->auth_methods[i]->extra_logs[j]; j++) syslog(L_NOTICE, "%s auth also-log: %s", Client.host, auth->auth_methods[i]->extra_logs[j]); } user = auth_external(&Client, command, resdir, username, password); if (user == NULL) syslog(L_TRACE, "%s auth authenticator failed", Client.host); else { syslog(L_TRACE, "%s auth authenticator successful, user %s", Client.host, user); break; } } } free(resdir); return user; } inn-2.6.0/nnrpd/tls.c0000644000175200017520000005750212575023702014020 0ustar iuliusiulius/* $Id: tls.c 9841 2015-05-02 16:27:37Z iulius $ ** ** tls.c -- TLSv1 functions. ** Copyright (C) 2000 Kenichi Okada . ** ** Author: Kenichi Okada ** Created: 2000-02-22 ** ** [RFC 2246] "The TLS Protocol Version 1.0" ** by Christopher Allen and ** Tim Dierks (1999/01) ** ** [RFC 2595] "Using TLS with IMAP, POP3 and ACAP" ** by Chris Newman (1999/06) */ #include "config.h" #include "clibrary.h" #include #include #include #include "nnrpd.h" #include "inn/innconf.h" /* Outside the ifdef so that make depend works even ifndef HAVE_OPENSSL. */ #include "tls.h" #ifdef HAVE_OPENSSL /* We must keep some of the info available. */ static bool tls_initialized = false; static int verify_depth; static int verify_error = X509_V_OK; static int do_dump = 0; static SSL_CTX *CTX = NULL; SSL *tls_conn = NULL; #define CCERT_BUFSIZ 256 int tls_serverengine = 0; int tls_serveractive = 0; /* Available or not. */ char *tls_peer_subject = NULL; char *tls_peer_issuer = NULL; char *tls_peer_fingerprint = NULL; int tls_clientactive = 0; /* Available or not. */ char *tls_peer_CN= NULL; char *tls_issuer_CN = NULL; const char *tls_protocol = NULL; const char *tls_cipher_name = NULL; int tls_cipher_usebits = 0; int tls_cipher_algbits = 0; int tls_loglevel = 0; /* ** Taken from OpenSSL apps/s_cb.c. ** Tim -- this seems to just be giving logging messages. */ static void apps_ssl_info_callback(const SSL *s, int where, int ret) { const char *str; int w; if (tls_loglevel==0) return; w = where & ~SSL_ST_MASK; if (w & SSL_ST_CONNECT) str = "SSL_connect"; else if (w & SSL_ST_ACCEPT) str = "SSL_accept"; else str = "undefined"; if (where & SSL_CB_LOOP) { if (tls_serverengine && (tls_loglevel >= 2)) Printf("%s:%s", str, SSL_state_string_long(s)); } else if (where & SSL_CB_ALERT) { str = (where & SSL_CB_READ) ? "read" : "write"; if ((tls_serverengine && (tls_loglevel >= 2)) || ((ret & 0xff) != SSL3_AD_CLOSE_NOTIFY)) Printf("SSL3 alert %s:%s:%s", str, SSL_alert_type_string_long(ret), SSL_alert_desc_string_long(ret)); } else if (where & SSL_CB_EXIT) { if (ret == 0) Printf("%s:failed in %s", str, SSL_state_string_long(s)); else if (ret < 0) { Printf("%s:error in %s", str, SSL_state_string_long(s)); } } } /* ** Hardcoded DH parameter files, from OpenSSL. ** For information on how these files were generated, see ** "Assigned Number for SKIP Protocols" ** . */ static const char file_dh512[] = "-----BEGIN DH PARAMETERS-----\n\ MEYCQQD1Kv884bEpQBgRjXyEpwpy1obEAxnIByl6ypUM2Zafq9AKUJsCRtMIPWak\n\ XUGfnHy9iUsiGSa6q6Jew1XpKgVfAgEC\n\ -----END DH PARAMETERS-----\n"; static const char file_dh1024[] = "-----BEGIN DH PARAMETERS-----\n\ MIGHAoGBAPSI/VhOSdvNILSd5JEHNmszbDgNRR0PfIizHHxbLY7288kjwEPwpVsY\n\ jY67VYy4XTjTNP18F1dDox0YbN4zISy1Kv884bEpQBgRjXyEpwpy1obEAxnIByl6\n\ ypUM2Zafq9AKUJsCRtMIPWakXUGfnHy9iUsiGSa6q6Jew1XpL3jHAgEC\n\ -----END DH PARAMETERS-----\n"; static const char file_dh2048[] = "-----BEGIN DH PARAMETERS-----\n\ MIIBCAKCAQEA9kJXtwh/CBdyorrWqULzBej5UxE5T7bxbrlLOCDaAadWoxTpj0BV\n\ 89AHxstDqZSt90xkhkn4DIO9ZekX1KHTUPj1WV/cdlJPPT2N286Z4VeSWc39uK50\n\ T8X8dryDxUcwYc58yWb/Ffm7/ZFexwGq01uejaClcjrUGvC/RgBYK+X0iP1YTknb\n\ zSC0neSRBzZrM2w4DUUdD3yIsxx8Wy2O9vPJI8BD8KVbGI2Ou1WMuF040zT9fBdX\n\ Q6MdGGzeMyEstSr/POGxKUAYEY18hKcKctaGxAMZyAcpesqVDNmWn6vQClCbAkbT\n\ CD1mpF1Bn5x8vYlLIhkmuquiXsNV6TILOwIBAg==\n\ -----END DH PARAMETERS-----\n"; static const char file_dh4096[] = "-----BEGIN DH PARAMETERS-----\n\ MIICCAKCAgEA+hRyUsFN4VpJ1O8JLcCo/VWr19k3BCgJ4uk+d+KhehjdRqNDNyOQ\n\ l/MOyQNQfWXPeGKmOmIig6Ev/nm6Nf9Z2B1h3R4hExf+zTiHnvVPeRBhjdQi81rt\n\ Xeoh6TNrSBIKIHfUJWBh3va0TxxjQIs6IZOLeVNRLMqzeylWqMf49HsIXqbcokUS\n\ Vt1BkvLdW48j8PPv5DsKRN3tloTxqDJGo9tKvj1Fuk74A+Xda1kNhB7KFlqMyN98\n\ VETEJ6c7KpfOo30mnK30wqw3S8OtaIR/maYX72tGOno2ehFDkq3pnPtEbD2CScxc\n\ alJC+EL7RPk5c/tgeTvCngvc1KZn92Y//EI7G9tPZtylj2b56sHtMftIoYJ9+ODM\n\ sccD5Piz/rejE3Ome8EOOceUSCYAhXn8b3qvxVI1ddd1pED6FHRhFvLrZxFvBEM9\n\ ERRMp5QqOaHJkM+Dxv8Cj6MqrCbfC4u+ZErxodzuusgDgvZiLF22uxMZbobFWyte\n\ OvOzKGtwcTqO/1wV5gKkzu1ZVswVUQd5Gg8lJicwqRWyyNRczDDoG9jVDxmogKTH\n\ AaqLulO7R8Ifa1SwF2DteSGVtgWEN8gDpN3RBmmPTDngyF2DHb5qmpnznwtFKdTL\n\ KWbuHn491xNO25CQWMtem80uKw+pTnisBRF/454n1Jnhub144YRBoN8CAQI=\n\ -----END DH PARAMETERS-----\n"; /* ** Load hardcoded DH parameters. */ static DH * load_dh_buffer (const char *buffer, size_t len) { BIO *bio; DH *dh = NULL; bio = BIO_new_mem_buf((char *) buffer, len); if (bio == NULL) return NULL; dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL); /* If (dh == NULL) log error? */ BIO_free(bio); return dh; } /* ** Generate empheral DH key. Because this can take a long ** time to compute, we use precomputed parameters of the ** common key sizes. ** ** These values can be static (once loaded or computed) since ** the OpenSSL library can effectively generate random keys ** from the information provided. ** ** EDH keying is slightly less efficient than static RSA keying, ** but it offers Perfect Forward Secrecy (PFS). ** ** FIXME: support user-specified files, to eliminate risk of ** "small group" attacks. */ static DH * tmp_dh_cb(SSL *s UNUSED, int export UNUSED, int keylength) { DH *r = NULL; static DH *dh = NULL; static DH *dh512 = NULL; static DH *dh1024 = NULL; static DH *dh2048 = NULL; static DH *dh4096 = NULL; switch (keylength) { case 512: if (dh512 == NULL) dh512 = load_dh_buffer(file_dh512, sizeof file_dh512); r = dh512; break; case 1024: if (dh1024 == NULL) dh1024 = load_dh_buffer(file_dh1024, sizeof file_dh1024); r = dh1024; break; case 2048: if (dh2048 == NULL) dh2048 = load_dh_buffer(file_dh2048, sizeof file_dh2048); r = dh2048; break; case 4096: if (dh4096 == NULL) dh4096 = load_dh_buffer(file_dh4096, sizeof file_dh4096); r = dh4096; break; default: /* We should check current keylength vs. requested keylength * also, this is an extremely expensive operation! */ dh = DH_generate_parameters(keylength, DH_GENERATOR_2, NULL, NULL); r = dh; } return r; } /* ** Taken from OpenSSL apps/s_cb.c. */ static int verify_callback(int ok, X509_STORE_CTX * ctx) { char buf[256]; X509 *err_cert; int err; int depth; syslog(L_NOTICE,"Doing a peer verify"); err_cert = X509_STORE_CTX_get_current_cert(ctx); err = X509_STORE_CTX_get_error(ctx); depth = X509_STORE_CTX_get_error_depth(ctx); X509_NAME_oneline(X509_get_subject_name(err_cert), buf, sizeof(buf)); if ((tls_serveractive) && (tls_loglevel >= 1)) Printf("Peer cert verify depth=%d %s", depth, buf); if (ok==0) { syslog(L_NOTICE, "verify error:num=%d:%s", err, X509_verify_cert_error_string(err)); if (verify_depth >= depth) { ok = 1; verify_error = X509_V_OK; } else { ok = 0; verify_error = X509_V_ERR_CERT_CHAIN_TOO_LONG; } } switch (ctx->error) { case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: X509_NAME_oneline(X509_get_issuer_name(ctx->current_cert), buf, sizeof(buf)); syslog(L_NOTICE, "issuer= %s", buf); break; case X509_V_ERR_CERT_NOT_YET_VALID: case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: syslog(L_NOTICE, "cert not yet valid"); break; case X509_V_ERR_CERT_HAS_EXPIRED: case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: syslog(L_NOTICE, "cert has expired"); break; } if ((tls_serveractive) && (tls_loglevel >= 1)) Printf("verify return:%d", ok); return (ok); } /* ** Taken from OpenSSL crypto/bio/b_dump.c, modified to save a lot of strcpy ** and strcat by Matti Aarnio. */ #define TRUNCATE #define DUMP_WIDTH 16 static int tls_dump(const char *s, int len) { int ret = 0; char buf[160 + 1]; char *ss; int i; int j; int rows; int trunc; unsigned char ch; trunc = 0; #ifdef TRUNCATE for (; (len > 0) && ((s[len - 1] == ' ') || (s[len - 1] == '\0')); len--) trunc++; #endif rows = (len / DUMP_WIDTH); if ((rows * DUMP_WIDTH) < len) rows++; for (i = 0; i < rows; i++) { buf[0] = '\0'; /* Start with empty string. */ ss = buf; snprintf(ss, sizeof(buf), "%04x ", i * DUMP_WIDTH); ss += strlen(ss); for (j = 0; j < DUMP_WIDTH; j++) { if (((i * DUMP_WIDTH) + j) >= len) { strlcpy(ss, " ", sizeof(buf) - (ss - buf)); } else { ch = ((unsigned char) *((const char *)(s) + i * DUMP_WIDTH + j)) & 0xff; snprintf(ss, sizeof(buf) - (ss - buf), "%02x%c", ch, j == 7 ? '|' : ' '); ss += 3; } } ss += strlen(ss); *ss += ' '; for (j = 0; j < DUMP_WIDTH; j++) { if (((i * DUMP_WIDTH) + j) >= len) break; ch = ((unsigned char) *((const char *)(s) + i * DUMP_WIDTH + j)) & 0xff; *ss += (((ch >= ' ') && (ch <= '~')) ? ch : '.'); if (j == 7) *ss += ' '; } *ss = 0; /* If this is the last call, then update the ddt_dump thing so that * we will move the selection point in the debug window. */ if (tls_loglevel>0) Printf("%s", buf); ret += strlen(buf); } #ifdef TRUNCATE if (trunc > 0) { snprintf(buf, sizeof(buf), "%04x - \n", len+ trunc); if (tls_loglevel>0) Printf("%s", buf); ret += strlen(buf); } #endif return (ret); } /* ** Set up the cert things on the server side. We do need both the ** private key (in key_file) and the cert (in cert_file). ** Both files may be identical. ** ** This function is taken from OpenSSL apps/s_cb.c. */ static int set_cert_stuff(SSL_CTX * ctx, char *cert_file, char *key_file) { struct stat buf; if (cert_file != NULL) { if (SSL_CTX_use_certificate_file(ctx, cert_file, SSL_FILETYPE_PEM) <= 0) { syslog(L_ERROR, "unable to get certificate from '%s'", cert_file); return (0); } if (key_file == NULL) key_file = cert_file; /* Check ownership and permissions of key file. * Look at the real file (stat) and not a possible symlink (lstat). */ if (stat(key_file, &buf) == -1) { syslog(L_ERROR, "unable to stat private key '%s'", key_file); return (0); } /* Check that the key file is a real file, not readable by * everyone. If the mode is 440 or 640, make sure the group owner * is the news group (to prevent the failure case of having news:users * as the owner and group. */ if (!S_ISREG(buf.st_mode) || (buf.st_mode & 0137) != 0 || ((buf.st_mode & 0040) != 0 && buf.st_gid != getegid())) { syslog(L_ERROR, "bad ownership or permissions on private key" " '%s': private key must be mode 640 at most, and readable" " by the news group only", key_file); return (0); } if (SSL_CTX_use_PrivateKey_file(ctx, key_file, SSL_FILETYPE_PEM) <= 0) { syslog(L_ERROR, "unable to get private key from '%s'", key_file); return (0); } /* Now we know that a key and cert have been set against * the SSL context. */ if (!SSL_CTX_check_private_key(ctx)) { syslog(L_ERROR, "private key does not match the certificate public key"); return (0); } } return (1); } #ifdef HAVE_OPENSSL_ECC /* ** Provide an ECKEY from a curve name. ** Accepts a NULL pointer as the name. ** ** Returns the key, or NULL on error. */ static EC_KEY * eckey_from_name(char *name) { EC_KEY *eckey; size_t ncurves, nitems, i; EC_builtin_curve *builtin_curves; const char *sname; if (name == NULL) { return (NULL); } /* See EC_GROUP_new(3) for the details of this expressive dance. */ ncurves = EC_get_builtin_curves(NULL, 0); /* Number of curves. */ builtin_curves = xmalloc(ncurves * sizeof(EC_builtin_curve)); nitems = EC_get_builtin_curves(builtin_curves, ncurves); if (nitems != ncurves) { syslog(L_ERROR, "got %lu curves from EC_get_builtin_curves, " "expected %lu", (unsigned long) nitems, (unsigned long) ncurves); } for (i = 0; i < nitems; i++) { sname = OBJ_nid2sn(builtin_curves[i].nid); if (strcmp(sname, name) == 0) { break; } } if (i == nitems) { syslog(L_ERROR, "tlseccurve '%s' not found", name); free(builtin_curves); return (NULL); } eckey = EC_KEY_new_by_curve_name(builtin_curves[i].nid); free(builtin_curves); return (eckey); } #endif /* HAVE_OPENSSL_ECC */ /* ** This is the setup routine for the SSL server. As nnrpd might be called ** more than once, we only want to do the initialization one time. ** ** The skeleton of this function is taken from OpenSSL apps/s_server.c. ** ** Returns -1 on error. */ int tls_init_serverengine(int verifydepth, int askcert, int requirecert, char *tls_CAfile, char *tls_CApath, char *tls_cert_file, char *tls_key_file, bool prefer_server_ciphers, bool tls_compression, struct vector *tls_proto_vect, char *tls_ciphers, char *tls_ec_curve UNUSED) { int off = 0; int verify_flags = SSL_VERIFY_NONE; char *CApath; char *CAfile; char *s_cert_file; char *s_key_file; struct stat buf; size_t tls_protos = 0; size_t i; #ifdef HAVE_OPENSSL_ECC EC_KEY *eckey; #endif if (tls_serverengine) return (0); /* Already running. */ if (tls_loglevel >= 2) Printf("starting TLS engine"); SSL_load_error_strings(); SSLeay_add_ssl_algorithms(); CTX = SSL_CTX_new(SSLv23_server_method()); if (CTX == NULL) { return (-1); }; off |= SSL_OP_ALL; /* Work around all known bugs. */ SSL_CTX_set_options(CTX, off); SSL_CTX_set_info_callback(CTX, apps_ssl_info_callback); SSL_CTX_sess_set_cache_size(CTX, 128); if (strlen(tls_CAfile) == 0) CAfile = NULL; else CAfile = tls_CAfile; if (strlen(tls_CApath) == 0) CApath = NULL; else CApath = tls_CApath; if ((!SSL_CTX_load_verify_locations(CTX, CAfile, CApath)) || (!SSL_CTX_set_default_verify_paths(CTX))) { if (tls_loglevel >= 2) Printf("TLS engine: cannot load CA data\n"); return (-1); } if (strlen(tls_cert_file) == 0) s_cert_file = NULL; else s_cert_file = tls_cert_file; if (strlen(tls_key_file) == 0) s_key_file = NULL; else s_key_file = tls_key_file; if (!set_cert_stuff(CTX, s_cert_file, s_key_file)) { if (tls_loglevel >= 2) Printf("TLS engine: cannot load cert/key data\n"); return (-1); } /* Load some randomization data from /dev/urandom, if it exists. * FIXME: should also check for ".rand" file, update it on exit. */ if (stat("/dev/urandom", &buf) == 0) RAND_load_file("/dev/urandom", 16 * 1024); SSL_CTX_set_tmp_dh_callback(CTX, tmp_dh_cb); SSL_CTX_set_options(CTX, SSL_OP_SINGLE_DH_USE); #ifdef HAVE_OPENSSL_ECC SSL_CTX_set_options(CTX, SSL_OP_SINGLE_ECDH_USE); /* We set a curve here by name if provided * or we use OpenSSL (>= 1.0.2) auto-selection * or we default to NIST P-256. */ eckey = eckey_from_name(tls_ec_curve); if (eckey != NULL) { SSL_CTX_set_tmp_ecdh(CTX, eckey); } else { # ifdef SSL_CTX_set_ecdh_auto SSL_CTX_set_ecdh_auto(CTX, 1); # else SSL_CTX_set_tmp_ecdh(CTX, EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)); # endif /* SSL_CTX_set_ecdh_auto */ } #endif /* HAVE_OPENSSL_ECC */ if (prefer_server_ciphers) { #ifdef SSL_OP_CIPHER_SERVER_PREFERENCE SSL_CTX_set_options(CTX, SSL_OP_CIPHER_SERVER_PREFERENCE); #endif } if ((tls_proto_vect != NULL) && (tls_proto_vect->count > 0)) { for (i = 0; i < tls_proto_vect->count; i++) { if (tls_proto_vect->strings[i] != NULL) { if (strcmp(tls_proto_vect->strings[i], "SSLv2") == 0) { tls_protos |= INN_TLS_SSLv2; } else if (strcmp(tls_proto_vect->strings[i], "SSLv3") == 0) { tls_protos |= INN_TLS_SSLv3; } else if (strcmp(tls_proto_vect->strings[i], "TLSv1") == 0) { tls_protos |= INN_TLS_TLSv1; } else if (strcmp(tls_proto_vect->strings[i], "TLSv1.1") == 0) { tls_protos |= INN_TLS_TLSv1_1; } else if (strcmp(tls_proto_vect->strings[i], "TLSv1.2") == 0) { tls_protos |= INN_TLS_TLSv1_2; } else { syslog(L_ERROR, "TLS engine: unknown protocol '%s' in tlsprotocols", tls_proto_vect->strings[i]); } } } } else { /* Default value: allow only TLS protocols. */ tls_protos = (INN_TLS_TLSv1 | INN_TLS_TLSv1_1 | INN_TLS_TLSv1_2); } if ((tls_protos & INN_TLS_SSLv2) == 0) { SSL_CTX_set_options(CTX, SSL_OP_NO_SSLv2); } if ((tls_protos & INN_TLS_SSLv3) == 0) { SSL_CTX_set_options(CTX, SSL_OP_NO_SSLv3); } if ((tls_protos & INN_TLS_TLSv1) == 0) { SSL_CTX_set_options(CTX, SSL_OP_NO_TLSv1); } if ((tls_protos & INN_TLS_TLSv1_1) == 0) { #ifdef SSL_OP_NO_TLSv1_1 SSL_CTX_set_options(CTX, SSL_OP_NO_TLSv1_1); #endif } if ((tls_protos & INN_TLS_TLSv1_2) == 0) { #ifdef SSL_OP_NO_TLSv1_2 SSL_CTX_set_options(CTX, SSL_OP_NO_TLSv1_2); #endif } if (tls_ciphers != NULL) { if (SSL_CTX_set_cipher_list(CTX, tls_ciphers) == 0) { syslog(L_ERROR, "TLS engine: cannot set cipher list"); return (-1); } } if (!tls_compression) { #ifdef SSL_OP_NO_COMPRESSION SSL_CTX_set_options(CTX, SSL_OP_NO_COMPRESSION); #endif } verify_depth = verifydepth; if (askcert!=0) verify_flags |= SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE; if (requirecert) verify_flags |= SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT | SSL_VERIFY_CLIENT_ONCE; SSL_CTX_set_verify(CTX, verify_flags, verify_callback); SSL_CTX_set_client_CA_list(CTX, SSL_load_client_CA_file(CAfile)); tls_serverengine = 1; return (0); } /* ** The function called by nnrpd to initialize the TLS support. Calls ** tls_init_serverengine and checks the result. On any sort of failure, ** nnrpd will exit. ** ** Returns -1 on error. */ int tls_init(void) { int ssl_result; if (tls_initialized) return 0; ssl_result = tls_init_serverengine(5, /* Depth to verify. */ 0, /* Can client auth? */ 0, /* Required client to auth? */ innconf->tlscafile, innconf->tlscapath, innconf->tlscertfile, innconf->tlskeyfile, innconf->tlspreferserverciphers, innconf->tlscompression, innconf->tlsprotocols, innconf->tlsciphers, innconf->tlseccurve); if (ssl_result == -1) { Reply("%d Error initializing TLS\r\n", initialSSL ? NNTP_FAIL_TERMINATING : NNTP_ERR_STARTTLS); syslog(L_ERROR, "error initializing TLS: " "[CA_file: %s] [CA_path: %s] [cert_file: %s] [key_file: %s]", innconf->tlscafile, innconf->tlscapath, innconf->tlscertfile, innconf->tlskeyfile); if (initialSSL) ExitWithStats(1, false); return -1; } tls_initialized = true; return 0; } /* ** Taken from OpenSSL apps/s_cb.c. */ static long bio_dump_cb(BIO * bio, int cmd, const char *argp, int argi, long argl UNUSED, long ret) { if (!do_dump) return (ret); if (cmd == (BIO_CB_READ | BIO_CB_RETURN)) { Printf("read from %08lX [%08lX] (%d bytes => %ld (0x%X))", (long unsigned int) bio, (long unsigned int) argp, argi, ret, (unsigned int) ret); tls_dump(argp, (int) ret); return (ret); } else if (cmd == (BIO_CB_WRITE | BIO_CB_RETURN)) { Printf("write to %08lX [%08lX] (%d bytes => %ld (0x%X))", (long unsigned int) bio, (long unsigned int) argp, argi, ret, (unsigned int) ret); tls_dump(argp, (int) ret); } return (ret); } /* ** This is the actual startup routine for the connection. We expect ** that the buffers are flushed and the "382 Continue with TLS negociation" ** was sent to the client (if using STARTTLS), so that we can immediately ** start the TLS handshake process. ** ** layerbits and authid are filled in on success; authid is only ** filled in if the client authenticated. */ int tls_start_servertls(int readfd, int writefd) { int sts; int keepalive; SSL_SESSION *session; SSL_CIPHER *cipher; if (!tls_serverengine) { /* It should never happen. */ syslog(L_ERROR, "tls_engine not running"); return (-1); } if (tls_loglevel >= 1) Printf("setting up TLS connection"); if (tls_conn == NULL) { tls_conn = (SSL *) SSL_new(CTX); } if (tls_conn == NULL) { return (-1); } SSL_clear(tls_conn); #if defined(SOL_SOCKET) && defined(SO_KEEPALIVE) /* Set KEEPALIVE to catch broken socket connections. */ keepalive = 1; if (setsockopt(readfd, SOL_SOCKET, SO_KEEPALIVE, &keepalive, sizeof(keepalive)) < 0) syslog(L_ERROR, "fd %d can't setsockopt(KEEPALIVE) %m", readfd); if (setsockopt(writefd, SOL_SOCKET, SO_KEEPALIVE, &keepalive, sizeof(keepalive)) < 0) syslog(L_ERROR, "fd %d can't setsockopt(KEEPALIVE) %m", writefd); #endif /* SOL_SOCKET && SO_KEEPALIVE */ /* Set the file descriptors for SSL to use. */ if (SSL_set_rfd(tls_conn, readfd)==0) { return (-1); } if (SSL_set_wfd(tls_conn, writefd)==0) { return (-1); } /* This is the actual handshake routine. It will do all the negotiations * and will check the client cert etc. */ SSL_set_accept_state(tls_conn); /* We do have an SSL_set_fd() and now suddenly a BIO_ routine is called? * Well there is a BIO below the SSL routines that is automatically * created for us, so we can use it for debugging purposes. */ if (tls_loglevel >= 3) BIO_set_callback(SSL_get_rbio(tls_conn), bio_dump_cb); /* Dump the negotiation for loglevels 3 and 4. */ if (tls_loglevel >= 3) do_dump = 1; if ((sts = SSL_accept(tls_conn)) <= 0) { session = SSL_get_session(tls_conn); if (session) { SSL_CTX_remove_session(CTX, session); } if (tls_conn) SSL_free(tls_conn); tls_conn = NULL; return (-1); } /* Only loglevel 4 dumps everything. */ if (tls_loglevel < 4) do_dump = 0; tls_protocol = SSL_get_version(tls_conn); cipher = (SSL_CIPHER *) SSL_get_current_cipher(tls_conn); tls_cipher_name = SSL_CIPHER_get_name(cipher); tls_cipher_usebits = SSL_CIPHER_get_bits(cipher, &tls_cipher_algbits); tls_serveractive = 1; syslog(L_NOTICE, "starttls: %s with cipher %s (%d/%d bits) no authentication", tls_protocol, tls_cipher_name, tls_cipher_usebits, tls_cipher_algbits); return (0); } ssize_t SSL_writev (SSL *ssl, const struct iovec *vector, int count) { static char *buffer = NULL; static size_t allocsize = 0; char *bp; size_t bytes, to_copy; int i; /* Find the total number of bytes to be written. */ bytes = 0; for (i = 0; i < count; ++i) bytes += vector[i].iov_len; /* Allocate a buffer to hold the data. */ if (NULL == buffer) { buffer = (char *) xmalloc(bytes); allocsize = bytes; } else if (bytes > allocsize) { buffer = (char *) xrealloc (buffer, bytes); allocsize = bytes; } /* Copy the data into BUFFER. */ to_copy = bytes; bp = buffer; for (i = 0; i < count; ++i) { #define min(a, b) ((a) > (b) ? (b) : (a)) size_t copy = min (vector[i].iov_len, to_copy); memcpy (bp, vector[i].iov_base, copy); bp += copy; to_copy -= copy; if (to_copy == 0) break; } return SSL_write (ssl, buffer, bytes); } #endif /* HAVE_OPENSSL */ inn-2.6.0/nnrpd/line.c0000644000175200017520000002002512575023702014133 0ustar iuliusiulius/* $Id: line.c 9563 2013-11-10 13:13:34Z iulius $ ** ** Line by line reading support from sockets/pipes. ** ** Written by Alex Kiernan . ** ** This code implements an infinitely (well size_t) long single line ** read routine. To protect against eating all available memory, it ** actually starts discarding characters if you try to send more than ** the maximum article size in a single line. ** */ #include "config.h" #include "clibrary.h" #include #ifdef HAVE_SYS_SELECT_H # include #endif #include "inn/messages.h" #include "nnrpd.h" #include #include "tls.h" #ifdef HAVE_OPENSSL extern SSL *tls_conn; #endif /* ** Free a previously allocated line structure. */ void line_free(struct line *line) { static const struct line nullline = {0, 0, 0, 0}; if (line && line->start) { free(line->start); *line = nullline; } } #ifdef HAVE_OPENSSL /* ** Alarm signal handler for client timeout. */ static void alarmHandler(int s UNUSED) { SSL_shutdown(tls_conn); tls_conn = NULL; errno = ECONNRESET; } #endif /* ** Initialise a new line structure. */ void line_init(struct line *line) { assert(line); line->allocated = NNTP_MAXLEN_COMMAND; line->where = line->start = xmalloc(line->allocated); line->remaining = 0; } /* ** Reset a line structure. */ void line_reset(struct line *line) { assert(line); line->where = line->start; line->remaining = 0; } /* ** Timeout is used only if HAVE_OPENSSL is defined. */ static ssize_t line_doread(void *p, size_t len, int timeout UNUSED) { ssize_t n; do { #ifdef HAVE_OPENSSL if (tls_conn) { int err; xsignal(SIGALRM, alarmHandler); do { alarm(timeout); n = SSL_read(tls_conn, p, len); alarm(0); if (tls_conn == NULL) { break; } err = SSL_get_error(tls_conn, n); switch (err) { case SSL_ERROR_SYSCALL: break; case SSL_ERROR_SSL: SSL_shutdown(tls_conn); tls_conn = NULL; errno = ECONNRESET; break; } } while (err == SSL_ERROR_WANT_READ); xsignal (SIGALRM, SIG_DFL); } else #endif /* HAVE_OPENSSL */ do { n = read(STDIN_FILENO, p, len); } while (n == -1 && errno == EINTR); if (n <= 0) break; /* EOF or error. */ #ifdef HAVE_SASL if (sasl_conn && sasl_ssf) { /* Security layer in place, decode the data. */ const char *out; unsigned outlen; int r; if ((r = sasl_decode(sasl_conn, p, n, &out, &outlen)) == SASL_OK) { if (outlen) memcpy(p, out, outlen); n = outlen; } else { sysnotice("sasl_decode() failed: %s; %s", sasl_errstring(r, NULL, NULL), sasl_errdetail(sasl_conn)); n = -1; } } #endif /* HAVE_SASL */ } while (n == 0); /* Split SASL blob, need to read more data. */ return n; } READTYPE line_read(struct line *line, int timeout, const char **p, size_t *len, size_t *stripped) { char *where; char *lf = NULL; READTYPE r = RTok; assert(line != NULL); assert(line->start != NULL); /* Shuffle any trailing portion not yet processed to the start of * the buffer. */ if (line->remaining != 0) { if (line->start != line->where) { memmove(line->start, line->where, line->remaining); } lf = memchr(line->start, '\n', line->remaining); } where = line->start + line->remaining; /* If we found a line terminator in the data we have, we don't need * to ask for any more. */ if (lf == NULL) { do { fd_set rmask; int i; ssize_t count; /* If we've filled the line buffer, double the size, * reallocate the buffer and try again. */ if (where == line->start + line->allocated) { size_t newsize = line->allocated * 2; /* Don't grow the buffer bigger than the maximum * article size we'll accept. */ if (PERMaccessconf->localmaxartsize > NNTP_MAXLEN_COMMAND) if (newsize > PERMaccessconf->localmaxartsize) newsize = PERMaccessconf->localmaxartsize; /* If we're trying to grow from the same size, to the * same size, we must have hit the localmaxartsize * buffer for a second (or subsequent) time -- the user * is likely trying to DOS us, so don't double the * size any more, just overwrite characters until they * stop, then discard the whole thing. */ if (newsize == line->allocated) { warn("%s overflowed our line buffer (%lu), " "discarding further input", Client.host, PERMaccessconf->localmaxartsize); where = line->start; r = RTlong; } else { line->start = xrealloc(line->start, newsize); where = line->start + line->allocated; line->allocated = newsize; } } #ifdef HAVE_OPENSSL /* It seems that the SSL_read cannot be mixed with select() * as in the current code. SSL communicates in its own data * blocks and hand shaking. The do_readline using SSL_read * could return, but still with a partial line in the SSL_read * buffer. Then the server SSL routine would sit there waiting * for completion of that data block while nnrpd sat at the * select() routine waiting for more data from the server. * * Here, we decide to just bypass the select() wait. Unlike * innd with multiple threads, the select on nnrpd is just * waiting on a single file descriptor, so it is not really * essential with blocked read like SSL_read. Using an alarm * signal around SSL_read for non active timeout, SSL works * without dead locks. However, without the select() wait, * the IDLE timer stat won't be collected... */ if (tls_conn == NULL) { #endif /* Wait for activity on stdin, updating timer stats as we * go. */ do { struct timeval t; FD_ZERO(&rmask); FD_SET(STDIN_FILENO, &rmask); t.tv_sec = timeout; t.tv_usec = 0; TMRstart(TMR_IDLE); i = select(STDIN_FILENO + 1, &rmask, NULL, NULL, &t); TMRstop(TMR_IDLE); if (i == -1 && errno != EINTR) { syswarn("%s can't select", Client.host); return RTtimeout; } } while (i == -1); /* If stdin didn't select, we must have timed out. */ if (i == 0 || !FD_ISSET(STDIN_FILENO, &rmask)) return RTtimeout; #ifdef HAVE_OPENSSL } #endif count = line_doread(where, line->allocated - (where - line->start), timeout); /* Give timeout for read errors. */ if (count < 0) { sysnotice("%s can't read", Client.host); return RTtimeout; } /* If we hit EOF, terminate the string and send it back. */ if (count == 0) { assert((where + count) < (line->start + line->allocated)); where[count] = '\0'; return RTeof; } /* Search for `\n' in what we just read. If we find it we'll * drop out and return the line for processing */ lf = memchr(where, '\n', count); where += count; } while (lf == NULL); } /* Remember where we've processed up to, so we can start off there * next time. */ line->where = lf + 1; line->remaining = where - line->where; if (r == RTok) { /* If we see a full CRLF pair, strip them both off before * returning the line to our caller. If we just get an LF * we'll accept that too (debugging INN can then be less annoying). */ if (lf > line->start && lf[-1] == '\r') { --lf; if (stripped != NULL) (*stripped)++; } *lf = '\0'; if (stripped != NULL) (*stripped)++; *len = lf - line->start; *p = line->start; } return r; } inn-2.6.0/nnrpd/list.c0000644000175200017520000002620412575023702014164 0ustar iuliusiulius/* $Id: list.c 9145 2010-10-30 09:44:46Z iulius $ ** ** LIST commands. */ #include "config.h" #include "clibrary.h" #include "nnrpd.h" #include "inn/ov.h" #include "inn/innconf.h" #include "inn/messages.h" typedef struct _LISTINFO { const char *method; const char * File; void (*impl)(struct _LISTINFO *, int ac, char *av[]); bool Required; const char * Items; const char * Format; } LISTINFO; static void cmd_list_schema(LISTINFO *lp, int ac, char *av[]); static void cmd_list_headers(LISTINFO *lp, int ac, char *av[]); static LISTINFO INFOactive = { "ACTIVE", INN_PATH_ACTIVE, NULL, true, "list of active newsgroups", "Newsgroups in form \"group high low status\"" }; static LISTINFO INFOactivetimes = { "ACTIVE.TIMES", INN_PATH_ACTIVETIMES, NULL, false, "list of newsgroup creation times", "Newsgroup creation times in form \"group time who\"" }; static LISTINFO INFOcounts = { "COUNTS", INN_PATH_ACTIVE, NULL, true, "list of active newsgroups with estimated counts", "Newsgroups in form \"group high low count status\"" }; static LISTINFO INFOdistribs = { "DISTRIBUTIONS", INN_PATH_NNRPDIST, NULL, false, "list of newsgroup distributions", "Distributions in form \"distribution description\"" }; static LISTINFO INFOheaders = { "HEADERS", NULL, cmd_list_headers, true, "list of supported headers and metadata items", "Headers and metadata items supported" }; static LISTINFO INFOsubs = { "SUBSCRIPTIONS", INN_PATH_NNRPSUBS, NULL, false, "list of recommended newsgroup subscriptions", "Recommended subscriptions in form \"group\"" }; static LISTINFO INFOdistribpats = { "DISTRIB.PATS", INN_PATH_DISTPATS, NULL, false, "list of distribution patterns", "Default distributions in form \"weight:group-pattern:distribution\"" }; static LISTINFO INFOgroups = { "NEWSGROUPS", INN_PATH_NEWSGROUPS, NULL, true, "list of newsgroup descriptions", "Newsgroup descriptions in form \"group description\"" }; static LISTINFO INFOmoderators = { "MODERATORS", INN_PATH_MODERATORS, NULL, false, "list of submission templates", "Newsgroup moderators in form \"group-pattern:submission-template\"" }; static LISTINFO INFOschema = { "OVERVIEW.FMT", NULL, cmd_list_schema, true, "overview format", "Order of fields in overview database" }; static LISTINFO INFOmotd = { "MOTD", INN_PATH_MOTD_NNRPD, NULL, false, "message of the day", "Message of the day text in UTF-8" }; static LISTINFO *info[] = { &INFOactive, &INFOactivetimes, &INFOcounts, &INFOdistribs, &INFOheaders, &INFOsubs, &INFOdistribpats, &INFOgroups, &INFOmoderators, &INFOschema, &INFOmotd, }; /* ** List the overview schema (standard and extra fields). */ static void cmd_list_schema(LISTINFO *lp, int ac UNUSED, char *av[] UNUSED) { const struct cvector *standard; unsigned int i; Reply("%d %s\r\n", NNTP_OK_LIST, lp->Format); standard = overview_fields(); for (i = 0; i < standard->count; ++i) { Printf("%s:\r\n", standard->strings[i]); } for (i = 0; i < OVextra->count; ++i) { Printf("%s:full\r\n", OVextra->strings[i]); } Printf(".\r\n"); } /* ** List supported headers and metadata information. */ static void cmd_list_headers(LISTINFO *lp, int ac, char *av[]) { bool range; range = (ac > 2 && strcasecmp(av[2], "RANGE") == 0); if (ac > 2 && (strcasecmp(av[2], "MSGID") != 0) && !range) { Reply("%d Syntax error in arguments\r\n", NNTP_ERR_SYNTAX); return; } Reply("%d %s\r\n", NNTP_OK_LIST, lp->Format); Printf(":\r\n"); if (range) { /* These information are only known by the overview system, * and are only accessible with a range. */ Printf(":bytes\r\n"); Printf(":lines\r\n"); } Printf(".\r\n"); } /* ** List a single newsgroup. Called by LIST ACTIVE with a single argument. ** This is quicker than parsing the whole active file, but only works with ** single groups. It also doesn't work for aliased groups, since overview ** doesn't know what group the group is aliased to (yet). Returns whether we ** were able to answer the command. */ static bool CMD_list_single(char *group) { char *grplist[2] = { NULL, NULL }; int lo, hi, count, flag; if (PERMspecified) { grplist[0] = group; if (!PERMmatch(PERMreadlist, grplist)) return false; } if (OVgroupstats(group, &lo, &hi, &count, &flag) && flag != NF_FLAG_ALIAS) { /* When the connected user has the right to locally post, mention it. */ if (PERMaccessconf->locpost && (flag == NF_FLAG_IGNORE || flag == NF_FLAG_JUNK || flag == NF_FLAG_NOLOCAL)) flag = NF_FLAG_OK; /* When a newsgroup is empty, the high water mark should be one less * than the low water mark according to RFC 3977. */ if (count == 0) lo = hi + 1; Reply("%d %s\r\n", NNTP_OK_LIST, INFOactive.Format); Printf("%s %0*u %0*u %c\r\n", group, ARTNUMPRINTSIZE, hi, ARTNUMPRINTSIZE, lo, flag); Printf(".\r\n"); return true; } return false; } /* ** Main LIST function. */ void CMDlist(int ac, char *av[]) { QIOSTATE *qp; char *p; char *save; char *path; char *q; char *grplist[2]; LISTINFO *lp; char *wildarg = NULL; char savec; unsigned int i; int lo, hi, count, flag; p = av[1]; /* LIST ACTIVE is the default LIST command. If a keyword is provided, * we check whether it is defined. */ if (p == NULL) { lp = &INFOactive; } else { lp = NULL; for (i = 0; i < ARRAY_SIZE(info); ++i) { if (strcasecmp(p, info[i]->method) == 0) { lp = info[i]; break; } } } /* If no defined LIST keyword is found, we return. */ if (lp == NULL) { Reply("%d Unknown LIST keyword\r\n", NNTP_ERR_SYNTAX); return; } if (lp == &INFOactive) { if (ac == 3) { wildarg = av[2]; /* No need to parse the active file for a single group. */ if (CMD_list_single(wildarg)) return; } } else if (lp == &INFOgroups || lp == &INFOactivetimes || lp == &INFOcounts || lp == &INFOheaders || lp == &INFOsubs) { if (ac == 3) wildarg = av[2]; } /* Three arguments can be passed only when ACTIVE, ACTIVE.TIMES, COUNTS * HEADERS, NEWSGROUPS or SUBSCRIPTIONS keywords are used. */ if (ac > 2 && !wildarg) { Reply("%d Unexpected wildmat or argument\r\n", NNTP_ERR_SYNTAX); return; } /* If a function is provided for the given keyword, we call it. */ if (lp->impl != NULL) { lp->impl(lp, ac, av); return; } path = innconf->pathetc; /* The active, active.times and newsgroups files are in pathdb. */ if ((strstr(lp->File, "active") != NULL) || (strstr(lp->File, "newsgroups") != NULL)) path = innconf->pathdb; path = concatpath(path, lp->File); qp = QIOopen(path); free(path); if (qp == NULL) { Reply("%d No %s available\r\n", NNTP_ERR_UNAVAILABLE, lp->Items); /* Only the active and newsgroups files are required. */ if (lp->Required || errno != ENOENT) { /* %m outputs strerror(errno). */ syslog(L_ERROR, "%s can't fopen %s %m", Client.host, lp->File); } return; } Reply("%d %s\r\n", NNTP_OK_LIST, lp->Format); if (!PERMspecified && lp != &INFOmotd) { /* Optimize for unlikely case of no permissions and false default. */ QIOclose(qp); Printf(".\r\n"); return; } /* Set up group list terminator. */ grplist[1] = NULL; /* Read lines, ignore long ones. */ while ((p = QIOread(qp)) != NULL) { /* Check that the output does not break the NNTP protocol. */ if (p[0] == '.' && p[1] != '.') { syslog(L_ERROR, "%s bad dot-stuffing in %s", Client.host, lp->File); continue; } if (lp == &INFOmotd) { if (is_valid_utf8(p)) { Printf("%s\r\n", p); } else { syslog(L_ERROR, "%s bad encoding in %s (UTF-8 expected)", Client.host, lp->File); } continue; } /* Matching patterns against patterns is not that * good but it is better than nothing... */ if (lp == &INFOdistribpats) { if (*p == '\0' || *p == '#' || *p == ';' || *p == ' ') continue; if (PERMspecified) { if ((q = strchr(p, ':')) == NULL) continue; q++; if ((save = strchr(q, ':')) == NULL) continue; *save = '\0'; grplist[0] = q; if (!PERMmatch(PERMreadlist, grplist)) continue; *save = ':'; } Printf("%s\r\n", p); continue; } if (lp == &INFOdistribs || lp == &INFOmoderators) { if (*p != '\0' && *p != '#' && *p != ';' && *p != ' ') { if (is_valid_utf8(p)) { Printf("%s\r\n", p); } else if (lp == &INFOdistribs) { syslog(L_ERROR, "%s bad encoding in %s (UTF-8 expected)", Client.host, lp->File); } } continue; } savec = '\0'; for (save = p; *save != '\0'; save++) { if (*save == ' ' || *save == '\t') { savec = *save; *save = '\0'; break; } } /* Check whether the reader has access to the newsgroup. */ if (PERMspecified) { grplist[0] = p; if (!PERMmatch(PERMreadlist, grplist)) continue; } /* Check whether the newsgroup matches the wildmat pattern, * if given. */ if (wildarg && !uwildmat(p, wildarg)) continue; if (lp == &INFOcounts) { if (OVgroupstats(p, &lo, &hi, &count, &flag)) { /* When a newsgroup is empty, the high water mark should be * one less than the low water mark according to RFC 3977. */ if (count == 0) lo = hi + 1; if (flag != NF_FLAG_ALIAS) { Printf("%s %u %u %u %c\r\n", p, hi, lo, count, PERMaccessconf->locpost && (flag == NF_FLAG_IGNORE || flag == NF_FLAG_JUNK || flag == NF_FLAG_NOLOCAL) ? NF_FLAG_OK : flag); } else if (savec != '\0') { *save = savec; if ((q = strrchr(p, NF_FLAG_ALIAS)) != NULL) { *save = '\0'; Printf("%s %u %u %u %s\r\n", p, hi, lo, count, q); } } } continue; } if (savec != '\0') *save = savec; if (lp == &INFOactive) { /* When the connected user has the right to locally post, mention it. */ if (PERMaccessconf->locpost && (q = strrchr(p, ' ')) != NULL) { q++; if (*q == NF_FLAG_IGNORE || *q == NF_FLAG_JUNK || *q == NF_FLAG_NOLOCAL) *q = NF_FLAG_OK; } } Printf("%s\r\n", p); } QIOclose(qp); Printf(".\r\n"); } inn-2.6.0/nnrpd/cache.c0000644000175200017520000000605712575023702014260 0ustar iuliusiulius/* $Id: cache.c 8709 2009-11-06 21:47:11Z iulius $ ** ** Message-ID to storage token cache. ** ** Written by Alex Kiernan (alex.kiernan@thus.net). ** ** Implementation of a message-ID to storage token cache which can be ** built during (X)OVER/(X)HDR/XPAT/NEWNEWS. If we hit in the cache when ** retrieving articles, the (relatively) expensive cost of a trip ** through the history database is saved. */ #include "config.h" #include "clibrary.h" #include "inn/innconf.h" #include "inn/tst.h" #include "inn/list.h" #include "inn/libinn.h" #include "inn/storage.h" #include "cache.h" /* ** Pointer to the message-ID to storage token ternary search tree. */ static struct tst *msgidcache; /* ** Count of message-IDs in the cache so that someone doing GROUP, ** (X)OVER, GROUP, (X)OVER, etc. for example doesn't blow up with ** out of memory. */ static unsigned long msgcachecount; struct cache_entry { struct node node; HASH hash; TOKEN token; }; static struct list unused, used; /* ** Add a translation from HASH, h, to TOKEN, t, to the message-ID ** cache. */ void cache_add(const HASH h, const TOKEN t) { if (innconf->msgidcachesize != 0) { struct cache_entry *entry, *old; const unsigned char *p; void *exist; if (!msgidcache) { msgidcache = tst_init((innconf->msgidcachesize + 9) / 10); list_new(&unused); list_new(&used); } entry = xmalloc(sizeof *entry); entry->hash = h; entry->token = t; p = (unsigned char *) HashToText(h); if (tst_insert(msgidcache, p, entry, 0, &exist) == TST_DUPLICATE_KEY) { free(entry); old = exist; list_remove(&old->node); list_addtail(&unused, &old->node); } else { list_addtail(&unused, &entry->node); ++msgcachecount; } if (msgcachecount >= innconf->msgidcachesize) { /* Need to throw away a node. */ entry = (struct cache_entry *)list_remhead(&used); if (entry == NULL) entry = (struct cache_entry *)list_remhead(&unused); if (entry != NULL) { tst_delete(msgidcache, (unsigned char *) HashToText(entry->hash)); free(entry); } } } } /* ** Lookup (and remove if found) a message-ID to TOKEN mapping. If this ** is a final lookup (ARTICLE, BODY, (X)HDR, XPAT), we remove it if we ** find it since this matches the observed behaviour of most clients, but ** cache it just in case we can reuse it if they issue multiple ** commands against the same message-ID (e.g. HEAD, STAT). */ TOKEN cache_get(const HASH h, bool final) { static HASH last_hash; static TOKEN last_token; static const TOKEN empty_token = { TOKEN_EMPTY, 0, "" }; if (HashCompare(&h, &last_hash) == 0 && !HashEmpty(last_hash)) return last_token; if (msgidcache) { struct cache_entry *entry; entry = tst_search(msgidcache, (unsigned char *) HashToText(h)); if (entry != NULL) { list_remove(&entry->node); if (!final) list_addtail(&unused, &entry->node); else list_addtail(&used, &entry->node); last_hash = entry->hash; last_token = entry->token; return last_token; } } return empty_token; } inn-2.6.0/nnrpd/Makefile0000644000175200017520000003700212575023702014503 0ustar iuliusiulius## $Id: Makefile 9815 2015-03-25 20:26:58Z iulius $ include ../Makefile.global top = .. CFLAGS = $(GCFLAGS) $(SSL_CPPFLAGS) $(SASL_CPPFLAGS) ALL = nnrpd SOURCES = article.c auth-ext.c cache.c group.c commands.c line.c \ list.c misc.c newnews.c nnrpd.c perl.c perm.c post.c \ python.c sasl.c tls.c track.c INCLUDES = cache.h nnrpd.h post.h tls.h OBJECTS = $(SOURCES:.c=.o) all: $(ALL) warnings: $(MAKE) COPT='$(WARNINGS)' all install: all $(LI_XPUB) nnrpd $D$(PATHBIN)/nnrpd bootstrap: clean clobber distclean maintclean: rm -f *.o $(ALL) nnrpdp profiled rm -rf .libs ## Compilation rules. NNRPDLIBS = $(LIBSTORAGE) $(LIBHIST) $(LIBINN) $(STORAGE_LIBS) \ $(PERL_LIBS) $(PYTHON_LIBS) $(SSL_LDFLAGS) $(SSL_LIBS) \ $(CRYPTO_LIBS) $(SASL_LDFLAGS) $(SASL_LIBS) $(LIBS) .c.o: $(CC) $(CFLAGS) $(SSL_CPPFLAGS) $(SASL_CPPFLAGS) -c $< perl.o: perl.c ; $(CC) $(CFLAGS) $(PERL_CPPFLAGS) -c perl.c python.o: python.c ; $(CC) $(CFLAGS) $(PYTHON_CPPFLAGS) -c python.c nnrpd: $(OBJECTS) $(LIBHIST) $(LIBSTORAGE) $(LIBINN) $(LIBLD) $(LDFLAGS) -o $@ $(OBJECTS) $(NNRPDLIBS) $(LIBINN): ; (cd ../lib ; $(MAKE)) $(LIBSTORAGE): ; (cd ../storage ; $(MAKE)) $(LIBHIST): ; (cd ../history ; $(MAKE)) ## Profiling. These rules have not been checked for a while and may need ## some work. profiled: nnrpdp nnrpdp: $(SOURCES) rm -f $(OBJECTS) $(MAKEPROFILING) nnrpd mv nnrpd nnrpdp rm -f $(OBJECTS) ## Dependencies. Default list, below, is probably good enough. depend: $(SOURCES) $(MAKEDEPEND) '$(CFLAGS) $(PERL_CPPFLAGS) $(PYTHON_CPPFLAGS)' \ $(SOURCES) # DO NOT DELETE THIS LINE -- make depend depends on it. article.o: article.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/innconf.h \ ../include/inn/messages.h ../include/inn/wire.h nnrpd.h \ ../include/portable/macros.h ../include/portable/socket.h \ ../include/portable/getaddrinfo.h ../include/portable/getnameinfo.h \ ../include/inn/qio.h ../include/inn/libinn.h ../include/inn/xmalloc.h \ ../include/inn/xwrite.h ../include/inn/nntp.h ../include/inn/paths.h \ ../include/inn/storage.h ../include/inn/options.h \ ../include/inn/vector.h ../include/inn/timer.h ../include/inn/ov.h \ ../include/inn/history.h ../include/inn/storage.h tls.h cache.h auth-ext.o: auth-ext.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/buffer.h \ ../include/inn/messages.h ../include/inn/vector.h nnrpd.h \ ../include/portable/macros.h ../include/portable/socket.h \ ../include/portable/getaddrinfo.h ../include/portable/getnameinfo.h \ ../include/inn/qio.h ../include/inn/libinn.h ../include/inn/xmalloc.h \ ../include/inn/xwrite.h ../include/inn/nntp.h ../include/inn/paths.h \ ../include/inn/storage.h ../include/inn/options.h ../include/inn/timer.h cache.o: cache.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/innconf.h \ ../include/inn/tst.h ../include/inn/list.h ../include/inn/libinn.h \ ../include/inn/xmalloc.h ../include/inn/xwrite.h \ ../include/inn/storage.h ../include/inn/options.h cache.h group.o: group.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/innconf.h nnrpd.h \ ../include/portable/macros.h ../include/portable/socket.h \ ../include/portable/getaddrinfo.h ../include/portable/getnameinfo.h \ ../include/inn/qio.h ../include/inn/libinn.h ../include/inn/xmalloc.h \ ../include/inn/xwrite.h ../include/inn/nntp.h ../include/inn/paths.h \ ../include/inn/storage.h ../include/inn/options.h \ ../include/inn/vector.h ../include/inn/timer.h ../include/inn/ov.h \ ../include/inn/history.h ../include/inn/storage.h commands.o: commands.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h nnrpd.h ../include/portable/macros.h \ ../include/portable/socket.h ../include/portable/getaddrinfo.h \ ../include/portable/getnameinfo.h ../include/inn/qio.h \ ../include/inn/libinn.h ../include/inn/xmalloc.h ../include/inn/xwrite.h \ ../include/inn/nntp.h ../include/inn/paths.h ../include/inn/storage.h \ ../include/inn/options.h ../include/inn/vector.h ../include/inn/timer.h \ ../include/inn/fdflag.h ../include/inn/portable-socket.h \ ../include/inn/ov.h ../include/inn/history.h ../include/inn/storage.h \ ../include/inn/innconf.h ../include/inn/messages.h \ ../include/inn/version.h tls.h line.o: line.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/messages.h nnrpd.h \ ../include/portable/macros.h ../include/portable/socket.h \ ../include/portable/getaddrinfo.h ../include/portable/getnameinfo.h \ ../include/inn/qio.h ../include/inn/libinn.h ../include/inn/xmalloc.h \ ../include/inn/xwrite.h ../include/inn/nntp.h ../include/inn/paths.h \ ../include/inn/storage.h ../include/inn/options.h \ ../include/inn/vector.h ../include/inn/timer.h tls.h list.o: list.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h nnrpd.h ../include/portable/macros.h \ ../include/portable/socket.h ../include/portable/getaddrinfo.h \ ../include/portable/getnameinfo.h ../include/inn/qio.h \ ../include/inn/libinn.h ../include/inn/xmalloc.h ../include/inn/xwrite.h \ ../include/inn/nntp.h ../include/inn/paths.h ../include/inn/storage.h \ ../include/inn/options.h ../include/inn/vector.h ../include/inn/timer.h \ ../include/inn/ov.h ../include/inn/history.h ../include/inn/storage.h \ ../include/inn/innconf.h ../include/inn/messages.h misc.o: misc.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/innconf.h nnrpd.h \ ../include/portable/macros.h ../include/portable/socket.h \ ../include/portable/getaddrinfo.h ../include/portable/getnameinfo.h \ ../include/inn/qio.h ../include/inn/libinn.h ../include/inn/xmalloc.h \ ../include/inn/xwrite.h ../include/inn/nntp.h ../include/inn/paths.h \ ../include/inn/storage.h ../include/inn/options.h \ ../include/inn/vector.h ../include/inn/timer.h tls.h ../include/inn/ov.h \ ../include/inn/history.h ../include/inn/storage.h newnews.o: newnews.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/innconf.h \ ../include/inn/messages.h ../include/inn/wire.h nnrpd.h \ ../include/portable/macros.h ../include/portable/socket.h \ ../include/portable/getaddrinfo.h ../include/portable/getnameinfo.h \ ../include/inn/qio.h ../include/inn/libinn.h ../include/inn/xmalloc.h \ ../include/inn/xwrite.h ../include/inn/nntp.h ../include/inn/paths.h \ ../include/inn/storage.h ../include/inn/options.h \ ../include/inn/vector.h ../include/inn/timer.h ../include/inn/ov.h \ ../include/inn/history.h ../include/inn/storage.h cache.h nnrpd.o: nnrpd.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h \ ../include/portable/alloca.h ../include/clibrary.h ../include/config.h \ ../include/inn/macros.h ../include/portable/stdbool.h \ ../include/portable/setproctitle.h ../include/portable/macros.h \ ../include/portable/socket.h ../include/portable/getaddrinfo.h \ ../include/portable/getnameinfo.h ../include/inn/innconf.h \ ../include/inn/libinn.h ../include/inn/xmalloc.h ../include/inn/xwrite.h \ ../include/inn/messages.h ../include/inn/network.h \ ../include/inn/portable-socket.h ../include/inn/network-innbind.h \ ../include/inn/newsuser.h ../include/inn/ov.h ../include/inn/history.h \ ../include/inn/storage.h ../include/inn/options.h \ ../include/inn/version.h nnrpd.h ../include/inn/qio.h \ ../include/inn/nntp.h ../include/inn/paths.h ../include/inn/storage.h \ ../include/inn/vector.h ../include/inn/timer.h tls.h perl.o: perl.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/innconf.h nnrpd.h \ ../include/portable/macros.h ../include/portable/socket.h \ ../include/portable/getaddrinfo.h ../include/portable/getnameinfo.h \ ../include/inn/qio.h ../include/inn/libinn.h ../include/inn/xmalloc.h \ ../include/inn/xwrite.h ../include/inn/nntp.h ../include/inn/paths.h \ ../include/inn/storage.h ../include/inn/options.h \ ../include/inn/vector.h ../include/inn/timer.h post.h \ ../include/ppport.h ../include/innperl.h perm.o: perm.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/conffile.h \ ../include/portable/macros.h ../include/inn/network.h \ ../include/inn/portable-socket.h ../include/inn/portable-getaddrinfo.h \ ../include/inn/portable-getnameinfo.h ../include/inn/innconf.h \ ../include/innperl.h nnrpd.h ../include/portable/macros.h \ ../include/portable/socket.h ../include/inn/qio.h \ ../include/inn/libinn.h ../include/inn/xmalloc.h ../include/inn/xwrite.h \ ../include/inn/nntp.h ../include/inn/paths.h ../include/inn/storage.h \ ../include/inn/options.h ../include/inn/vector.h ../include/inn/timer.h post.o: post.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/innconf.h nnrpd.h \ ../include/portable/macros.h ../include/portable/socket.h \ ../include/portable/getaddrinfo.h ../include/portable/getnameinfo.h \ ../include/inn/qio.h ../include/inn/libinn.h ../include/inn/xmalloc.h \ ../include/inn/xwrite.h ../include/inn/nntp.h ../include/inn/paths.h \ ../include/inn/storage.h ../include/inn/options.h \ ../include/inn/vector.h ../include/inn/timer.h ../include/inn/ov.h \ ../include/inn/history.h ../include/inn/storage.h post.h python.o: python.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h \ ../include/clibrary.h ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/innconf.h nnrpd.h \ ../include/portable/macros.h ../include/portable/socket.h \ ../include/portable/getaddrinfo.h ../include/portable/getnameinfo.h \ ../include/inn/qio.h ../include/inn/libinn.h ../include/inn/xmalloc.h \ ../include/inn/xwrite.h ../include/inn/nntp.h ../include/inn/paths.h \ ../include/inn/storage.h ../include/inn/options.h \ ../include/inn/vector.h ../include/inn/timer.h ../include/inn/hashtab.h sasl.o: sasl.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/messages.h nnrpd.h \ ../include/portable/macros.h ../include/portable/socket.h \ ../include/portable/getaddrinfo.h ../include/portable/getnameinfo.h \ ../include/inn/qio.h ../include/inn/libinn.h ../include/inn/xmalloc.h \ ../include/inn/xwrite.h ../include/inn/nntp.h ../include/inn/paths.h \ ../include/inn/storage.h ../include/inn/options.h \ ../include/inn/vector.h ../include/inn/timer.h ../include/inn/ov.h \ ../include/inn/history.h ../include/inn/storage.h tls.o: tls.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h nnrpd.h ../include/portable/macros.h \ ../include/portable/socket.h ../include/portable/getaddrinfo.h \ ../include/portable/getnameinfo.h ../include/inn/qio.h \ ../include/inn/libinn.h ../include/inn/xmalloc.h ../include/inn/xwrite.h \ ../include/inn/nntp.h ../include/inn/paths.h ../include/inn/storage.h \ ../include/inn/options.h ../include/inn/vector.h ../include/inn/timer.h \ ../include/inn/innconf.h tls.h track.o: track.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/innconf.h nnrpd.h \ ../include/portable/macros.h ../include/portable/socket.h \ ../include/portable/getaddrinfo.h ../include/portable/getnameinfo.h \ ../include/inn/qio.h ../include/inn/libinn.h ../include/inn/xmalloc.h \ ../include/inn/xwrite.h ../include/inn/nntp.h ../include/inn/paths.h \ ../include/inn/storage.h ../include/inn/options.h \ ../include/inn/vector.h ../include/inn/timer.h inn-2.6.0/nnrpd/article.c0000644000175200017520000011367012575023702014640 0ustar iuliusiulius/* $Id: article.c 9846 2015-05-03 14:56:15Z iulius $ ** ** Article-related routines. */ #include "config.h" #include "clibrary.h" #include #if HAVE_LIMITS_H # include #endif #include #include #include "inn/innconf.h" #include "inn/messages.h" #include "inn/wire.h" #include "nnrpd.h" #include "inn/ov.h" #include "tls.h" #include "cache.h" #ifdef HAVE_OPENSSL extern SSL *tls_conn; #endif /* ** Data structures for use in ARTICLE/HEAD/BODY/STAT common code. */ typedef enum _SENDTYPE { STarticle, SThead, STbody, STstat } SENDTYPE; typedef struct _SENDDATA { SENDTYPE Type; int ReplyCode; const char *Item; } SENDDATA; static ARTHANDLE *ARThandle = NULL; static SENDDATA SENDbody = { STbody, NNTP_OK_BODY, "body" }; static SENDDATA SENDarticle = { STarticle, NNTP_OK_ARTICLE, "article" }; static SENDDATA SENDstat = { STstat, NNTP_OK_STAT, "status" }; static SENDDATA SENDhead = { SThead, NNTP_OK_HEAD, "head" }; static struct iovec iov[IOV_MAX > 1024 ? 1024 : IOV_MAX]; static int queued_iov = 0; static void PushIOvHelper(struct iovec* vec, int* countp) { int result = 0; TMRstart(TMR_NNTPWRITE); #ifdef HAVE_SASL if (sasl_conn && sasl_ssf) { int i; for (i = 0; i < *countp; i++) { write_buffer(vec[i].iov_base, vec[i].iov_len); } } else { #endif /* HAVE_SASL */ #ifdef HAVE_OPENSSL if (tls_conn) { Again: result = SSL_writev(tls_conn, vec, *countp); switch (SSL_get_error(tls_conn, result)) { case SSL_ERROR_NONE: case SSL_ERROR_SYSCALL: break; case SSL_ERROR_WANT_WRITE: goto Again; break; case SSL_ERROR_SSL: SSL_shutdown(tls_conn); tls_conn = NULL; errno = ECONNRESET; break; case SSL_ERROR_ZERO_RETURN: break; } } else #endif /* HAVE_OPENSSL */ result = xwritev(STDOUT_FILENO, vec, *countp); #ifdef HAVE_SASL } #endif TMRstop(TMR_NNTPWRITE); if (result == -1) { /* We can't recover, since we can't resynchronise with our * peer. */ ExitWithStats(1, true); } *countp = 0; } static void PushIOvRateLimited(void) { double start, end, elapsed, target; struct iovec newiov[IOV_MAX > 1024 ? 1024 : IOV_MAX]; int newiov_len; int sentiov; int i; int bytesfound; int chunkbittenoff; struct timeval waittime; while (queued_iov) { bytesfound = newiov_len = 0; sentiov = 0; for (i = 0; (i < queued_iov) && (bytesfound < MaxBytesPerSecond); i++) { if ((signed)iov[i].iov_len + bytesfound > MaxBytesPerSecond) { chunkbittenoff = MaxBytesPerSecond - bytesfound; newiov[newiov_len].iov_base = iov[i].iov_base; newiov[newiov_len++].iov_len = chunkbittenoff; iov[i].iov_base = (char *)iov[i].iov_base + chunkbittenoff; iov[i].iov_len -= chunkbittenoff; bytesfound += chunkbittenoff; } else { newiov[newiov_len++] = iov[i]; sentiov++; bytesfound += iov[i].iov_len; } } assert(sentiov <= queued_iov); start = TMRnow_double(); PushIOvHelper(newiov, &newiov_len); end = TMRnow_double(); target = (double) bytesfound / MaxBytesPerSecond; elapsed = end - start; if (elapsed < 1 && elapsed < target) { waittime.tv_sec = 0; waittime.tv_usec = (target - elapsed) * 1e6; start = TMRnow_double(); if (select(0, NULL, NULL, NULL, &waittime) != 0) syswarn("%s: select in PushIOvRateLimit failed", Client.host); end = TMRnow_double(); IDLEtime += end - start; } memmove(iov, &iov[sentiov], (queued_iov - sentiov) * sizeof(struct iovec)); queued_iov -= sentiov; } } static void PushIOv(void) { TMRstart(TMR_NNTPWRITE); fflush(stdout); TMRstop(TMR_NNTPWRITE); if (MaxBytesPerSecond != 0) PushIOvRateLimited(); else PushIOvHelper(iov, &queued_iov); } static void SendIOv(const char *p, int len) { char *q; if (queued_iov) { q = (char *)iov[queued_iov - 1].iov_base + iov[queued_iov - 1].iov_len; if (p == q) { iov[queued_iov - 1].iov_len += len; return; } } iov[queued_iov].iov_base = (char*)p; iov[queued_iov++].iov_len = len; if (queued_iov == IOV_MAX) PushIOv(); } static char *_IO_buffer_ = NULL; static int highwater = 0; static void PushIOb(void) { write_buffer(_IO_buffer_, highwater); highwater = 0; } static void SendIOb(const char *p, int len) { int tocopy; if (_IO_buffer_ == NULL) _IO_buffer_ = xmalloc(BIG_BUFFER); while (len > 0) { tocopy = (len > (BIG_BUFFER - highwater)) ? (BIG_BUFFER - highwater) : len; memcpy(&_IO_buffer_[highwater], p, tocopy); p += tocopy; highwater += tocopy; len -= tocopy; if (highwater == BIG_BUFFER) PushIOb(); } } /* ** If we have an article open, close it. */ void ARTclose(void) { if (ARThandle) { SMfreearticle(ARThandle); ARThandle = NULL; } } bool ARTinstorebytoken(TOKEN token) { ARTHANDLE *art; struct timeval stv, etv; if (PERMaccessconf->nnrpdoverstats) { gettimeofday(&stv, NULL); } art = SMretrieve(token, RETR_STAT); /* XXX This isn't really overstats, is it? */ if (PERMaccessconf->nnrpdoverstats) { gettimeofday(&etv, NULL); OVERartcheck+=(etv.tv_sec - stv.tv_sec) * 1000; OVERartcheck+=(etv.tv_usec - stv.tv_usec) / 1000; } if (art) { SMfreearticle(art); return true; } return false; } /* ** If the article name is valid, open it and stuff in the ID. */ static bool ARTopen(ARTNUM artnum) { static ARTNUM save_artnum; TOKEN token; /* Re-use article if it's the same one. */ if (save_artnum == artnum) { if (ARThandle) return true; } ARTclose(); if (!OVgetartinfo(GRPcur, artnum, &token)) return false; TMRstart(TMR_READART); ARThandle = SMretrieve(token, RETR_ALL); TMRstop(TMR_READART); if (ARThandle == NULL) { return false; } save_artnum = artnum; return true; } /* ** Open the article for a given message-ID. */ static bool ARTopenbyid(char *msg_id, ARTNUM *ap, bool final) { TOKEN token; *ap = 0; token = cache_get(HashMessageID(msg_id), final); if (token.type == TOKEN_EMPTY) { if (History == NULL) { time_t statinterval; /* Do lazy opens of the history file: lots of clients * will never ask for anything by message-ID, so put off * doing the work until we have to. */ History = HISopen(HISTORY, innconf->hismethod, HIS_RDONLY); if (!History) { syslog(L_NOTICE, "can't initialize history"); Reply("%d NNTP server unavailable; try later\r\n", NNTP_FAIL_TERMINATING); ExitWithStats(1, true); } statinterval = 30; HISctl(History, HISCTLS_STATINTERVAL, &statinterval); } if (!HISlookup(History, msg_id, NULL, NULL, NULL, &token)) return false; } if (token.type == TOKEN_EMPTY) return false; TMRstart(TMR_READART); ARThandle = SMretrieve(token, RETR_ALL); TMRstop(TMR_READART); if (ARThandle == NULL) { return false; } return true; } /* ** Send a (part of) a file to stdout, doing newline and dot conversion. */ static void ARTsendmmap(SENDTYPE what) { const char *p, *q, *r; const char *s, *path, *xref, *endofpath; char lastchar; ARTcount++; GRParticles++; lastchar = -1; /* Get the headers and detect if wire format. */ if (what == STarticle) { q = ARThandle->data; p = ARThandle->data + ARThandle->len; } else { for (q = p = ARThandle->data; p < (ARThandle->data + ARThandle->len); p++) { if (*p == '\r') continue; if (*p == '\n') { if (lastchar == '\n') { if (what == SThead) { if (*(p-1) == '\r') p--; break; } else { q = p + 1; p = ARThandle->data + ARThandle->len; break; } } } lastchar = *p; } } /* q points to the start of the article buffer, p to the end of it. */ if (VirtualPathlen > 0 && (what != STbody)) { path = wire_findheader(ARThandle->data, ARThandle->len, "Path", true); if (path == NULL) { SendIOv(".\r\n", 3); ARTgetsize += 3; PushIOv(); ARTget++; return; } else { xref = wire_findheader(ARThandle->data, ARThandle->len, "Xref", true); if (xref == NULL) { SendIOv(".\r\n", 3); ARTgetsize += 3; PushIOv(); ARTget++; return; } } endofpath = wire_endheader(path, ARThandle->data + ARThandle->len - 1); if (endofpath == NULL) { SendIOv(".\r\n", 3); ARTgetsize += 3; PushIOv(); ARTget++; return; } if ((r = memchr(xref, ' ', p - xref)) == NULL || r == p) { SendIOv(".\r\n", 3); ARTgetsize += 3; PushIOv(); ARTget++; return; } /* r points to the first space in the Xref: header. */ for (s = path, lastchar = '\0'; s + VirtualPathlen + 1 < endofpath; lastchar = *s++) { if ((lastchar != '\0' && lastchar != '!') || *s != *VirtualPath || strncasecmp(s, VirtualPath, VirtualPathlen - 1) != 0) continue; if (*(s + VirtualPathlen - 1) != '\0' && *(s + VirtualPathlen - 1) != '!') continue; break; } if (s + VirtualPathlen + 1 < endofpath) { if (xref > path) { SendIOv(q, path - q); SendIOv(s, xref - s); SendIOv(VirtualPath, VirtualPathlen - 1); SendIOv(r, p - r); } else { SendIOv(q, xref - q); SendIOv(VirtualPath, VirtualPathlen - 1); SendIOv(r, path - r); SendIOv(s, p - s); } } else { /* Double the '!' (thus, adding one '!') in Path: header. */ if (xref > path) { SendIOv(q, path - q); SendIOv(VirtualPath, VirtualPathlen); SendIOv("!", 1); SendIOv(path, xref - path); SendIOv(VirtualPath, VirtualPathlen - 1); SendIOv(r, p - r); } else { SendIOv(q, xref - q); SendIOv(VirtualPath, VirtualPathlen - 1); SendIOv(r, path - r); SendIOv(VirtualPath, VirtualPathlen); SendIOv("!", 1); SendIOv(path, p - path); } } } else SendIOv(q, p - q); ARTgetsize += p - q; if (what == SThead) { SendIOv(".\r\n", 3); ARTgetsize += 3; } else if (memcmp((ARThandle->data + ARThandle->len - 5), "\r\n.\r\n", 5)) { if (memcmp((ARThandle->data + ARThandle->len - 2), "\r\n", 2)) { SendIOv("\r\n.\r\n", 5); ARTgetsize += 5; } else { SendIOv(".\r\n", 3); ARTgetsize += 3; } } PushIOv(); ARTget++; } /* ** Return the header from the specified file, or NULL if not found. */ char * GetHeader(const char *header, bool stripspaces) { const char *p, *q, *r, *s, *t; char *w, *wnew, prevchar; /* Bogus value here to make sure that it isn't initialized to \n. */ char lastchar = ' '; const char *limit; const char *cmplimit; static char *retval = NULL; static int retlen = 0; int headerlen; bool pathheader = false; bool xrefheader = false; limit = ARThandle->data + ARThandle->len; cmplimit = ARThandle->data + ARThandle->len - strlen(header) - 1; for (p = ARThandle->data; p < cmplimit; p++) { if (*p == '\r') continue; if ((lastchar == '\n') && (*p == '\n')) { return NULL; } if ((lastchar == '\n') || (p == ARThandle->data)) { headerlen = strlen(header); if (strncasecmp(p, header, headerlen) == 0 && p[headerlen] == ':' && ISWHITE(p[headerlen+1])) { p += headerlen + 2; if (stripspaces) { for (; (p < limit) && ISWHITE(*p) ; p++); } for (q = p; q < limit; q++) if ((*q == '\r') || (*q == '\n')) { /* Check for continuation header lines. */ t = q + 1; if (t < limit) { if ((*q == '\r' && *t == '\n')) { t++; if (t == limit) break; } if ((*t == '\t' || *t == ' ')) { for (; (t < limit) && ISWHITE(*t) ; t++); q = t; } else { break; } } else { break; } } if (q == limit) return NULL; if (strncasecmp("Path", header, headerlen) == 0) pathheader = true; else if (strncasecmp("Xref", header, headerlen) == 0) xrefheader = true; if (retval == NULL) { /* Possibly add '!' (a second one) at the end of the virtual path. * So it is +2 because of '\0'. */ retlen = q - p + VirtualPathlen + 2; retval = xmalloc(retlen); } else { if ((q - p + VirtualPathlen + 2) > retlen) { retlen = q - p + VirtualPathlen + 2; retval = xrealloc(retval, retlen); } } if (pathheader && (VirtualPathlen > 0)) { const char *endofpath; const char *endofarticle; endofarticle = ARThandle->data + ARThandle->len - 1; endofpath = wire_endheader(p, endofarticle); if (endofpath == NULL) return NULL; for (s = p, prevchar = '\0'; s + VirtualPathlen + 1 < endofpath; prevchar = *s++) { if ((prevchar != '\0' && prevchar != '!') || *s != *VirtualPath || strncasecmp(s, VirtualPath, VirtualPathlen - 1) != 0) continue; if (*(s + VirtualPathlen - 1) != '\0' && *(s + VirtualPathlen - 1) != '!') continue; break; } if (s + VirtualPathlen + 1 < endofpath) { memcpy(retval, s, q - s); *(retval + (int)(q - s)) = '\0'; } else { memcpy(retval, VirtualPath, VirtualPathlen); *(retval + VirtualPathlen) = '!'; memcpy(retval + VirtualPathlen + 1, p, q - p); *(retval + (int)(q - p) + VirtualPathlen + 1) = '\0'; } } else if (xrefheader && (VirtualPathlen > 0)) { if ((r = memchr(p, ' ', q - p)) == NULL) return NULL; for (; (r < q) && isspace((unsigned char) *r) ; r++); if (r == q) return NULL; /* Copy the virtual path without its final '!'. */ memcpy(retval, VirtualPath, VirtualPathlen - 1); memcpy(retval + VirtualPathlen - 1, r - 1, q - r + 1); *(retval + (int)(q - r) + VirtualPathlen) = '\0'; } else { memcpy(retval, p, q - p); *(retval + (int)(q - p)) = '\0'; } for (w = retval, wnew = retval; *w; w++) { if (*w == '\r' && w[1] == '\n') { w++; continue; } if (*w == '\0' || *w == '\t' || *w == '\r' || *w == '\n') { *wnew = ' '; } else { *wnew = *w; } wnew++; } *wnew = '\0'; return retval; } } lastchar = *p; } return NULL; } /* ** Fetch part or all of an article and send it to the client. */ void CMDfetch(int ac, char *av[]) { char buff[SMBUF]; SENDDATA *what; bool mid, ok; ARTNUM art; char *msgid; ARTNUM tart; bool final = false; mid = (ac > 1 && IsValidMessageID(av[1], true)); /* Check the syntax of the arguments first. */ if (ac > 1 && !IsValidArticleNumber(av[1])) { /* It is better to check for a number before a message-ID because * '<' could have been forgotten and the error message should then * report a syntax error in the message-ID. */ if (isdigit((unsigned char) av[1][0])) { Reply("%d Syntax error in article number\r\n", NNTP_ERR_SYNTAX); return; } else if (!mid) { Reply("%d Syntax error in message-ID\r\n", NNTP_ERR_SYNTAX); return; } } /* Find what to send; get permissions. */ ok = PERMcanread; switch (*av[0]) { default: what = &SENDbody; final = true; break; case 'a': case 'A': what = &SENDarticle; final = true; break; case 's': case 'S': what = &SENDstat; break; case 'h': case 'H': what = &SENDhead; /* Poster might do a HEAD command to verify the article. */ ok = PERMcanread || PERMcanpost; break; } /* Trying to read. */ if (GRPcount == 0 && !mid) { Reply("%d Not in a newsgroup\r\n", NNTP_FAIL_NO_GROUP); return; } /* Check authorizations. If an article number is requested * (not a message-ID), we check whether the group is still readable. */ if (!ok || (!mid && PERMgroupmadeinvalid)) { Reply("%d Read access denied\r\n", PERMcanauthenticate ? NNTP_FAIL_AUTH_NEEDED : NNTP_ERR_ACCESS); return; } /* Requesting by message-ID? */ if (mid) { if (!ARTopenbyid(av[1], &art, final)) { Reply("%d No such article\r\n", NNTP_FAIL_MSGID_NOTFOUND); return; } if (!PERMartok()) { ARTclose(); Reply("%d Read access denied for this article\r\n", PERMcanauthenticate ? NNTP_FAIL_AUTH_NEEDED : NNTP_ERR_ACCESS); return; } Reply("%d %lu %s %s\r\n", what->ReplyCode, art, av[1], what->Item); if (what->Type != STstat) { ARTsendmmap(what->Type); } ARTclose(); return; } /* Default is to get current article, or specified article. */ if (ac == 1) { if (ARTnumber < ARTlow || ARTnumber > ARThigh) { Reply("%d Current article number %lu is invalid\r\n", NNTP_FAIL_ARTNUM_INVALID, ARTnumber); return; } snprintf(buff, sizeof(buff), "%lu", ARTnumber); tart = ARTnumber; } else { /* We have already checked that the article number is valid. */ strlcpy(buff, av[1], sizeof(buff)); tart = (ARTNUM)atol(buff); } /* Open the article and send the reply. */ if (!ARTopen(tart)) { Reply("%d No such article number %lu\r\n", NNTP_FAIL_ARTNUM_NOTFOUND, tart); return; } if ((msgid = GetHeader("Message-ID", true)) == NULL) { ARTclose(); Reply("%d No such article number %lu\r\n", NNTP_FAIL_ARTNUM_NOTFOUND, tart); return; } if (ac > 1) ARTnumber = tart; /* A message-ID does not have more than 250 octets. */ Reply("%d %s %.250s %s\r\n", what->ReplyCode, buff, msgid, what->Item); if (what->Type != STstat) ARTsendmmap(what->Type); ARTclose(); } /* ** Go to the next or last (really previous) article in the group. */ void CMDnextlast(int ac UNUSED, char *av[]) { char *msgid; int save, delta, errcode; bool next; const char *message; /* Trying to read. */ if (GRPcount == 0) { Reply("%d Not in a newsgroup\r\n", NNTP_FAIL_NO_GROUP); return; } /* No syntax to check. Only check authorizations. */ if (!PERMcanread || PERMgroupmadeinvalid) { Reply("%d Read access denied\r\n", PERMcanauthenticate ? NNTP_FAIL_AUTH_NEEDED : NNTP_ERR_ACCESS); return; } if (ARTnumber < ARTlow || ARTnumber > ARThigh) { Reply("%d Current article number %lu is invalid\r\n", NNTP_FAIL_ARTNUM_INVALID, ARTnumber); return; } /* NEXT? */ next = (av[0][0] == 'n' || av[0][0] == 'N'); if (next) { delta = 1; errcode = NNTP_FAIL_NEXT; message = "next"; } else { delta = -1; errcode = NNTP_FAIL_PREV; message = "previous"; } save = ARTnumber; msgid = NULL; do { ARTnumber += delta; if (ARTnumber < ARTlow || ARTnumber > ARThigh) { Reply("%d No %s article to retrieve\r\n", errcode, message); ARTnumber = save; return; } if (!ARTopen(ARTnumber)) continue; msgid = GetHeader("Message-ID", true); ARTclose(); } while (msgid == NULL); Reply("%d %lu %s Article retrieved; request text separately\r\n", NNTP_OK_STAT, ARTnumber, msgid); } /* ** Parse a range (in av[1]) which may be any of the following: ** - An article number. ** - An article number followed by a dash to indicate all following. ** - An article number followed by a dash followed by another article ** number. ** ** In the last case, if the second number is less than the first number, ** then the range contains no articles. ** ** In addition to RFC 3977, we also accept: ** - A dash followed by an article number to indicate all previous. ** - A dash for everything. ** ** ac is the number of arguments in the command: ** LISTGROUP news.software.nntp 12-42 ** gives ac=3 and av[1] should match "12-42" (whence the "av+1" call ** of CMDgetrange). ** ** rp->Low and rp->High will contain the values of the range. ** *DidReply will be true if this function sends an answer. */ bool CMDgetrange(int ac, char *av[], ARTRANGE *rp, bool *DidReply) { char *p; *DidReply = false; if (ac == 1) { /* No arguments, do only current article. */ if (ARTnumber < ARTlow || ARTnumber > ARThigh) { Reply("%d Current article number %lu is invalid\r\n", NNTP_FAIL_ARTNUM_INVALID, ARTnumber); *DidReply = true; return false; } rp->High = rp->Low = ARTnumber; return true; } /* Check the syntax. */ if (!IsValidRange(av[1])) { Reply("%d Syntax error in range\r\n", NNTP_ERR_SYNTAX); *DidReply = true; return false; } /* Got just a single number? */ if ((p = strchr(av[1], '-')) == NULL) { rp->Low = rp->High = atol(av[1]); return true; } /* "-" becomes "\0" and we parse the low water mark. * Note that atol() returns 0 if no valid number * is found at the beginning of *p. */ *p++ = '\0'; rp->Low = atol(av[1]); /* Adjust the low water mark. */ if (rp->Low < ARTlow) rp->Low = ARTlow; /* Parse and adjust the high water mark. * "12-" gives everything from 12 to the end. * We do not bother about "42-12" or "42-0" in this function. */ if ((*p == '\0') || ((rp->High = atol(p)) > ARThigh)) rp->High = ARThigh; p--; *p = '-'; return true; } /* ** Apply virtual hosting to an Xref: field. */ static char * vhost_xref(char *p) { char *space; size_t offset; char *field = NULL; space = strchr(p, ' '); if (space == NULL) { warn("malformed Xref: `%s'", p); goto fail; } offset = space - p; space = strchr(p + offset, ' '); if (space == NULL) { warn("malformed Xref: `%s'", p); goto fail; } field = concat(PERMaccessconf->domain, space, NULL); fail: free(p); return field; } /* ** Dump parts of the overview database with the OVER command. ** The legacy XOVER is also kept, with its specific behaviour. */ void CMDover(int ac, char *av[]) { bool DidReply, HasNotReplied; ARTRANGE range; struct timeval stv, etv; ARTNUM artnum; void *handle; char *data, *r; const char *p, *q; int len, useIOb = 0; TOKEN token; struct cvector *vector = NULL; bool xover, mid; xover = (strcasecmp(av[0], "XOVER") == 0); mid = (ac > 1 && IsValidMessageID(av[1], true)); if (mid && !xover) { /* FIXME: We still do not support OVER MSGID, sorry! */ Reply("%d Overview by message-ID unsupported\r\n", NNTP_ERR_UNAVAILABLE); return; } /* Check the syntax of the arguments first. * We do not accept a message-ID for XOVER, contrary to OVER. A range * is accepted for both of them. */ if (ac > 1 && !IsValidRange(av[1])) { /* It is better to check for a range before a message-ID because * '<' could have been forgotten and the error message should then * report a syntax error in the message-ID. */ if (xover || isdigit((unsigned char) av[1][0]) || av[1][0] == '-') { Reply("%d Syntax error in range\r\n", NNTP_ERR_SYNTAX); return; } else if (!mid) { Reply("%d Syntax error in message-ID\r\n", NNTP_ERR_SYNTAX); return; } } /* Trying to read. */ if (GRPcount == 0) { Reply("%d Not in a newsgroup\r\n", NNTP_FAIL_NO_GROUP); return; } /* Check authorizations. If a range is requested (not a message-ID), * we check whether the group is still readable. */ if (!PERMcanread || (!mid && PERMgroupmadeinvalid)) { Reply("%d Read access denied\r\n", PERMcanauthenticate ? NNTP_FAIL_AUTH_NEEDED : NNTP_ERR_ACCESS); return; } /* Parse range. CMDgetrange() correctly sets the range when * there is no arguments. */ if (!CMDgetrange(ac, av, &range, &DidReply)) if (DidReply) return; if (PERMaccessconf->nnrpdoverstats) { OVERcount++; gettimeofday(&stv, NULL); } if ((handle = (void *)OVopensearch(GRPcur, range.Low, range.High)) == NULL) { /* The response code for OVER is different if a range is provided. * Note that XOVER answers OK. */ if (ac > 1) Reply("%d No articles in %s\r\n", xover ? NNTP_OK_OVER : NNTP_FAIL_ARTNUM_NOTFOUND, av[1]); else Reply("%d No such article number %lu\r\n", xover ? NNTP_OK_OVER : NNTP_FAIL_ARTNUM_NOTFOUND, ARTnumber); if (xover) Printf(".\r\n"); return; } if (PERMaccessconf->nnrpdoverstats) { gettimeofday(&etv, NULL); OVERtime+=(etv.tv_sec - stv.tv_sec) * 1000; OVERtime+=(etv.tv_usec - stv.tv_usec) / 1000; } if (PERMaccessconf->nnrpdoverstats) gettimeofday(&stv, NULL); /* If OVSTATICSEARCH is true, then the data returned by OVsearch is only valid until the next call to OVsearch. In this case, we must use SendIOb because it copies the data. */ OVctl(OVSTATICSEARCH, &useIOb); HasNotReplied = true; while (OVsearch(handle, &artnum, &data, &len, &token, NULL)) { if (PERMaccessconf->nnrpdoverstats) { gettimeofday(&etv, NULL); OVERtime+=(etv.tv_sec - stv.tv_sec) * 1000; OVERtime+=(etv.tv_usec - stv.tv_usec) / 1000; } if (len == 0 || (PERMaccessconf->nnrpdcheckart && !ARTinstorebytoken(token))) { if (PERMaccessconf->nnrpdoverstats) { OVERmiss++; gettimeofday(&stv, NULL); } continue; } if (PERMaccessconf->nnrpdoverstats) { OVERhit++; OVERsize += len; } if (HasNotReplied) { if (ac > 1) Reply("%d Overview information for %s follows\r\n", NNTP_OK_OVER, av[1]); else Reply("%d Overview information for %lu follows\r\n", NNTP_OK_OVER, ARTnumber); fflush(stdout); HasNotReplied = false; } vector = overview_split(data, len, NULL, vector); r = overview_get_standard_header(vector, OVERVIEW_MESSAGE_ID); if (r == NULL) { if (PERMaccessconf->nnrpdoverstats) { gettimeofday(&stv, NULL); } continue; } cache_add(HashMessageID(r), token); free(r); if (VirtualPathlen > 0 && overhdr_xref != -1) { if ((size_t)(overhdr_xref + 1) >= vector->count) { if (PERMaccessconf->nnrpdoverstats) { gettimeofday(&stv, NULL); } continue; } p = vector->strings[overhdr_xref] + sizeof("Xref: ") - 1; while ((p < data + len) && *p == ' ') ++p; q = memchr(p, ' ', data + len - p); if (q == NULL) { if (PERMaccessconf->nnrpdoverstats) { gettimeofday(&stv, NULL); } continue; } /* Copy the virtual path without its final '!'. */ if(useIOb) { SendIOb(data, p - data); SendIOb(VirtualPath, VirtualPathlen - 1); SendIOb(q, len - (q - data)); } else { SendIOv(data, p - data); SendIOv(VirtualPath, VirtualPathlen - 1); SendIOv(q, len - (q - data)); } } else { if(useIOb) SendIOb(data, len); else SendIOv(data, len); } if (PERMaccessconf->nnrpdoverstats) gettimeofday(&stv, NULL); } if (PERMaccessconf->nnrpdoverstats) { gettimeofday(&etv, NULL); OVERtime+=(etv.tv_sec - stv.tv_sec) * 1000; OVERtime+=(etv.tv_usec - stv.tv_usec) / 1000; } if (vector) cvector_free(vector); if (HasNotReplied) { /* The response code for OVER is different if a range is provided. * Note that XOVER answers OK. */ if (ac > 1) Reply("%d No articles in %s\r\n", xover ? NNTP_OK_OVER : NNTP_FAIL_ARTNUM_NOTFOUND, av[1]); else Reply("%d No such article number %lu\r\n", xover ? NNTP_OK_OVER : NNTP_FAIL_ARTNUM_NOTFOUND, ARTnumber); if (xover) Printf(".\r\n"); } else { if(useIOb) { SendIOb(".\r\n", 3); PushIOb(); } else { SendIOv(".\r\n", 3); PushIOv(); } } if (PERMaccessconf->nnrpdoverstats) gettimeofday(&stv, NULL); OVclosesearch(handle); if (PERMaccessconf->nnrpdoverstats) { gettimeofday(&etv, NULL); OVERtime+=(etv.tv_sec - stv.tv_sec) * 1000; OVERtime+=(etv.tv_usec - stv.tv_usec) / 1000; } } /* ** Access specific fields from an article with HDR. ** The legacy XHDR and XPAT are also kept, with their specific behaviours. */ void CMDpat(int ac, char *av[]) { char *p; unsigned long i; ARTRANGE range; bool IsBytes, IsLines; bool IsMetaBytes, IsMetaLines; bool DidReply, HasNotReplied; const char *header; char *pattern; char *text; int Overview; ARTNUM artnum; char buff[SPOOLNAMEBUFF]; void *handle; char *data; int len; TOKEN token; struct cvector *vector = NULL; bool hdr, mid; hdr = (strcasecmp(av[0], "HDR") == 0); mid = (ac > 2 && IsValidMessageID(av[2], true)); /* Check the syntax of the arguments first. */ if (ac > 2 && !IsValidRange(av[2])) { /* It is better to check for a range before a message-ID because * '<' could have been forgotten and the error message should then * report a syntax error in the message-ID. */ if (isdigit((unsigned char) av[2][0]) || av[2][0] == '-') { Reply("%d Syntax error in range\r\n", NNTP_ERR_SYNTAX); return; } else if (!mid) { Reply("%d Syntax error in message-ID\r\n", NNTP_ERR_SYNTAX); return; } } header = av[1]; /* If metadata is asked for, convert it to headers that * the overview database knows. */ IsBytes = (strcasecmp(header, "Bytes") == 0); IsLines = (strcasecmp(header, "Lines") == 0); IsMetaBytes = (strcasecmp(header, ":bytes") == 0); IsMetaLines = (strcasecmp(header, ":lines") == 0); /* Make these changes because our overview database does * not currently know metadata names. */ if (IsMetaBytes) header = "Bytes"; if (IsMetaLines) header = "Lines"; /* We only allow :bytes and :lines for metadata. */ if (!IsMetaLines && !IsMetaBytes) { p = av[1]; p++; if (strncasecmp(header, ":", 1) == 0 && IsValidHeaderName(p)) { Reply("%d Unsupported metadata request\r\n", NNTP_ERR_UNAVAILABLE); return; } else if (!IsValidHeaderName(header)) { Reply("%d Syntax error in header name\r\n", NNTP_ERR_SYNTAX); return; } } /* Trying to read. */ if (GRPcount == 0 && !mid) { Reply("%d Not in a newsgroup\r\n", NNTP_FAIL_NO_GROUP); return; } /* Check authorizations. If a range is requested (not a message-ID), * we check whether the group is still readable. */ if (!PERMcanread || (!mid && PERMgroupmadeinvalid)) { Reply("%d Read access denied\r\n", PERMcanauthenticate ? NNTP_FAIL_AUTH_NEEDED : NNTP_ERR_ACCESS); return; } if (ac > 3) /* Necessarily XPAT. */ pattern = Glom(&av[3]); else pattern = NULL; /* We will only do the loop once. It is just in order to easily break. */ do { /* Message-ID specified? */ if (mid) { /* FIXME: We do not handle metadata requests by message-ID. */ if (hdr && (IsMetaBytes || IsMetaLines)) { Reply("%d Metadata requests by message-ID unsupported\r\n", NNTP_ERR_UNAVAILABLE); break; } p = av[2]; if (!ARTopenbyid(p, &artnum, false)) { Reply("%d No such article\r\n", NNTP_FAIL_MSGID_NOTFOUND); break; } if (!PERMartok()) { ARTclose(); Reply("%d Read access denied for this article\r\n", PERMcanauthenticate ? NNTP_FAIL_AUTH_NEEDED : NNTP_ERR_ACCESS); break; } Reply("%d Header information for %s follows (from the article)\r\n", hdr ? NNTP_OK_HDR : NNTP_OK_HEAD, av[1]); if ((text = GetHeader(av[1], false)) != NULL && (!pattern || uwildmat_simple(text, pattern))) Printf("%s %s\r\n", hdr ? "0" : p, text); else if (hdr) { /* We always have to answer something with HDR. */ Printf("0 \r\n"); } ARTclose(); Printf(".\r\n"); break; } /* Parse range. CMDgetrange() correctly sets the range when * there is no arguments. */ if (!CMDgetrange(ac - 1, av + 1, &range, &DidReply)) if (DidReply) break; /* In overview? */ Overview = overview_index(header, OVextra); HasNotReplied = true; /* Not in overview, we have to fish headers out from the articles. */ if (Overview < 0 || IsBytes || IsLines) { for (i = range.Low; i <= range.High && range.High > 0; i++) { if (!ARTopen(i)) continue; if (HasNotReplied) { Reply("%d Header information for %s follows (from articles)\r\n", hdr ? NNTP_OK_HDR : NNTP_OK_HEAD, av[1]); HasNotReplied = false; } p = GetHeader(header, false); if (p && (!pattern || uwildmat_simple(p, pattern))) { snprintf(buff, sizeof(buff), "%lu ", i); SendIOb(buff, strlen(buff)); SendIOb(p, strlen(p)); SendIOb("\r\n", 2); } else if (hdr) { /* We always have to answer something with HDR. */ snprintf(buff, sizeof(buff), "%lu \r\n", i); SendIOb(buff, strlen(buff)); } ARTclose(); } if (HasNotReplied) { if (hdr) { if (ac > 2) Reply("%d No articles in %s\r\n", NNTP_FAIL_ARTNUM_NOTFOUND, av[2]); else Reply("%d No such article number %lu\r\n", NNTP_FAIL_ARTNUM_NOTFOUND, ARTnumber); } else { Reply("%d No header information for %s follows (from articles)\r\n", NNTP_OK_HEAD, av[1]); Printf(".\r\n"); } break; } else { SendIOb(".\r\n", 3); } PushIOb(); break; } /* Okay then, we can grab values from the overview database. */ handle = (void *)OVopensearch(GRPcur, range.Low, range.High); if (handle == NULL) { if (hdr) { if (ac > 2) Reply("%d No articles in %s\r\n", NNTP_FAIL_ARTNUM_NOTFOUND, av[2]); else Reply("%d No such article number %lu\r\n", NNTP_FAIL_ARTNUM_NOTFOUND, ARTnumber); } else { Reply("%d No header information for %s follows (from overview)\r\n", NNTP_OK_HEAD, av[1]); Printf(".\r\n"); } break; } while (OVsearch(handle, &artnum, &data, &len, &token, NULL)) { if (len == 0 || (PERMaccessconf->nnrpdcheckart && !ARTinstorebytoken(token))) continue; if (HasNotReplied) { Reply("%d Header or metadata information for %s follows (from overview)\r\n", hdr ? NNTP_OK_HDR : NNTP_OK_HEAD, av[1]); HasNotReplied = false; } vector = overview_split(data, len, NULL, vector); if (Overview < OVERVIEW_MAX) { p = overview_get_standard_header(vector, Overview); } else { p = overview_get_extra_header(vector, header); } if (p != NULL) { if (PERMaccessconf->virtualhost && Overview == overhdr_xref) { p = vhost_xref(p); if (p == NULL) { if (hdr) { snprintf(buff, sizeof(buff), "%lu \r\n", artnum); SendIOb(buff, strlen(buff)); } continue; } } if (!pattern || uwildmat_simple(p, pattern)) { snprintf(buff, sizeof(buff), "%lu ", artnum); SendIOb(buff, strlen(buff)); SendIOb(p, strlen(p)); SendIOb("\r\n", 2); } /* No need to have another condition for HDR because * pattern is NULL for it, and p is not NULL here. */ free(p); } else if (hdr) { snprintf(buff, sizeof(buff), "%lu \r\n", artnum); SendIOb(buff, strlen(buff)); } } if (HasNotReplied) { if (hdr) { if (ac > 2) Reply("%d No articles in %s\r\n", NNTP_FAIL_ARTNUM_NOTFOUND, av[2]); else Reply("%d Current article number %lu is invalid\r\n", NNTP_FAIL_ARTNUM_INVALID, ARTnumber); } else { Reply("%d No header or metadata information for %s follows (from overview)\r\n", NNTP_OK_HEAD, av[1]); Printf(".\r\n"); } break; } else { SendIOb(".\r\n", 3); } PushIOb(); OVclosesearch(handle); } while (0); if (vector) cvector_free(vector); if (pattern) free(pattern); } inn-2.6.0/nnrpd/nnrpd.h0000644000175200017520000002241012575023702014332 0ustar iuliusiulius/* $Id: nnrpd.h 9795 2015-03-18 20:31:16Z iulius $ ** ** NetNews Reading Protocol server. */ #include "config.h" #include "portable/macros.h" #include "portable/socket.h" #include #include #include #include #include #ifdef HAVE_SYS_TIME_H # include #endif #include #include "inn/qio.h" #include "inn/libinn.h" #include "inn/nntp.h" #include "inn/paths.h" #include "inn/storage.h" #include "inn/vector.h" #include "inn/timer.h" #ifndef HAVE_SASL_SASL_H # undef HAVE_SASL #endif #ifdef HAVE_SASL #include #include #endif /* ** A range of article numbers. */ typedef struct _ARTRANGE { ARTNUM Low; ARTNUM High; } ARTRANGE; /* ** Access configuration for each reader. */ typedef struct _ACCESSGROUP { char *name; char *key; char *read; char *post; char *users; char *rejectwith; int allownewnews; bool allowihave; int locpost; int allowapproved; int used; int localtime; int strippath; int nnrpdperlfilter; int nnrpdpythonfilter; char *fromhost; char *pathhost; char *organization; char *moderatormailer; char *domain; char *complaints; int spoolfirst; int checkincludedtext; int clienttimeout; unsigned long localmaxartsize; int readertrack; int strippostcc; int addinjectiondate; int addinjectionpostingaccount; int addinjectionpostinghost; char *nnrpdposthost; unsigned long nnrpdpostport; int nnrpdoverstats; int backoff_auth; char *backoff_db; unsigned long backoff_k; unsigned long backoff_postfast; unsigned long backoff_postslow; unsigned long backoff_trigger; int nnrpdcheckart; int nnrpdauthsender; int virtualhost; char *newsmaster; long maxbytespersecond; } ACCESSGROUP; /* ** What line_read returns. */ typedef enum _READTYPE { RTeof, RTok, RTlong, RTtimeout } READTYPE; /* ** Structure used by line_read to keep track of what has been read. */ struct line { char *start; char *where; size_t remaining; size_t allocated; }; /* ** Information about the currently connected client. Eventually, this struct ** may become the repository for all nnrpd global state, or at least the ** state that relates to a particular client connection. */ struct client { char host[SMBUF]; char ip[INET6_ADDRSTRLEN]; unsigned short port; char serverhost[SMBUF]; char serverip[INET6_ADDRSTRLEN]; unsigned short serverport; }; /* ** Information about the schema of the news overview files. */ typedef struct _ARTOVERFIELD { char *Header; int Length; bool NeedsHeader; } ARTOVERFIELD; /* ** Supported timers. If you add new timers to this list, also add them to ** the list of tags in nnrpd.c. */ enum timer { TMR_IDLE = TMR_APPLICATION, /* Server is completely idle. */ TMR_NEWNEWS, /* Executing NEWNEWS command. */ TMR_READART, /* Reading an article (SMretrieve). */ TMR_CHECKART, /* Checking an article (ARTinstorebytoken). */ TMR_NNTPREAD, /* Reading from the peer. */ TMR_NNTPWRITE, /* Writing to the peer. */ TMR_MAX }; #if defined(MAINLINE) #define EXTERN /* NULL */ #else #define EXTERN extern #endif /* defined(MAINLINE) */ EXTERN bool PERMauthorized; EXTERN bool PERMcanauthenticate; #ifdef HAVE_OPENSSL EXTERN bool PERMcanauthenticatewithoutSSL; #endif EXTERN bool PERMcanpost; EXTERN bool PERMcanpostgreeting; EXTERN bool PERMcanread; EXTERN bool PERMneedauth; EXTERN bool PERMspecified; EXTERN bool PERMgroupmadeinvalid; EXTERN ACCESSGROUP *PERMaccessconf; EXTERN bool Tracing; EXTERN bool Offlinepost; EXTERN bool initialSSL; EXTERN char **PERMreadlist; EXTERN char **PERMpostlist; EXTERN struct client Client; EXTERN char Username[SMBUF]; extern char *ACTIVETIMES; extern char *HISTORY; extern char *ACTIVE; extern char *NEWSGROUPS; extern char *NNRPACCESS; EXTERN char PERMuser[SMBUF]; EXTERN FILE *locallog; EXTERN ARTNUM ARTnumber; /* Current article number. */ EXTERN ARTNUM ARThigh; /* Current high number for group. */ EXTERN ARTNUM ARTlow; /* Current low number for group. */ EXTERN unsigned long ARTcount; /* Number of articles in group. */ EXTERN long MaxBytesPerSecond; /* Maximum bytes per sec a client can use, defaults to 0. */ EXTERN long ARTget; EXTERN long ARTgettime; EXTERN long ARTgetsize; EXTERN long OVERcount; /* Number of (X)OVER commands. */ EXTERN long OVERhit; /* Number of (X)OVER records found in overview. */ EXTERN long OVERmiss; /* Number of (X)OVER records found in articles. */ EXTERN long OVERtime; /* Number of ms spent sending (X)OVER data. */ EXTERN long OVERsize; /* Number of bytes of (X)OVER data sent. */ EXTERN long OVERdbz; /* Number of ms spent reading dbz data. */ EXTERN long OVERseek; /* Number of ms spent seeking history. */ EXTERN long OVERget; /* Number of ms spent reading history. */ EXTERN long OVERartcheck; /* Number of ms spent article check. */ EXTERN double IDLEtime; EXTERN unsigned long GRParticles; EXTERN long GRPcount; EXTERN char *GRPcur; EXTERN long POSTreceived; EXTERN long POSTrejected; EXTERN bool BACKOFFenabled; EXTERN char *VirtualPath; EXTERN int VirtualPathlen; EXTERN struct history *History; EXTERN struct line NNTPline; EXTERN struct vector *OVextra; EXTERN int overhdr_xref; EXTERN bool LLOGenable; extern const char *ARTpost(char *article, char *idbuff, bool *permanent); extern void ARTclose(void); extern int TrimSpaces(char *line); extern void InitBackoffConstants(void); extern char *PostRecFilename(char *ip, char *user); extern int LockPostRec(char *path); extern int LockPostRec(char *path); extern void UnlockPostRec(char *path); extern int RateLimit(long *sleeptime, char *path); extern void ExitWithStats(int x, bool readconf) __attribute__ ((__noreturn__)); extern char *GetHeader(const char *header, bool stripspaces); extern void GRPreport(void); extern bool NGgetlist(char ***argvp, char *list); extern bool PERMartok(void); extern void PERMgetaccess(char *nnrpaccess); extern void PERMgetpermissions(void); extern void PERMlogin(char *uname, char *pass, int* code, char *errorstr); extern bool PERMmatch(char **Pats, char **list); extern bool ParseDistlist(char ***argvp, char *list); extern void SetDefaultAccess(ACCESSGROUP*); extern void Reply(const char *fmt, ...) __attribute__((__format__(printf, 1, 2))); extern void Printf(const char *fmt, ...) __attribute__((__format__(printf, 1, 2))); extern void CMDauthinfo (int ac, char** av); extern void CMDcapabilities (int ac, char** av); extern void CMDdate (int ac, char** av); extern void CMDfetch (int ac, char** av); extern void CMDgroup (int ac, char** av); extern void CMDhelp (int ac, char** av); extern void CMDlist (int ac, char** av); extern void CMDmode (int ac, char** av); extern void CMDnewgroups (int ac, char** av); extern void CMDnewnews (int ac, char** av); extern void CMDnextlast (int ac, char** av); extern void CMDover (int ac, char** av); extern void CMDpost (int ac, char** av); extern void CMDquit (int ac, char** av) __attribute__ ((__noreturn__)); extern void CMDxgtitle (int ac, char** av); extern void CMDpat (int ac, char** av); extern void CMD_unimp (int ac, char** av); #ifdef HAVE_OPENSSL extern void CMDstarttls (int ac, char** av); #endif extern bool CMDgetrange(int ac, char *av[], ARTRANGE *rp, bool *DidReply); /* ** Run a resolver or authenticator. The directory is where to look for the ** command if not fully qualified. username and password may be NULL to run ** resolvers. */ char *auth_external(struct client *, const char *command, const char *directory, const char *username, const char *password); void write_buffer(const char *buff, ssize_t len); extern char *HandleHeaders(char *article); extern bool ARTinstorebytoken(TOKEN token); extern int TrackClient(char *client, char* user, size_t len); #ifdef DO_PERL extern void loadPerl(void); extern void perlAccess(char *user, struct vector *access_vec); extern void perlAuthenticate(char *user, char *passwd, int *code, char *errorstring, char*newUser); extern void perlAuthInit(void); #endif /* DO_PERL */ #ifdef DO_PYTHON extern bool PY_use_dynamic; void PY_authenticate(char *path, char *Username, char *Password, int *code, char *errorstring, char *newUser); void PY_access(char* path, struct vector *access_vec, char *Username); void PY_close_python(void); int PY_dynamic(char *Username, char *NewsGroup, int PostFlag, char **reply_message); void PY_dynamic_init (char* file); #endif /* DO_PYTHON */ void line_free(struct line *); void line_init(struct line *); void line_reset(struct line *); READTYPE line_read(struct line *, int, const char **, size_t *, size_t *); #ifdef HAVE_SASL extern sasl_conn_t *sasl_conn; extern int sasl_ssf, sasl_maxout; extern sasl_callback_t sasl_callbacks[]; void SASLauth(int ac, char *av[]); void SASLnewserver(void); #endif /* HAVE_SASL */ inn-2.6.0/nnrpd/auth-ext.c0000644000175200017520000002406212575023702014750 0ustar iuliusiulius/* $Id: auth-ext.c 9696 2014-09-20 06:17:39Z iulius $ ** ** External authenticator support. ** ** Run an external resolver or authenticator to determine the username of the ** client and return that information to INN. For more information about the ** protocol used, see doc/external-auth. */ #include "config.h" #include "clibrary.h" #include #include #include #include "inn/buffer.h" #include "inn/messages.h" #include "inn/vector.h" #include "nnrpd.h" /* Holds the details about a running child process. */ struct process { pid_t pid; int read_fd; /* Read from child. */ int write_fd; /* Write to child. */ int error_fd; }; /* ** Given the client information struct, a string indicating the program to ** run (possibly including arguments) and the directory in which to look for ** the command if it's not fully-qualified, start that program and return a ** struct process providing the PID and file descriptors. */ static struct process * start_process(struct client *client, const char *command, const char *dir) { struct process *process; int rd[2], wr[2], er[2]; pid_t pid; char *path; struct vector *args; /* Parse the command and find the path to the binary. */ args = vector_split_space(command, NULL); path = args->strings[0]; if (path[0] != '/') { path = concatpath(dir, path); } /* Set up the pipes and run the program. */ if (pipe(rd) < 0 || pipe(wr) < 0 || pipe(er) < 0) { syswarn("%s auth: cannot create pipe", client->host); return NULL; } pid = fork(); switch (pid) { case -1: close(rd[0]); close(rd[1]); close(wr[0]); close(wr[1]); close(er[0]); close(er[1]); syswarn("%s auth: cannot fork", client->host); return NULL; case 0: if (dup2(wr[0], 0) < 0 || dup2(rd[1], 1) < 0 || dup2(er[1], 2) < 0) { syswarn("%s auth: cannot set up file descriptors", client->host); _exit(1); } close(rd[0]); close(rd[1]); close(wr[0]); close(wr[1]); close(er[0]); close(er[1]); if (vector_exec(path, args) < 0) { syswarn("%s auth: cannot exec %s", client->host, path); _exit(1); } } /* In the parent. Close excess file descriptors and build return. */ close(rd[1]); close(wr[0]); close(er[1]); process = xmalloc(sizeof(struct process)); process->pid = pid; process->read_fd = rd[0]; process->write_fd = wr[1]; process->error_fd = er[0]; return process; } /* ** Handle a result line from the program which has already been ** nul-terminated at the end of the line. If User: is seen, point ** the second argument at newly allocated space for it. */ static void handle_result(struct client *client UNUSED, const char *line, char **user) { if (strncasecmp(line, "User:", strlen("User:")) == 0) { if (*user != NULL) free(*user); *user = xstrdup(line + strlen("User:")); } } /* ** Handle an error line from the program by logging it. */ static void handle_error(struct client *client, const char *line, char **user UNUSED) { notice("%s auth: program error: %s", client->host, line); } /* ** Read a line of data from the given file descriptor. Return the number of ** bytes read or -1 on buffer overflow. Takes the file descriptor, the ** buffer used for that file descriptor, and the handler function to call for ** each line. Points the fourth argument to a username, if one was found. */ static ssize_t output(struct client *client, int fd, struct buffer *buffer, void (*handler)(struct client *, const char *, char **), char **user) { char *line; char *start; ssize_t count; /* Read the data. */ buffer_compact(buffer); count = buffer_read(buffer, fd); if (buffer->left >= buffer->size - 1) return -1; if (count < 0) return count; /* If reached end of file, process anything left as a line. */ if (count == 0) { if (buffer->left > 0) { buffer->data[buffer->used + buffer->left] = '\0'; handler(client, buffer->data + buffer->used, user); buffer->used += buffer->left; buffer->left = 0; } return count; } /* Otherwise, break what we got up into lines and process each one. */ start = buffer->data + buffer->used; line = memchr(start, '\n', buffer->left); while (line != NULL) { *line = '\0'; if (line > start && line[-1] == '\r') line[-1] = '\0'; handler(client, start, user); buffer->used += line - start + 1; buffer->left -= line - start + 1; start = buffer->data + buffer->used; line = memchr(start, '\n', buffer->left); } return count; } /* ** Wait for the program to produce output. For each bit of output, call ** handle_output with the appropriate handler function. After end of file or ** an error, check and report on the exit status. Returns the username in ** newly allocated memory, or NULL if none was found. ** ** Currently, use a hard-coded five-second timeout for all programs. This ** might need to be configurable later. */ static char * handle_output(struct client *client, struct process *process) { fd_set fds, rfds; struct timeval tv; int maxfd, status, fd; ssize_t count; pid_t result; struct buffer *readbuf; struct buffer *errorbuf; double start, end; bool found; bool killed = false; bool errored = false; char *user = NULL; FD_ZERO(&fds); FD_SET(process->read_fd, &fds); FD_SET(process->error_fd, &fds); maxfd = process->read_fd > process->error_fd ? process->read_fd : process->error_fd; readbuf = buffer_new(); buffer_resize(readbuf, 1024); errorbuf = buffer_new(); buffer_resize(errorbuf, 1024); /* Loop until we get an error or end of file. */ while (1) { tv.tv_sec = 5; tv.tv_usec = 0; rfds = fds; start = TMRnow_double(); status = select(maxfd + 1, &rfds, NULL, NULL, &tv); end = TMRnow_double(); IDLEtime += end - start; if (status <= 0) { if (status == 0) syswarn("%s auth: program timeout", client->host); else { if (errno == EINTR) continue; syswarn("%s auth: select failed", client->host); } killed = true; kill(process->pid, SIGTERM); break; } found = false; count = 0; if (FD_ISSET(process->read_fd, &rfds)) { fd = process->read_fd; count = output(client, fd, readbuf, handle_result, &user); if (count > 0) found = true; } if (count >= 0 && FD_ISSET(process->error_fd, &rfds)) { fd = process->error_fd; count = output(client, fd, errorbuf, handle_error, &user); if (count > 0) { found = true; errored = true; } } if (!found) { close(process->read_fd); close(process->error_fd); if (count < 0) { warn("%s auth: output too long from program", client->host); killed = true; kill(process->pid, SIGTERM); } break; } } buffer_free(readbuf); buffer_free(errorbuf); /* Wait for the program to exit. */ do { result = waitpid(process->pid, &status, 0); } while (result == -1 && errno == EINTR); if (result != process->pid) { syswarn("%s auth: cannot wait for program", client->host); goto fail; } if (WIFEXITED(status) && WEXITSTATUS(status) == 0) return user; else { if (WIFSIGNALED(status) && (!killed || WTERMSIG(status) != SIGTERM)) notice("%s auth: program caught signal %d", client->host, WTERMSIG(status)); else if (WIFEXITED(status) && !errored) notice("%s auth: program exited with status %d", client->host, WEXITSTATUS(status)); } fail: if (user != NULL) free(user); return NULL; } /* ** Append the standard connection information to the provided buffer. */ static void append_client_info(struct client *client, struct buffer *data) { if (*client->host) buffer_append_sprintf(data, "ClientHost: %s\r\n", client->host); if (*client->ip) buffer_append_sprintf(data, "ClientIP: %s\r\n", client->ip); if (client->port != 0) buffer_append_sprintf(data, "ClientPort: %hu\r\n", client->port); if (*client->serverip) buffer_append_sprintf(data, "LocalIP: %s\r\n", client->serverip); if (client->serverport != 0) buffer_append_sprintf(data, "LocalPort: %hu\r\n", client->serverport); } /* ** Execute a program to get the remote username. Takes the client info, the ** command to run, the subdirectory in which to look for programs, and ** optional username and password information to pass to the program. ** Returns the username in newly allocated memory if successful, NULL ** otherwise. */ char * auth_external(struct client *client, const char *command, const char *directory, const char *username, const char *password) { char *user; struct process *process; struct buffer *input; /* Start the program. */ process = start_process(client, command, directory); if (process == NULL) return NULL; /* Feed it data. */ input = buffer_new(); append_client_info(client, input); if (username != NULL) buffer_append_sprintf(input, "ClientAuthname: %s\r\n", username); if (password != NULL) buffer_append_sprintf(input, "ClientPassword: %s\r\n", password); buffer_append_sprintf(input, ".\r\n"); xwrite(process->write_fd, input->data, input->left); close(process->write_fd); buffer_free(input); /* Get the results. */ user = handle_output(client, process); free(process); return user; } inn-2.6.0/nnrpd/commands.c0000644000175200017520000005767512575023702015032 0ustar iuliusiulius/* $Id: commands.c 9850 2015-05-03 15:25:53Z iulius $ ** ** Miscellaneous commands. */ #include "config.h" #include "clibrary.h" #include #include "nnrpd.h" #include "inn/fdflag.h" #include "inn/ov.h" #include "inn/innconf.h" #include "inn/messages.h" #include "inn/version.h" #include "tls.h" #ifdef HAVE_OPENSSL extern bool nnrpd_starttls_done; #endif /* HAVE_OPENSSL */ typedef struct { char *name; ARTNUM high; ARTNUM low; unsigned long count; } GROUPDATA; extern const char *NNRPinstance; /* ** Check after a successful authentication if the currently selected ** newsgroup is still readable. AUTHINFO SASL and STARTTLS do not need ** it because the NNTP protocol is reset after it. ** ** Return true if the group must be made invalid. */ static bool makeGroupInvalid(void) { bool hookpresent = false; char *grplist[2]; /* If no group has been selected yet, it is considered as valid. */ if (GRPcur == NULL) { return false; } #ifdef DO_PYTHON hookpresent = PY_use_dynamic; if (hookpresent) { char *reply; /* Authorize user using Python module method dynamic. */ if (PY_dynamic(PERMuser, GRPcur, false, &reply) < 0) { syslog(L_NOTICE, "PY_dynamic(): authorization skipped due to no Python dynamic method defined"); } else { if (reply != NULL) { syslog(L_TRACE, "PY_dynamic() returned a refuse string for user %s at %s who wants to read %s: %s", PERMuser, Client.host, GRPcur, reply); free(reply); return true; } } } #endif /* DO_PYTHON */ if (!hookpresent) { if (PERMspecified) { grplist[0] = GRPcur; grplist[1] = NULL; if (!PERMmatch(PERMreadlist, grplist)) { return true; } } else { return true; } } if (!hookpresent && !PERMcanread) { return true; } return false; } /* Returns: ** -1 for problem (such as no such authenticator, etc.). ** 1 for authentication succeeded. ** 0 for authentication failed. */ static char *PERMauthstring; static int PERMgeneric(char *av[], char *accesslist, size_t size) { char path[BIG_BUFFER], *fields[6], *p; size_t j; int i, pan[2], status; pid_t pid; struct stat stb; av += 2; PERMcanread = false; PERMcanpost = false; PERMaccessconf->locpost = false; PERMaccessconf->allowapproved = false; PERMaccessconf->allowihave = false; PERMaccessconf->allownewnews = false; if (!*av) { Reply("%d No authenticator provided\r\n", NNTP_ERR_SYNTAX); return -1; } /* Check for ".." (not "../"). Path must not be changed! */ if (strstr(av[0], "..") != NULL) { Reply("%d .. in authenticator %s\r\n", NNTP_ERR_SYNTAX, av[0]); return -1; } /* 502 if authentication will fail. */ if (!PERMcanauthenticate) { if (PERMauthorized && !PERMneedauth) Reply("%d Already authenticated\r\n", NNTP_ERR_ACCESS); else Reply("%d Authentication will fail\r\n", NNTP_ERR_ACCESS); return -1; } if (strchr(INN_PATH_AUTHDIR,'/') == NULL) snprintf(path, sizeof(path), "%s/%s/%s/%s", innconf->pathbin, INN_PATH_AUTHDIR, INN_PATH_AUTHDIR_GENERIC, av[0]); else snprintf(path, sizeof(path), "%s/%s/%s", INN_PATH_AUTHDIR, INN_PATH_AUTHDIR_GENERIC, av[0]); #if !defined(S_IXUSR) && defined(_S_IXUSR) #define S_IXUSR _S_IXUSR #endif /* !defined(S_IXUSR) && defined(_S_IXUSR) */ #if !defined(S_IXUSR) && defined(S_IEXEC) #define S_IXUSR S_IEXEC #endif /* !defined(S_IXUSR) && defined(S_IEXEC) */ if (stat(path, &stb) || !(stb.st_mode&S_IXUSR)) { Reply("%d No such authenticator %s\r\n", NNTP_ERR_UNAVAILABLE, av[0]); return -1; } /* Create a pipe. */ if (pipe(pan) < 0) { Reply("%d Can't pipe %s\r\n", NNTP_FAIL_ACTION, strerror(errno)); syslog(L_FATAL, "can't pipe for %s %m", av[0]); return -1; } for (i = 0; (pid = fork()) < 0; i++) { if (i == (long) innconf->maxforks) { Reply("%d Can't fork %s\r\n", NNTP_FAIL_ACTION, strerror(errno)); syslog(L_FATAL, "can't fork %s %m", av[0]); return -1; } syslog(L_NOTICE, "can't fork %s -- waiting", av[0]); sleep(5); } /* Run the child, with redirection. */ if (pid == 0) { close(STDERR_FILENO); /* Close existing stderr. */ close(pan[PIPE_READ]); /* stderr goes down the pipe. */ if (pan[PIPE_WRITE] != STDERR_FILENO) { if ((i = dup2(pan[PIPE_WRITE], STDERR_FILENO)) != STDERR_FILENO) { syslog(L_FATAL, "can't dup2 %d to %d got %d %m", pan[PIPE_WRITE], STDERR_FILENO, i); _exit(1); } close(pan[PIPE_WRITE]); } fdflag_close_exec(STDIN_FILENO, false); fdflag_close_exec(STDOUT_FILENO, false); fdflag_close_exec(STDERR_FILENO, false); execv(path, av); /* RFC 2980 requires 500 if there are unspecified errors during * the execution of the provided program. */ Reply("%d Program error occurred\r\n", NNTP_ERR_COMMAND); syslog(L_FATAL, "can't execv %s %m", path); _exit(1); } close(pan[PIPE_WRITE]); read(pan[PIPE_READ], path, sizeof(path)); waitpid(pid, &status, 0); if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) return 0; if ((p = strchr(path, '\n')) != NULL) *p = '\0'; if (PERMauthstring) free(PERMauthstring); PERMauthstring = xstrdup(path); //syslog(L_NOTICE, "%s (%ld) returned: %d %s %d\n", av[0], (long) pid, i, path, status); /* Split "host:permissions:user:pass:groups" into fields. */ for (fields[0] = path, j = 0, p = path; *p; p++) if (*p == ':') { *p = '\0'; ++j; if (j < ARRAY_SIZE(fields)) { fields[j] = p + 1; } else { Reply("%d Program error occurred\r\n", NNTP_FAIL_ACTION); syslog(L_FATAL, "over-long response from %s", av[0]); return -1; } } if (j < 4) { Reply("%d Program error occurred\r\n", NNTP_FAIL_ACTION); syslog(L_FATAL, "short response from %s", av[0]); return -1; } PERMcanread = strchr(fields[1], 'R') != NULL; PERMcanpost = strchr(fields[1], 'P') != NULL; PERMaccessconf->allowapproved = strchr(fields[1], 'A') != NULL; PERMaccessconf->locpost = strchr(fields[1], 'L') != NULL; PERMaccessconf->allowihave = strchr(fields[1], 'I') != NULL; PERMaccessconf->allownewnews = strchr(fields[1], 'N') != NULL; snprintf(PERMuser, sizeof(PERMuser), "%s@%s", fields[2], fields[0]); //strlcpy(PERMpass, fields[3], sizeof(PERMpass)); strlcpy(accesslist, fields[4], size); //for (i = 0; fields[i] && i < 5; i++) // syslog(L_NOTICE, "fields[%d] = %s\n", i, fields[i]); return 1; } /* ** The AUTHINFO command. */ void CMDauthinfo(int ac, char *av[]) { static char User[SMBUF]; static char Password[SMBUF]; /* XXX BIG_BUFFER, if changed, should also be changed in perl.c and python.c. */ char accesslist[BIG_BUFFER]; char errorstr[BIG_BUFFER]; int code; if (strcasecmp(av[1], "GENERIC") == 0) { char *logrec = Glom(av); /* Go on parsing the command line. */ ac--; (void) reArgify(av[ac], &av[ac], -1, true); strlcpy(PERMuser, "", sizeof(PERMuser)); /* Arguments are checked by PERMgeneric(). */ switch (PERMgeneric(av, accesslist, sizeof(accesslist))) { case 1: PERMspecified = NGgetlist(&PERMreadlist, accesslist); PERMpostlist = PERMreadlist; syslog(L_NOTICE, "%s auth %s (%s -> %s)", Client.host, PERMuser, logrec, PERMauthstring? PERMauthstring: "" ); Reply("%d Authentication succeeded\r\n", NNTP_OK_AUTHINFO); PERMneedauth = false; PERMauthorized = true; PERMcanauthenticate = false; PERMgroupmadeinvalid = makeGroupInvalid(); free(logrec); return; case 0: syslog(L_NOTICE, "%s bad_auth %s (%s)", Client.host, PERMuser, logrec); /* We keep the right 481 code here instead of the wrong 502 * answer suggested in RFC 2080. */ Reply("%d Authentication failed\r\n", NNTP_FAIL_AUTHINFO_BAD); free(logrec); return; default: /* Lower level (-1) has already issued a reply. */ return; } } else if (strcasecmp(av[1], "SASL") == 0) { #ifdef HAVE_SASL /* Go on parsing the command line. */ ac--; ac += reArgify(av[ac], &av[ac], -1, true); /* Arguments are checked by SASLauth(). */ SASLauth(ac, av); #else Reply("%d SASL authentication unsupported\r\n", NNTP_ERR_SYNTAX); return; #endif /* HAVE_SASL */ } else { /* Each time AUTHINFO USER is used, the new username is cached. */ if (strcasecmp(av[1], "USER") == 0) { /* 502 if authentication will fail. */ if (!PERMcanauthenticate) { if (PERMauthorized && !PERMneedauth) Reply("%d Already authenticated\r\n", NNTP_ERR_ACCESS); else Reply("%d Authentication will fail\r\n", NNTP_ERR_ACCESS); return; } #ifdef HAVE_OPENSSL /* Check whether STARTTLS must be used before trying to authenticate. */ if (PERMcanauthenticate && !PERMcanauthenticatewithoutSSL && !nnrpd_starttls_done) { Reply("%d Encryption required\r\n", NNTP_FAIL_PRIVACY_NEEDED); return; } #endif strlcpy(User, av[2], sizeof(User)); Reply("%d Enter password\r\n", NNTP_CONT_AUTHINFO); return; } /* If it is not AUTHINFO PASS, we do not support the provided subcommand. */ if (strcasecmp(av[1], "PASS") != 0) { Reply("%d Bad AUTHINFO param\r\n", NNTP_ERR_SYNTAX); return; } /* 502 if authentication will fail. */ if (!PERMcanauthenticate) { if (PERMauthorized && !PERMneedauth) Reply("%d Already authenticated\r\n", NNTP_ERR_ACCESS); else Reply("%d Authentication will fail\r\n", NNTP_ERR_ACCESS); return; } #ifdef HAVE_OPENSSL /* Check whether STARTTLS must be used before trying to authenticate. */ if (PERMcanauthenticate && !PERMcanauthenticatewithoutSSL && !nnrpd_starttls_done) { Reply("%d Encryption required\r\n", NNTP_FAIL_PRIVACY_NEEDED); return; } #endif /* AUTHINFO PASS cannot be sent before AUTHINFO USER. */ if (User[0] == '\0') { Reply("%d Authentication commands issued out of sequence\r\n", NNTP_FAIL_AUTHINFO_REJECT); return; } /* There is a cached username and a password is provided. */ strlcpy(Password, av[2], sizeof(Password)); errorstr[0] = '\0'; code = NNTP_FAIL_AUTHINFO_BAD; PERMlogin(User, Password, &code, errorstr); PERMgetpermissions(); /* If authentication is successful. */ if (!PERMneedauth) { syslog(L_NOTICE, "%s user %s", Client.host, PERMuser); if (LLOGenable) { fprintf(locallog, "%s user (%s):%s\n", Client.host, Username, PERMuser); fflush(locallog); } Reply("%d Authentication succeeded\r\n", NNTP_OK_AUTHINFO); PERMneedauth = false; PERMauthorized = true; PERMcanauthenticate = false; PERMgroupmadeinvalid = makeGroupInvalid(); return; } /* For backwards compatibility, we return 481 instead of 502 (which had * the same meaning as 481 in RFC 2980). */ if (code == NNTP_ERR_ACCESS) { code = NNTP_FAIL_AUTHINFO_BAD; } syslog(L_NOTICE, "%s bad_auth", Client.host); /* Return 403 in case the return code is not 481. */ if (errorstr[0] != '\0') { syslog(L_NOTICE, "%s script error str: %s", Client.host, errorstr); Reply("%d %s\r\n", code != NNTP_FAIL_AUTHINFO_BAD ? NNTP_FAIL_ACTION : code, errorstr); } else { Reply("%d Authentication failed\r\n", code != NNTP_FAIL_AUTHINFO_BAD ? NNTP_FAIL_ACTION : code); } } } /* ** The DATE command. Useful mainly in conjunction with NEWNEWS. */ void CMDdate(int ac UNUSED, char *av[] UNUSED) { time_t now; struct tm *gmt; now = time(NULL); gmt = gmtime(&now); if (now == (time_t) -1 || gmt == NULL) { Reply("%d Can't get time, %s\r\n", NNTP_FAIL_ACTION, strerror(errno)); return; } Reply("%d %04d%02d%02d%02d%02d%02d\r\n", NNTP_INFO_DATE, gmt->tm_year + 1900, gmt->tm_mon + 1, gmt->tm_mday, gmt->tm_hour, gmt->tm_min, gmt->tm_sec); } /* ** Handle the MODE command. ** Note that MODE STREAM must return 501 as an unknown MODE variant ** because nnrpd does not implement STREAMING. */ void CMDmode(int ac UNUSED, char *av[]) { if (strcasecmp(av[1], "READER") == 0) /* In the case AUTHINFO has already been successfully used, * nnrpd must answer as a no-op (it still advertises the READER * capability but not MODE-READER). */ Reply("%d %s InterNetNews NNRP server %s ready (%s)\r\n", (PERMcanpost || (PERMcanauthenticate && PERMcanpostgreeting)) ? NNTP_OK_BANNER_POST : NNTP_OK_BANNER_NOPOST, PERMaccessconf->pathhost, INN_VERSION_STRING, (!PERMneedauth && PERMcanpost) ? "posting ok" : "no posting"); else Reply("%d Unknown MODE variant\r\n", NNTP_ERR_SYNTAX); } static int GroupCompare(const void *a1, const void* b1) { const GROUPDATA *a = a1; const GROUPDATA *b = b1; return strcmp(a->name, b->name); } /* ** Display new newsgroups since a given date and time. */ void CMDnewgroups(int ac, char *av[]) { char *p; char *q; QIOSTATE *qp; time_t date; char *grplist[2]; int hi, lo, count, flag; GROUPDATA *grouplist = NULL; GROUPDATA key; GROUPDATA *gd; int listsize = 0; int numgroups = 0; int numfound = 0; int i; bool local = true; /* Check the arguments and parse the date. */ if (ac > 3) { if (strcasecmp(av[3], "GMT") == 0) local = false; else { Reply("%d Syntax error for \"GMT\"\r\n", NNTP_ERR_SYNTAX); return; } } date = parsedate_nntp(av[1], av[2], local); if (date == (time_t) -1) { Reply("%d Bad date\r\n", NNTP_ERR_SYNTAX); return; } /* Log an error if active.times doesn't exist, but don't return an error to the client. The most likely cause of this is a new server installation that's yet to have any new groups created, and returning an error was causing needless confusion. Just return the empty list of groups. */ if ((qp = QIOopen(ACTIVETIMES)) == NULL) { syslog(L_ERROR, "%s can't fopen %s %m", Client.host, ACTIVETIMES); Reply("%d No new newsgroups\r\n", NNTP_OK_NEWGROUPS); Printf(".\r\n"); return; } /* Read the file, ignoring long lines. */ while ((p = QIOread(qp)) != NULL) { if ((q = strchr(p, ' ')) == NULL) continue; *q++ = '\0'; if ((time_t) atol(q) < date) continue; if (!OVgroupstats(p, &lo, &hi, &count, &flag)) continue; if (PERMspecified) { grplist[0] = p; grplist[1] = NULL; if (!PERMmatch(PERMreadlist, grplist)) continue; } else continue; if (grouplist == NULL) { grouplist = xmalloc(1000 * sizeof(GROUPDATA)); listsize = 1000; } if (listsize <= numgroups) { listsize += 1000; grouplist = xrealloc(grouplist, listsize * sizeof(GROUPDATA)); } grouplist[numgroups].high = hi; grouplist[numgroups].low = lo; grouplist[numgroups].count = count; grouplist[numgroups].name = xstrdup(p); numgroups++; } QIOclose(qp); if ((qp = QIOopen(ACTIVE)) == NULL) { syslog(L_ERROR, "%s can't fopen %s %m", Client.host, ACTIVE); Reply("%d Can't open active file\r\n", NNTP_FAIL_ACTION); for (i = 0; i < numgroups; i++) { free(grouplist[i].name); } free(grouplist); return; } qsort(grouplist, numgroups, sizeof(GROUPDATA), GroupCompare); Reply("%d New newsgroups follow\r\n", NNTP_OK_NEWGROUPS); for (numfound = numgroups; (p = QIOread(qp)) && numfound;) { /* p will contain the name of the newsgroup. * When the syntax of the line is not correct, we continue * with the following line. */ if ((q = strchr(p, ' ')) == NULL) continue; *q++ = '\0'; /* Find out the end of the high water mark. */ if ((q = strchr(q, ' ')) == NULL) continue; q++; /* Find out the end of the low water mark. * q will contain the flag of the newsgroup. */ if ((q = strchr(q, ' ')) == NULL) continue; q++; key.name = p; if ((gd = bsearch(&key, grouplist, numgroups, sizeof(GROUPDATA), GroupCompare)) == NULL) continue; Printf("%s %lu %lu %s\r\n", p, gd->high, gd->low, q); numfound--; } for (i = 0; i < numgroups; i++) { free(grouplist[i].name); } free(grouplist); QIOclose(qp); Printf(".\r\n"); } /* ** Handle the POST and IHAVE commands. */ void CMDpost(int ac, char *av[]) { static char *article; static int size; char *p, *q; char *end; int longline; READTYPE r; int i; long l; long sleeptime; char *path; const char *response; char idbuff[SMBUF]; static int backoff_inited = false; bool ihave, permanent; ihave = (strcasecmp(av[0], "IHAVE") == 0); /* Check whether the message-ID is valid for IHAVE. */ if (ihave && ac == 2 && !IsValidMessageID(av[1], true)) { Reply("%d Syntax error in message-ID\r\n", NNTP_ERR_SYNTAX); return; } /* Check authorizations. */ if (ihave && (!PERMaccessconf->allowihave || !PERMcanpost)) { syslog(L_NOTICE, "%s noperm ihave without permission", Client.host); Reply("%d IHAVE command disabled by administrator\r\n", PERMcanauthenticate ? NNTP_FAIL_AUTH_NEEDED : NNTP_ERR_ACCESS); return; } if (!ihave && !PERMcanpost) { syslog(L_NOTICE, "%s noperm post without permission", Client.host); Reply("%d Posting not allowed\r\n", PERMcanauthenticate && PERMcanpostgreeting ? NNTP_FAIL_AUTH_NEEDED : NNTP_FAIL_POST_AUTH); return; } if (!backoff_inited) { /* Exponential posting backoff. */ InitBackoffConstants(); backoff_inited = true; } /* Dave's posting limiter. Limit postings to a certain rate. * And now we support multiprocess rate limits. Questions? * E-mail . */ if (BACKOFFenabled) { /* Acquire lock (this could be in RateLimit but that would * invoke the spaghetti factor). */ if ((path = (char *) PostRecFilename(Client.ip, PERMuser)) == NULL) { Reply("%d Retry later\r\n", ihave ? NNTP_FAIL_IHAVE_DEFER : NNTP_FAIL_POST_AUTH); return; } if (LockPostRec(path) == 0) { syslog(L_ERROR, "%s error write locking '%s'", Client.host, path); Reply("%d Retry later\r\n", ihave ? NNTP_FAIL_IHAVE_DEFER : NNTP_FAIL_POST_AUTH); return; } if (!RateLimit(&sleeptime,path)) { syslog(L_ERROR, "%s can't check rate limit info", Client.host); Reply("%d Retry later\r\n", ihave ? NNTP_FAIL_IHAVE_DEFER : NNTP_FAIL_POST_AUTH); UnlockPostRec(path); return; } else if (sleeptime != 0L) { syslog(L_NOTICE,"%s post sleep time is now %ld", Client.host, sleeptime); sleep(sleeptime); } /* Remove the lock here so that only one nnrpd process does the * backoff sleep at once. Other procs are sleeping for the lock. */ UnlockPostRec(path); } /* End backoff code. */ /* Start at beginning of buffer. */ if (article == NULL) { size = 4096; article = xmalloc(size); } idbuff[0] = 0; if (ihave) { /* Check whether it is a duplicate. */ if (History == NULL) { time_t statinterval = 30; History = HISopen(HISTORY, innconf->hismethod, HIS_RDONLY); if (!History) { syslog(L_NOTICE, "can't initialize history"); Reply("%d NNTP server unavailable; try later\r\n", NNTP_FAIL_TERMINATING); ExitWithStats(1, true); } HISctl(History, HISCTLS_STATINTERVAL, &statinterval); } if (HIScheck(History, av[1])) { Reply("%d Duplicate\r\n", NNTP_FAIL_IHAVE_REFUSE); return; } else { Reply("%d Send it; end with .\r\n", NNTP_CONT_IHAVE); } } else { if ((p = GenerateMessageID(PERMaccessconf->domain)) != NULL) { if (VirtualPathlen > 0) { q = p; if ((p = strchr(p, '@')) != NULL) { *p = '\0'; snprintf(idbuff, sizeof(idbuff), "%s%s@%s>", q, NNRPinstance, PERMaccessconf->domain); } } else { strlcpy(idbuff, p, sizeof(idbuff)); } } Reply("%d Ok, recommended message-ID %s\r\n", NNTP_CONT_POST, idbuff); } fflush(stdout); p = article; end = &article[size]; longline = 0; for (l = 1; ; l++) { size_t len; const char *line; r = line_read(&NNTPline, PERMaccessconf->clienttimeout, &line, &len, NULL); switch (r) { default: warn("%s internal %d in post", Client.host, r); /* FALLTHROUGH */ case RTtimeout: warn("%s timeout in post", Client.host); ExitWithStats(1, false); /* NOTREACHED */ case RTeof: warn("%s EOF in post", Client.host); ExitWithStats(1, false); /* NOTREACHED */ case RTlong: if (longline == 0) longline = l; continue; case RTok: break; } /* If it is the terminator, break out. */ if (strcmp(line, ".") == 0) { break; } /* If they broke our line length limit, there's little point * in processing any more of their input. */ if (longline != 0) { continue; } /* +2 because of the \n\0 we append; note we don't add the 2 * when increasing the size of the buffer as ART_LINE_MALLOC * will always be larger than 2 bytes. */ if ((len + 2) > (size_t)(end - p)) { i = p - article; size += len + 4096; article = xrealloc(article, size); end = &article[size]; p = i + article; } /* Reverse any byte-stuffing. */ if (*line == '.') { ++line; --len; } memcpy(p, line, len); p += len; *p++ = '\n'; *p = '\0'; } if (longline) { warn("%s too long in post", Client.host); Reply("%d Line %d too long\r\n", ihave ? NNTP_FAIL_IHAVE_REJECT : NNTP_FAIL_POST_REJECT, longline); POSTrejected++; return; } /* Send the article to the server. */ response = ARTpost(article, idbuff, &permanent); if (response == NULL) { notice("%s %s ok %s", Client.host, ihave ? "ihave" : "post", idbuff); Reply("%d Article received %s\r\n", ihave ? NNTP_OK_IHAVE : NNTP_OK_POST, idbuff); POSTreceived++; } else { if ((p = strchr(response, '\r')) != NULL) *p = '\0'; if ((p = strchr(response, '\n')) != NULL) *p = '\0'; notice("%s %s failed %s", Client.host, ihave ? "ihave" : "post", response); if (!ihave || permanent) { /* For permanent errors, reject the message. */ Reply("%d %s\r\n", ihave ? NNTP_FAIL_IHAVE_REJECT : NNTP_FAIL_POST_REJECT, response); } else { /* Non-permanent errors only have relevance to IHAVE, for * these we have the error status from the upstream * server to report. It includes the answer code. */ Reply("%s\r\n", response); } POSTrejected++; } } inn-2.6.0/nnrpd/python.c0000644000175200017520000006232312575023702014534 0ustar iuliusiulius/* $Id: python.c 9901 2015-06-14 15:31:12Z iulius $ ** ** python.c: Embed Python in the style of nnrpd's Perl stuff ** (authentication, authorization and dynamic hooks ** only at this point). ** ** Written by Ilya Etingof , 1999. ** ** This code bases on Python work for innd filtering done by ** G.J. Andruk . Also it borrows some ideas from ** TCL/Perl work done by Bob Heiney and Christophe Wolfhugel. ** ** A quick note regarding Python exceptions: functions like ** PyObject_GetAttrString(PyObject *o, const char *attr_name) ** raise an exception when they fail, even though they return NULL. ** And as exceptions accumulate from caller to caller and so on, ** it generates weird issues with Python scripts afterwards. So such ** uses should be checked before. For instance with: ** PyObject_HasAttrString(PyObject *o, const char *attr_name). */ #include "config.h" #ifdef DO_PYTHON /* Python redefines _POSIX_C_SOURCE, so undef it to suppress warnings. */ #undef _POSIX_C_SOURCE /* Make "s#" use Py_ssize_t rather than int. */ #define PY_SSIZE_T_CLEAN /* Python.h must be included after having defined PY_SSIZE_T_CLEAN, * and before any standard headers are included (because Python may * define some pre-processor definitions which affect the standard * headers on some systems). */ #include "Python.h" /* Define Py_ssize_t when it does not exist (Python < 2.5.0). */ #if PY_VERSION_HEX < 0x02050000 typedef int Py_ssize_t; #endif #include "clibrary.h" #include "inn/innconf.h" #include "nnrpd.h" #include "inn/hashtab.h" /* Values relate name of hook to array index. */ #define PYTHONauthen 1 #define PYTHONaccess 2 #define PYTHONdynamic 3 #define PYTHONtypes_max 4 /* Values relate type of method to array index. */ #define PYTHONmain 1 #define PYTHONinit 2 #define PYTHONclose 3 #define PYTHONmethods_max 4 /* Key names for attributes dictionary. */ #define PYTHONhostname "hostname" #define PYTHONipaddress "ipaddress" #define PYTHONport "port" #define PYTHONinterface "interface" #define PYTHONintipaddr "intipaddr" #define PYTHONintport "intport" #define PYTHONuser "user" #define PYTHONpass "pass" #define PYTHONtype "type" #define PYTHONnewsgroup "newsgroup" /* Max number of items in dictionary to pass to auth methods. */ #define _PY_MAX_AUTH_ITEM 10 /* Pointers to external Python objects. */ PyObject *PYAuthObject = NULL; /* Dictionary of params to pass to authentication methods. */ PyObject *PYauthinfo = NULL; PyObject **PYauthitem = NULL; /* Forward declaration. */ static PyObject *PY_set_auth_hook(PyObject *dummy, PyObject *args); void PY_load_python(void); PyObject* PY_setup(int type, int method, char *file); static const void *file_key(const void *p); static bool file_equal(const void *k, const void *p); static void file_free(void *p); static void file_trav(void *data, void* null); bool PythonLoaded = false; /* Structure for storage of attributes for a module file. */ typedef struct PyFile { char *file; bool loaded[PYTHONtypes_max]; PyObject *procs[PYTHONtypes_max][PYTHONmethods_max]; } PyFile; /* Hash for storing files. */ struct hash *files; /* For passing the dynamic module filename from perm.c. */ char* dynamic_file; /* ** Authenticate connecting host by username&password. ** ** code contains the NNTP reply code as returned by Python method ** or -1 if method is not defined. */ void PY_authenticate(char* file, char *Username, char *Password, int *code, char *errorstring, char *newUser) { PyObject *result, *item, *proc; int authnum; int i; char *temp; PY_load_python(); proc = PY_setup(PYTHONauthen, PYTHONmain, file); /* Return if authentication method is not defined. */ if (proc == NULL) *code = -1; /* Initialize PythonAuthObject with connect method specific items. */ authnum = 0; /* Client hostname. */ PYauthitem[authnum] = PyBuffer_FromMemory(Client.host, strlen(Client.host)); PyDict_SetItemString(PYauthinfo, PYTHONhostname, PYauthitem[authnum++]); /* Client IP number. */ PYauthitem[authnum] = PyBuffer_FromMemory(Client.ip, strlen(Client.ip)); PyDict_SetItemString(PYauthinfo, PYTHONipaddress, PYauthitem[authnum++]); /* Client port number. */ PYauthitem[authnum] = PyInt_FromLong(Client.port); PyDict_SetItemString(PYauthinfo, PYTHONport, PYauthitem[authnum++]); /* Server interface the connection comes to. */ PYauthitem[authnum] = PyBuffer_FromMemory(Client.serverhost, strlen(Client.serverhost)); PyDict_SetItemString(PYauthinfo, PYTHONinterface, PYauthitem[authnum++]); /* Server IP number. */ PYauthitem[authnum] = PyBuffer_FromMemory(Client.serverip, strlen(Client.serverip)); PyDict_SetItemString(PYauthinfo, PYTHONintipaddr, PYauthitem[authnum++]); /* Server port number. */ PYauthitem[authnum] = PyInt_FromLong(Client.serverport); PyDict_SetItemString(PYauthinfo, PYTHONintport, PYauthitem[authnum++]); /* Username if known. */ if (Username == NULL) { PYauthitem[authnum] = Py_None; } else { PYauthitem[authnum] = PyBuffer_FromMemory(Username, strlen(Username)); } PyDict_SetItemString(PYauthinfo, PYTHONuser, PYauthitem[authnum++]); /* Password if known. */ if (Password == NULL) { PYauthitem[authnum] = Py_None; } else { PYauthitem[authnum] = PyBuffer_FromMemory(Password, strlen(Password)); } PyDict_SetItemString(PYauthinfo, PYTHONpass, PYauthitem[authnum++]); /* ** Now invoke authenticate method and see if it likes this user. */ result = PyObject_CallFunction(proc, (char *) "O", PYauthinfo); /* Check the response. */ if (result == NULL || !PyTuple_Check(result) || ((PyTuple_Size(result) != 2) && (PyTuple_Size(result) != 3))) { syslog(L_ERROR, "python authenticate method returned wrong result"); Reply("%d Internal error (2). Goodbye!\r\n", NNTP_FAIL_TERMINATING); ExitWithStats(1, true); } /* Get the NNTP response code. */ item = PyTuple_GetItem(result, 0); /* Check the item. */ if (!PyInt_Check(item)) { syslog(L_ERROR, "python authenticate method returned bad NNTP response code"); Reply("%d Internal error (2). Goodbye!\r\n", NNTP_FAIL_TERMINATING); ExitWithStats(1, true); } /* Store the code. */ *code = PyInt_AS_LONG(item); /* Get the error string. */ item = PyTuple_GetItem(result, 1); /* Check the item. */ if (!PyString_Check(item)) { syslog(L_ERROR, "python authenticate method returned bad error string"); Reply("%d Internal error (2). Goodbye!\r\n", NNTP_FAIL_TERMINATING); ExitWithStats(1, true); } /* Store error string. */ temp = PyString_AS_STRING(item); strlcpy(errorstring, temp, BIG_BUFFER); if (PyTuple_Size(result) == 3) { /* Get the username string. */ item = PyTuple_GetItem(result, 2); /* Check the item. */ if (!PyString_Check(item)) { syslog(L_ERROR, "python authenticate method returned bad username string"); Reply("%d Internal error (2). Goodbye!\r\n", NNTP_FAIL_TERMINATING); ExitWithStats(1, true); } /* Store user string. */ temp = PyString_AS_STRING(item); strlcpy(newUser, temp, BIG_BUFFER); } /* Clean up the dictionary object. */ PyDict_Clear(PYauthinfo); /* Clean up dictionary items. */ for (i = 0; i < authnum; i++) { if (PYauthitem[i] != Py_None) { Py_DECREF(PYauthitem[i]); } } /* Log auth result. */ syslog(L_NOTICE, "python authenticate method succeeded, return code %d, error string %s", *code, errorstring); } /* ** Create an access group based on the values returned by the script in file. ** ** The access_vec vector is set. ** If the access group cannot be generated, the connection is closed. */ void PY_access(char* file, struct vector *access_vec, char *Username) { PyObject *result, *key, *value, *proc; char *buffer; int authnum; int i; Py_ssize_t pos; PY_load_python(); proc = PY_setup(PYTHONaccess, PYTHONmain, file); /* Exit if access method is not defined. */ if (proc == NULL) { syslog(L_ERROR, "python access method not defined"); Reply("%d Internal error (3). Goodbye!\r\n", NNTP_FAIL_TERMINATING); ExitWithStats(1, true); } /* Initialize PythonAuthObject with group method specific items. */ authnum = 0; /* Client hostname. */ PYauthitem[authnum] = PyBuffer_FromMemory(Client.host, strlen(Client.host)); PyDict_SetItemString(PYauthinfo, PYTHONhostname, PYauthitem[authnum++]); /* Client IP number. */ PYauthitem[authnum] = PyBuffer_FromMemory(Client.ip, strlen(Client.ip)); PyDict_SetItemString(PYauthinfo, PYTHONipaddress, PYauthitem[authnum++]); /* Client port number. */ PYauthitem[authnum] = PyInt_FromLong(Client.port); PyDict_SetItemString(PYauthinfo, PYTHONport, PYauthitem[authnum++]); /* Server interface the connection comes to. */ PYauthitem[authnum] = PyBuffer_FromMemory(Client.serverhost, strlen(Client.serverhost)); PyDict_SetItemString(PYauthinfo, PYTHONinterface, PYauthitem[authnum++]); /* Server IP number. */ PYauthitem[authnum] = PyBuffer_FromMemory(Client.serverip, strlen(Client.serverip)); PyDict_SetItemString(PYauthinfo, PYTHONintipaddr, PYauthitem[authnum++]); /* Server port number. */ PYauthitem[authnum] = PyInt_FromLong(Client.serverport); PyDict_SetItemString(PYauthinfo, PYTHONintport, PYauthitem[authnum++]); /* Username. */ PYauthitem[authnum] = PyBuffer_FromMemory(Username, strlen(Username)); PyDict_SetItemString(PYauthinfo, PYTHONuser, PYauthitem[authnum++]); /* Password is not known. */ PYauthitem[authnum] = Py_None; PyDict_SetItemString(PYauthinfo, PYTHONpass, PYauthitem[authnum++]); /* ** Now invoke newsgroup access method. */ result = PyObject_CallFunction(proc, (char *) "O", PYauthinfo); /* Check the response. */ if (result == NULL || result == Py_None || !PyDict_Check(result)) { syslog(L_ERROR, "python access method returned wrong result -- expected a dictionary"); Reply("%d Internal error (2). Goodbye!\r\n", NNTP_FAIL_TERMINATING); ExitWithStats(1, true); } /* Resize vector to dictionary length. */ vector_resize(access_vec, PyDict_Size(result) - 1); /* Store dict values in proper format in access vector. */ pos = 0; buffer = xmalloc(BIG_BUFFER); while(PyDict_Next(result, &pos, &key, &value)) { if (!PyString_Check(key)) { syslog(L_ERROR, "python access method return dictionary key %ld not a string", (long int) pos); Reply("%d Internal error (2). Goodbye!\r\n", NNTP_FAIL_TERMINATING); ExitWithStats(1, false); } if (!PyString_Check(value)) { syslog(L_ERROR, "python access method return dictionary value %ld not a string", (long int) pos); Reply("%d Internal error (2). Goodbye!\r\n", NNTP_FAIL_TERMINATING); ExitWithStats(1, false); } strlcpy(buffer, PyString_AsString(key), BIG_BUFFER); strlcat(buffer, ": \"", BIG_BUFFER); strlcat(buffer, PyString_AsString(value), BIG_BUFFER); strlcat(buffer, "\"\n", BIG_BUFFER); vector_add(access_vec, buffer); } free(buffer); /* Clean up the dictionary object. */ PyDict_Clear(PYauthinfo); /* Clean up dictionary items. */ for (i = 0; i < authnum; i++) { if (PYauthitem[i] != Py_None) { Py_DECREF(PYauthitem[i]); } } /* Log auth result. */ syslog(L_NOTICE, "python access method succeeded"); } /* ** Initialize dynamic access control code. */ void PY_dynamic_init (char* file) { dynamic_file = xstrdup(file); PY_use_dynamic = true; } /* ** Determine dynamic user access rights to a given newsgroup. ** ** Return 0 if the requested privilege is granted or a positive value ** and a reply_message pointer initialized with reply message. ** Return negative value if dynamic method is not defined. */ int PY_dynamic(char *Username, char *NewsGroup, int PostFlag, char **reply_message) { PyObject *result, *proc; char *string, *temp; int authnum; int i; PY_load_python(); proc = PY_setup(PYTHONdynamic, PYTHONmain, dynamic_file); /* Return if dynamic method is not defined. */ if (proc == NULL) return -1; /* Initialize PythonAuthObject with group method specific items. */ authnum = 0; /* Client hostname. */ PYauthitem[authnum] = PyBuffer_FromMemory(Client.host, strlen(Client.host)); PyDict_SetItemString(PYauthinfo, PYTHONhostname, PYauthitem[authnum++]); /* Client IP number. */ PYauthitem[authnum] = PyBuffer_FromMemory(Client.ip, strlen(Client.ip)); PyDict_SetItemString(PYauthinfo, PYTHONipaddress, PYauthitem[authnum++]); /* Client port number. */ PYauthitem[authnum] = PyInt_FromLong(Client.port); PyDict_SetItemString(PYauthinfo, PYTHONport, PYauthitem[authnum++]); /* Server interface the connection comes to. */ PYauthitem[authnum] = PyBuffer_FromMemory(Client.serverhost, strlen(Client.serverhost)); PyDict_SetItemString(PYauthinfo, PYTHONinterface, PYauthitem[authnum++]); /* Server IP number. */ PYauthitem[authnum] = PyBuffer_FromMemory(Client.serverip, strlen(Client.serverip)); PyDict_SetItemString(PYauthinfo, PYTHONintipaddr, PYauthitem[authnum++]); /* Server port number. */ PYauthitem[authnum] = PyInt_FromLong(Client.serverport); PyDict_SetItemString(PYauthinfo, PYTHONintport, PYauthitem[authnum++]); /* Username. */ PYauthitem[authnum] = PyBuffer_FromMemory(Username, strlen(Username)); PyDict_SetItemString(PYauthinfo, PYTHONuser, PYauthitem[authnum++]); /* Password is not known. */ PYauthitem[authnum] = Py_None; PyDict_SetItemString(PYauthinfo, PYTHONpass, PYauthitem[authnum++]); /* Assign authentication type. */ PYauthitem[authnum] = PyBuffer_FromMemory((char *)(PostFlag ? "post" : "read"), 4); PyDict_SetItemString(PYauthinfo, PYTHONtype, PYauthitem[authnum++]); /* Newsgroup user tries to access. */ PYauthitem[authnum] = PyBuffer_FromMemory(NewsGroup, strlen(NewsGroup)); PyDict_SetItemString(PYauthinfo, PYTHONnewsgroup, PYauthitem[authnum++]); /* ** Now invoke newsgroup dynamic access method and see if ** it likes this user to access this newsgroup. */ result = PyObject_CallFunction(proc, (char *) "O", PYauthinfo); /* Check the response. */ if (result == NULL || (result != Py_None && !PyString_Check(result))) { syslog(L_ERROR, "python dynamic method (%s access) returned wrong result", PostFlag ? "post" : "read"); Reply("%d Internal error (2). Goodbye!\r\n", NNTP_FAIL_TERMINATING); ExitWithStats(1, false); } /* Get the response string. */ if (result == Py_None) { string = NULL; } else { temp = PyString_AS_STRING(result); string = xstrdup(temp); } /* Clean up the dictionary object. */ PyDict_Clear(PYauthinfo); /* Clean up dictionary items. */ for (i = 0; i < authnum; i++) { if (PYauthitem[i] != Py_None) { Py_DECREF(PYauthitem[i]); } } /* Log auth result. */ syslog(L_NOTICE, "python dynamic method (%s access) succeeded, refuse string: %s", PostFlag ? "post" : "read", string == NULL ? "" : string); /* Initialize reply string. */ if (reply_message != NULL) *reply_message = string; /* Return result. */ return string == NULL ? 0 : 1; } /* ** This runs when nnrpd shuts down. If Python is closed and reopened ** in the same process, files and dynamic_file are reused so they ** must point to NULL. */ void PY_close_python(void) { if (files != NULL) { hash_traverse(files, file_trav, NULL); hash_free(files); files = NULL; } if (dynamic_file != NULL) { free(dynamic_file); dynamic_file = NULL; } } /* ** Traversal function for PY_close_python. */ void file_trav(void *data, void* null UNUSED) { PyFile *fp = data; int j; PyObject *result, *func; for (j = 1; j < PYTHONtypes_max; j++) { if (fp->loaded[j] != false) { func = fp->procs[j][PYTHONclose]; if (func != NULL) { result = PyObject_CallFunction(func, NULL); Py_XDECREF(result); } } } } /* ** Python's syslog module isn't compiled in by default. It's easier ** to do it this way, and the switch block looks pretty in a color ** editor). */ static PyObject * PY_syslog(PyObject *self UNUSED, PyObject *args) { char *loglevel; int levellen; char *logmsg; int msglen; int priority; /* Get loglevel and message. */ if (!PyArg_ParseTuple(args, (char *) "s#s#", &loglevel, &levellen, &logmsg, &msglen)) return NULL; /* Assign syslog priority by abbreviated names. */ switch (*loglevel) { case 'd': case 'D': priority = LOG_DEBUG ; break; case 'i': case 'I': priority = LOG_INFO ; break; case 'n': case 'N': priority = LOG_NOTICE ; break; case 'w': case 'W': priority = LOG_WARNING ; break; case 'e': case 'E': priority = LOG_ERR ; break; case 'c': case 'C': priority = LOG_CRIT ; break; case 'a': case 'A': priority = LOG_ALERT ; break; default: priority = LOG_NOTICE ; } /* Log the message. */ syslog(priority, "python: %s", logmsg); /* Return None. */ Py_INCREF(Py_None); return Py_None; } /* ** Make the internal nnrpd module's functions visible to Python. Python ** annoyingly doesn't use const where appropriate in its structure ** definitions, so we have to add casts for all of the string parameters that ** we're initializing with constant strings. */ #define METHOD(name, func, flags, help) \ { (char *)(name), (func), (flags), (char *)(help) } static PyMethodDef nnrpdPyMethods[] = { METHOD("set_auth_hook", PY_set_auth_hook, METH_VARARGS, ""), METHOD("syslog", PY_syslog, METH_VARARGS, ""), METHOD(NULL, NULL, 0, "") }; /* ** Called by the external module so it can register itself with nnrpd. */ static PyObject * PY_set_auth_hook(PyObject *dummy UNUSED, PyObject *args) { PyObject *result = NULL; PyObject *temp; /* set_auth_hook method should return a pointer to nnrpd auth object. */ if (PyArg_ParseTuple(args, (char *) "O:set_auth_hook", &temp)) { Py_XINCREF(temp); Py_XDECREF(PYAuthObject); PYAuthObject = temp; Py_INCREF(Py_None); result = Py_None; } /* Return a pointer to nnrpd auth method. */ return result; } /* ** Load the Python interpreter. */ void PY_load_python(void) { if (!PythonLoaded) { /* ** Add path for nnrpd module. The environment variable PYTHONPATH ** does it; one can also append innconf->pathfilter to sys.path once ** Python has been initialized. */ setenv("PYTHONPATH", innconf->pathfilter, 1); /* Load up the interpreter ;-O */ Py_Initialize(); /* It makes Python sad when its stdout or stderr are closed. */ if ((fileno(stdout) == -1) || (fileno(stderr) == -1)) PyRun_SimpleString("import sys; sys.stdout=sys.stderr=open('/dev/null', 'a')"); /* See if Python initialized OK. */ if (!Py_IsInitialized()) { syslog(L_ERROR, "python interpreter NOT initialized"); return; } /* Build a module interface to certain nnrpd functions. */ Py_InitModule((char *) "nnrpd", nnrpdPyMethods); /* ** Grab space for authinfo dictionary so we aren't forever ** recreating them. */ PYauthinfo = PyDict_New(); PYauthitem = xcalloc(_PY_MAX_AUTH_ITEM, sizeof(PyObject *)); /* Create hash to store file attributes. */ files = hash_create(4, hash_string, file_key, file_equal, file_free); PythonLoaded = true; syslog(L_NOTICE, "python interpreter initialized OK"); } } /* ** Check that a method exists and is callable. Set up a pointer to ** the corresponding PyObject, or NULL if not found. */ static void PYdefonemethod(PyFile *fp, int type, int method, const char *methname, int realtype) { PyObject **methptr; methptr = &fp->procs[type][method]; /* There is no need to check the existence of methods useless for our realtype. */ if (type == realtype) { /* ** We check with HasAttrString() the existence of the method because ** otherwise, in case it does not exist, an exception is raised by Python, ** although the result of the function is NULL. */ if (PyObject_HasAttrString(PYAuthObject, (char *) methname) == 1) { /* Get a pointer to given method. */ *methptr = PyObject_GetAttrString(PYAuthObject, (char *) methname); } else { *methptr = NULL; } /* See if such method is defined. */ if (*methptr == NULL) syslog(L_NOTICE, "python method %s not found", methname); else { /* See if it is callable. */ if (PyCallable_Check(*methptr) == 0) { syslog(L_ERROR, "python object %s found but not a function", methname); Py_DECREF(*methptr); *methptr = NULL; } } } else { *methptr = NULL; } } /* ** Look up all the known python methods and set up ** pointers to them so that we could call them from nnrpd. */ static void PYdefmethods(PyFile *fp, int realtype) { /* Get a reference to authenticate() method. */ PYdefonemethod(fp, PYTHONauthen, PYTHONmain, "authenticate", realtype); /* Get a reference to authen_init() method. */ PYdefonemethod(fp, PYTHONauthen, PYTHONinit, "authen_init", realtype); /* Get a reference to authen_close() method. */ PYdefonemethod(fp, PYTHONauthen, PYTHONclose, "authen_close", realtype); /* Get a reference to access() method. */ PYdefonemethod(fp, PYTHONaccess, PYTHONmain, "access", realtype); /* Get a reference to access_init() method. */ PYdefonemethod(fp, PYTHONaccess, PYTHONinit, "access_init", realtype); /* Get a reference to access_close() method. */ PYdefonemethod(fp, PYTHONaccess, PYTHONclose, "access_close", realtype); /* Get a reference to dynamic() method. */ PYdefonemethod(fp, PYTHONdynamic, PYTHONmain, "dynamic", realtype); /* Get a reference to dynamic_init() method. */ PYdefonemethod(fp, PYTHONdynamic, PYTHONinit, "dynamic_init", realtype); /* Get a reference to dynamic_close() method. */ PYdefonemethod(fp, PYTHONdynamic, PYTHONclose, "dynamic_close", realtype); } /* ** Called when a python hook is needed -- this gets the scripts hooked in. */ PyObject* PY_setup(int type, int method, char *file) { int i; PyFile *fp; PyObject *result; /* Check to see if this file is in files. */ if ((fp = hash_lookup(files, file)) == NULL) { fp = xmalloc(sizeof(PyFile)); fp->file = xstrdup(file); for (i = 1; i < PYTHONtypes_max; i++) { fp->loaded[i] = false; } /* Load up external module. */ (void) PyImport_ImportModule(file); /* See if nnrpd auth object is defined in auth module. */ if (PYAuthObject == NULL) { syslog(L_ERROR, "python auth object is not defined"); Reply("%d Internal error (3). Goodbye!\r\n", NNTP_FAIL_TERMINATING); PY_close_python(); ExitWithStats(1, false); } else { /* Set up pointers to known Python methods. */ PYdefmethods(fp, type); } hash_insert(files, file, fp); if ((!fp->loaded[type]) && (fp->procs[type][PYTHONinit] != NULL)) { result = PyObject_CallFunction(fp->procs[type][PYTHONinit], NULL); if (result != NULL) { Py_XDECREF(result); } fp->loaded[type] = true; } } return fp->procs[type][method]; } /* ** Return the key (filename) from a file struct, used by the hash table. */ static const void * file_key(const void *p) { const struct PyFile *f = p; return f->file; } /* ** Check to see if a provided key matches the key of a PyFile struct, ** used by the hash table. */ static bool file_equal(const void *k, const void *p) { const char *key = k; const struct PyFile *f = p; return strcmp(key, f->file) == 0; } /* ** Free a file, used by the hash table. */ static void file_free(void *p) { struct PyFile *fp = p; int i, j; free(fp->file); for (i = 1; i < PYTHONtypes_max; i++) { for (j = 1; j < PYTHONmethods_max; j++) { if (fp->procs[i][j] != NULL) { Py_DECREF(fp->procs[i][j]); } } } free(fp); } #endif /* defined(DO_PYTHON) */ inn-2.6.0/nnrpd/misc.c0000644000175200017520000003411112575023702014140 0ustar iuliusiulius/* $Id: misc.c 9563 2013-11-10 13:13:34Z iulius $ ** ** Miscellaneous support routines. */ #include "config.h" #include "clibrary.h" /* Needed on AIX 4.1 to get fd_set and friends. */ #ifdef HAVE_SYS_SELECT_H # include #endif #include "inn/innconf.h" #include "nnrpd.h" #include "tls.h" /* Outside the ifdef so that make depend works even ifndef HAVE_OPENSSL. */ #include "inn/ov.h" #ifdef HAVE_OPENSSL extern SSL *tls_conn; extern int tls_cipher_usebits; extern char *tls_peer_CN; extern bool nnrpd_starttls_done; #endif /* ** Match a list of newsgroup specifiers against a list of newsgroups. ** func is called to see if there is a match. */ bool PERMmatch(char **Pats, char **list) { int i; char *p; int match = false; if (Pats == NULL || Pats[0] == NULL) return true; for ( ; *list; list++) { for (i = 0; (p = Pats[i]) != NULL; i++) { if (p[0] == '!') { if (uwildmat(*list, ++p)) match = false; } else if (uwildmat(*list, p)) match = true; } if (match) /* If we can read it in one group, we can read it, period. */ return true; } return false; } /* ** Check to see if user is allowed to see this article by matching ** Xref: (or Newsgroups:) line. */ bool PERMartok(void) { static char **grplist; char *p, **grp; if (!PERMspecified) return false; if ((p = GetHeader("Xref", true)) == NULL) { /* In case article does not include Xref:. */ if ((p = GetHeader("Newsgroups", true)) != NULL) { if (!NGgetlist(&grplist, p)) /* No newgroups or null entry. */ return true; } else { return true; } } else { /* Skip path element. */ if ((p = strchr(p, ' ')) == NULL) return true; for (p++ ; *p == ' ' ; p++); if (*p == '\0') return true; if (!NGgetlist(&grplist, p)) /* No newgroups or null entry. */ return true; /* Chop ':' and article number. */ for (grp = grplist ; *grp != NULL ; grp++) { if ((p = strchr(*grp, ':')) == NULL) return true; *p = '\0'; } } #ifdef DO_PYTHON if (PY_use_dynamic) { char *reply; /* Authorize user at a Python authorization module. */ if (PY_dynamic(PERMuser, p, false, &reply) < 0) { syslog(L_NOTICE, "PY_dynamic(): authorization skipped due to no Python dynamic method defined"); } else { if (reply != NULL) { syslog(L_TRACE, "PY_dynamic() returned a refuse string for user %s at %s who wants to read %s: %s", PERMuser, Client.host, p, reply); free(reply); return false; } return true; } } #endif /* DO_PYTHON */ return PERMmatch(PERMreadlist, grplist); } /* ** Parse a newsgroups line, return true if there were any. */ bool NGgetlist(char ***argvp, char *list) { char *p; for (p = list; *p; p++) if (*p == ',') *p = ' '; return Argify(list, argvp) != 0; } /********************************************************************* * POSTING RATE LIMITS -- The following code implements posting rate * limits. News clients are indexed by IP number (or PERMuser, see * config file). After a relatively configurable number of posts, the nnrpd * process will sleep for a period of time before posting anything. * * Each time that IP number posts a message, the time of * posting and the previous sleep time is stored. The new sleep time * is computed based on these values. * * To compute the new sleep time, the previous sleep time is, for most * cases multiplied by a factor (backoff_k). * * See inn.conf(5) for how this code works. * *********************************************************************/ /* Defaults are pass through, i.e. not enabled . * NEW for INN 1.8 -- Use the inn.conf file to specify the following: * * backoffk: * backoffpostfast: * backoffpostslow: * backofftrigger: * backoffdb: * backoffauth: * * You may also specify posting backoffs on a per user basis. To do this, * turn on backoffauth. * * Now these are runtime constants. */ static char postrec_dir[SMBUF]; /* Where is the post record directory? */ void InitBackoffConstants(void) { struct stat st; /* Default is not to enable this code. */ BACKOFFenabled = false; /* Read the runtime config file to get parameters. */ if ((PERMaccessconf->backoff_db == NULL) || !(PERMaccessconf->backoff_postslow >= 1L)) return; /* Need this database for backing off. */ strlcpy(postrec_dir, PERMaccessconf->backoff_db, sizeof(postrec_dir)); if (stat(postrec_dir, &st) < 0) { if (ENOENT == errno) { if (!MakeDirectory(postrec_dir, true)) { syslog(L_ERROR, "%s cannot create backoff_db '%s': %s",Client.host,postrec_dir,strerror(errno)); return; } } else { syslog(L_ERROR, "%s cannot stat backoff_db '%s': %s",Client.host,postrec_dir,strerror(errno)); return; } } if (!S_ISDIR(st.st_mode)) { syslog(L_ERROR, "%s backoff_db '%s' is not a directory",Client.host,postrec_dir); return; } BACKOFFenabled = true; return; } /* ** PostRecs are stored in individual files. I didn't have a better ** way offhand, don't want to touch DBZ, and the number of posters is ** small compared to the number of readers. This is the filename corresponding ** to an IP number. */ char * PostRecFilename(char *ip, char *user) { static char buff[SPOOLNAMEBUFF]; char dirbuff[SPOOLNAMEBUFF]; struct in_addr inaddr; unsigned long int addr; unsigned char quads[4]; unsigned int i; if (PERMaccessconf->backoff_auth) { snprintf(buff, sizeof(buff), "%s/%s", postrec_dir, user); return(buff); } if (inet_aton(ip, &inaddr) < 1) { /* If inet_aton() fails, we'll assume it's an IPv6 address. We'll * also assume for now that we're dealing with a limited number of * IPv6 clients so we'll place their files all in the same * directory for simplicity. Someday we'll need to change this to * something more scalable such as DBZ when IPv6 clients become * more popular. */ snprintf(buff, sizeof(buff), "%s/%s", postrec_dir, ip); return(buff); } /* If it's an IPv4 address just fall through. */ addr = ntohl(inaddr.s_addr); for (i=0; i<4; i++) quads[i] = (unsigned char) (0xff & (addr>>(i*8))); snprintf(dirbuff, sizeof(dirbuff), "%s/%03d%03d/%03d", postrec_dir, quads[3], quads[2], quads[1]); if (!MakeDirectory(dirbuff,true)) { syslog(L_ERROR, "%s Unable to create postrec directories '%s': %s", Client.host, dirbuff, strerror(errno)); return NULL; } snprintf(buff, sizeof(buff), "%s/%03d", dirbuff, quads[0]); return(buff); } /* ** Lock the post rec file. Return 1 on lock, 0 on error. */ int LockPostRec(char *path) { char lockname[SPOOLNAMEBUFF]; char temp[SPOOLNAMEBUFF]; int statfailed = 0; snprintf(lockname, sizeof(lockname), "%s.lock", path); for (;; sleep(5)) { int fd; struct stat st; time_t now; fd = open(lockname, O_WRONLY|O_EXCL|O_CREAT, 0600); if (fd >= 0) { /* We got the lock! */ snprintf(temp, sizeof(temp), "pid:%ld\n", (unsigned long) getpid()); write(fd, temp, strlen(temp)); close(fd); return(1); } /* No lock. See if the file is there. */ if (stat(lockname, &st) < 0) { syslog(L_ERROR, "%s cannot stat lock file %s", Client.host, strerror(errno)); if (statfailed++ > 5) return(0); continue; } /* If lockfile is older than the value of * PERMaccessconf->backoff_postslow, remove it. */ statfailed = 0; time(&now); if (now < (time_t) (st.st_ctime + PERMaccessconf->backoff_postslow)) continue; syslog(L_ERROR, "%s removing stale lock file %s", Client.host, lockname); unlink(lockname); } } void UnlockPostRec(char *path) { char lockname[SPOOLNAMEBUFF]; snprintf(lockname, sizeof(lockname), "%s.lock", path); if (unlink(lockname) < 0) { syslog(L_ERROR, "%s can't unlink lock file: %s", Client.host,strerror(errno)) ; } return; } /* ** Get the stored postrecord for that IP. */ static int GetPostRecord(char *path, long *lastpost, long *lastsleep, long *lastn) { static char buff[SMBUF]; FILE *fp; char *s; fp = fopen(path,"r"); if (fp == NULL) { if (errno == ENOENT) { return 1; } syslog(L_ERROR, "%s Error opening '%s': %s", Client.host, path, strerror(errno)); return 0; } if (fgets(buff,SMBUF,fp) == NULL) { syslog(L_ERROR, "%s Error reading '%s': %s", Client.host, path, strerror(errno)); fclose(fp); return 0; } *lastpost = atol(buff); if ((s = strchr(buff,',')) == NULL) { syslog(L_ERROR, "%s bad data in postrec file: '%s'", Client.host, buff); fclose(fp); return 0; } s++; *lastsleep = atol(s); if ((s = strchr(s,',')) == NULL) { syslog(L_ERROR, "%s bad data in postrec file: '%s'", Client.host, buff); fclose(fp); return 0; } s++; *lastn = atol(s); fclose(fp); return 1; } /* ** Store the postrecord for that IP. */ static int StorePostRecord(char *path, time_t lastpost, long lastsleep, long lastn) { FILE *fp; fp = fopen(path,"w"); if (fp == NULL) { syslog(L_ERROR, "%s Error opening '%s': %s", Client.host, path, strerror(errno)); return 0; } fprintf(fp,"%ld,%ld,%ld\n",(long) lastpost,lastsleep,lastn); fclose(fp); return 1; } /* ** Return the proper sleeptime. Return false on error. */ int RateLimit(long *sleeptime, char *path) { time_t now; long prevpost, prevsleep, prevn, n; now = time(NULL); prevpost = 0L; prevsleep = 0L; prevn = 0L; n = 0L; if (!GetPostRecord(path, &prevpost, &prevsleep, &prevn)) { syslog(L_ERROR, "%s can't get post record: %s", Client.host, strerror(errno)); return 0; } /* Just because yer paranoid doesn't mean they ain't out ta get ya. * This is called paranoid clipping. */ if (prevn < 0L) prevn = 0L; if (prevsleep < 0L) prevsleep = 0L; if ((unsigned long) prevsleep > PERMaccessconf->backoff_postfast) prevsleep = PERMaccessconf->backoff_postfast; /* Compute the new sleep time. */ *sleeptime = 0L; if (prevpost <= 0L) { prevpost = 0L; prevn = 1L; } else { n = now - prevpost; if (n < 0L) { syslog(L_NOTICE,"%s previous post was in the future (%ld sec)", Client.host,n); n = 0L; } if ((unsigned long) n < PERMaccessconf->backoff_postfast) { if ((unsigned long) prevn >= PERMaccessconf->backoff_trigger) { *sleeptime = 1 + (prevsleep * PERMaccessconf->backoff_k); } } else if ((unsigned long) n < PERMaccessconf->backoff_postslow) { if ((unsigned long) prevn >= PERMaccessconf->backoff_trigger) { *sleeptime = prevsleep; } } else { prevn = 0L; } prevn++; } *sleeptime = ((*sleeptime) > (long) PERMaccessconf->backoff_postfast) ? (long) PERMaccessconf->backoff_postfast : (*sleeptime); /* This ought to trap this bogon. */ if ((*sleeptime) < 0L) { syslog(L_ERROR,"%s Negative sleeptime detected: %ld, prevsleep: %ld, N: %ld", Client.host, *sleeptime, prevsleep, n); *sleeptime = 0L; } /* Store the postrecord. */ if (!StorePostRecord(path, now, *sleeptime, prevn)) { syslog(L_ERROR, "%s can't store post record: %s", Client.host, strerror(errno)); return 0; } return 1; } #ifdef HAVE_OPENSSL /* ** The STARTTLS command. RFC 4642. */ void CMDstarttls(int ac UNUSED, char *av[] UNUSED) { int result; bool boolval; if (nnrpd_starttls_done) { Reply("%d Already using an active TLS layer\r\n", NNTP_ERR_ACCESS); return; } /* If the client is already authenticated, STARTTLS is not possible. */ if (PERMauthorized && !PERMneedauth && !PERMcanauthenticate) { Reply("%d Already authenticated without the use of a security layer\r\n", NNTP_ERR_ACCESS); return; } result = tls_init(); if (result == -1) { /* No reply because tls_init() has already sent one. */ return; } Reply("%d Begin TLS negotiation now\r\n", NNTP_CONT_STARTTLS); fflush(stdout); /* Must flush our buffers before starting TLS. */ result = tls_start_servertls(0, /* Read. */ 1); /* Write. */ if (result == -1) { /* No reply because we have already sent NNTP_CONT_STARTTLS. * We close the connection. */ ExitWithStats(1, false); } #ifdef HAVE_SASL /* Tell SASL about the negotiated layer. */ result = sasl_setprop(sasl_conn, SASL_SSF_EXTERNAL, (sasl_ssf_t *) &tls_cipher_usebits); if (result != SASL_OK) { syslog(L_NOTICE, "sasl_setprop() failed: CMDstarttls()"); } result = sasl_setprop(sasl_conn, SASL_AUTH_EXTERNAL, tls_peer_CN); if (result != SASL_OK) { syslog(L_NOTICE, "sasl_setprop() failed: CMDstarttls()"); } #endif /* HAVE_SASL */ nnrpd_starttls_done = true; /* Close out any existing article, report group stats. * RFC 4642 requires the reset of any knowledge about the client. */ if (GRPcur) { ARTclose(); GRPreport(); OVctl(OVCACHEFREE, &boolval); free(GRPcur); GRPcur = NULL; if (ARTcount) syslog(L_NOTICE, "%s exit for STARTTLS articles %ld groups %ld", Client.host, ARTcount, GRPcount); GRPcount = 0; PERMgroupmadeinvalid = false; } /* Reset our read buffer so as to prevent plaintext command injection. */ line_reset(&NNTPline); } #endif /* HAVE_OPENSSL */ inn-2.6.0/nnrpd/post.c0000644000175200017520000012556312575023702014206 0ustar iuliusiulius/* $Id: post.c 9924 2015-08-04 12:36:38Z iulius $ ** ** Check article, send it to the local server. */ #include "config.h" #include "clibrary.h" #include "inn/innconf.h" #include "nnrpd.h" #include "inn/ov.h" #include "post.h" #define FLUSH_ERROR(F) (fflush((F)) == EOF || ferror((F))) #define HEADER_DELTA 20 static char *tmpPtr ; static char Error[SMBUF]; static char NGSEPS[] = NG_SEPARATOR; char **OtherHeaders; int OtherCount; bool HeadersModified; static int OtherSize; static const char * const BadDistribs[] = { BAD_DISTRIBS }; /* ** Do not modify the table without also looking at post.h for potential ** changes in the order of the fields. */ HEADER Table[] = { /* Name CanSet Type Size Value */ { "Path", true, HTstd, 0, NULL, NULL, 0 }, { "From", true, HTreq, 0, NULL, NULL, 0 }, { "Newsgroups", true, HTreq, 0, NULL, NULL, 0 }, { "Subject", true, HTreq, 0, NULL, NULL, 0 }, { "Control", true, HTstd, 0, NULL, NULL, 0 }, { "Supersedes", true, HTstd, 0, NULL, NULL, 0 }, { "Followup-To", true, HTstd, 0, NULL, NULL, 0 }, { "Date", true, HTstd, 0, NULL, NULL, 0 }, { "Organization", true, HTstd, 0, NULL, NULL, 0 }, { "Lines", true, HTstd, 0, NULL, NULL, 0 }, { "Sender", true, HTstd, 0, NULL, NULL, 0 }, { "Approved", true, HTstd, 0, NULL, NULL, 0 }, { "Archive", true, HTstd, 0, NULL, NULL, 0 }, { "Distribution", true, HTstd, 0, NULL, NULL, 0 }, { "Expires", true, HTstd, 0, NULL, NULL, 0 }, { "Message-ID", true, HTstd, 0, NULL, NULL, 0 }, { "References", true, HTstd, 0, NULL, NULL, 0 }, { "Reply-To", true, HTstd, 0, NULL, NULL, 0 }, { "NNTP-Posting-Host", false, HTobs, 0, NULL, NULL, 0 }, { "Mime-Version", true, HTstd, 0, NULL, NULL, 0 }, { "Content-Type", true, HTstd, 0, NULL, NULL, 0 }, { "Content-Transfer-Encoding", true, HTstd, 0, NULL, NULL, 0 }, { "X-Trace", false, HTobs, 0, NULL, NULL, 0 }, { "X-Complaints-To", false, HTobs, 0, NULL, NULL, 0 }, { "NNTP-Posting-Date", false, HTobs, 0, NULL, NULL, 0 }, { "Xref", false, HTstd, 0, NULL, NULL, 0 }, { "Injection-Date", true, HTstd, 0, NULL, NULL, 0 }, { "Injection-Info", false, HTstd, 0, NULL, NULL, 0 }, { "Summary", true, HTstd, 0, NULL, NULL, 0 }, { "Keywords", true, HTstd, 0, NULL, NULL, 0 }, { "User-Agent", true, HTstd, 0, NULL, NULL, 0 }, { "Date-Received", false, HTobs, 0, NULL, NULL, 0 }, { "Received", false, HTobs, 0, NULL, NULL, 0 }, { "Posted", false, HTobs, 0, NULL, NULL, 0 }, { "Posting-Version", false, HTobs, 0, NULL, NULL, 0 }, { "Relay-Version", false, HTobs, 0, NULL, NULL, 0 }, { "Cc", true, HTstd, 0, NULL, NULL, 0 }, { "Bcc", true, HTstd, 0, NULL, NULL, 0 }, { "To", true, HTstd, 0, NULL, NULL, 0 }, { "Archived-At", true, HTstd, 0, NULL, NULL, 0 }, { "Also-Control", false, HTobs, 0, NULL, NULL, 0 }, { "Article-Names", false, HTobs, 0, NULL, NULL, 0 }, { "Article-Updates", false, HTobs, 0, NULL, NULL, 0 }, { "See-Also", false, HTobs, 0, NULL, NULL, 0 }, /* The Comments: and Original-Sender: header fields can appear more than once * in the headers of an article. Consequently, we MUST NOT put them here. */ }; HEADER *EndOfTable = ARRAY_END(Table); /* ** Turn any \r or \n in text into spaces. Used to splice back multi-line ** headers into a single line. ** Taken from innd.c. */ static char * Join(char *text) { char *p; for (p = text; *p; p++) if (*p == '\n' || *p == '\r') *p = ' '; return text; } /* ** Return a short name that won't overrun our buffer or syslog's buffer. ** q should either be p, or point into p where the "interesting" part is. ** Taken from innd.c. */ static char * MaxLength(char *p, char *q) { static char buff[80]; unsigned int i; /* Return an empty string when p is NULL. */ if (p == NULL) { *buff = '\0'; return buff; } /* Already short enough? */ i = strlen(p); if (i < sizeof buff - 1) return Join(p); /* Don't want casts to unsigned to go horribly wrong. */ if (q < p || q > p + i) q = p; /* Simple case of just want the beginning? */ if (q == NULL || (size_t)(q - p) < sizeof(buff) - 4) { strlcpy(buff, p, sizeof(buff) - 3); strlcat(buff, "...", sizeof(buff)); } else if ((p + i) - q < 10) { /* Is getting last 10 characters good enough? */ strlcpy(buff, p, sizeof(buff) - 13); strlcat(buff, "...", sizeof(buff) - 10); strlcat(buff, &p[i - 10], sizeof(buff)); } else { /* Not in last 10 bytes, so use double ellipses. */ strlcpy(buff, p, sizeof(buff) - 16); strlcat(buff, "...", sizeof(buff) - 13); strlcat(buff, &q[-5], sizeof(buff) - 3); strlcat(buff, "...", sizeof(buff)); } return Join(buff); } /* ** Trim leading and trailing spaces, return the length of the result. */ int TrimSpaces(char *p) { char *start; for (start = p; ISWHITE(*start) || *start == '\n'; start++) continue; for (p = start + strlen(start); p > start && isspace((unsigned char) p[-1]); p--) continue; return (int)(p - start); } /* ** Mark the end of the header starting at p, and return a pointer ** to the start of the next one or NULL. Handles continuations. */ static char * NextHeader(char *p) { char *q; for (q = p; (p = strchr(p, '\n')) != NULL; p++) { /* Note that '\r\n' has temporarily been internally replaced by '\n'. * Therefore, the count takes it into account (+1, besides the * length (p-q+1) of the string). */ if (p - q + 2 > MAXARTLINELENGTH) { strlcpy(Error, "Header line too long", sizeof(Error)); return NULL; } /* Check if there is a continuation line for the header. */ if (ISWHITE(p[1])) { q = p + 1; continue; } *p = '\0'; return p + 1; } strlcpy(Error, "Article has no body -- just headers", sizeof(Error)); return NULL; } /* ** Strip any headers off the article and dump them into the table. ** On error, return NULL and fill in Error. */ static char * StripOffHeaders(char *article) { char *p; char *q; HEADER *hp; char c; /* Scan through buffer, a header at a time. */ for (p = article; ; ) { /* See if it's a known header. */ c = islower((unsigned char) *p) ? toupper((unsigned char) *p) : *p; for (hp = Table; hp < ARRAY_END(Table); hp++) { if (c == hp->Name[0] && p[hp->Size] == ':' && strncasecmp(p, hp->Name, hp->Size) == 0) { if (hp->Type == HTobs) { snprintf(Error, sizeof(Error), "Obsolete %s: header", hp->Name); return NULL; } if (hp->Value) { snprintf(Error, sizeof(Error), "Duplicate %s: header", hp->Name); return NULL; } hp->Value = &p[hp->Size + 1]; /* '\r\n' is replaced with '\n', and unnecessary to consider * '\r'. */ for (q = &p[hp->Size + 1]; ISWHITE(*q) || (*q == '\n' && ISWHITE(q[1])); q++) { continue; } hp->Body = q; break; } } /* No; add it to the set of other headers. */ if (hp == ARRAY_END(Table)) { if (OtherCount >= OtherSize - 1) { OtherSize += HEADER_DELTA; OtherHeaders = xrealloc(OtherHeaders, OtherSize * sizeof(char *)); } OtherHeaders[OtherCount++] = p; } /* Get start of next header; if it's a blank line, we hit the end. */ if ((p = NextHeader(p)) == NULL) { /* Error set in NextHeader(). */ return NULL; } if (*p == '\n') break; } return p + 1; } /* ** Check the control message, and see if it's legit. Return pointer to ** error message if not. */ static const char * CheckControl(char *ctrl) { char *p; char *q; char save; /* Snip off the first word. */ for (p = ctrl; ISWHITE(*p); p++) continue; for (ctrl = p; *p && !ISWHITE(*p); p++) continue; if (p == ctrl) return "Empty control message"; save = *p; *p = '\0'; if (strcasecmp(ctrl, "cancel") == 0) { for (q = p + 1; ISWHITE(*q); q++) continue; if (*q == '\0') return "Message-ID missing in cancel"; } else if (strcasecmp(ctrl, "checkgroups") == 0 || strcasecmp(ctrl, "ihave") == 0 || strcasecmp(ctrl, "sendme") == 0 || strcasecmp(ctrl, "newgroup") == 0 || strcasecmp(ctrl, "rmgroup") == 0) ; else { snprintf(Error, sizeof(Error), "\"%s\" is not a valid control message", MaxLength(ctrl, ctrl)); return Error; } *p = save; return NULL; } /* ** Check the Distribution: header, and exit on error. */ static const char * CheckDistribution(char *p) { static char SEPS[] = " \t,"; const char * const *dp; if ((p = strtok(p, SEPS)) == NULL) return "Can't parse Distribution: header"; do { for (dp = BadDistribs; *dp; dp++) if (uwildmat(p, *dp)) { snprintf(Error, sizeof(Error), "Illegal distribution \"%s\"", MaxLength(p,p)); return Error; } } while ((p = strtok((char *)NULL, SEPS)) != NULL); return NULL; } /* ** Process all the headers. ** Return NULL if okay, or an error message. */ static const char * ProcessHeaders(char *idbuff, bool needmoderation) { static char datebuff[40]; static char localdatebuff[40]; static char orgbuff[SMBUF]; static char pathidentitybuff[SMBUF]; static char complaintsbuff[SMBUF]; static char postingaccountbuff[SMBUF]; static char postinghostbuff[SMBUF]; static char sendbuff[SMBUF]; static char injectioninfobuff[SMBUF]; static char *newpath = NULL; HEADER *hp; char *p; time_t t, now; const char *error; pid_t pid; bool addvirtual = false; /* Get the current time, used for creating and checking dates. */ now = time(NULL); /* datebuff is used for both Injection-Date: and Date: header fields * so we have to set it now, and it has to be the UTC date. */ if (!makedate(-1, false, datebuff, sizeof(datebuff))) return "Can't generate Date: header"; /* Do some preliminary fix-ups. */ for (hp = Table; hp < ARRAY_END(Table); hp++) { if (!hp->CanSet && hp->Value) { snprintf(Error, sizeof(Error), "Can't set system %s: header", hp->Name); return Error; } if (hp->Value) { hp->Len = TrimSpaces(hp->Value); /* If the header is empty, we just remove it. We do not reject * the article, contrary to what an injecting agent is supposed * to do per Section 3.5 of RFC 5537. (A revision to RFC 5537 * may someday allow again that existing and useful feature.) */ if (hp->Len == 0) hp->Value = hp->Body = NULL; } } /* Set the Injection-Date: header. */ /* Start with this header because it MUST NOT be added in case * the article already contains both Message-ID: and Date: * header fields (possibility of multiple injections). */ if (HDR(HDR__INJECTION_DATE) == NULL) { /* If moderation is needed, do not add an Injection-Date: header field. */ if (!needmoderation && PERMaccessconf->addinjectiondate) { if ((HDR(HDR__MESSAGEID) == NULL) || (HDR(HDR__DATE) == NULL)) { HDR_SET(HDR__INJECTION_DATE, datebuff); } } } else { t = parsedate_rfc5322_lax(HDR(HDR__INJECTION_DATE)); if (t == (time_t) -1) return "Can't parse Injection-Date: header"; if (t > now + DATE_FUZZ) return "Article injected in the future"; } /* If authorized, add the header based on our info. If not authorized, * zap the Sender: header so we don't put out unauthenticated data. */ if (PERMaccessconf->nnrpdauthsender) { if (PERMauthorized && PERMuser[0] != '\0') { p = strchr(PERMuser, '@'); if (p == NULL) { snprintf(sendbuff, sizeof(sendbuff), "%s@%s", PERMuser, Client.host); } else { snprintf(sendbuff, sizeof(sendbuff), "%s", PERMuser); } HDR_SET(HDR__SENDER, sendbuff); } else { HDR_CLEAR(HDR__SENDER); } } /* Set the Date: header. */ if (HDR(HDR__DATE) == NULL) { if (PERMaccessconf->localtime) { if (!makedate(-1, true, localdatebuff, sizeof(localdatebuff))) return "Can't generate local date header"; HDR_SET(HDR__DATE, localdatebuff); } else { HDR_SET(HDR__DATE, datebuff); } } else { t = parsedate_rfc5322_lax(HDR(HDR__DATE)); if (t == (time_t) -1) return "Can't parse Date: header"; if (t > now + DATE_FUZZ) return "Article posted in the future"; /* This check is done for Date: by nnrpd. * innd, as a relaying agent, does not check it when an Injection-Date: * header is present. */ if (innconf->artcutoff != 0) { long cutoff = innconf->artcutoff * 24 * 60 * 60; if (t < now - cutoff) return "Article posted too far in the past (check still " "done for legacy reasons on the Date: header)"; } } /* Newsgroups: is checked later. */ if (HDR(HDR__CONTROL) != NULL) { if ((error = CheckControl(HDR(HDR__CONTROL))) != NULL) return error; } /* Set the Message-ID: header. */ if (HDR(HDR__MESSAGEID) == NULL) { HDR_SET(HDR__MESSAGEID, idbuff); } if (!IsValidMessageID(HDR(HDR__MESSAGEID), true)) { return "Can't parse Message-ID: header"; } /* Set the Path: header. */ if (HDR(HDR__PATH) == NULL || PERMaccessconf->strippath) { /* Note that innd will put host name here for us. */ /* If moderation is needed, do not update the Path: header field. */ if (!needmoderation) HDR_SET(HDR__PATH, (char *) PATHMASTER); else if (PERMaccessconf->strippath) HDR_CLEAR(HDR__PATH); if (VirtualPathlen > 0) addvirtual = true; } else { /* Check that the article has not been injected yet. */ for (p = HDR(HDR__PATH); *p != '\0'; p++) { if (*p == '.' && strncasecmp(p, ".POSTED", 7) == 0 && (p[7] == '.' || p[7] == '!' || p[7] == ' ' || p[7] == '\t' || p[7] == '\r' || p[7] == '\n') && (p == HDR(HDR__PATH) || p[-1] == '!')) { return "Path: header shows a previous injection of the article"; } } /* Check whether the virtual host name is required. */ if ((VirtualPathlen > 0) && (p = strchr(HDR(HDR__PATH), '!')) != NULL) { *p = '\0'; if (strcasecmp(HDR(HDR__PATH), PERMaccessconf->pathhost) != 0) addvirtual = true; *p = '!'; } else if (VirtualPathlen > 0) addvirtual = true; } if (newpath != NULL) free(newpath); if (PERMaccessconf->addinjectionpostinghost) { if (addvirtual) { newpath = concat(VirtualPath, ".POSTED.", Client.host, "!", HDR(HDR__PATH), (char *) 0); } else { newpath = concat(".POSTED.", Client.host, "!", HDR(HDR__PATH), (char *) 0); } } else { if (addvirtual) { newpath = concat(VirtualPath, ".POSTED!", HDR(HDR__PATH), (char *) 0); } else { newpath = concat(".POSTED!", HDR(HDR__PATH), (char *) 0); } } /* If moderation is needed, do not update the Path: header field. */ if (!needmoderation) HDR_SET(HDR__PATH, newpath); /* Reply-To: is left alone. */ /* Sender: is set above. */ /* Check the Expires: header. */ if (HDR(HDR__EXPIRES) && parsedate_rfc5322_lax(HDR(HDR__EXPIRES)) == -1) return "Can't parse Expires: header"; /* References: is left alone. */ /* Control: is checked above. */ /* Check the Distribution: header. */ if ((p = HDR(HDR__DISTRIBUTION)) != NULL) { p = xstrdup(p); error = CheckDistribution(p); free(p); if (error != NULL) return error; } /* Set the Organization: header. */ if (HDR(HDR__ORGANIZATION) == NULL && (p = PERMaccessconf->organization) != NULL) { strlcpy(orgbuff, p, sizeof(orgbuff)); HDR_SET(HDR__ORGANIZATION, orgbuff); } /* Keywords: is left alone. */ /* Summary: is left alone. */ /* Approved: is left alone. */ /* Lines: should not be generated. */ /* Supersedes: is left alone. */ /* Set the Injection-Info: header. */ /* Set the path identity. */ if (VirtualPathlen > 0) { p = PERMaccessconf->domain; } else { if ((p = GetFQDN(PERMaccessconf->domain)) == NULL) { p = (char *) "unknown"; } } snprintf(pathidentitybuff, sizeof(pathidentitybuff), "%s", p); /* Set the posting-account value. */ if (PERMaccessconf->addinjectionpostingaccount && PERMuser[0] != '\0') { snprintf(postingaccountbuff, sizeof(postingaccountbuff), "; posting-account=\"%s\"", PERMuser); } /* Set the posting-host identity. * Check a proper definition of Client.host and Client.ip * (we already saw the case of "localhost:" without IP), * when getpeername fails. */ if ((strlen(Client.host) > 0) || (strlen(Client.ip) > 0)) { if ((strcmp(Client.host, Client.ip) == 0) || (strlen(Client.host) == 0)) { snprintf(postinghostbuff, sizeof(postinghostbuff), "; posting-host=\"%s\"", Client.ip); } else if (strlen(Client.ip) == 0) { snprintf(postinghostbuff, sizeof(postinghostbuff), "; posting-host=\"%s\"", Client.host); } else { snprintf(postinghostbuff, sizeof(postinghostbuff), "; posting-host=\"%s:%s\"", Client.host, Client.ip); } } /* Set the logging-data attribute. */ pid = getpid(); /* Set the mail-complaints-to attribute. */ if ((p = PERMaccessconf->complaints) != NULL) { snprintf(complaintsbuff, sizeof(complaintsbuff), "%s", p); } else { static const char newsmaster[] = NEWSMASTER; if ((p = PERMaccessconf->fromhost) != NULL && strchr(newsmaster, '@') == NULL) { snprintf(complaintsbuff, sizeof(complaintsbuff), "%s@%s", newsmaster, p); } else { snprintf(complaintsbuff, sizeof(complaintsbuff), "%s", newsmaster); } } /* ARTpost() will convert bare LF to CRLF. Do not use CRLF here.*/ snprintf(injectioninfobuff, sizeof(injectioninfobuff), "%s%s%s;\n\tlogging-data=\"%ld\"; mail-complaints-to=\"%s\"", pathidentitybuff, PERMaccessconf->addinjectionpostingaccount && PERMuser[0] != '\0' ? postingaccountbuff : "", PERMaccessconf->addinjectionpostinghost ? postinghostbuff : "", (long) pid, complaintsbuff); /* If moderation is needed, do not add an Injection-Info: header field. */ if (!needmoderation) HDR_SET(HDR__INJECTION_INFO, injectioninfobuff); /* Clear out some headers that should not be here. */ if (PERMaccessconf->strippostcc) { HDR_CLEAR(HDR__CC); HDR_CLEAR(HDR__BCC); HDR_CLEAR(HDR__TO); } /* Now make sure everything is there. */ for (hp = Table; hp < ARRAY_END(Table); hp++) if (hp->Type == HTreq && hp->Value == NULL) { snprintf(Error, sizeof(Error), "Missing required %s: header", hp->Name); return Error; } return NULL; } /* ** See if the user has more included text than new text. Simple-minded, ** but reasonably effective for catching neophyte's mistakes. Son-of-1036 ** says: ** ** NOTE: While encouraging trimming is desirable, the 50% rule imposed ** by some old posting agents is both inadequate and counterproductive. ** Posters do not respond to it by being more selective about quoting; ** they respond by padding short responses, or by using different ** quoting styles to defeat automatic analysis. The former adds ** unnecessary noise and volume, while the latter also defeats more ** useful forms of automatic analysis that reading agents might wish to ** do. ** ** NOTE: At the very least, if a minimum-unquoted quota is being set, ** article bodies shorter than (say) 20 lines, or perhaps articles ** which exceed the quota by only a few lines, should be exempt. This ** avoids the ridiculous situation of complaining about a 5-line ** response to a 6-line quote. ** ** Accordingly, bodies shorter than 20 lines are exempt. A line starting ** with >, |, or : is included text. Decrement the count on lines starting ** with < so that we don't reject diff(1) output. */ static const char * CheckIncludedText(const char *p, int lines) { int i; if (lines < 20) return NULL; for (i = 0; ; p++) { switch (*p) { case '>': i++; break; case '|': i++; break; case ':': i++; break; case '<': i--; break; default: break; } p = strchr(p, '\n'); if (p == NULL) break; } if (i * 2 > lines) return "Article not posted -- more included text than new text"; return NULL; } /* ** Try to mail an article to the moderator of the group. */ static const char * MailArticle(char *group, char *article) { static char CANTSEND[] = "Can't send text to mailer"; FILE *F; HEADER *hp; int i; char *address; char buff[SMBUF]; char *mta; /* Try to get the address first. */ if ((address = GetModeratorAddress(NULL, NULL, group, PERMaccessconf->moderatormailer)) == NULL) { snprintf(Error, sizeof(Error), "No mailing address for \"%s\" -- %s", group, "ask your news administrator to fix this"); free(group); return Error; } free(group); /* Now build up the command (ignore format/argument mismatch errors, * in case %s isn't in inconf->mta) and send the headers. */ if ((mta = innconf->mta) == NULL) return "Can't start mailer -- mta not set"; #pragma GCC diagnostic ignored "-Wformat-nonliteral" snprintf(buff, sizeof(buff), innconf->mta, address); #pragma GCC diagnostic warning "-Wformat-nonliteral" if ((F = popen(buff, "w")) == NULL) return "Can't start mailer"; fprintf(F, "To: %s\n", address); if (FLUSH_ERROR(F)) { pclose(F); return CANTSEND; } /* Write the headers, a blank line, then the article. */ for (hp = Table; hp < ARRAY_END(Table); hp++) if (hp->Value) { if (*hp->Value == ' ' || *hp->Value == '\t') fprintf(F, "%s:%s\n", hp->Name, hp->Value); else fprintf(F, "%s: %s\n", hp->Name, hp->Value); if (FLUSH_ERROR(F)) { pclose(F); return CANTSEND; } } for (i = 0; i < OtherCount; i++) { fprintf(F, "%s\n", OtherHeaders[i]); if (FLUSH_ERROR(F)) { pclose(F); return CANTSEND; } } fprintf(F, "\n"); i = strlen(article); if (fwrite(article, 1, i, F) != (size_t)i) return "Can't send article"; if (FLUSH_ERROR(F)) { pclose(F); return CANTSEND; } i = pclose(F); if (i) { snprintf(Error, sizeof(Error), "Mailer exited with status %d -- %s", i, "Article might not have been mailed"); return Error; } return NULL; } /* ** Check the newsgroups and make sure they're all valid, that none are ** moderated, etc. */ static const char * ValidNewsgroups(char *hdr, char **modgroup) { static char distbuff[SMBUF]; char *groups; char *p; bool approved; struct _DDHANDLE *h; char *grplist[2]; bool IsNewgroup; bool FoundOne; int flag; bool hookpresent = false; #ifdef DO_PYTHON hookpresent = PY_use_dynamic; #endif /* DO_PYTHON */ p = HDR(HDR__CONTROL); IsNewgroup = (p && strncasecmp(p, "newgroup", 8) == 0); groups = xstrdup(hdr); if ((p = strtok(groups, NGSEPS)) == NULL) { free(groups); return "Can't parse Newsgroups: header"; } Error[0] = '\0'; /* Reject all articles with Approved: headers unless the user is allowed to * add them, even to unmoderated or local groups. We want to reject them * to unmoderated groups in case there's a disagreement of opinion * between various sites as to the moderation status. */ approved = HDR(HDR__APPROVED) != NULL; if (approved && !PERMaccessconf->allowapproved) { snprintf(Error, sizeof(Error), "You are not allowed to approve postings"); } FoundOne = false; h = DDstart((FILE *)NULL, (FILE *)NULL); do { if (innconf->mergetogroups && strncmp(p, "to.", 3) == 0) p = (char *) "to"; if (!hookpresent && PERMspecified) { grplist[0] = p; grplist[1] = NULL; if (!PERMmatch(PERMpostlist, grplist)) { snprintf(Error, sizeof(Error), "You are not allowed to post to %s\r\n", p); } } if (!OVgroupstats(p, NULL, NULL, NULL, &flag)) continue; FoundOne = true; DDcheck(h, p); switch (flag) { case NF_FLAG_OK: #ifdef DO_PYTHON if (PY_use_dynamic) { char *reply; /* Authorize user using Python module method dynamic. */ if (PY_dynamic(PERMuser, p, true, &reply) < 0) { syslog(L_NOTICE, "PY_dynamic(): authorization skipped due to no Python dynamic method defined"); } else { if (reply != NULL) { syslog(L_TRACE, "PY_dynamic() returned a refuse string for user %s at %s who wants to post to %s: %s", PERMuser, Client.host, p, reply); snprintf(Error, sizeof(Error), "%s\r\n", reply); free(reply); break; } } } #endif /* DO_PYTHON */ break; case NF_FLAG_MODERATED: if (!approved && modgroup != NULL && !*modgroup) *modgroup = xstrdup(p); break; case NF_FLAG_IGNORE: case NF_FLAG_JUNK: case NF_FLAG_NOLOCAL: if (!PERMaccessconf->locpost) snprintf(Error, sizeof(Error), "Postings to \"%s\" are not allowed here", p); break; case NF_FLAG_ALIAS: snprintf(Error, sizeof(Error), "The newsgroup \"%s\" has been renamed\n", p); break; } } while ((p = strtok((char *)NULL, NGSEPS)) != NULL); free(groups); if (!FoundOne && !IsNewgroup) snprintf(Error, sizeof(Error), "No valid newsgroups in \"%s\"", MaxLength(hdr,hdr)); if (Error[0]) { tmpPtr = DDend(h); free(tmpPtr); if (modgroup != NULL && *modgroup != NULL) { free(*modgroup); *modgroup = NULL; } return Error; } p = DDend(h); if (HDR(HDR__DISTRIBUTION) == NULL && *p) { strlcpy(distbuff, p, sizeof(distbuff)); HDR_SET(HDR__DISTRIBUTION, distbuff); } free(p); return NULL; } /* ** Send a QUIT message to the server, eat its reply. */ static void SendQuit(FILE *FromServer, FILE *ToServer) { char buff[NNTP_MAXLEN_COMMAND]; fprintf(ToServer, "QUIT\r\n"); fflush(ToServer); fclose(ToServer); fgets(buff, sizeof buff, FromServer); fclose(FromServer); } /* ** Offer the article to the server, return its reply. */ static int OfferArticle(char *buff, int buffsize, FILE *FromServer, FILE *ToServer) { /* We have a valid message-ID here (checked beforehand). */ fprintf(ToServer, "IHAVE %s\r\n", HDR(HDR__MESSAGEID)); if (FLUSH_ERROR(ToServer) || fgets(buff, buffsize, FromServer) == NULL) { snprintf(buff, buffsize, "Can't send %s to server, %s", "IHAVE", strerror(errno)); return -1; } return atoi(buff); } /* ** Spool article to temp file. */ static const char * SpoolitTo(char *article, char *err, char *SpoolDir) { static char CANTSPOOL[NNTP_MAXLEN_COMMAND+2]; HEADER *hp; FILE *F = NULL; int i, fd; char *tmpspool = NULL; char *spoolfile = NULL; char *q; /* Initialize the returned error message. */ snprintf(CANTSPOOL, sizeof(CANTSPOOL), "%s and can't write text to local spool file", err); /* Try to write it to the spool dir. */ tmpspool = concatpath(SpoolDir, ".XXXXXX"); fd = mkstemp(tmpspool); if (fd < 0) { syslog(L_FATAL, "can't create temporary spool file %s %m", tmpspool); goto fail; } F = fdopen(fd, "w"); if (F == NULL) { syslog(L_FATAL, "can't open %s %m", tmpspool); goto fail; } fchmod(fileno(F), BATCHFILE_MODE); /* Write the headers and a blank line. */ for (hp = Table; hp < ARRAY_END(Table); hp++) if (hp->Value) { q = xstrndup(hp->Value, hp->Body - hp->Value + hp->Len); if (*hp->Value == ' ' || *hp->Value == '\t') fprintf(F, "%s:%s\n", hp->Name, q); else fprintf(F, "%s: %s\n", hp->Name, q); if (FLUSH_ERROR(F)) { fclose(F); free(q); goto fail; } free(q); } for (i = 0; i < OtherCount; i++) { fprintf(F, "%s\n", OtherHeaders[i]); if (FLUSH_ERROR(F)) { fclose(F); goto fail; } } fprintf(F, "\n"); /* Write the article body. */ i = strlen(article); if (fwrite(article, 1, i, F) != (size_t)i) { fclose(F); goto fail; } /* Flush and catch any errors. */ if (fclose(F)) goto fail; /* Rename the spool file to something rnews will pick up. */ spoolfile = concatpath(SpoolDir, "XXXXXX"); fd = mkstemp(spoolfile); if (fd < 0) { syslog(L_FATAL, "can't create spool file %s %m", spoolfile); goto fail; } close(fd); if (rename(tmpspool, spoolfile) < 0) { syslog(L_FATAL, "can't rename %s %s %m", tmpspool, spoolfile); goto fail; } /* Article has been spooled. */ free(tmpspool); free(spoolfile); return NULL; fail: if (tmpspool != NULL) free(tmpspool); if (spoolfile != NULL) free(spoolfile); return CANTSPOOL; } /* ** Spool article to temp file. */ static const char * Spoolit(char *article, char *err) { return SpoolitTo(article, err, innconf->pathincoming); } static char * Towire(char *p) { char *q, *r, *s; int curlen, len = BIG_BUFFER; for (r = p, q = s = xmalloc(len); *r != '\0' ;) { curlen = q - s; if (curlen + 3 > len) { len += BIG_BUFFER; s = xrealloc(s, len); q = s + curlen; } if (*r == '\n') { if (r > p) { if (*(r - 1) != '\r') *q++ = '\r'; } else { /* This should not happen. */ free(s); return NULL; } } *q++ = *r++; } curlen = q - s; if (curlen + 1 > len) { len++; s = xrealloc(s, len); q = s + curlen; } *q = '\0'; return s; } /* ** The main function which handles POST and IHAVE. */ const char * ARTpost(char *article, char *idbuff, bool *permanent) { int i; char *p, *q; char *next; HEADER *hp; FILE *ToServer; FILE *FromServer; char buff[NNTP_MAXLEN_COMMAND + 2], frombuf[SMBUF]; char *modgroup = NULL; const char *error; char *TrackID; char *DirTrackID; FILE *ftd; /* Assume errors are permanent, until we discover otherwise. */ *permanent = true; /* Set up the other headers list. */ if (OtherHeaders == NULL) { OtherSize = HEADER_DELTA; OtherHeaders = xmalloc(OtherSize * sizeof(char *)); } /* Basic processing. */ OtherCount = 0; for (hp = Table; hp < ARRAY_END(Table); hp++) { hp->Size = strlen(hp->Name); hp->Value = hp->Body = NULL; } if ((article = StripOffHeaders(article)) == NULL) return Error; for (i = 0, p = article; p; i++, p = next + 1) if ((next = strchr(p, '\n')) == NULL) break; if (PERMaccessconf->checkincludedtext) { if ((error = CheckIncludedText(article, i)) != NULL) return error; } /* modgroup is set when moderated newsgroups are found in the * Newsgroups: header field, and the article does not contain * an Approved: header field. * Therefore, moderation will be needed. * * Be sure to check that a Newsgroups: header field exists * because ProcessHeaders() still has not been called. It would * have rejected the message. */ if (HDR(HDR__NEWSGROUPS) != NULL) { if ((error = ValidNewsgroups(HDR(HDR__NEWSGROUPS), &modgroup)) != NULL) return error; } if ((error = ProcessHeaders(idbuff, modgroup != NULL)) != NULL) { if (modgroup != NULL) free(modgroup); return error; } if (i == 0 && HDR(HDR__CONTROL) == NULL) { if (modgroup != NULL) free(modgroup); return "Article is empty"; } strlcpy(frombuf, HDR(HDR__FROM), sizeof(frombuf)); /* Unfold the From: header field. */ for (p = frombuf; p < frombuf + sizeof(frombuf); ) if ((p = strchr(p, '\n')) == NULL) break; else *p++ = ' '; /* Try to rewrite the From: header field in a cleaner format. */ HeaderCleanFrom(frombuf); /* Now perform basic checks of the From: header field. * Pass leading '@' chars because they are not part of an address. */ p = frombuf; while (*p == '@') { p++; } p = strchr(p, '@'); if (p != NULL) { p = strrchr(p+1, '.'); if (p == NULL) { if (modgroup) free(modgroup); return "From: address not in Internet syntax"; } } else { if (modgroup) free(modgroup); return "From: address not in Internet syntax"; } if ((p = HDR(HDR__FOLLOWUPTO)) != NULL && strcmp(p, "poster") != 0 && (error = ValidNewsgroups(p, (char **)NULL)) != NULL) { if (modgroup) free(modgroup); return error; } if ((PERMaccessconf->localmaxartsize != 0) && (strlen(article) > PERMaccessconf->localmaxartsize)) { snprintf(Error, sizeof(Error), "Article is bigger than local limit of %lu bytes\n", PERMaccessconf->localmaxartsize); if (modgroup) free(modgroup); return Error; } #if defined(DO_PERL) /* Calls the Perl subroutine for headers management. */ p = PERMaccessconf->nnrpdperlfilter ? HandleHeaders(article) : NULL; if (p != NULL) { char SDir[255]; if (idbuff) { if (modgroup) snprintf(idbuff, SMBUF, "(mailed to moderator for %s)", modgroup); else if (HDR(HDR__MESSAGEID) != idbuff) { strlcpy(idbuff, HDR(HDR__MESSAGEID), SMBUF); } } if (strncmp(p, "DROP", 4) == 0) { syslog(L_NOTICE, "%s post failed %s", Client.host, p); if (modgroup) free(modgroup); return NULL; } else if (strncmp(p, "SPOOL", 5) == 0) { syslog(L_NOTICE, "%s post failed %s", Client.host, p); strlcpy(SDir, innconf->pathincoming, sizeof(SDir)); if (modgroup) { free(modgroup); strlcat(SDir, "/spam/mod", sizeof(SDir)); return SpoolitTo(article, p, SDir); } else { strlcat(SDir, "/spam", sizeof(SDir)); return SpoolitTo(article, p, SDir); } } else if (strncmp(p, "CLOSE", 5) == 0) { syslog(L_NOTICE, "%s post failed %s", Client.host, p); Reply("%d NNTP server unavailable; no posting\r\n", NNTP_FAIL_TERMINATING); POSTrejected++; ExitWithStats(1, true); } else { if (modgroup) free(modgroup); return p; } } #endif /* defined(DO_PERL) */ /* Handle mailing to moderated groups. */ if (modgroup) { if (idbuff != NULL) { const char *retstr; retstr = MailArticle(modgroup, article); /* MailArticle frees modgroup. */ strlcpy (idbuff, "(mailed to moderator)", SMBUF); return retstr; } return MailArticle(modgroup, article); } if (idbuff != NULL && HDR(HDR__MESSAGEID) != idbuff) { strlcpy(idbuff, HDR(HDR__MESSAGEID), SMBUF); } if (PERMaccessconf->spoolfirst) return Spoolit(article, Error); if (Offlinepost) return Spoolit(article,Error); /* Open a local connection to the server. */ if (PERMaccessconf->nnrpdposthost != NULL) i = NNTPconnect(PERMaccessconf->nnrpdposthost, PERMaccessconf->nnrpdpostport, &FromServer, &ToServer, buff, sizeof(buff)); else { #if defined(HAVE_UNIX_DOMAIN_SOCKETS) i = NNTPlocalopen(&FromServer, &ToServer, buff, sizeof(buff)); #else i = NNTPremoteopen(innconf->port, &FromServer, &ToServer, buff, sizeof(buff)); #endif /* defined(HAVE_UNIX_DOMAIN_SOCKETS) */ } /* If we cannot open the connection, initialize the error message and * attempt to recover from this by spooling it locally. */ if (i < 0) { if (buff[0]) strlcpy(Error, buff, sizeof(Error)); else { snprintf(Error, sizeof(Error), "Can't send connect request to server, %s", strerror(errno)); } return Spoolit(article,Error); } if (Tracing) syslog(L_TRACE, "%s post_connect %s", Client.host, PERMaccessconf->nnrpdposthost ? PERMaccessconf->nnrpdposthost : "localhost"); /* The code below ignores too many return values for my tastes. At least * they are all inside cases that are most likely never going to happen -- * for example, if the server crashes. */ /* Offer article to server. */ i = OfferArticle(buff, (int)sizeof buff, FromServer, ToServer); if (i == NNTP_FAIL_AUTH_NEEDED) { /* Send authorization. */ if (NNTPsendpassword(PERMaccessconf->nnrpdposthost, FromServer, ToServer) < 0) { snprintf(Error, sizeof(Error), "Can't authorize with %s", PERMaccessconf->nnrpdposthost ? PERMaccessconf->nnrpdposthost : "innd"); return Spoolit(article,Error); } i = OfferArticle(buff, (int)sizeof buff, FromServer, ToServer); } if (i != NNTP_CONT_IHAVE) { strlcpy(Error, buff, sizeof(Error)); SendQuit(FromServer, ToServer); if (i == NNTP_FAIL_IHAVE_REJECT || i == NNTP_FAIL_IHAVE_DEFER) { *permanent = false; } /* As the syntax of the IHAVE command sent by nnrpd is valid, * the only valid case of response is a refusal. */ if (i != NNTP_FAIL_IHAVE_REFUSE) return Spoolit(article, Error); return Error; } if (Tracing) syslog(L_TRACE, "%s post starting", Client.host); /* Write the headers and a blank line. */ for (hp = Table; hp < ARRAY_END(Table); hp++) if (hp->Value) { q = xstrndup(hp->Value, hp->Body - hp->Value + hp->Len); if (strchr(q, '\n') != NULL) { if ((p = Towire(q)) != NULL) { /* There is no white space, if hp->Value and hp->Body are the same. */ if (*hp->Value == ' ' || *hp->Value == '\t') fprintf(ToServer, "%s:%s\r\n", hp->Name, p); else fprintf(ToServer, "%s: %s\r\n", hp->Name, p); free(p); } } else { /* There is no white space, if hp->Value and hp->Body are the same. */ if (*hp->Value == ' ' || *hp->Value == '\t') fprintf(ToServer, "%s:%s\r\n", hp->Name, q); else fprintf(ToServer, "%s: %s\r\n", hp->Name, q); } free(q); } for (i = 0; i < OtherCount; i++) { if (strchr(OtherHeaders[i], '\n') != NULL) { if ((p = Towire(OtherHeaders[i])) != NULL) { fprintf(ToServer, "%s\r\n", p); free(p); } } else { fprintf(ToServer, "%s\r\n", OtherHeaders[i]); } } fprintf(ToServer, "\r\n"); if (FLUSH_ERROR(ToServer)) { snprintf(Error, sizeof(Error), "Can't send headers to server, %s", strerror(errno)); fclose(FromServer); fclose(ToServer); return Spoolit(article, Error); } /* Send the article, get the server's reply. */ if (NNTPsendarticle(article, ToServer, true) < 0 || fgets(buff, sizeof buff, FromServer) == NULL) { snprintf(Error, sizeof(Error), "Can't send article to server, %s", strerror(errno)); fclose(FromServer); fclose(ToServer); return Spoolit(article, Error); } /* Did the server want the article? */ if ((i = atoi(buff)) != NNTP_OK_IHAVE) { strlcpy(Error, buff, sizeof(Error)); SendQuit(FromServer, ToServer); syslog(L_TRACE, "%s server rejects %s from %s", Client.host, HDR(HDR__MESSAGEID), HDR(HDR__PATH)); if (i != NNTP_FAIL_IHAVE_REJECT && i != NNTP_FAIL_IHAVE_REFUSE) return Spoolit(article, Error); if (i == NNTP_FAIL_IHAVE_REJECT || i == NNTP_FAIL_IHAVE_DEFER) { *permanent = false; } return Error; } /* Send a quit and close down. */ SendQuit(FromServer, ToServer); /* Tracking. */ if (PERMaccessconf->readertrack) { TrackID = concat(innconf->pathlog, "/trackposts/track.", HDR(HDR__MESSAGEID), (char *) 0); if ((ftd = fopen(TrackID,"w")) == NULL) { DirTrackID = concatpath(innconf->pathlog, "trackposts"); MakeDirectory(DirTrackID, false); free(DirTrackID); } if (ftd == NULL && (ftd = fopen(TrackID,"w")) == NULL) { syslog(L_ERROR, "%s (%s) open %s: %m", Client.host, Username, TrackID); free(TrackID); return NULL; } for (hp = Table; hp < ARRAY_END(Table); hp++) if (hp->Value) { q = xstrndup(hp->Value, hp->Body - hp->Value + hp->Len); if (strchr(q, '\n') != NULL) { if ((p = Towire(q)) != NULL) { /* There is no white space, if hp->Value and hp->Body are the same. */ if (*hp->Value == ' ' || *hp->Value == '\t') fprintf(ftd, "%s:%s\r\n", hp->Name, p); else fprintf(ftd, "%s: %s\r\n", hp->Name, p); free(p); } } else { /* There is no white space, if hp->Value and hp->Body are the same. */ if (*hp->Value == ' ' || *hp->Value == '\t') fprintf(ftd, "%s:%s\r\n", hp->Name, q); else fprintf(ftd, "%s: %s\r\n", hp->Name, q); } free(q); } for (i = 0 ; i < OtherCount ; i++) { if (strchr(OtherHeaders[i], '\n') != NULL) { if ((p = Towire(OtherHeaders[i])) != NULL) { fprintf(ftd, "%s\r\n", p); free(p); } } else { fprintf(ftd, "%s\r\n", OtherHeaders[i]); } } fprintf(ftd,"\r\n"); NNTPsendarticle(article, ftd, true); if (fclose(ftd) != EOF) { syslog(L_NOTICE, "%s (%s) posttrack ok %s", Client.host, Username, TrackID); if (LLOGenable) fprintf(locallog, "%s (%s) posttrack ok %s\n", Client.host, Username, TrackID); } else { syslog(L_ERROR, "%s (%s) posttrack error 2 %s", Client.host, Username, TrackID); } free(TrackID); } return NULL; } inn-2.6.0/nnrpd/group.c0000644000175200017520000002111712575023702014343 0ustar iuliusiulius/* $Id: group.c 8904 2010-01-17 19:45:19Z iulius $ ** ** Newsgroups and the active file. */ #include "config.h" #include "clibrary.h" #include "inn/innconf.h" #include "nnrpd.h" #include "inn/ov.h" /* ** Change to or list the specified newsgroup. If invalid, stay in the old ** group. ** Do not forget to free(group) before any return after "group" has been set. */ void CMDgroup(int ac, char *av[]) { ARTNUM i; int low, high; char *grplist[2]; char *group; void *handle; TOKEN token; int count; bool boolval; bool hookpresent = false; #ifdef DO_PYTHON hookpresent = PY_use_dynamic; #endif /* DO_PYTHON */ /* Parse arguments. */ if (ac == 1) { if (GRPcur == NULL) { Reply("%d No group specified\r\n", NNTP_FAIL_NO_GROUP); return; } else { group = xstrdup(GRPcur); } } else { group = xstrdup(av[1]); } /* Check whether the second argument is valid (for LISTGROUP). */ if (ac == 3 && !IsValidRange(av[2])) { Reply("%d Syntax error in range\r\n", NNTP_ERR_SYNTAX); free(group); return; } /* Check authorizations. */ if (!hookpresent && !PERMcanread) { Reply("%d Read access denied\r\n", PERMcanauthenticate ? NNTP_FAIL_AUTH_NEEDED : NNTP_ERR_ACCESS); free(group); return; } /* FIXME: Temporarily work around broken API. */ if (!OVgroupstats(group, &low, &high, &count, NULL)) { Reply("%d No such group %s\r\n", NNTP_FAIL_BAD_GROUP, group); free(group); return; } #ifdef DO_PYTHON if (PY_use_dynamic) { char *reply; /* Authorize user using Python module method dynamic. */ if (PY_dynamic(PERMuser, group, false, &reply) < 0) { syslog(L_NOTICE, "PY_dynamic(): authorization skipped due to no Python dynamic method defined"); } else { if (reply != NULL) { syslog(L_TRACE, "PY_dynamic() returned a refuse string for user %s at %s who wants to read %s: %s", PERMuser, Client.host, group, reply); Reply("%d %s\r\n", PERMcanauthenticate ? NNTP_FAIL_AUTH_NEEDED : NNTP_ERR_ACCESS, reply); free(group); free(reply); return; } } } #endif /* DO_PYTHON */ if (!hookpresent) { if (PERMspecified) { grplist[0] = group; grplist[1] = NULL; if (!PERMmatch(PERMreadlist, grplist)) { Reply("%d Read access denied\r\n", PERMcanauthenticate ? NNTP_FAIL_AUTH_NEEDED : NNTP_ERR_ACCESS); free(group); return; } } else { Reply("%d Read access denied\r\n", PERMcanauthenticate ? NNTP_FAIL_AUTH_NEEDED : NNTP_ERR_ACCESS); free(group); return; } } /* Close out any existing article, report group stats. */ ARTclose(); GRPreport(); /* These values must be changed after the Python dynamic hook and everything * that can lead to a failure of authorization. */ ARTlow = low; ARThigh = high; /* Doing a GROUP command? */ if (strcasecmp(av[0], "GROUP") == 0) { if (count == 0) { Reply("%d 0 %lu %lu %s\r\n", NNTP_OK_GROUP, ARThigh+1, ARThigh, group); } else { /* If we are an NFS reader, check the last nfsreaderdelay * articles in the group to see if they arrived in the * last nfsreaderdelay (default 60) seconds. If they did, * don't report them as we don't want them to appear too * soon. */ if (innconf->nfsreader != 0) { ARTNUM low, prev; time_t now, arrived; time(&now); /* We assume that during the last nfsreaderdelay seconds, * we did not receive more than 1 article per second. */ if (ARTlow + innconf->nfsreaderdelay > ARThigh) low = ARTlow; else low = ARThigh - innconf->nfsreaderdelay; handle = OVopensearch(group, low, ARThigh); if (!handle) { Reply("%d group disappeared\r\n", NNTP_FAIL_ACTION); free(group); return; } prev = low; while (OVsearch(handle, &i, NULL, NULL, NULL, &arrived)) { if ((time_t) (arrived + innconf->nfsreaderdelay) > now) { ARThigh = prev; /* No need to update the count since it is only * an estimate but make sure it is not too high. */ if ((unsigned int)count > ARThigh - ARTlow) count = ARThigh - ARTlow + 1; break; } prev = i; } OVclosesearch(handle); } Reply("%d %d %lu %lu %s\r\n", NNTP_OK_GROUP, count, ARTlow, ARThigh, group); } GRPcount++; ARTnumber = (count == 0 ? 0 : ARTlow); if (GRPcur) { if (strcmp(GRPcur, group) != 0) { OVctl(OVCACHEFREE, &boolval); free(GRPcur); GRPcur = xstrdup(group); } } else GRPcur = xstrdup(group); PERMgroupmadeinvalid = false; } else { /* Must be doing a LISTGROUP command. We used to just return something bland here ("Article list follows"), but reference NNTP returns the same data as GROUP does and since we have it all available it shouldn't hurt to return the same thing. */ ARTRANGE range; bool DidReply; /* Parse the range. */ if (ac == 3) { /* CMDgetrange() expects av[1] to contain the range. * It is av[2] for LISTGROUP. * We already know that GRPcur exists. */ if (!CMDgetrange(ac, av + 1, &range, &DidReply)) { if (DidReply) { free(group); return; } } } else { range.Low = ARTlow; range.High = ARThigh; } if (count == 0) { Reply("%d 0 %lu %lu %s\r\n", NNTP_OK_GROUP, ARThigh+1, ARThigh, group); Printf(".\r\n"); } else { Reply("%d %d %lu %lu %s\r\n", NNTP_OK_GROUP, count, ARTlow, ARThigh, group); /* If OVopensearch() is restricted to the range, it returns NULL * in case there isn't any article within the range. We already * know that the group exists. */ if ((handle = OVopensearch(group, range.Low, range.High)) != NULL) { while (OVsearch(handle, &i, NULL, NULL, &token, NULL)) { if (PERMaccessconf->nnrpdcheckart && !ARTinstorebytoken(token)) continue; Printf("%lu\r\n", i); } OVclosesearch(handle); } Printf(".\r\n"); } GRPcount++; ARTnumber = (count == 0 ? 0 : ARTlow); if (GRPcur) { if (strcmp(GRPcur, group) != 0) { OVctl(OVCACHEFREE, &boolval); free(GRPcur); GRPcur = xstrdup(group); } } else GRPcur = xstrdup(group); PERMgroupmadeinvalid = false; } free(group); } /* ** Report on the number of articles read in the group, and clear the count. */ void GRPreport(void) { char buff[SPOOLNAMEBUFF]; if (GRPcur) { strlcpy(buff, GRPcur, sizeof(buff)); syslog(L_NOTICE, "%s group %s %lu", Client.host, buff, GRParticles); GRParticles = 0; } } /* ** Used by ANU-News clients. */ void CMDxgtitle(int ac, char *av[]) { QIOSTATE *qp; char *line; char *p; char *q; char *grplist[2]; char save; /* Parse the arguments. */ if (ac == 1) { if (GRPcount == 0) { /* Keep the legacy response code 481 instead of 412. */ Reply("%d No group specified\r\n", NNTP_FAIL_XGTITLE); return; } p = GRPcur; } else p = av[1]; if (!PERMspecified) { Reply("%d No descriptions follow\r\n", NNTP_OK_XGTITLE); Printf(".\r\n"); return; } /* Open the file, get ready to scan. */ if ((qp = QIOopen(NEWSGROUPS)) == NULL) { syslog(L_ERROR, "%s can't open %s %m", Client.host, NEWSGROUPS); Reply("%d Can't open %s\r\n", NNTP_FAIL_XGTITLE, NEWSGROUPS); return; } Reply("%d Descriptions in form \"group description\"\r\n", NNTP_OK_XGTITLE); /* Print all lines with matching newsgroup name. */ while ((line = QIOread(qp)) != NULL) { for (q = line; *q && !ISWHITE(*q); q++) continue; save = *q; *q = '\0'; if (uwildmat(line, p)) { if (PERMspecified) { grplist[0] = line; grplist[1] = NULL; if (!PERMmatch(PERMreadlist, grplist)) continue; } *q = save; Printf("%s\r\n", line); } } /* Done. */ QIOclose(qp); Printf(".\r\n"); } inn-2.6.0/nnrpd/sasl.c0000644000175200017520000002547712575023702014166 0ustar iuliusiulius/* ** AUTHINFO SASL functionality. ** ** $Id: sasl.c 9870 2015-05-17 17:23:07Z iulius $ */ #include "config.h" #include "clibrary.h" #include "inn/messages.h" #include "nnrpd.h" /* Outside the ifdef so that make depend works even ifndef HAVE_OPENSSL. */ #include "inn/ov.h" #ifdef HAVE_OPENSSL extern int tls_cipher_usebits; extern char *tls_peer_CN; extern bool nnrpd_starttls_done; #endif /* HAVE_OPENSSL */ #ifdef HAVE_SASL sasl_conn_t *sasl_conn = NULL; int sasl_ssf = 0; int sasl_maxout = NNTP_MAXLEN_COMMAND; sasl_callback_t sasl_callbacks[] = { /* XXX Do we want a proxy callback? */ /* XXX Add a getopt callback? */ { SASL_CB_LIST_END, NULL, NULL } }; #define BASE64_BUF_SIZE 21848 /* Per RFC 4422: (floor(n/3) + 1) * 4 where n = 16 kB = 16384 bytes. */ /* ** Check if the argument is a valid mechanism according to RFC 4643: ** ** mechanism = 1*20mech-char ** mech-char = UPPER / DIGIT / "-" / "_" */ static bool IsValidMechanism(const char *string) { int len = 0; const unsigned char *p; /* Not NULL. */ if (string == NULL) return false; p = (const unsigned char *) string; for (; *p != '\0'; p++) { len++; if (!isalnum((unsigned char) *p) && *p != '-' && *p != '_') return false; } if (len > 0 && len < 21) return true; else return false; } /* ** Create a new SASL server authentication object. */ void SASLnewserver(void) { if (sasl_conn != NULL) { sasl_dispose(&sasl_conn); sasl_conn = NULL; sasl_ssf = 0; sasl_maxout = NNTP_MAXLEN_COMMAND; } if (sasl_server_new("nntp", NULL, NULL, NULL, NULL, NULL, SASL_SUCCESS_DATA, &sasl_conn) != SASL_OK) { syslog(L_FATAL, "sasl_server_new() failed"); Reply("%d SASL server unavailable. Try later!\r\n", NNTP_FAIL_TERMINATING); ExitWithStats(1, true); } else { /* XXX Fill in SASL_IPLOCALPORT and SASL_IPREMOTEPORT. */ sasl_security_properties_t secprops; memset(&secprops, 0, sizeof(secprops)); secprops.security_flags = SASL_SEC_NOANONYMOUS; secprops.max_ssf = 256; secprops.maxbufsize = NNTP_MAXLEN_COMMAND; sasl_setprop(sasl_conn, SASL_SEC_PROPS, &secprops); #ifdef HAVE_OPENSSL /* Tell SASL about the negotiated TLS layer. */ if (nnrpd_starttls_done) { if (sasl_setprop(sasl_conn, SASL_SSF_EXTERNAL, (sasl_ssf_t *) &tls_cipher_usebits) != SASL_OK) { syslog(L_NOTICE, "sasl_setprop() failed: TLS layer for SASL"); } if (sasl_setprop(sasl_conn, SASL_AUTH_EXTERNAL, tls_peer_CN) != SASL_OK) { syslog(L_NOTICE, "sasl_setprop() failed: TLS layer for SASL"); } } #endif } } void SASLauth(int ac, char *av[]) { const char *mech; const char *clientin = NULL; unsigned int clientinlen = 0; size_t tclientinlen = 0; const char *serverout = NULL; unsigned int serveroutlen; char base64[BASE64_BUF_SIZE+1]; const char *canon_user = NULL; const int *ssfp = NULL; const int *maxoutp; const void *property; int r = SASL_OK; int r1; bool base64error = false; if (ac < 3 || ac > 4) { /* In fact, ac > 4 here. */ Reply("%d Too many arguments\r\n", NNTP_ERR_SYNTAX); return; } mech = av[2]; if (!IsValidMechanism(mech)) { Reply("%d Syntax error in mechanism\r\n", NNTP_ERR_SYNTAX); return; } /* 502 if authentication will fail. */ if (!PERMcanauthenticate) { if (PERMauthorized && !PERMneedauth) Reply("%d Already authenticated\r\n", NNTP_ERR_ACCESS); else Reply("%d Authentication will fail\r\n", NNTP_ERR_ACCESS); return; } #ifdef HAVE_OPENSSL /* Check whether STARTTLS must be used before trying to authenticate * with AUTHINFO SASL PLAIN, LOGIN or EXTERNAL. */ if (PERMcanauthenticate && !PERMcanauthenticatewithoutSSL && !nnrpd_starttls_done && ((strcasecmp(mech, "PLAIN") == 0 || strcasecmp(mech, "LOGIN") == 0 || strcasecmp(mech, "EXTERNAL") == 0))) { Reply("%d Encryption required\r\n", NNTP_FAIL_PRIVACY_NEEDED); return; } #endif if (ac == 4) { /* Initial response. */ clientin = av[3]; if (strcmp(clientin, "=") == 0) { /* Zero-length initial response. */ clientin = ""; clientinlen = 0; } else { /* Decode the response. On error, SASL_CONTINUE should not be * given because we know for sure that we have already received * the whole challenge/response. Use SASL_BADPROT instead, * in order to indicate a base64-encoding error. */ r1 = sasl_decode64(clientin, strlen(clientin), base64, BASE64_BUF_SIZE, &clientinlen); clientin = base64; r = (r1 == SASL_CONTINUE ? SASL_BADPROT : r1); base64error = (r == SASL_BADPROT); } } if (r == SASL_OK) { /* Start the exchange. */ r = sasl_server_start(sasl_conn, mech, clientin, clientinlen, &serverout, &serveroutlen); } while (r == SASL_CONTINUE || (r == SASL_OK && serveroutlen != 0)) { if (serveroutlen != 0) { /* Encode the server challenge. * In sasl_encode64() calls, the fourth argument is the length * of the third including the null terminator. */ r1 = sasl_encode64(serverout, serveroutlen, base64, BASE64_BUF_SIZE+1, NULL); if (r1 != SASL_OK) r = r1; } /* Check for failure or success. */ if (r != SASL_CONTINUE) break; /* Send the challenge to the client. */ Reply("%d %s\r\n", NNTP_CONT_SASL, serveroutlen != 0 ? base64 : "="); fflush(stdout); /* Get the response from the client. */ r1 = line_read(&NNTPline, PERMaccessconf->clienttimeout, &clientin, &tclientinlen, NULL); clientinlen = tclientinlen; switch (r1) { case RTok: if (clientinlen <= BASE64_BUF_SIZE) break; /* FALLTHROUGH */ case RTlong: warn("%s response too long in AUTHINFO SASL", Client.host); Reply("%d Too long response\r\n", NNTP_FAIL_TERMINATING); ExitWithStats(1, false); break; case RTtimeout: warn("%s timeout in AUTHINFO SASL", Client.host); /* No answer. */ ExitWithStats(1, false); break; case RTeof: warn("%s EOF in AUTHINFO SASL", Client.host); Reply("%d EOF\r\n", NNTP_FAIL_TERMINATING); ExitWithStats(1, false); break; default: warn("%s internal %d in AUTHINFO SASL", Client.host, r); Reply("%d Internal error\r\n", NNTP_FAIL_TERMINATING); ExitWithStats(1, false); break; } /* Check if client cancelled. */ if (strcmp(clientin, "*") == 0) { /* Restart the SASL server in order to be able to reauthenticate. * Call that function before the reply because in case of failure, * 400 is sent. */ SASLnewserver(); Reply("%d Client cancelled authentication\r\n", NNTP_FAIL_AUTHINFO_BAD); return; } if (strcmp(clientin, "=") == 0) { /* Zero-length answer. */ clientin = ""; clientinlen = 0; } else { /* Decode the response. On error, SASL_CONTINUE should not be * given because we know for sure that we have already received * the whole challenge/response. Use SASL_BADPROT instead, * in order to indicate a base64-encoding error. */ r1 = sasl_decode64(clientin, clientinlen, base64, BASE64_BUF_SIZE, &clientinlen); clientin = base64; r = (r1 == SASL_CONTINUE ? SASL_BADPROT : r1); base64error = (r == SASL_BADPROT); } /* Do the next step. */ if (r == SASL_OK) { r = sasl_server_step(sasl_conn, clientin, clientinlen, &serverout, &serveroutlen); } } /* Fetch the username (authorization ID). */ if (r == SASL_OK) { r = sasl_getprop(sasl_conn, SASL_USERNAME, &property); canon_user = property; } /* Grab info about the negotiated layer. */ if (r == SASL_OK) { r = sasl_getprop(sasl_conn, SASL_SSF, &property); ssfp = property; } if (r == SASL_OK) { r = sasl_getprop(sasl_conn, SASL_MAXOUTBUF, &property); maxoutp = property; } if (r == SASL_OK) { /* Success. */ strlcpy(PERMuser, canon_user, sizeof(PERMuser)); PERMgetpermissions(); PERMneedauth = false; PERMauthorized = true; PERMcanauthenticate = false; syslog(L_NOTICE, "%s user %s", Client.host, PERMuser); if (serveroutlen) Reply("%d %s\r\n", NNTP_OK_SASL, base64); else Reply("%d Authentication succeeded\r\n", NNTP_OK_AUTHINFO); /* Save info about the negotiated security layer for I/O functions. */ sasl_ssf = *ssfp; sasl_maxout = (*maxoutp == 0 || *maxoutp > NNTP_MAXLEN_COMMAND) ? NNTP_MAXLEN_COMMAND : *maxoutp; if (sasl_ssf > 0) { /* Close out any existing article, report group stats. * RFC 4643 requires the reset of any knowledge about the client. */ if (GRPcur) { bool boolval; ARTclose(); GRPreport(); OVctl(OVCACHEFREE, &boolval); free(GRPcur); GRPcur = NULL; if (ARTcount) syslog(L_NOTICE, "%s exit for AUTHINFO SASL articles %ld groups %ld", Client.host, ARTcount, GRPcount); GRPcount = 0; PERMgroupmadeinvalid = false; } /* Reset our read buffer so as to prevent plaintext command injection. */ line_reset(&NNTPline); } } else { /* Failure. */ int resp_code; const char *errstring = sasl_errstring(r, NULL, NULL); syslog(L_NOTICE, "%s bad_auth", Client.host); switch (r) { case SASL_BADPROT: resp_code = (base64error ? NNTP_ERR_BASE64 : NNTP_FAIL_AUTHINFO_REJECT); break; case SASL_BADPARAM: case SASL_NOTDONE: resp_code = NNTP_FAIL_AUTHINFO_REJECT; break; case SASL_NOMECH: resp_code = NNTP_ERR_UNAVAILABLE; break; case SASL_ENCRYPT: resp_code = NNTP_FAIL_PRIVACY_NEEDED; break; default: resp_code = NNTP_FAIL_AUTHINFO_BAD; break; } /* Restart the SASL server in order to be able to reauthenticate. * Call that function before the reply because in case of failure, * 400 is sent. */ SASLnewserver(); Reply("%d %s\r\n", resp_code, errstring ? errstring : "Authentication failed"); } } #endif /* HAVE_SASL */ inn-2.6.0/nnrpd/track.c0000644000175200017520000000277012575023702014317 0ustar iuliusiulius/* $Id: track.c 8034 2008-09-20 07:13:18Z iulius $ ** ** User and post tracking database. */ #include "config.h" #include "clibrary.h" #include "inn/innconf.h" #include "nnrpd.h" #define MAX_LEN 180 /* ** TrackClient determines whether or not ** we are interested in tracking the activities ** of the currently connected host. We have to ** rely on an external process to set up the ** entries in the database though, which makes ** this only as reliable as the process that ** sets this up... */ /* ** Format of the input line is the following one. ** : */ int TrackClient(char *client, char *user, size_t len) { int RARTon; FILE *fd; char line[MAX_LEN],*p,*pp,*lp; char *dbfile; dbfile = concatpath(innconf->pathetc, "nnrpd.track"); RARTon=false; strlcpy(user, "unknown", len); if ((fd=fopen(dbfile,"r"))!=NULL) { while((fgets(line,(MAX_LEN - 1),fd))!=NULL) { if (line[0] == '#' || line[0] == '\n') continue; if ((p=strchr(line,' ')) != NULL) *p='\0'; if ((p=strchr(line,'\n')) != NULL) *p='\0'; if ((p=strchr(line,':')) != NULL) { *p++='\0'; } else { p=NULL; } pp=line; if ((lp=strchr(pp,'*')) != NULL) { pp=++lp; } if (strstr(client,pp)!=NULL) { RARTon=true; if (p != NULL) strlcpy(user,p,len); break; } } fclose(fd); } else { RARTon=false; syslog(L_NOTICE, "%s No logging -- can't read %s", Client.host, dbfile); } free(dbfile); return RARTon; } inn-2.6.0/authprogs/0000755000175200017520000000000012575023677013747 5ustar iuliusiuliusinn-2.6.0/authprogs/libauth.h0000644000175200017520000000257212575023702015543 0ustar iuliusiulius/* $Id: libauth.h 7183 2005-04-10 22:46:50Z rra $ ** ** Some utility functions for writing authenticators and resolvers. */ #ifndef LIBAUTH_H #define LIBAUTH_H 1 #include "config.h" #include "portable/socket.h" #include #include /* Holds the resolver information from nnrpd. */ struct res_info { char *clienthostname; char *clientip; char *clientport; char *localip; char *localport; }; /* Holds the authentication information from nnrpd. */ struct auth_info { char *username; char *password; }; BEGIN_DECLS /* Reads connection information from a file descriptor (normally stdin, when talking to nnrpd) and returns a new res_info or auth_info struct, or returns NULL on failure. Note that the fields will never be NULL; if the corresponding information is missing, it is an error (which will be logged and NULL will be returned). The client is responsible for freeing the struct and its fields; this can be done by calling the appropriate destruction function below. */ extern struct auth_info *get_auth_info(FILE *); extern struct res_info *get_res_info (FILE *); /* Free a res_info or auth_info struct. */ extern void free_auth_info(struct auth_info *); extern void free_res_info (struct res_info *); /* Return the user string to nnrpd. */ extern void print_user(const char *); END_DECLS #endif /* !LIBAUTH_H */ inn-2.6.0/authprogs/ident.c0000644000175200017520000000743412575023702015213 0ustar iuliusiulius/* $Id: ident.c 9893 2015-06-14 10:05:50Z iulius $ ** ** Ident authenticator. */ #include "config.h" #include "clibrary.h" #include #include #include #include #include "inn/messages.h" #include "inn/network.h" #include "inn/libinn.h" #include "libauth.h" #define IDENT_PORT 113 /* ** The signal handler for a timeout. Just exit with a non-zero status. */ static void timeout(int sig UNUSED) { exit(1); } int main(int argc, char *argv[]) { struct servent *s; char buf[2048]; struct res_info *res; int sock; int opt; int truncate_domain = 0; char *iter; char *p; unsigned int got; int identport; char *endstr; message_program_name = "ident"; xsignal_norestart(SIGALRM, timeout); alarm(15); s = getservbyname("ident", "tcp"); if (s == NULL) identport = IDENT_PORT; else identport = ntohs(s->s_port); while ((opt = getopt(argc, argv, "p:t")) != -1) { switch (opt) { case 'p': for (iter = optarg; *iter; iter++) if (*iter < '0' || *iter > '9') break; if (*iter) { /* not entirely numeric */ s = getservbyname(optarg, "tcp"); if (s == NULL) die("cannot getsrvbyname(%s, tcp)", optarg); identport = s->s_port; } else identport = atoi(optarg); break; case 't': truncate_domain = 1; break; } } /* Read the connection info from stdin. */ res = get_res_info(stdin); if (res == NULL) die("did not get client information from nnrpd"); /* Connect back to the client system. */ sock = network_connect_host(res->clientip, identport, res->localip, DEFAULT_TIMEOUT); if (sock < 0) { if (errno != ECONNREFUSED) sysdie("cannot connect to ident server"); else sysdie("client host does not accept ident connections"); } /* send the request out */ snprintf(buf, sizeof(buf), "%s , %s\r\n", res->clientport, res->localport); opt = xwrite(sock, buf, strlen(buf)); if (opt < 0) sysdie("cannot write to ident server"); free_res_info(res); /* get the answer back */ got = 0; do { opt = read(sock, buf+got, sizeof(buf)-got); if (opt < 0) sysdie("cannot read from ident server"); else if (!opt) die("end of file from ident server before response"); while (opt--) if (buf[got] != '\n') got++; } while (buf[got] != '\n'); buf[got] = '\0'; if (got > 0 && buf[got-1] == '\r') buf[got-1] = '\0'; /* buf now contains the entire ident response. */ if (!(iter = strchr(buf, ':'))) /* malformed response */ die("malformed response \"%s\" from ident server", buf); iter++; while (*iter && ISWHITE(*iter)) iter++; endstr = iter; while (*endstr && *endstr != ':' && !ISWHITE(*endstr)) endstr++; if (!*endstr) /* malformed response */ die("malformed response \"%s\" from ident server", buf); if (*endstr != ':') { *endstr++ = '\0'; while (*endstr != ':') endstr++; } *endstr = '\0'; if (strcmp(iter, "ERROR") == 0) die("ident server reported an error"); else if (strcmp(iter, "USERID") != 0) die("ident server returned \"%s\", not USERID", iter); /* skip the operating system */ if (!(iter = strchr(endstr+1, ':'))) exit(1); /* everything else is username */ iter++; while (*iter && ISWHITE(*iter)) iter++; if (*iter == '\0' || *iter == '[') /* null, or encrypted response */ die("ident response is null or encrypted"); for (p = iter; *p != '\0' && !ISWHITE(*p); p++) ; *p = '\0'; if (truncate_domain) { p = strchr(iter, '@'); if (p != NULL) *p = '\0'; } print_user(iter); exit(0); } inn-2.6.0/authprogs/auth_krb5.c0000644000175200017520000001113212575023702015762 0ustar iuliusiulius/* $Id: auth_krb5.c 9792 2015-03-17 20:10:25Z iulius $ ** ** Check an username and password against Kerberos v5. ** ** Based on nnrpkrb5auth by Christopher P. Lindsey ** See ** ** This program takes a username and password pair from nnrpd and checks ** checks their validity against a Kerberos v5 KDC by attempting to obtain a ** TGT. With the -i command line option, appends / to ** the username prior to authentication. ** ** Special thanks to Von Welch for giving me the initial ** code on which the Kerberos V authentication is based many years ago, and ** for introducing me to Kerberos back in '96. ** ** Also, thanks to Graeme Mathieson for his inspiration ** through the pamckpasswd program. */ #include "config.h" #include "clibrary.h" #include "libauth.h" #ifdef HAVE_ET_COM_ERR_H # include #elif defined(HAVE_KERBEROSV5_COM_ERR_H) # include #else # include #endif #ifdef HAVE_KRB5_H # include #elif HAVE_KERBEROSV5_KRB5_H # include #else # include #endif #include "inn/messages.h" #include "inn/libinn.h" /* ** Check the username and password by attempting to get a TGT. Returns 1 on ** success and 0 on failure. Errors are reported via com_err. */ static int krb5_check_password(const char *principal, const char *password) { krb5_error_code code; krb5_context ctx; krb5_creds creds; krb5_principal princ = NULL; krb5_get_init_creds_opt opts; bool creds_valid = false; int result = 0; code = krb5_init_context(&ctx); if (code != 0) { com_err(message_program_name, code, "initializing krb5 context"); return 0; } code = krb5_parse_name(ctx, principal, &princ); if (code != 0) { com_err(message_program_name, code, "parsing principal name %.100s", principal); goto cleanup; } memset(&opts, 0, sizeof(opts)); krb5_get_init_creds_opt_init(&opts); krb5_get_init_creds_opt_set_forwardable(&opts, 0); krb5_get_init_creds_opt_set_proxiable(&opts, 0); code = krb5_get_init_creds_password(ctx, &creds, princ, (char *) password, NULL, NULL, 0, NULL, &opts); if (code == 0) { krb5_verify_init_creds_opt vopts; creds_valid = true; memset(&opts, 0, sizeof(vopts)); krb5_verify_init_creds_opt_init(&vopts); code = krb5_verify_init_creds(ctx, &creds, princ, NULL, NULL, &vopts); } if (code == 0) result = 1; else { switch (code) { case KRB5KRB_AP_ERR_BAD_INTEGRITY: com_err(message_program_name, 0, "bad password for %.100s", principal); break; case KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN: com_err(message_program_name, 0, "unknown user %.100s", principal); break; default: com_err(message_program_name, code, "checking Kerberos password for %.100s", principal); } } cleanup: if (creds_valid) krb5_free_cred_contents(ctx, &creds); if (princ != NULL) krb5_free_principal(ctx, princ); krb5_free_context(ctx); return result; } int main (int argc, char *argv[]) { struct auth_info *authinfo; char *new_user; message_program_name = "auth_krb5"; /* Retrieve the username and passwd from nnrpd. */ authinfo = get_auth_info(stdin); /* Must have a username/password, and no '@' in the address. @ checking is there to prevent authentication against another Kerberos realm; there should be a -r command line option to make this check unnecessary in the future. */ if (authinfo == NULL) die("no authentication information from nnrpd"); if (authinfo->username[0] == '\0') die("null username"); if (strchr(authinfo->username, '@') != NULL) die("username contains @, not allowed"); /* May need to prepend instance name if -i option was given. */ if (argc > 1) { if (argc == 3 && strcmp(argv[1], "-i") == 0) { new_user = concat(authinfo->username, "/", argv[2], (char *) 0); free(authinfo->username); authinfo->username = new_user; } else { die("error parsing command-line options"); } } if (krb5_check_password(authinfo->username, authinfo->password)) { print_user(authinfo->username); exit(0); } else { die("failure validating password"); } } inn-2.6.0/authprogs/domain.c0000644000175200017520000000264512575023702015356 0ustar iuliusiulius/* $Id: domain.c 7585 2006-11-21 09:37:51Z eagle $ ** ** Domain authenticator. ** ** Compares the domain of the client connection to the first argument given ** on the command line, and returns the host portion of the connecting host ** as the user if it matches. */ #include "config.h" #include "clibrary.h" #include "inn/messages.h" #include "inn/libinn.h" #include "libauth.h" int main(int argc, char *argv[]) { char *p, *host; struct res_info *res; if (argc != 2) die("Usage: domain "); message_program_name = "domain"; /* Read the connection information from stdin. */ res = get_res_info(stdin); if (res == NULL) die("did not get ClientHost data from nnrpd"); host = res->clienthostname; /* Check the host against the provided domain. Allow the domain to be specified both with and without a leading period; if without, make sure that there is a period right before where it matches in the host. */ p = strstr(host, argv[1]); if (p == host) die("host %s matches the domain exactly", host); if (p == NULL || (argv[1][0] != '.' && p != host && *(p - 1) != '.')) die("host %s didn't match domain %s", host, argv[1]); /* Peel off the portion of the host before where the provided domain matches and return it as the user. */ if (argv[1][0] != '.') p--; *p = '\0'; print_user(host); return 0; } inn-2.6.0/authprogs/Makefile0000644000175200017520000001271112575023702015376 0ustar iuliusiulius## $Id: Makefile 9794 2015-03-17 20:49:15Z iulius $ include ../Makefile.global top = .. CFLAGS = $(GCFLAGS) ALL = ckpasswd domain ident radius $(KRB5_AUTH) LIBAUTH = libauth.o SOURCES = auth_krb5.c ckpasswd.c domain.c ident.c libauth.c radius.c all: $(ALL) warnings: $(MAKE) COPT='$(WARNINGS)' all install: all if [ x"$(KRB5_AUTH)" != x ] ; then \ $(LI_XPUB) auth_krb5 $(D)$(PATHAUTHPASSWD)/auth_krb5 ; \ fi for F in ckpasswd radius ; do \ $(LI_XPUB) $$F $D$(PATHAUTHPASSWD)/$$F ; \ done for F in domain ident ; do \ $(LI_XPUB) $$F $D$(PATHAUTHRESOLV)/$$F ; \ done bootstrap: clean clobber distclean maintclean: rm -f *.o $(ALL) rm -rf .libs profiled: $(MAKEPROFILING) all ## Compilation rules. LINK = $(LIBLD) $(LDFLAGS) -o $@ AUTH_LIBS = $(LIBAUTH) $(LIBINN) $(LIBS) CK_LIBS = $(CRYPT_LIBS) $(SHADOW_LIBS) $(PAM_LIBS) $(DBM_LIBS) auth_krb5: auth_krb5.o $(LIBAUTH) $(LIBINN) $(LINK) auth_krb5.o $(KRB5_LDFLAGS) $(KRB5_LIBS) $(AUTH_LIBS) ckpasswd: ckpasswd.o $(LIBAUTH) $(LIBINN) $(LINK) ckpasswd.o $(CK_LIBS) $(AUTH_LIBS) domain: domain.o $(LIBAUTH) $(LIBINN) $(LINK) domain.o $(AUTH_LIBS) ident: ident.o $(LIBAUTH) $(LIBINN) $(LINK) ident.o $(AUTH_LIBS) radius: radius.o $(LIBAUTH) $(LIBINN) $(LINK) radius.o $(AUTH_LIBS) auth_krb5.o: auth_krb5.c $(CC) $(CFLAGS) $(KRB5_CPPFLAGS) -c auth_krb5.c ckpasswd.o: ckpasswd.c $(CC) $(CFLAGS) $(DBM_CPPFLAGS) -c ckpasswd.c $(LIBINN): ; (cd ../lib ; $(MAKE)) $(LIBSTORAGE): ; (cd ../storage ; $(MAKE)) $(LIBAUTH): libauth.h libauth.c ## Dependencies. Default list, below, is probably good enough. depend: Makefile $(SOURCES) $(MAKEDEPEND) '$(CFLAGS)' $(SOURCES) # DO NOT DELETE THIS LINE -- make depend depends on it. auth_krb5.o: auth_krb5.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h libauth.h ../include/portable/socket.h \ ../include/portable/macros.h ../include/portable/getaddrinfo.h \ ../include/portable/getnameinfo.h ../include/inn/messages.h \ ../include/inn/libinn.h ../include/inn/xmalloc.h ../include/inn/xwrite.h ckpasswd.o: ckpasswd.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/messages.h \ ../include/inn/qio.h ../include/inn/vector.h ../include/inn/libinn.h \ ../include/inn/xmalloc.h ../include/inn/xwrite.h libauth.h \ ../include/portable/socket.h ../include/portable/macros.h \ ../include/portable/getaddrinfo.h ../include/portable/getnameinfo.h domain.o: domain.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/messages.h \ ../include/inn/libinn.h ../include/inn/xmalloc.h ../include/inn/xwrite.h \ libauth.h ../include/portable/socket.h ../include/portable/macros.h \ ../include/portable/getaddrinfo.h ../include/portable/getnameinfo.h ident.o: ident.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/messages.h \ ../include/inn/network.h ../include/inn/portable-socket.h \ ../include/inn/portable-getaddrinfo.h \ ../include/inn/portable-getnameinfo.h ../include/inn/libinn.h \ ../include/inn/xmalloc.h ../include/inn/xwrite.h libauth.h \ ../include/portable/socket.h libauth.o: libauth.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/portable/socket.h \ ../include/portable/macros.h ../include/portable/getaddrinfo.h \ ../include/portable/getnameinfo.h ../include/inn/messages.h libauth.h \ ../include/inn/libinn.h ../include/inn/xmalloc.h ../include/inn/xwrite.h radius.o: radius.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/innconf.h \ ../include/inn/md5.h ../include/inn/messages.h ../include/inn/libinn.h \ ../include/inn/xmalloc.h ../include/inn/xwrite.h ../include/inn/nntp.h \ ../include/inn/paths.h ../include/conffile.h \ ../include/portable/macros.h libauth.h ../include/portable/socket.h \ ../include/portable/macros.h ../include/portable/getaddrinfo.h \ ../include/portable/getnameinfo.h inn-2.6.0/authprogs/libauth.c0000644000175200017520000001200512575023702015526 0ustar iuliusiulius/* $Id: libauth.c 7585 2006-11-21 09:37:51Z eagle $ ** ** Common code for authenticators and resolvers. ** ** Collects common code to read information from nnrpd that should be done ** the same for all authenticators, and common code to get information about ** the incoming connection. */ #include "config.h" #include "clibrary.h" #include "portable/socket.h" #include "inn/messages.h" #include "libauth.h" #include "inn/libinn.h" #define NAMESTR "ClientAuthname: " #define PASSSTR "ClientPassword: " #define CLIHOST "ClientHost: " #define CLIIP "ClientIP: " #define CLIPORT "ClientPort: " #define LOCIP "LocalIP: " #define LOCPORT "LocalPort: " /* ** Main loop. If res != NULL, expects to get resolver info from nnrpd, and ** writes it into the struct. If auth != NULL, expects to get authentication ** info from nnrpd, and writes it into the struct. */ static bool get_connection_info(FILE *stream, struct res_info *res, struct auth_info *auth) { char buff[SMBUF]; size_t length; /* Zero fields first (anything remaining NULL after is missing data). */ if (res != NULL) { res->clienthostname = NULL; res->clientip = NULL; res->clientport = NULL; res->localip = NULL; res->localport = NULL; } if (auth != NULL) { auth->username = NULL; auth->password = NULL; } /* Read input from nnrpd a line at a time, stripping \r\n. */ while (fgets(buff, sizeof(buff), stream) != NULL) { length = strlen(buff); if (length == 0 || buff[length - 1] != '\n') return false; buff[length - 1] = '\0'; if (length > 1 && buff[length - 2] == '\r') buff[length - 2] = '\0'; /* Parse */ if (strncmp(buff, ".", 2) == 0) break; else if (auth != NULL && strncmp(buff, NAMESTR, strlen(NAMESTR)) == 0) auth->username = xstrdup(buff + strlen(NAMESTR)); else if (auth != NULL && strncmp(buff, PASSSTR, strlen(PASSSTR)) == 0) auth->password = xstrdup(buff + strlen(PASSSTR)); else if (res != NULL && strncmp(buff, CLIHOST, strlen(CLIHOST)) == 0) res->clienthostname = xstrdup(buff + strlen(CLIHOST)); else if (res != NULL && strncmp(buff, CLIIP, strlen(CLIIP)) == 0) res->clientip = xstrdup(buff + strlen(CLIIP)); else if (res != NULL && strncmp(buff, CLIPORT, strlen(CLIPORT)) == 0) res->clientport = xstrdup(buff + strlen(CLIPORT)); else if (res != NULL && strncmp(buff, LOCIP, strlen(LOCIP)) == 0) res->localip = xstrdup(buff + strlen(LOCIP)); else if (res != NULL && strncmp(buff, LOCPORT, strlen(LOCPORT)) == 0) res->localport = xstrdup(buff + strlen(LOCPORT)); else { debug("libauth: unexpected data from nnrpd: \"%s\"", buff); } } /* If some field is missing, free the rest and error out. */ if (auth != NULL && (auth->username == NULL || auth->password == NULL)) { warn("libauth: requested authenticator data not sent by nnrpd"); return false; } if (res != NULL && (res->clienthostname == NULL || res->clientip == NULL || res->clientport == NULL || res->localip == NULL || res->localport == NULL)) { warn("libauth: requested resolver data not sent by nnrpd"); return false; } return true; } /* ** Free a struct res_info, including all of its members. */ void free_res_info(struct res_info *res) { if (res == NULL) return; if (res->clientip != NULL) free(res->clientip); if (res->clientport != NULL) free(res->clientport); if (res->localip != NULL) free(res->localip); if (res->localport != NULL) free(res->localport); if (res->clienthostname != NULL) free(res->clienthostname); free(res); } /* ** Free a struct auth_info, including all of its members. */ void free_auth_info(struct auth_info *auth) { if (auth == NULL) return; if (auth->username != NULL) free(auth->username); if (auth->password != NULL) free(auth->password); free(auth); } /* ** Read resolver information from nnrpd, returning an allocated struct on ** success. */ struct res_info * get_res_info(FILE *stream) { struct res_info *res = xmalloc(sizeof(struct res_info)); if (get_connection_info(stream, res, NULL)) return res; free_res_info(res); return NULL; } /* ** Read authenticator information from nnrpd, returning an allocated struct ** on success. */ struct auth_info * get_auth_info(FILE *stream) { struct auth_info *auth = xmalloc(sizeof(struct auth_info)); if (get_connection_info(stream, NULL, auth)) return auth; free_auth_info(auth); return NULL; } /* ** Print the User: result on standard output in the format expected by ** nnrpd. The string passed in should be exactly the user, with no ** extraneous leading or trailing whitespace. */ void print_user(const char *user) { printf("User:%s\r\n", user); } inn-2.6.0/authprogs/radius.c0000644000175200017520000004043112575023702015371 0ustar iuliusiulius/* $Id: radius.c 9695 2014-09-20 05:53:22Z iulius $ ** ** Authenticate a user against a remote radius server. */ #include "config.h" #include "clibrary.h" #include #include #include #include #include #ifdef HAVE_SYS_TIME_H # include #endif #include /* Needed on AIX 4.1 to get fd_set and friends. */ #if HAVE_SYS_SELECT_H # include #endif #include "inn/innconf.h" #include "inn/md5.h" #include "inn/messages.h" #include "inn/libinn.h" #include "inn/nntp.h" #include "inn/paths.h" #include "conffile.h" #include "libauth.h" #define RADIUS_LOCAL_PORT NNTP_PORT #define AUTH_VECTOR_LEN 16 typedef struct _auth_req { unsigned char code; unsigned char id; unsigned short length; unsigned char vector[AUTH_VECTOR_LEN]; unsigned char data[NNTP_MAXLEN_COMMAND*2]; int datalen; } auth_req; typedef struct _rad_config_t { char *secret; /* pseudo encryption thingy secret that radius uses */ char *radhost; /* parameters for talking to the remote radius sever */ int radport; char *lochost; int locport; char *prefix, *suffix; /* futz with the username, if necessary */ int ignore_source; struct _rad_config_t *next; /* point to any additional servers */ } rad_config_t; typedef struct _sending_t { auth_req req; int reqlen; struct sockaddr_in sinr; struct _sending_t *next; } sending_t; #define RADlbrace 1 #define RADrbrace 2 #define RADserver 10 #define RADhost 11 #define RADsecret 12 #define RADport 13 #define RADlochost 14 #define RADlocport 15 #define RADprefix 16 #define RADsuffix 17 #define RADsource 18 static CONFTOKEN radtoks[] = { { RADlbrace, (char *) "{" }, { RADrbrace, (char *) "}" }, { RADserver, (char *) "server" }, { RADhost, (char *) "radhost:" }, { RADsecret, (char *) "secret:" }, { RADport, (char *) "radport:" }, { RADlochost, (char *) "lochost:" }, { RADlocport, (char *) "locport:" }, { RADprefix, (char *) "prefix:" }, { RADsuffix, (char *) "suffix:" }, { RADsource, (char *) "ignore-source:" }, { 0, NULL } }; static rad_config_t *get_radconf(void) { rad_config_t *new; new = xcalloc(1, sizeof(rad_config_t)); new->next = NULL; return new; } static int read_config(char *authfile, rad_config_t *radconf) { int inbrace; rad_config_t *radconfig=NULL; CONFFILE *file; CONFTOKEN *token; int type; char *iter; if ((file = CONFfopen(authfile)) == NULL) sysdie("cannot open config file %s", authfile); inbrace = 0; while ((token = CONFgettoken(radtoks, file)) != NULL) { if (!inbrace) { if (token->type != RADserver) die("expected server keyword on line %d", file->lineno); if ((token = CONFgettoken(0, file)) == NULL) die("expected server name on line %d", file->lineno); if ((token = CONFgettoken(radtoks, file)) == NULL || token->type != RADlbrace) die("expected { on line %d", file->lineno); inbrace = 1; if (radconfig == NULL) radconfig = radconf; else { radconfig->next = get_radconf(); radconfig = radconfig->next; } } else { type = token->type; if (type == RADrbrace) inbrace = 0; else { if ((token = CONFgettoken(0, file)) == NULL) die("keyword with no value on line %d", file->lineno); iter = token->name; /* what are we setting? */ switch(type) { case RADsecret: if (radconfig->secret) continue; radconfig->secret = xstrdup(iter); break; case RADhost: if (radconfig->radhost) continue; radconfig->radhost = xstrdup(iter); break; case RADport: if (radconfig->radport) continue; radconfig->radport = atoi(iter); break; case RADlochost: if (radconfig->lochost) continue; radconfig->lochost = xstrdup(iter); break; case RADlocport: if (radconfig->locport) continue; radconfig->locport = atoi(iter); break; case RADprefix: if (radconfig->prefix) continue; radconfig->prefix = xstrdup(iter); break; case RADsuffix: if (radconfig->suffix) continue; radconfig->suffix = xstrdup(iter); break; case RADsource: if (!strcasecmp(iter, "true")) radconfig->ignore_source = 1; else if (!strcasecmp(iter, "false")) radconfig->ignore_source = 0; else die("expected true or false after ignore-source on line %d", file->lineno); break; default: die("unknown keyword on line %d", file->lineno); } } } } CONFfclose(file); if (!radconf->radhost) die("no radius host specified"); else if (!radconf->secret) die("no shared secret with radius host specified"); return(0); } #define PW_AUTH_UDP_PORT 1645 #define PW_AUTHENTICATION_REQUEST 1 #define PW_AUTHENTICATION_ACK 2 #define PW_AUTHENTICATION_REJECT 3 #define PW_USER_NAME 1 #define PW_PASSWORD 2 #define PW_SERVICE_TYPE 6 #define PW_SERVICE_AUTH_ONLY 8 #define RAD_NAS_IP_ADDRESS 4 /* IP address */ #define RAD_NAS_PORT 5 /* Integer */ static void req_copyto (auth_req *to, sending_t *from) { *to = from->req; } static void req_copyfrom (sending_t *to, auth_req *from) { to->req = *from; } static int rad_auth(rad_config_t *radconfig, char *uname, char *pass) { auth_req req; int i, j, jlen, passstart; unsigned char secbuf[128]; char hostname[SMBUF]; unsigned char digest[MD5_DIGESTSIZE]; struct timeval seed; struct sockaddr_in sinl; int sock; struct hostent *hent; int passlen; time_t now, end; struct timeval tmout; int got; fd_set rdfds; uint32_t nvalue; socklen_t slen; int authtries= 3; /* number of times to try reaching the radius server */ rad_config_t *config; sending_t *reqtop, *sreq, *new; int done; /* set up the linked list */ config = radconfig; if (config == NULL) { warn("no configuration file"); return(-2); } else { /* setting sreq to NULL guarantees reqtop will be properly set later */ sreq = NULL; reqtop = NULL; } while (config != NULL){ new = xmalloc(sizeof(sending_t)); new->next = NULL; if (sreq == NULL){ reqtop = new; sreq = new; } else { sreq->next = new; sreq = sreq->next; } req_copyto(&req, sreq); /* first, build the sockaddrs */ memset(&sinl, '\0', sizeof(sinl)); memset(&sreq->sinr, '\0', sizeof(sreq->sinr)); sinl.sin_family = AF_INET; sreq->sinr.sin_family = AF_INET; if (config->lochost == NULL) { if (gethostname(hostname, sizeof(hostname)) != 0) { syswarn("cannot get local hostname"); return(-2); } config->lochost = xstrdup(hostname); } if (config->lochost) { if (inet_aton(config->lochost, &sinl.sin_addr) != 1) { if ((hent = gethostbyname(config->lochost)) == NULL) { warn("cannot gethostbyname lochost %s", config->lochost); return(-2); } memcpy(&sinl.sin_addr.s_addr, hent->h_addr, sizeof(struct in_addr)); } } if (inet_aton(config->radhost, &sreq->sinr.sin_addr) != 1) { if ((hent = gethostbyname(config->radhost)) == NULL) { warn("cannot gethostbyname radhost %s", config->radhost); return(-2); } memcpy(&sreq->sinr.sin_addr.s_addr, hent->h_addr_list[0], sizeof(struct in_addr)); } if (config->radport) sreq->sinr.sin_port = htons(config->radport); else sreq->sinr.sin_port = htons(PW_AUTH_UDP_PORT); /* seed the random number generator for the auth vector */ gettimeofday(&seed, 0); srandom((unsigned) seed.tv_sec+seed.tv_usec); /* build the visible part of the auth vector randomly */ for (i = 0; i < AUTH_VECTOR_LEN; i++) req.vector[i] = random() % 256; strlcpy((char *) secbuf, config->secret, sizeof(secbuf)); memcpy(secbuf+strlen(config->secret), req.vector, AUTH_VECTOR_LEN); md5_hash(secbuf, strlen(config->secret)+AUTH_VECTOR_LEN, digest); /* fill in the auth_req data */ req.code = PW_AUTHENTICATION_REQUEST; req.id = 0; /* bracket the username in the configured prefix/suffix */ req.data[0] = PW_USER_NAME; req.data[1] = 2; req.data[2] = '\0'; if (config->prefix) { req.data[1] += strlen(config->prefix); strlcat((char *) &req.data[2], config->prefix, sizeof(req.data) - 2); } req.data[1] += strlen(uname); strlcat((char *)&req.data[2], uname, sizeof(req.data) - 2); if (!strchr(uname, '@') && config->suffix) { req.data[1] += strlen(config->suffix); strlcat((char *)&req.data[2], config->suffix, sizeof(req.data) - 2); } req.datalen = req.data[1]; /* set the password */ passstart = req.datalen; req.data[req.datalen] = PW_PASSWORD; /* Null pad the password */ passlen = (strlen(pass) + 15) / 16; passlen *= 16; req.data[req.datalen+1] = passlen+2; strlcpy((char *)&req.data[req.datalen+2], pass, sizeof(req.data) - req.datalen - 2); passlen -= strlen(pass); while (passlen--) req.data[req.datalen+passlen+2+strlen(pass)] = '\0'; req.datalen += req.data[req.datalen+1]; /* Add NAS_PORT and NAS_IP_ADDRESS into request */ if ((nvalue = config->locport) == 0) nvalue = RADIUS_LOCAL_PORT; req.data[req.datalen++] = RAD_NAS_PORT; req.data[req.datalen++] = sizeof(nvalue) + 2; nvalue = htonl(nvalue); memcpy(req.data + req.datalen, &nvalue, sizeof(nvalue)); req.datalen += sizeof(nvalue); req.data[req.datalen++] = RAD_NAS_IP_ADDRESS; req.data[req.datalen++] = sizeof(struct in_addr) + 2; memcpy(req.data + req.datalen, &sinl.sin_addr.s_addr, sizeof(struct in_addr)); req.datalen += sizeof(struct in_addr); /* we're only doing authentication */ req.data[req.datalen] = PW_SERVICE_TYPE; req.data[req.datalen+1] = 6; req.data[req.datalen+2] = (PW_SERVICE_AUTH_ONLY >> 24) & 0x000000ff; req.data[req.datalen+3] = (PW_SERVICE_AUTH_ONLY >> 16) & 0x000000ff; req.data[req.datalen+4] = (PW_SERVICE_AUTH_ONLY >> 8) & 0x000000ff; req.data[req.datalen+5] = PW_SERVICE_AUTH_ONLY & 0x000000ff; req.datalen += req.data[req.datalen+1]; /* filled in the data, now we know what the actual length is. */ req.length = 4+AUTH_VECTOR_LEN+req.datalen; /* "encrypt" the password */ for (i = 0; i < req.data[passstart+1]-2; i += sizeof(HASH)) { jlen = sizeof(HASH); if (req.data[passstart+1]-(unsigned)i-2 < sizeof(HASH)) jlen = req.data[passstart+1]-i-2; for (j = 0; j < jlen; j++) req.data[passstart+2+i+j] ^= digest[j]; if (jlen == sizeof(HASH)) { /* Recalculate the digest from the HASHed previous */ strlcpy((char *) secbuf, config->secret, sizeof(secbuf)); memcpy(secbuf+strlen(config->secret), &req.data[passstart+2+i], sizeof(HASH)); md5_hash(secbuf, strlen(config->secret)+sizeof(HASH), digest); } } sreq->reqlen = req.length; req.length = htons(req.length); req_copyfrom(sreq, &req); /* Go to the next record in the list */ config = config->next; } /* YAYY! The auth_req is ready to go! Build the reply socket and send out * the message. */ /* now, build the sockets */ if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { syswarn("cannot build reply socket"); return(-1); } if (bind(sock, (struct sockaddr*) &sinl, sizeof(sinl)) < 0) { syswarn("cannot bind reply socket"); close(sock); return(-1); } for(done = 0; authtries > 0 && !done; authtries--) { for (config = radconfig, sreq = reqtop; sreq != NULL && !done; config = config->next, sreq = sreq->next){ req_copyto(&req, sreq); /* send out the packet and wait for reply. */ if (sendto(sock, (char *)&req, sreq->reqlen, 0, (struct sockaddr*) &sreq->sinr, sizeof (struct sockaddr_in)) < 0) { syswarn("cannot send auth_reg"); close(sock); return(-1); } /* wait 5 seconds maximum for a radius reply. */ now = time(0); end = now+5; tmout.tv_sec = 6; tmout.tv_usec = 0; FD_ZERO(&rdfds); /* store the old vector to verify next checksum */ memcpy(secbuf+sizeof(req.vector), req.vector, sizeof(req.vector)); FD_SET(sock, &rdfds); got = select(sock+1, &rdfds, 0, 0, &tmout); if (got < 0) { syswarn("cannot not select"); break; } else if (got == 0) { /* timer ran out */ now = time(0); tmout.tv_sec = end - now + 1; tmout.tv_usec = 0; warn("timeout talking to remote radius server %s:%d", inet_ntoa(sreq->sinr.sin_addr), ntohs(sreq->sinr.sin_port)); continue; } slen = sizeof(sinl); if ((jlen = recvfrom(sock, (char *)&req, sizeof(req)-sizeof(int), 0, (struct sockaddr*) &sinl, &slen)) < 0) { syswarn("cannot recvfrom"); break; } if (!config->ignore_source) { if (sinl.sin_addr.s_addr != sreq->sinr.sin_addr.s_addr || (sinl.sin_port != sreq->sinr.sin_port)) { warn("received unexpected UDP packet from %s:%d", inet_ntoa(sinl.sin_addr), ntohs(sinl.sin_port)); continue; } } sreq->reqlen = ntohs(req.length); if (jlen < 4+AUTH_VECTOR_LEN || jlen != sreq->reqlen) { warn("received badly-sized packet"); continue; } /* verify the checksum */ memcpy(((char*)&req)+sreq->reqlen, config->secret, strlen(config->secret)); memcpy(secbuf, req.vector, sizeof(req.vector)); memcpy(req.vector, secbuf+sizeof(req.vector), sizeof(req.vector)); md5_hash((unsigned char *)&req, strlen(config->secret)+sreq->reqlen, digest); if (memcmp(digest, secbuf, sizeof(HASH)) != 0) { warn("checksum didn't match"); continue; } /* FINALLY! Got back a known-good packet. See if we're in. */ close(sock); return (req.code == PW_AUTHENTICATION_ACK) ? 0 : -1; done = 1; req_copyfrom(sreq, &req); break; } } if (authtries == 0) warn("cannot talk to any remote radius servers"); return(-2); } #define RAD_HAVE_HOST 1 #define RAD_HAVE_PORT 2 #define RAD_HAVE_PREFIX 4 #define RAD_HAVE_SUFFIX 8 #define RAD_HAVE_LOCHOST 16 #define RAD_HAVE_LOCPORT 32 int main(int argc, char *argv[]) { int opt; int havefile, haveother; struct auth_info *authinfo; rad_config_t radconfig; int retval; char *radius_config; message_program_name = "radius"; if (!innconf_read(NULL)) exit(1); memset(&radconfig, '\0', sizeof(rad_config_t)); haveother = havefile = 0; while ((opt = getopt(argc, argv, "f:h")) != -1) { switch (opt) { case 'f': if (haveother) die("-f flag after another flag"); if (!havefile) { /* override the standard config completely if the user * specifies an alternate config file */ memset(&radconfig, '\0', sizeof(rad_config_t)); havefile = 1; } read_config(optarg, &radconfig); break; case 'h': printf("Usage: radius [-f config]\n"); exit(0); } } if (argc != optind) exit(2); if (!havefile) { radius_config = concatpath(innconf->pathetc, INN_PATH_RADIUS_CONFIG); read_config(radius_config, &radconfig); free(radius_config); } authinfo = get_auth_info(stdin); if (authinfo == NULL) die("failed getting auth info"); if (authinfo->username[0] == '\0') die("empty username"); /* got username and password, check that they're valid */ retval = rad_auth(&radconfig, authinfo->username, authinfo->password); if (retval == -1) die("user %s password doesn't match", authinfo->username); else if (retval == -2) /* couldn't talk to the radius server.. output logged above. */ exit(1); else if (retval != 0) die("unexpected return code from authentication function: %d", retval); /* radius password matches! */ print_user(authinfo->username); exit(0); } inn-2.6.0/authprogs/ckpasswd.c0000644000175200017520000002651412575023702015727 0ustar iuliusiulius/* $Id: ckpasswd.c 9586 2013-12-14 20:09:55Z iulius $ ** ** The default username/password authenticator. ** ** This program is intended to be run by nnrpd and handle usernames and ** passwords. It can authenticate against a regular flat file (the type ** managed by htpasswd), a DBM file, the system password file or shadow file, ** or PAM. */ #include "config.h" #include "clibrary.h" #include "inn/messages.h" #include "inn/qio.h" #include "inn/vector.h" #include "inn/libinn.h" #include "libauth.h" #if HAVE_CRYPT_H # include #endif #include #include #include /* ** If compiling with Berkeley DB, use its ndbm compatibility layer ** in preference to other libraries. */ #if defined(HAVE_DBM) || defined(HAVE_BDB_NDBM) # if HAVE_BDB_NDBM # define DB_DBM_HSEARCH 1 # include # elif HAVE_NDBM_H # include # elif HAVE_DB1_NDBM_H # include # elif HAVE_GDBM_SLASH_NDBM_H # include # elif HAVE_GDBM_HYPHEN_NDBM_H # include # endif # define OPT_DBM "d:" #else # define OPT_DBM "" #endif #if HAVE_GETSPNAM # include # define OPT_SHADOW "s" #else # define OPT_SHADOW "" #endif #if HAVE_PAM # if HAVE_SECURITY_PAM_APPL_H # include # else # include # endif #endif /* ** The PAM conversation function. ** ** Since we already have all the information and can't ask the user ** questions, we can't quite follow the real PAM protocol. Instead, we just ** return the password in response to every question that PAM asks. There ** appears to be no generic way to determine whether the message in question ** is indeed asking for the password.... ** ** This function allocates an array of struct pam_response to return to the ** PAM libraries that's never freed. For this program, this isn't much of an ** issue, since it will likely only be called once and then the program will ** exit. This function uses malloc and strdup instead of xmalloc and xstrdup ** intentionally so that the PAM conversation will be closed cleanly if we ** run out of memory rather than simply terminated. ** ** appdata_ptr contains the password we were given. */ #if HAVE_PAM static int pass_conv(int num_msg, PAM_CONST struct pam_message **msgm UNUSED, struct pam_response **response, void *appdata_ptr) { int i; *response = malloc(num_msg * sizeof(struct pam_response)); if (*response == NULL) return PAM_CONV_ERR; for (i = 0; i < num_msg; i++) { (*response)[i].resp = strdup((char *)appdata_ptr); (*response)[i].resp_retcode = 0; } return PAM_SUCCESS; } #endif /* HAVE_PAM */ /* ** Authenticate a user via PAM. ** ** Attempts to authenticate a user with PAM, returning true if the user ** successfully authenticates and false otherwise. Note that this function ** doesn't attempt to handle any remapping of the authenticated user by the ** PAM stack, but just assumes that the authenticated user was the same as ** the username given. ** ** Right now, all failures are handled via die. This may be worth revisiting ** in case we want to try other authentication methods if this fails for a ** reason other than the system not having PAM support. */ #if !HAVE_PAM static bool auth_pam(char *username UNUSED, char *password UNUSED) { return false; } #else static bool auth_pam(const char *username, char *password) { pam_handle_t *pamh; struct pam_conv conv; int status; conv.conv = pass_conv; conv.appdata_ptr = password; status = pam_start("nnrpd", username, &conv, &pamh); if (status != PAM_SUCCESS) die("pam_start failed: %s", pam_strerror(pamh, status)); status = pam_authenticate(pamh, PAM_SILENT); if (status != PAM_SUCCESS) die("pam_authenticate failed: %s", pam_strerror(pamh, status)); status = pam_acct_mgmt(pamh, PAM_SILENT); if (status != PAM_SUCCESS) die("pam_acct_mgmt failed: %s", pam_strerror(pamh, status)); status = pam_end(pamh, status); if (status != PAM_SUCCESS) die("pam_end failed: %s", pam_strerror(pamh, status)); /* If we get to here, the user successfully authenticated. */ return true; } #endif /* HAVE_PAM */ /* ** Try to get a password out of a dbm file. The dbm file should have the ** username for the key and the crypted password as the value. The crypted ** password, if found, is returned as a newly allocated string; otherwise, ** NULL is returned. */ #if !(defined(HAVE_DBM) || defined(HAVE_BDB_NDBM)) static char * password_dbm(char *user UNUSED, const char *file UNUSED) { return NULL; } #else static char * password_dbm(char *name, const char *file) { datum key, value; DBM *database; char *password; database = dbm_open(file, O_RDONLY, 0600); if (database == NULL) return NULL; key.dptr = name; key.dsize = strlen(name); value = dbm_fetch(database, key); if (value.dptr == NULL) { dbm_close(database); return NULL; } password = xmalloc(value.dsize + 1); strlcpy(password, value.dptr, value.dsize + 1); dbm_close(database); return password; } #endif /* HAVE_DBM || HAVE_BDB_NDBM */ /* ** Try to get a password out of the system /etc/shadow file. The crypted ** password, if found, is returned as a newly allocated string; otherwise, ** NULL is returned. */ #if !HAVE_GETSPNAM static char * password_shadow(const char *user UNUSED) { return NULL; } #else static char * password_shadow(const char *user) { struct spwd *spwd; spwd = getspnam(user); if (spwd != NULL) return xstrdup(spwd->sp_pwdp); return NULL; } #endif /* HAVE_GETSPNAM */ /* ** Try to get a password out of a file. The crypted password, if found, is ** returned as a newly allocated string; otherwise, NULL is returned. */ static char * password_file(const char *username, const char *file) { QIOSTATE *qp; char *line, *password; struct cvector *info = NULL; qp = QIOopen(file); if (qp == NULL) return NULL; for (line = QIOread(qp); line != NULL; line = QIOread(qp)) { if (*line == '#' || *line == '\n') continue; info = cvector_split(line, ':', info); if (info->count < 2 || strcmp(info->strings[0], username) != 0) continue; password = xstrdup(info->strings[1]); QIOclose(qp); cvector_free(info); return password; } if (QIOtoolong(qp)) die("line too long in %s", file); if (QIOerror(qp)) sysdie("error reading %s", file); QIOclose(qp); cvector_free(info); return NULL; } /* ** Try to get a password out of the system password file. The crypted ** password, if found, is returned as a newly allocated string; otherwise, ** NULL is returned. */ static char * password_system(const char *username) { struct passwd *pwd; pwd = getpwnam(username); if (pwd != NULL) return xstrdup(pwd->pw_passwd); return NULL; } /* ** Try to get the name of a user's primary group out of the system group ** file. The group, if found, is returned as a newly allocated string; ** otherwise, NULL is returned. If the username is not found, NULL is ** returned. */ static char * group_system(const char *username) { struct passwd *pwd; struct group *gr; pwd = getpwnam(username); if (pwd == NULL) return NULL; gr = getgrgid(pwd->pw_gid); if (gr == NULL) return NULL; return xstrdup(gr->gr_name); } /* ** Output username (and group, if desired) in correct return format. */ static void output_user(const char *username, bool wantgroup) { if (wantgroup) { char *group = group_system(username); if (group == NULL) die("group info for user %s not available", username); printf("User:%s@%s\r\n", username, group); } else print_user(username); } /* ** Main routine. ** ** We handle the variences between systems with #if blocks above, so that ** this code can look fairly clean. */ int main(int argc, char *argv[]) { enum authtype { AUTH_NONE, AUTH_SHADOW, AUTH_FILE, AUTH_DBM }; int opt; enum authtype type = AUTH_NONE; bool wantgroup = false; const char *filename = NULL; struct auth_info *authinfo = NULL; char *password = NULL; message_program_name = "ckpasswd"; while ((opt = getopt(argc, argv, "gf:u:p:" OPT_DBM OPT_SHADOW)) != -1) { switch (opt) { case 'g': if (type == AUTH_DBM || type == AUTH_FILE) die("-g option is incompatible with -d or -f"); wantgroup = true; break; case 'd': if (type != AUTH_NONE) die("only one of -s, -f, or -d allowed"); if (wantgroup) die("-g option is incompatible with -d or -f"); type = AUTH_DBM; filename = optarg; break; case 'f': if (type != AUTH_NONE) die("only one of -s, -f, or -d allowed"); if (wantgroup) die("-g option is incompatible with -d or -f"); type = AUTH_FILE; filename = optarg; break; case 's': if (type != AUTH_NONE) die("only one of -s, -f, or -d allowed"); type = AUTH_SHADOW; break; case 'u': if (authinfo == NULL) { authinfo = xmalloc(sizeof(struct auth_info)); authinfo->password = NULL; } authinfo->username = optarg; break; case 'p': if (authinfo == NULL) { authinfo = xmalloc(sizeof(struct auth_info)); authinfo->username = NULL; } authinfo->password = optarg; break; default: exit(1); } } if (argc != optind) die("extra arguments given"); if (authinfo != NULL && authinfo->username == NULL) die("-u option is required if -p option is given"); if (authinfo != NULL && authinfo->password == NULL) die("-p option is required if -u option is given"); /* Unless a username or password was given on the command line, assume we're being run by nnrpd. */ if (authinfo == NULL) authinfo = get_auth_info(stdin); if (authinfo == NULL) die("no authentication information from nnrpd"); if (authinfo->username[0] == '\0') die("null username"); /* Run the appropriate authentication routines. */ switch (type) { case AUTH_SHADOW: password = password_shadow(authinfo->username); if (password == NULL) password = password_system(authinfo->username); break; case AUTH_FILE: password = password_file(authinfo->username, filename); break; case AUTH_DBM: password = password_dbm(authinfo->username, filename); break; case AUTH_NONE: if (auth_pam(authinfo->username, authinfo->password)) { output_user(authinfo->username, wantgroup); exit(0); } password = password_system(authinfo->username); break; } if (password == NULL) die("user %s unknown", authinfo->username); if (strcmp(password, crypt(authinfo->password, password)) != 0) die("invalid password for user %s", authinfo->username); /* The password matched. */ output_user(authinfo->username, wantgroup); exit(0); } inn-2.6.0/contrib/0000755000175200017520000000000012575023677013373 5ustar iuliusiuliusinn-2.6.0/contrib/stathist.in0000644000175200017520000000344012575023702015554 0ustar iuliusiulius#!/usr/bin/perl -w # Parse log files created by innd history profiler # 2001/01/29 - Fabien Tassin use strict; use FileHandle; my $file = shift || "stathist.log"; if ($file eq '-h' || $file eq '--help') { print "Usage: stathist [logfile]\n"; exit 0; } sub parse { my $file = shift; my $f = new FileHandle $file; unless (defined $f) { print STDERR "Can't open file: $!\n"; return {}; } my $data = {}; my $begin = 1; my @stack = (); while (defined (my $line = <$f>)) { next if $begin && $line !~ / HIS(havearticle|write|setup) begin/; $begin = 0; chomp $line; my @c = split /[\[\]\(\) ]+/, $line; ($c[4] eq 'begin') && do { push @stack, $c[3]; my $d = $data; for my $l (@stack) { unless (defined $$d{$l}) { $$d{$l}{'min'} = 1E10; $$d{$l}{'total'} = $$d{$l}{'count'} = $$d{$l}{'max'} = 0; } $d = $$d{$l} } } || ($c[4] eq 'end') && do { my $d = $data; for my $l (@stack) { $d = $$d{$l}; } $$d{'count'}++; $$d{'total'} += $c[5]; $$d{'min'} = $c[5] if $$d{'min'} > $c[5]; $$d{'max'} = $c[5] if $$d{'max'} < $c[5]; pop @stack; }; } $f->close; $data; } sub report { my $data = shift; my $inc = shift; unless (defined $inc) { printf "%-16s %10s %14s %10s %10s %10s\n\n", "Function", "Invoked", "Total(s)", "Min(ms)", "Avg(ms)", "Max(ms)"; $inc = 0; } for my $key (sort keys %$data) { next unless $key =~ m/^HIS/; printf "%-16s %10d %14.6f %10.3f %10.3f %10.3f\n", (' ' x $inc) . $key, $$data{$key}{'count'}, $$data{$key}{'total'}, $$data{$key}{'min'} * 1000, $$data{$key}{'total'} / $$data{$key}{'count'} * 1000, $$data{$key}{'max'} * 1000; &report($$data{$key}, $inc + 1) } } my $data = &parse($file); &report($data); inn-2.6.0/contrib/authmysql.config0000644000175200017520000000104012575023702016571 0ustar iuliusiulius# Config for authmysql.pl # Format # Label:Column # Needed labels are User, Pass, DB, HOST, usercol, passcol and groupcol # The Column refers to either the detalis needed to connect to the # MySQL database or the column for the table. DB:blah # the database to connect to HOST:localhost # which host it resides on TABLE:passwd # the table to query against User:foo # who to connect as Pass:bar # pass for above usercol:username # column which containts the username passcol:password # column which includes unix crypt style password inn-2.6.0/contrib/mm_ckpasswd0000755000175200017520000000432412575023702015621 0ustar iuliusiulius#!/usr/bin/env python # P@draigBrady.com # # This is a ckpasswd equivalent that verifies user passwords against the # passwords stored in a Mailman list configuration file. It allows users # to access newsgroups with the same password as they use to access their # Mailman configuration. # # To use this program, it needs to run as the Mailman user, and therefore # may have to be run from a wrapper. Other than that, use it like ckpasswd # in a readers.conf file as an auth parameter, passing it the configuration # file for the list as its only parameter. # # This program is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the Free # Software Foundation; either version 2 of the License, or (at your option) # any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License # for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, # Inc., 675 Mass Ave, Cambridge, MA 02139, USA. """Reads username and password from stdin, and database to use as first parameter. and is meant to be used like ckpasswd is. returns: 0 success 1 bad username &/or password 2 other errors""" import sys import marshal import string import os os.setgid(41) try: filename = sys.argv[1] except IndexError: sys.stderr.write("Usage: " + sys.argv[0] + " /path/to/config.db\n") sys.exit(2) fp = open(filename) d = marshal.load(fp) passwords=d['passwords'] fp.close() username="" password="" while 1: line=sys.stdin.readline() if line == '': break try: name, value = map(string.strip,line.split(':')) if name == "ClientAuthname": username=value if name == "ClientPassword": password=value except: pass if not username or not password: sys.exit(2) auth=0 try: if passwords[username] == password: auth=1 except: pass if auth==1: print "User:%s" % username sys.exit(0) else: sys.exit(1) inn-2.6.0/contrib/nnrp.access2readers.conf.in0000644000175200017520000000717012575023702020506 0ustar iuliusiulius#! /usr/bin/perl -w ## $Id: nnrp.access2readers.conf.in 9772 2014-12-16 20:46:02Z iulius $ ## ## Convert an old-style nnrp.access file to readers.conf format. ## (readers.conf replaced nnrp.access, starting with INN 2.3.0.) ## ## Written by Jeffrey M. Vinocur . ## ## This work is hereby placed in the public domain by its author. use strict; my ($src, $passwd, $dst, $debug); if (@ARGV == 1) { $src = shift; $passwd = 'passwd'; $debug = 1; } elsif (@ARGV == 3) { ($src, $passwd, $dst) = @ARGV; $debug = 0; } else { usage(); } my ($address, $access, $user, $pass, $groups); my ($noauth, $shadow); my %passwords; my @badsyntax; my @duplicates; my $OUT; open(SRC, $src) or die "Could not open $src: $!\n"; if ($debug) { $OUT = \*STDOUT; } else { open(DST, ">$dst") or die "Could not open $dst: $!\n"; $OUT = \*DST; } while () { chomp; # Keep comments and blank lines. if (/^\s*$/) { print $OUT "\n"; next; } if (/(\#.*)/) { print $OUT "$1\n"; } s/\s*\#.*//; next if /^$/; # TODO - The following syntax is currently not recognized: # host:/path/to/another/file # %DEFINE0:bar.* # bar.foo.net:RP:::%0 ($address, $access, $user, $pass, $groups) = split /:/; unless (defined $groups) { push (@badsyntax, $.); next; } # If there is whitespace in user/pass, access is disabled. $noauth = ($user =~ /\s/ or $pass =~ /\s/) ? 1 : 0; # If user is '+', then authentication is against /etc/shadow. $shadow = ($user eq '+') ? 1 : 0; $user = '' if ($noauth or $shadow); # Only keep R, P, N, and L for the access rights. $access =~ s/[^RPNL]//g; unless ($user eq '') { push (@duplicates, $.) if defined ($passwords{$user}); $passwords{$user} = $pass; } print $OUT "auth \"$address\" {\n"; print $OUT " hosts: \"$address\"\n"; print $OUT " key: \"line$.\"\n"; print $OUT " auth: \"ckpasswd -f $passwd\"\n" unless $user eq ''; print $OUT " auth: \"ckpasswd -s\"\n" if $shadow; print $OUT " default: \"\"\n" if $user eq '' and not $shadow; print $OUT "}\n"; print $OUT "access \"$address\" {\n"; print $OUT " key: \"line$.\"\n"; print $OUT " newsgroups: \"$groups\"\n"; print $OUT " access: \"$access\"\n"; print $OUT " users: \"$user\"\n" unless $user eq ''; print $OUT " reject_with: \"Access disabled.\"\n" if $noauth; print $OUT "}\n\n"; } close SRC; srand($$^time); if (!$debug) { close $OUT; open(PASSWD, ">$passwd") or die "Could not open $passwd: $!\n"; $OUT = \*PASSWD; } while (my ($user, $pass) = each %passwords) { $pass = crypt($pass, seedchar().seedchar()); print $OUT "$user:$pass\n"; } close PASSWD unless $debug; print STDERR "\nHad trouble with syntax on line", @badsyntax > 1 ? 's ' : ' ', join(", ", @badsyntax), ".\n" if @badsyntax; print STDERR "\nFound username duplication on line", @duplicates > 1 ? 's ' : ' ', join(", ", @duplicates), ".\n" if @duplicates; sub seedchar { # From Randal Schwarz. ('a'..'z','A'..'Z','0'..'9','.','/')[rand(64)]; } sub usage { print STDERR << "__END__"; Usage: $0 [ ] Use $0 to convert an old-style nnrp.access file to readers.conf format. All unique user/password pairs from the nnrp.access file are placed in the passwd file (in format suitable for ckpasswd). If the second and third arguments are missing, everything is printed instead to stdout for examination purposes. __END__ exit 1; } inn-2.6.0/contrib/auth_pass.README0000644000175200017520000000707712575023702016241 0ustar iuliusiuliusThis directory contains sample authorization programs for use with the AUTHINFO GENERIC command in nnrpd. The first program in here is from Doug Needham. I have successfully tested this program when connecting to nnrpd by hand, but I've not taken the time to figure out how to get my newsreader to use AUTHINFO GENERIC. There is no Makefile here and no serious testing of it, so it's not integrated. If you have success using it and care to share what you've done. Please drop me a note (). Thanks. Notes: * AUTHINFO GENERIC is currently deprecated in favour of AUTHINFO USER/PASS and AUTHINFO SASL, as described in RFC 4643. * In case you write to , please CC too. --------------------------------------------------------------------------- Replied: Fri, 26 Jul 1996 19:29:17 +0200 Replied: Douglas Wade Needham Received: by gw.home.vix.com id UAA05867; Thu, 25 Jul 1996 20:45:27 -0700 (PDT) Received: (from dneedham@localhost) by dneedham.inhouse.compuserve.com (8.7.4/8.6.9) id XAA21103; Thu, 25 Jul 1996 23:45:25 -0400 (EDT) From: Douglas Wade Needham Message-Id: <199607260345.XAA21103@dneedham.inhouse.compuserve.com> Subject: A sample program for authinfo generic (for inn 1.5) To: inn-workers@vix.com (INN Gurus/Workers) Date: Thu, 25 Jul 1996 23:45:25 -0400 (EDT) Cc: inn@isc.org, brister@vix.com (James A. Brister) X-Mailer: ELM [version 2.4 PL25] MIME-Version: 1.0 Content-Type: multipart/mixed; boundary=%#%record%#% Status: U --%#%record%#% Content-Type: text/plain; charset=US-ASCII Content-Transfer-Encoding: 7bit Content-Length: 1894 Hi folks... Finally started to get some time to clear some things from my todo list...Here is a sample program which can be used by "authinfo generic" to validate a user against the password file on the news host. While not a great example, it does demonstrate how you can write an authentication program. All I ask is that credit be given. A couple of notes that I have found out about these programs for those of you who may be interested in writing your own... 1) These programs have stdin and stdout connected all the way back to the reader, so they can carry on a dialog in whatever fashion they want to with the user's news reader. This can include passing Kerberos tickets, encrypted or hashed passwords, or doing a challenge-response type session for authenticating the user rather than passing the password in clear-text across the network. 2) Regardless of the outcome, the authentication program must send NNRPD a record such as is found in nnrp.access by writing it to stderr. 3) Successful authentication is indicated by a zero exit status, and unsuccessful authentication is indicated by a non-zero exit status. 4) Need I say it (again)...these programs can be a security hole unless care is taken to avoid SUID programs and those that transmit/recieve passwords in the clear (especially those that use login passwords). We should give some thought to doing a similiar program for Kerberos authentication (what sort of instance should we use???) and other authentication methods such as Compuserve's Distributed Authentication (guess I should do this one once the standard is finialized with the IETF 8) ). Also, a question for the list as a whole... what readers easily support authinfo generic (including running a program at the reader's end to do things like challenge-response)??? Well...here it is...enjoy 8)... - doug #### See auth_pass.c ##### inn-2.6.0/contrib/README0000644000175200017520000001401012575023702014234 0ustar iuliusiuliusThis directory contains unsupported contributions to INN. Most of these programs are of interest to a limited set of sites, require some manual modifications to make work, and/or are separately maintained independent of INN. Programs in here may or may not have been tested on the latest version of INN, so keep that in mind when trying them out. The INN developers may not be able to answer bug reports for these utilities; it's best to send them to the original author. Volunteers who would like to take particularly useful applications in this directory and make them suitable for inclusion in INN proper are heartily encouraged, but discuss this on . Sometimes there's a reason why this hasn't already been done or something specific that's needed before they can be included. Type "make " to build any of the following programs and then copy the binary to somewhere on your PATH to use it. For details on what each program does, see below, as well as the comments at the beginning of each file (if any). In addition to these files, also see the contrib section of the INN FTP site at for more software designed to work with INN. ------------------------- analyze-traffic.pl Analyzes which newsgroups are receiving the most traffic and which peer is most responsible for each high-traffic group. archivegz A compressing version of archive, writing out .gz files instead of plain text files. May not work with the storage API without some changes to use sm. authmysql An authenticator that checks a username and password against a MySQL database. Could also be easily modified to check against any other type of database that Perl's DBI module can talk to. auth_pass A sample authorization program for use with the (deprecated) AUTHINFO GENERIC command in nnrpd. backlogstat Prints informations about the current state of innfeed's backlog, if any. cleannewsgroups Performs various cleanups on the newsgroups file. count_overview.pl Counts the groups in a bunch of Xref records. delayer Sits in a data stream and delays it by some constant period of time. Mostly useful for delaying innfeed feeds to allow cancels a chance to remove articles before innfeed sends them to your peers. See the beginning of the file for an example of how to use it. expirectl Automatically builds expire.ctl based on current available disk space and a template, adjusting the expiration times of groups based on a weight and the available space. Uses a template expire.ctl.ctl file; see the end of expirectl.c for a sample. findreadgroups Scans the news log files and generates a file giving readership counts by newsgroup. Used by makeexpctl and makestorconf. fixhist Performs various cleanups and sanity checks on the history database. innconfcheck Merges your inn.conf settings with the inn.conf man page to make it easier to be sure that your settings match what you want. Edit this script to add the correct paths to the man page; see the comments at the beginning of this script. innreport-filter.xslt Copies individual sections from innreport's HTML files. makeexpctl Generates an expire.ctl based on what newsgroups are actually read. Uses data generated by findreadgroups. This script will require editing before being usable for your server. makestorconf Generates a storage.conf file putting frequently read newsgroups into timecaf rather than CNFS. Uses data gefnerated by findreadgroups. This script will require editing before being usable for your server. mkbuf Creates a CNFS cycbuff; see the comments at the beginning of this script. mlockfile Locks files given on the command line into memory using mlock (only tested on Solaris). Useful primarily for locking the history files (history.hash and history.index) into memory on a system with sufficient memory to speed history lookups in innd. This seems to help some systems quite a lot and others not at all. mm_ckpasswd A ckpasswd replacement that checks user passwords against a Mailman list configuration database. See the comments at the top of the script for information on how to use it. newsresp Opens an NNTP channel to a server and takes a peek at various response times. Can check the round-trip time and the history lookup time. See the comments at the beginning of the source for more details. nnrp.access2readers.conf Converts an old-style nnrp.access file to readers.conf format. (readers.conf replaced nnrp.access, starting with INN 2.3.0.) pullart Attempts to pull news articles out of CNFS cycbuffs. Useful for emergency recoveries. reset-cnfs Clears a CNFS cycbuff; see the comments at the beginning of this script. respool Takes a list of tokens on stdin and respools them, by retrieving the article, storing it again, and then calling SMcancel on the previous instance of the article. Note that after running this program, you'd need to rebuild the history and overview, since it doesn't update either. sample.init.script Tries to increase the file descriptor limits to the maximum allowed by the system since INN and related programs can be file descriptor hogs. It then starts INN. sample.init.systemd Sample systemd-style init script for INN. stathist Parses and summarizes the log files created by the history profiling code. thdexpire A dynamic expire daemon for timehash and timecaf spools. It should be started along with innd and periodically looks if news spool space is getting tight, and then frees space by removing articles until enough is free. It is an adjunct to (not a replacement for) INN's expire program. tunefeed Given two active files, attempts to produce a good set of wildmat patterns for newsfeeds to minimize the number of rejects. For full documentation, run "perldoc tunefeed". inn-2.6.0/contrib/cleannewsgroups.in0000644000175200017520000000300212575023702017122 0ustar iuliusiulius#! /usr/bin/perl -w # fixscript will replace this line with code to load INN::Config # This script cleans the newsgroups file: # * Groups no longer in the active file are removed. # * Duplicate entries are removed. The last of a set of duplicates # is the one retained. That way, you could simply append the # new/revised entries from a docheckgroups run and then this script # will remove the old ones. # * Groups with no description are removed. # * Groups matching the $remove regexp are removed. $remove=''; # $remove='^alt\.'; open ACT, $INN::Config::active or die "Can't open $INN::Config::active: $!\n"; while() { ($group) = split; $act{$group} = 1 unless($remove ne "" && $group =~ /$remove/o); } close ACT; open NG, $INN::Config::newsgroups or die "Can't open $INN::Config::newsgroups: $!\n"; while() { chomp; ($group, $desc) = split /\s+/,$_,2; next unless(defined $act{$group}); next if(!defined $desc); next if($desc =~ /^[?\s]*$/); next if($desc =~ /^no desc(ription)?(\.)?$/i); $hist{$group} = $desc; } close NG; open NG, ">$INN::Config::newsgroups.new" or die "Can't open $INN::Config::newsgroups.new for write: $!\n"; foreach $group (sort keys %act) { if(defined $hist{$group}) { print NG "$group\t$hist{$group}\n" or die "Can't write: $!\n"; } } close NG or die "Can't close: $!\n"; rename "$INN::Config::newsgroups.new", $INN::Config::newsgroups or die "Can't rename $INN::Config::newsgroups.new to $INN::Config::newsgroups: $!\n"; inn-2.6.0/contrib/count_overview.pl0000755000175200017520000000110312575023702016771 0ustar iuliusiulius#!/usr/local/bin/perl # # count_overview.pl: Count the groups in a bunch of xref records. while (<>) { chop; @xreflist = split(/\t/); # split apart record $_ = $xreflist[$#xreflist]; # xref is last. @xreflist = reverse(split(/ /)); #break part xref line. pop @xreflist; # get rid xref header pop @xreflist; while ($current = pop @xreflist) { ($current) = split(/:/,$current); #get newsgroup name $groups{$current}++; #tally } } # display accumulated groups and counts. foreach $current (sort keys %groups) { printf "%-50s\t%5d\n", $current, $groups{$current}; } inn-2.6.0/contrib/makestorconf.in0000644000175200017520000000222212575023702016401 0ustar iuliusiulius#! /usr/bin/perl -w # fixscript will replace this line with code to load INN::Config # Create storage.conf script based on recently read articles. $readfile="$INN::Config::pathdb/readgroups"; $outfile="$INN::Config::pathdb/storage.conf"; outloop: for ($level=9 ; $level >= 2; --$level) { # clear groups hash. foreach $i (keys %groups) { delete $groups{$i}; } if (open(RDF, "$INN::Config::sort $readfile|")) { while () { chop; next if (/^group/); # bogus @foo=split(/ /); # foo[0] should be group, foo[1] lastreadtime @bar=split(/\./,$foo[0]); if ( $level >= scalar @bar) { $grf = join(".", @bar); } else { $grf=join(".", @bar[0..($level-1)]) . ".*"; } $groups{$grf} = 1; } close(RDF); } $grlist = join(",",keys(%groups)); last outloop if (length($grlist) < 2048); } open(OUT, ">$outfile") || die "cant open $outfile"; #open(OUT, ">/dev/tty"); print OUT <<"EOF" ; method cnfs { newsgroups: control,control.* class: 1 options: MINI } method timecaf { newsgroups: $grlist class: 1 } method cnfs { newsgroups: * options: MONGO class: 0 } EOF close(OUT); exit(0); inn-2.6.0/contrib/respool.c0000644000175200017520000000447512575023702015221 0ustar iuliusiulius/* ** Refile articles into the storage manager under the current storage.conf ** rules, deleting articles from their old place in the spool. ** Written 10-09-99 by rmtodd@servalan.servalan.com ** ** Note that history and overview will have to be rebuilt for the moved ** articles to be visible after they're moved. */ /* include foo needed by libinn/storage manager */ #include "config.h" #include "clibrary.h" #include #include "inn/innconf.h" #include "inn/qio.h" #include "inn/libinn.h" #include "inn/paths.h" #include "inn/storage.h" char *ME; static void ProcessLine(char *line) { char *tokenptr; int len; ARTHANDLE *art; ARTHANDLE newart = ARTHANDLE_INITIALIZER; TOKEN token, newtoken; char *arttmp; tokenptr = line; /* zap newline at end of tokenptr, if present. */ len = strlen(tokenptr); if (tokenptr[len-1] == '\n') { tokenptr[len-1] = '\0'; } if (!IsToken(tokenptr)) { fprintf(stderr, "%s: bad token format %s\n", ME, tokenptr); return; } token = TextToToken(tokenptr); if ((art = SMretrieve(token, RETR_ALL)) == NULL) return; len = art->len; arttmp = xmalloc(len); memcpy(arttmp, art->data, len); SMfreearticle(art); if (!SMcancel(token)) { fprintf(stderr, "%s: cant cancel %s:%s\n", ME, tokenptr, SMerrorstr); return; } newart.data = arttmp; newart.len = len; newart.arrived = (time_t) 0; /* set current time */ newart.token = (TOKEN *)NULL; newtoken = SMstore(newart); if (newtoken.type == TOKEN_EMPTY) { fprintf(stderr, "%s: cant store article:%s\n", ME, SMerrorstr); return; } free(arttmp); printf("refiled %s ",TokenToText(token)); printf("to %s\n", TokenToText(newtoken)); return; } int main(int argc UNUSED, char *argv[]) { bool one = true; char buff[SMBUF]; ME = argv[0]; if (!innconf_read(NULL)) exit(1); if (!SMsetup(SM_PREOPEN, &one) || !SMsetup(SM_RDWR, (void *)&one)) { fprintf(stderr, "can't init storage manager"); exit(1); } if (!SMinit()) { fprintf(stderr, "Can't init storage manager: %s", SMerrorstr); } while (fgets(buff, SMBUF, stdin)) { ProcessLine(buff); } printf("\nYou will now need to rebuild history and overview for the moved" "\narticles to be visible again.\n"); exit(0); } inn-2.6.0/contrib/expirectl.c0000644000175200017520000002072612575023702015532 0ustar iuliusiulius/* * EXPIRECTL.C * * expirectl * * This program uses expire.ctl.ctl as input; please see the end of this * file for an example of such a file. */ /* * Date: Mon, 21 Nov 1994 12:29:52 -0801 * From: Matthew Dillon * Message-Id: <199411212030.MAA21835@apollo.west.oic.com> * To: rsalz@uunet.uu.net * Subject: Re: INN is great, bug fix for BSDI * * [...] * Oh, while I'm at it, I also wrote a cute program that builds the * expire.ctl file dynamically based on available space. Feel free * to include this in the dist (or not) as you please. * * Basically, the expirectl programs determines the amount of disk blocks * and inodes free in the spool and creates a new expire.ctl file based * on an expire.ctl.ctl template. The template specifies expiration times * as a fraction of nominal. expirectl adjusts the nominal expiration * up or down based on available disk space. * * The idea is to make expiration as hands off as possible. I tested * it on a smaller spool and it appeared to work fine. Currently it * only works for single-partition news spools tho. The above spool * will not really exercise the program for another 14 days or so :-). */ #include "config.h" #include "clibrary.h" #include #include /* Following portability code lifted from inndf.c */ #if HAVE_STATVFS # include # define df_stat(p, s) (statvfs((p), (s)) == 0) # define df_declare(s) struct statvfs s # define df_total(s) ((s).f_blocks) # define df_avail(s) ((s).f_bavail) # define df_scale(s) ((s).f_frsize == 0 ? (s).f_bsize : (s).f_frsize) # define df_files(s) ((s).f_files) # define df_favail(s) ((s).f_favail) #elif HAVE_STATFS # if HAVE_SYS_VFS_H # include # endif # if HAVE_SYS_PARAM_H # include # endif # if HAVE_SYS_MOUNT_H # include # endif # ifdef __ultrix__ # define df_stat(p, s) (statfs((p), (s)) >= 1) # define df_declare(s) struct fs_data s # define df_total(s) ((s).fd_btot) # define df_avail(s) ((s).fd_bfreen) # define df_scale(s) 1024 # define df_files(s) ((s).fd_gtot) # define df_favail(s) ((s).fd_gfree) # else # define df_stat(p, s) (statfs((p), (s)) == 0) # define df_declare(s) struct statfs s # define df_total(s) ((s).f_blocks) # define df_avail(s) ((s).f_bavail) # define df_scale(s) ((s).f_bsize) # define df_files(s) ((s).f_files) # define df_favail(s) ((s).f_ffree) # endif #else # error "Platform not supported. Neither statvfs nor statfs available." #endif #define EXPIRE_CTL_DIR "/home/news" #define NEWS_SPOOL "/home/news/spool/news/." #define EXPIRE_DAYS EXPIRE_CTL_DIR "/expire.days" #define EXPIRE_CTL EXPIRE_CTL_DIR "/expire.ctl" #define EXPIRE_CTL_CTL EXPIRE_CTL_DIR "/expire.ctl.ctl" int main(int ac, char **av) { df_declare(sfs); long minFree = 100 * 1024 * 1024; long minIFree = 20 * 1024; long expireDays = 2; time_t expireIncTime = time(NULL) - 24 * 60 * 60; int modified = 0; int verbose = 0; /* * options */ { int i; for (i = 1; i < ac; ++i) { char *ptr = av[i]; if (*ptr == '-') { ptr += 2; switch(ptr[-1]) { case 'v': verbose = 1; break; case 'f': modified = 1; break; case 'n': modified = -1; break; case 'b': minFree = strtol(((*ptr) ? ptr : av[++i]), &ptr, 0); if (*ptr == 'k') minFree *= 1024; if (*ptr == 'm') minFree *= 1024 * 1024; break; case 'i': minIFree = strtol(((*ptr) ? ptr : av[++i]), NULL, 0); if (*ptr == 'k') minIFree *= 1024; if (*ptr == 'm') minIFree *= 1024 * 1024; break; default: fprintf(stderr, "bad option: %s\n", ptr - 2); exit(1); } } else { fprintf(stderr, "bad option: %s\n", ptr); exit(1); } } } if (!df_stat("/home/news/spool/news/.", &sfs)) { fprintf(stderr, "expirectl: couldn't fsstat /home/news/spool/news/.\n"); exit(1); } /* * Load /home/news/expire.days */ { FILE *fi; char buf[256]; if ((fi = fopen(EXPIRE_DAYS, "r")) != NULL) { while (fgets(buf, sizeof(buf), fi) != NULL) { if (strncmp(buf, "time", 4) == 0) { expireIncTime = strtol(buf + 4, NULL, 0); } else if (strncmp(buf, "days", 4) == 0) { expireDays = strtol(buf + 4, NULL, 0); } } fclose(fi); } else { if (modified >= 0) modified = 1; printf("creating %s\n", EXPIRE_DAYS); } } /* * print status */ if (verbose) { printf("spool: %4.2lfM / %3.2lfKinode free\n", (double)df_scale(sfs) * (double)df_avail(sfs) / (1024.0 * 1024.0), (double)df_favail(sfs) / 1024.0 ); printf("decrs: %4.2lfM / %3.2lfKinode\n", (double)(minFree) / (double)(1024*1024), (double)(minIFree) / (double)(1024) ); printf("incrs: %4.2lfM / %3.2lfKinode\n", (double)(minFree * 2) / (double)(1024*1024), (double)(minIFree * 2) / (double)(1024) ); } /* * Check limits, update as appropriate */ { double bytes; long inodes; bytes = (double)df_scale(sfs) * (double)df_avail(sfs); inodes = df_favail(sfs); if (bytes < (double)minFree || inodes < minIFree) { if (--expireDays <= 0) { expireDays = 1; expireIncTime = time(NULL) - 24 * 60 * 60; } if (modified >= 0) modified = 1; printf("decrement expiration to %ld days\n", expireDays); } else if (bytes >= (double)minFree * 2.0 && inodes >= minIFree * 2) { long dt = (long)(time(NULL) - expireIncTime); if (dt >= 60 * 60 * 24 || dt < -60) { ++expireDays; expireIncTime = time(NULL); if (modified >= 0) modified = 1; printf("increment expiration to %ld days\n", expireDays); } else { printf("will increment expiration later\n"); } } else if (verbose) { printf("expiration unchanged: %ld\n", expireDays); } } /* * Write EXPIRE_CTL file from EXPIRE_CTL_CTL template */ if (modified > 0) { FILE *fi; FILE *fo; if ((fi = fopen(EXPIRE_CTL_CTL, "r")) != NULL) { if ((fo = fopen(EXPIRE_CTL ".tmp", "w")) != NULL) { char sbuf[2048]; char dbuf[4096]; while (fgets(sbuf, sizeof(sbuf), fi) != NULL) { char *base = sbuf; char *sptr; char *dptr = dbuf; while ((sptr = strchr(base, '[')) != NULL) { double d; int m = 0; bcopy(base, dptr, sptr - base); dptr += sptr - base; d = strtod(sptr + 1, &sptr); if (*sptr == '/') m = strtol(sptr + 1, &sptr, 0); if (*sptr == ']') { long v = (long)((double)expireDays * d + 0.5); if (v < 1) v = 1; if (v < m) v = m; sprintf(dptr, "%ld", v); dptr += strlen(dptr); ++sptr; } base = sptr; } strcpy(dptr, base); fputs(dbuf, fo); } fclose(fo); if (rename(EXPIRE_CTL ".tmp", EXPIRE_CTL) != 0) { fprintf(stderr, "rename(%s,%s): %s\n", EXPIRE_CTL ".tmp", EXPIRE_CTL, strerror(errno) ); } } fclose(fi); } } /* * Write EXPIRE_DAYS file */ if (modified > 0) { FILE *fo; if ((fo = fopen(EXPIRE_DAYS, "w")) != NULL) { fprintf(fo, "time 0x%08lx\n", (unsigned long) expireIncTime); fprintf(fo, "days %ld\n", expireDays); fclose(fo); } else { fprintf(stderr, "unable to create %s\n", EXPIRE_DAYS); } } exit(0); } /* # Start of sample expire.ctl.ctl file. # EXPIRE.CTL.CTL (EXPIRE.CTL GENERATED FROM EXPIRE.CTL.CTL !!!) # # The expire.ctl file is generated by the expirectl program from the # expire.ctl.ctl file. The expirectl program calculates the proper # expiration based on the number of free inodes and free bytes available. # # This file is exactly expire.ctl but with the multiplier [N] replaced by # a calculated value, where a multiplier of '1' nominally fills the whole # disk. # # Any field [N] is substituted after being multiplied by the expiration # time (in days). A integer minimum can also be specified with a slash, # as in [N/minimum]. # # expirectl is normally run just after expire is run. Note that expirectl # isn't very useful for the case where you are 'catching up' on news after # a long period of downtime UNLESS you use the -p option to expire. /remember/:[1.2/20] ## Keep for 1-10 days, allow Expires headers to work. # *:A:1:[1.0]:[6.0] *.advocacy:A:1:[0.5]:[2.0] alt.binaries.pictures.erotica:A:1:[0.8]:[2.0] # permanent, semi-permanent # best.intro:A:never:never:never best.announce:A:5:60:120 best.general:A:never:never:never best.bugs:A:never:never:never # End of sample expire.ctl.ctl file. */ inn-2.6.0/contrib/backlogstat.in0000644000175200017520000000551112575023702016210 0ustar iuliusiulius#! /usr/bin/perl -w # fixscript will replace this line with code to load INN::Config # backlogstat - display backlog to sites # based on bklog by bill davidsen # breaks if backlog-directory in innfeed.conf is not "innfeed" my $dir = "$INN::Config::pathspool/innfeed"; my $Revision = '1.8'; use strict; use warnings; use Getopt::Std; use vars qw($opt_H $opt_h $opt_n $opt_t $opt_k $opt_S $opt_d); $| = 1; # option processing &getopts('HhntkS:d:') || &Usage; &Usage if $opt_h; # open the directory; $dir = $opt_d if $opt_d; print "$opt_d\n"; chdir($dir) or die "Can't cd to $dir"; opendir(DIR, ".") or die "Can't open dir"; my %nodes; while (my $name = readdir(DIR)) { # must be a file, correct name, non-zero size my $size; next unless -f $name; next unless ($size = -s $name); next unless $name =~ m/.*\.(in|out)put/; my $io = $1; (my $nodename = $name) =~ s/\..*//; # check for only some sites wanted next if ($opt_S && $nodename !~ /^${opt_S}.*/); # here we do the counts if asked if ($opt_n) { # open the file and count lines if (open(IN, "<$name")) { if ($name =~ m/.*\.input/) { my $offset = + 0; seek(IN, $offset, 0); } $size = 0; for ($size = 0; ; ++$size) {}; close IN; } } else { # get the offset on .input files if ($name =~ m/.*\.input/ && open(IN, "<$name")) { my $offset = + 0; $size -= $offset; close IN; } } $nodes{$nodename} = () unless defined $nodes{$nodename}; $nodes{$nodename}->{$io} = ( $opt_k ? $size / 1024 : $size ); } closedir DIR; # output the data for each node if (my $numnodes = keys %nodes) { if ($opt_H) { if ($opt_n) { print " <---------- posts ----------->\n"; } else { print " <---------- bytes ----------->\n"; } } my $ofmt; if ($opt_k) { print " input(k) output(k) total(k) Feed Name\n" if $opt_H; $ofmt = ( $opt_n ? "%10.2f" : "%10.1f" ); } else { print " input output total Feed Name\n" if $opt_H; $ofmt = "%10d"; } for my $node (sort keys %nodes) { my $hash = $nodes{$node}; my $size_in = $hash->{in} || 0; my $size_out = $hash->{out} || 0; my $size_tot = $size_in + $size_out; printf "${ofmt} ${ofmt} ${ofmt} %s\n", $size_in, $size_out, $size_tot, $node; } } else { print "NO backlog!\n"; } exit 0; sub Usage { print "\n" . "bklog - print innfeed backlog info - v$Revision\n" . "\n" . "Format:\n" . " bklog [ options ]\n" . "\n" . "Options:\n" . " -H output a header at the top of the output\n" . " -k scale all numbers in k (1024) units\n" . " -n count number of arts, not bytes of backlog filesize\n" . " Note: this may be SLOW for large files!\n" . " -Sxx Display only site names starting with xx\n" . " -d dir Use \"dir\" instead of \$pathspool/innfeed\n" . "\n" . " -h HELP - this is all, you got it!\n" . "\n"; exit 1; } inn-2.6.0/contrib/sample.init.systemd0000644000175200017520000000117612575023702017222 0ustar iuliusiulius# $Id: sample.init.systemd 9769 2014-12-14 20:53:16Z iulius $ # # This is a simple, bare-bones example of a systemd-style init script for INN. # [Unit] Description=InterNetNews Daemon Documentation=http://www.eyrie.org/~eagle/software/inn/ After=syslog.target network.target ConditionPathExists=/etc/news/inn.conf [Service] User=news PermissionsStartOnly=true PIDFile=/run/news/innd.pid Type=forking ExecStart=/usr/libexec/news/rc.news ExecReload=/usr/libexec/news/ctlinnd -t 20 reload '' 'reload asked' ExecStop=/bin/su -m news -s /bin/sh -c '/usr/libexec/news/rc.news stop' KillMode=control-group [Install] WantedBy=multi-user.target inn-2.6.0/contrib/findreadgroups.in0000644000175200017520000000156512575023702016733 0ustar iuliusiulius#! /usr/bin/perl -w # fixscript will replace this line with code to load INN::Config # Keep track of which groups are currently being read. Takes logfile input # on stdin. $readfile="$INN::Config::newsetc/readgroups"; $curtime = time; $oldtime = $curtime - 30 * 86400; # 30 days in the past if (open(RDF, $readfile)) { while () { chop; @foo=split(/ /); # foo[0] should be group, foo[1] lastreadtime if ($foo[1] < $oldtime) { next; # skip entries that are too old. } $groups{$foo[0]} = $foo[1]; } close(RDF); } # read input logs. while (<>) { next unless /nnrpd/; next unless / group /; chop; @foo = split(/ +/); # group name is in the 8th field. $groups{$foo[7]} = $curtime; } open(WRF, ">$readfile") || die "cannot open $readfile for write.\n"; foreach $i (keys %groups) { print WRF $i, " ", $groups{$i}, "\n"; } exit(0); inn-2.6.0/contrib/innreport-filter.xslt0000644000175200017520000000671512575023702017610 0ustar iuliusiulius inn-2.6.0/contrib/thdexpire.in0000644000175200017520000004130712575023702015711 0ustar iuliusiulius#! /usr/bin/perl -w # fixscript will replace this line with code to load INN::Config $ID='$Id: thdexpire.in 9408 2012-05-28 18:42:29Z iulius $$'; use POSIX ":fcntl_h"; use SDBM_File; use Getopt::Std; use INN::Utils::Shlock; # With the -M switch this program installs its own man page. #----------------------------------------------------------------------------- =head1 NAME thdexpire - dynamic expire daemon for timehash and timecaf storage =head1 SYNOPSIS B [ B<-t> I ] [ B<-f> I ] [ B<-i> I ] [ B<-m> I ] [ B<-x> I ] [ B<-N> ] [ B<-v> I ] B =head1 DESCRIPTION This is a daemon, to be started along with B, which periodically looks if news spool space is getting tight, and frees space by removing articles until enough is free. It is an adjunct (not a replacement) to INNs B program. =head2 Setting Up =over 4 =item 1. Configure your storage classes carefully. Let the default go in class 100 and choose the storage classes as relative (percent) retention times. E.g. if you want to give C a fifth of the default time, put them in class 20. Storage classes above 200 are ignored by this program. 0 expires immediately. An example is given in L<"EXAMPLES">. =item 2. Set up your F in a way that it puts only a maximum cap on retention times. Run B from B as usual. However, it should only expire articles which have an Expires line or are in classes above 200. See L<"EXAMPLES">. =item 3. Ensure to start this daemon along with B. =item 4. To get information and statistics, run B (in parallel to a running daemon). This will show you the current actual retention times. =back =head2 How It Works B works directly on the spool. It assumes the layout described in the timehash and timecaf sections of L as of INN-2.x-CURRENT (Dec. 5, 1998). For every storage class associated with timehash/timecaf, B keeps a I which is the modification time of the oldest article/CAF file in this class. This time is chosen so that the difference of the work time of class N to now (i.e. the I for class N) will be N/100 of the retention time of class 100. The work time of all classes is continuously adjusted as time goes by. Articles and CAF files which are older than the work time are deleted. =head1 OPTIONS =over 8 =item B<-t> I Check for free space every I minutes (default 30). =item B<-f> I Leave I kilobytes of free disk space on each spool filesystem (default 50000). =item B<-i> I Leave I inodes free on each spool filesystem (default 5000). =item B<-m> I Set the minimum normal holding time for class 100 to I days (default 7). =item B<-x> I Set the absolute minimum holding time for any article to I seconds (default 86400, i.e. 1 day). =item B<-N> Do not delete any articles, just print what would be done. =item B<-v> I Set the verbosity level. Values from 1 to 3 are meaningful, where higher levels are mostly for debugging. =item B<-r> Do not run as a daemon, instead print a report from the database (see L) on the available storage classes, current expire times and other stuff. =back =head1 EXAMPLES Here is an example F file: # Large postings in binary groups are expired fast: # 20% retention time method timehash { newsgroups: *.binaries.*,*.binaer.*,*.dateien.*,alt.mag.* size: 30000 class: 20 } # Local groups and *.answers groups don't expire at all with # thdexpire. These are handled by Expires lines and a cutoff # in expire.ctl. method timehash { newsgroups: *.answers,news.announce.*,local.* class: 201 } # Expires lines are honored if they dont exceed 90 days. # Exempt those postings from thdexpire handling. method timehash { newsgroups: * expires: 1d,90d class: 202 } # Default: should be class 100 because thdexpire bases its # calculations thereupon. method timecaf { newsgroups: * class: 100 } And here is an F which fits: # Our local groups are held 6 months local.*:A:7:180:180 # Everything else is handled by thdexpire, or Expires lines *:A:7:never:never Note that B does not actually use these files, they just configure other parts of the news system in an appropriate way. =head1 FILES =over 4 =item I/thdexpstat.{dir,pag} Holds state information like classes, expire times, oldest articles. When this file is missing, it will be rebuilt the next time the daemon is started, which basically means scanning the spool directories to find the oldest articles. With the B<-r> option, the contents of this file are printed. =item I/thdexpire.pid Contains the PID of the running daemon. =back =head1 SIGNALS I or I can be sent to the daemon at any time, causing it to gracefully exit immediately. =head1 SEE ALSO L, L, L =head1 NOTES This version needs the B program supplied with newer releases of INN. The filenames for timecaf were wrong in older versions of the INN documentation. This program uses the true filenames, as found by reading the INN source. =head1 DIAGNOSTICS Any error messages are printed on standard error. Normal progress messages, as specified by the B<-v> option, are printed on standard output. =head1 BUGS Storage classes which are in I but not on disk (i.e. which have never been filed into) when the daemon starts are ignored. The code is ugly and uses too many global variables. Should probably rewrite it in C. =head1 RESTRICTIONS Directories which are left empty are not removed. The overview database is not affected by B, it has to be cleaned up by the daily regular B run. This may need a patch to B. =head1 AUTHOR Olaf Titz . Use and distribution of this work is permitted under the same terms as the B package. =head1 HISTORY Inspired by the old B program for the traditional spool. June 1998: wrote the first version for timehash. November 1998: added code for timecaf, works on multiple spool filesystems, PODed documentation. July 1999: bugfixes. =cut #----------------------------------------------------------------------------- my $lockfile = "$INN::Config::innddir/thdexpire.pid"; END { # In case we bail out, while holding a lock. INN::Utils::Shlock::releaselocks(); } chdir $INN::Config::spool || die "chdir $INN::Config::spool: $!"; $opt_r=0; # make a report $opt_t=30; # check interval in minutes $opt_f=50000; # required space in kilobytes $opt_i=5000; # required space in inodes $opt_m=7; # minimum normal (class 100) time in days $opt_x=86400; # absolute minimum hold time in seconds $opt_N=0; # dont actually delete articles $opt_v=0; # verbosity level $opt_M=0; # install man page getopts("rt:f:i:m:x:Nv:M"); $sfile="$INN::Config::pathdb/thdexpstat"; $ID=~/ ([^,]+,v [^ ]+)/; $ID=$1; if ($opt_M) { print "Installing thdexpire(8) man page\n"; $0=~m:^(.*)/([^/]+)$:; chdir $1 || die "chdir $1"; exec "pod2man --section=8 --center='Contributed News Software'" . " --release='$ID' $2 >$INN::Config::pathnews/man/man8/thdexpire.8"; } if ($opt_r) { tie(%S, SDBM_File, $sfile, O_RDONLY, 0664) || die "open $sfile: $!"; &report; untie %S; exit 0; } # Acquire a lock. INN::Utils::Shlock::lock($lockfile, 5) or die ("cannot create lockfile $lockfile"); tie(%S, SDBM_File, $sfile, O_RDWR|O_CREAT, 0664) || die "open $sfile: $!"; $SIG{'TERM'}=$SIG{'INT'}='finish'; $|=1; printf "%s starting at %s\n", $ID, &wtime(time) if ($opt_v>0); undef @c; $NOW=time; $ac=$cc=0; opendir(CD, ".") || &err("opendir $INN::Config::spool: $!"); while ($cd=readdir(CD), defined($cd)) { $cd=~/^time(caf)?-([0-9a-f][0-9a-f])$/i || next; $c{hex($2)}=1 unless hex($2)>200; } closedir CD; @classes=sort {$a<=>$b} keys %c; foreach $c (@classes) { &initclass($c); $S{"work$;$c"}=$S{"oldest$;$c"}&0xFFFFFF00; } $S{"classes"}=join(",", @classes); $S{"inittime"}=time; $S{"ID"}=$ID; printf "Checked %d articles, %d CAFs in %d seconds\n", $ac, $cc, time-$NOW if ($ac+$cc>0 && $opt_v>0); chdir $INN::Config::spool || die "chdir $INN::Config::spool: $!"; while (1) { $S{"lastrun"}=$NOW=time; printf "%s\n", &wtime($NOW) if ($opt_v>0); $nt=0; foreach $c (@classes) { $t=($NOW-$S{"work$;$c"})*100/$c; $nt=$t if ($nt<$t); } printf "Normal time (class 100): %s\n", &xtime($NOW-$nt) if ($opt_v>0); if ($nt<$opt_m*24*60*60) { printf " capped at minimum %d days\n", $opt_m if ($opt_v>0); $nt=$opt_m*24*60*60; } if ($nt>180*24*60*60) { print " capped at maximum 180 days\n" if ($opt_v>0); $nt=180*24*60*60; } $S{"normaltime"}=$nt; $decrement=$opt_t*60; $pass=$need=0; $x="/"; undef %needk; undef %needi; foreach $c (@classes) { $Dart{$c}=$Dcaf{$c}=$Dkb{$c}=$Dino{$c}=0; $y=sprintf("time-%02x", $c); if (-d $y) { @S=stat(_); if ($#S>=0) { $dev{$y}=$S[0]; unless (defined($needk{$S[0]})) { $x.=" $y"; $needk{$S[0]}=$needi{$S[0]}=-1; } } } $y=sprintf("timecaf-%02x", $c); if (-d $y) { @S=stat(_); if ($#S>=0) { $dev{$y}=$S[0]; unless (defined($needk{$S[0]})) { $x.=" $y"; $needk{$S[0]}=$needi{$S[0]}=-1; } } } } if (open(D, "$INN::Config::newsbin/inndf $x |")) { while () { @S=split(/\s+/, $_); $needk{$dev{$S[0]}}=$opt_f-$S[1] unless ($S[0] eq "/"); } close D; } if (open(D, "$INN::Config::newsbin/inndf -i $x |")) { while () { @S=split(/\s+/, $_); $needi{$dev{$S[0]}}=$opt_i-$S[1] unless ($S[0] eq "/"); } close D; } foreach $c (keys %needk) { printf "Device %d needs to free %d kilobytes, %d inodes\n", $c, $needk{$c}<0?0:$needk{$c}, $needi{$c}<0?0:$needi{$c} if ($opt_v>0 && ($needk{$c}>0 || $needi{$c}>0)); if ($needk{$c}>0 || $needi{$c}>0) { ++$need; } } if ($opt_v>0 && $need<=0) { print " (nothing to do)\n"; $tt=0; } else { $error=0; while (!$error && $need>0) { if ($S{"normaltime"}-$decrement<$opt_m*24*60*60) { print " Normal time hit minimum\n" if ($opt_v>0); last; } $S{"normaltime"}-=$decrement; printf " normal time (100) becomes %ld\n", $S{"normaltime"} if ($opt_v>2); ++$pass; $Dart=$Dcaf=$Dkb=$Dino=$need=0; foreach $c (keys %needk) { if ($needk{$c}>0 || $needi{$c}>0) { ++$need; } } if ($need) { foreach $c (@classes) { &worktime($c, $NOW-($S{"normaltime"}*$c/100)); $Dart+=$dart; $Dcaf+=$dcaf; $Dkb+=$dbb>>10; $Dino+=$dino; $Dart{$c}+=$dart; $Dcaf{$c}+=$dcaf; $Dkb{$c}+=$dbb>>10; $Dino{$c}+=$dino; last if ($error); } } if ($Dart+$Dcaf) { printf " pass %d deleted %d arts, %d CAFs, %d kb\n", $pass, $Dart, $Dcaf, $Dkb if ($opt_v>1); $decrement-=$decrement>>2 if ($decrement>10*60); } else { $decrement+=$decrement>>1 if ($decrement<4*60*60); } } $Dkb=$Dart=$Dcaf=$Dino=0; foreach $c (@classes) { printf " class %3d: deleted %6d arts %6d CAFs %10d kb\n", $c, $Dart{$c}, $Dcaf{$c}, $Dkb{$c} if ($opt_v>1); $Dkb+=$Dkb{$c}; $Dart+=$Dart{$c}; $Dcaf+=$Dcaf{$c}; } $tt=time-$NOW; printf " deleted %d articles, %d CAFs, %d kb in %d seconds\n", $Dart, $Dcaf, $Dkb, time-$NOW if ($opt_v>0); if ($tt>$opt_t*60) { printf STDERR "Round needed %d seconds, interval is %d\n", $tt, $opt_t*60; $tt=$opt_t*60; } } sleep $opt_t*60-$tt; } &finish(0); sub initclass { my $C=shift; if (!$S{"blocksize$;$C$;CAF"}) { # Determine filesystem blocksize # unfortunately no way in perl to statfs my $x=sprintf("%s/timecaf-%02x/test%d", $INN::Config::spool, $C, $$); if (open(A, ">$x")) { print A "X" x 4096; close A; @S=stat $x; $#S>=12 || die "stat: $!"; if ($S[12]) { $S{"blocksize$;$C$;CAF"}=$S[7]/$S[12]; } else { $S{"blocksize$;$C$;CAF"}=512; warn "hack around broken stat blocksize"; } unlink $x; } } return if ($S{"oldest$;$C"}); my $oldest=time; $S{"oldest$;$C"}=$oldest; my $base=sprintf("%s/time-%02x", $INN::Config::spool, $C); my $count=0; if (chdir $base) { printf "Finding oldest in class %d (%s)\n", $C, $base if ($opt_v>0); opendir(D0, "."); while ($d1=readdir(D0), defined($d1)) { $d1=~/^[0-9a-f][0-9a-f]$/ || next; chdir $d1; opendir(D1, ".") || next; while ($d2=readdir(D1), defined($d2)) { $d2=~/^[0-9a-f][0-9a-f]$/ || next; chdir $d2; opendir(D2, ".") || next; while ($a=readdir(D2), defined($a)) { $a=~/^\./ && next; @S=stat($a); $oldest=$S[9] if ($S[9]<$oldest); ++$count; } closedir D2; chdir ".."; } closedir D1; chdir ".."; } closedir D0; $ac+=$count; } $base=sprintf("%s/timecaf-%02x", $INN::Config::spool, $C); if (chdir $base) { printf "Finding oldest in class %d (%s)\n", $C, $base if ($opt_v>0); opendir(D0, "."); while ($d1=readdir(D0), defined($d1)) { $d1=~/^[0-9a-f][0-9a-f]$/ || next; chdir $d1; opendir(D1, ".") || next; while ($a=readdir(D1), defined($a)) { $a=~/^\./ && next; @S=stat($a); $oldest=$S[9] if ($S[9]<$oldest); ++$count; } closedir D1; chdir ".."; } closedir D0; $cc+=$count; } $S{"count$;$C"}=$count; $S{"oldest$;$C"}=$oldest; } sub worktime { my $C=shift; my $goal=shift; $goal&=0xFFFFFF00; printf " goal for class %d becomes %s\n", $C, &xtime($goal) if ($opt_v>2); if ($goal>$NOW-$opt_x) { printf " goal for class %d cut off\n", $C if ($opt_v>1); $error=1; return; } $dart=$dcaf=$dbb=$dino=0; $hdir=sprintf("time-%02x", $C); $cdir=sprintf("timecaf-%02x", $C); while (($_=$S{"work$;$C"})<$goal) { printf " running: %08x\n", $_ if ($opt_v>2); ($aa,$bb,$cc) = (($_>>24)&0xFF, ($_>>16)&0xFF, ($_>>8)&0xFF); $dir=sprintf("%s/%02x/%02x", $hdir, $bb, $cc); $pat=sprintf("[0-9a-f]{4}-%02x[0-9a-f]{2}", $aa); if (opendir(D, $dir)) { while ($_=readdir(D), defined($_)) { /^$pat$/ || next; $art="$dir/$_"; @S=stat($art); if ($#S>=7) { if ($opt_N) { print " would delete $art" if ($opt_v>2); } else { print " deleting $art" if ($opt_v>2); unlink $art; } ++$dart; ++$dino; printf " %d kb\n", $S[7]>>10 if ($opt_v>2); $dbb+=$S[7]; $needk{$dev{$hdir}}-=$S[7]>>10; $needi{$dev{$hdir}}--; } } } else { printf " (no dir %s)\n", $dir if ($opt_v>2); } $caf=sprintf("%s/%02x/%02x%02x.CF", $cdir, $bb, $aa, $cc); @S=stat($caf); if ($#S>=12) { if ($opt_N) { print " would delete $caf" if ($opt_v>2); } else { print " deleting $caf" if ($opt_v>2); unlink $caf; } $y=0; if (open(C, $caf)) { # try to find how much there is in the CAF sysread(C, $_, 16); @C=unpack("a4LLL", $_); if ($C[0] eq "CRMT") { $y=$C[3]-$C[1]; $dart+=$y; } close C; } ++$dcaf; ++$dino; if ($S[12]) { $x=$S[12]*$S{"blocksize$;$C$;CAF"}; } else { $x=$S[7]; warn "hack around broken stat blocksize"; } printf " %d arts %d kb\n", $y, $x>>10 if ($opt_v>2); $dbb+=$x; $needk{$dev{$cdir}}-=$x>>10; $needi{$dev{$cdir}}--; } $S{"work$;$C"}+=0x100; $S{"oldest$;$C"}=$S{"work$;$C"} unless ($opt_N); } } sub report { $NOW=time; my $cc=$S{"classes"}; my $nt=$S{"normaltime"}; unless ($cc && $nt) { print "Not initialized.\n"; return; } printf "Version: %s (this: %s)\n", $S{"ID"}, $ID; printf "Started at: %s\n", &xtime($S{"inittime"}) if ($S{"inittime"}); printf "Last run: %s\n", &xtime($S{"lastrun"}) if ($S{"lastrun"}); printf "Classes: %s\n", $cc; foreach $c (split(/,/, $cc)) { printf "Class %d:\n", $c; #printf " Initial count %d articles\n", $S{"count$;$c"}; printf " Oldest article: %s\n", &xtime($S{"oldest$;$c"}); printf " Expiring at: %s\n", &xtime($S{"work$;$c"}); printf " Normal time: %s\n", &xtime($NOW-$nt*$c/100); printf " Filesystem block size (CAF): %d\n", $S{"blocksize$;$c$;CAF"}; } } sub wtime { my $t=shift; my @T=localtime($t); sprintf("%04d-%02d-%02d %02d:%02d", $T[5]+1900, $T[4]+1, $T[3], $T[2], $T[1]); } sub xtime { my $t=shift; if ($NOW-$t<0 || $NOW-$t>350*24*60*60) { return &wtime($t); } my @T=localtime($t); my @D=gmtime($NOW-$t); sprintf("%04d-%02d-%02d %02d:%02d (%dd %dh %dm)", $T[5]+1900, $T[4]+1, $T[3], $T[2], $T[1], $D[7], $D[2], $D[1]); } sub err { printf STDERR "%s\n", shift; &finish(0); } sub finish { untie(%S); # Unlock. INN::Utils::Shlock::unlock($lockfile); exit 0; } #----------------------------------------------------------------------------- inn-2.6.0/contrib/fixhist0000755000175200017520000000413712575023702014771 0ustar iuliusiulius#!/usr/local/bin/perl # # history database sanity checker # David Barr # version 1.4 # w/mods from: hucka@eecs.umich.edu # Katsuhiro Kondou # version 1.1 # Throw away history entries with: # malformed lines (too long, contain nulls or special characters) # # INN Usage: # ctlinnd throttle 'fixing history' # ./fixhist history.n # makedbz -s `wc -l ) { chop; ($msgid,$dates,$arts,$xtra) = split('\t'); if ($xtra) { &tossit(); # too many fields next; } if (!($dates) && (($arts) || ($xtra))) { &tossit(); # if not date field, then the rest next; # should be empty } if (length($msgid) >= $MAXKEYLEN) { &tossit(); # message-id too long next; } if ($msgid !~ /^<[^<> ]*>$/) { if ($msgid =~ /^\[[0-9A-F]{32}\]$/) { if ($arts ne "") { if ($arts =~ /^\@[0-9A-F]{56}\@$/) { $arts =~ s/^\@([0-9A-F]{36})([0-9A-F]{20})\@$/\@${1}\@/; print "$msgid\t$dates\t$arts\n"; next; } if ($arts !~ /^\@[0-9A-F]{36}\@$/) { &tossit(); next; } } } else { &tossit(); # malformed msg-ids next; } } else { if ($arts ne "" && ($arts !~ /[^\/]*\/[0-9]*/)) { &tossit(); # malformed articles list next; } } if (/[\000-\010\012-\037\177-\237]/) { # non-control chars except tab &tossit(); # illegal chars next; } if ($dates) { if ($dates =~ /[^\d~\-]/) { # rudimentary check &tossit(); # full check would be too slow next; } } print "$_\n"; $count++; $0 = "history line $./$count" if $. % 50000 == 0; } print STDERR "Done. Now run:\nmakedbz -s $count -f history.n\n"; sub tossit { print STDERR "$_\n"; } inn-2.6.0/contrib/mlockfile.c0000644000175200017520000001022612575023702015472 0ustar iuliusiulius/* $Id: mlockfile.c 9716 2014-09-21 19:53:42Z iulius $ */ /* Locks the files given on the command line into memory using mlock. This code has only been tested on Solaris and may not work on other platforms. Contributed by Alex Kiernan . */ #include #include #include #include #include #include #include #include #include #include #include struct mlock { const char *path; struct stat st; void *base; off_t offset; size_t length; }; void inn_lock_files(struct mlock *); char *progname; int flush = 0; int interval = 60000; void inn_lock_files(struct mlock *ml) { for (; ml->path != NULL; ++ml) { int fd; fd = open(ml->path, O_RDONLY); if (fd == -1) { fprintf(stderr, "%s: can't open `%s' - %s\n", progname, ml->path, strerror(errno)); } else { struct stat st; /* check if size, inode or device of the path have * changed, if so unlock the previous file & lock the new * one */ if (fstat(fd, &st) != 0) { fprintf(stderr, "%s: can't stat `%s' - %s\n", progname, ml->path, strerror(errno)); } else if (ml->st.st_ino != st.st_ino || ml->st.st_dev != st.st_dev || ml->st.st_size != st.st_size) { if (ml->base != MAP_FAILED) munmap(ml->base, ml->length > 0 ? ml->length : (size_t) ml->st.st_size); /* free everything here, so in case of failure we try * again next time */ ml->st.st_ino = 0; ml->st.st_dev = 0; ml->st.st_size = 0; ml->base = mmap(NULL, ml->length > 0 ? ml->length : (size_t) st.st_size, PROT_READ, MAP_SHARED, fd, ml->offset); if (ml->base == MAP_FAILED) { fprintf(stderr, "%s: can't mmap `%s' - %s\n", progname, ml->path, strerror(errno)); } else { if (mlock(ml->base, ml->length > 0 ? ml->length : (size_t) st.st_size) != 0) { fprintf(stderr, "%s: can't mlock `%s' - %s\n", progname, ml->path, strerror(errno)); } else { ml->st = st; } } } else if (flush) { msync(ml->base, ml->length > 0 ? ml->length : (size_t) st.st_size, MS_SYNC); } } close (fd); } } static void usage(void) { fprintf(stderr, "usage: %s [-f] [-i interval] file[@offset[:length]] ...\n", progname); fprintf(stderr, " -f\tflush locked bitmaps at interval\n"); fprintf(stderr, " -i interval\n\tset interval between checks/flushes\n"); } int main(int argc, char *argv[]) { struct mlock *ml; int i; progname = *argv; while ((i = getopt(argc, argv, "fi:")) != EOF) { switch (i) { case 'i': interval = 1000 * atoi(optarg); break; case 'f': flush = 1; break; default: usage(); return EX_USAGE; } } argc -= optind; argv += optind; /* construct list of pathnames which we're to operate on, zero out * the "cookies" so we lock it in core first time through */ ml = malloc((1 + argc) * sizeof ml); for (i = 0; argc--; ++i, ++argv) { char *at; off_t offset = 0; size_t length = 0; ml[i].path = *argv; ml[i].st.st_ino = 0; ml[i].st.st_dev = 0; ml[i].st.st_size = 0; ml[i].base = MAP_FAILED; /* if we have a filename of the form ...@offset:length, only * map in that portion of the file */ at = strchr(*argv, '@'); if (at != NULL) { char *end; *at++ = '\0'; errno = 0; offset = strtoull(at, &end, 0); if (errno != 0) { fprintf(stderr, "%s: can't parse offset `%s' - %s\n", progname, at, strerror(errno)); return EX_USAGE; } if (*end == ':') { at = end + 1; errno = 0; length = strtoul(at, &end, 0); if (errno != 0) { fprintf(stderr, "%s: can't parse length `%s' - %s\n", progname, at, strerror(errno)); return EX_USAGE; } } if (*end != '\0') { fprintf(stderr, "%s: unrecognised separator `%c'\n", progname, *end); return EX_USAGE; } } ml[i].offset = offset; ml[i].length = length; } ml[i].path = NULL; /* loop over the list of paths, sleeping 60s between iterations */ for (;;) { inn_lock_files(ml); poll(NULL, 0, interval); } return EX_OSERR; } inn-2.6.0/contrib/pullart.c0000644000175200017520000001423312575023702015212 0ustar iuliusiulius/* June 14, 1999 Recover text articles from cyclic buffers Articles start with "\0Path:" and end with "\r\n.\r\n" Tested with INND 2.2 under AIX 4.2 rifkin@uconn.edu */ /* (1) Pull 16 bytes at a time (2) Last 7 bytes must be \000\000\000Path (3) When found, print "\nPath"; (4) print subsequent bytes until \r\n.\r\n found */ #include "config.h" #include "clibrary.h" #define INFILE 1 #define FILEPREFIX 2 #define HEADER 3 #define STRING 4 /* String buffer size */ #define NBUFF 512 #define MAX_ART_SIZE 2200000 #define WRITEMSG printf ("File %s line %i\n", __FILE__, __LINE__); \ fflush(stdout); #define WRITEVAR(VAR_NAME,VAR_TYPE) \ { \ printf ("FILE %s LINE %i :", __FILE__, __LINE__); \ printf ("%s = ", #VAR_NAME); \ printf (#VAR_TYPE, (VAR_NAME) ); \ printf ("\n"); \ } #define WRITETXT(TEXT) \ printf ("FILE %s LINE %i \"%s\"\n", __FILE__, __LINE__, TEXT); \ fflush(stdout); #if 0 #define WRITEMSG #define WRITEVAR(X,Y) #endif int WriteArticle (char *, int, char *, char *, char *, int); char ArtHead[7] = {0, 0, 0, 'P', 'a', 't', 'h'}; char ArtTail[5] = {'\r', '\n', '.', '\r', '\n'}; int LenTail = 5; int main (int argc, char *argv[]) { FILE *Infile; int NumTailCharFound; bool ReadingArticle = false; char buffer[32]; char *obuffer = NULL; char *header = NULL; char *string = NULL; int osize = MAX_ART_SIZE; int opos = 0; int i; int nchar; int fileno = 0; int artno = 0; /* Check number of args */ if (argc<3) { printf ("Usage: pullart [
]\n"); printf (" Read cycbuffer and print all articles whose\n"); printf (" article header
contains .\n"); printf (" Articles are written to files name .nnnnnn\n"); printf (" where nnnnnn is numbered sequentially from 0.\n"); printf (" If
and not specified, all articles\n"); printf (" are written.\n"); printf (" Examples:\n"); printf (" pullart /news3/cycbuff.3 alt.rec Newsgroup: alt.rec\n"); printf (" pullart /news3/cycbuff.3 all\n"); printf (" pullart firstbuff article Subject bluejay\n"); return 0; } /* Allocate output buffer */ obuffer = (char *) calloc (osize+1, sizeof(char)); if (obuffer==NULL) { printf ("Cannot allocate obuffer[]\n"); return 1; } /* Open input file */ Infile = fopen (argv[INFILE], "rb"); if (Infile==NULL) { printf ("Cannot open input file.\n"); return 1; } if (argc>=4) header = argv[HEADER]; if (argc>=5) string = argv[STRING]; if (*header=='\0') header=NULL; if (*string=='\0') string=NULL; /*test*/ printf ("filename <%s>\n", argv[INFILE]); printf ("fileprefix <%s>\n", argv[FILEPREFIX]); printf ("header <%s>\n", header); printf ("string <%s>\n", string); /* Skip first 0x38000 16byte buffers */ i = fseek (Infile, 0x38000L, SEEK_SET); /* Read following 16 byte buffers */ ReadingArticle = false; NumTailCharFound = 0; nchar=0; artno=0; while ( 0!=fread(buffer, 16, 1, Infile) ) { nchar+=16; /* Found start of article, start writing to obuffer */ if (0==memcmp(buffer+9, ArtHead, 7)) { ReadingArticle = true; memcpy (obuffer, "Path", 4); opos = 4; continue; } /* Currnetly reading article */ if (ReadingArticle) { for (i=0; i<16; i++) { /* Article too big, drop it and move on */ if (opos>=osize) { printf ("article number %i bigger than buffer size %i.\n", artno+1, osize); artno++; ReadingArticle=false; break; } /* Add current character to output buffer, but remove \r */ if ('\r' != buffer[i]) obuffer[opos++] = buffer[i]; /* Check for article ending sequence */ if (buffer[i]==ArtTail[NumTailCharFound]) { NumTailCharFound++; } else NumTailCharFound=0; /* End found, write article, reset for next */ if (NumTailCharFound==LenTail) { ReadingArticle = false; NumTailCharFound = 0; /* Add trailing \0 to buffer */ obuffer[opos+1] = '\0'; fileno += WriteArticle (obuffer, opos, argv[FILEPREFIX], header, string, fileno); artno++; break; } } } } fclose (Infile); return 0; } /* Writes article stored in buff[] if it has a "Newsgroups:" header line which contains *newsgroup Write to a file named fileprefix.fileno */ int WriteArticle (char *buff, int n, char *fileprefix, char *headerin, char *string, int fileno) { char *begptr; char *endptr; char *newsptr; char savechar; char header[NBUFF]; char filename[NBUFF]; FILE *outfile; /* Prevent buffer overflow due to fileprefix too long */ if (strlen(fileprefix)>384) { printf ("program error: cannot have file prefix greater then 384 characters\n"); exit(1); } /* Is header here? Search if header string requested, leave if not found */ if (headerin!=NULL) { /* Find \nHEADER */ strlcpy(header, "\n", sizeof(header)); strlcat(header, headerin, sizeof(header)); begptr = strstr (buff, header); /* return if Header name not found */ if (begptr==NULL) { return 0; } /* Header found. What about string? Search if string requested, leave if not found */ if (string!=NULL) { /* Find end of header line */ begptr++; endptr = strchr (begptr, '\n'); /* Something is wrong, end of header not found, do not write * article */ if (endptr==NULL) return 0; /* Temporarily make string end a null char */ savechar = *endptr; *endptr = '\0'; newsptr = strstr (begptr, string); /* Requested newsgroup not found */ if (newsptr==NULL) return 0; /* Restore character at end of header string */ *endptr = savechar; } /* No string specified */ } /* No header specified */ /* Open file, write buffer, close file */ snprintf (filename, sizeof(filename), "%s.%06i", fileprefix, fileno); outfile = fopen (filename, "wt"); if (outfile==NULL) { printf ("Cannot open file name %s\n", filename); exit(1); } while (n--) fprintf (outfile, "%c", *buff++); fclose (outfile); /* Return number of files written */ return 1; } inn-2.6.0/contrib/innconfcheck0000755000175200017520000001304412575023702015740 0ustar iuliusiulius#!/bin/ksh ### INNCONFcheck v1.1 ### Revision history: # v1.0 B. Galliart (designed to work with 2.3 inn.conf man page) # v1.1 B. Galliart (optional support for using inn.conf POD src instead) ### Description: # This script is written to inner-mix the inn.conf settings with the # documentation from the inn.conf man page. The concept was shamelessly # ripped off of a CGI application provided at Mib Software's Usenet Rapid # Knowledge Transfer (http://www.mibsoftware.com/userkt/inn2.0/). # The idea is that a news administrator usually must go through the # task of reading the inn.conf man page in parallel with the inn.conf # inn.conf to confirm that the settings are set as desired. Manually # matching up the two files can become troublesome. This script should # make the task easier and hopefully reduce the chance a misconfiguration # is missed. ### Known bugs: # - Is very dependent on the format of the man page. It is know NOT to # work with the inn.conf man pages written before INN 2.3 and may # require minor rewriting to address future revisions of inn.conf # Note: this known bug is addressed via the "EDITPOD" option below # but is not enabled by default (details explained below). # # - SECURITY! While taken from the concept of a CGI script, it is not # intended to be a CGI script itself. It is *assumed* that the # inn.conf file is provided by a "trusted" source. ### License: this script is provided under the same terms as the majority # of INN 2.3.0 as stated in the file "inn-2.3.0/LICENSE" ### Warrenty/Disclaimer: There is no warrenty provided. For details, please # refer to the file "inn-2.3.0/LICENSE" from the INN 2.3 package ################ ### The User Modifiable Parameters/Settings: # INNCONF should be set to the actual location of the inn.conf file INNCONF=/usr/local/news/etc/inn.conf # INNCONFMAN should be set to the location of the inn.conf man page INNCONFMAN=/usr/local/news/man/man5/inn.conf.5 # INNCONFPOD should be set to the location of the inn.conf POD source # INNCONFPOD=/usr/local/src/inn-2.3.0/doc/pod/inn.conf.pod INNCONFPOD=/usr/local/news/man/man5/inn.conf.pod # NROFF should be set to an approbate program for formating the man page # this could be the vendor provided nroff, the FSF's groff (which could be # used for producing PostScript output) or Earl Hood's man2html from # http://www.oac.uci.edu/indiv/ehood/man2html.html # NROFF=man2html NROFF="nroff -man" # Pager should be set to an approbate binary for making the output # readable in the user's desired method. Possible settings include # page, more, less, ghostview, lynx, mozilla, lpr, etc. If no pager # application is desire then by setting it to "cat" will cause the output # to continue on to stdout. PAGER=less # By default the script uses the inn.conf man page before being processed # by nroff to edit in the actual inn.conf settings. The problem with this # approach is that if the format of the inn.conf man page ever changes # assumptions about the format that this script makes will probably break. # Presently, the base/orginal format of the inn.conf man page is in perl # POD documentation. The formating of this file is less likely to change # in the future and is a cleaner format for automated editing. However, # their is some disadvantages to using this file. First disadvantage, # the POD file is not installed by INN 2.3.0 by default (see INNCONFPOD # enviromental variable for setting the script to find the file in the # correct location). Second disadvantage, pod2man does not appear to # support using stdin so the edited POD must be temporarily stored as a # file. Finally, the last disadvantage, the script is slower due to the # added processing time of pod2man. Weighing the advantages and # disadvantages to both approaches are left to the user. If you wish to # have innconfcheck edit the POD file then change the variable below to # a setting of "1", otherwise leave it with the setting of "0" EDITPOD=0 ################ ### The Script: (non-developers should not need to go beyond this point) # All variable settings in inn.conf should not contain a comment # character of "#" and should have a ":" in the line. These variable names # should then be matched up with the man page "items" in the inn.conf file. # In the INN 2.3 man page, these items appear in the following format: # .Ip "\fIvariable name\fR" 4 # Hence, if there exists an entry in the inn.conf of "verifycancels: false" # then the awk script will produce: # s#^.Ip "\fIvarifycancels\f$" 4#.Ip "\verifycancels: false\f$" 4# # once piped to sed, this expression will replace the man page item to # include the setting from the inn.conf file. The nroff and pager # applications then polish the script off to provide a documented formated # in a way that is easier to find incorrect setting withen. if [ $EDITPOD -eq 0 ] ; then grep -v "#" $INNCONF | grep ":" | \ awk 'BEGIN { FS = ":" } { print "s#^.Ip \042\\\\fI"$1"\\\\fR\042 4#.Ip \042\\\\fI"$0"\\\\fR\042 4#" }' | \ sed -f - $INNCONFMAN | $NROFF | $PAGER else # The next part is similar to above but provides working from the POD source # instead of from the resulting nroff/man page. This section is discussed # in more detail above with the "EDITPOD" setting. grep -v "#" $INNCONF | grep ":" | \ awk 'BEGIN { FS = ":" } { print "s#=item I<"$1">#=item I<"$0">#" }' | \ sed -f - $INNCONFPOD > /tmp/innconfcheck-$$ pod2man /tmp/innconfcheck-$$ | $NROFF | $PAGER rm -f /tmp/innconfcheck-$$ fi # That's all. # EOF inn-2.6.0/contrib/Makefile0000644000175200017520000000405112575023702015020 0ustar iuliusiulius## $Id: Makefile 9770 2014-12-14 20:55:09Z iulius $ ## ## There are no installation rules or other top-level rules for this ## directory as it's not properly part of INN. Installation should be ## done by the user by hand for those files that they're interested in. include ../Makefile.global top = .. CFLAGS = $(GCFLAGS) ALL = archivegz auth_pass backlogstat cleannewsgroups \ delayer expirectl \ findreadgroups makeexpctl makestorconf mlockfile newsresp \ nnrp.access2readers.conf pullart reset-cnfs respool \ stathist thdexpire \ tunefeed all: $(ALL) warnings: $(MAKE) COPT='$(WARNINGS)' all clean clobber distclean maintclean: rm -f *.o $(ALL) rm -rf .libs $(FIXSCRIPT): @echo Run configure before running make. See INSTALL for details. @exit 1 ## Compilation rules. LINK = $(LIBLD) $(LDFLAGS) -o $@ FIX = $(FIXSCRIPT) STORELIBS = $(LIBSTORAGE) $(LIBHIST) $(LIBINN) $(STORAGE_LIBS) $(LIBS) auth_pass: auth_pass.o ; $(LINK) auth_pass.o $(LIBINN) $(CRYPT_LIBS) expirectl: expirectl.o ; $(LINK) expirectl.o mlockfile: mlockfile.o ; $(LINK) mlockfile.o newsresp: newsresp.o ; $(LINK) newsresp.o $(LIBS) pullart: pullart.o ; $(LINK) pullart.o $(LIBINN) reset-cnfs: reset-cnfs.o ; $(LINK) reset-cnfs.o respool: respool.o ; $(LINK) respool.o $(STORELIBS) archivegz: archivegz.in $(FIX) ; $(FIX) -i archivegz.in backlogstat: backlogstat.in $(FIX) ; $(FIX) backlogstat.in cleannewsgroups: cleannewsgroups.in $(FIX) ; $(FIX) cleannewsgroups.in delayer: delayer.in $(FIX) ; $(FIX) -i delayer.in findreadgroups: findreadgroups.in $(FIX) ; $(FIX) findreadgroups.in makeexpctl: makeexpctl.in $(FIX) ; $(FIX) makeexpctl.in makestorconf: makestorconf.in $(FIX) ; $(FIX) makestorconf.in nnrp.access2readers.conf: nnrp.access2readers.conf.in $(FIX) ; $(FIX) -i nnrp.access2readers.conf.in stathist: stathist.in $(FIX) ; $(FIX) -i stathist.in thdexpire: thdexpire.in $(FIX) ; $(FIX) thdexpire.in tunefeed: tunefeed.in $(FIX) ; $(FIX) -i tunefeed.in inn-2.6.0/contrib/authmysql0000755000175200017520000000545412575023702015345 0ustar iuliusiulius#!/usr/bin/perl -w # # Authenticate users for INN against a MySQL database. # Written by Daniel Marsh # Covered under the same license as INN in general. # You really shouldn't need to edit this file. # To test via tcsh: # ( echo 'ClientAuthname: test' ; echo 'ClientPassword: monkey' ) | ./authmysql.pl use DBI; use strict; # get STDIN and retun it as a HASH my %stdin = get_stdin(); # edit this path to wherever you want your config to reside. my %conf = readconf('/usr/local/news/etc/authmysql.config'); # read our config # create our dbi string for the connection $conf{'database'} = "dbi:mysql:database=$conf{'DB'};host=$conf{'HOST'};"; # open the database connection my $dbh = db_open(%conf); # create the query we're going to pass through my $query = "SELECT $conf{'passcol'} FROM $conf{'TABLE'} WHERE $conf{'usercol'}=\'$stdin{'ClientAuthname'}\'"; # get our statement handler... not really a result. my $result = $dbh->prepare($query); $result->execute; my $cryptedpw = $result->fetchrow_array(); # the database needs to store this encrypted $stdin{'ClientPassword'} = crypt($stdin{'ClientPassword'}, $cryptedpw); # encrypt the client password we were given. # this query is to return a count of 1. there should either be one # match of the encrypted password/username or none. $query = "SELECT COUNT(*) FROM $conf{'TABLE'} WHERE" ."$conf{'usercol'}=\'$stdin{'ClientAuthname'}\' and" ."$conf{'passcol'}=\'$stdin{'ClientPassword'}\'"; $result = $dbh->prepare($query); $result->execute; my $query_result = $result->fetchrow_array(); # this should be 1 or 0, 1 on success $result->finish; # need to get the return shit working now so it will reply proper # information to news clients if( $query_result ) { # the user authenticated fine. print "user:$stdin{'ClientAuthname'}\n"; # return based on nnrpd # auth specs. exit(0); # exit with no error } else { exit(502); # exit with error. username/password incorrect } # subs below sub readconf { my ($file) = @_; my %conf; open(CONF, "$file"); while () { chomp; next if $_ =~ /^#.*$/; next if $_ =~ /\s+/; $_ =~ s/#.*$//g; if ( $_ ne "" ) { my ($key, $value) = split(':', $_, 2); $conf{$key} = $_; } } close(CONF); return(%conf); } sub db_open { my (%conf) = @_; my ($err, $dbh); if( $dbh = DBI->connect($conf{'database'}, $conf{'User'}, $conf{'Pass'}) ) { return($dbh); } else { $err = "Failure opening database ".$DBI::errstr; } return(undef, $err); } sub get_stdin { my %stdin; while () { chomp; next if $_ =~ /^#.*$/; next if $_ =~ /\s+/; $_ =~ s/#.*$//g; $_ =~ s/\s+//g; if ( $_ ne "" ) { my $key = $_; my ($key, $value) = split(':', $_, 2); $stdin{$key} = $value; } } return(%stdin); } inn-2.6.0/contrib/reset-cnfs.c0000644000175200017520000000351712575023702015603 0ustar iuliusiulius/* Quick and Dirty Hack to reset a CNFS buffer without having to DD the * Entire Thing from /dev/zero again. */ #include "config.h" #include "clibrary.h" #include #include #include int main(int argc, char *argv[]) { int fd; int i, j; char buf[512]; #ifdef DO_LARGEFILES struct stat64 st; #else struct stat st; #endif int numwr; bool status = true; bzero(buf, sizeof(buf)); for (i = 1; i < argc; i++) { #ifdef DO_LARGEFILES if ((fd = open(argv[i], O_LARGEFILE | O_RDWR, 0664)) < 0) { #else if ((fd = open(argv[i], O_RDWR, 0664)) < 0) { #endif fprintf(stderr, "Could not open file %s: %s\n", argv[i], strerror(errno)); status = false; } else { #ifdef DO_LARGEFILES if (fstat64(fd, &st) < 0) { #else if (fstat(fd, &st) < 0) { #endif fprintf(stderr, "Could not stat file %s: %s\n", argv[i], strerror(errno)); status = false; } else { /* Each bit in the bitfield is 512 bytes of data. Each byte * has 8 bits, so calculate as 512 * 8 bytes of data, plus * fuzz. buf has 512 bytes in it, therefore containing data * for (512 * 8) * 512 bytes of data. */ numwr = (st.st_size / (512*8) / sizeof(buf)) + 50; printf("File %s: %lu %u\n", argv[i], (long unsigned) st.st_size, numwr); for (j = 0; j < numwr; j++) { if (!(j % 100)) { printf("\t%d/%d\n", j, numwr); } write(fd, buf, sizeof(buf)); } status = true; } close(fd); } } if (status) { exit(0); } else { exit(1); } } inn-2.6.0/contrib/mkbuf0000755000175200017520000000066412575023702014420 0ustar iuliusiulius#!/usr/bin/perl sub usage { print STDERR "Usage: $0 \n"; exit 1; } usage if(@ARGV != 2); $buf1k = "\0"x1024; $buf1m = "$buf1k"x1024; $kb = $ARGV[0] * 1; &usage if($kb == 0); if($ARGV[1] eq '-') { open(FILE, "|cat") or die; } else { open(FILE, ">$ARGV[1]") or die; } for($i = 0; $i+1024 <= $kb; $i+=1024) { print FILE $buf1m or die; } if($i < $kb) { print FILE "$buf1k"x($kb-$i) or die; } close FILE; inn-2.6.0/contrib/auth_pass.c0000644000175200017520000001117012575023702015513 0ustar iuliusiulius/* * auth_pass.c ( $Revision: 9701 $ ) * * Abstract: * * This module is the complete source for a sample "authinfo generic" * program. This program takes a user's login name and password * (supplied either as arguments or as responses to prompts) and * validates them against the contents of the password database. * * If the user properly authenticates themselves, a nnrp.auth style * record indicating the user's authenticated login and permitting * reading and posting to all groups is output on stderr (for reading by * nnrpd) and the program exits with a 0 status. If the user fails to * authenticate, then a record with the attempted login name and no * access is output on stderr and a non-zero exit status is returned. * * Exit statuses: * 0 Successfully authenticated. * 1 getpeername() failed, returned a bad address family, or * gethostbyaddr() failed. * 2 Entry not found in password file. * 3 No permission to read passwords, or password field is '*'. * 4 Bad password match. * * Environment: * Run by nnrpd with stdin/stdout connected to the reader and stderr * connected back to nnrpd. This program will need to be run as suid * root on systems where passwords are stored in a file readable only by * root. * * Written 1996 July 6 by Douglas Wade Needham (dneedham@oucsace.cs.ohiou.edu). * */ #include "config.h" #include "clibrary.h" #include "portable/socket.h" #include #include #if HAVE_CRYPT_H # include #endif int main(int argc, char** argv) /*+ * Abstract: * Main routine of the program, implementing all prompting, validation, * and status returns. * * Arguments: * argc Argument count. * argv Null terminated argument vector. * * Returns: * Exits according to program status values. * * Variables: * hp Pointer to host entry. * length General integer variable * password Password given by user. * peername Hostname of the peer. * pwd Pointer to entry from passwd file. * sin Socket address structure. * username User's login name. */ { struct hostent * hp; socklen_t length; char password[256]; char peername[1024]; struct passwd * pwd; struct sockaddr_in sin; char username[32]; /* * Get the user name and password if needed. */ if (argc<2) { fprintf(stdout, "Username: "); fflush(stdout); fgets(username, sizeof(username), stdin); } else { strlcpy(username, argv[1], sizeof(username)); } if (argc<3) { fprintf(stdout, "Password: "); fflush(stdout); fgets(password, sizeof(password), stdin); } else { strlcpy(password, argv[2], sizeof(password)); } /* * Strip CR's and NL's from the end. */ length = strlen(username)-1; while (username[length] == '\r' || username[length] == '\n') { username[length--] = '\0'; } length = strlen(password)-1; while (password[length] == '\r' || password[length] == '\n') { password[length--] = '\0'; } /* * Get the hostname of the peer. */ length = sizeof(sin); if (getpeername(0, (struct sockaddr *)&sin, &length) < 0) { if (!isatty(0)) { fprintf(stderr, "cant getpeername()::%s:+:!*\n", username); exit(1); } strlcpy(peername, "localhost", sizeof(peername)); } else if (sin.sin_family != AF_INET) { fprintf(stderr, "Bad address family %ld::%s:+:!*\n", (long)sin.sin_family, username); exit(1); } else if ((hp = gethostbyaddr((char *)&sin.sin_addr, sizeof(sin.sin_addr), AF_INET)) == NULL) { strlcpy(peername, inet_ntoa(sin.sin_addr), sizeof(peername)); } else { strlcpy(peername, hp->h_name, sizeof(peername)); } /* * Get the user name in the passwd file. */ if ((pwd = getpwnam(username)) == NULL) { /* * No entry in the passwd file. */ fprintf(stderr, "%s::%s:+:!*\n", peername, username); exit(2); } /* * Make sure we managed to read in the password. */ if (strcmp(pwd->pw_passwd, "*")==0) { /* * No permission to read passwords. */ fprintf(stderr, "%s::%s:+:!*\n", peername, username); exit(3); } /* * Verify the password. */ if (strcmp(pwd->pw_passwd, crypt(password, pwd->pw_passwd))!=0) { /* * Password was invalid. */ fprintf(stderr, "%s::%s:+:!*\n", peername, username); exit(4); } /* * We managed to authenticate the user. */ fprintf(stderr, "%s:RP:%s:+:*\n", peername, username); exit(0); } inn-2.6.0/contrib/analyze-traffic.pl0000755000175200017520000001545612575023702017012 0ustar iuliusiulius#!/usr/bin/perl -w ######################################################################## # # analyze-traffic.pl # # Written by Jeffrey M. Vinocur # This work is hereby placed in the public domain by its author. # # Script for keeping track of which newsgroups are receiving the most # traffic (by article count or byte usage), and which peer is most # responsible for the traffic in each high-traffic group. # ######################################################################## # # Usage: # # 1. Add an entry in $pathetc/newsfeeds like the one below, and issue # `ctlinnd reload newsfeeds traffic` (you can change the path to # whatever you like). # # analyze!\ # :*\ # :Tf,WgsbmnN:/usr/local/news/log/traffic # # You may find it useful to restrict the articles being logged, # either by modifing the wildmat pattern, or by using the /exclude # notation to indicate articles that have passed through some # servers should not be included. # # Also, if $USE_ALL_GROUPS (see below) is false, you can leave out # the N flag, which may eliminate some parsing errors. # # 2. Wait for some data. # # 3. Run analyze-traffic.pl on the logged data (you can pass the # filename as an argument or feed the data on standard input). # You probably want to pipe it into a file, `less`, or `tail` as the # output is a line for every group that has received an article # according the input data. # # There are some options hardcoded into the script below, under # "Constants" -- check for customization, if you like. # # 4. Be sure to comment out the newsfeeds entry when done, or set # up some sort of log rotation, or INN will eventually fill up your # disk... # ######################################################################## # # Implementation notes and known bugs: # # - We try (if $USE_ALL_GROUPS is set, below) to count crossposted # towards each listed group (even ones not carried on the server!), # but since some articles have funky Newsgroups headers, that can # backfire. So parsing can fail, which usually results in the # relevant line being skipped, but occasionally can cause Perl to # issue warnings (and perhaps produce funny things in the output). # # A workaround would be to repeat e.g. the Message-ID at the end of # the intput format (i.e. WgsbmnNm), and then the script could read as # many lines as necessary until that ad hoc end-of-record marker # appeared. I haven't found a need for this yet, though. # # - The input format is a sequence of lines, each containing a number of # space-separated fields. Check newsfeeds(5) for what the semantics # are, but an example line (wrapped), for reference, looks like: # # rec.aviation.military [space] # news-out.maxwell.syr.edu [space] # 2796 [space] # <3Jvua.104184$My6.1642017@twister.tampabay.rr.com> [space] # @030247454E45524C31000016AD3100000004@ [space] # rec.aviation.military,rec.travel.usa-canada, [no space here] # sci.electronics.design,sci.econ,sci.environment # # - The output format is a sequence of lines, one for each newsgroup, # with three tab-separated fields. They are sorted by either the # second or third field, depending on $SORT_BY_SIZE, below. The first # field is the name of the newsgroup. The second is the total number # of articles appearing in that newsgroup followed by, in parentheses, # the short name of the peer (see about $TLD_REGEX below) responsible # for the most articles and the percentage it made up. The third is # the total number of kilobytes of (accepted) traffic in that # newsgroup, followed similarly by the peer responsible for the most # traffic in that group. It looks something like this: # # news.lists.filters 1057 arts (63% syr) 7105.9 KB (36% cox) # # The short names are made by taking the last component of the # (dot-separated) peer name that doesn't match /$TLD_REGEX/. The idea # is that, for example, "isc.org" would be listed as "isc", and # "demon.co.uk" would be listed as "demon". Adjust $TLD_REGEX as # needed to trim the top-level domains in your part of the world. # # If your peers have very long short names, the output may look # somewhat funny. Similar things can happen with newsgroup names, so # those longer than $FIELD1_WIDTH will be truncated to fit. (You can # set $FIELD1_WIDTH to '' to skip this truncation, in which case the # first column will not be space-padded and the output will look a bit # ragged.) # ######################################################################## # # Constants: my $USE_ALL_GROUPS = 1; # if 0, use only group article is stored under my $SORT_BY_SIZE = 1; # if 0, sort output by number of articles my $FIELD1_WIDTH = 30; # maximum length of newsgroup name, '' for none my $TLD_REGEX = '^(?:com|net|org|edu|gov|mil|ac|co|uk|au|ca|de)$'; # feel free to add any others as needed ######################################################################## use strict; my %stats; while( <> ) { my ($group, $peer, $bytes, $id, $token, @Newsgroups) = split; next unless ($USE_ALL_GROUPS ? @Newsgroups : $token); # bad input line my @groups = map { split /\s*,\s*/ } @Newsgroups; foreach ($USE_ALL_GROUPS && @groups ? @groups : $group) { my $s = $stats{$_} ||= { count => 0, bytes => 0, peers => {}, }; $s->{count}++; $s->{bytes} += $bytes; $s->{peers}->{$peer}->{count}++; $s->{peers}->{$peer}->{bytes} += $bytes; } } my $f = $SORT_BY_SIZE ? 'bytes' : 'count'; foreach (sort { $stats{$a}->{$f} <=> $stats{$b}->{$f} } (keys %stats)) { my %s = %{$stats{$_}}; my ($topcount,$topcountwho) = &max('count', $s{peers}); my ($topbytes,$topbyteswho) = &max('bytes', $s{peers}); $topcountwho = &trim($topcountwho); $topbyteswho = &trim($topbyteswho); my $countf = int(100 * $topcount / $s{count}); my $bytesf = int(100 * $topbytes / $s{bytes}); my $kb = 0.1 * int($s{bytes} * 10 / 1024); my $ng = $FIELD1_WIDTH eq '' ? $_ : substr($_,0,$FIELD1_WIDTH); print +(sprintf("%-${FIELD1_WIDTH}s\t", $ng)), "$s{count} arts ($countf% $topcountwho)\t", "${kb} KB ($bytesf% $topbyteswho)\n"; } 1; sub trim { my @parts = split(/\./, $_[0]); my $part; while( defined($part = pop(@parts)) ) { last unless $part =~ /$TLD_REGEX/o; } return defined($part) ? $part : $_[0]; } sub max { my $x = 0; my $who; my ($field, $listref) = @_; while( my ($peer, $stats) = each %{$listref} ) { if( $stats->{$field} > $x ) { $x = $stats->{$field}; $who = $peer; } } return ($x, $who); } inn-2.6.0/contrib/makeexpctl.in0000644000175200017520000000523112575023702016046 0ustar iuliusiulius#! /usr/bin/perl -w # fixscript will replace this line with code to load INN::Config # Create expire.ctl script based on recently read articles. Argument gives # scale factor to use to adjust expires. $readfile="$INN::Config::pathdb/readgroups"; $expirectl=$INN::Config::expirectl; $curtime = time; $oldtime = $curtime - 30 * 86400; # 30 days in the past if (open(RDF, $readfile)) { while () { chop; @foo=split(/ /); # foo[0] should be group, foo[1] lastreadtime if ($foo[1] < $oldtime) { next; # skip entries that are too old. } $groups{$foo[0]} = $foo[1]; } close(RDF); } $scale = $ARGV[0]; if ($scale <= 0) { die "invalid scale parameter\n"; } rename($expirectl, "$expirectl.OLD") || die "rename $expirectl failed!\n"; open(OUTFILE, ">$expirectl") || die "open $expirectl for write failed!\n"; print OUTFILE <<'EOF' ; ## expire.ctl - expire control file ## Format: ## /remember/: ## :::: ## First line gives history retention; other lines specify expiration ## for newsgroups. Must have a "*:A:..." line which is the default. ## Wildmat-style patterns for the newsgroups ## Pick one of M U A -- modifies pattern to be only ## moderated, unmoderated, or all groups ## Minimum number of days to keep article ## Default number of days to keep the article ## Flush article after this many days ## , , and can be floating-point numbers or the ## word "never". Times are based on the arrival time for expire and expireover ## (unless -p is used; see expire(8) and expireover(8)), and on the posting ## time for history retention. ## ## See the expire.ctl man page for more information. # How long to remember old history entries for. /remember/:11 # EOF # defaults for most groups. printline("*", "A", 1); printline("alt*,misc*,news*,rec*,sci*,soc*,talk*,vmsnet*","U",3); printline("alt*,misc*,news*,rec*,sci*,soc*,talk*,vmsnet*","M",5); printline("comp*,gnu*,info*,ok*,ecn*,uok*", "U", 5); printline("comp*,gnu*,info*,ok*,ecn*,uok*", "M", 7); # and now handle each group that's regularly read, # assinging them 3* normal max expire foreach $i (keys %groups) { printline($i, "A", 21); } # and now put some overrides for groups which are too likely to fill spool if # we let them go to autoexpire. printline("*binaries*,*pictures*", "A", 0.5); printline("control*","A",1); printline("control.cancel","A",0.5); printline("news.lists.filters,alt.nocem.misc","A",1); close(OUTFILE); exit(1); sub printline { local($grpstr, $mflag, $len) = @_; print OUTFILE $grpstr,":",$mflag,":",$len*$scale,":",$len*$scale,":",$len*$scale,"\n"; } inn-2.6.0/contrib/archivegz.in0000644000175200017520000002124612575023702015677 0ustar iuliusiulius#!/usr/bin/perl # Copyright 1999 Stephen M. Benoit, Service Providers of America. # See notice at end of this file. # # Filename: archivegz.pl # Author: Stephen M. Benoit (benoits@servicepro.com) # Created: Wed Apr 14 13:56:01 1999 # Version: $Id: archivegz.in 4329 2001-01-14 13:47:52Z rra $ # $RCSID='$Id: archivegz.in 4329 2001-01-14 13:47:52Z rra $ '; # Specify command line options, and decode the command line. require 'newgetopt.pl'; require 'newusage.pl'; @opts = ( "help|usage;;print this message", "version;;print version", "a=s;;directory to archive in instead of the default", "f;;directory names will be flattened out", "i=s;;append one line to the index file for each article (Destination name, Message ID, Subject)", "m;; Files are copied by making a link. Not applicable, ignored", "r;;Suppress stderr redirection to /var/log/news/errlog", "n=s;;the news spool (source) directory (default=/var/spool/news/)", "t=i;;timeout that separates batches (default 10 seconds)", ";;input", # Examples. # # "OPT;;Option without an argument", # "OPT!;;Negatable option without an argument", # "VAR=T;;Option with mandatory argumet T = s(tring),i(nteger), or f(loat). # "VAR:T;;Option with optional argument. # "OPT|AAA|BBB";;AAA and BBB are aliases for OPT", # "VAR=T@";;Push option argument onto array @opt_VAR" ); $ignorecase = 0; $badopt = !&NGetOpt(&NMkOpts(@opts)); # $badarg = (@ARGV != 0); if ($badarg || $badopt || $opt_help) { &NUsage($0,0,'',@opts); exit ($badopt||$badarg); } if ($opt_version) {print STDERR "$RCSID\n"; exit 0} # -------------------------------------------------------------------- # --- constants and defaults --- $NEWS_ROOT = "/var/spool/news/"; $NEWS_ERR = "/var/log/news/errlog"; $NEWS_ARCHIVE = $NEWS_ROOT . "news.archive/"; $timeout = 10; if ($opt_t) { $timeout = $opt_t;} if ($timeout<1) {$timeout=1;} # -------------------------------------------------------------------- sub regexp_escape { local($data)=@_; $data =~ s+\\+\\\\+gi; # replace \ with \\ $data =~ s+\/+\\\/+gi; # replace / with \/ $data =~ s/([\+\*\?\[\]\(\)\{\}\.\|])/\\$1/gi; # replace +*?[](){}.| return $data; } sub fhbits { local(@fhlist) = split(' ',$_[0]); local($bits); for (@fhlist) { vec($bits,fileno($_),1) = 1; } $bits; } sub timed_getline { my ($fileh,$timeout)=@_; my $filehandle = (ref($fileh) ? (ref($fileh) eq 'GLOB' || UNIVERSAL::isa($fileh, 'GLOB') || UNIVERSAL::isa($fileh, 'IO::Handle')) : (ref(\$fileh) eq 'GLOB')); local(*FILEH) = *$fileh{FILEHANDLE}; local($rin,$win,$ein); local($rout,$wout,$eout); $rin = $win = $ein = ''; $rin = fhbits('FILEH'); $ein = $rin | $win; local($nfound); local($offset)=0; local($accum)=''; local($done)=0; local($result); $nfound = select($rout=$rin, $wout=$win, $eout=$ein, $timeout); if ($nfound>0) { # use sysread() to get characters up to end-of-line (incl.) while (!$done) { $result = sysread(FILEH, $accum, 1, $offset); if ($result<=0) { $done=1; return undef; } if (substr($accum,$offset,1) eq "\n") { $done=1; } else { $offset+=$result; } } } return $accum; } # -------------------------------------------------------------------- # --- source spool directory --- if ($opt_n) { if ($opt_n !~ /^\//) # absolute path? { $opt_n = $NEWS_ROOT . $opt_n; } if ($opt_n !~ /\/$/) # must end with / { $opt_n .= '/'; } $NEWS_ROOT = $opt_n; } # --- archive directory --- if ($opt_a) { if ($opt_a !~ /^\//) # absolute path? { $opt_a = $NEWS_ROOT . $opt_a; } if ($opt_a !~ /\/$/) # must end with / { $opt_a .= '/'; } $NEWS_ARCHIVE = $opt_a; } # --- redirect stderr --- if (!$opt_r) { open(SAVEERR, ">&STDERR"); open(STDERR, ">>$NEWS_ERR") || die "Can't redirect stderr"; } # --- get input file opened --- if ($infilename=shift(@ARGV)) { if ($infilename !~ /^\//) # absolute filename? { $infilename = $NEWS_ROOT . $infilename; } } else { $infilename="-"; } open(INFILE,"<$infilename"); $done=0; while (!$done) { %sourcefile=(); %destfile=(); %destname=(); # --- loop over each line in infile --- # comments start with '#', ignore blank lines, each line is a filename while ($srcfile = &timed_getline(INFILE,$timeout)) { if ($srcfile =~ /\#/) {$srcfile = $`;} if ($srcfile =~ /^\s*/) {$srcfile = $';} if ($srcfile =~ /\s*$/) {$srcfile = $`;} if ($srcfile) # if a filename survived all that... { if ($srcfile !~ /^\//) # absolute filename? { $srcfile = $NEWS_ROOT . $srcfile; } # $srcfile is now a valid, absolute filename # split filename into news directory, newsgroup and article number $artnum=-1; $remaining=$srcfile; if ($remaining =~ /\/(\d*)$/) # remove / and article number { $artnum = $1; $remaining=$`;} $regex = ®exp_escape($NEWS_ROOT); if ($remaining =~ /^$regex/) # split off news dir { $newsdir = $&; $grpdir = $';} else { $newsdir = ''; $grpdir = $remaining; } # ... otherwise, grp = dir $newsgrp = $grpdir; $newsgrp =~ s/\//\./g; # replace slash (/) with dot (.) if ($opt_f) { $grpdir = "$newsgrp.gz"; } else { $grpdir .= "/archive.gz"; } $destfile = $NEWS_ARCHIVE . $grpdir; # print STDERR "$srcfile --> $newsgrp --> $destfile\n"; if ($sourcefile{$newsgrp}) {$sourcefile{$newsgrp} .= " ";} $sourcefile{$newsgrp} .= $srcfile; $destfile{$newsgrp} = $destfile; $destname{$newsgrp} = $grpdir; } } # --- is there anything to do at this time? --- if (%destfile) { # --- open INDEX --- if ($opt_i) { # make sure directory exists if ($opt_i =~ /\/[^\/]*$/) { $dirbase=$`; system("mkdir -p $dirbase"); } open(INDEX,">>$opt_i"); } # --- make sure that archive file can be written (make parent dirs) --- if ($destfile{$group} =~ /\/[^\/]*$/) { $dirbase=$`; system("mkdir -p $dirbase"); } # --- process each article --- foreach $group (keys(%destfile)) { # --- gzip the concatenated document, appending archive file --- open(GZIP, "|gzip -c >> $destfile{$group}") || die "Can't open gzip"; # --- concatenate the articles, keeping header info if needed --- @accum_headers=(); foreach $srcfile (split(/\s+/, $sourcefile{$group})) { # print STDERR "reading $srcfile...\n"; $this_doc=''; open(DOC, "<$srcfile"); while ($line=) { $this_doc .= $line; } close(DOC); print GZIP $this_doc; if ($opt_i) { # --- get header information and store it in index $subject=''; $mesageid=''; $destname=''; if ($this_doc =~ /Subject:\s*(.*)/) { $subject = $1; } if ($subject =~ /^\s*/) {$subject = $';} if ($subject =~ /\s*$/) {$subject = $`;} if ($this_doc =~ /Message-ID:\s*(.*)/) {$messageid = $1; } if ($messageid =~ /^\s*/) {$messageid = $';} if ($messageid =~ /\s*$/) {$messageid = $`;} print INDEX "$destname{$group} $messageid $subject\n"; } } close(GZIP); } # --- close index file --- if ($opt_i) { close(INDEX); } } if (!defined($srcfile)) # file was closed { $done=1; last; # "break" } } # --- restore stderr --- if (!$opt_r) { close(STDERR); open(STDERR,">>&SAVEERR"); } # --- close input file --- close(INFILE); __END__ # Local Variables: # mode: perl # End: # Copyright 1999 Stephen M. Benoit, Service Providers of America (SPA). # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose without fee is hereby granted without fee, # provided that the above copyright notice appear in all copies and that both # that copyright notice and this permission notice appear in supporting # documentation, and that the name of SPA not be used in advertising or # publicity pertaining to distribution of the software without specific, # written prior permission. SPA makes no representations about the # suitability of this software for any purpose. It is provided "as is" # without express or implied warranty. # # SPA DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL # SPA BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY # DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN # AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. inn-2.6.0/contrib/newsresp.c0000644000175200017520000001717612575023702015406 0ustar iuliusiulius/* newsresp.c - EUnet - bilse */ /* * From: Koen De Vleeschauwer * Subject: Re: innfeed-users: innfeed: measuring server response time * To: jeff.garzik@spinne.com (Jeff Garzik) * Date: Tue, 13 May 1997 16:33:27 +0200 (MET DST) * Cc: innfeed-users@vix.com * * > Is there an easy way to measure server response time, and print it out * > on the innfeed status page? Cyclone's nntpTime measures login banner * > response time and an article add and lookup operation. * > * > It seems to me that innfeed could do something very similar. It could * > very easily sample gettimeofday() or Time.Now to determine a remote * > server's average response time for lookups, lookup failures, article * > send throughput, whatever. * > * > These statistics might be invaluable to developers creating advanced * > connection and article delivery algorithms. If I knew, for example, * > that a site's article send/save throughput was really fast, but history * > lookups were really slow, my algorithm could reserve a channel or two * > for TAKETHIS-only use. * * We use a stand-alone program which opens up an additional nntp channel * from time to time and takes a peek at the various response times. * It's also interesting to tune one's own box. * I've included the source code; please consider this supplied 'as is'; * bugs and features alike. SunOS, Solaris and Irix ought to be ok; * eg. gcc -traditional -o newsresp ./newsresp.c -lnsl -lsocket on S0laris. * If a host has an uncommonly long banner you may have to change a constant * somewhere; forget. Please note one has to interpret the output; * eg. whether one is measuring rtt or history lookup time. * * Basic usage is: * news 1 % newsresp -n 5 news.eu.net * --------------------------------- * news.eu.net is 134.222.90.2 port 119 * elap diff * 0.0 0.0 Connecting ... * 0.0 0.0 OK, waiting for prompt * 0.0 0.0 <<< 200 EU.net InterNetNews server INN 1.5.1 17-Dec-1996 re [...] * 0.0 0.0 >>> ihave <244796399@a> * 0.0 0.0 <<< 335 * 0.0 0.0 >>> . * 0.0 0.0 <<< 437 Empty article * 0.0 0.0 >>> ihave <244796398@a> * 0.0 0.0 <<< 335 * 0.0 0.0 >>> . * 0.0 0.0 <<< 437 Empty article * 0.0 0.0 >>> ihave <244796397@a> * 0.0 0.0 <<< 335 * 0.0 0.0 >>> . * 0.0 0.0 <<< 437 Empty article * 0.0 0.0 >>> ihave <244796396@a> * 0.1 0.0 <<< 335 * 0.1 0.0 >>> . * 0.1 0.0 <<< 437 Empty article * 0.1 0.0 >>> ihave <244796395@a> * 0.1 0.0 <<< 335 * 0.1 0.0 >>> . * 0.1 0.0 <<< 437 Empty article * 0.1 0.0 >>> quit * 0.1 0.0 <<< 205 . */ #include "clibrary.h" #include "portable/socket.h" #include #ifdef HAVE_SYS_TIME_H # include #endif #include #define NNTPPORT 119 void error(const char *); void fatal(const char *); void ierror(const char *, const char *); void ifatal(const char *, const char *); unsigned int do_time(unsigned int); void ptime(void); void massagebuff(int, char *); bool punt(int); struct sockaddr_in sock_in; int sock; char buf[1024]; int main(int argc, char *argv[]) { int errflg = 0, c; bool status = true; struct hostent *host; unsigned long temp; unsigned numart = 1; struct protoent *tcp_proto; char **whoP; while ( (c = getopt(argc,argv,"n:")) != -1 ) switch ( c ) { case 'n': sscanf(optarg,"%u",&numart); break; default : errflg++; } if ( numart == 0 || optind == argc ) errflg++; if ( errflg ) { fprintf(stderr,"Usage: %s [-n articles] host ...\n",argv[0]); exit(1); } if ( (tcp_proto = getprotobyname("tcp")) == 0 ) fatal("getprotobyname"); for ( whoP = argv+optind; *whoP != 0; whoP++ ) { if ( (sock = socket(PF_INET,SOCK_STREAM,tcp_proto->p_proto)) < 0 ) fatal("socket"); temp = inet_addr(*whoP); if ( temp != (unsigned long) -1 ) { sock_in.sin_addr.s_addr = temp; sock_in.sin_family = AF_INET; } else { host = gethostbyname(*whoP); if ( host ) { sock_in.sin_family = host->h_addrtype; memcpy(&sock_in.sin_addr,host->h_addr,host->h_length); } else { fprintf(stderr,"gethostbyname can't find %s\n",*whoP); exit(1); } } sock_in.sin_port = htons(NNTPPORT); printf("---------------------------------\n%s is %s port %d\n", *whoP,inet_ntoa(sock_in.sin_addr),ntohs(sock_in.sin_port)); status = punt(numart); close(sock); } if (status) { exit(0); } else { exit(1); } } void error(const char *what) { ptime(); fflush(stdout); perror(what); } void fatal(const char *what) { error(what); exit(2); } void ierror(const char *how, const char *what) { printf("Expected %s, received %s; bailing out.\n", how, what); } void ifatal(const char *how, const char *what) { ierror(how, what); exit(1); } unsigned int do_time(unsigned int start) { struct timeval now; gettimeofday(&now,(struct timezone *)0); return ( now.tv_sec*1000 + now.tv_usec/1000 - start ); } unsigned int start, elapsed, diff; void ptime(void) { diff = elapsed; elapsed = do_time(start); diff = elapsed - diff; printf("%5.1f %5.1f ",((float)elapsed)/1000.0,((float)diff)/1000.0); } void massagebuff(int bread, char *buf) { char *p; if ( bread > 55 ) strcpy(buf+55," [...]\n"); else buf[bread] = '\0'; for ( p = buf; *p != '\0'; ) if ( *p != '\r' ) /* We like to do it RISC style. */ p++; else { *p = ' '; p++; } } bool punt(int numart) { static char ihave[32], dot[] = ".\r\n", quit[] = "quit\r\n"; struct timeval start_tv; int bread; printf(" elap diff\n"); diff = elapsed = 0; gettimeofday(&start_tv,(struct timezone *)0); start = start_tv.tv_sec*1000 + start_tv.tv_usec/1000; ptime(); printf("Connecting ...\n"); if ( connect(sock,(struct sockaddr*)&sock_in,sizeof(sock_in)) < 0 ) { error("connect"); return false; } ptime(); printf("OK, waiting for prompt\n"); if ( (bread=read(sock,buf,sizeof(buf))) < 0 ) { error("read socket"); return false; } massagebuff(bread,buf); ptime(); printf("<<< %s",buf); if ( strncmp(buf,"200",3) != 0 && strncmp(buf,"201",3) != 0 ) { ierror("200 or 201",buf); return false; } do { snprintf(ihave,sizeof(ihave),"ihave <%u@a>\r\n",start+numart); ptime(); printf(">>> %s",ihave); if ( write(sock,ihave,strlen(ihave)) != (int) strlen(ihave) ) { error("write socket"); return false; } if ( (bread=read(sock,buf,sizeof(buf))) < 0 ) { error("read socket"); return false; } massagebuff(bread,buf); ptime(); printf("<<< %s",buf); if ( strncmp(buf,"335",3) != 0 && strncmp(buf,"435",3) != 0 ) { ierror("335 or 435",buf); return false; } if ( strncmp(buf,"335",3) == 0 ) { ptime(); printf(">>> %s",dot); if ( write(sock,dot,sizeof(dot)-1) != sizeof(dot)-1 ) { error("write socket"); return false; } if ( (bread=read(sock,buf,sizeof(buf))) < 0 ) { error("read socket"); return false; } massagebuff(bread,buf); ptime(); printf("<<< %s",buf); if ( strncmp(buf,"437",3) != 0 && strncmp(buf,"235",3) != 0 ) { ierror("437 or 235",buf); return false; } } } while ( --numart != 0 ); ptime(); printf(">>> %s",quit); if ( write(sock,quit,sizeof(quit)-1) != sizeof(quit)-1 ) { error("write socket"); return false; } if ( (bread=read(sock,buf,sizeof(buf))) < 0 ) { error("read socket"); return false; } massagebuff(bread,buf); ptime(); printf("<<< %s",buf); if ( strncmp(buf,"205",3) != 0 ) { ierror("205",buf); return false; } return true; } inn-2.6.0/contrib/sample.init.script0000644000175200017520000000304012575023702017026 0ustar iuliusiulius#! /bin/sh # $Id: sample.init.script 9598 2014-02-08 13:48:49Z iulius $ # # This is a simple, bare-bones example of a SysV-style init.d script for INN. # It tries to increase the file descriptor limits to the maximum allowed by # the system since INN and related programs can be file descriptor hogs. test -f '/rc.news' || exit 0 start () { ulimit -n unlimited # Start INN. su news -s /bin/sh -c '/rc.news' >> /rc.news 2>&1 # Start another nnrpd daemon, handling initial TLS connections, on port 563. # (The preferred way would be to use port 119 and STARTTLS but not all news # readers support it yet.) #su news -s /bin/sh -c '/nnrpd -D -p 563 -S' >> /rc.news 2>&1 } case "$1" in start) start ;; stop) # Stop INN. su news -s /bin/sh -c '/rc.news stop' >> /rc.news 2>&1 # Stop possible other nnrpd daemons. One of the two following commands is enough. #start-stop-daemon --stop --name nnrpd --quiet --oknodo #su news -s /bin/sh -c 'killall nnrpd' >> /rc.news 2>&1 ;; reload|force-reload) # Reload INN. /ctlinnd -t 20 reload '' 'reload asked' ;; restart) # Restart INN. if [ -f '/innd.pid' ]; then /ctlinnd -t 20 throttle 'restart asked' > /dev/null || true /ctlinnd -t 20 xexec innd > /dev/null || start else start fi ;; *) echo "Recognized arguments: start|stop|reload|force-reload|restart" >&2 exit 1 ;; esac exit 0 inn-2.6.0/contrib/tunefeed.in0000644000175200017520000004225212575023702015514 0ustar iuliusiulius#!/usr/bin/perl $version = q$Id: tunefeed.in 9767 2014-12-07 21:13:43Z iulius $; # # tunefeed -- Compare active files with a remote site to tune a feed. # Copyright 1998 by Russ Allbery # # This program is free software; you can redistribute it and/or modify it # under the same terms as Perl itself. ############################################################################ # Site configuration ############################################################################ # A list of hierarchies in the Big Eight. %big8 = map { $_ => 1 } qw(comp humanities misc news rec sci soc talk); # A list of hierarchies that are considered global and not language # hierarchies. %global = map { $_ => 1 } qw(bionet bit biz borland ddn gnu gov ieee info linux k12 microsoft netscape tnn vmsnet); # The pattern matching local-only hierarchies (that we should disregard when # doing feed matching). %ignore = map { $_ => 1 } qw(clari control junk); ############################################################################ # Modules and declarations ############################################################################ require 5.003; use Getopt::Long qw(GetOptions); use strict; use vars qw(%big8 $days %global %ignore $threshold %traffic $version); ############################################################################ # Active file hashing and analysis ############################################################################ # Read in an active file, putting those groups into a hash where the key is # the name of the group and the value is always 1. If the optional third # argument is true, exclude any groups in the hierarchies listed in %local # and use this active file to store traffic information (in a rather # simple-minded fashion). sub hash { my ($file, $hash, $local) = @_; open (ACTIVE, $file) or die "$0: cannot open $file: $!\n"; local $_; while () { my ($group, $high, $low, $flags) = split; next if ($flags =~ /^=|^x/); my $hierarchy = (split (/\./, $group, 2))[0]; next if ($local && $ignore{$hierarchy}); $$hash{$group} = 1; $traffic{$group} = ($high - $low) / $days if $local; } close ACTIVE; } # Read in a file that gives traffic statistics. We assume it's in the form # group, whitespace, number of articles per day, and we just read it # directly into the %traffic hash. sub traffic { my ($file) = @_; open (TRAFFIC, $file) or die "$0: cannot open $file: $!\n"; local $_; while () { my ($group, $traffic) = split; $traffic{$group} = $traffic; } close TRAFFIC; } # Pull off the first X nodes of a group name. sub prefix { my ($group, $count) = @_; my @group = split (/\./, $group); splice (@group, $count); join ('.', @group); } # Find the common hierarchical prefix of a list. sub common { my (@list) = @_; my @prefix = split (/\./, shift @list); local $_; while (defined ($_ = shift @list)) { my @group = split /\./; my $i; $i++ while ($prefix[$i] && $prefix[$i] eq $group[$i]); if ($i <= $#prefix) { splice (@prefix, $i) } } join ('.', @prefix); } # Given two lists, a list of groups that the remote site does have and a # list of groups that the remote site doesn't have, in a single hierarchy, # perform a smash. The object is to find the minimal pattern that expresses # just the groups they want. We're also given the common prefix of all the # groups in the have and exclude lists, and a flag indicating whether we're # coming in with a positive assumption (all groups sent unless excluded) or # a negative assumption (no groups sent unless added). sub smash { my ($have, $exclude, $top, $positive) = @_; my (@positive, @negative); my $level = ($top =~ tr/././) + 1; # Start with the positive assumption. We make copies of our @have and # @exclude arrays since we're going to be needing the virgin ones again # later for the negative assumption. If we're coming in with the # negative assumption, we have to add a wildcarded entry to switch # assumptions, and we also have to deal with the cases where there is a # real group at the head of the hierarchy. my @have = @$have; my @exclude = @$exclude; if ($top eq $have[0]) { shift @have; push (@positive, "$top*") unless $positive; } else { if ($top eq $exclude[0]) { if ($positive && $traffic{$top} > $threshold) { push (@positive, "!$top"); } shift @exclude; } push (@positive, "$top.*") unless $positive; } # Now that we've got things started, keep in mind that we're set up so # that every group will be sent *unless* it's excluded. So we step # through the list of exclusions. The idea here is to pull together all # of the exclusions with the same prefix (going one level deeper into # the newsgroup names than we're currently at), and then find all the # groups with the same prefix that the remote site *does* want. If # there aren't any, then we can just exclude that whole prefix provided # that we're saving enough traffic to make it worthwhile (checked # against the threshold). If there are, and if the threshold still # makes it worthwhile to worry about this, we call this sub recursively # to compute the best pattern for that prefix. while (defined ($_ = shift @exclude)) { my ($prefix) = prefix ($_, $level + 1); my @drop = ($_); my @keep; my $traffic = $traffic{$_}; while ($exclude[0] =~ /^\Q$prefix./) { $traffic += $traffic{$exclude[0]}; push (@drop, shift @exclude); } $prefix = common (@drop); my $saved = $traffic; while (@have && $have[0] le $prefix) { shift @have } while ($have[0] =~ /^\Q$prefix./) { $traffic += $traffic{$have[0]}; push (@keep, shift @have); } next unless $saved > $threshold; if (@keep) { $traffic{"$prefix*"} = $traffic; push (@positive, smash (\@keep, \@drop, $prefix, 1)); } elsif (@drop == 1) { push (@positive, "!$_"); } elsif ($prefix eq $_) { push (@positive, "!$prefix*"); } else { push (@positive, "!$prefix.*"); } } # Now we do essentially the same thing, but from the negative # perspective (adding a wildcard pattern as necessary to make sure that # we're not sending all groups and then finding the groups we are # sending and trying to smash them into minimal wildcard patterns). @have = @$have; @exclude = @$exclude; if ($top eq $exclude[0]) { shift @exclude; push (@negative, "!$top*") if $positive; } else { if ($top eq $have[0]) { push (@negative, $top) unless $positive; shift @have; } push (@negative, "!$top.*") if $positive; } # This again looks pretty much the same as what we do for the positive # case; the primary difference is that we have to make sure that we send # them every group that they want, so we still err on the side of # sending too much, rather than too little. while (defined ($_ = shift @have)) { my ($prefix) = prefix ($_, $level + 1); my @keep = ($_); my @drop; my $traffic = $traffic{$_}; while ($have[0] =~ /^\Q$prefix./) { $traffic += $traffic{$have[0]}; push (@keep, shift @have); } $prefix = common (@keep); while (@exclude && $exclude[0] le $prefix) { shift @exclude } my $saved = 0; while ($exclude[0] =~ /^\Q$prefix./) { $saved += $traffic{$exclude[0]}; push (@drop, shift @exclude); } if (@drop && $saved > $threshold) { $traffic{"$prefix*"} = $traffic + $saved; push (@negative, smash (\@keep, \@drop, $prefix, 0)); } elsif (@keep == 1) { push (@negative, $_); } elsif ($prefix eq $_) { push (@negative, "$prefix*"); } else { push (@negative, "$prefix.*"); } } # Now that we've built both the positive and negative case, we decide # which to return. We want the one that's the most succinct, and if # both descriptions are equally succinct, we return the negative case on # the grounds that it's likely to send less of what they don't want. (@positive < @negative) ? @positive : @negative; } ############################################################################ # Output ############################################################################ # We want to sort Big Eight ahead of alt.* ahead of global non-language # hierarchies ahead of regionals and language hierarchies. sub score { my ($hierarchy) = @_; if ($big8{$hierarchy}) { return 1 } elsif ($hierarchy eq 'alt') { return 2 } elsif ($global{$hierarchy}) { return 3 } else { return 4 } } # Our special sort routine for hierarchies. It calls score to get a # hierarchy score and sorts on that first. sub by_hierarchy { (score $a) <=> (score $b) || $a cmp $b; } # Given a reference to a list of patterns, output it in some reasonable # form. Currently, this is lines prefixed by a tab, with continuation lines # like INN likes to have in newsfeeds, 76 column margin, and with a line # break each time the hierarchy score changes. sub output { my ($patterns) = @_; my ($last, $line); for (@$patterns) { my ($hierarchy) = /^!?([^.]+)/; my $score = score $hierarchy; $line += 1 + length $_; if (($last && $score > $last) || $line > 76) { print ",\\\n\t"; $line = 8 + length $_; } elsif ($last) { print ','; } else { print "\t"; $line += 8; } print; $last = $score; } print "\n"; } ############################################################################ # Main routine ############################################################################ # Clean up the name of this program for error messages. my $fullpath = $0; $0 =~ s%.*/%%; # Parse the command line. Our argument is the path to an active file (we # tell the difference by seeing if it contains a /). my ($help, $print_version); Getopt::Long::config ('bundling'); GetOptions ('help|h' => \$help, 'days|d=i' => \$days, 'threshold|t=i' => \$threshold, 'version|v' => \$print_version) or exit 1; # Set a default for the minimum threshold traffic required to retain an # exclusion, and assume that active file differences represent one day of # traffic unless told otherwise. $threshold = (defined $threshold) ? $threshold : 250; $days ||= 1; # If they asked for our version number, abort and just print that. if ($print_version) { my ($program, $ver) = (split (' ', $version))[1,2]; $program =~ s/,v$//; die "$program $ver\n"; } # If they asked for help, give them the documentation. if ($help) { print "Feeding myself to perldoc, please wait....\n"; exec ('perldoc', '-t', $fullpath) or die "$0: can't fork: $!\n"; } # Hash the active files, skipping groups we ignore in the local one. Make # sure we have our two files listed first. unless (@ARGV == 2 || @ARGV == 3) { die "Usage: $0 [-hv] [-t ] []\n"; } my (%local, %remote); hash (shift, \%local, 1); hash (shift, \%remote); traffic (shift) if @ARGV; # Now, we analyze the differences between the two feeds. We're trying to # build a pattern of what *we* should send *them*, so stuff that's in # %remote and not in %local doesn't concern us. Rather, we're looking for # stuff that we carry that they don't, since that's what we'll want to # exclude from a full feed. my (%have, %exclude, %count, $have, $exclude, $positive); for (sort keys %local) { my ($hierarchy) = (split /\./); $count{$hierarchy}++; $traffic{"$hierarchy*"} += $traffic{$_}; if ($remote{$_}) { push (@{$have{$hierarchy}}, $_); $have++ } else { push (@{$exclude{$hierarchy}}, $_); $exclude++ } } my @patterns; if ($have > $exclude * 4) { push (@patterns, "*"); $positive = 1; } for (sort by_hierarchy keys %count) { if ($have{$_} && !$exclude{$_}) { push (@patterns, "$_.*") unless $positive; } elsif ($exclude{$_} && !$have{$_}) { push (@patterns, "!$_.*") if $positive; } else { push (@patterns, smash ($have{$_}, $exclude{$_}, $_, $positive)); } } output (\@patterns); __END__ ############################################################################ # Documentation ############################################################################ =head1 NAME tunefeed - Build a newsgroups pattern for a remote feed =head1 SYNOPSIS B [B<-hv>] [B<-t> I] [B<-d> I] I I [I] =head1 DESCRIPTION Given two active files, B generates an INN newsfeeds pattern for a feed from the first site to the second, that sends the second site everything in its active file carried by the first site but tries to minimize the number of rejected articles. It does this by noting differences between the two active files and then trying to generate wildcard patterns that cover the similarities without including much (or any) unwanted traffic. I and I should be standard active files. You can probably get the active file of a site that you feed (provided they're running INN) by connecting to their NNTP port and typing C. B makes an effort to avoid complex patterns when they're of minimal gain. I is the number of messages per day at which to worry about excluding a group; if a group the remote site doesn't want to receive gets below that number of messages per day, then that group is either sent or not sent depending on which choice results in the simplest (shortest) wildcard pattern. If you want a pattern that exactly matches what the remote site wants, use C<-t 0>. Ideally, B likes to be given the optional third argument, I, which points at a file listing traffic numbers for each group. The format of this file is a group name, whitespace, and then the number of messages per day it receives. Without such a file, B will attempt to guess traffic by taking the difference between the high and low numbers in the active file as the amount of traffic in that group per day. This will almost always not be accurate, but it should at least be a ballpark figure. If you know approximately how many days of traffic the active file numbers represent, you can tell B this information using the B<-d> flag. B's output will look something like: comp.*,humanities.classics,misc.*,news.*,rec.*,sci.*,soc.*,talk.*,\ alt.*,!alt.atheism,!alt.binaries.*,!alt.nocem.misc,!alt.punk*,\ !alt.sex*,!alt.video.dvd,\ bionet.*,biz.*,gnu.*,vmsnet.*,\ ba.*,!ba.jobs.agency,ca.*,sbay.* (with each line prefixed by a tab, and with standard INN newsfeeds continuation syntax). Due to the preferences of the author, it will also be sorted as Big Eight, then alt.*, then global non-language hierarchies, then regional and language hierarchies. =head1 OPTIONS =over 4 =item B<-h>, B<--help> Print out this documentation (which is done simply by feeding the script to C. =item B<-v>, B<--version> Print out the version of B and exit. =item B<-d> I, B<--days>=I Assume that the difference between the high and low numbers in the active file represent I days of traffic. =item B<-t> I, B<--threshold>=I Allow any group with less than I articles per day in traffic to be either sent or not sent depending on which choice makes the wildcard patterns simpler. If a threshold isn't specified, the default value is 250. =back =head1 BUGS This program takes a long time to run, not to mention being a nasty memory hog. The algorithm is thorough, but definitely not very optimized, and isn't all that friendly. Guessing traffic from active file numbers is going to produce very skewed results on sites with expiration policies that vary widely by group. There is no way to optimize for size in avoiding rejections, only quantity of articles. There should be a way to turn off the author's idiosyncratic ordering of hierarchies, or to specify a different ordering, without editing this script. This script should attempt to retrieve the active file from the remote site automatically if so desired. This script should be able to be given some existing wildcard patterns and take them into account when generating new ones. =head1 CAVEATS Please be aware that your neighbor's active file may not accurately represent the groups they wish to receive from you. As with everything, choices made by automated programs like this one should be reviewed by a human and the remote site should be notified, and if they have sent explicit patterns, those should be honored instead. I definitely do *not* recommend running this program on any sort of automated basis. =head1 AUTHOR Russ Allbery Eeagle@eyrie.orgE =cut inn-2.6.0/contrib/delayer.in0000644000175200017520000000254412575023702015342 0ustar iuliusiulius#!/usr/bin/perl # -*- perl -*- # # delay lines for N seconds. # # primarily meant to be used with INN to generate a delayed feed with innfeed. # # put it into your newsfeeds file like # # innfeed-delayed!\ # :!*\ # :Tc,Wnm*,S16384:/usr/local/news/bin/delayer 60 \ # /usr/local/news/bin/innfeed -c innfeed-delayed.conf # # done by christian mock sometime in july 1998, # and put into the public domain. # $delay = shift || die "usage: $0 delay prog-n-args\n"; $timeout = $delay; $eof = 0; open(OUT, "|" . join(" ", @ARGV)) || die "open |prog-n-args: $!\n"; #select(OUT); #$| = 1; #select(STDOUT); $rin = ''; vec($rin,fileno(STDIN),1) = 1; while(!$eof || $#queue >= 0) { if(!$eof) { ($nfound,$timeleft) = select($rout=$rin, undef, undef, $timeout); } else { sleep($timeout); } $now = time(); $exp = $now + $delay; if(!$eof && vec($rout,fileno(STDIN),1)) { $line = ; if(!defined $line) { # exit NOW! foreach(@queue) { s/^[^:]+://g; print OUT; } close(OUT); sleep(1); exit; } push(@queue, "$exp:$line"); } if($#queue < 0) { undef $timeout; next; } ($first, $line) = split(/:/, $queue[0], 2); while($#queue >= 0 && $first <= $now) { print OUT $line; shift(@queue); ($first, $line) = split(/:/, $queue[0], 2); } $timeout = $first - $now; } inn-2.6.0/INSTALL0000644000175200017520000024663212575023702012766 0ustar iuliusiuliusWelcome to INN 2.6! Please read this document thoroughly before trying to install INN. You'll be glad you did. If you are upgrading from a major release of INN prior to 2.3, it is recommended that you make copies of your old configuration files and use them as guides for doing a clean installation and configuration of 2.6. Many config files have changed, some have been added, and some have been removed. You'll find it much easier to start with a fresh install than to try to update your old installation. This is particularly true if you're upgrading from a version of INN prior to 2.0. If you are upgrading from INN 2.3 or later, you may be able to just update the binaries, scripts, and man pages by running: make update after building INN and then comparing the new sample configuration files with your current ones to see if anything has changed. If you take this route, the old binaries and scripts will be saved with an extension of ".OLD" so that you can easily back out. Note that if you do not want to have such backup copies, you can deactivate this behaviour with "make BACKUP_OPTION='' update" instead of a mere "make update". Be sure to configure INN with the same options that you used previously if you take this approach (in particular, INN compiled with --enable-largefiles can't read the data structures written by INN compiled without that flag, and vice versa). If you don't remember what options you used but you have your old build tree, look at the comments at the beginning of config.status. If you made ckpasswd setuid root so that you could use system passwords, you'll have to do that again after "make update". (It's much better to use PAM instead if you can.) If you use "make update" to upgrade from a previous major release of INN, also look at the new sample configuration files in samples to see if there are new options of interest to you. For more information about recent changes, see NEWS. Supported Systems As much as possible, INN is written in portable C and should work on any Unix platform. It does, however, make extensive use of mmap(2) and certain other constructs that may be poorly or incompletely implemented, particularly on very old operating systems. INN has been confirmed to work on the following operating systems: AIX 4.3 FreeBSD 2.2.x and up HP-UX 10.20 and up Linux 2.x (tested with libc 5.4, glibc 2.0 and up) Mac OS X 10.2 and up NetBSD 1.6 and up OpenBSD 2.8 and up SCO 5.0.4 (tested with gcc 2.8.1, cc) Solaris 2.5.x and up UnixWare 7.1 UX/4800 R11 and up If you have gotten INN working on an operating system other than the ones listed above, please let us know at . Before You Begin INN requires several other packages be installed in order to be fully functional (or in some cases, to work at all): * In order to build INN, you will need a C compiler that understands ANSI C. If you are trying to install INN on an operating system that doesn't have an ANSI C compiler (such as SunOS), installing gcc is recommended. You can get it from or its mirrors. INN is tested with gcc more thoroughly than with any other compiler, so even if you have another compiler available, you may wish to use gcc instead. * Currently, in order to build INN, you will need an implementation of yacc. GNU bison (from or its mirrors) will work fine. We hope to remove this requirement in the future. * INN requires at least Perl 5.8.0 to build and to run several subsystems. In order to process control messages, controlchan needs the "MIME::Parser" module available from CPAN ("MIME-tools" in modules/by-module/MIME/, for instance on ). This Perl module has probably already been packaged for your distribution. INN is tested primarily with newer versions of Perl, so it's generally recommended that you install the latest stable distribution of Perl before compiling INN. For instructions on obtaining and installing Perl, see . Note that you may need to use the same compiler and options (particularly large file support) for Perl and INN. If you're using a version of Perl prior to 5.6.0, you may need to make sure that the Perl versions of your system header files have been generated in order for "Sys::Syslog" to work properly (used by various utility programs, including controlchan). To do this, run the following two commands: # cd /usr/include # h2ph * sys/* An even better approach is to install Perl 5.6.1 or later, which have a fixed version of "Sys::Syslog" that doesn't require this (as well as many other improvements over earlier versions of Perl). * The INN Makefiles use the syntax "include FILE", rather than the syntax expected by some BSDish systems of ".include ". If your system expects the latter syntax, the recommended solution is to install GNU make from . You may have GNU make already installed as gmake, in which case using gmake rather than make to build INN should be sufficient. * If you want to enable support for authenticated control messages (this is not required, but is highly recommended for systems carrying public Usenet hierarchies) then you will need to install some version of PGP. The recommended version is GnuPG, since it's actively developed, supports OpenPGP, is freely available from and free to use for any purpose (in the US and elsewhere), and (as of version 1.0.4 at least) supports the RSA signatures used by most current control message senders. * If you want to use either the Python embedded hooks, you'll need to have a suitable version of Python installed. See doc/hook-python for more information. * Many of INN's optional features require other packages (primarily libraries) be installed. If you wish to use any of these optional features, you will need to install those packages first. Here is a table of configure options enabling optional features and the software and versions you'll need: --with-perl Perl 5.004_03 or higher, 5.8.0+ recommended --with-python Python 2.2.0 or higher, 2.5.0+ recommended (3.x versions currently not supported) --with-bdb Berkeley DB 4.4 or higher, 4.7+ recommended --with-zlib zlib 1.x or higher --with-openssl OpenSSL 0.9.6 or higher --with-sasl Cyrus SASL 2.x or higher --with-krb5 MIT Kerberos v5 1.2.x or higher If any of these libraries (other than Perl or Python) are built shared and installed in locations where your system doesn't search for shared libraries by default, you may need to encode the paths to those shared libraries in the INN binaries. For more information on shared library paths, see . For most systems, setting the environment variable LD_RUN_PATH to a colon-separated list of additional directories in which to look for shared libraries before building INN will be sufficient. Unpacking the Distribution Released versions of INN are available from . New major releases will be announced on (see README) when they're made. If you want a more cutting-edge version, you can obtain current snapshots from . These are snapshots of the INN Subversion tree taken daily; there are two snapshots made each night (one of the current development branch, and one of the stable branch consisting of bug fixes to the previous major release). They are stored in date format; in other words the snapshots from April 6th, 2000, would be named inn-CURRENT-20000406.tar.gz and inn-STABLE-20000406.tar.gz. Choose the newest file of whichever branch you prefer. (Note that the downloading, configuring, and compiling steps can be done while logged in as any user.) The distribution is in gzip compressed tar archive format. To extract it, execute: gunzip -c | tar -xf - Extracting the source distribution will create a directory named inn- or inn-- where the source resides. Installing INN Before beginning installation, you should make sure that there is a user on your system named "news", and that this user's primary group is set to a group called "news". You can change these with the --with-news-user and --with-news-group options to configure (see below). The home directory of this user should be set to the directory under which you wish to install INN (/usr/local/news is the default and is a good choice). This location will be set as *pathnews* in inn.conf. INN will install itself as this user and group. You can change these if you want, but these are the defaults and it's easier to stick with them on a new installation. By default, INN sends reports to the user "usenet". This account is used for instance by controlchan (notifying any changes as for newsgroups) or news.daily (sending Usenet daily reports). You can change it with the --with-news-master option to configure (see below). There is also the *mailto* keyword which can be given to news.daily in order to modify the mail address to which these reports are sent. WARNING: By default, INN installs various configuration files as group-writeable, and in general INN is not hardened from a security standpoint against an attack by someone who is already in the "news" group. In general, you should consider membership in the news group as equivalent to access to the news account. You should not rely on being able to keep anyone with access to the news GID from converting that into access to the news UID. The recommended configuration is to have the only member of the group "news" be the user "news". Installing INN so that all of its files are under a single directory tree, rather than scattering binaries, libraries, and man pages throughout the file system, is strongly recommended. It helps keep everything involved in the operation of INN together as a unit and will make the installation instructions easier to follow. As a side note, whenever doing anything with a running news server, first log in as this user. That way, you can ensure that all files created by any commands you run are created with the right ownership to be readable by the server. Particularly avoid doing anything in the news spool itself as root, and make sure you fix the ownership of any created files if you have to. INN doesn't like files in the news spool owned by a user other than the news user. However, since certain binaries need to be setuid root, indiscriminate use of "chown news" is not the solution. (If you don't like to log in to system accounts, careful use of "chmod g+s" on directories and a umask of 002 or 007 may suffice.) INN uses GNU autoconf and a generated configure script to make configuration rather painless. Unless you have a rather abnormal setup, configure should be able to completely configure INN for your system. If you want to change the defaults, you can invoke the configure script with one or more command line options. Type: ./configure --help for a full list of supported options. Some of the most commonly used options are: --prefix=PATH Sets the installation prefix for INN. The default is /usr/local/news. All of INN's programs and support files will be installed under this directory. This should match the home directory of your news user (it will make installation and maintenance easier). It is not recommended to set this to /usr; if you decide to do that anyway, make sure to point INN's temporary directory at a directory that isn't world-writeable (see --with-tmp-dir below). --with-db-dir=PATH Sets the prefix for INN database files. The default is PREFIX/db, where PREFIX is /usr/local/news unless overridden with the option above. The history and active files will be stored in this directory, and writes to those files are an appreciable percentage of INN's disk activity. The history file can also be quite large (requiring up to 2 GB or more during nightly expire), so this is a common portion of INN to move to a different file system. --with-spool-dir=PATH Sets the prefix for the news spool (when using any storage method other than CNFS) and the overview spool. The default is PREFIX/spool. This is another common portion of INN to move to a different file system (often /news). --with-tmp-dir=PATH Sets the directory in which INN will create temporary files. This should under no circumstances be the same as the system temporary directory or otherwise be set to a world-writeable directory, since INN doesn't take care to avoid symlink attacks and other security problems possible with a world-writeable directory. This directory should be reserved for the exclusive use of INN and only writeable by the news user. Usage is generally light, so this is unlikely to need a separate partition. It's also possible to set the paths for most other sections of the INN installation independently; see "./configure --help" and look for the --with-*-dir=PATH options. --enable-reduced-depends Requests that library probes assume shared libraries are in use and dependencies of libraries should not be probed. It therefore tries to minimize the shared library dependencies of the resulting binaries on platforms with proper shared library dependencies. This is not enabled by default, and is of interest primarily to people building packages for distributions. --enable-largefiles Enables large file support. This is not enabled by default, even on platforms that support it, because it changes the format of INN's on-disk databases (making it difficult to upgrade an earlier INN installation) and can significantly increase the size of some of the history database files. Large file support is not necessary unless your history database is so large that it exceeds 2 GB or you want to use CNFS buffers larger than 2 GB on 32-bit platforms (and some very rare 64-bit platforms that aren't Linux). The history, tradindexed and buffindexed overview, and timecaf databases written by an INN built with this option are incompatible with those written by an INN without this option. (CNFS buffers are compatible.) --enable-tagged-hash Use tagged hash table for the history database. The tagged hash format uses much less memory but is somewhat slower. This option is recommended if you have less than 256 MB of RAM on your news server. If you install INN without tagged hash (the default) and expire takes an excessive amount of time, you should make sure the RAM in your system satisfies the following formula: ram > 10 * tablesize ram: Amount of system RAM (in bytes) tablesize: 3rd field on the 1st line of history.dir (bytes) If you don't have at least that much RAM, try rebuilding INN with tagged hash enabled. NOTE: --enable-largefiles cannot be used with --enable-tagged-hash. --with-perl Enables support for embedded Perl, allowing you to install filter scripts written in Perl. Highly recommended, because many really good spam filters are written in Perl. See doc/hook-perl for all the details. Even if you do not use this option, INN still requires Perl as mentioned above. --with-python Enables support for Python, allowing you to install filter and authentication scripts written in Python. You will need Python 2.2.0 or later installed on your system to enable this option. See doc/hook-python for all the details. Please note that Python 3.x is currently not supported. --with-innd-port=PORT By default, innbind(8) refuses to bind to any port under 1024 other than 119, 433 and 563 for security reasons (to prevent attacks on rsh(1)-based commands and replacing standard system daemons). If you want to run innd on a different port under 1024, you'll need to tell configure what port you intend to use. (You'll also still need to set the port number in inn.conf or give it to innd on the command line.) --with-syslog-facility=FACILITY Specifies the syslog facility that INN programs should log to. The default is LOG_NEWS unless configure detects that your system doesn't understand that facility, in which case it uses LOG_LOCAL1. This flag overrides the automatic detection. Be sure to specify a facility not used by anything else on your system (one of LOG_LOCAL0 through LOG_LOCAL7, for example). --enable-uucp-rnews If this option is given to configure, rnews will be installed setuid "news", owned by group "uucp", and mode 4550. This will allow the UUCP subsystem to run rnews to process UUCP batches of news articles. Prior to INN 2.3, installing rnews setuid "news" was standard; since most sites no longer use UUCP, it is no longer the default as of INN 2.3 and must be requested at configure time. You probably don't want to use this option unless your server accepts UUCP news batches. --enable-setgid-inews If this option is given to configure, inews will be installed setgid "news" and world-executable so that non-privileged users on the news server machine can use inews to post articles locally (somewhat faster than opening a new network connection). For standalone news servers, by far the most common configuration now, there's no need to use this option; even if you have regular login accounts on your news server, INN's inews can post fine via a network connection to your running news server and doesn't need to use the local socket (which is what setgid enables it to do). Installing inews setgid was the default prior to INN 2.3. --with-bdb=PATH Enables support for Berkeley DB (4.4 or higher), which means that it will then be possible to use the ovdb overview method if you wish. Enabling this configure option doesn't mean you'll be required to use ovdb, but it does require that Berkeley DB be installed on your system (including the header files, not just the runtime libraries). If a path is given, it sets the installed directory of Berkeley DB (configure will search for it in standard locations, but if you have it installed elsewhere, you may need this option). This directory is expected to have subdirectories include and lib (lib32 and lib64 are also checked), containing respectively db.h, and the library itself. In case non-standard paths to the Berkeley DB libraries are used, one or both of the options --with-bdb-include and --with-bdb-lib can be given to configure with a path. If the Berkeley DB library is found at configure time, INN will be built with Berkeley DB support unless the --without-bdb flag is explicitly passed to configure. --with-zlib=PATH The ovdb storage method can optionally use compression. If --with-bdb is set, and configure finds a suitable Berkeley DB version, configure will by default also try to find the zlib library. INN will then be built with zlib support unless the --without-zlib flag is explicitly passed to configure. In case non-standard paths to the zlib library are used, one or both of the options --with-zlib-include and --with-zlib-lib can be given to configure with a path. --with-openssl=PATH Enables support for TLS/SSL for news reading, which means it will be possible to have TLS encrypted NNTP connections between your server and newsreaders. This option requires OpenSSL be installed on your system (including the header files, not just the runtime libraries). If a path is given, it sets the installed directory of OpenSSL. After compiling and installing INN with this option, you'll still need to make a certificate and private key to use TLS. See below for details on how to do that. In case non-standard paths to the OpenSSL libraries are used, one or both of the options --with-openssl-include and --with-openssl-lib can be given to configure with a path. If the OpenSSL SSL and crypto libraries are found at configure time, INN will be built with TLS/SSL support unless the --without-openssl flag is explicitly passed to configure. For the most common installation, a standalone news server, a suggested set of options is: ./configure --with-perl provided that you have the necessary version of Perl installed. (Compiling with an embedded Perl interpreter will allow you to use one of the available excellent spam filters if you so choose.) If the configure program runs successfully, then you are ready to build the distribution. From the root of the INN source tree, type: make At this point you can step away from the computer for a little while and have a quick snack while INN compiles. On a decently fast system it should only take five or ten minutes at the most to build. Once the build has completed successfully, you are ready to install INN into its final home. Type: make install Under most circumstances, you will need to run the "make install" command as root, so that INN can create the directories it needs, change ownerships (if you did not compile as the news user) and install a setuid program needed to raise resource limits and allow innd to bind to ports under 1024. This step will install INN under the install directory (/usr/local/news, known as *pathnews* in inn.conf, unless you specified something else to the configure script). Exceptionally, if you are installing INN into a directory writable by the news user, you can run "make install" as the configured news user. The resulting INN installation won't be able to use ports below 1024 (including the default NNTP port of 119) unless you then run "make install-root" as root, which will install only the setuid helper program. If you are building INN on a different system than the one on which it will eventually run, and you therefore want INN to install its files in a virtual root directory and not into the actual filesystem of the build machine, you can pass the virtual root directory to the install step via the DESTDIR variable. You then just need to type: make DESTDIR=/tmp/inn-root install In the above example, all of INN's directories and files would be installed under the /tmp/inn-root directory instead of the normal / (root) directory. Just replace "/tmp/inn-root" with whatever directory you wish to install INN in (the result will be in /tmp/inn-root/usr/local/news if the default value of /usr/local/news for *pathnews* has not been changed). This won't affect any of the paths compiled into INN; it's used primarily in some situations like building a software distribution, where software has to be installed into some file system that will later be mounted as / on the final system. Using DESTDIR permits to build INN, install it into a virtual root directory, and package the resulting files in a single step, without manual intervention, by a non-root user. It is possible to build and install INN in this way, but you must set a DESTDIR value for the install step, and additionally, you must tell the installation process that it should omit executing "chown" and "chgrp" commands (which will fail if the installation step is being executed by a non-root user). To do this, type: CHOWNPROG=set CHGRPPROG=set make DESTDIR=/tmp/inn-root install WARNING: if you install INN in this manner, none of the resulting directories and files in the virtual root directory will be owned by the correct users and groups. It is YOUR responsibility to ensure the resulting directories and files are packaged with the users, groups, and modes which INN wanted them to be installed with! Please note that INN's shared library interface is not stable and may change drastically in future releases. For this reason, it's also not properly versioned and won't be until some degree of stability is guaranteed, and the relevant header files are not installed. Only INN should use INN's shared libraries, and you should only use the shared libraries corresponding to the version of INN that you're installing. Also, when updating an existing version of INN, INN tries to save backup copies of all files but man pages and shared libraries so that you can revert to the previous installed version. Shared libraries are not backed up because this confuses ldconfig on some systems (such as Linux) and the symbolic links for the libraries may point to the ".OLD" versions. Nonetheless, reverting to a previous version of INN should work fine for official releases (that is to say releases that are not daily STABLE or CURRENT snapshots) because a proper versioning is done, if needed, during the release of a new version. If you are configuring TLS/SSL support for newsreaders, you must make a certificate and private key at least once. Type: make cert as root in order to do this. You can also type "make DESTDIR=/tmp/inn-root cert" as either root (preferred) or a non-root user. All of the warnings and caveats mentioned above apply. You are now ready for the really fun part: configuring your copy of INN! Choosing an Article Storage Format The first thing to decide is how INN should store articles on your system. There are four different methods for you to choose from, each of which has its own advantages and disadvantages. INN can support all four at the same time, so you can store certain newsgroups in one method and other newsgroups in another method. The supported storage formats are: tradspool This is the storage method used by all versions of INN previous to 2.0. Articles are stored as individual text files whose names are the same as the article number. The articles are divided up into directories based on the newsgroup name. For example, article 12345 in news.software.nntp would be stored as news/software/nntp/12345 relative to the root of the article spool. Advantages: Widely used and well-understood storage mechanism, can read article spools written by older versions of INN, compatible with all third-party INN add-ons, provides easy and direct access to the articles stored on your server and makes writing programs that fiddle with the news spool very easy, and gives you fine control over article retention times. Disadvantages: Takes a very fast file system and I/O system to keep up with current Usenet traffic volumes due to file system overhead. Groups with heavy traffic tend to create a bottleneck because of inefficiencies in storing large numbers of article files in a single directory. Requires a nightly expire program to delete old articles out of the news spool, a process that can slow down the server for several hours or more. timehash Articles are stored as individual files as in tradspool, but are divided into directories based on the arrival time to ensure that no single directory contains so many files as to cause a bottleneck. Advantages: Heavy traffic groups do not cause bottlenecks, and fine control of article retention time is still possible. Disadvantages: The ability to easily find all articles in a given newsgroup and manually fiddle with the article spool is lost, and INN still suffers from speed degradation due to file system overhead (creating and deleting individual files is a slow operation). timecaf Similar to timehash, articles are stored by arrival time, but instead of writing a separate file for each article, multiple articles are put in the same file. Advantages: Roughly four times faster than timehash for article writes, since much of the file system overhead is bypassed, while still retaining the same fine control over article retention time. Disadvantages: Even worse than timehash, and similar to CNFS (below), using this method means giving up all but the most careful manually fiddling with your article spool. As one of the newer and least widely used storage types, timecaf has not been as thoroughly tested as the other methods. cnfs CNFS stores articles sequentially in pre-configured buffer files. When the end of the buffer is reached, new articles are stored from the beginning of the buffer, overwriting older articles. Advantages: Blazingly fast because no file creations or deletions are necessary to store an article. Unlike all other storage methods, does not require manual article expiration; old articles are deleted to make room for new ones when the buffers get too full. Also, with CNFS your server will never throttle itself due to a full spool disk, and groups are restricted to just the buffer files you give them so that they can never use more than the amount of disk space you allocate to them. Disadvantages: Article retention times are more difficult to control because old articles are overwritten automatically. Attacks on Usenet, such as flooding or massive amounts of spam, can result in wanted articles expiring much faster than you intended (with no warning). Some general recommendations: If you are installing a transit news server (one that just accepts news and sends it out again to other servers and doesn't support any readers), use CNFS exclusively and don't worry about any of the other storage methods. Otherwise, put high-volume groups and groups whose articles you don't need to keep around very long (binaries groups, *.jobs*, news.lists.filters, etc.) in CNFS buffers, and use timehash, timecaf, or tradspool (if you have a fast I/O subsystem or need to be able to go through the spool manually) for everything else. You'll probably find it most convenient to keep special hierarchies like local hierarchies and hierarchies that should never expire in tradspool. Choosing an Overview Storage Mechanism If your news server will be supporting readers, you'll also need to choose an overview storage mechanism (by setting *ovmethod* in inn.conf). There are three overview mechanisms to choose from: tradindexed It is very fast for readers, but it has to update two files for each incoming article and can be quite slow to write. buffindexed It can keep up with a large feed more easily, since it uses large buffers to store all overview information, but it's somewhat slower for readers (although not as slow as the unified overview in INN 2.2). You will need to create the buffers for it to use (very similar to creating CNFS buffers) and list the available buffers in buffindexed.conf. See buffindexed.conf(5) for more information. ovdb It stores overview data in a Berkeley DB database; it's fast and very robust, but may require more disk space. See the ovdb(5) man page for more information on it. Configuring INN All documentation from this point on assumes that you have set up the news user on your system as suggested in "Installing INN" so that the root of your INN installation is ~news (*pathnews* in inn.conf). If you've moved things around by using options with "configure", you'll need to adjust the instructions to account for that. All of INN's configuration files are located in *pathetc in inn.conf*. Unless noted otherwise, any files referred to below are in this directory. When you first install INN, a sample of each file (containing lots of comments) is installed in *pathetc*; refer to these for concrete examples of everything discussed in this section. All of INN's configuration files, all of the programs that come with it, and some of its library routines have documentation in the form of man pages. These man pages were installed in *pathnews*/share/man as part of the INN installation process and are the most complete reference to how INN works. You're strongly encouraged to refer to the man pages frequently while configuring INN, and for quick reference afterwards. Any detailed questions about individual configuration files or the behavior of specific programs should be answered in them. You may want to add *pathnews*/share/man to your MANPATH environment variable; otherwise, you may have to use a command like: man -M /share/man inn.conf to see the inn.conf(5) man page (for example). Before we begin, it is worth mentioning the wildmat pattern matching syntax used in many configuration files. These are simple wildcard matches using the asterisk ("*") as the wildcard character, much like the simple wildcard expansion used by Unix shells. In many cases, wildmat patterns can be specified in a comma-separated list to indicate a list of newsgroups. When used in this fashion, each pattern is checked in turn to see if it matches, and the last pattern in the line that matches the group name is used. Patterns beginning with "!" mean to exclude groups matching that pattern. For example: *,!comp.*,comp.os.* In this case, we're saying we match everything ("*"), except that we don't match anything under comp ("!comp.*"), unless it is actually under the comp.os hierarchy ("comp.os.*"). This is because non-comp groups will match only the first pattern (so we want them), comp.os groups will match all three patterns (so we want them too, because the third pattern counts in this case), and all other comp groups will match the first and second patterns and will be excluded by the second pattern. Some uses of wildmat patterns also support "poison" patterns (patterns starting with "@"). These patterns behave just like "!" patterns when checked against a single newsgroup name. Where they become special is for articles crossposted to multiple newsgroups; normally, such an article will be considered to match a pattern if any of the newsgroups it is posted to matches the pattern. If any newsgroup the article is posted to matches an expression beginning with "@", however, that article will not match the pattern even if other newsgroups to which it was posted match other expressions. For instance, if an article is crossposted between misc.foo and misc.bar, the pattern: misc.*,!misc.bar will match that article whereas the pattern: misc.*,@misc.bar will not match that article. An article posted only to misc.bar will fail to match either pattern. See uwildmat(3) for full details on wildmat patterns. In all INN configuration files, blank lines and lines beginning with a "#" symbol are considered comments and are ignored. Be careful, not all files permit comments to begin in the middle of the line. inn.conf The first, and most important file is inn.conf. This file is organized as a series of parameter-value pairs, one per line. The parameter is first, followed by a colon and one or more whitespace characters, and then the value itself. For some parameters the value is a string or a number; for others it is true or false. (True values can be written as "yes", "true", or "on", whichever you prefer. Similarly, false values can be written as "no", "false", or "off".) inn.conf contains dozens of changeable parameters (see inn.conf(5) for full details), but only a few really need to be edited during normal operation: allownewnews If set to true then INN will support the NEWNEWS command for news readers. While this can be an expensive operation, its speed has been improved considerably as of INN 2.3 and it's probably safe to turn on without risking excessive server load. The default is true. (Note that the *access* setting in readers.conf overrides this value; see readers.conf(5) for more details.) complaints Used to set the value of the mail-complaints-to attribute of the Injection-Info: header, which is added to all articles posted locally. The usual value would be something like "abuse@example.com" or "postmaster@example.com". If not specified, the newsmaster e-mail address will be used. hiscachesize The amount of memory (in kilobytes) to allocate for a cache of recently used history file entries. Setting this to 0 disables history caching. History caching can greatly increase the number of articles per second that your server is capable of processing. A value of 256 is a good default choice. logipaddr If set to true (the default), INN will log the IP address (or hostname, if the host is listed in incoming.conf with a hostname) of the remote host from which it received an article. If set to false, the trailing Path: header entry is logged instead. If you are using controlchan (see below) and need to process ihave/sendme control messages (this is very, very unlikely, so if you don't know what this means, don't worry about it), make sure you set this to false, since controlchan needs a site name, not an IP address. organization Set this to the name of your organization as you want it to appear in the Organization: header of all articles posted locally and not already containing that header. This will be overridden by the value of the ORGANIZATION environment variable (if it exists). If neither this parameter nor the environment variable is set, no Organization: header will be added to posts which lack one. pathhost This is the name of your news server as you wish it to appear in the Path: header of all postings which travel through your server (this includes local posts and incoming posts that you forward out to other sites). If this parameter is unspecified, the fully-qualified domain name (FQDN) of the machine will be used instead. Please use the FQDN of your server or an alias for your server unless you have a very good reason not to; a future version of the news RFCs may require this. rlimitnofile If set to a non-negative value (the default is -1), INN (both innd and innfeed) will try to raise the maximum number of open file descriptors to this value when it starts. This may be needed if you have lots of incoming and outgoing feeds. Note that the maximum value for this setting is very operating-system-dependent, and you may have to reconfigure your system (possibly even recompile your kernel) to increase it. See "File Descriptor Limits" for complete details. There are tons of other possible settings; you may want to read through inn.conf(5) to get a feel for your options. Don't worry if you don't understand the purpose of most of them right now. Some of the settings are only needed for very obscure things, and with more experience running your news server the rest will make more sense. newsfeeds newsfeeds determines how incoming articles are redistributed to your peers and to other INN processes. newsfeeds is very versatile and contains dozens of options; we will touch on just the basics here. The man page contains more detailed information. newsfeeds is organized as a series of feed entries. Each feed entry is composed of four fields separated by colons. Entries may span multiple lines by using a backslash ("\") to indicate that the next line is a continuation of the current line. (Note that comments don't interact with backslashes in the way you might expect. A commented-out line ending in a backslash will still be considered continued on the next line, possibly resulting in more commented out than you intended or bizarre syntax errors. In general, it's best to avoid commenting out lines in the middle of continuation lines.) The first field in an entry is the name of the feed. It must be unique, and for feeds to other news servers it is usually set to the actual hostname of the remote server (this makes things easier). The name can optionally be followed by a slash ("/") and a comma-separated exclude list. If the feed name or any of the names in the exclude list appear in the Path: header of an article, then that article will not be forwarded to the feed as it is assumed that it has passed through that site once already. The exclude list is useful when a news server's hostname is not the same as what it puts in the Path: header of its articles, or when you don't want a feed to receive articles from a certain source. The second field specifies a set of desired newsgroups and distribution lists, given as newsgroup-pattern/distribution-list. The distribution list is not described here; see newsfeeds(5) for information (it's not used that frequently in practice). The newsgroup pattern is a wildmat-style pattern list as described above (supporting "@"). The third field is a comma-separated list of flags that determine both the type of feed entry and sets certain parameters for the entry. See newsfeeds(5) for information on the flag settings; you can do a surprising amount with them. The three most common patterns, and the ones mainly used for outgoing news feeds to other sites, are "Tf,Wnm" (to write out a batch file of articles to be sent, suitable for processing by nntpsend and innxmit), "Tm" (to send the article to a funnel feed, used with innfeed), and "Tc,Wnm*" (to collect a funnel feed and send it via a channel feed to an external program, used to send articles to innfeed). The fourth field is a multi-purpose parameter whose meaning depends on the settings of the flags in the third field. To get a feel for it using the examples above, for file feeds ("Tf") it's the name of the file to write, for funnel feeds ("Tm") it's the name of the feed entry to funnel into, and for channel feeds ("Tc") it's the name of the program to run and feed references to articles. Now that you have a rough idea of the file layout, we'll begin to add the actual feed entries. First, we'll set up the special "ME" entry. This entry is required and serves two purposes: the newsgroup pattern specified here is prepended to the newsgroup list of all other feeds, and the distribution pattern for this entry determines what distributions (from the Distribution: header of incoming articles) are accepted from remote sites by your server. The example in the sample newsfeeds file is a good starting point. If you are going to create a local hierarchy that should not be distributed off of your system, it may be useful to exclude it from the default subscription pattern, but default subscription patterns are somewhat difficult to use right so you may want to just exclude it specifically from every feed instead. The "ME" entry tends to confuse a lot of people, so this point is worth repeating: the newsgroup patterns set the default subscription for *outgoing* feeds, and the distribution patterns set the acceptable Distribution: header entries for *incoming* articles. This is confusing enough that it may change in later versions of INN. There are two basic ways to feed articles to remote sites. The most common for large sites and particularly for transit news servers is innfeed(8), which sends articles to remote sites in real time (the article goes out to all peers that are supposed to receive it immediately after your server accepts it). For smaller sites, particularly sites where the only outgoing messages will be locally posted articles, it's more common to batch outgoing articles and send them every ten minutes or so from cron using nntpsend(8) and innxmit(8). Batching gives you more control and tends to be extremely stable and reliable, but it's much slower and can't handle high volume very well. Batching outgoing posts is easy to set up; for each peer, add an entry to newsfeeds that looks like: remote.example.com/news.example.com\ :\ :Tf,Wnm: where is the wildmat pattern for the newsgroups that site wants. In this example, the actual name of the remote site is "remote.example.com", but it puts "news.example.com" in the Path: header. If the remote site puts its actual hostname in the Path: header, you won't need the "/news.example.com" part. This entry will cause innd to write out a file in *pathspool*/outgoing named remote.example.com and containing the Message-ID and storage token of each message to send to that site. (The storage token is INN's internal pointer to where an article is stored; to retrieve an article given its storage token, use sm(8)). innxmit knows how to read files of this format and send those articles to the remote site. For information on setting it up to run periodically, see "Setting Up the Cron Jobs" below. You will also need to set up a config file for nntpsend; see the man page for nntpsend.ctl(5) for more information. If instead you want to use innfeed to send outgoing messages (recommended for sites with more than a couple of peers), you need some slightly more complex magic. You still set up a separate entry for each of your peers, but rather than writing out batch files, they all "funnel" into a special innfeed entry. That special entry collects all of the separate funnel feeds and sends the data through a special sort of feed to an external program (innfeed in this case); this is a "channel" feed. First, the special channel feed entry for innfeed that will collect all the funnel feeds: innfeed!\ :!*\ :Tc,Wnm*:/innfeed -y (adjust the path to innfeed(8) if you installed it elsewhere). Note that we don't feed this entry any articles directly (its newsgroup pattern is "!*"). Note also that the name of this entry ends in an exclamation point. This is a standard convention for all special feeds; since the delimiter for the Path: header is "!", no site name containing that character can ever match the name of a real site. Next, set up entries for each remote site to which you will be feeding articles. All of these entries should be of the form: remote.example.com/news.example.com\ :\ :Tm:innfeed! specifying that they funnel into the "innfeed!" feed. As in the previous example for batching, "remote.example.com" is the actual name of the remote peer, "news.example.com" is what it puts in the Path: header (if different than the actual name of the server), and is the wildmat pattern of newsgroups to be sent. As an alternative to NNTP, INN may also feed news out to an IMAP server, by using imapfeed(8), which is almost identical to innfeed. The feed entry for this is as follows: imapfeed!\ :!*\ :Tc,Wnm*,S16384:/imapfeed And set up entries for each remote site like: remote.example.com/news.example.com\ :\ :Tm:imapfeed! For more information on imapfeed, look at the innfeed/imap_connection.c file. For more information on IMAP in general, see RFC 3501. Finally, there is a special entry for controlchan(8), which processes newsgroup control messages, that should always be in newsfeeds unless you never want to honor any control messages. This entry should look like: controlchan!\ :!*,control,control.*,!control.cancel\ :AC,Tc,Wnsm:/controlchan (modified for the actual path to controlchan if you put it somewhere else). See "Processing Newsgroup Control Messages" for more details. For those of you upgrading from earlier versions of INN, note that the functionality of overchan(8) and crosspost is now incorporated into INN and neither of those programs is necessary. Although crosspost is no longer shipped with INN (and will not be working if used), you can still use overchan if you make sure to set *useoverchan* to true in inn.conf so that innd doesn't write overview data itself, but be careful: innd may accept articles faster than overchan can process the data. incoming.conf incoming.conf file specifies which machines are permitted to connect to your host and feed it articles. Remote servers you peer with should be listed here. Connections from hosts not listed in this file will (if you don't allow readers) be rejected or (if you allow readers) be handed off to nnrpd and checked against the access restrictions in readers.conf. Start with the sample incoming.conf and, for each remote peer, add an entry like: peer remote.example.com { } This uses the default parameters for that feed and allows incoming connections from a machine named "remote.example.com". If that peer could be connecting from several different machines, instead use an entry like: peer remote.example.com { hostname: "remote.example.com, news.example.com" } This will allow either "remote.example.com" or "news.example.com" to feed articles to you. (In general, you should add new peer lines for each separate remote site you peer with, and list multiple host names using the *hostname* key if one particular remote site uses multiple servers.) You can restrict the newsgroups a remote site is allowed to send you, using the same sort of pattern that newsfeeds(5) uses. For example, if you want to prevent "example.com" hosts from sending you any articles in the "local.*" hierarchy (even if they're crossposted to other groups), change the above to: peer remote.example.com { patterns: "*, @local.*" hostname: "remote.example.com, news.example.com" } Note, however, that restricting what a remote side can send you will *not* reduce your incoming bandwidth usage. The remote site will still send you the entire article; INN will just reject it rather than saving it to disk. To reduce bandwidth, you have to contact your peers and ask them not to send you the traffic you don't want. There are various other things you can set, including the maximum number of connections the remote host will be allowed. See incoming.conf(5) for all the details. Note for those familiar with older versions of INN: this file replaces the old hosts.nntp configuration file. cycbuff.conf cycbuff.conf is only required if CNFS is used. If you aren't using CNFS, skip this section. CNFS stores articles in logical objects called *metacycbuffs*. Each metacycbuff is in turn composed of one or more physical buffers called *cycbuffs*. As articles are written to the metacycbuff, each article is written to the next cycbuff in the list in a round-robin fashion (unless "sequential" mode is specified, in which case each cycbuff is filled before moving on to the next). This is so that you can distribute the individual cycbuffs across multiple physical disks and balance the load between them. There are two ways to create your cycbuffs: 1. Use a block device directly. This will probably give you the most speed since it avoids the file system overhead of large files, but it requires your OS support mmap(2) on a block device. Solaris supports this, as do late Linux 2.4 kernels. FreeBSD does not at last report. Also on many PC-based Unixes it is difficult to create more than eight partitions, which may limit your options. 2. Use a real file on a filesystem. This will probably be a bit slower than using a block device directly, but it should work on any Unix system. If you're having doubts, use option #2; it's easier to set up and should work regardless of your operating system. More information about the creation of these cycbuffs can be found in cycbuff.conf(5) man page. Now you need to decide on the sizes of your cycbuffs and metacycbuffs. You'll probably want to separate the heavy-traffic groups ("alt.binaries.*" and maybe a few other things like "*.jobs*" and "news.lists.filters") into their own metacycbuff so that they don't overrun the server and push out articles on the more useful groups. If you have any local groups that you want to stay around for a while then you should put them in their own metacycbuff as well, so that they don't get pushed out by other traffic. (Or you might store them in one of the other storage methods, such as tradspool.) For each metacycbuff, you now need to determine how many cycbuffs will make up the metacycbuff, the size of those cycbuffs, and where they will be stored. Also, when laying out your cycbuffs, you will want to try to arrange them across as many physical disks as possible (or use a striped disk array and put them all on that). In order to use any cycbuff larger than 2 GB on 32-bit platforms (and some very rare 64-bit platforms that aren't Linux), you need to build INN with the --enable-largefiles option. See "Installing INN" for more information and some caveats. For each cycbuff you will be creating, add a line to cycbuff.conf like the following: cycbuff:NAME:/path/to/buffer:SIZE NAME must be unique and must be at most seven characters long. Something simple like "BUFF00", "BUFF01", etc. is a decent choice, or you may want to use something that includes the SCSI target and slice number of the partition. SIZE is the buffer size in kilobytes (if you're trying to stay under 2 GB, keep your sizes below 2097152). Now, you need to tell INN how to group your cycbuffs into metacycbuffs. This is similar to creating cycbuff entries: metacycbuff:BUFFNAME:CYCBUF1,CYCBUF2,CYCBUF3 BUFFNAME is the name of the metacycbuff and must be unique and at most eight characters long. These should be a bit more meaningful than the cycbuff names since they will be used in other config files as well. Try to name them after what will be stored in them; for example, if this metacycbuff will hold alt.binaries postings, "BINARIES" would be a good choice. The last part of the entry is a comma-separated list of all of the cycbuffs that should be used to build this metacycbuff. Each cycbuff should only appear in one metacycbuff line, and all metacycbuff lines must occur after all cycbuff lines in the file. If you want INN to fill each cycbuff before moving on to the next one rather than writing to them round-robin, add ":SEQUENTIAL" to the end of the metacycbuff line. This may give noticeably better performance when using multiple cycbuffs on the same spindle (such as partitions or slices of a larger disk), but will probably give worse performance if your cycbuffs are spread out across a lot of spindles. By default, CNFS data is flushed to disk every 25 articles. If you're running a small server with a light article load, this could mean losing quite a few articles in a crash. You can change this interval by adding a cycbuffupdate: line to your cycbuff.conf file; see cycbuff.conf(5) for more details. Finally, you have to create the cycbuffs. See "Creating the Article Spool (CNFS only)" for more information on how to do that. storage.conf storage.conf determines where incoming articles will be stored (what storage method, and in the case of CNFS, what metacycbuff). Each entry in the file defines a storage class for articles. The first matching storage class is used to store the article; if no storage class matches, INN will reject that article. (This is almost never what you want, so make sure this file ends in a catch-all entry that will match everything.) A storage class definition looks like this: method { newsgroups: class: size: [,] expires: [,] options: exactmatch: } is the name of the storage method to use to store articles in this class ("cnfs", "timehash", "timecaf", "tradspool", or the special method "trash" that accepts the article and throws it away). The first parameter is a wildmat pattern in the same format used by the newsfeeds(5) file, and determines what newsgroups are accepted by this storage class. The second parameter is a unique number identifying this storage class and should be between 0 and 255. It can be used to control article expiration, and for timehash and timecaf will set the top-level directory in which articles accepted by this storage class are stored. The easiest way to deal with this parameter is to just number all storage classes in storage.conf sequentially. The assignment of a particular number to a storage class is arbitrary but *permanent* (since it is used in storage tokens). The third parameter can be used to accept only articles in a certain size range into this storage class. A of 0 (or a missing ) means no upper limit (and of course a of 0 would mean no lower limit, because all articles are more than zero bytes long). If you don't want to limit the size of articles accepted by this storage class, leave this parameter out entirely. The fourth parameter you probably don't want to use lets you assign storage classes based on the Expires: header of incoming articles. The exact details are in storage.conf(5). It's very easy to use this parameter incorrectly; leave it out entirely unless you've read the man page and know what you're doing. The fifth parameter is the options parameter. Currently only CNFS uses this field; it should contain the name of the metacycbuff used to store articles in this storage class. The sixth parameter is a boolean which is false by default: any non-zero number of matching newsgroups is sufficient, provided no newsgroup matches a poison wildmat. If it is set to true, all the newsgroups in the Newsgroups: header of incoming articles will be examined to see if they match the newsgroups wildmat pattern. If you're using CNFS exclusively, just create one storage class for each metacycbuff that you have defined in cycbuff.conf and set the newsgroups pattern according to what newsgroups should be stored in that buffer. If you're using timehash or timecaf, the storage class IDs are used to store articles in separate directory trees, which you can take advantage of to put particular storage classes on different disks. Also, currently storage class is the only way to specify expiration time, so you will need to divide up your newsgroups based on how long you want to retain articles in those groups and create a storage class for each such collection of newsgroups. Make note of the storage class IDs you assign as they will be needed when you edit expire.ctl a bit later. expire.ctl expire.ctl sets the expiration policy for articles stored on the server. Be careful, since the default configuration will expire most articles after 15 days; in most circumstances this deletion is *permanent*, so read this whole section carefully if you want to keep local hierarchies forever. (See archive(8) for a way to automate backups of important articles.) Only one entry is required for all storage classes; it looks like: /remember/:11 This entry says how long to keep the message-IDs for articles that have already expired in the history file so that the server doesn't accept them again. Occasionally, fairly old articles will get regurgitated somewhere and offered to you again, so even after you've expired articles from your spool, you want to keep them around in your history file for a little while to ensure you don't get duplicates. INN will reject any articles more than a certain number of days old (the *artcutoff* parameter in inn.conf, defaulting to 10); the number on the "/remember/" line should be one more than that number in order to take into account articles whose posting date is one day into the future. CNFS makes no further use of expire.ctl, since articles stored in CNFS buffers expire automatically when the buffer runs out of free space (but see the "-N" option in expireover(8) if you really want to expire them earlier). For other storage methods, there are two different syntaxes of this file, depending on *groupbaseexpiry* in inn.conf. If it is set to false, expire.ctl takes entries of the form: ::: is the number assigned to a storage class in storage.conf. is the number of days to keep normal articles in that storage class (decimal values are allowed). For articles that don't have an Expires: header, those are the only two values that matter. For articles with an Expires: header, the other two values come into play; the date given in the Expires: header of an article will be honored, subject to the constraints set by and . All articles in this storage class will be kept for at least days, regardless of their Expires: headers, and all articles in this storage class will be expired after days, even if their Expires: headers specify a longer life. All three of these fields can also contain the special keyword "never". If is "never", only articles with explicit Expires: headers will ever be expired. If is "never", articles with explicit Expires: headers will be kept forever. Setting to "never" says to honor Expires: headers even if they specify dates far into the future. (Note that if is set to "never", all articles with Expires: headers are kept forever and the value of is not used.) If the value of *groupbaseexpiry* is true, expire.ctl takes entries of the form: :::: is a wildmat expression ("!" and "@" not permitted, and only a single expression, not a comma-separated set of them). Each expiration line applies to groups matching the wildmat expression. is "M" for moderated groups, "U" for unmoderated groups, and "A" for groups with any moderation status; the line only matches groups with the indicated expiration status. All of the other fields have the same meaning as above. readers.conf Provided that *noreader* is set to false in inn.conf, any connection from a host that doesn't match an entry in incoming.conf (as well as any connection from a host that does match such an entry, but has issued a MODE READER command) will be handed off to nnrpd(8), the part of INN that supports newsreading clients. nnrpd uses readers.conf to determine whether a given connection is allowed to read news, and if so what newsgroups the client can read and post to. There are a variety of fairly complicated things that one can do with readers.conf, things like run external authentication programs that can query RADIUS servers. See readers.conf(5) and the example file for all the gory details. Here's an example of probably the simplest reasonable configuration, one that only allows clients in the "example.com" domain to read from the server and allows any host in that domain to read and post to all groups: auth "example.com" { hosts: "example.com, *.example.com" default: "" default-domain: "example.com" } access "all" { users: "*@example.com" newsgroups: "*" } If you're running a server for one particular domain, want to allow all hosts within that domain to read and post to any group on the server, and want to deny access to anyone outside that domain, just use the above and change "example.com" in the above to your domain and you're all set. Lots of examples of more complicated things are in the sample file. Creating the Article Spool (CNFS only) If you are using actual files as your CNFS buffers, you will need to pre-create those files, ensuring they're the right size. The easiest way to do this is with dd. For each cycbuff in cycbuff.conf, create the buffer with the following commands (as the news user): dd if=/dev/zero of=/path/to/buffer bs=1k count=BUFFERSIZE chmod 664 /path/to/buffer Substitute the correct path to the buffer and the size of the buffer as specified in cycbuff.conf. This will create a zero-filled file of the correct size; it may take a while, so be prepared to wait. Here's a command that will print out the dd(1) commands that you should run: awk -F: \ '/^cy/ { printf "dd if=/dev/zero of=%s bs=1k count=%s\n", $3, $4 }' \ /cycbuff.conf If you are using block devices, you don't technically have to do anything at all (since INN is capable of using the devices in /dev), but you probably want to create special device files for those devices somewhere for INN's private use. It is more convenient to keep all of INN's stuff together, but more importantly, the device files used by INN really should be owned by the news user and group, and you may not want to do that with the files in /dev. To create the device files for INN, use mknod(8) with a type of "b", getting the major and minor device numbers from the existing devices in /dev. There's a small shell script in cycbuff.conf(5) that may help with this. Make sure to create the device files in the location INN expects them (specified in cycbuff.conf). Solaris users please note: on Solaris, do not use block devices that include the first cylinder of the disk. Solaris doesn't protect the superblock from being overwritten by an application writing to block devices and includes it in the first cylinder of the disk, so unless you use a slice that starts with cylinder 1 instead of 0, INN will invalidate the partition table when it tries to initialize the cycbuff and all further accesses will fail until you repartition. Creating the Database Files At this point, you need to set up the news database directory (in *pathdb*). This directory will hold the active(5) file (the list of newsgroups you carry), the active.times(5) file (the creator and creation time of newsgroups created since the server was initialized), the newsgroups(5) file (descriptions for all the newsgroups you carry), and the history(5) file (a record of every article the server currently has or has seen in the past few days, used to decide whether to accept or refuse new incoming messages). Before starting to work on this, make sure you're logged on as the news user, since all of these files need to be owned by that user. This is a good policy to always follow; if you are doing any maintenance work on your news server, log on as the news user. Don't do maintenance work as root. Also make sure that *pathbin* is in the default path of the news user (and while you're at it, make sure *pathnews*/share/man is in the default MANPATH) so that you can run INN maintenance commands without having to type the full path. If you already have a server set up (if you're upgrading, or setting up a new server based on an existing server), copy active and newsgroups from that server into *pathdb*. Otherwise, you'll need to figure out what newsgroups you want to carry and create new active and newsgroups files for them. If you plan to carry a full feed, or something close to that, go to and download active and newsgroups from there; that will start you off with reasonably complete files. If you plan to only carry a small set of groups, the default minimal active file installed by INN is a good place to start; you can create additional groups after the server is running by using "ctlinnd newgroup". (Another option is to use actsync(8) to synchronize your newsgroup list to that of another server.) "control", "control.cancel" and "junk" must exist as newsgroups in your active file for INN to start, and creating pseudogroups for the major types of control messages is strongly encouraged for all servers that aren't standalone. If you don't want these groups to be visible to clients, do *not* delete them; simply hide them in readers.conf. "to" must also exist as a newsgroup if you have *mergetogroups* set in inn.conf. Next, you need to create an empty history database. To do this, type: cd touch history makedbz -i -o (Note that if you install INN with "make install", you do not need to run these commands: the installation takes care of creating the history database.) When it finishes, make sure the file permissions are correct on all the files you've just created: chmod 644 * Your news database files are now ready to go. Configuring syslog While some logs are handled internally, INN also logs a wide variety of information via syslog. INN's nightly report programs know how to roll and summarize those syslog log files, but when you first install INN you need to set them up. If your system understands the "news" syslog facility, INN will use it; otherwise, it will log to "local1". Nearly every modern system has a "news" syslog facility so you can safely assume that yours does, but if in doubt take a look at the output from running "configure". You should see a line that looks like: checking log level for news... LOG_NEWS If that says LOG_LOCAL1 instead, change the below instructions to use "local1" instead of "news". Edit /etc/syslog.conf on your system and add lines that look like the following: news.crit /news.crit news.err /news.err news.notice /news.notice (Change the path names as necessary in order to match *pathlog*, which defaults to /usr/local/news/log.) These lines *must* be tab-delimited, so don't copy and paste from these instructions. Type it in by hand and make sure you use a tab, or you'll get mysterious failures. You'll also want to make sure that news log messages don't fill your other log files (INN generates a lot of log traffic); so for every entry in /etc/syslog.conf that starts with "*", add ";news.none" to the end of the first column. For example, if you have a line like: *.err /dev/console change it to: *.err;news.none /dev/console (You can choose not to do this for the higher priority log messages, if you want to make sure they go to your normal high-priority log files as well as INN's. Don't bother with anything lower priority than "crit", though. news.err isn't interesting enough to want to see all the time.) Now, make sure that the news log files exist; syslog generally won't create files automatically. Enter the following commands: touch /news.crit touch /news.err touch /news.notice chown news /news.* chgrp news /news.* (again adjusting the paths if necessary for your installation). Finally, send a HUP signal to syslogd to make it re-read its configuration file. Setting Up the Cron Jobs INN requires a special cron job to be set up on your system to run news.daily(8) which performs daily server maintenance tasks such as article expiration and the processing and rotation of the server logs. Since it will slow the server down while it is running, it should be run during periods of low server usage, such as in the middle of the night. To run it at 3am, for example, add the following entry to the news user's crontab file: 0 3 * * * /news.daily expireover lowmark or, if your system does not have per-user crontabs, put the following line into your system crontab instead: 0 3 * * * su news -s /bin/sh -c '/news.daily expireover lowmark' If you're using any non-CNFS storage methods, add "delayrm" to the above option list for news.daily. The news user obviously must be able to run cron jobs. On Solaris, this means that it must have a valid /etc/shadow entry and must not be locked (although it may be a non-login account). There may be similar restrictions with other operating systems. If you use the batching method to send news, also set up a cron job to run nntpsend(8) every ten minutes. nntpsend will run innxmit for all non-empty pending batch files to send pending news to your peers. That cron entry should look something like: 0,10,20,30,40,50 * * * * /nntpsend The pathnames and user ID used above are the installation defaults; change them to match your installation if you used something other than the defaults. The parameters passed to news.daily in the above example are the most common (and usually the most efficient) ones to use. More information on what these parameters do can be found in the news.daily(8) man page. You may also want to run rnews each hour to process spooled messages created while innd is not available: 12 * * * * /rnews -U And it is also a good practice to refresh each day INN's cached IP addresses: 30 2 * * * /ctlinnd -t 120 -s reload incoming.conf 'flush cache' File Descriptor Limits INN likes to use a lot of file descriptors, particularly if you have a lot of peers. Depending on what your system defaults are, you may need to make sure the default limit is increased for INN (particularly for innd and innfeed). This is vital on Solaris, which defaults (at least as of 2.6) to an absurdly low limit of 64 file descriptors per process. One way to increase the number of file descriptors is to set *rlimitnofile* in inn.conf to a higher value. This will cause innd and innfeed to try to increase the file descriptor limits when they start. Note, however, that INN won't be able to increase the limits above the hard limits set by your operating system; on some systems, that hard limit is normally 256 file descriptors (Linux, for example). On others, like Solaris, it's 1024. Increasing the limit beyond that value may require serious system configuration work. (On some operating systems, it requires patching and recompiling the kernel. On Solaris it can be changed in /etc/system, but for 2.6 or earlier the limit cannot be increased beyond 1024 without breaking select(2) and thereby breaking all of INN. For current versions of Linux, you may be able to change the maximum by writing to /proc/sys/fs/file-max.) 256 file descriptors will probably be enough for all but the largest sites. There is no harm in setting the limits higher than you actually need (provided they're set to something lower than or equal to your system hard limit). 256 is therefore a reasonable value to try. If you're installing INN on a Solaris system, particularly if you're installing it on a dedicated news server machine, it may be easier to just increase the default file descriptor limit across the board for all processes. You can do that by putting the line: set rlim_fd_cur = 256 in /etc/system and rebooting. You can increase it all the way to 1024 (and may need to if you have a particularly large site), but that can cause RPC and some stdio applications to break. It therefore probably isn't a good idea on a machine that isn't dedicated to INN. Starting and Stopping the System INN is started via the shell script rc.news. This must be run as the news user and not as root. To start INN on system boot, you therefore want to put something like: su news -s /bin/sh -c /rc.news in the system boot scripts. If innd is stopped or killed, you can restart it by running rc.news by hand as the news user. The rc.news script may also be used to shut down INN, with the "stop" option: su news -s /bin/sh -c '/rc.news stop' In the contrib directory of this source tree, two sample init scripts are provided: one for people using System V-style init.d directories, and another for people using systemd-style init scripts. If you wish to use TLS/SSL for your readers, you need to start a second nnrpd to listen to these connections to NNTPS port 563 and put something like that in your init scripts: su news -s /bin/sh -c '/nnrpd -D -c /readers-ssl.conf -p 563 -S' where readers-ssl.conf is the file which indicates whether a given connection is allowed to read and post news (you can also use the previously created readers.conf file to handle TLS/SSL connections). Note that a news client which supports the STARTTLS command can also use the conventional NNTP port 119 to initiate a TLS connection. However, as such clients are not widespread yet, using the separate port 563 is still common practice (though discouraged). See nnrpd(8) for more information about TLS support. In the shutdown section of the init script, you can put: start-stop-daemon --stop --name nnrpd --quiet --oknodo or if you do not have a start-stop-daemon utility: su news -s /bin/sh -c 'killall nnrpd' Processing Newsgroup Control Messages Control messages are specially-formatted messages that tell news servers to take various actions. Cancels (commands to delete messages) are handled internally by INN, and all other control messages are processed by controlchan, a channel feed program which should be run out of newsfeeds if you want your news server to process any control messages; see "Configuring INN" for specific instructions. Note that *pgpverify* must also be set to true in inn.conf. The actions of controlchan are determined by control.ctl, which lists who can perform what actions. The primary control messages to be concerned with are "newgroup" (to create a newsgroup), "rmgroup" (to remove a newsgroup), and "checkgroups" (to compare the list of groups carried in a hierarchy to a canonical list). INN comes with a control.ctl file that processes control messages in most major public hierarchies; if you don't want to act on all those control messages, you should remove from that file all entries for hierarchies you don't want to carry. The last version of that file is available from . You can tell INN to just authenticate control messages based on the From: header of the message, but this is obviously perilous and control messages are widely forged. Many hierarchies sign all of their control messages with PGP, allowing news servers to verify their authenticity, and checking those signatures for hierarchies that use them is highly recommended. controlchan knows how to do this (using pgpverify) without additional configuration, but you do have to provide it with a public key ring containing the public keys of all of the hierarchy administrators whose control messages you want to check. INN expects the public key ring to either be in the default location for a PGP public key ring for the news user (generally *pathnews*/.gnupg for GnuPG and *pathnews*/.pgp for old PGP implementations), or in *pathetc*/pgp (/usr/local/news/etc/pgp by default). The latter is the recommended path. To add a key to that key ring, use: gpg --import --homedir=/pgp where is a file containing the hierarchy key. Change the homedir setting to point to *pathetc*/pgp if you have INN installed in a non-default location. If you're using the old-style PGP program, an equivalent command is: env PGPPATH=/pgp pgp You can safely answer "no" to questions about whether you want to sign, trust, or certify keys. And you may afterwards do: cp /pgp/pubring.gpg /pgp/trustedkeys.gpg The URLs from which you can get hierarchy keys are noted in comments in control.ctl. tries to collect the major hierarchy keys and you can just import the whole file. If you want to make sure that the keys have been correcly imported, use: gpg --list-keys If you are using GnuPG, please note that the first user ID on the key will be the one that's used by INN for verification and must match the key listed in control.ctl. If a hierarchy key has multiple user IDs, you may have to remove all the user IDs except the one that matches the control.ctl entry using "gpg --edit-key" and the "delkey" command. $Id: install.pod 9936 2015-09-02 12:35:17Z iulius $ inn-2.6.0/m4/0000755000175200017520000000000012575023701012237 5ustar iuliusiuliusinn-2.6.0/m4/openssl.m40000644000175200017520000001111312575023702014162 0ustar iuliusiuliusdnl Find the compiler and linker flags for OpenSSL. dnl $Id: openssl.m4 9570 2013-11-21 15:23:24Z iulius $ dnl dnl Finds the compiler and linker flags for linking with both the OpenSSL SSL dnl library and its crypto library. Provides the --with-openssl, dnl --with-openssl-lib, and --with-openssl-include configure options to dnl specify non-standard paths to the OpenSSL libraries. dnl dnl Provides the macro INN_LIB_OPENSSL and sets the substitution variables dnl OPENSSL_CPPFLAGS, OPENSSL_LDFLAGS, OPENSSL_LIBS, CRYPTO_CPPFLAGS, dnl CRYPTO_LDFLAGS, and CRYPTO_LIBS. Also provides INN_LIB_OPENSSL_SWITCH and dnl INN_LIB_CRYPTO_SWITCH to set CPPFLAGS, LDFLAGS, and LIBS to include the dnl SSL or crypto libraries, saving the current values first, and dnl INN_LIB_OPENSSL_RESTORE and INN_LIB_CRYPTO_RESTORE to restore those dnl settings to before the last INN_LIB_OPENSSL_SWITCH or dnl INN_LIB_CRYPTO_SWITCH. Defines HAVE_OPENSSL and sets inn_use_OPENSSL to dnl true if the library is found. dnl dnl Provides the INN_LIB_OPENSSL_OPTIONAL macro, which should be used if dnl OpenSSL support is optional. This macro will still set the substitution dnl variables and shell variables described above, but they'll be empty unless dnl OpenSSL libraries are detected. HAVE_OPENSSL will be defined only if the dnl library is found. dnl dnl Depends on INN_ENABLE_REDUCED_DEPENDS and the lib-helper.m4 framework. dnl dnl The canonical version of this file is maintained in the rra-c-util dnl package, available at . dnl dnl Written by Russ Allbery dnl Copyright 2010, 2013 dnl The Board of Trustees of the Leland Stanford Junior University dnl dnl This file is free software; the authors give unlimited permission to copy dnl and/or distribute it, with or without modifications, as long as this dnl notice is preserved. dnl Save the current CPPFLAGS, LDFLAGS, and LIBS settings and switch to dnl versions that include the SSL or crypto flags. Used as a wrapper, with dnl INN_LIB_OPENSSL_RESTORE or INN_LIB_CRYPTO_RESTORE, around tests. AC_DEFUN([INN_LIB_OPENSSL_SWITCH], [INN_LIB_HELPER_SWITCH([OPENSSL])]) AC_DEFUN([INN_LIB_CRYPTO_SWITCH], [INN_LIB_HELPER_SWITCH([CRYPTO])]) dnl Restore CPPFLAGS, LDFLAGS, and LIBS to their previous values (before dnl INN_LIB_OPENSSL_SWITCH or INN_LIB_CRYPTO_SWITCH were called). AC_DEFUN([INN_LIB_OPENSSL_RESTORE], [INN_LIB_HELPER_RESTORE([OPENSSL])]) AC_DEFUN([INN_LIB_CRYPTO_RESTORE], [INN_LIB_HELPER_RESTORE([CRYPTO])]) dnl Checks if the OpenSSL and crypto libraries are present. The single dnl argument, if "true", says to fail if the OpenSSL SSL library could not be dnl found. AC_DEFUN([_INN_LIB_OPENSSL_INTERNAL], [AC_REQUIRE([INN_ENABLE_REDUCED_DEPENDS]) INN_LIB_HELPER_PATHS([OPENSSL]) CRYPTO_CPPFLAGS="$OPENSSL_CPPFLAGS" CRYPTO_LDFLAGS="$OPENSSL_LDFLAGS" CRYPTO_LIBS= AC_SUBST([CRYPTO_CPPFLAGS]) AC_SUBST([CRYPTO_LDFLAGS]) AC_SUBST([CRYPTO_LIBS]) INN_LIB_OPENSSL_SWITCH inn_openssl_extra= LIBS= AS_IF([test x"$inn_reduced_depends" != xtrue], [AC_SEARCH_LIBS([dlopen], [dl])]) inn_openssl_extra="$LIBS" LIBS="$inn_OPENSSL_save_LIBS" AC_CHECK_LIB([crypto], [AES_cbc_encrypt], [CRYPTO_LIBS="-lcrypto $inn_openssl_extra"], [AS_IF([test x"$1" = xtrue], [AC_MSG_ERROR([cannot find usable OpenSSL crypto library])])], [$inn_openssl_extra]) AS_IF([test x"$inn_reduced_depends" = xtrue], [AC_CHECK_LIB([ssl], [SSL_library_init], [OPENSSL_LIBS=-lssl], [AS_IF([test x"$1" = xtrue], [AC_MSG_ERROR([cannot find usable OpenSSL library])])])], [AC_CHECK_LIB([ssl], [SSL_library_init], [OPENSSL_LIBS="-lssl $CRYPTO_LIBS"], [AS_IF([test x"$1" = xtrue], [AC_MSG_ERROR([cannot find usable OpenSSL library])])], [$CRYPTO_LIBS])]) INN_LIB_OPENSSL_RESTORE]) dnl The main macro for packages with mandatory OpenSSL support. AC_DEFUN([INN_LIB_OPENSSL], [INN_LIB_HELPER_VAR_INIT([OPENSSL]) INN_LIB_HELPER_WITH([openssl], [OpenSSL], [OPENSSL]) _INN_LIB_OPENSSL_INTERNAL([true]) inn_use_OPENSSL=true AC_DEFINE([HAVE_OPENSSL], 1, [Define if libssl is available.])]) dnl The main macro for packages with optional OpenSSL support. AC_DEFUN([INN_LIB_OPENSSL_OPTIONAL], [INN_LIB_HELPER_VAR_INIT([OPENSSL]) INN_LIB_HELPER_WITH_OPTIONAL([openssl], [OpenSSL], [OPENSSL]) AS_IF([test x"$inn_use_OPENSSL" != xfalse], [AS_IF([test x"$inn_use_OPENSSL" = xtrue], [_INN_LIB_OPENSSL_INTERNAL([true])], [_INN_LIB_OPENSSL_INTERNAL([false])])]) AS_IF([test x"$OPENSSL_LIBS" != x], [inn_use_OPENSSL=true AC_DEFINE([HAVE_OPENSSL], 1, [Define if libssl is available.])])]) inn-2.6.0/m4/lt~obsolete.m40000644000175200017520000001375612575023702015070 0ustar iuliusiulius# lt~obsolete.m4 -- aclocal satisfying obsolete definitions. -*-Autoconf-*- # # Copyright (C) 2004, 2005, 2007, 2009 Free Software Foundation, Inc. # Written by Scott James Remnant, 2004. # # This file is free software; the Free Software Foundation gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. # serial 5 lt~obsolete.m4 # These exist entirely to fool aclocal when bootstrapping libtool. # # In the past libtool.m4 has provided macros via AC_DEFUN (or AU_DEFUN) # which have later been changed to m4_define as they aren't part of the # exported API, or moved to Autoconf or Automake where they belong. # # The trouble is, aclocal is a bit thick. It'll see the old AC_DEFUN # in /usr/share/aclocal/libtool.m4 and remember it, then when it sees us # using a macro with the same name in our local m4/libtool.m4 it'll # pull the old libtool.m4 in (it doesn't see our shiny new m4_define # and doesn't know about Autoconf macros at all.) # # So we provide this file, which has a silly filename so it's always # included after everything else. This provides aclocal with the # AC_DEFUNs it wants, but when m4 processes it, it doesn't do anything # because those macros already exist, or will be overwritten later. # We use AC_DEFUN over AU_DEFUN for compatibility with aclocal-1.6. # # Anytime we withdraw an AC_DEFUN or AU_DEFUN, remember to add it here. # Yes, that means every name once taken will need to remain here until # we give up compatibility with versions before 1.7, at which point # we need to keep only those names which we still refer to. # This is to help aclocal find these macros, as it can't see m4_define. AC_DEFUN([LTOBSOLETE_VERSION], [m4_if([1])]) m4_ifndef([AC_LIBTOOL_LINKER_OPTION], [AC_DEFUN([AC_LIBTOOL_LINKER_OPTION])]) m4_ifndef([AC_PROG_EGREP], [AC_DEFUN([AC_PROG_EGREP])]) m4_ifndef([_LT_AC_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_AC_PROG_ECHO_BACKSLASH])]) m4_ifndef([_LT_AC_SHELL_INIT], [AC_DEFUN([_LT_AC_SHELL_INIT])]) m4_ifndef([_LT_AC_SYS_LIBPATH_AIX], [AC_DEFUN([_LT_AC_SYS_LIBPATH_AIX])]) m4_ifndef([_LT_PROG_LTMAIN], [AC_DEFUN([_LT_PROG_LTMAIN])]) m4_ifndef([_LT_AC_TAGVAR], [AC_DEFUN([_LT_AC_TAGVAR])]) m4_ifndef([AC_LTDL_ENABLE_INSTALL], [AC_DEFUN([AC_LTDL_ENABLE_INSTALL])]) m4_ifndef([AC_LTDL_PREOPEN], [AC_DEFUN([AC_LTDL_PREOPEN])]) m4_ifndef([_LT_AC_SYS_COMPILER], [AC_DEFUN([_LT_AC_SYS_COMPILER])]) m4_ifndef([_LT_AC_LOCK], [AC_DEFUN([_LT_AC_LOCK])]) m4_ifndef([AC_LIBTOOL_SYS_OLD_ARCHIVE], [AC_DEFUN([AC_LIBTOOL_SYS_OLD_ARCHIVE])]) m4_ifndef([_LT_AC_TRY_DLOPEN_SELF], [AC_DEFUN([_LT_AC_TRY_DLOPEN_SELF])]) m4_ifndef([AC_LIBTOOL_PROG_CC_C_O], [AC_DEFUN([AC_LIBTOOL_PROG_CC_C_O])]) m4_ifndef([AC_LIBTOOL_SYS_HARD_LINK_LOCKS], [AC_DEFUN([AC_LIBTOOL_SYS_HARD_LINK_LOCKS])]) m4_ifndef([AC_LIBTOOL_OBJDIR], [AC_DEFUN([AC_LIBTOOL_OBJDIR])]) m4_ifndef([AC_LTDL_OBJDIR], [AC_DEFUN([AC_LTDL_OBJDIR])]) m4_ifndef([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH], [AC_DEFUN([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH])]) m4_ifndef([AC_LIBTOOL_SYS_LIB_STRIP], [AC_DEFUN([AC_LIBTOOL_SYS_LIB_STRIP])]) m4_ifndef([AC_PATH_MAGIC], [AC_DEFUN([AC_PATH_MAGIC])]) m4_ifndef([AC_PROG_LD_GNU], [AC_DEFUN([AC_PROG_LD_GNU])]) m4_ifndef([AC_PROG_LD_RELOAD_FLAG], [AC_DEFUN([AC_PROG_LD_RELOAD_FLAG])]) m4_ifndef([AC_DEPLIBS_CHECK_METHOD], [AC_DEFUN([AC_DEPLIBS_CHECK_METHOD])]) m4_ifndef([AC_LIBTOOL_PROG_COMPILER_NO_RTTI], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_NO_RTTI])]) m4_ifndef([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE], [AC_DEFUN([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE])]) m4_ifndef([AC_LIBTOOL_PROG_COMPILER_PIC], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_PIC])]) m4_ifndef([AC_LIBTOOL_PROG_LD_SHLIBS], [AC_DEFUN([AC_LIBTOOL_PROG_LD_SHLIBS])]) m4_ifndef([AC_LIBTOOL_POSTDEP_PREDEP], [AC_DEFUN([AC_LIBTOOL_POSTDEP_PREDEP])]) m4_ifndef([LT_AC_PROG_EGREP], [AC_DEFUN([LT_AC_PROG_EGREP])]) m4_ifndef([LT_AC_PROG_SED], [AC_DEFUN([LT_AC_PROG_SED])]) m4_ifndef([_LT_CC_BASENAME], [AC_DEFUN([_LT_CC_BASENAME])]) m4_ifndef([_LT_COMPILER_BOILERPLATE], [AC_DEFUN([_LT_COMPILER_BOILERPLATE])]) m4_ifndef([_LT_LINKER_BOILERPLATE], [AC_DEFUN([_LT_LINKER_BOILERPLATE])]) m4_ifndef([_AC_PROG_LIBTOOL], [AC_DEFUN([_AC_PROG_LIBTOOL])]) m4_ifndef([AC_LIBTOOL_SETUP], [AC_DEFUN([AC_LIBTOOL_SETUP])]) m4_ifndef([_LT_AC_CHECK_DLFCN], [AC_DEFUN([_LT_AC_CHECK_DLFCN])]) m4_ifndef([AC_LIBTOOL_SYS_DYNAMIC_LINKER], [AC_DEFUN([AC_LIBTOOL_SYS_DYNAMIC_LINKER])]) m4_ifndef([_LT_AC_TAGCONFIG], [AC_DEFUN([_LT_AC_TAGCONFIG])]) m4_ifndef([AC_DISABLE_FAST_INSTALL], [AC_DEFUN([AC_DISABLE_FAST_INSTALL])]) m4_ifndef([_LT_AC_LANG_CXX], [AC_DEFUN([_LT_AC_LANG_CXX])]) m4_ifndef([_LT_AC_LANG_F77], [AC_DEFUN([_LT_AC_LANG_F77])]) m4_ifndef([_LT_AC_LANG_GCJ], [AC_DEFUN([_LT_AC_LANG_GCJ])]) m4_ifndef([AC_LIBTOOL_LANG_C_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_C_CONFIG])]) m4_ifndef([_LT_AC_LANG_C_CONFIG], [AC_DEFUN([_LT_AC_LANG_C_CONFIG])]) m4_ifndef([AC_LIBTOOL_LANG_CXX_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_CXX_CONFIG])]) m4_ifndef([_LT_AC_LANG_CXX_CONFIG], [AC_DEFUN([_LT_AC_LANG_CXX_CONFIG])]) m4_ifndef([AC_LIBTOOL_LANG_F77_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_F77_CONFIG])]) m4_ifndef([_LT_AC_LANG_F77_CONFIG], [AC_DEFUN([_LT_AC_LANG_F77_CONFIG])]) m4_ifndef([AC_LIBTOOL_LANG_GCJ_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_GCJ_CONFIG])]) m4_ifndef([_LT_AC_LANG_GCJ_CONFIG], [AC_DEFUN([_LT_AC_LANG_GCJ_CONFIG])]) m4_ifndef([AC_LIBTOOL_LANG_RC_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_RC_CONFIG])]) m4_ifndef([_LT_AC_LANG_RC_CONFIG], [AC_DEFUN([_LT_AC_LANG_RC_CONFIG])]) m4_ifndef([AC_LIBTOOL_CONFIG], [AC_DEFUN([AC_LIBTOOL_CONFIG])]) m4_ifndef([_LT_AC_FILE_LTDLL_C], [AC_DEFUN([_LT_AC_FILE_LTDLL_C])]) m4_ifndef([_LT_REQUIRED_DARWIN_CHECKS], [AC_DEFUN([_LT_REQUIRED_DARWIN_CHECKS])]) m4_ifndef([_LT_AC_PROG_CXXCPP], [AC_DEFUN([_LT_AC_PROG_CXXCPP])]) m4_ifndef([_LT_PREPARE_SED_QUOTE_VARS], [AC_DEFUN([_LT_PREPARE_SED_QUOTE_VARS])]) m4_ifndef([_LT_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_PROG_ECHO_BACKSLASH])]) m4_ifndef([_LT_PROG_F77], [AC_DEFUN([_LT_PROG_F77])]) m4_ifndef([_LT_PROG_FC], [AC_DEFUN([_LT_PROG_FC])]) m4_ifndef([_LT_PROG_CXX], [AC_DEFUN([_LT_PROG_CXX])]) inn-2.6.0/m4/python.m40000644000175200017520000000365012575023702014027 0ustar iuliusiuliusdnl python.m4 -- Probe for the details needed to embed Python. dnl $Id: python.m4 9901 2015-06-14 15:31:12Z iulius $ dnl dnl Defines INN_ARG_PYTHON, which sets up the --with-python command line dnl argument and also sets various flags needed for embedded Python if it is dnl requested. dnl dnl We use the distutils.sysconfig module shipped with Python 2.2.0 and later dnl to find the compiler and linker flags to use to embed Python. AC_DEFUN([INN_ARG_PYTHON], [AC_ARG_VAR([PYTHON], [Location of Python interpreter]) AC_ARG_WITH([python], [AS_HELP_STRING([--with-python], [Embedded Python module support [no]])], [AS_CASE([$withval], [yes], [DO_PYTHON=DO AC_DEFINE([DO_PYTHON], [1], [Define to compile in Python module support.])], [no], [DO_PYTHON=DONT], [AC_MSG_ERROR([invalid argument to --with-python])])], [DO_PYTHON=DONT]) AS_IF([test x"$DO_PYTHON" = xDO], [INN_PATH_PROG_ENSURE([PYTHON], [python]) AC_MSG_CHECKING([for Python linkage]) py_include=`$PYTHON -c 'import distutils.sysconfig; \ print(distutils.sysconfig.get_python_inc())'` PYTHON_CPPFLAGS="-I$py_include" py_ver=`$PYTHON -c 'import sys; print(sys.version[[:3]])'` py_libdir=`$PYTHON -c 'import distutils.sysconfig; \ print(distutils.sysconfig.get_python_lib(0, 1))'` py_linkage=`$PYTHON -c 'import distutils.sysconfig; \ print(" ".join(distutils.sysconfig.get_config_vars("LIBS", \ "LIBC", "LIBM", "LOCALMODLIBS", "BASEMODLIBS", \ "LINKFORSHARED", "LDFLAGS")))'` py_configdir=`$PYTHON -c 'import distutils.sysconfig; \ print(distutils.sysconfig.get_config_var("LIBPL"))'` PYTHON_LIBS="-L$py_configdir -lpython$py_ver $py_linkage" PYTHON_LIBS=`echo $PYTHON_LIBS | sed -e 's/[ \\t]*/ /g'` AC_MSG_RESULT([$py_libdir])], [PYTHON_CPPFLAGS= PYTHON_LIBS=]) AC_SUBST([PYTHON_CPPFLAGS]) AC_SUBST([PYTHON_LIBS])]) inn-2.6.0/m4/socket.m40000644000175200017520000001120212575023702013766 0ustar iuliusiuliusdnl Various checks for socket support and macros. dnl $Id: socket.m4 9661 2014-08-30 11:56:11Z iulius $ dnl dnl This is a collection of various Autoconf macros for checking networking dnl and socket properties. The macros provided are: dnl dnl INN_FUNC_GETADDRINFO_ADDRCONFIG dnl INN_MACRO_IN6_ARE_ADDR_EQUAL dnl INN_MACRO_SA_LEN dnl dnl They use a separate internal source macro to make the code easier to read. dnl dnl The canonical version of this file is maintained in the rra-c-util dnl package, available at . dnl dnl Copyright 2008, 2009, 2011 dnl The Board of Trustees of the Leland Stanford Junior University dnl Copyright (c) 2004, 2005, 2006, 2007, 2008, 2009 dnl by Internet Systems Consortium, Inc. ("ISC") dnl Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, dnl 2002, 2003 by The Internet Software Consortium and Rich Salz dnl dnl This code is derived from software contributed to the Internet Software dnl Consortium by Rich Salz. dnl dnl Permission to use, copy, modify, and distribute this software for any dnl purpose with or without fee is hereby granted, provided that the above dnl copyright notice and this permission notice appear in all copies. dnl dnl THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH dnl REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF dnl MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY dnl SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES dnl WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN dnl ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR dnl IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. dnl Source used by INN_FUNC_GETADDRINFO_ADDRCONFIG. AC_DEFUN([_INN_FUNC_GETADDRINFO_ADDRCONFIG_SOURCE], [[ #include #include #include #include int main(void) { struct addrinfo hints, *ai; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_ADDRCONFIG; return (getaddrinfo("localhost", NULL, &hints, &ai) != 0); } ]]) dnl Check whether the AI_ADDRCONFIG flag works properly with getaddrinfo. dnl If so, set HAVE_GETADDRINFO_ADDRCONFIG. AC_DEFUN([INN_FUNC_GETADDRINFO_ADDRCONFIG], [AC_CACHE_CHECK([for working AI_ADDRCONFIG flag], [inn_cv_func_getaddrinfo_addrconfig_works], [AC_RUN_IFELSE([AC_LANG_SOURCE([_INN_FUNC_GETADDRINFO_ADDRCONFIG_SOURCE])], [inn_cv_func_getaddrinfo_addrconfig_works=yes], [inn_cv_func_getaddrinfo_addrconfig_works=no], [inn_cv_func_getaddrinfo_addrconfig_works=no])]) AS_IF([test x"$inn_cv_func_getaddrinfo_addrconfig_works" = xyes], [AC_DEFINE([HAVE_GETADDRINFO_ADDRCONFIG], 1, [Define if the AI_ADDRCONFIG flag works with getaddrinfo.])])]) dnl Source used by INN_MACRO_IN6_ARE_ADDR_EQUAL. Test borrowed from a bug dnl report by tmoestl@gmx.net for glibc. AC_DEFUN([_INN_MACRO_IN6_ARE_ADDR_EQUAL_SOURCE], [[ #include #include #include #include int main (void) { struct in6_addr a; struct in6_addr b; inet_pton(AF_INET6, "fe80::1234:5678:abcd", &a); inet_pton(AF_INET6, "fe80::1234:5678:abcd", &b); return IN6_ARE_ADDR_EQUAL(&a, &b) ? 0 : 1; } ]]) dnl Check whether the IN6_ARE_ADDR_EQUAL macro is broken (like glibc 2.1.3) or dnl missing. AC_DEFUN([INN_MACRO_IN6_ARE_ADDR_EQUAL], [AC_CACHE_CHECK([whether IN6_ARE_ADDR_EQUAL macro is broken], [inn_cv_in6_are_addr_equal_broken], [AC_RUN_IFELSE([AC_LANG_SOURCE([_INN_MACRO_IN6_ARE_ADDR_EQUAL_SOURCE])], [inn_cv_in6_are_addr_equal_broken=no], [inn_cv_in6_are_addr_equal_broken=yes], [inn_cv_in6_are_addr_equal_broken=yes])]) AS_IF([test x"$inn_cv_in6_are_addr_equal_broken" = xyes], [AC_DEFINE([HAVE_BROKEN_IN6_ARE_ADDR_EQUAL], 1, [Define if your IN6_ARE_ADDR_EQUAL macro is broken.])])]) dnl Source used by INN_MACRO_SA_LEN. AC_DEFUN([_INN_MACRO_SA_LEN_SOURCE], [[ #include #include int main(void) { struct sockaddr sa; int x = SA_LEN(&sa); } ]]) dnl Check whether the SA_LEN macro is available. This should give the length dnl of a struct sockaddr regardless of type. AC_DEFUN([INN_MACRO_SA_LEN], [AC_CACHE_CHECK([for SA_LEN macro], [inn_cv_sa_len_macro], [AC_LINK_IFELSE([AC_LANG_SOURCE([_INN_MACRO_SA_LEN_SOURCE])], [inn_cv_sa_len_macro=yes], [inn_cv_sa_len_macro=no])]) AS_IF([test x"$inn_cv_sa_len_macro" = xyes], [AC_DEFINE([HAVE_SA_LEN], 1, [Define if defines the SA_LEN macro.])])]) inn-2.6.0/m4/aux-libs.m40000644000175200017520000000113012575023702014221 0ustar iuliusiuliusdnl aux-libs.m4 -- Search for libraries and store them a variable. dnl $Id: aux-libs.m4 6544 2003-12-26 03:23:31Z rra $ dnl dnl Search for a particular library, and if found, add that library to the dnl specified variable (the third argument) and run the commands given in the dnl fourth argument, if any. This is for libraries we don't want to pollute dnl LIBS with. Takes the same arguments as AC_SEARCH_LIBS otherwise. AC_DEFUN([INN_SEARCH_AUX_LIBS], [inn_save_LIBS=$LIBS LIBS=${$3} AC_SEARCH_LIBS($1, $2, [$3=$LIBS $4], $5, $6) LIBS=$inn_save_LIBS AC_SUBST($3)]) inn-2.6.0/m4/lib-pathname.m40000644000175200017520000000523612575023702015051 0ustar iuliusiuliusdnl Determine the library path name. dnl $Id: lib-pathname.m4 9564 2013-11-10 18:27:16Z iulius $ dnl dnl Red Hat systems and some other Linux systems use lib64 and lib32 rather dnl than just lib in some circumstances. This file provides an Autoconf dnl macro, INN_SET_LDFLAGS, which given a variable, a prefix, and an optional dnl suffix, adds -Lprefix/lib, -Lprefix/lib32, or -Lprefix/lib64 to the dnl variable depending on which directories exist and the size of a long in dnl the compilation environment. If a suffix is given, a slash and that dnl suffix will be appended, to allow for adding a subdirectory of the library dnl directory. dnl dnl This file also provides the Autoconf macro INN_SET_LIBDIR, which sets the dnl libdir variable to PREFIX/lib{,32,64} as appropriate. dnl dnl The canonical version of this file is maintained in the rra-c-util dnl package, available at . dnl dnl Written by Russ Allbery dnl Copyright 2008, 2009 dnl The Board of Trustees of the Leland Stanford Junior University dnl dnl This file is free software; the authors give unlimited permission to copy dnl and/or distribute it, with or without modifications, as long as this dnl notice is preserved. dnl Probe for the alternate library name that we should attempt on this dnl architecture, given the size of an int, and set inn_lib_arch_name to that dnl name. Separated out so that it can be AC_REQUIRE'd and not run multiple dnl times. dnl dnl There is an unfortunate abstraction violation here where we assume we know dnl the cache variable name used by Autoconf. Unfortunately, Autoconf doesn't dnl provide any other way of getting at that information in shell that I can dnl see. AC_DEFUN([_INN_LIB_ARCH_NAME], [inn_lib_arch_name=lib AC_CHECK_SIZEOF([long]) AS_IF([test "$ac_cv_sizeof_long" -eq 4 && test -d /usr/lib32], [inn_lib_arch_name=lib32], [AS_IF([test "$ac_cv_sizeof_long" -eq 8 && test -d /usr/lib64], [inn_lib_arch_name=lib64])])]) dnl Set VARIABLE to -LPREFIX/lib{,32,64} or -LPREFIX/lib{,32,64}/SUFFIX as dnl appropriate. AC_DEFUN([INN_SET_LDFLAGS], [AC_REQUIRE([_INN_LIB_ARCH_NAME]) AS_IF([test -d "$2/$inn_lib_arch_name"], [AS_IF([test x"$3" = x], [$1="[$]$1 -L$2/${inn_lib_arch_name}"], [$1="[$]$1 -L$2/${inn_lib_arch_name}/$3"])], [AS_IF([test x"$3" = x], [$1="[$]$1 -L$2/lib"], [$1="[$]$1 -L$2/lib/$3"])]) $1=`echo "[$]$1" | sed -e 's/^ *//'`]) dnl Set libdir to PREFIX/lib{,32,64} as appropriate. AC_DEFUN([INN_SET_LIBDIR], [AC_REQUIRE([_INN_LIB_ARCH_NAME]) AS_IF([test -d "$1/$inn_lib_arch_name"], [libdir="$1/${inn_lib_arch_name}"], [libdir="$1/lib"])]) inn-2.6.0/m4/ltoptions.m40000644000175200017520000003007312575023702014540 0ustar iuliusiulius# Helper functions for option handling. -*- Autoconf -*- # # Copyright (C) 2004, 2005, 2007, 2008, 2009 Free Software Foundation, # Inc. # Written by Gary V. Vaughan, 2004 # # This file is free software; the Free Software Foundation gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. # serial 7 ltoptions.m4 # This is to help aclocal find these macros, as it can't see m4_define. AC_DEFUN([LTOPTIONS_VERSION], [m4_if([1])]) # _LT_MANGLE_OPTION(MACRO-NAME, OPTION-NAME) # ------------------------------------------ m4_define([_LT_MANGLE_OPTION], [[_LT_OPTION_]m4_bpatsubst($1__$2, [[^a-zA-Z0-9_]], [_])]) # _LT_SET_OPTION(MACRO-NAME, OPTION-NAME) # --------------------------------------- # Set option OPTION-NAME for macro MACRO-NAME, and if there is a # matching handler defined, dispatch to it. Other OPTION-NAMEs are # saved as a flag. m4_define([_LT_SET_OPTION], [m4_define(_LT_MANGLE_OPTION([$1], [$2]))dnl m4_ifdef(_LT_MANGLE_DEFUN([$1], [$2]), _LT_MANGLE_DEFUN([$1], [$2]), [m4_warning([Unknown $1 option `$2'])])[]dnl ]) # _LT_IF_OPTION(MACRO-NAME, OPTION-NAME, IF-SET, [IF-NOT-SET]) # ------------------------------------------------------------ # Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. m4_define([_LT_IF_OPTION], [m4_ifdef(_LT_MANGLE_OPTION([$1], [$2]), [$3], [$4])]) # _LT_UNLESS_OPTIONS(MACRO-NAME, OPTION-LIST, IF-NOT-SET) # ------------------------------------------------------- # Execute IF-NOT-SET unless all options in OPTION-LIST for MACRO-NAME # are set. m4_define([_LT_UNLESS_OPTIONS], [m4_foreach([_LT_Option], m4_split(m4_normalize([$2])), [m4_ifdef(_LT_MANGLE_OPTION([$1], _LT_Option), [m4_define([$0_found])])])[]dnl m4_ifdef([$0_found], [m4_undefine([$0_found])], [$3 ])[]dnl ]) # _LT_SET_OPTIONS(MACRO-NAME, OPTION-LIST) # ---------------------------------------- # OPTION-LIST is a space-separated list of Libtool options associated # with MACRO-NAME. If any OPTION has a matching handler declared with # LT_OPTION_DEFINE, dispatch to that macro; otherwise complain about # the unknown option and exit. m4_defun([_LT_SET_OPTIONS], [# Set options m4_foreach([_LT_Option], m4_split(m4_normalize([$2])), [_LT_SET_OPTION([$1], _LT_Option)]) m4_if([$1],[LT_INIT],[ dnl dnl Simply set some default values (i.e off) if boolean options were not dnl specified: _LT_UNLESS_OPTIONS([LT_INIT], [dlopen], [enable_dlopen=no ]) _LT_UNLESS_OPTIONS([LT_INIT], [win32-dll], [enable_win32_dll=no ]) dnl dnl If no reference was made to various pairs of opposing options, then dnl we run the default mode handler for the pair. For example, if neither dnl `shared' nor `disable-shared' was passed, we enable building of shared dnl archives by default: _LT_UNLESS_OPTIONS([LT_INIT], [shared disable-shared], [_LT_ENABLE_SHARED]) _LT_UNLESS_OPTIONS([LT_INIT], [static disable-static], [_LT_ENABLE_STATIC]) _LT_UNLESS_OPTIONS([LT_INIT], [pic-only no-pic], [_LT_WITH_PIC]) _LT_UNLESS_OPTIONS([LT_INIT], [fast-install disable-fast-install], [_LT_ENABLE_FAST_INSTALL]) ]) ])# _LT_SET_OPTIONS ## --------------------------------- ## ## Macros to handle LT_INIT options. ## ## --------------------------------- ## # _LT_MANGLE_DEFUN(MACRO-NAME, OPTION-NAME) # ----------------------------------------- m4_define([_LT_MANGLE_DEFUN], [[_LT_OPTION_DEFUN_]m4_bpatsubst(m4_toupper([$1__$2]), [[^A-Z0-9_]], [_])]) # LT_OPTION_DEFINE(MACRO-NAME, OPTION-NAME, CODE) # ----------------------------------------------- m4_define([LT_OPTION_DEFINE], [m4_define(_LT_MANGLE_DEFUN([$1], [$2]), [$3])[]dnl ])# LT_OPTION_DEFINE # dlopen # ------ LT_OPTION_DEFINE([LT_INIT], [dlopen], [enable_dlopen=yes ]) AU_DEFUN([AC_LIBTOOL_DLOPEN], [_LT_SET_OPTION([LT_INIT], [dlopen]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the `dlopen' option into LT_INIT's first parameter.]) ]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_DLOPEN], []) # win32-dll # --------- # Declare package support for building win32 dll's. LT_OPTION_DEFINE([LT_INIT], [win32-dll], [enable_win32_dll=yes case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-cegcc*) AC_CHECK_TOOL(AS, as, false) AC_CHECK_TOOL(DLLTOOL, dlltool, false) AC_CHECK_TOOL(OBJDUMP, objdump, false) ;; esac test -z "$AS" && AS=as _LT_DECL([], [AS], [1], [Assembler program])dnl test -z "$DLLTOOL" && DLLTOOL=dlltool _LT_DECL([], [DLLTOOL], [1], [DLL creation program])dnl test -z "$OBJDUMP" && OBJDUMP=objdump _LT_DECL([], [OBJDUMP], [1], [Object dumper program])dnl ])# win32-dll AU_DEFUN([AC_LIBTOOL_WIN32_DLL], [AC_REQUIRE([AC_CANONICAL_HOST])dnl _LT_SET_OPTION([LT_INIT], [win32-dll]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the `win32-dll' option into LT_INIT's first parameter.]) ]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_WIN32_DLL], []) # _LT_ENABLE_SHARED([DEFAULT]) # ---------------------------- # implement the --enable-shared flag, and supports the `shared' and # `disable-shared' LT_INIT options. # DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'. m4_define([_LT_ENABLE_SHARED], [m4_define([_LT_ENABLE_SHARED_DEFAULT], [m4_if($1, no, no, yes)])dnl AC_ARG_ENABLE([shared], [AS_HELP_STRING([--enable-shared@<:@=PKGS@:>@], [build shared libraries @<:@default=]_LT_ENABLE_SHARED_DEFAULT[@:>@])], [p=${PACKAGE-default} case $enableval in yes) enable_shared=yes ;; no) enable_shared=no ;; *) enable_shared=no # Look at the argument we got. We use all the common list separators. lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," for pkg in $enableval; do IFS="$lt_save_ifs" if test "X$pkg" = "X$p"; then enable_shared=yes fi done IFS="$lt_save_ifs" ;; esac], [enable_shared=]_LT_ENABLE_SHARED_DEFAULT) _LT_DECL([build_libtool_libs], [enable_shared], [0], [Whether or not to build shared libraries]) ])# _LT_ENABLE_SHARED LT_OPTION_DEFINE([LT_INIT], [shared], [_LT_ENABLE_SHARED([yes])]) LT_OPTION_DEFINE([LT_INIT], [disable-shared], [_LT_ENABLE_SHARED([no])]) # Old names: AC_DEFUN([AC_ENABLE_SHARED], [_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[shared]) ]) AC_DEFUN([AC_DISABLE_SHARED], [_LT_SET_OPTION([LT_INIT], [disable-shared]) ]) AU_DEFUN([AM_ENABLE_SHARED], [AC_ENABLE_SHARED($@)]) AU_DEFUN([AM_DISABLE_SHARED], [AC_DISABLE_SHARED($@)]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AM_ENABLE_SHARED], []) dnl AC_DEFUN([AM_DISABLE_SHARED], []) # _LT_ENABLE_STATIC([DEFAULT]) # ---------------------------- # implement the --enable-static flag, and support the `static' and # `disable-static' LT_INIT options. # DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'. m4_define([_LT_ENABLE_STATIC], [m4_define([_LT_ENABLE_STATIC_DEFAULT], [m4_if($1, no, no, yes)])dnl AC_ARG_ENABLE([static], [AS_HELP_STRING([--enable-static@<:@=PKGS@:>@], [build static libraries @<:@default=]_LT_ENABLE_STATIC_DEFAULT[@:>@])], [p=${PACKAGE-default} case $enableval in yes) enable_static=yes ;; no) enable_static=no ;; *) enable_static=no # Look at the argument we got. We use all the common list separators. lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," for pkg in $enableval; do IFS="$lt_save_ifs" if test "X$pkg" = "X$p"; then enable_static=yes fi done IFS="$lt_save_ifs" ;; esac], [enable_static=]_LT_ENABLE_STATIC_DEFAULT) _LT_DECL([build_old_libs], [enable_static], [0], [Whether or not to build static libraries]) ])# _LT_ENABLE_STATIC LT_OPTION_DEFINE([LT_INIT], [static], [_LT_ENABLE_STATIC([yes])]) LT_OPTION_DEFINE([LT_INIT], [disable-static], [_LT_ENABLE_STATIC([no])]) # Old names: AC_DEFUN([AC_ENABLE_STATIC], [_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[static]) ]) AC_DEFUN([AC_DISABLE_STATIC], [_LT_SET_OPTION([LT_INIT], [disable-static]) ]) AU_DEFUN([AM_ENABLE_STATIC], [AC_ENABLE_STATIC($@)]) AU_DEFUN([AM_DISABLE_STATIC], [AC_DISABLE_STATIC($@)]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AM_ENABLE_STATIC], []) dnl AC_DEFUN([AM_DISABLE_STATIC], []) # _LT_ENABLE_FAST_INSTALL([DEFAULT]) # ---------------------------------- # implement the --enable-fast-install flag, and support the `fast-install' # and `disable-fast-install' LT_INIT options. # DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'. m4_define([_LT_ENABLE_FAST_INSTALL], [m4_define([_LT_ENABLE_FAST_INSTALL_DEFAULT], [m4_if($1, no, no, yes)])dnl AC_ARG_ENABLE([fast-install], [AS_HELP_STRING([--enable-fast-install@<:@=PKGS@:>@], [optimize for fast installation @<:@default=]_LT_ENABLE_FAST_INSTALL_DEFAULT[@:>@])], [p=${PACKAGE-default} case $enableval in yes) enable_fast_install=yes ;; no) enable_fast_install=no ;; *) enable_fast_install=no # Look at the argument we got. We use all the common list separators. lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," for pkg in $enableval; do IFS="$lt_save_ifs" if test "X$pkg" = "X$p"; then enable_fast_install=yes fi done IFS="$lt_save_ifs" ;; esac], [enable_fast_install=]_LT_ENABLE_FAST_INSTALL_DEFAULT) _LT_DECL([fast_install], [enable_fast_install], [0], [Whether or not to optimize for fast installation])dnl ])# _LT_ENABLE_FAST_INSTALL LT_OPTION_DEFINE([LT_INIT], [fast-install], [_LT_ENABLE_FAST_INSTALL([yes])]) LT_OPTION_DEFINE([LT_INIT], [disable-fast-install], [_LT_ENABLE_FAST_INSTALL([no])]) # Old names: AU_DEFUN([AC_ENABLE_FAST_INSTALL], [_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[fast-install]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the `fast-install' option into LT_INIT's first parameter.]) ]) AU_DEFUN([AC_DISABLE_FAST_INSTALL], [_LT_SET_OPTION([LT_INIT], [disable-fast-install]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the `disable-fast-install' option into LT_INIT's first parameter.]) ]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_ENABLE_FAST_INSTALL], []) dnl AC_DEFUN([AM_DISABLE_FAST_INSTALL], []) # _LT_WITH_PIC([MODE]) # -------------------- # implement the --with-pic flag, and support the `pic-only' and `no-pic' # LT_INIT options. # MODE is either `yes' or `no'. If omitted, it defaults to `both'. m4_define([_LT_WITH_PIC], [AC_ARG_WITH([pic], [AS_HELP_STRING([--with-pic@<:@=PKGS@:>@], [try to use only PIC/non-PIC objects @<:@default=use both@:>@])], [lt_p=${PACKAGE-default} case $withval in yes|no) pic_mode=$withval ;; *) pic_mode=default # Look at the argument we got. We use all the common list separators. lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," for lt_pkg in $withval; do IFS="$lt_save_ifs" if test "X$lt_pkg" = "X$lt_p"; then pic_mode=yes fi done IFS="$lt_save_ifs" ;; esac], [pic_mode=default]) test -z "$pic_mode" && pic_mode=m4_default([$1], [default]) _LT_DECL([], [pic_mode], [0], [What type of objects to build])dnl ])# _LT_WITH_PIC LT_OPTION_DEFINE([LT_INIT], [pic-only], [_LT_WITH_PIC([yes])]) LT_OPTION_DEFINE([LT_INIT], [no-pic], [_LT_WITH_PIC([no])]) # Old name: AU_DEFUN([AC_LIBTOOL_PICMODE], [_LT_SET_OPTION([LT_INIT], [pic-only]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the `pic-only' option into LT_INIT's first parameter.]) ]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_PICMODE], []) ## ----------------- ## ## LTDL_INIT Options ## ## ----------------- ## m4_define([_LTDL_MODE], []) LT_OPTION_DEFINE([LTDL_INIT], [nonrecursive], [m4_define([_LTDL_MODE], [nonrecursive])]) LT_OPTION_DEFINE([LTDL_INIT], [recursive], [m4_define([_LTDL_MODE], [recursive])]) LT_OPTION_DEFINE([LTDL_INIT], [subproject], [m4_define([_LTDL_MODE], [subproject])]) m4_define([_LTDL_TYPE], []) LT_OPTION_DEFINE([LTDL_INIT], [installable], [m4_define([_LTDL_TYPE], [installable])]) LT_OPTION_DEFINE([LTDL_INIT], [convenience], [m4_define([_LTDL_TYPE], [convenience])]) inn-2.6.0/m4/compress.m40000644000175200017520000000607312575023702014343 0ustar iuliusiuliusdnl compress.m4 -- Log compression handling. dnl $Id: compress.m4 9075 2010-06-05 08:38:06Z iulius $ dnl dnl By default, INN compresses logs with gzip, but some people may want to use dnl compress instead, and others may want to use bzip2, or even not compress dnl at all their logs. INN also needs to locate gzip regardless, since it's dnl used for compressed rnews batches, and needs to know how to uncompress .Z dnl files. dnl dnl There are two macros defined here. The first, INN_ARG_COMPRESS, sets the dnl command-line option that lets the user specify what compression program to dnl use for logs. The second, INN_PATH_COMPRESS, hunts down the appropriate dnl compression programs. dnl Choose the log compression method; the argument should not be a full path, dnl just the name of the compression type. AC_DEFUN([INN_ARG_COMPRESS], [AC_ARG_WITH([log-compress], [AS_HELP_STRING([--with-log-compress=METHOD], [Log compression method [gzip]])], LOG_COMPRESS=$with_log_compress, LOG_COMPRESS=gzip) case "$LOG_COMPRESS" in bzip2) LOG_COMPRESSEXT=".bz2" ;; cat) LOG_COMPRESSEXT="" ;; compress) LOG_COMPRESSEXT=".Z" ;; gzip) LOG_COMPRESSEXT=".gz" ;; *) AC_MSG_ERROR([unknown log compression type $LOG_COMPRESS]) ;; esac AC_SUBST([LOG_COMPRESS]) AC_SUBST([LOG_COMPRESSEXT])]) dnl Track down the appropriate compression programs. We always look for gzip dnl and compress (although we may not use the result of looking for compress dnl in the future; right now, the only user is innshellvars), and we also look dnl for some other compression program if another was requested. We also need dnl to find a program to use to uncompress .Z files, preferring gzip if found. dnl dnl Only the program for log compression has to be found; for other purposes, dnl use the bare program name if it can't be found in the path. AC_DEFUN([INN_PATH_COMPRESS], [AC_ARG_VAR([BZIP2], [Location of bzip2 program]) AC_ARG_VAR([CAT], [Location of cat program]) AC_ARG_VAR([COMPRESS], [Location of compress program]) AC_ARG_VAR([GZIP], [Location of gzip program]) AC_PATH_PROG([BZIP2], [bzip2], [bzip2]) AC_PATH_PROG([CAT], [cat], [cat]) AC_PATH_PROG([COMPRESS], [compress], [compress]) AC_PATH_PROG([GZIP], [gzip], [gzip]) case "$LOG_COMPRESS" in bzip2) if test x"$BZIP2" = xbzip2 ; then AC_MSG_ERROR([bzip2 not found but specified for log compression]) fi LOG_COMPRESS=$BZIP2 ;; cat) if test x"$CAT" = xcat ; then AC_MSG_ERROR([cat not found but specified for log compression]) fi LOG_COMPRESS=$CAT ;; compress) if test x"$COMPRESS" = xcompress ; then AC_MSG_ERROR([compress not found but specified for log compression]) fi LOG_COMPRESS=$COMPRESS ;; gzip) if test x"$GZIP" = xgzip ; then AC_MSG_ERROR([gzip not found but specified for log compression]) fi LOG_COMPRESS=$GZIP ;; *) INN_PATH_PROG_ENSURE([LOG_COMPRESS], [$LOG_COMPRESS]) ;; esac if test x"$GZIP" != xgzip || test x"$COMPRESS" = xcompress ; then UNCOMPRESS="$GZIP -d" else UNCOMPRESS="$COMPRESS -d" fi AC_SUBST([UNCOMPRESS])]) inn-2.6.0/m4/paths.m40000644000175200017520000000465512575023702013633 0ustar iuliusiuliusdnl paths.m4 -- Configure various paths used by INN. dnl $Id: paths.m4 8495 2009-05-24 12:28:19Z iulius $ dnl dnl INN has quite a few more configurable paths than autoconf supports by dnl default. The regular --*dir options are honored where appropriate, but dnl for the rest that are non-standard, add --with-*-dir options. dnl dnl Also set the output variables PATH_CONFIG and PATH_TMP with fully expanded dnl versions of tmpdir and sysconfdir, suitable for use in C code. dnl This is the generic macro for those arguments; it takes the name of the dnl directory, the path relative to $prefix if none given to configure, the dnl variable to set, and the help string. AC_DEFUN([_INN_ARG_DIR], [AC_ARG_WITH([$1-dir], [$4], [$3=$with_$1_dir], [$3=$2]) AC_SUBST($3)]) dnl And here are all the paths. AC_DEFUN([INN_ARG_PATHS], [_INN_ARG_DIR([control], ['${bindir}/control'], [CONTROLDIR], [AS_HELP_STRING([--with-control-dir=PATH], [Path for control programs [PREFIX/bin/control]])]) _INN_ARG_DIR([db], ['${prefix}/db'], [DBDIR], [AS_HELP_STRING([--with-db-dir=PATH], [Path for news database files [PREFIX/db]])]) _INN_ARG_DIR([doc], ['${prefix}/doc'], [docdir], [AS_HELP_STRING([--with-doc-dir=PATH], [Path for news documentation [PREFIX/doc]])]) _INN_ARG_DIR([filter], ['${bindir}/filter'], [FILTERDIR], [AS_HELP_STRING([--with-filter-dir=PATH], [Path for embedded filters [PREFIX/bin/filter]])]) _INN_ARG_DIR([http], ['${prefix}/http'], [HTTPDIR], [AS_HELP_STRING([--with-http-dir=PATH], [Path for web pages [PREFIX/http]])]) _INN_ARG_DIR([libperl], ['${libdir}/perl'], [LIBPERLDIR], [AS_HELP_STRING([--with-libperl-dir=PATH], [Path for Perl modules [PREFIX/lib/perl]])]) _INN_ARG_DIR([log], ['${prefix}/log'], [LOGDIR], [AS_HELP_STRING([--with-log-dir=PATH], [Path for news logs [PREFIX/log]])]) _INN_ARG_DIR([run], ['${prefix}/run'], [RUNDIR], [AS_HELP_STRING([--with-run-dir=PATH], [Path for news PID/runtime files [PREFIX/run]])]) _INN_ARG_DIR([spool], ['${prefix}/spool'], [SPOOLDIR], [AS_HELP_STRING([--with-spool-dir=PATH], [Path for news storage [PREFIX/spool]])]) _INN_ARG_DIR([tmp], ['${prefix}/tmp'], [tmpdir], [AS_HELP_STRING([--with-tmp-dir=PATH], [Path for temporary files [PREFIX/tmp]])]) dnl Some additional paths used by inn/paths.h. eval PATH_CONFIG="$sysconfdir" eval PATH_TMP="$tmpdir" AC_SUBST([PATH_CONFIG]) AC_SUBST([PATH_TMP])]) inn-2.6.0/m4/lib-depends.m40000644000175200017520000000247712575023702014702 0ustar iuliusiuliusdnl Provides option to change library probes. dnl $Id: lib-depends.m4 9564 2013-11-10 18:27:16Z iulius $ dnl dnl This file provides INN_ENABLE_REDUCED_DEPENDS, which adds the configure dnl option --enable-reduced-depends to request that library probes assume dnl shared libraries are in use and dependencies of libraries should not be dnl probed. If this option is given, the shell variable inn_reduced_depends dnl is set to true; otherwise, it is set to false. dnl dnl This macro doesn't do much but is defined separately so that other macros dnl can require it with AC_REQUIRE. dnl dnl The canonical version of this file is maintained in the rra-c-util dnl package, available at . dnl dnl Written by Russ Allbery dnl Copyright 2005, 2006, 2007 dnl The Board of Trustees of the Leland Stanford Junior University dnl dnl This file is free software; the authors give unlimited permission to copy dnl and/or distribute it, with or without modifications, as long as this dnl notice is preserved. AC_DEFUN([INN_ENABLE_REDUCED_DEPENDS], [inn_reduced_depends=false AC_ARG_ENABLE([reduced-depends], [AS_HELP_STRING([--enable-reduced-depends], [Try to minimize shared library dependencies])], [AS_IF([test x"$enableval" = xyes], [inn_reduced_depends=true])])]) inn-2.6.0/m4/libtool.m40000644000175200017520000106011112575023702014146 0ustar iuliusiulius# libtool.m4 - Configure libtool for the host system. -*-Autoconf-*- # # Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, # 2006, 2007, 2008, 2009, 2010, 2011 Free Software # Foundation, Inc. # Written by Gordon Matzigkeit, 1996 # # This file is free software; the Free Software Foundation gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. m4_define([_LT_COPYING], [dnl # Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, # 2006, 2007, 2008, 2009, 2010, 2011 Free Software # Foundation, Inc. # Written by Gordon Matzigkeit, 1996 # # This file is part of GNU Libtool. # # GNU Libtool is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 2 of # the License, or (at your option) any later version. # # As a special exception to the GNU General Public License, # if you distribute this file as part of a program or library that # is built using GNU Libtool, you may include this file under the # same distribution terms that you use for the rest of that program. # # GNU Libtool is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNU Libtool; see the file COPYING. If not, a copy # can be downloaded from http://www.gnu.org/licenses/gpl.html, or # obtained by writing to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ]) # serial 57 LT_INIT # LT_PREREQ(VERSION) # ------------------ # Complain and exit if this libtool version is less that VERSION. m4_defun([LT_PREREQ], [m4_if(m4_version_compare(m4_defn([LT_PACKAGE_VERSION]), [$1]), -1, [m4_default([$3], [m4_fatal([Libtool version $1 or higher is required], 63)])], [$2])]) # _LT_CHECK_BUILDDIR # ------------------ # Complain if the absolute build directory name contains unusual characters m4_defun([_LT_CHECK_BUILDDIR], [case `pwd` in *\ * | *\ *) AC_MSG_WARN([Libtool does not cope well with whitespace in `pwd`]) ;; esac ]) # LT_INIT([OPTIONS]) # ------------------ AC_DEFUN([LT_INIT], [AC_PREREQ([2.58])dnl We use AC_INCLUDES_DEFAULT AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl AC_BEFORE([$0], [LT_LANG])dnl AC_BEFORE([$0], [LT_OUTPUT])dnl AC_BEFORE([$0], [LTDL_INIT])dnl m4_require([_LT_CHECK_BUILDDIR])dnl dnl Autoconf doesn't catch unexpanded LT_ macros by default: m4_pattern_forbid([^_?LT_[A-Z_]+$])dnl m4_pattern_allow([^(_LT_EOF|LT_DLGLOBAL|LT_DLLAZY_OR_NOW|LT_MULTI_MODULE)$])dnl dnl aclocal doesn't pull ltoptions.m4, ltsugar.m4, or ltversion.m4 dnl unless we require an AC_DEFUNed macro: AC_REQUIRE([LTOPTIONS_VERSION])dnl AC_REQUIRE([LTSUGAR_VERSION])dnl AC_REQUIRE([LTVERSION_VERSION])dnl AC_REQUIRE([LTOBSOLETE_VERSION])dnl m4_require([_LT_PROG_LTMAIN])dnl _LT_SHELL_INIT([SHELL=${CONFIG_SHELL-/bin/sh}]) dnl Parse OPTIONS _LT_SET_OPTIONS([$0], [$1]) # This can be used to rebuild libtool when needed LIBTOOL_DEPS="$ltmain" # Always use our own libtool. LIBTOOL='$(SHELL) $(top_builddir)/libtool' AC_SUBST(LIBTOOL)dnl _LT_SETUP # Only expand once: m4_define([LT_INIT]) ])# LT_INIT # Old names: AU_ALIAS([AC_PROG_LIBTOOL], [LT_INIT]) AU_ALIAS([AM_PROG_LIBTOOL], [LT_INIT]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_PROG_LIBTOOL], []) dnl AC_DEFUN([AM_PROG_LIBTOOL], []) # _LT_CC_BASENAME(CC) # ------------------- # Calculate cc_basename. Skip known compiler wrappers and cross-prefix. m4_defun([_LT_CC_BASENAME], [for cc_temp in $1""; do case $cc_temp in compile | *[[\\/]]compile | ccache | *[[\\/]]ccache ) ;; distcc | *[[\\/]]distcc | purify | *[[\\/]]purify ) ;; \-*) ;; *) break;; esac done cc_basename=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"` ]) # _LT_FILEUTILS_DEFAULTS # ---------------------- # It is okay to use these file commands and assume they have been set # sensibly after `m4_require([_LT_FILEUTILS_DEFAULTS])'. m4_defun([_LT_FILEUTILS_DEFAULTS], [: ${CP="cp -f"} : ${MV="mv -f"} : ${RM="rm -f"} ])# _LT_FILEUTILS_DEFAULTS # _LT_SETUP # --------- m4_defun([_LT_SETUP], [AC_REQUIRE([AC_CANONICAL_HOST])dnl AC_REQUIRE([AC_CANONICAL_BUILD])dnl AC_REQUIRE([_LT_PREPARE_SED_QUOTE_VARS])dnl AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH])dnl _LT_DECL([], [PATH_SEPARATOR], [1], [The PATH separator for the build system])dnl dnl _LT_DECL([], [host_alias], [0], [The host system])dnl _LT_DECL([], [host], [0])dnl _LT_DECL([], [host_os], [0])dnl dnl _LT_DECL([], [build_alias], [0], [The build system])dnl _LT_DECL([], [build], [0])dnl _LT_DECL([], [build_os], [0])dnl dnl AC_REQUIRE([AC_PROG_CC])dnl AC_REQUIRE([LT_PATH_LD])dnl AC_REQUIRE([LT_PATH_NM])dnl dnl AC_REQUIRE([AC_PROG_LN_S])dnl test -z "$LN_S" && LN_S="ln -s" _LT_DECL([], [LN_S], [1], [Whether we need soft or hard links])dnl dnl AC_REQUIRE([LT_CMD_MAX_LEN])dnl _LT_DECL([objext], [ac_objext], [0], [Object file suffix (normally "o")])dnl _LT_DECL([], [exeext], [0], [Executable file suffix (normally "")])dnl dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_CHECK_SHELL_FEATURES])dnl m4_require([_LT_PATH_CONVERSION_FUNCTIONS])dnl m4_require([_LT_CMD_RELOAD])dnl m4_require([_LT_CHECK_MAGIC_METHOD])dnl m4_require([_LT_CHECK_SHAREDLIB_FROM_LINKLIB])dnl m4_require([_LT_CMD_OLD_ARCHIVE])dnl m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl m4_require([_LT_WITH_SYSROOT])dnl _LT_CONFIG_LIBTOOL_INIT([ # See if we are running on zsh, and set the options which allow our # commands through without removal of \ escapes INIT. if test -n "\${ZSH_VERSION+set}" ; then setopt NO_GLOB_SUBST fi ]) if test -n "${ZSH_VERSION+set}" ; then setopt NO_GLOB_SUBST fi _LT_CHECK_OBJDIR m4_require([_LT_TAG_COMPILER])dnl case $host_os in aix3*) # AIX sometimes has problems with the GCC collect2 program. For some # reason, if we set the COLLECT_NAMES environment variable, the problems # vanish in a puff of smoke. if test "X${COLLECT_NAMES+set}" != Xset; then COLLECT_NAMES= export COLLECT_NAMES fi ;; esac # Global variables: ofile=libtool can_build_shared=yes # All known linkers require a `.a' archive for static linking (except MSVC, # which needs '.lib'). libext=a with_gnu_ld="$lt_cv_prog_gnu_ld" old_CC="$CC" old_CFLAGS="$CFLAGS" # Set sane defaults for various variables test -z "$CC" && CC=cc test -z "$LTCC" && LTCC=$CC test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS test -z "$LD" && LD=ld test -z "$ac_objext" && ac_objext=o _LT_CC_BASENAME([$compiler]) # Only perform the check for file, if the check method requires it test -z "$MAGIC_CMD" && MAGIC_CMD=file case $deplibs_check_method in file_magic*) if test "$file_magic_cmd" = '$MAGIC_CMD'; then _LT_PATH_MAGIC fi ;; esac # Use C for the default configuration in the libtool script LT_SUPPORTED_TAG([CC]) _LT_LANG_C_CONFIG _LT_LANG_DEFAULT_CONFIG _LT_CONFIG_COMMANDS ])# _LT_SETUP # _LT_PREPARE_SED_QUOTE_VARS # -------------------------- # Define a few sed substitution that help us do robust quoting. m4_defun([_LT_PREPARE_SED_QUOTE_VARS], [# Backslashify metacharacters that are still active within # double-quoted strings. sed_quote_subst='s/\([["`$\\]]\)/\\\1/g' # Same as above, but do not quote variable references. double_quote_subst='s/\([["`\\]]\)/\\\1/g' # Sed substitution to delay expansion of an escaped shell variable in a # double_quote_subst'ed string. delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g' # Sed substitution to delay expansion of an escaped single quote. delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g' # Sed substitution to avoid accidental globbing in evaled expressions no_glob_subst='s/\*/\\\*/g' ]) # _LT_PROG_LTMAIN # --------------- # Note that this code is called both from `configure', and `config.status' # now that we use AC_CONFIG_COMMANDS to generate libtool. Notably, # `config.status' has no value for ac_aux_dir unless we are using Automake, # so we pass a copy along to make sure it has a sensible value anyway. m4_defun([_LT_PROG_LTMAIN], [m4_ifdef([AC_REQUIRE_AUX_FILE], [AC_REQUIRE_AUX_FILE([ltmain.sh])])dnl _LT_CONFIG_LIBTOOL_INIT([ac_aux_dir='$ac_aux_dir']) ltmain="$ac_aux_dir/ltmain.sh" ])# _LT_PROG_LTMAIN ## ------------------------------------- ## ## Accumulate code for creating libtool. ## ## ------------------------------------- ## # So that we can recreate a full libtool script including additional # tags, we accumulate the chunks of code to send to AC_CONFIG_COMMANDS # in macros and then make a single call at the end using the `libtool' # label. # _LT_CONFIG_LIBTOOL_INIT([INIT-COMMANDS]) # ---------------------------------------- # Register INIT-COMMANDS to be passed to AC_CONFIG_COMMANDS later. m4_define([_LT_CONFIG_LIBTOOL_INIT], [m4_ifval([$1], [m4_append([_LT_OUTPUT_LIBTOOL_INIT], [$1 ])])]) # Initialize. m4_define([_LT_OUTPUT_LIBTOOL_INIT]) # _LT_CONFIG_LIBTOOL([COMMANDS]) # ------------------------------ # Register COMMANDS to be passed to AC_CONFIG_COMMANDS later. m4_define([_LT_CONFIG_LIBTOOL], [m4_ifval([$1], [m4_append([_LT_OUTPUT_LIBTOOL_COMMANDS], [$1 ])])]) # Initialize. m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS]) # _LT_CONFIG_SAVE_COMMANDS([COMMANDS], [INIT_COMMANDS]) # ----------------------------------------------------- m4_defun([_LT_CONFIG_SAVE_COMMANDS], [_LT_CONFIG_LIBTOOL([$1]) _LT_CONFIG_LIBTOOL_INIT([$2]) ]) # _LT_FORMAT_COMMENT([COMMENT]) # ----------------------------- # Add leading comment marks to the start of each line, and a trailing # full-stop to the whole comment if one is not present already. m4_define([_LT_FORMAT_COMMENT], [m4_ifval([$1], [ m4_bpatsubst([m4_bpatsubst([$1], [^ *], [# ])], [['`$\]], [\\\&])]m4_bmatch([$1], [[!?.]$], [], [.]) )]) ## ------------------------ ## ## FIXME: Eliminate VARNAME ## ## ------------------------ ## # _LT_DECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION], [IS-TAGGED?]) # ------------------------------------------------------------------- # CONFIGNAME is the name given to the value in the libtool script. # VARNAME is the (base) name used in the configure script. # VALUE may be 0, 1 or 2 for a computed quote escaped value based on # VARNAME. Any other value will be used directly. m4_define([_LT_DECL], [lt_if_append_uniq([lt_decl_varnames], [$2], [, ], [lt_dict_add_subkey([lt_decl_dict], [$2], [libtool_name], [m4_ifval([$1], [$1], [$2])]) lt_dict_add_subkey([lt_decl_dict], [$2], [value], [$3]) m4_ifval([$4], [lt_dict_add_subkey([lt_decl_dict], [$2], [description], [$4])]) lt_dict_add_subkey([lt_decl_dict], [$2], [tagged?], [m4_ifval([$5], [yes], [no])])]) ]) # _LT_TAGDECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION]) # -------------------------------------------------------- m4_define([_LT_TAGDECL], [_LT_DECL([$1], [$2], [$3], [$4], [yes])]) # lt_decl_tag_varnames([SEPARATOR], [VARNAME1...]) # ------------------------------------------------ m4_define([lt_decl_tag_varnames], [_lt_decl_filter([tagged?], [yes], $@)]) # _lt_decl_filter(SUBKEY, VALUE, [SEPARATOR], [VARNAME1..]) # --------------------------------------------------------- m4_define([_lt_decl_filter], [m4_case([$#], [0], [m4_fatal([$0: too few arguments: $#])], [1], [m4_fatal([$0: too few arguments: $#: $1])], [2], [lt_dict_filter([lt_decl_dict], [$1], [$2], [], lt_decl_varnames)], [3], [lt_dict_filter([lt_decl_dict], [$1], [$2], [$3], lt_decl_varnames)], [lt_dict_filter([lt_decl_dict], $@)])[]dnl ]) # lt_decl_quote_varnames([SEPARATOR], [VARNAME1...]) # -------------------------------------------------- m4_define([lt_decl_quote_varnames], [_lt_decl_filter([value], [1], $@)]) # lt_decl_dquote_varnames([SEPARATOR], [VARNAME1...]) # --------------------------------------------------- m4_define([lt_decl_dquote_varnames], [_lt_decl_filter([value], [2], $@)]) # lt_decl_varnames_tagged([SEPARATOR], [VARNAME1...]) # --------------------------------------------------- m4_define([lt_decl_varnames_tagged], [m4_assert([$# <= 2])dnl _$0(m4_quote(m4_default([$1], [[, ]])), m4_ifval([$2], [[$2]], [m4_dquote(lt_decl_tag_varnames)]), m4_split(m4_normalize(m4_quote(_LT_TAGS)), [ ]))]) m4_define([_lt_decl_varnames_tagged], [m4_ifval([$3], [lt_combine([$1], [$2], [_], $3)])]) # lt_decl_all_varnames([SEPARATOR], [VARNAME1...]) # ------------------------------------------------ m4_define([lt_decl_all_varnames], [_$0(m4_quote(m4_default([$1], [[, ]])), m4_if([$2], [], m4_quote(lt_decl_varnames), m4_quote(m4_shift($@))))[]dnl ]) m4_define([_lt_decl_all_varnames], [lt_join($@, lt_decl_varnames_tagged([$1], lt_decl_tag_varnames([[, ]], m4_shift($@))))dnl ]) # _LT_CONFIG_STATUS_DECLARE([VARNAME]) # ------------------------------------ # Quote a variable value, and forward it to `config.status' so that its # declaration there will have the same value as in `configure'. VARNAME # must have a single quote delimited value for this to work. m4_define([_LT_CONFIG_STATUS_DECLARE], [$1='`$ECHO "$][$1" | $SED "$delay_single_quote_subst"`']) # _LT_CONFIG_STATUS_DECLARATIONS # ------------------------------ # We delimit libtool config variables with single quotes, so when # we write them to config.status, we have to be sure to quote all # embedded single quotes properly. In configure, this macro expands # each variable declared with _LT_DECL (and _LT_TAGDECL) into: # # ='`$ECHO "$" | $SED "$delay_single_quote_subst"`' m4_defun([_LT_CONFIG_STATUS_DECLARATIONS], [m4_foreach([_lt_var], m4_quote(lt_decl_all_varnames), [m4_n([_LT_CONFIG_STATUS_DECLARE(_lt_var)])])]) # _LT_LIBTOOL_TAGS # ---------------- # Output comment and list of tags supported by the script m4_defun([_LT_LIBTOOL_TAGS], [_LT_FORMAT_COMMENT([The names of the tagged configurations supported by this script])dnl available_tags="_LT_TAGS"dnl ]) # _LT_LIBTOOL_DECLARE(VARNAME, [TAG]) # ----------------------------------- # Extract the dictionary values for VARNAME (optionally with TAG) and # expand to a commented shell variable setting: # # # Some comment about what VAR is for. # visible_name=$lt_internal_name m4_define([_LT_LIBTOOL_DECLARE], [_LT_FORMAT_COMMENT(m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [description])))[]dnl m4_pushdef([_libtool_name], m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [libtool_name])))[]dnl m4_case(m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [value])), [0], [_libtool_name=[$]$1], [1], [_libtool_name=$lt_[]$1], [2], [_libtool_name=$lt_[]$1], [_libtool_name=lt_dict_fetch([lt_decl_dict], [$1], [value])])[]dnl m4_ifval([$2], [_$2])[]m4_popdef([_libtool_name])[]dnl ]) # _LT_LIBTOOL_CONFIG_VARS # ----------------------- # Produce commented declarations of non-tagged libtool config variables # suitable for insertion in the LIBTOOL CONFIG section of the `libtool' # script. Tagged libtool config variables (even for the LIBTOOL CONFIG # section) are produced by _LT_LIBTOOL_TAG_VARS. m4_defun([_LT_LIBTOOL_CONFIG_VARS], [m4_foreach([_lt_var], m4_quote(_lt_decl_filter([tagged?], [no], [], lt_decl_varnames)), [m4_n([_LT_LIBTOOL_DECLARE(_lt_var)])])]) # _LT_LIBTOOL_TAG_VARS(TAG) # ------------------------- m4_define([_LT_LIBTOOL_TAG_VARS], [m4_foreach([_lt_var], m4_quote(lt_decl_tag_varnames), [m4_n([_LT_LIBTOOL_DECLARE(_lt_var, [$1])])])]) # _LT_TAGVAR(VARNAME, [TAGNAME]) # ------------------------------ m4_define([_LT_TAGVAR], [m4_ifval([$2], [$1_$2], [$1])]) # _LT_CONFIG_COMMANDS # ------------------- # Send accumulated output to $CONFIG_STATUS. Thanks to the lists of # variables for single and double quote escaping we saved from calls # to _LT_DECL, we can put quote escaped variables declarations # into `config.status', and then the shell code to quote escape them in # for loops in `config.status'. Finally, any additional code accumulated # from calls to _LT_CONFIG_LIBTOOL_INIT is expanded. m4_defun([_LT_CONFIG_COMMANDS], [AC_PROVIDE_IFELSE([LT_OUTPUT], dnl If the libtool generation code has been placed in $CONFIG_LT, dnl instead of duplicating it all over again into config.status, dnl then we will have config.status run $CONFIG_LT later, so it dnl needs to know what name is stored there: [AC_CONFIG_COMMANDS([libtool], [$SHELL $CONFIG_LT || AS_EXIT(1)], [CONFIG_LT='$CONFIG_LT'])], dnl If the libtool generation code is destined for config.status, dnl expand the accumulated commands and init code now: [AC_CONFIG_COMMANDS([libtool], [_LT_OUTPUT_LIBTOOL_COMMANDS], [_LT_OUTPUT_LIBTOOL_COMMANDS_INIT])]) ])#_LT_CONFIG_COMMANDS # Initialize. m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS_INIT], [ # The HP-UX ksh and POSIX shell print the target directory to stdout # if CDPATH is set. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH sed_quote_subst='$sed_quote_subst' double_quote_subst='$double_quote_subst' delay_variable_subst='$delay_variable_subst' _LT_CONFIG_STATUS_DECLARATIONS LTCC='$LTCC' LTCFLAGS='$LTCFLAGS' compiler='$compiler_DEFAULT' # A function that is used when there is no print builtin or printf. func_fallback_echo () { eval 'cat <<_LTECHO_EOF \$[]1 _LTECHO_EOF' } # Quote evaled strings. for var in lt_decl_all_varnames([[ \ ]], lt_decl_quote_varnames); do case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in *[[\\\\\\\`\\"\\\$]]*) eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED \\"\\\$sed_quote_subst\\"\\\`\\\\\\"" ;; *) eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" ;; esac done # Double-quote double-evaled strings. for var in lt_decl_all_varnames([[ \ ]], lt_decl_dquote_varnames); do case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in *[[\\\\\\\`\\"\\\$]]*) eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\"" ;; *) eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" ;; esac done _LT_OUTPUT_LIBTOOL_INIT ]) # _LT_GENERATED_FILE_INIT(FILE, [COMMENT]) # ------------------------------------ # Generate a child script FILE with all initialization necessary to # reuse the environment learned by the parent script, and make the # file executable. If COMMENT is supplied, it is inserted after the # `#!' sequence but before initialization text begins. After this # macro, additional text can be appended to FILE to form the body of # the child script. The macro ends with non-zero status if the # file could not be fully written (such as if the disk is full). m4_ifdef([AS_INIT_GENERATED], [m4_defun([_LT_GENERATED_FILE_INIT],[AS_INIT_GENERATED($@)])], [m4_defun([_LT_GENERATED_FILE_INIT], [m4_require([AS_PREPARE])]dnl [m4_pushdef([AS_MESSAGE_LOG_FD])]dnl [lt_write_fail=0 cat >$1 <<_ASEOF || lt_write_fail=1 #! $SHELL # Generated by $as_me. $2 SHELL=\${CONFIG_SHELL-$SHELL} export SHELL _ASEOF cat >>$1 <<\_ASEOF || lt_write_fail=1 AS_SHELL_SANITIZE _AS_PREPARE exec AS_MESSAGE_FD>&1 _ASEOF test $lt_write_fail = 0 && chmod +x $1[]dnl m4_popdef([AS_MESSAGE_LOG_FD])])])# _LT_GENERATED_FILE_INIT # LT_OUTPUT # --------- # This macro allows early generation of the libtool script (before # AC_OUTPUT is called), incase it is used in configure for compilation # tests. AC_DEFUN([LT_OUTPUT], [: ${CONFIG_LT=./config.lt} AC_MSG_NOTICE([creating $CONFIG_LT]) _LT_GENERATED_FILE_INIT(["$CONFIG_LT"], [# Run this file to recreate a libtool stub with the current configuration.]) cat >>"$CONFIG_LT" <<\_LTEOF lt_cl_silent=false exec AS_MESSAGE_LOG_FD>>config.log { echo AS_BOX([Running $as_me.]) } >&AS_MESSAGE_LOG_FD lt_cl_help="\ \`$as_me' creates a local libtool stub from the current configuration, for use in further configure time tests before the real libtool is generated. Usage: $[0] [[OPTIONS]] -h, --help print this help, then exit -V, --version print version number, then exit -q, --quiet do not print progress messages -d, --debug don't remove temporary files Report bugs to ." lt_cl_version="\ m4_ifset([AC_PACKAGE_NAME], [AC_PACKAGE_NAME ])config.lt[]dnl m4_ifset([AC_PACKAGE_VERSION], [ AC_PACKAGE_VERSION]) configured by $[0], generated by m4_PACKAGE_STRING. Copyright (C) 2011 Free Software Foundation, Inc. This config.lt script is free software; the Free Software Foundation gives unlimited permision to copy, distribute and modify it." while test $[#] != 0 do case $[1] in --version | --v* | -V ) echo "$lt_cl_version"; exit 0 ;; --help | --h* | -h ) echo "$lt_cl_help"; exit 0 ;; --debug | --d* | -d ) debug=: ;; --quiet | --q* | --silent | --s* | -q ) lt_cl_silent=: ;; -*) AC_MSG_ERROR([unrecognized option: $[1] Try \`$[0] --help' for more information.]) ;; *) AC_MSG_ERROR([unrecognized argument: $[1] Try \`$[0] --help' for more information.]) ;; esac shift done if $lt_cl_silent; then exec AS_MESSAGE_FD>/dev/null fi _LTEOF cat >>"$CONFIG_LT" <<_LTEOF _LT_OUTPUT_LIBTOOL_COMMANDS_INIT _LTEOF cat >>"$CONFIG_LT" <<\_LTEOF AC_MSG_NOTICE([creating $ofile]) _LT_OUTPUT_LIBTOOL_COMMANDS AS_EXIT(0) _LTEOF chmod +x "$CONFIG_LT" # configure is writing to config.log, but config.lt does its own redirection, # appending to config.log, which fails on DOS, as config.log is still kept # open by configure. Here we exec the FD to /dev/null, effectively closing # config.log, so it can be properly (re)opened and appended to by config.lt. lt_cl_success=: test "$silent" = yes && lt_config_lt_args="$lt_config_lt_args --quiet" exec AS_MESSAGE_LOG_FD>/dev/null $SHELL "$CONFIG_LT" $lt_config_lt_args || lt_cl_success=false exec AS_MESSAGE_LOG_FD>>config.log $lt_cl_success || AS_EXIT(1) ])# LT_OUTPUT # _LT_CONFIG(TAG) # --------------- # If TAG is the built-in tag, create an initial libtool script with a # default configuration from the untagged config vars. Otherwise add code # to config.status for appending the configuration named by TAG from the # matching tagged config vars. m4_defun([_LT_CONFIG], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl _LT_CONFIG_SAVE_COMMANDS([ m4_define([_LT_TAG], m4_if([$1], [], [C], [$1]))dnl m4_if(_LT_TAG, [C], [ # See if we are running on zsh, and set the options which allow our # commands through without removal of \ escapes. if test -n "${ZSH_VERSION+set}" ; then setopt NO_GLOB_SUBST fi cfgfile="${ofile}T" trap "$RM \"$cfgfile\"; exit 1" 1 2 15 $RM "$cfgfile" cat <<_LT_EOF >> "$cfgfile" #! $SHELL # `$ECHO "$ofile" | sed 's%^.*/%%'` - Provide generalized library-building support services. # Generated automatically by $as_me ($PACKAGE$TIMESTAMP) $VERSION # Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`: # NOTE: Changes made to this file will be lost: look at ltmain.sh. # _LT_COPYING _LT_LIBTOOL_TAGS # ### BEGIN LIBTOOL CONFIG _LT_LIBTOOL_CONFIG_VARS _LT_LIBTOOL_TAG_VARS # ### END LIBTOOL CONFIG _LT_EOF case $host_os in aix3*) cat <<\_LT_EOF >> "$cfgfile" # AIX sometimes has problems with the GCC collect2 program. For some # reason, if we set the COLLECT_NAMES environment variable, the problems # vanish in a puff of smoke. if test "X${COLLECT_NAMES+set}" != Xset; then COLLECT_NAMES= export COLLECT_NAMES fi _LT_EOF ;; esac _LT_PROG_LTMAIN # We use sed instead of cat because bash on DJGPP gets confused if # if finds mixed CR/LF and LF-only lines. Since sed operates in # text mode, it properly converts lines to CR/LF. This bash problem # is reportedly fixed, but why not run on old versions too? sed '$q' "$ltmain" >> "$cfgfile" \ || (rm -f "$cfgfile"; exit 1) _LT_PROG_REPLACE_SHELLFNS mv -f "$cfgfile" "$ofile" || (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile") chmod +x "$ofile" ], [cat <<_LT_EOF >> "$ofile" dnl Unfortunately we have to use $1 here, since _LT_TAG is not expanded dnl in a comment (ie after a #). # ### BEGIN LIBTOOL TAG CONFIG: $1 _LT_LIBTOOL_TAG_VARS(_LT_TAG) # ### END LIBTOOL TAG CONFIG: $1 _LT_EOF ])dnl /m4_if ], [m4_if([$1], [], [ PACKAGE='$PACKAGE' VERSION='$VERSION' TIMESTAMP='$TIMESTAMP' RM='$RM' ofile='$ofile'], []) ])dnl /_LT_CONFIG_SAVE_COMMANDS ])# _LT_CONFIG # LT_SUPPORTED_TAG(TAG) # --------------------- # Trace this macro to discover what tags are supported by the libtool # --tag option, using: # autoconf --trace 'LT_SUPPORTED_TAG:$1' AC_DEFUN([LT_SUPPORTED_TAG], []) # C support is built-in for now m4_define([_LT_LANG_C_enabled], []) m4_define([_LT_TAGS], []) # LT_LANG(LANG) # ------------- # Enable libtool support for the given language if not already enabled. AC_DEFUN([LT_LANG], [AC_BEFORE([$0], [LT_OUTPUT])dnl m4_case([$1], [C], [_LT_LANG(C)], [C++], [_LT_LANG(CXX)], [Go], [_LT_LANG(GO)], [Java], [_LT_LANG(GCJ)], [Fortran 77], [_LT_LANG(F77)], [Fortran], [_LT_LANG(FC)], [Windows Resource], [_LT_LANG(RC)], [m4_ifdef([_LT_LANG_]$1[_CONFIG], [_LT_LANG($1)], [m4_fatal([$0: unsupported language: "$1"])])])dnl ])# LT_LANG # _LT_LANG(LANGNAME) # ------------------ m4_defun([_LT_LANG], [m4_ifdef([_LT_LANG_]$1[_enabled], [], [LT_SUPPORTED_TAG([$1])dnl m4_append([_LT_TAGS], [$1 ])dnl m4_define([_LT_LANG_]$1[_enabled], [])dnl _LT_LANG_$1_CONFIG($1)])dnl ])# _LT_LANG m4_ifndef([AC_PROG_GO], [ ############################################################ # NOTE: This macro has been submitted for inclusion into # # GNU Autoconf as AC_PROG_GO. When it is available in # # a released version of Autoconf we should remove this # # macro and use it instead. # ############################################################ m4_defun([AC_PROG_GO], [AC_LANG_PUSH(Go)dnl AC_ARG_VAR([GOC], [Go compiler command])dnl AC_ARG_VAR([GOFLAGS], [Go compiler flags])dnl _AC_ARG_VAR_LDFLAGS()dnl AC_CHECK_TOOL(GOC, gccgo) if test -z "$GOC"; then if test -n "$ac_tool_prefix"; then AC_CHECK_PROG(GOC, [${ac_tool_prefix}gccgo], [${ac_tool_prefix}gccgo]) fi fi if test -z "$GOC"; then AC_CHECK_PROG(GOC, gccgo, gccgo, false) fi ])#m4_defun ])#m4_ifndef # _LT_LANG_DEFAULT_CONFIG # ----------------------- m4_defun([_LT_LANG_DEFAULT_CONFIG], [AC_PROVIDE_IFELSE([AC_PROG_CXX], [LT_LANG(CXX)], [m4_define([AC_PROG_CXX], defn([AC_PROG_CXX])[LT_LANG(CXX)])]) AC_PROVIDE_IFELSE([AC_PROG_F77], [LT_LANG(F77)], [m4_define([AC_PROG_F77], defn([AC_PROG_F77])[LT_LANG(F77)])]) AC_PROVIDE_IFELSE([AC_PROG_FC], [LT_LANG(FC)], [m4_define([AC_PROG_FC], defn([AC_PROG_FC])[LT_LANG(FC)])]) dnl The call to [A][M_PROG_GCJ] is quoted like that to stop aclocal dnl pulling things in needlessly. AC_PROVIDE_IFELSE([AC_PROG_GCJ], [LT_LANG(GCJ)], [AC_PROVIDE_IFELSE([A][M_PROG_GCJ], [LT_LANG(GCJ)], [AC_PROVIDE_IFELSE([LT_PROG_GCJ], [LT_LANG(GCJ)], [m4_ifdef([AC_PROG_GCJ], [m4_define([AC_PROG_GCJ], defn([AC_PROG_GCJ])[LT_LANG(GCJ)])]) m4_ifdef([A][M_PROG_GCJ], [m4_define([A][M_PROG_GCJ], defn([A][M_PROG_GCJ])[LT_LANG(GCJ)])]) m4_ifdef([LT_PROG_GCJ], [m4_define([LT_PROG_GCJ], defn([LT_PROG_GCJ])[LT_LANG(GCJ)])])])])]) AC_PROVIDE_IFELSE([AC_PROG_GO], [LT_LANG(GO)], [m4_define([AC_PROG_GO], defn([AC_PROG_GO])[LT_LANG(GO)])]) AC_PROVIDE_IFELSE([LT_PROG_RC], [LT_LANG(RC)], [m4_define([LT_PROG_RC], defn([LT_PROG_RC])[LT_LANG(RC)])]) ])# _LT_LANG_DEFAULT_CONFIG # Obsolete macros: AU_DEFUN([AC_LIBTOOL_CXX], [LT_LANG(C++)]) AU_DEFUN([AC_LIBTOOL_F77], [LT_LANG(Fortran 77)]) AU_DEFUN([AC_LIBTOOL_FC], [LT_LANG(Fortran)]) AU_DEFUN([AC_LIBTOOL_GCJ], [LT_LANG(Java)]) AU_DEFUN([AC_LIBTOOL_RC], [LT_LANG(Windows Resource)]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_CXX], []) dnl AC_DEFUN([AC_LIBTOOL_F77], []) dnl AC_DEFUN([AC_LIBTOOL_FC], []) dnl AC_DEFUN([AC_LIBTOOL_GCJ], []) dnl AC_DEFUN([AC_LIBTOOL_RC], []) # _LT_TAG_COMPILER # ---------------- m4_defun([_LT_TAG_COMPILER], [AC_REQUIRE([AC_PROG_CC])dnl _LT_DECL([LTCC], [CC], [1], [A C compiler])dnl _LT_DECL([LTCFLAGS], [CFLAGS], [1], [LTCC compiler flags])dnl _LT_TAGDECL([CC], [compiler], [1], [A language specific compiler])dnl _LT_TAGDECL([with_gcc], [GCC], [0], [Is the compiler the GNU compiler?])dnl # If no C compiler was specified, use CC. LTCC=${LTCC-"$CC"} # If no C compiler flags were specified, use CFLAGS. LTCFLAGS=${LTCFLAGS-"$CFLAGS"} # Allow CC to be a program name with arguments. compiler=$CC ])# _LT_TAG_COMPILER # _LT_COMPILER_BOILERPLATE # ------------------------ # Check for compiler boilerplate output or warnings with # the simple compiler test code. m4_defun([_LT_COMPILER_BOILERPLATE], [m4_require([_LT_DECL_SED])dnl ac_outfile=conftest.$ac_objext echo "$lt_simple_compile_test_code" >conftest.$ac_ext eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err _lt_compiler_boilerplate=`cat conftest.err` $RM conftest* ])# _LT_COMPILER_BOILERPLATE # _LT_LINKER_BOILERPLATE # ---------------------- # Check for linker boilerplate output or warnings with # the simple link test code. m4_defun([_LT_LINKER_BOILERPLATE], [m4_require([_LT_DECL_SED])dnl ac_outfile=conftest.$ac_objext echo "$lt_simple_link_test_code" >conftest.$ac_ext eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err _lt_linker_boilerplate=`cat conftest.err` $RM -r conftest* ])# _LT_LINKER_BOILERPLATE # _LT_REQUIRED_DARWIN_CHECKS # ------------------------- m4_defun_once([_LT_REQUIRED_DARWIN_CHECKS],[ case $host_os in rhapsody* | darwin*) AC_CHECK_TOOL([DSYMUTIL], [dsymutil], [:]) AC_CHECK_TOOL([NMEDIT], [nmedit], [:]) AC_CHECK_TOOL([LIPO], [lipo], [:]) AC_CHECK_TOOL([OTOOL], [otool], [:]) AC_CHECK_TOOL([OTOOL64], [otool64], [:]) _LT_DECL([], [DSYMUTIL], [1], [Tool to manipulate archived DWARF debug symbol files on Mac OS X]) _LT_DECL([], [NMEDIT], [1], [Tool to change global to local symbols on Mac OS X]) _LT_DECL([], [LIPO], [1], [Tool to manipulate fat objects and archives on Mac OS X]) _LT_DECL([], [OTOOL], [1], [ldd/readelf like tool for Mach-O binaries on Mac OS X]) _LT_DECL([], [OTOOL64], [1], [ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4]) AC_CACHE_CHECK([for -single_module linker flag],[lt_cv_apple_cc_single_mod], [lt_cv_apple_cc_single_mod=no if test -z "${LT_MULTI_MODULE}"; then # By default we will add the -single_module flag. You can override # by either setting the environment variable LT_MULTI_MODULE # non-empty at configure time, or by adding -multi_module to the # link flags. rm -rf libconftest.dylib* echo "int foo(void){return 1;}" > conftest.c echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ -dynamiclib -Wl,-single_module conftest.c" >&AS_MESSAGE_LOG_FD $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ -dynamiclib -Wl,-single_module conftest.c 2>conftest.err _lt_result=$? # If there is a non-empty error log, and "single_module" # appears in it, assume the flag caused a linker warning if test -s conftest.err && $GREP single_module conftest.err; then cat conftest.err >&AS_MESSAGE_LOG_FD # Otherwise, if the output was created with a 0 exit code from # the compiler, it worked. elif test -f libconftest.dylib && test $_lt_result -eq 0; then lt_cv_apple_cc_single_mod=yes else cat conftest.err >&AS_MESSAGE_LOG_FD fi rm -rf libconftest.dylib* rm -f conftest.* fi]) AC_CACHE_CHECK([for -exported_symbols_list linker flag], [lt_cv_ld_exported_symbols_list], [lt_cv_ld_exported_symbols_list=no save_LDFLAGS=$LDFLAGS echo "_main" > conftest.sym LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym" AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])], [lt_cv_ld_exported_symbols_list=yes], [lt_cv_ld_exported_symbols_list=no]) LDFLAGS="$save_LDFLAGS" ]) AC_CACHE_CHECK([for -force_load linker flag],[lt_cv_ld_force_load], [lt_cv_ld_force_load=no cat > conftest.c << _LT_EOF int forced_loaded() { return 2;} _LT_EOF echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&AS_MESSAGE_LOG_FD $LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&AS_MESSAGE_LOG_FD echo "$AR cru libconftest.a conftest.o" >&AS_MESSAGE_LOG_FD $AR cru libconftest.a conftest.o 2>&AS_MESSAGE_LOG_FD echo "$RANLIB libconftest.a" >&AS_MESSAGE_LOG_FD $RANLIB libconftest.a 2>&AS_MESSAGE_LOG_FD cat > conftest.c << _LT_EOF int main() { return 0;} _LT_EOF echo "$LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a" >&AS_MESSAGE_LOG_FD $LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a 2>conftest.err _lt_result=$? if test -s conftest.err && $GREP force_load conftest.err; then cat conftest.err >&AS_MESSAGE_LOG_FD elif test -f conftest && test $_lt_result -eq 0 && $GREP forced_load conftest >/dev/null 2>&1 ; then lt_cv_ld_force_load=yes else cat conftest.err >&AS_MESSAGE_LOG_FD fi rm -f conftest.err libconftest.a conftest conftest.c rm -rf conftest.dSYM ]) case $host_os in rhapsody* | darwin1.[[012]]) _lt_dar_allow_undefined='${wl}-undefined ${wl}suppress' ;; darwin1.*) _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;; darwin*) # darwin 5.x on # if running on 10.5 or later, the deployment target defaults # to the OS version, if on x86, and 10.4, the deployment # target defaults to 10.4. Don't you love it? case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in 10.0,*86*-darwin8*|10.0,*-darwin[[91]]*) _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;; 10.[[012]]*) _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;; 10.*) _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;; esac ;; esac if test "$lt_cv_apple_cc_single_mod" = "yes"; then _lt_dar_single_mod='$single_module' fi if test "$lt_cv_ld_exported_symbols_list" = "yes"; then _lt_dar_export_syms=' ${wl}-exported_symbols_list,$output_objdir/${libname}-symbols.expsym' else _lt_dar_export_syms='~$NMEDIT -s $output_objdir/${libname}-symbols.expsym ${lib}' fi if test "$DSYMUTIL" != ":" && test "$lt_cv_ld_force_load" = "no"; then _lt_dsymutil='~$DSYMUTIL $lib || :' else _lt_dsymutil= fi ;; esac ]) # _LT_DARWIN_LINKER_FEATURES([TAG]) # --------------------------------- # Checks for linker and compiler features on darwin m4_defun([_LT_DARWIN_LINKER_FEATURES], [ m4_require([_LT_REQUIRED_DARWIN_CHECKS]) _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_automatic, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported if test "$lt_cv_ld_force_load" = "yes"; then _LT_TAGVAR(whole_archive_flag_spec, $1)='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience ${wl}-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`' m4_case([$1], [F77], [_LT_TAGVAR(compiler_needs_object, $1)=yes], [FC], [_LT_TAGVAR(compiler_needs_object, $1)=yes]) else _LT_TAGVAR(whole_archive_flag_spec, $1)='' fi _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(allow_undefined_flag, $1)="$_lt_dar_allow_undefined" case $cc_basename in ifort*) _lt_dar_can_shared=yes ;; *) _lt_dar_can_shared=$GCC ;; esac if test "$_lt_dar_can_shared" = "yes"; then output_verbose_link_cmd=func_echo_all _LT_TAGVAR(archive_cmds, $1)="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" _LT_TAGVAR(module_cmds, $1)="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" _LT_TAGVAR(module_expsym_cmds, $1)="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" m4_if([$1], [CXX], [ if test "$lt_cv_apple_cc_single_mod" != "yes"; then _LT_TAGVAR(archive_cmds, $1)="\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dsymutil}" _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dar_export_syms}${_lt_dsymutil}" fi ],[]) else _LT_TAGVAR(ld_shlibs, $1)=no fi ]) # _LT_SYS_MODULE_PATH_AIX([TAGNAME]) # ---------------------------------- # Links a minimal program and checks the executable # for the system default hardcoded library path. In most cases, # this is /usr/lib:/lib, but when the MPI compilers are used # the location of the communication and MPI libs are included too. # If we don't find anything, use the default library path according # to the aix ld manual. # Store the results from the different compilers for each TAGNAME. # Allow to override them for all tags through lt_cv_aix_libpath. m4_defun([_LT_SYS_MODULE_PATH_AIX], [m4_require([_LT_DECL_SED])dnl if test "${lt_cv_aix_libpath+set}" = set; then aix_libpath=$lt_cv_aix_libpath else AC_CACHE_VAL([_LT_TAGVAR([lt_cv_aix_libpath_], [$1])], [AC_LINK_IFELSE([AC_LANG_PROGRAM],[ lt_aix_libpath_sed='[ /Import File Strings/,/^$/ { /^0/ { s/^0 *\([^ ]*\) *$/\1/ p } }]' _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` # Check for a 64-bit object if we didn't find anything. if test -z "$_LT_TAGVAR([lt_cv_aix_libpath_], [$1])"; then _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` fi],[]) if test -z "$_LT_TAGVAR([lt_cv_aix_libpath_], [$1])"; then _LT_TAGVAR([lt_cv_aix_libpath_], [$1])="/usr/lib:/lib" fi ]) aix_libpath=$_LT_TAGVAR([lt_cv_aix_libpath_], [$1]) fi ])# _LT_SYS_MODULE_PATH_AIX # _LT_SHELL_INIT(ARG) # ------------------- m4_define([_LT_SHELL_INIT], [m4_divert_text([M4SH-INIT], [$1 ])])# _LT_SHELL_INIT # _LT_PROG_ECHO_BACKSLASH # ----------------------- # Find how we can fake an echo command that does not interpret backslash. # In particular, with Autoconf 2.60 or later we add some code to the start # of the generated configure script which will find a shell with a builtin # printf (which we can use as an echo command). m4_defun([_LT_PROG_ECHO_BACKSLASH], [ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO AC_MSG_CHECKING([how to print strings]) # Test print first, because it will be a builtin if present. if test "X`( print -r -- -n ) 2>/dev/null`" = X-n && \ test "X`print -r -- $ECHO 2>/dev/null`" = "X$ECHO"; then ECHO='print -r --' elif test "X`printf %s $ECHO 2>/dev/null`" = "X$ECHO"; then ECHO='printf %s\n' else # Use this function as a fallback that always works. func_fallback_echo () { eval 'cat <<_LTECHO_EOF $[]1 _LTECHO_EOF' } ECHO='func_fallback_echo' fi # func_echo_all arg... # Invoke $ECHO with all args, space-separated. func_echo_all () { $ECHO "$*" } case "$ECHO" in printf*) AC_MSG_RESULT([printf]) ;; print*) AC_MSG_RESULT([print -r]) ;; *) AC_MSG_RESULT([cat]) ;; esac m4_ifdef([_AS_DETECT_SUGGESTED], [_AS_DETECT_SUGGESTED([ test -n "${ZSH_VERSION+set}${BASH_VERSION+set}" || ( ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO PATH=/empty FPATH=/empty; export PATH FPATH test "X`printf %s $ECHO`" = "X$ECHO" \ || test "X`print -r -- $ECHO`" = "X$ECHO" )])]) _LT_DECL([], [SHELL], [1], [Shell to use when invoking shell scripts]) _LT_DECL([], [ECHO], [1], [An echo program that protects backslashes]) ])# _LT_PROG_ECHO_BACKSLASH # _LT_WITH_SYSROOT # ---------------- AC_DEFUN([_LT_WITH_SYSROOT], [AC_MSG_CHECKING([for sysroot]) AC_ARG_WITH([sysroot], [ --with-sysroot[=DIR] Search for dependent libraries within DIR (or the compiler's sysroot if not specified).], [], [with_sysroot=no]) dnl lt_sysroot will always be passed unquoted. We quote it here dnl in case the user passed a directory name. lt_sysroot= case ${with_sysroot} in #( yes) if test "$GCC" = yes; then lt_sysroot=`$CC --print-sysroot 2>/dev/null` fi ;; #( /*) lt_sysroot=`echo "$with_sysroot" | sed -e "$sed_quote_subst"` ;; #( no|'') ;; #( *) AC_MSG_RESULT([${with_sysroot}]) AC_MSG_ERROR([The sysroot must be an absolute path.]) ;; esac AC_MSG_RESULT([${lt_sysroot:-no}]) _LT_DECL([], [lt_sysroot], [0], [The root where to search for ]dnl [dependent libraries, and in which our libraries should be installed.])]) # _LT_ENABLE_LOCK # --------------- m4_defun([_LT_ENABLE_LOCK], [AC_ARG_ENABLE([libtool-lock], [AS_HELP_STRING([--disable-libtool-lock], [avoid locking (might break parallel builds)])]) test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes # Some flags need to be propagated to the compiler or linker for good # libtool support. case $host in ia64-*-hpux*) # Find out which ABI we are using. echo 'int i;' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then case `/usr/bin/file conftest.$ac_objext` in *ELF-32*) HPUX_IA64_MODE="32" ;; *ELF-64*) HPUX_IA64_MODE="64" ;; esac fi rm -rf conftest* ;; *-*-irix6*) # Find out which ABI we are using. echo '[#]line '$LINENO' "configure"' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then if test "$lt_cv_prog_gnu_ld" = yes; then case `/usr/bin/file conftest.$ac_objext` in *32-bit*) LD="${LD-ld} -melf32bsmip" ;; *N32*) LD="${LD-ld} -melf32bmipn32" ;; *64-bit*) LD="${LD-ld} -melf64bmip" ;; esac else case `/usr/bin/file conftest.$ac_objext` in *32-bit*) LD="${LD-ld} -32" ;; *N32*) LD="${LD-ld} -n32" ;; *64-bit*) LD="${LD-ld} -64" ;; esac fi fi rm -rf conftest* ;; x86_64-*kfreebsd*-gnu|x86_64-*linux*|powerpc*-*linux*| \ s390*-*linux*|s390*-*tpf*|sparc*-*linux*) # Find out which ABI we are using. echo 'int i;' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then case `/usr/bin/file conftest.o` in *32-bit*) case $host in x86_64-*kfreebsd*-gnu) LD="${LD-ld} -m elf_i386_fbsd" ;; x86_64-*linux*) case `/usr/bin/file conftest.o` in *x86-64*) LD="${LD-ld} -m elf32_x86_64" ;; *) LD="${LD-ld} -m elf_i386" ;; esac ;; powerpc64le-*) LD="${LD-ld} -m elf32lppclinux" ;; powerpc64-*) LD="${LD-ld} -m elf32ppclinux" ;; s390x-*linux*) LD="${LD-ld} -m elf_s390" ;; sparc64-*linux*) LD="${LD-ld} -m elf32_sparc" ;; esac ;; *64-bit*) case $host in x86_64-*kfreebsd*-gnu) LD="${LD-ld} -m elf_x86_64_fbsd" ;; x86_64-*linux*) LD="${LD-ld} -m elf_x86_64" ;; powerpcle-*) LD="${LD-ld} -m elf64lppc" ;; powerpc-*) LD="${LD-ld} -m elf64ppc" ;; s390*-*linux*|s390*-*tpf*) LD="${LD-ld} -m elf64_s390" ;; sparc*-*linux*) LD="${LD-ld} -m elf64_sparc" ;; esac ;; esac fi rm -rf conftest* ;; *-*-sco3.2v5*) # On SCO OpenServer 5, we need -belf to get full-featured binaries. SAVE_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -belf" AC_CACHE_CHECK([whether the C compiler needs -belf], lt_cv_cc_needs_belf, [AC_LANG_PUSH(C) AC_LINK_IFELSE([AC_LANG_PROGRAM([[]],[[]])],[lt_cv_cc_needs_belf=yes],[lt_cv_cc_needs_belf=no]) AC_LANG_POP]) if test x"$lt_cv_cc_needs_belf" != x"yes"; then # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf CFLAGS="$SAVE_CFLAGS" fi ;; *-*solaris*) # Find out which ABI we are using. echo 'int i;' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then case `/usr/bin/file conftest.o` in *64-bit*) case $lt_cv_prog_gnu_ld in yes*) case $host in i?86-*-solaris*) LD="${LD-ld} -m elf_x86_64" ;; sparc*-*-solaris*) LD="${LD-ld} -m elf64_sparc" ;; esac # GNU ld 2.21 introduced _sol2 emulations. Use them if available. if ${LD-ld} -V | grep _sol2 >/dev/null 2>&1; then LD="${LD-ld}_sol2" fi ;; *) if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then LD="${LD-ld} -64" fi ;; esac ;; esac fi rm -rf conftest* ;; esac need_locks="$enable_libtool_lock" ])# _LT_ENABLE_LOCK # _LT_PROG_AR # ----------- m4_defun([_LT_PROG_AR], [AC_CHECK_TOOLS(AR, [ar], false) : ${AR=ar} : ${AR_FLAGS=cru} _LT_DECL([], [AR], [1], [The archiver]) _LT_DECL([], [AR_FLAGS], [1], [Flags to create an archive]) AC_CACHE_CHECK([for archiver @FILE support], [lt_cv_ar_at_file], [lt_cv_ar_at_file=no AC_COMPILE_IFELSE([AC_LANG_PROGRAM], [echo conftest.$ac_objext > conftest.lst lt_ar_try='$AR $AR_FLAGS libconftest.a @conftest.lst >&AS_MESSAGE_LOG_FD' AC_TRY_EVAL([lt_ar_try]) if test "$ac_status" -eq 0; then # Ensure the archiver fails upon bogus file names. rm -f conftest.$ac_objext libconftest.a AC_TRY_EVAL([lt_ar_try]) if test "$ac_status" -ne 0; then lt_cv_ar_at_file=@ fi fi rm -f conftest.* libconftest.a ]) ]) if test "x$lt_cv_ar_at_file" = xno; then archiver_list_spec= else archiver_list_spec=$lt_cv_ar_at_file fi _LT_DECL([], [archiver_list_spec], [1], [How to feed a file listing to the archiver]) ])# _LT_PROG_AR # _LT_CMD_OLD_ARCHIVE # ------------------- m4_defun([_LT_CMD_OLD_ARCHIVE], [_LT_PROG_AR AC_CHECK_TOOL(STRIP, strip, :) test -z "$STRIP" && STRIP=: _LT_DECL([], [STRIP], [1], [A symbol stripping program]) AC_CHECK_TOOL(RANLIB, ranlib, :) test -z "$RANLIB" && RANLIB=: _LT_DECL([], [RANLIB], [1], [Commands used to install an old-style archive]) # Determine commands to create old-style static archives. old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs' old_postinstall_cmds='chmod 644 $oldlib' old_postuninstall_cmds= if test -n "$RANLIB"; then case $host_os in openbsd*) old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$tool_oldlib" ;; *) old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$tool_oldlib" ;; esac old_archive_cmds="$old_archive_cmds~\$RANLIB \$tool_oldlib" fi case $host_os in darwin*) lock_old_archive_extraction=yes ;; *) lock_old_archive_extraction=no ;; esac _LT_DECL([], [old_postinstall_cmds], [2]) _LT_DECL([], [old_postuninstall_cmds], [2]) _LT_TAGDECL([], [old_archive_cmds], [2], [Commands used to build an old-style archive]) _LT_DECL([], [lock_old_archive_extraction], [0], [Whether to use a lock for old archive extraction]) ])# _LT_CMD_OLD_ARCHIVE # _LT_COMPILER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS, # [OUTPUT-FILE], [ACTION-SUCCESS], [ACTION-FAILURE]) # ---------------------------------------------------------------- # Check whether the given compiler option works AC_DEFUN([_LT_COMPILER_OPTION], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_SED])dnl AC_CACHE_CHECK([$1], [$2], [$2=no m4_if([$4], , [ac_outfile=conftest.$ac_objext], [ac_outfile=$4]) echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="$3" # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. # The option is referenced via a variable to avoid confusing sed. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&AS_MESSAGE_LOG_FD) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&AS_MESSAGE_LOG_FD echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then $2=yes fi fi $RM conftest* ]) if test x"[$]$2" = xyes; then m4_if([$5], , :, [$5]) else m4_if([$6], , :, [$6]) fi ])# _LT_COMPILER_OPTION # Old name: AU_ALIAS([AC_LIBTOOL_COMPILER_OPTION], [_LT_COMPILER_OPTION]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_COMPILER_OPTION], []) # _LT_LINKER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS, # [ACTION-SUCCESS], [ACTION-FAILURE]) # ---------------------------------------------------- # Check whether the given linker option works AC_DEFUN([_LT_LINKER_OPTION], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_SED])dnl AC_CACHE_CHECK([$1], [$2], [$2=no save_LDFLAGS="$LDFLAGS" LDFLAGS="$LDFLAGS $3" echo "$lt_simple_link_test_code" > conftest.$ac_ext if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then # The linker can only warn and ignore the option if not recognized # So say no if there are warnings if test -s conftest.err; then # Append any errors to the config.log. cat conftest.err 1>&AS_MESSAGE_LOG_FD $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if diff conftest.exp conftest.er2 >/dev/null; then $2=yes fi else $2=yes fi fi $RM -r conftest* LDFLAGS="$save_LDFLAGS" ]) if test x"[$]$2" = xyes; then m4_if([$4], , :, [$4]) else m4_if([$5], , :, [$5]) fi ])# _LT_LINKER_OPTION # Old name: AU_ALIAS([AC_LIBTOOL_LINKER_OPTION], [_LT_LINKER_OPTION]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_LINKER_OPTION], []) # LT_CMD_MAX_LEN #--------------- AC_DEFUN([LT_CMD_MAX_LEN], [AC_REQUIRE([AC_CANONICAL_HOST])dnl # find the maximum length of command line arguments AC_MSG_CHECKING([the maximum length of command line arguments]) AC_CACHE_VAL([lt_cv_sys_max_cmd_len], [dnl i=0 teststring="ABCD" case $build_os in msdosdjgpp*) # On DJGPP, this test can blow up pretty badly due to problems in libc # (any single argument exceeding 2000 bytes causes a buffer overrun # during glob expansion). Even if it were fixed, the result of this # check would be larger than it should be. lt_cv_sys_max_cmd_len=12288; # 12K is about right ;; gnu*) # Under GNU Hurd, this test is not required because there is # no limit to the length of command line arguments. # Libtool will interpret -1 as no limit whatsoever lt_cv_sys_max_cmd_len=-1; ;; cygwin* | mingw* | cegcc*) # On Win9x/ME, this test blows up -- it succeeds, but takes # about 5 minutes as the teststring grows exponentially. # Worse, since 9x/ME are not pre-emptively multitasking, # you end up with a "frozen" computer, even though with patience # the test eventually succeeds (with a max line length of 256k). # Instead, let's just punt: use the minimum linelength reported by # all of the supported platforms: 8192 (on NT/2K/XP). lt_cv_sys_max_cmd_len=8192; ;; mint*) # On MiNT this can take a long time and run out of memory. lt_cv_sys_max_cmd_len=8192; ;; amigaos*) # On AmigaOS with pdksh, this test takes hours, literally. # So we just punt and use a minimum line length of 8192. lt_cv_sys_max_cmd_len=8192; ;; netbsd* | freebsd* | openbsd* | darwin* | dragonfly*) # This has been around since 386BSD, at least. Likely further. if test -x /sbin/sysctl; then lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax` elif test -x /usr/sbin/sysctl; then lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax` else lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs fi # And add a safety zone lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` ;; interix*) # We know the value 262144 and hardcode it with a safety zone (like BSD) lt_cv_sys_max_cmd_len=196608 ;; os2*) # The test takes a long time on OS/2. lt_cv_sys_max_cmd_len=8192 ;; osf*) # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not # nice to cause kernel panics so lets avoid the loop below. # First set a reasonable default. lt_cv_sys_max_cmd_len=16384 # if test -x /sbin/sysconfig; then case `/sbin/sysconfig -q proc exec_disable_arg_limit` in *1*) lt_cv_sys_max_cmd_len=-1 ;; esac fi ;; sco3.2v5*) lt_cv_sys_max_cmd_len=102400 ;; sysv5* | sco5v6* | sysv4.2uw2*) kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null` if test -n "$kargmax"; then lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[[ ]]//'` else lt_cv_sys_max_cmd_len=32768 fi ;; *) lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null` if test -n "$lt_cv_sys_max_cmd_len" && \ test undefined != "$lt_cv_sys_max_cmd_len"; then lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` else # Make teststring a little bigger before we do anything with it. # a 1K string should be a reasonable start. for i in 1 2 3 4 5 6 7 8 ; do teststring=$teststring$teststring done SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}} # If test is not a shell built-in, we'll probably end up computing a # maximum length that is only half of the actual maximum length, but # we can't tell. while { test "X"`env echo "$teststring$teststring" 2>/dev/null` \ = "X$teststring$teststring"; } >/dev/null 2>&1 && test $i != 17 # 1/2 MB should be enough do i=`expr $i + 1` teststring=$teststring$teststring done # Only check the string length outside the loop. lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1` teststring= # Add a significant safety factor because C++ compilers can tack on # massive amounts of additional arguments before passing them to the # linker. It appears as though 1/2 is a usable value. lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2` fi ;; esac ]) if test -n $lt_cv_sys_max_cmd_len ; then AC_MSG_RESULT($lt_cv_sys_max_cmd_len) else AC_MSG_RESULT(none) fi max_cmd_len=$lt_cv_sys_max_cmd_len _LT_DECL([], [max_cmd_len], [0], [What is the maximum length of a command?]) ])# LT_CMD_MAX_LEN # Old name: AU_ALIAS([AC_LIBTOOL_SYS_MAX_CMD_LEN], [LT_CMD_MAX_LEN]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_SYS_MAX_CMD_LEN], []) # _LT_HEADER_DLFCN # ---------------- m4_defun([_LT_HEADER_DLFCN], [AC_CHECK_HEADERS([dlfcn.h], [], [], [AC_INCLUDES_DEFAULT])dnl ])# _LT_HEADER_DLFCN # _LT_TRY_DLOPEN_SELF (ACTION-IF-TRUE, ACTION-IF-TRUE-W-USCORE, # ACTION-IF-FALSE, ACTION-IF-CROSS-COMPILING) # ---------------------------------------------------------------- m4_defun([_LT_TRY_DLOPEN_SELF], [m4_require([_LT_HEADER_DLFCN])dnl if test "$cross_compiling" = yes; then : [$4] else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF [#line $LINENO "configure" #include "confdefs.h" #if HAVE_DLFCN_H #include #endif #include #ifdef RTLD_GLOBAL # define LT_DLGLOBAL RTLD_GLOBAL #else # ifdef DL_GLOBAL # define LT_DLGLOBAL DL_GLOBAL # else # define LT_DLGLOBAL 0 # endif #endif /* We may have to define LT_DLLAZY_OR_NOW in the command line if we find out it does not work in some platform. */ #ifndef LT_DLLAZY_OR_NOW # ifdef RTLD_LAZY # define LT_DLLAZY_OR_NOW RTLD_LAZY # else # ifdef DL_LAZY # define LT_DLLAZY_OR_NOW DL_LAZY # else # ifdef RTLD_NOW # define LT_DLLAZY_OR_NOW RTLD_NOW # else # ifdef DL_NOW # define LT_DLLAZY_OR_NOW DL_NOW # else # define LT_DLLAZY_OR_NOW 0 # endif # endif # endif # endif #endif /* When -fvisbility=hidden is used, assume the code has been annotated correspondingly for the symbols needed. */ #if defined(__GNUC__) && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) int fnord () __attribute__((visibility("default"))); #endif int fnord () { return 42; } int main () { void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); int status = $lt_dlunknown; if (self) { if (dlsym (self,"fnord")) status = $lt_dlno_uscore; else { if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; else puts (dlerror ()); } /* dlclose (self); */ } else puts (dlerror ()); return status; }] _LT_EOF if AC_TRY_EVAL(ac_link) && test -s conftest${ac_exeext} 2>/dev/null; then (./conftest; exit; ) >&AS_MESSAGE_LOG_FD 2>/dev/null lt_status=$? case x$lt_status in x$lt_dlno_uscore) $1 ;; x$lt_dlneed_uscore) $2 ;; x$lt_dlunknown|x*) $3 ;; esac else : # compilation failed $3 fi fi rm -fr conftest* ])# _LT_TRY_DLOPEN_SELF # LT_SYS_DLOPEN_SELF # ------------------ AC_DEFUN([LT_SYS_DLOPEN_SELF], [m4_require([_LT_HEADER_DLFCN])dnl if test "x$enable_dlopen" != xyes; then enable_dlopen=unknown enable_dlopen_self=unknown enable_dlopen_self_static=unknown else lt_cv_dlopen=no lt_cv_dlopen_libs= case $host_os in beos*) lt_cv_dlopen="load_add_on" lt_cv_dlopen_libs= lt_cv_dlopen_self=yes ;; mingw* | pw32* | cegcc*) lt_cv_dlopen="LoadLibrary" lt_cv_dlopen_libs= ;; cygwin*) lt_cv_dlopen="dlopen" lt_cv_dlopen_libs= ;; darwin*) # if libdl is installed we need to link against it AC_CHECK_LIB([dl], [dlopen], [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"],[ lt_cv_dlopen="dyld" lt_cv_dlopen_libs= lt_cv_dlopen_self=yes ]) ;; *) AC_CHECK_FUNC([shl_load], [lt_cv_dlopen="shl_load"], [AC_CHECK_LIB([dld], [shl_load], [lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-ldld"], [AC_CHECK_FUNC([dlopen], [lt_cv_dlopen="dlopen"], [AC_CHECK_LIB([dl], [dlopen], [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"], [AC_CHECK_LIB([svld], [dlopen], [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld"], [AC_CHECK_LIB([dld], [dld_link], [lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-ldld"]) ]) ]) ]) ]) ]) ;; esac if test "x$lt_cv_dlopen" != xno; then enable_dlopen=yes else enable_dlopen=no fi case $lt_cv_dlopen in dlopen) save_CPPFLAGS="$CPPFLAGS" test "x$ac_cv_header_dlfcn_h" = xyes && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H" save_LDFLAGS="$LDFLAGS" wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\" save_LIBS="$LIBS" LIBS="$lt_cv_dlopen_libs $LIBS" AC_CACHE_CHECK([whether a program can dlopen itself], lt_cv_dlopen_self, [dnl _LT_TRY_DLOPEN_SELF( lt_cv_dlopen_self=yes, lt_cv_dlopen_self=yes, lt_cv_dlopen_self=no, lt_cv_dlopen_self=cross) ]) if test "x$lt_cv_dlopen_self" = xyes; then wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\" AC_CACHE_CHECK([whether a statically linked program can dlopen itself], lt_cv_dlopen_self_static, [dnl _LT_TRY_DLOPEN_SELF( lt_cv_dlopen_self_static=yes, lt_cv_dlopen_self_static=yes, lt_cv_dlopen_self_static=no, lt_cv_dlopen_self_static=cross) ]) fi CPPFLAGS="$save_CPPFLAGS" LDFLAGS="$save_LDFLAGS" LIBS="$save_LIBS" ;; esac case $lt_cv_dlopen_self in yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;; *) enable_dlopen_self=unknown ;; esac case $lt_cv_dlopen_self_static in yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;; *) enable_dlopen_self_static=unknown ;; esac fi _LT_DECL([dlopen_support], [enable_dlopen], [0], [Whether dlopen is supported]) _LT_DECL([dlopen_self], [enable_dlopen_self], [0], [Whether dlopen of programs is supported]) _LT_DECL([dlopen_self_static], [enable_dlopen_self_static], [0], [Whether dlopen of statically linked programs is supported]) ])# LT_SYS_DLOPEN_SELF # Old name: AU_ALIAS([AC_LIBTOOL_DLOPEN_SELF], [LT_SYS_DLOPEN_SELF]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_DLOPEN_SELF], []) # _LT_COMPILER_C_O([TAGNAME]) # --------------------------- # Check to see if options -c and -o are simultaneously supported by compiler. # This macro does not hard code the compiler like AC_PROG_CC_C_O. m4_defun([_LT_COMPILER_C_O], [m4_require([_LT_DECL_SED])dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_TAG_COMPILER])dnl AC_CACHE_CHECK([if $compiler supports -c -o file.$ac_objext], [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)], [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=no $RM -r conftest 2>/dev/null mkdir conftest cd conftest mkdir out echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="-o out/conftest2.$ac_objext" # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&AS_MESSAGE_LOG_FD) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&AS_MESSAGE_LOG_FD echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then _LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes fi fi chmod u+w . 2>&AS_MESSAGE_LOG_FD $RM conftest* # SGI C++ compiler will create directory out/ii_files/ for # template instantiation test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files $RM out/* && rmdir out cd .. $RM -r conftest $RM conftest* ]) _LT_TAGDECL([compiler_c_o], [lt_cv_prog_compiler_c_o], [1], [Does compiler simultaneously support -c and -o options?]) ])# _LT_COMPILER_C_O # _LT_COMPILER_FILE_LOCKS([TAGNAME]) # ---------------------------------- # Check to see if we can do hard links to lock some files if needed m4_defun([_LT_COMPILER_FILE_LOCKS], [m4_require([_LT_ENABLE_LOCK])dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl _LT_COMPILER_C_O([$1]) hard_links="nottested" if test "$_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)" = no && test "$need_locks" != no; then # do not overwrite the value of need_locks provided by the user AC_MSG_CHECKING([if we can lock with hard links]) hard_links=yes $RM conftest* ln conftest.a conftest.b 2>/dev/null && hard_links=no touch conftest.a ln conftest.a conftest.b 2>&5 || hard_links=no ln conftest.a conftest.b 2>/dev/null && hard_links=no AC_MSG_RESULT([$hard_links]) if test "$hard_links" = no; then AC_MSG_WARN([`$CC' does not support `-c -o', so `make -j' may be unsafe]) need_locks=warn fi else need_locks=no fi _LT_DECL([], [need_locks], [1], [Must we lock files when doing compilation?]) ])# _LT_COMPILER_FILE_LOCKS # _LT_CHECK_OBJDIR # ---------------- m4_defun([_LT_CHECK_OBJDIR], [AC_CACHE_CHECK([for objdir], [lt_cv_objdir], [rm -f .libs 2>/dev/null mkdir .libs 2>/dev/null if test -d .libs; then lt_cv_objdir=.libs else # MS-DOS does not allow filenames that begin with a dot. lt_cv_objdir=_libs fi rmdir .libs 2>/dev/null]) objdir=$lt_cv_objdir _LT_DECL([], [objdir], [0], [The name of the directory that contains temporary libtool files])dnl m4_pattern_allow([LT_OBJDIR])dnl AC_DEFINE_UNQUOTED(LT_OBJDIR, "$lt_cv_objdir/", [Define to the sub-directory in which libtool stores uninstalled libraries.]) ])# _LT_CHECK_OBJDIR # _LT_LINKER_HARDCODE_LIBPATH([TAGNAME]) # -------------------------------------- # Check hardcoding attributes. m4_defun([_LT_LINKER_HARDCODE_LIBPATH], [AC_MSG_CHECKING([how to hardcode library paths into programs]) _LT_TAGVAR(hardcode_action, $1)= if test -n "$_LT_TAGVAR(hardcode_libdir_flag_spec, $1)" || test -n "$_LT_TAGVAR(runpath_var, $1)" || test "X$_LT_TAGVAR(hardcode_automatic, $1)" = "Xyes" ; then # We can hardcode non-existent directories. if test "$_LT_TAGVAR(hardcode_direct, $1)" != no && # If the only mechanism to avoid hardcoding is shlibpath_var, we # have to relink, otherwise we might link with an installed library # when we should be linking with a yet-to-be-installed one ## test "$_LT_TAGVAR(hardcode_shlibpath_var, $1)" != no && test "$_LT_TAGVAR(hardcode_minus_L, $1)" != no; then # Linking always hardcodes the temporary library directory. _LT_TAGVAR(hardcode_action, $1)=relink else # We can link without hardcoding, and we can hardcode nonexisting dirs. _LT_TAGVAR(hardcode_action, $1)=immediate fi else # We cannot hardcode anything, or else we can only hardcode existing # directories. _LT_TAGVAR(hardcode_action, $1)=unsupported fi AC_MSG_RESULT([$_LT_TAGVAR(hardcode_action, $1)]) if test "$_LT_TAGVAR(hardcode_action, $1)" = relink || test "$_LT_TAGVAR(inherit_rpath, $1)" = yes; then # Fast installation is not supported enable_fast_install=no elif test "$shlibpath_overrides_runpath" = yes || test "$enable_shared" = no; then # Fast installation is not necessary enable_fast_install=needless fi _LT_TAGDECL([], [hardcode_action], [0], [How to hardcode a shared library path into an executable]) ])# _LT_LINKER_HARDCODE_LIBPATH # _LT_CMD_STRIPLIB # ---------------- m4_defun([_LT_CMD_STRIPLIB], [m4_require([_LT_DECL_EGREP]) striplib= old_striplib= AC_MSG_CHECKING([whether stripping libraries is possible]) if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then test -z "$old_striplib" && old_striplib="$STRIP --strip-debug" test -z "$striplib" && striplib="$STRIP --strip-unneeded" AC_MSG_RESULT([yes]) else # FIXME - insert some real tests, host_os isn't really good enough case $host_os in darwin*) if test -n "$STRIP" ; then striplib="$STRIP -x" old_striplib="$STRIP -S" AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) fi ;; *) AC_MSG_RESULT([no]) ;; esac fi _LT_DECL([], [old_striplib], [1], [Commands to strip libraries]) _LT_DECL([], [striplib], [1]) ])# _LT_CMD_STRIPLIB # _LT_SYS_DYNAMIC_LINKER([TAG]) # ----------------------------- # PORTME Fill in your ld.so characteristics m4_defun([_LT_SYS_DYNAMIC_LINKER], [AC_REQUIRE([AC_CANONICAL_HOST])dnl m4_require([_LT_DECL_EGREP])dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_OBJDUMP])dnl m4_require([_LT_DECL_SED])dnl m4_require([_LT_CHECK_SHELL_FEATURES])dnl AC_MSG_CHECKING([dynamic linker characteristics]) m4_if([$1], [], [ if test "$GCC" = yes; then case $host_os in darwin*) lt_awk_arg="/^libraries:/,/LR/" ;; *) lt_awk_arg="/^libraries:/" ;; esac case $host_os in mingw* | cegcc*) lt_sed_strip_eq="s,=\([[A-Za-z]]:\),\1,g" ;; *) lt_sed_strip_eq="s,=/,/,g" ;; esac lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e $lt_sed_strip_eq` case $lt_search_path_spec in *\;*) # if the path contains ";" then we assume it to be the separator # otherwise default to the standard path separator (i.e. ":") - it is # assumed that no part of a normal pathname contains ";" but that should # okay in the real world where ";" in dirpaths is itself problematic. lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED 's/;/ /g'` ;; *) lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED "s/$PATH_SEPARATOR/ /g"` ;; esac # Ok, now we have the path, separated by spaces, we can step through it # and add multilib dir if necessary. lt_tmp_lt_search_path_spec= lt_multi_os_dir=`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null` for lt_sys_path in $lt_search_path_spec; do if test -d "$lt_sys_path/$lt_multi_os_dir"; then lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path/$lt_multi_os_dir" else test -d "$lt_sys_path" && \ lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path" fi done lt_search_path_spec=`$ECHO "$lt_tmp_lt_search_path_spec" | awk ' BEGIN {RS=" "; FS="/|\n";} { lt_foo=""; lt_count=0; for (lt_i = NF; lt_i > 0; lt_i--) { if ($lt_i != "" && $lt_i != ".") { if ($lt_i == "..") { lt_count++; } else { if (lt_count == 0) { lt_foo="/" $lt_i lt_foo; } else { lt_count--; } } } } if (lt_foo != "") { lt_freq[[lt_foo]]++; } if (lt_freq[[lt_foo]] == 1) { print lt_foo; } }'` # AWK program above erroneously prepends '/' to C:/dos/paths # for these hosts. case $host_os in mingw* | cegcc*) lt_search_path_spec=`$ECHO "$lt_search_path_spec" |\ $SED 's,/\([[A-Za-z]]:\),\1,g'` ;; esac sys_lib_search_path_spec=`$ECHO "$lt_search_path_spec" | $lt_NL2SP` else sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" fi]) library_names_spec= libname_spec='lib$name' soname_spec= shrext_cmds=".so" postinstall_cmds= postuninstall_cmds= finish_cmds= finish_eval= shlibpath_var= shlibpath_overrides_runpath=unknown version_type=none dynamic_linker="$host_os ld.so" sys_lib_dlsearch_path_spec="/lib /usr/lib" need_lib_prefix=unknown hardcode_into_libs=no # when you set need_version to no, make sure it does not cause -set_version # flags to be left without arguments need_version=unknown case $host_os in aix3*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a' shlibpath_var=LIBPATH # AIX 3 has no versioning support, so we append a major version to the name. soname_spec='${libname}${release}${shared_ext}$major' ;; aix[[4-9]]*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no hardcode_into_libs=yes if test "$host_cpu" = ia64; then # AIX 5 supports IA64 library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}' shlibpath_var=LD_LIBRARY_PATH else # With GCC up to 2.95.x, collect2 would create an import file # for dependence libraries. The import file would start with # the line `#! .'. This would cause the generated library to # depend on `.', always an invalid library. This was fixed in # development snapshots of GCC prior to 3.0. case $host_os in aix4 | aix4.[[01]] | aix4.[[01]].*) if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' echo ' yes ' echo '#endif'; } | ${CC} -E - | $GREP yes > /dev/null; then : else can_build_shared=no fi ;; esac # AIX (on Power*) has no versioning support, so currently we can not hardcode correct # soname into executable. Probably we can add versioning support to # collect2, so additional links can be useful in future. if test "$aix_use_runtimelinking" = yes; then # If using run time linking (on AIX 4.2 or later) use lib.so # instead of lib.a to let people know that these are not # typical AIX shared libraries. library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' else # We preserve .a as extension for shared libraries through AIX4.2 # and later when we are not doing run time linking. library_names_spec='${libname}${release}.a $libname.a' soname_spec='${libname}${release}${shared_ext}$major' fi shlibpath_var=LIBPATH fi ;; amigaos*) case $host_cpu in powerpc) # Since July 2007 AmigaOS4 officially supports .so libraries. # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' ;; m68k) library_names_spec='$libname.ixlibrary $libname.a' # Create ${libname}_ixlibrary.a entries in /sys/libs. finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([[^/]]*\)\.ixlibrary$%\1%'\''`; test $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' ;; esac ;; beos*) library_names_spec='${libname}${shared_ext}' dynamic_linker="$host_os ld.so" shlibpath_var=LIBRARY_PATH ;; bsdi[[45]]*) version_type=linux # correct to gnu/linux during the next big refactor need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' shlibpath_var=LD_LIBRARY_PATH sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" # the default ld.so.conf also contains /usr/contrib/lib and # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow # libtool to hard-code these into programs ;; cygwin* | mingw* | pw32* | cegcc*) version_type=windows shrext_cmds=".dll" need_version=no need_lib_prefix=no case $GCC,$cc_basename in yes,*) # gcc library_names_spec='$libname.dll.a' # DLL is installed to $(libdir)/../bin by postinstall_cmds postinstall_cmds='base_file=`basename \${file}`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname~ chmod a+x \$dldir/$dlname~ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; fi' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' shlibpath_overrides_runpath=yes case $host_os in cygwin*) # Cygwin DLLs use 'cyg' prefix rather than 'lib' soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}' m4_if([$1], [],[ sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/lib/w32api"]) ;; mingw* | cegcc*) # MinGW DLLs use traditional 'lib' prefix soname_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}' ;; pw32*) # pw32 DLLs use 'pw' prefix rather than 'lib' library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}' ;; esac dynamic_linker='Win32 ld.exe' ;; *,cl*) # Native MSVC libname_spec='$name' soname_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}' library_names_spec='${libname}.dll.lib' case $build_os in mingw*) sys_lib_search_path_spec= lt_save_ifs=$IFS IFS=';' for lt_path in $LIB do IFS=$lt_save_ifs # Let DOS variable expansion print the short 8.3 style file name. lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"` sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path" done IFS=$lt_save_ifs # Convert to MSYS style. sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([[a-zA-Z]]\\):| /\\1|g' -e 's|^ ||'` ;; cygwin*) # Convert to unix form, then to dos form, then back to unix form # but this time dos style (no spaces!) so that the unix form looks # like /cygdrive/c/PROGRA~1:/cygdr... sys_lib_search_path_spec=`cygpath --path --unix "$LIB"` sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null` sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` ;; *) sys_lib_search_path_spec="$LIB" if $ECHO "$sys_lib_search_path_spec" | [$GREP ';[c-zC-Z]:/' >/dev/null]; then # It is most probably a Windows format PATH. sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` else sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` fi # FIXME: find the short name or the path components, as spaces are # common. (e.g. "Program Files" -> "PROGRA~1") ;; esac # DLL is installed to $(libdir)/../bin by postinstall_cmds postinstall_cmds='base_file=`basename \${file}`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' shlibpath_overrides_runpath=yes dynamic_linker='Win32 link.exe' ;; *) # Assume MSVC wrapper library_names_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext} $libname.lib' dynamic_linker='Win32 ld.exe' ;; esac # FIXME: first we should search . and the directory the executable is in shlibpath_var=PATH ;; darwin* | rhapsody*) dynamic_linker="$host_os dyld" version_type=darwin need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${major}$shared_ext ${libname}$shared_ext' soname_spec='${libname}${release}${major}$shared_ext' shlibpath_overrides_runpath=yes shlibpath_var=DYLD_LIBRARY_PATH shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' m4_if([$1], [],[ sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib"]) sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' ;; dgux*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH ;; freebsd* | dragonfly*) # DragonFly does not have aout. When/if they implement a new # versioning mechanism, adjust this. if test -x /usr/bin/objformat; then objformat=`/usr/bin/objformat` else case $host_os in freebsd[[23]].*) objformat=aout ;; *) objformat=elf ;; esac fi version_type=freebsd-$objformat case $version_type in freebsd-elf*) library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' need_version=no need_lib_prefix=no ;; freebsd-*) library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix' need_version=yes ;; esac shlibpath_var=LD_LIBRARY_PATH case $host_os in freebsd2.*) shlibpath_overrides_runpath=yes ;; freebsd3.[[01]]* | freebsdelf3.[[01]]*) shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; freebsd3.[[2-9]]* | freebsdelf3.[[2-9]]* | \ freebsd4.[[0-5]] | freebsdelf4.[[0-5]] | freebsd4.1.1 | freebsdelf4.1.1) shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; *) # from 4.6 on, and DragonFly shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; esac ;; haiku*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no dynamic_linker="$host_os runtime_loader" library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LIBRARY_PATH shlibpath_overrides_runpath=yes sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib' hardcode_into_libs=yes ;; hpux9* | hpux10* | hpux11*) # Give a soname corresponding to the major version so that dld.sl refuses to # link against other versions. version_type=sunos need_lib_prefix=no need_version=no case $host_cpu in ia64*) shrext_cmds='.so' hardcode_into_libs=yes dynamic_linker="$host_os dld.so" shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' if test "X$HPUX_IA64_MODE" = X32; then sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" else sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" fi sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; hppa*64*) shrext_cmds='.sl' hardcode_into_libs=yes dynamic_linker="$host_os dld.sl" shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; *) shrext_cmds='.sl' dynamic_linker="$host_os dld.sl" shlibpath_var=SHLIB_PATH shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' ;; esac # HP-UX runs *really* slowly unless shared libraries are mode 555, ... postinstall_cmds='chmod 555 $lib' # or fails outright, so override atomically: install_override_mode=555 ;; interix[[3-9]]*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; irix5* | irix6* | nonstopux*) case $host_os in nonstopux*) version_type=nonstopux ;; *) if test "$lt_cv_prog_gnu_ld" = yes; then version_type=linux # correct to gnu/linux during the next big refactor else version_type=irix fi ;; esac need_lib_prefix=no need_version=no soname_spec='${libname}${release}${shared_ext}$major' library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}' case $host_os in irix5* | nonstopux*) libsuff= shlibsuff= ;; *) case $LD in # libtool.m4 will add one of these switches to LD *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") libsuff= shlibsuff= libmagic=32-bit;; *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") libsuff=32 shlibsuff=N32 libmagic=N32;; *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") libsuff=64 shlibsuff=64 libmagic=64-bit;; *) libsuff= shlibsuff= libmagic=never-match;; esac ;; esac shlibpath_var=LD_LIBRARY${shlibsuff}_PATH shlibpath_overrides_runpath=no sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}" sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}" hardcode_into_libs=yes ;; # No shared lib support for Linux oldld, aout, or coff. linux*oldld* | linux*aout* | linux*coff*) dynamic_linker=no ;; # This must be glibc/ELF. linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no # Some binutils ld are patched to set DT_RUNPATH AC_CACHE_VAL([lt_cv_shlibpath_overrides_runpath], [lt_cv_shlibpath_overrides_runpath=no save_LDFLAGS=$LDFLAGS save_libdir=$libdir eval "libdir=/foo; wl=\"$_LT_TAGVAR(lt_prog_compiler_wl, $1)\"; \ LDFLAGS=\"\$LDFLAGS $_LT_TAGVAR(hardcode_libdir_flag_spec, $1)\"" AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])], [AS_IF([ ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null], [lt_cv_shlibpath_overrides_runpath=yes])]) LDFLAGS=$save_LDFLAGS libdir=$save_libdir ]) shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath # This implies no fast_install, which is unacceptable. # Some rework will be needed to allow for fast_install # before this can be enabled. hardcode_into_libs=yes # Append ld.so.conf contents to the search path if test -f /etc/ld.so.conf; then lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \[$]2)); skip = 1; } { if (!skip) print \[$]0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '` sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" fi # We used to test for /lib/ld.so.1 and disable shared libraries on # powerpc, because MkLinux only supported shared libraries with the # GNU dynamic linker. Since this was broken with cross compilers, # most powerpc-linux boxes support dynamic linking these days and # people can always --disable-shared, the test was removed, and we # assume the GNU/Linux dynamic linker is in use. dynamic_linker='GNU/Linux ld.so' ;; netbsdelf*-gnu) version_type=linux need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes dynamic_linker='NetBSD ld.elf_so' ;; netbsd*) version_type=sunos need_lib_prefix=no need_version=no if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' dynamic_linker='NetBSD (a.out) ld.so' else library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' dynamic_linker='NetBSD ld.elf_so' fi shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; newsos6) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes ;; *nto* | *qnx*) version_type=qnx need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes dynamic_linker='ldqnx.so' ;; openbsd*) version_type=sunos sys_lib_dlsearch_path_spec="/usr/lib" need_lib_prefix=no # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs. case $host_os in openbsd3.3 | openbsd3.3.*) need_version=yes ;; *) need_version=no ;; esac library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' shlibpath_var=LD_LIBRARY_PATH if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then case $host_os in openbsd2.[[89]] | openbsd2.[[89]].*) shlibpath_overrides_runpath=no ;; *) shlibpath_overrides_runpath=yes ;; esac else shlibpath_overrides_runpath=yes fi ;; os2*) libname_spec='$name' shrext_cmds=".dll" need_lib_prefix=no library_names_spec='$libname${shared_ext} $libname.a' dynamic_linker='OS/2 ld.exe' shlibpath_var=LIBPATH ;; osf3* | osf4* | osf5*) version_type=osf need_lib_prefix=no need_version=no soname_spec='${libname}${release}${shared_ext}$major' library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' shlibpath_var=LD_LIBRARY_PATH sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec" ;; rdos*) dynamic_linker=no ;; solaris*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes # ldd complains unless libraries are executable postinstall_cmds='chmod +x $lib' ;; sunos4*) version_type=sunos library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes if test "$with_gnu_ld" = yes; then need_lib_prefix=no fi need_version=yes ;; sysv4 | sysv4.3*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH case $host_vendor in sni) shlibpath_overrides_runpath=no need_lib_prefix=no runpath_var=LD_RUN_PATH ;; siemens) need_lib_prefix=no ;; motorola) need_lib_prefix=no need_version=no shlibpath_overrides_runpath=no sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' ;; esac ;; sysv4*MP*) if test -d /usr/nec ;then version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}' soname_spec='$libname${shared_ext}.$major' shlibpath_var=LD_LIBRARY_PATH fi ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) version_type=freebsd-elf need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes if test "$with_gnu_ld" = yes; then sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' else sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' case $host_os in sco3.2v5*) sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" ;; esac fi sys_lib_dlsearch_path_spec='/usr/lib' ;; tpf*) # TPF is a cross-target only. Preferred cross-host = GNU/Linux. version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; uts4*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH ;; *) dynamic_linker=no ;; esac AC_MSG_RESULT([$dynamic_linker]) test "$dynamic_linker" = no && can_build_shared=no variables_saved_for_relink="PATH $shlibpath_var $runpath_var" if test "$GCC" = yes; then variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" fi if test "${lt_cv_sys_lib_search_path_spec+set}" = set; then sys_lib_search_path_spec="$lt_cv_sys_lib_search_path_spec" fi if test "${lt_cv_sys_lib_dlsearch_path_spec+set}" = set; then sys_lib_dlsearch_path_spec="$lt_cv_sys_lib_dlsearch_path_spec" fi _LT_DECL([], [variables_saved_for_relink], [1], [Variables whose values should be saved in libtool wrapper scripts and restored at link time]) _LT_DECL([], [need_lib_prefix], [0], [Do we need the "lib" prefix for modules?]) _LT_DECL([], [need_version], [0], [Do we need a version for libraries?]) _LT_DECL([], [version_type], [0], [Library versioning type]) _LT_DECL([], [runpath_var], [0], [Shared library runtime path variable]) _LT_DECL([], [shlibpath_var], [0],[Shared library path variable]) _LT_DECL([], [shlibpath_overrides_runpath], [0], [Is shlibpath searched before the hard-coded library search path?]) _LT_DECL([], [libname_spec], [1], [Format of library name prefix]) _LT_DECL([], [library_names_spec], [1], [[List of archive names. First name is the real one, the rest are links. The last name is the one that the linker finds with -lNAME]]) _LT_DECL([], [soname_spec], [1], [[The coded name of the library, if different from the real name]]) _LT_DECL([], [install_override_mode], [1], [Permission mode override for installation of shared libraries]) _LT_DECL([], [postinstall_cmds], [2], [Command to use after installation of a shared archive]) _LT_DECL([], [postuninstall_cmds], [2], [Command to use after uninstallation of a shared archive]) _LT_DECL([], [finish_cmds], [2], [Commands used to finish a libtool library installation in a directory]) _LT_DECL([], [finish_eval], [1], [[As "finish_cmds", except a single script fragment to be evaled but not shown]]) _LT_DECL([], [hardcode_into_libs], [0], [Whether we should hardcode library paths into libraries]) _LT_DECL([], [sys_lib_search_path_spec], [2], [Compile-time system search path for libraries]) _LT_DECL([], [sys_lib_dlsearch_path_spec], [2], [Run-time system search path for libraries]) ])# _LT_SYS_DYNAMIC_LINKER # _LT_PATH_TOOL_PREFIX(TOOL) # -------------------------- # find a file program which can recognize shared library AC_DEFUN([_LT_PATH_TOOL_PREFIX], [m4_require([_LT_DECL_EGREP])dnl AC_MSG_CHECKING([for $1]) AC_CACHE_VAL(lt_cv_path_MAGIC_CMD, [case $MAGIC_CMD in [[\\/*] | ?:[\\/]*]) lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path. ;; *) lt_save_MAGIC_CMD="$MAGIC_CMD" lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR dnl $ac_dummy forces splitting on constant user-supplied paths. dnl POSIX.2 word splitting is done only on the output of word expansions, dnl not every word. This closes a longstanding sh security hole. ac_dummy="m4_if([$2], , $PATH, [$2])" for ac_dir in $ac_dummy; do IFS="$lt_save_ifs" test -z "$ac_dir" && ac_dir=. if test -f $ac_dir/$1; then lt_cv_path_MAGIC_CMD="$ac_dir/$1" if test -n "$file_magic_test_file"; then case $deplibs_check_method in "file_magic "*) file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` MAGIC_CMD="$lt_cv_path_MAGIC_CMD" if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | $EGREP "$file_magic_regex" > /dev/null; then : else cat <<_LT_EOF 1>&2 *** Warning: the command libtool uses to detect shared libraries, *** $file_magic_cmd, produces output that libtool cannot recognize. *** The result is that libtool may fail to recognize shared libraries *** as such. This will affect the creation of libtool libraries that *** depend on shared libraries, but programs linked with such libtool *** libraries will work regardless of this problem. Nevertheless, you *** may want to report the problem to your system manager and/or to *** bug-libtool@gnu.org _LT_EOF fi ;; esac fi break fi done IFS="$lt_save_ifs" MAGIC_CMD="$lt_save_MAGIC_CMD" ;; esac]) MAGIC_CMD="$lt_cv_path_MAGIC_CMD" if test -n "$MAGIC_CMD"; then AC_MSG_RESULT($MAGIC_CMD) else AC_MSG_RESULT(no) fi _LT_DECL([], [MAGIC_CMD], [0], [Used to examine libraries when file_magic_cmd begins with "file"])dnl ])# _LT_PATH_TOOL_PREFIX # Old name: AU_ALIAS([AC_PATH_TOOL_PREFIX], [_LT_PATH_TOOL_PREFIX]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_PATH_TOOL_PREFIX], []) # _LT_PATH_MAGIC # -------------- # find a file program which can recognize a shared library m4_defun([_LT_PATH_MAGIC], [_LT_PATH_TOOL_PREFIX(${ac_tool_prefix}file, /usr/bin$PATH_SEPARATOR$PATH) if test -z "$lt_cv_path_MAGIC_CMD"; then if test -n "$ac_tool_prefix"; then _LT_PATH_TOOL_PREFIX(file, /usr/bin$PATH_SEPARATOR$PATH) else MAGIC_CMD=: fi fi ])# _LT_PATH_MAGIC # LT_PATH_LD # ---------- # find the pathname to the GNU or non-GNU linker AC_DEFUN([LT_PATH_LD], [AC_REQUIRE([AC_PROG_CC])dnl AC_REQUIRE([AC_CANONICAL_HOST])dnl AC_REQUIRE([AC_CANONICAL_BUILD])dnl m4_require([_LT_DECL_SED])dnl m4_require([_LT_DECL_EGREP])dnl m4_require([_LT_PROG_ECHO_BACKSLASH])dnl AC_ARG_WITH([gnu-ld], [AS_HELP_STRING([--with-gnu-ld], [assume the C compiler uses GNU ld @<:@default=no@:>@])], [test "$withval" = no || with_gnu_ld=yes], [with_gnu_ld=no])dnl ac_prog=ld if test "$GCC" = yes; then # Check if gcc -print-prog-name=ld gives a path. AC_MSG_CHECKING([for ld used by $CC]) case $host in *-*-mingw*) # gcc leaves a trailing carriage return which upsets mingw ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; *) ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; esac case $ac_prog in # Accept absolute paths. [[\\/]]* | ?:[[\\/]]*) re_direlt='/[[^/]][[^/]]*/\.\./' # Canonicalize the pathname of ld ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'` while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` done test -z "$LD" && LD="$ac_prog" ;; "") # If it fails, then pretend we aren't using GCC. ac_prog=ld ;; *) # If it is relative, then search for the first ld in PATH. with_gnu_ld=unknown ;; esac elif test "$with_gnu_ld" = yes; then AC_MSG_CHECKING([for GNU ld]) else AC_MSG_CHECKING([for non-GNU ld]) fi AC_CACHE_VAL(lt_cv_path_LD, [if test -z "$LD"; then lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR for ac_dir in $PATH; do IFS="$lt_save_ifs" test -z "$ac_dir" && ac_dir=. if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then lt_cv_path_LD="$ac_dir/$ac_prog" # Check to see if the program is GNU ld. I'd rather use --version, # but apparently some variants of GNU ld only accept -v. # Break only if it was the GNU/non-GNU ld that we prefer. case `"$lt_cv_path_LD" -v 2>&1 &1 /dev/null 2>&1; then lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' lt_cv_file_magic_cmd='func_win32_libid' else # Keep this pattern in sync with the one in func_win32_libid. lt_cv_deplibs_check_method='file_magic file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' lt_cv_file_magic_cmd='$OBJDUMP -f' fi ;; cegcc*) # use the weaker test based on 'objdump'. See mingw*. lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?' lt_cv_file_magic_cmd='$OBJDUMP -f' ;; darwin* | rhapsody*) lt_cv_deplibs_check_method=pass_all ;; freebsd* | dragonfly*) if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then case $host_cpu in i*86 ) # Not sure whether the presence of OpenBSD here was a mistake. # Let's accept both of them until this is cleared up. lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[[3-9]]86 (compact )?demand paged shared library' lt_cv_file_magic_cmd=/usr/bin/file lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*` ;; esac else lt_cv_deplibs_check_method=pass_all fi ;; haiku*) lt_cv_deplibs_check_method=pass_all ;; hpux10.20* | hpux11*) lt_cv_file_magic_cmd=/usr/bin/file case $host_cpu in ia64*) lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|ELF-[[0-9]][[0-9]]) shared object file - IA64' lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so ;; hppa*64*) [lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF[ -][0-9][0-9])(-bit)?( [LM]SB)? shared object( file)?[, -]* PA-RISC [0-9]\.[0-9]'] lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl ;; *) lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|PA-RISC[[0-9]]\.[[0-9]]) shared library' lt_cv_file_magic_test_file=/usr/lib/libc.sl ;; esac ;; interix[[3-9]]*) # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|\.a)$' ;; irix5* | irix6* | nonstopux*) case $LD in *-32|*"-32 ") libmagic=32-bit;; *-n32|*"-n32 ") libmagic=N32;; *-64|*"-64 ") libmagic=64-bit;; *) libmagic=never-match;; esac lt_cv_deplibs_check_method=pass_all ;; # This must be glibc/ELF. linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) lt_cv_deplibs_check_method=pass_all ;; netbsd* | netbsdelf*-gnu) if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$' else lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|_pic\.a)$' fi ;; newos6*) lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (executable|dynamic lib)' lt_cv_file_magic_cmd=/usr/bin/file lt_cv_file_magic_test_file=/usr/lib/libnls.so ;; *nto* | *qnx*) lt_cv_deplibs_check_method=pass_all ;; openbsd*) if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|\.so|_pic\.a)$' else lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$' fi ;; osf3* | osf4* | osf5*) lt_cv_deplibs_check_method=pass_all ;; rdos*) lt_cv_deplibs_check_method=pass_all ;; solaris*) lt_cv_deplibs_check_method=pass_all ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) lt_cv_deplibs_check_method=pass_all ;; sysv4 | sysv4.3*) case $host_vendor in motorola) lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib) M[[0-9]][[0-9]]* Version [[0-9]]' lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*` ;; ncr) lt_cv_deplibs_check_method=pass_all ;; sequent) lt_cv_file_magic_cmd='/bin/file' lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB (shared object|dynamic lib )' ;; sni) lt_cv_file_magic_cmd='/bin/file' lt_cv_deplibs_check_method="file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB dynamic lib" lt_cv_file_magic_test_file=/lib/libc.so ;; siemens) lt_cv_deplibs_check_method=pass_all ;; pc) lt_cv_deplibs_check_method=pass_all ;; esac ;; tpf*) lt_cv_deplibs_check_method=pass_all ;; esac ]) file_magic_glob= want_nocaseglob=no if test "$build" = "$host"; then case $host_os in mingw* | pw32*) if ( shopt | grep nocaseglob ) >/dev/null 2>&1; then want_nocaseglob=yes else file_magic_glob=`echo aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ | $SED -e "s/\(..\)/s\/[[\1]]\/[[\1]]\/g;/g"` fi ;; esac fi file_magic_cmd=$lt_cv_file_magic_cmd deplibs_check_method=$lt_cv_deplibs_check_method test -z "$deplibs_check_method" && deplibs_check_method=unknown _LT_DECL([], [deplibs_check_method], [1], [Method to check whether dependent libraries are shared objects]) _LT_DECL([], [file_magic_cmd], [1], [Command to use when deplibs_check_method = "file_magic"]) _LT_DECL([], [file_magic_glob], [1], [How to find potential files when deplibs_check_method = "file_magic"]) _LT_DECL([], [want_nocaseglob], [1], [Find potential files using nocaseglob when deplibs_check_method = "file_magic"]) ])# _LT_CHECK_MAGIC_METHOD # LT_PATH_NM # ---------- # find the pathname to a BSD- or MS-compatible name lister AC_DEFUN([LT_PATH_NM], [AC_REQUIRE([AC_PROG_CC])dnl AC_CACHE_CHECK([for BSD- or MS-compatible name lister (nm)], lt_cv_path_NM, [if test -n "$NM"; then # Let the user override the test. lt_cv_path_NM="$NM" else lt_nm_to_check="${ac_tool_prefix}nm" if test -n "$ac_tool_prefix" && test "$build" = "$host"; then lt_nm_to_check="$lt_nm_to_check nm" fi for lt_tmp_nm in $lt_nm_to_check; do lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do IFS="$lt_save_ifs" test -z "$ac_dir" && ac_dir=. tmp_nm="$ac_dir/$lt_tmp_nm" if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext" ; then # Check to see if the nm accepts a BSD-compat flag. # Adding the `sed 1q' prevents false positives on HP-UX, which says: # nm: unknown option "B" ignored # Tru64's nm complains that /dev/null is an invalid object file case `"$tmp_nm" -B /dev/null 2>&1 | sed '1q'` in */dev/null* | *'Invalid file or object type'*) lt_cv_path_NM="$tmp_nm -B" break ;; *) case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in */dev/null*) lt_cv_path_NM="$tmp_nm -p" break ;; *) lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but continue # so that we can try to find one that supports BSD flags ;; esac ;; esac fi done IFS="$lt_save_ifs" done : ${lt_cv_path_NM=no} fi]) if test "$lt_cv_path_NM" != "no"; then NM="$lt_cv_path_NM" else # Didn't find any BSD compatible name lister, look for dumpbin. if test -n "$DUMPBIN"; then : # Let the user override the test. else AC_CHECK_TOOLS(DUMPBIN, [dumpbin "link -dump"], :) case `$DUMPBIN -symbols /dev/null 2>&1 | sed '1q'` in *COFF*) DUMPBIN="$DUMPBIN -symbols" ;; *) DUMPBIN=: ;; esac fi AC_SUBST([DUMPBIN]) if test "$DUMPBIN" != ":"; then NM="$DUMPBIN" fi fi test -z "$NM" && NM=nm AC_SUBST([NM]) _LT_DECL([], [NM], [1], [A BSD- or MS-compatible name lister])dnl AC_CACHE_CHECK([the name lister ($NM) interface], [lt_cv_nm_interface], [lt_cv_nm_interface="BSD nm" echo "int some_variable = 0;" > conftest.$ac_ext (eval echo "\"\$as_me:$LINENO: $ac_compile\"" >&AS_MESSAGE_LOG_FD) (eval "$ac_compile" 2>conftest.err) cat conftest.err >&AS_MESSAGE_LOG_FD (eval echo "\"\$as_me:$LINENO: $NM \\\"conftest.$ac_objext\\\"\"" >&AS_MESSAGE_LOG_FD) (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) cat conftest.err >&AS_MESSAGE_LOG_FD (eval echo "\"\$as_me:$LINENO: output\"" >&AS_MESSAGE_LOG_FD) cat conftest.out >&AS_MESSAGE_LOG_FD if $GREP 'External.*some_variable' conftest.out > /dev/null; then lt_cv_nm_interface="MS dumpbin" fi rm -f conftest*]) ])# LT_PATH_NM # Old names: AU_ALIAS([AM_PROG_NM], [LT_PATH_NM]) AU_ALIAS([AC_PROG_NM], [LT_PATH_NM]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AM_PROG_NM], []) dnl AC_DEFUN([AC_PROG_NM], []) # _LT_CHECK_SHAREDLIB_FROM_LINKLIB # -------------------------------- # how to determine the name of the shared library # associated with a specific link library. # -- PORTME fill in with the dynamic library characteristics m4_defun([_LT_CHECK_SHAREDLIB_FROM_LINKLIB], [m4_require([_LT_DECL_EGREP]) m4_require([_LT_DECL_OBJDUMP]) m4_require([_LT_DECL_DLLTOOL]) AC_CACHE_CHECK([how to associate runtime and link libraries], lt_cv_sharedlib_from_linklib_cmd, [lt_cv_sharedlib_from_linklib_cmd='unknown' case $host_os in cygwin* | mingw* | pw32* | cegcc*) # two different shell functions defined in ltmain.sh # decide which to use based on capabilities of $DLLTOOL case `$DLLTOOL --help 2>&1` in *--identify-strict*) lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib ;; *) lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib_fallback ;; esac ;; *) # fallback: assume linklib IS sharedlib lt_cv_sharedlib_from_linklib_cmd="$ECHO" ;; esac ]) sharedlib_from_linklib_cmd=$lt_cv_sharedlib_from_linklib_cmd test -z "$sharedlib_from_linklib_cmd" && sharedlib_from_linklib_cmd=$ECHO _LT_DECL([], [sharedlib_from_linklib_cmd], [1], [Command to associate shared and link libraries]) ])# _LT_CHECK_SHAREDLIB_FROM_LINKLIB # _LT_PATH_MANIFEST_TOOL # ---------------------- # locate the manifest tool m4_defun([_LT_PATH_MANIFEST_TOOL], [AC_CHECK_TOOL(MANIFEST_TOOL, mt, :) test -z "$MANIFEST_TOOL" && MANIFEST_TOOL=mt AC_CACHE_CHECK([if $MANIFEST_TOOL is a manifest tool], [lt_cv_path_mainfest_tool], [lt_cv_path_mainfest_tool=no echo "$as_me:$LINENO: $MANIFEST_TOOL '-?'" >&AS_MESSAGE_LOG_FD $MANIFEST_TOOL '-?' 2>conftest.err > conftest.out cat conftest.err >&AS_MESSAGE_LOG_FD if $GREP 'Manifest Tool' conftest.out > /dev/null; then lt_cv_path_mainfest_tool=yes fi rm -f conftest*]) if test "x$lt_cv_path_mainfest_tool" != xyes; then MANIFEST_TOOL=: fi _LT_DECL([], [MANIFEST_TOOL], [1], [Manifest tool])dnl ])# _LT_PATH_MANIFEST_TOOL # LT_LIB_M # -------- # check for math library AC_DEFUN([LT_LIB_M], [AC_REQUIRE([AC_CANONICAL_HOST])dnl LIBM= case $host in *-*-beos* | *-*-cegcc* | *-*-cygwin* | *-*-haiku* | *-*-pw32* | *-*-darwin*) # These system don't have libm, or don't need it ;; *-ncr-sysv4.3*) AC_CHECK_LIB(mw, _mwvalidcheckl, LIBM="-lmw") AC_CHECK_LIB(m, cos, LIBM="$LIBM -lm") ;; *) AC_CHECK_LIB(m, cos, LIBM="-lm") ;; esac AC_SUBST([LIBM]) ])# LT_LIB_M # Old name: AU_ALIAS([AC_CHECK_LIBM], [LT_LIB_M]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_CHECK_LIBM], []) # _LT_COMPILER_NO_RTTI([TAGNAME]) # ------------------------------- m4_defun([_LT_COMPILER_NO_RTTI], [m4_require([_LT_TAG_COMPILER])dnl _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)= if test "$GCC" = yes; then case $cc_basename in nvcc*) _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -Xcompiler -fno-builtin' ;; *) _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' ;; esac _LT_COMPILER_OPTION([if $compiler supports -fno-rtti -fno-exceptions], lt_cv_prog_compiler_rtti_exceptions, [-fno-rtti -fno-exceptions], [], [_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)="$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1) -fno-rtti -fno-exceptions"]) fi _LT_TAGDECL([no_builtin_flag], [lt_prog_compiler_no_builtin_flag], [1], [Compiler flag to turn off builtin functions]) ])# _LT_COMPILER_NO_RTTI # _LT_CMD_GLOBAL_SYMBOLS # ---------------------- m4_defun([_LT_CMD_GLOBAL_SYMBOLS], [AC_REQUIRE([AC_CANONICAL_HOST])dnl AC_REQUIRE([AC_PROG_CC])dnl AC_REQUIRE([AC_PROG_AWK])dnl AC_REQUIRE([LT_PATH_NM])dnl AC_REQUIRE([LT_PATH_LD])dnl m4_require([_LT_DECL_SED])dnl m4_require([_LT_DECL_EGREP])dnl m4_require([_LT_TAG_COMPILER])dnl # Check for command to grab the raw symbol name followed by C symbol from nm. AC_MSG_CHECKING([command to parse $NM output from $compiler object]) AC_CACHE_VAL([lt_cv_sys_global_symbol_pipe], [ # These are sane defaults that work on at least a few old systems. # [They come from Ultrix. What could be older than Ultrix?!! ;)] # Character class describing NM global symbol codes. symcode='[[BCDEGRST]]' # Regexp to match symbols that can be accessed directly from C. sympat='\([[_A-Za-z]][[_A-Za-z0-9]]*\)' # Define system-specific variables. case $host_os in aix*) symcode='[[BCDT]]' ;; cygwin* | mingw* | pw32* | cegcc*) symcode='[[ABCDGISTW]]' ;; hpux*) if test "$host_cpu" = ia64; then symcode='[[ABCDEGRST]]' fi ;; irix* | nonstopux*) symcode='[[BCDEGRST]]' ;; osf*) symcode='[[BCDEGQRST]]' ;; solaris*) symcode='[[BDRT]]' ;; sco3.2v5*) symcode='[[DT]]' ;; sysv4.2uw2*) symcode='[[DT]]' ;; sysv5* | sco5v6* | unixware* | OpenUNIX*) symcode='[[ABDT]]' ;; sysv4) symcode='[[DFNSTU]]' ;; esac # If we're using GNU nm, then use its standard symbol codes. case `$NM -V 2>&1` in *GNU* | *'with BFD'*) symcode='[[ABCDGIRSTW]]' ;; esac # Transform an extracted symbol line into a proper C declaration. # Some systems (esp. on ia64) link data and code symbols differently, # so use this general approach. lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern int \1();/p' -e 's/^$symcode* .* \(.*\)$/extern char \1;/p'" # Transform an extracted symbol line into symbol name and symbol address lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([[^ ]]*\)[[ ]]*$/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([[^ ]]*\) \([[^ ]]*\)$/ {\"\2\", (void *) \&\2},/p'" lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n -e 's/^: \([[^ ]]*\)[[ ]]*$/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([[^ ]]*\) \(lib[[^ ]]*\)$/ {\"\2\", (void *) \&\2},/p' -e 's/^$symcode* \([[^ ]]*\) \([[^ ]]*\)$/ {\"lib\2\", (void *) \&\2},/p'" # Handle CRLF in mingw tool chain opt_cr= case $build_os in mingw*) opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp ;; esac # Try without a prefix underscore, then with it. for ac_symprfx in "" "_"; do # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol. symxfrm="\\1 $ac_symprfx\\2 \\2" # Write the raw and C identifiers. if test "$lt_cv_nm_interface" = "MS dumpbin"; then # Fake it for dumpbin and say T for any non-static function # and D for any global variable. # Also find C++ and __fastcall symbols from MSVC++, # which start with @ or ?. lt_cv_sys_global_symbol_pipe="$AWK ['"\ " {last_section=section; section=\$ 3};"\ " /^COFF SYMBOL TABLE/{for(i in hide) delete hide[i]};"\ " /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\ " \$ 0!~/External *\|/{next};"\ " / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\ " {if(hide[section]) next};"\ " {f=0}; \$ 0~/\(\).*\|/{f=1}; {printf f ? \"T \" : \"D \"};"\ " {split(\$ 0, a, /\||\r/); split(a[2], s)};"\ " s[1]~/^[@?]/{print s[1], s[1]; next};"\ " s[1]~prfx {split(s[1],t,\"@\"); print t[1], substr(t[1],length(prfx))}"\ " ' prfx=^$ac_symprfx]" else lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[[ ]]\($symcode$symcode*\)[[ ]][[ ]]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'" fi lt_cv_sys_global_symbol_pipe="$lt_cv_sys_global_symbol_pipe | sed '/ __gnu_lto/d'" # Check to see that the pipe works correctly. pipe_works=no rm -f conftest* cat > conftest.$ac_ext <<_LT_EOF #ifdef __cplusplus extern "C" { #endif char nm_test_var; void nm_test_func(void); void nm_test_func(void){} #ifdef __cplusplus } #endif int main(){nm_test_var='a';nm_test_func();return(0);} _LT_EOF if AC_TRY_EVAL(ac_compile); then # Now try to grab the symbols. nlist=conftest.nm if AC_TRY_EVAL(NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist) && test -s "$nlist"; then # Try sorting and uniquifying the output. if sort "$nlist" | uniq > "$nlist"T; then mv -f "$nlist"T "$nlist" else rm -f "$nlist"T fi # Make sure that we snagged all the symbols we need. if $GREP ' nm_test_var$' "$nlist" >/dev/null; then if $GREP ' nm_test_func$' "$nlist" >/dev/null; then cat <<_LT_EOF > conftest.$ac_ext /* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ #if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE) /* DATA imports from DLLs on WIN32 con't be const, because runtime relocations are performed -- see ld's documentation on pseudo-relocs. */ # define LT@&t@_DLSYM_CONST #elif defined(__osf__) /* This system does not cope well with relocations in const data. */ # define LT@&t@_DLSYM_CONST #else # define LT@&t@_DLSYM_CONST const #endif #ifdef __cplusplus extern "C" { #endif _LT_EOF # Now generate the symbol file. eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext' cat <<_LT_EOF >> conftest.$ac_ext /* The mapping between symbol names and symbols. */ LT@&t@_DLSYM_CONST struct { const char *name; void *address; } lt__PROGRAM__LTX_preloaded_symbols[[]] = { { "@PROGRAM@", (void *) 0 }, _LT_EOF $SED "s/^$symcode$symcode* \(.*\) \(.*\)$/ {\"\2\", (void *) \&\2},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext cat <<\_LT_EOF >> conftest.$ac_ext {0, (void *) 0} }; /* This works around a problem in FreeBSD linker */ #ifdef FREEBSD_WORKAROUND static const void *lt_preloaded_setup() { return lt__PROGRAM__LTX_preloaded_symbols; } #endif #ifdef __cplusplus } #endif _LT_EOF # Now try linking the two files. mv conftest.$ac_objext conftstm.$ac_objext lt_globsym_save_LIBS=$LIBS lt_globsym_save_CFLAGS=$CFLAGS LIBS="conftstm.$ac_objext" CFLAGS="$CFLAGS$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)" if AC_TRY_EVAL(ac_link) && test -s conftest${ac_exeext}; then pipe_works=yes fi LIBS=$lt_globsym_save_LIBS CFLAGS=$lt_globsym_save_CFLAGS else echo "cannot find nm_test_func in $nlist" >&AS_MESSAGE_LOG_FD fi else echo "cannot find nm_test_var in $nlist" >&AS_MESSAGE_LOG_FD fi else echo "cannot run $lt_cv_sys_global_symbol_pipe" >&AS_MESSAGE_LOG_FD fi else echo "$progname: failed program was:" >&AS_MESSAGE_LOG_FD cat conftest.$ac_ext >&5 fi rm -rf conftest* conftst* # Do not use the global_symbol_pipe unless it works. if test "$pipe_works" = yes; then break else lt_cv_sys_global_symbol_pipe= fi done ]) if test -z "$lt_cv_sys_global_symbol_pipe"; then lt_cv_sys_global_symbol_to_cdecl= fi if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then AC_MSG_RESULT(failed) else AC_MSG_RESULT(ok) fi # Response file support. if test "$lt_cv_nm_interface" = "MS dumpbin"; then nm_file_list_spec='@' elif $NM --help 2>/dev/null | grep '[[@]]FILE' >/dev/null; then nm_file_list_spec='@' fi _LT_DECL([global_symbol_pipe], [lt_cv_sys_global_symbol_pipe], [1], [Take the output of nm and produce a listing of raw symbols and C names]) _LT_DECL([global_symbol_to_cdecl], [lt_cv_sys_global_symbol_to_cdecl], [1], [Transform the output of nm in a proper C declaration]) _LT_DECL([global_symbol_to_c_name_address], [lt_cv_sys_global_symbol_to_c_name_address], [1], [Transform the output of nm in a C name address pair]) _LT_DECL([global_symbol_to_c_name_address_lib_prefix], [lt_cv_sys_global_symbol_to_c_name_address_lib_prefix], [1], [Transform the output of nm in a C name address pair when lib prefix is needed]) _LT_DECL([], [nm_file_list_spec], [1], [Specify filename containing input files for $NM]) ]) # _LT_CMD_GLOBAL_SYMBOLS # _LT_COMPILER_PIC([TAGNAME]) # --------------------------- m4_defun([_LT_COMPILER_PIC], [m4_require([_LT_TAG_COMPILER])dnl _LT_TAGVAR(lt_prog_compiler_wl, $1)= _LT_TAGVAR(lt_prog_compiler_pic, $1)= _LT_TAGVAR(lt_prog_compiler_static, $1)= m4_if([$1], [CXX], [ # C++ specific cases for pic, static, wl, etc. if test "$GXX" = yes; then _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' case $host_os in aix*) # All AIX code is PIC. if test "$host_cpu" = ia64; then # AIX 5 now supports IA64 processor _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; m68k) # FIXME: we need at least 68020 code to build shared libraries, but # adding the `-m68020' flag to GCC prevents building anything better, # like `-m68040'. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4' ;; esac ;; beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) # PIC is the default for these OSes. ;; mingw* | cygwin* | os2* | pw32* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). # Although the cygwin gcc ignores -fPIC, still need this for old-style # (--disable-auto-import) libraries m4_if([$1], [GCJ], [], [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) ;; darwin* | rhapsody*) # PIC is the default on this platform # Common symbols not allowed in MH_DYLIB files _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' ;; *djgpp*) # DJGPP does not support shared libraries at all _LT_TAGVAR(lt_prog_compiler_pic, $1)= ;; haiku*) # PIC is the default for Haiku. # The "-static" flag exists, but is broken. _LT_TAGVAR(lt_prog_compiler_static, $1)= ;; interix[[3-9]]*) # Interix 3.x gcc -fpic/-fPIC options generate broken code. # Instead, we relocate shared libraries at runtime. ;; sysv4*MP*) if test -d /usr/nec; then _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic fi ;; hpux*) # PIC is the default for 64-bit PA HP-UX, but not for 32-bit # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag # sets the default TLS model and affects inlining. case $host_cpu in hppa*64*) ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; esac ;; *qnx* | *nto*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; esac else case $host_os in aix[[4-9]]*) # All AIX code is PIC. if test "$host_cpu" = ia64; then # AIX 5 now supports IA64 processor _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' else _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp' fi ;; chorus*) case $cc_basename in cxch68*) # Green Hills C++ Compiler # _LT_TAGVAR(lt_prog_compiler_static, $1)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a" ;; esac ;; mingw* | cygwin* | os2* | pw32* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). m4_if([$1], [GCJ], [], [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) ;; dgux*) case $cc_basename in ec++*) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' ;; ghcx*) # Green Hills C++ Compiler _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' ;; *) ;; esac ;; freebsd* | dragonfly*) # FreeBSD uses GNU C++ ;; hpux9* | hpux10* | hpux11*) case $cc_basename in CC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive' if test "$host_cpu" != ia64; then _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' fi ;; aCC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive' case $host_cpu in hppa*64*|ia64*) # +Z the default ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' ;; esac ;; *) ;; esac ;; interix*) # This is c89, which is MS Visual C++ (no shared libs) # Anyone wants to do a port? ;; irix5* | irix6* | nonstopux*) case $cc_basename in CC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' # CC pic flag -KPIC is the default. ;; *) ;; esac ;; linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) case $cc_basename in KCC*) # KAI C++ Compiler _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; ecpc* ) # old Intel C++ for x86_64 which still supported -KPIC. _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; icpc* ) # Intel C++, used to be incompatible with GCC. # ICC 10 doesn't accept -KPIC any more. _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; pgCC* | pgcpp*) # Portland Group C++ compiler _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; cxx*) # Compaq C++ # Make sure the PIC flag is empty. It appears that all Alpha # Linux and Compaq Tru64 Unix objects are PIC. _LT_TAGVAR(lt_prog_compiler_pic, $1)= _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; xlc* | xlC* | bgxl[[cC]]* | mpixl[[cC]]*) # IBM XL 8.0, 9.0 on PPC and BlueGene _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink' ;; *) case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C++ 5.9 _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' ;; esac ;; esac ;; lynxos*) ;; m88k*) ;; mvs*) case $cc_basename in cxx*) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-W c,exportall' ;; *) ;; esac ;; netbsd* | netbsdelf*-gnu) ;; *qnx* | *nto*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' ;; osf3* | osf4* | osf5*) case $cc_basename in KCC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,' ;; RCC*) # Rational C++ 2.4.1 _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' ;; cxx*) # Digital/Compaq C++ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # Make sure the PIC flag is empty. It appears that all Alpha # Linux and Compaq Tru64 Unix objects are PIC. _LT_TAGVAR(lt_prog_compiler_pic, $1)= _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; *) ;; esac ;; psos*) ;; solaris*) case $cc_basename in CC* | sunCC*) # Sun C++ 4.2, 5.x and Centerline C++ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' ;; gcx*) # Green Hills C++ Compiler _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' ;; *) ;; esac ;; sunos4*) case $cc_basename in CC*) # Sun C++ 4.x _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; lcc*) # Lucid _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' ;; *) ;; esac ;; sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) case $cc_basename in CC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; esac ;; tandem*) case $cc_basename in NCC*) # NonStop-UX NCC 3.20 _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' ;; *) ;; esac ;; vxworks*) ;; *) _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no ;; esac fi ], [ if test "$GCC" = yes; then _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' case $host_os in aix*) # All AIX code is PIC. if test "$host_cpu" = ia64; then # AIX 5 now supports IA64 processor _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; m68k) # FIXME: we need at least 68020 code to build shared libraries, but # adding the `-m68020' flag to GCC prevents building anything better, # like `-m68040'. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4' ;; esac ;; beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) # PIC is the default for these OSes. ;; mingw* | cygwin* | pw32* | os2* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). # Although the cygwin gcc ignores -fPIC, still need this for old-style # (--disable-auto-import) libraries m4_if([$1], [GCJ], [], [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) ;; darwin* | rhapsody*) # PIC is the default on this platform # Common symbols not allowed in MH_DYLIB files _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' ;; haiku*) # PIC is the default for Haiku. # The "-static" flag exists, but is broken. _LT_TAGVAR(lt_prog_compiler_static, $1)= ;; hpux*) # PIC is the default for 64-bit PA HP-UX, but not for 32-bit # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag # sets the default TLS model and affects inlining. case $host_cpu in hppa*64*) # +Z the default ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; esac ;; interix[[3-9]]*) # Interix 3.x gcc -fpic/-fPIC options generate broken code. # Instead, we relocate shared libraries at runtime. ;; msdosdjgpp*) # Just because we use GCC doesn't mean we suddenly get shared libraries # on systems that don't support them. _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no enable_shared=no ;; *nto* | *qnx*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' ;; sysv4*MP*) if test -d /usr/nec; then _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic fi ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; esac case $cc_basename in nvcc*) # Cuda Compiler Driver 2.2 _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Xlinker ' if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then _LT_TAGVAR(lt_prog_compiler_pic, $1)="-Xcompiler $_LT_TAGVAR(lt_prog_compiler_pic, $1)" fi ;; esac else # PORTME Check for flag to pass linker flags through the system compiler. case $host_os in aix*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' if test "$host_cpu" = ia64; then # AIX 5 now supports IA64 processor _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' else _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp' fi ;; mingw* | cygwin* | pw32* | os2* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). m4_if([$1], [GCJ], [], [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) ;; hpux9* | hpux10* | hpux11*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but # not for PA HP-UX. case $host_cpu in hppa*64*|ia64*) # +Z the default ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' ;; esac # Is there a better lt_prog_compiler_static that works with the bundled CC? _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive' ;; irix5* | irix6* | nonstopux*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # PIC (with -KPIC) is the default. _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) case $cc_basename in # old Intel for x86_64 which still supported -KPIC. ecc*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; # icc used to be incompatible with GCC. # ICC 10 doesn't accept -KPIC any more. icc* | ifort*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; # Lahey Fortran 8.1. lf95*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='--shared' _LT_TAGVAR(lt_prog_compiler_static, $1)='--static' ;; nagfor*) # NAG Fortran compiler _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,-Wl,,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*) # Portland Group compilers (*not* the Pentium gcc compiler, # which looks to be a dead project) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; ccc*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # All Alpha code is PIC. _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; xl* | bgxl* | bgf* | mpixl*) # IBM XL C 8.0/Fortran 10.1, 11.1 on PPC and BlueGene _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink' ;; *) case `$CC -V 2>&1 | sed 5q` in *Sun\ Ceres\ Fortran* | *Sun*Fortran*\ [[1-7]].* | *Sun*Fortran*\ 8.[[0-3]]*) # Sun Fortran 8.3 passes all unrecognized flags to the linker _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='' ;; *Sun\ F* | *Sun*Fortran*) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' ;; *Sun\ C*) # Sun C 5.9 _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' ;; *Intel*\ [[CF]]*Compiler*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; *Portland\ Group*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; esac ;; esac ;; newsos6) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; *nto* | *qnx*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' ;; osf3* | osf4* | osf5*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # All OSF/1 code is PIC. _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; rdos*) _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; solaris*) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' case $cc_basename in f77* | f90* | f95* | sunf77* | sunf90* | sunf95*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ';; *) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,';; esac ;; sunos4*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; sysv4 | sysv4.2uw2* | sysv4.3*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; sysv4*MP*) if test -d /usr/nec ;then _LT_TAGVAR(lt_prog_compiler_pic, $1)='-Kconform_pic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' fi ;; sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; unicos*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no ;; uts4*) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; *) _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no ;; esac fi ]) case $host_os in # For platforms which do not support PIC, -DPIC is meaningless: *djgpp*) _LT_TAGVAR(lt_prog_compiler_pic, $1)= ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)="$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])" ;; esac AC_CACHE_CHECK([for $compiler option to produce PIC], [_LT_TAGVAR(lt_cv_prog_compiler_pic, $1)], [_LT_TAGVAR(lt_cv_prog_compiler_pic, $1)=$_LT_TAGVAR(lt_prog_compiler_pic, $1)]) _LT_TAGVAR(lt_prog_compiler_pic, $1)=$_LT_TAGVAR(lt_cv_prog_compiler_pic, $1) # # Check to make sure the PIC flag actually works. # if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then _LT_COMPILER_OPTION([if $compiler PIC flag $_LT_TAGVAR(lt_prog_compiler_pic, $1) works], [_LT_TAGVAR(lt_cv_prog_compiler_pic_works, $1)], [$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])], [], [case $_LT_TAGVAR(lt_prog_compiler_pic, $1) in "" | " "*) ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)=" $_LT_TAGVAR(lt_prog_compiler_pic, $1)" ;; esac], [_LT_TAGVAR(lt_prog_compiler_pic, $1)= _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no]) fi _LT_TAGDECL([pic_flag], [lt_prog_compiler_pic], [1], [Additional compiler flags for building library objects]) _LT_TAGDECL([wl], [lt_prog_compiler_wl], [1], [How to pass a linker flag through the compiler]) # # Check to make sure the static flag actually works. # wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1) eval lt_tmp_static_flag=\"$_LT_TAGVAR(lt_prog_compiler_static, $1)\" _LT_LINKER_OPTION([if $compiler static flag $lt_tmp_static_flag works], _LT_TAGVAR(lt_cv_prog_compiler_static_works, $1), $lt_tmp_static_flag, [], [_LT_TAGVAR(lt_prog_compiler_static, $1)=]) _LT_TAGDECL([link_static_flag], [lt_prog_compiler_static], [1], [Compiler flag to prevent dynamic linking]) ])# _LT_COMPILER_PIC # _LT_LINKER_SHLIBS([TAGNAME]) # ---------------------------- # See if the linker supports building shared libraries. m4_defun([_LT_LINKER_SHLIBS], [AC_REQUIRE([LT_PATH_LD])dnl AC_REQUIRE([LT_PATH_NM])dnl m4_require([_LT_PATH_MANIFEST_TOOL])dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_EGREP])dnl m4_require([_LT_DECL_SED])dnl m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl m4_require([_LT_TAG_COMPILER])dnl AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries]) m4_if([$1], [CXX], [ _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'] case $host_os in aix[[4-9]]*) # If we're using GNU nm, then we don't want the "-C" option. # -C means demangle to AIX nm, but means don't demangle with GNU nm # Also, AIX nm treats weak defined symbols like other global defined # symbols, whereas GNU nm marks them as "W". if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' else _LT_TAGVAR(export_symbols_cmds, $1)='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' fi ;; pw32*) _LT_TAGVAR(export_symbols_cmds, $1)="$ltdll_cmds" ;; cygwin* | mingw* | cegcc*) case $cc_basename in cl*) _LT_TAGVAR(exclude_expsyms, $1)='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' ;; *) _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols' _LT_TAGVAR(exclude_expsyms, $1)=['[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname'] ;; esac ;; linux* | k*bsd*-gnu | gnu*) _LT_TAGVAR(link_all_deplibs, $1)=no ;; *) _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' ;; esac ], [ runpath_var= _LT_TAGVAR(allow_undefined_flag, $1)= _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(archive_cmds, $1)= _LT_TAGVAR(archive_expsym_cmds, $1)= _LT_TAGVAR(compiler_needs_object, $1)=no _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' _LT_TAGVAR(hardcode_automatic, $1)=no _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(hardcode_libdir_separator, $1)= _LT_TAGVAR(hardcode_minus_L, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported _LT_TAGVAR(inherit_rpath, $1)=no _LT_TAGVAR(link_all_deplibs, $1)=unknown _LT_TAGVAR(module_cmds, $1)= _LT_TAGVAR(module_expsym_cmds, $1)= _LT_TAGVAR(old_archive_from_new_cmds, $1)= _LT_TAGVAR(old_archive_from_expsyms_cmds, $1)= _LT_TAGVAR(thread_safe_flag_spec, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= # include_expsyms should be a list of space-separated symbols to be *always* # included in the symbol list _LT_TAGVAR(include_expsyms, $1)= # exclude_expsyms can be an extended regexp of symbols to exclude # it will be wrapped by ` (' and `)$', so one must not match beginning or # end of line. Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc', # as well as any symbol that contains `d'. _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'] # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out # platforms (ab)use it in PIC code, but their linkers get confused if # the symbol is explicitly referenced. Since portable code cannot # rely on this symbol name, it's probably fine to never include it in # preloaded symbol tables. # Exclude shared library initialization/finalization symbols. dnl Note also adjust exclude_expsyms for C++ above. extract_expsyms_cmds= case $host_os in cygwin* | mingw* | pw32* | cegcc*) # FIXME: the MSVC++ port hasn't been tested in a loooong time # When not using gcc, we currently assume that we are using # Microsoft Visual C++. if test "$GCC" != yes; then with_gnu_ld=no fi ;; interix*) # we just hope/assume this is gcc and not c89 (= MSVC++) with_gnu_ld=yes ;; openbsd*) with_gnu_ld=no ;; linux* | k*bsd*-gnu | gnu*) _LT_TAGVAR(link_all_deplibs, $1)=no ;; esac _LT_TAGVAR(ld_shlibs, $1)=yes # On some targets, GNU ld is compatible enough with the native linker # that we're better off using the native interface for both. lt_use_gnu_ld_interface=no if test "$with_gnu_ld" = yes; then case $host_os in aix*) # The AIX port of GNU ld has always aspired to compatibility # with the native linker. However, as the warning in the GNU ld # block says, versions before 2.19.5* couldn't really create working # shared libraries, regardless of the interface used. case `$LD -v 2>&1` in *\ \(GNU\ Binutils\)\ 2.19.5*) ;; *\ \(GNU\ Binutils\)\ 2.[[2-9]]*) ;; *\ \(GNU\ Binutils\)\ [[3-9]]*) ;; *) lt_use_gnu_ld_interface=yes ;; esac ;; *) lt_use_gnu_ld_interface=yes ;; esac fi if test "$lt_use_gnu_ld_interface" = yes; then # If archive_cmds runs LD, not CC, wlarc should be empty wlarc='${wl}' # Set some defaults for GNU ld with shared library support. These # are reset later if shared libraries are not supported. Putting them # here allows them to be overridden if necessary. runpath_var=LD_RUN_PATH _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' # ancient GNU ld didn't support --whole-archive et. al. if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' else _LT_TAGVAR(whole_archive_flag_spec, $1)= fi supports_anon_versioning=no case `$LD -v 2>&1` in *GNU\ gold*) supports_anon_versioning=yes ;; *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.10.*) ;; # catch versions < 2.11 *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ... *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ... *\ 2.11.*) ;; # other 2.11 versions *) supports_anon_versioning=yes ;; esac # See if GNU ld supports shared libraries. case $host_os in aix[[3-9]]*) # On AIX/PPC, the GNU linker is very broken if test "$host_cpu" != ia64; then _LT_TAGVAR(ld_shlibs, $1)=no cat <<_LT_EOF 1>&2 *** Warning: the GNU linker, at least up to release 2.19, is reported *** to be unable to reliably create shared libraries on AIX. *** Therefore, libtool is disabling shared libraries support. If you *** really care for shared libraries, you may want to install binutils *** 2.20 or above, or modify your PATH so that a non-GNU linker is found. *** You will then need to restart the configuration process. _LT_EOF fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='' ;; m68k) _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_minus_L, $1)=yes ;; esac ;; beos*) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(allow_undefined_flag, $1)=unsupported # Joseph Beckenbach says some releases of gcc # support --undefined. This deserves some investigation. FIXME _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; cygwin* | mingw* | pw32* | cegcc*) # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless, # as there is no search path for DLLs. _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-all-symbols' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols' _LT_TAGVAR(exclude_expsyms, $1)=['[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname'] if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' # If the export-symbols file already is a .def file (1st line # is EXPORTS), use it as is; otherwise, prepend... _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then cp $export_symbols $output_objdir/$soname.def; else echo EXPORTS > $output_objdir/$soname.def; cat $export_symbols >> $output_objdir/$soname.def; fi~ $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; haiku*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(link_all_deplibs, $1)=yes ;; interix[[3-9]]*) _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. # Instead, shared libraries are loaded at an image base (0x10000000 by # default) and relocated if they conflict, which is a slow very memory # consuming and fragmenting process. To avoid this, we pick a random, # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link # time. Moving up from 0x10000000 also allows more sbrk(2) space. _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' ;; gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu) tmp_diet=no if test "$host_os" = linux-dietlibc; then case $cc_basename in diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn) esac fi if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \ && test "$tmp_diet" = no then tmp_addflag=' $pic_flag' tmp_sharedflag='-shared' case $cc_basename,$host_cpu in pgcc*) # Portland Group C compiler _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' tmp_addflag=' $pic_flag' ;; pgf77* | pgf90* | pgf95* | pgfortran*) # Portland Group f77 and f90 compilers _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' tmp_addflag=' $pic_flag -Mnomain' ;; ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64 tmp_addflag=' -i_dynamic' ;; efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64 tmp_addflag=' -i_dynamic -nofor_main' ;; ifc* | ifort*) # Intel Fortran compiler tmp_addflag=' -nofor_main' ;; lf95*) # Lahey Fortran 8.1 _LT_TAGVAR(whole_archive_flag_spec, $1)= tmp_sharedflag='--shared' ;; xl[[cC]]* | bgxl[[cC]]* | mpixl[[cC]]*) # IBM XL C 8.0 on PPC (deal with xlf below) tmp_sharedflag='-qmkshrobj' tmp_addflag= ;; nvcc*) # Cuda Compiler Driver 2.2 _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' _LT_TAGVAR(compiler_needs_object, $1)=yes ;; esac case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C 5.9 _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' _LT_TAGVAR(compiler_needs_object, $1)=yes tmp_sharedflag='-G' ;; *Sun\ F*) # Sun Fortran 8.3 tmp_sharedflag='-G' ;; esac _LT_TAGVAR(archive_cmds, $1)='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' if test "x$supports_anon_versioning" = xyes; then _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib' fi case $cc_basename in xlf* | bgf* | bgxlf* | mpixlf*) # IBM XL Fortran 10.1 on PPC cannot create shared libs itself _LT_TAGVAR(whole_archive_flag_spec, $1)='--whole-archive$convenience --no-whole-archive' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' _LT_TAGVAR(archive_cmds, $1)='$LD -shared $libobjs $deplibs $linker_flags -soname $soname -o $lib' if test "x$supports_anon_versioning" = xyes; then _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $LD -shared $libobjs $deplibs $linker_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib' fi ;; esac else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; netbsd* | netbsdelf*-gnu) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' wlarc= else _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' fi ;; solaris*) if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then _LT_TAGVAR(ld_shlibs, $1)=no cat <<_LT_EOF 1>&2 *** Warning: The releases 2.8.* of the GNU linker cannot reliably *** create shared libraries on Solaris systems. Therefore, libtool *** is disabling shared libraries support. We urge you to upgrade GNU *** binutils to release 2.9.1 or newer. Another option is to modify *** your PATH or compiler configuration so that the native linker is *** used, and then restart. _LT_EOF elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) case `$LD -v 2>&1` in *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.1[[0-5]].*) _LT_TAGVAR(ld_shlibs, $1)=no cat <<_LT_EOF 1>&2 *** Warning: Releases of the GNU linker prior to 2.16.91.0.3 can not *** reliably create shared libraries on SCO systems. Therefore, libtool *** is disabling shared libraries support. We urge you to upgrade GNU *** binutils to release 2.16.91.0.3 or newer. Another option is to modify *** your PATH or compiler configuration so that the native linker is *** used, and then restart. _LT_EOF ;; *) # For security reasons, it is highly recommended that you always # use absolute paths for naming shared libraries, and exclude the # DT_RUNPATH tag from executables and libraries. But doing so # requires that you compile everything twice, which is a pain. if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac ;; sunos4*) _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags' wlarc= _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac if test "$_LT_TAGVAR(ld_shlibs, $1)" = no; then runpath_var= _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= fi else # PORTME fill in a description of your system's linker (not GNU ld) case $host_os in aix3*) _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=yes _LT_TAGVAR(archive_expsym_cmds, $1)='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname' # Note: this linker hardcodes the directories in LIBPATH if there # are no directories specified by -L. _LT_TAGVAR(hardcode_minus_L, $1)=yes if test "$GCC" = yes && test -z "$lt_prog_compiler_static"; then # Neither direct hardcoding nor static linking is supported with a # broken collect2. _LT_TAGVAR(hardcode_direct, $1)=unsupported fi ;; aix[[4-9]]*) if test "$host_cpu" = ia64; then # On IA64, the linker does run time linking by default, so we don't # have to do anything special. aix_use_runtimelinking=no exp_sym_flag='-Bexport' no_entry_flag="" else # If we're using GNU nm, then we don't want the "-C" option. # -C means demangle to AIX nm, but means don't demangle with GNU nm # Also, AIX nm treats weak defined symbols like other global # defined symbols, whereas GNU nm marks them as "W". if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' else _LT_TAGVAR(export_symbols_cmds, $1)='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' fi aix_use_runtimelinking=no # Test if we are trying to use run time linking or normal # AIX style linking. If -brtl is somewhere in LDFLAGS, we # need to do runtime linking. case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*) for ld_flag in $LDFLAGS; do if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then aix_use_runtimelinking=yes break fi done ;; esac exp_sym_flag='-bexport' no_entry_flag='-bnoentry' fi # When large executables or shared objects are built, AIX ld can # have problems creating the table of contents. If linking a library # or program results in "error TOC overflow" add -mminimal-toc to # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. _LT_TAGVAR(archive_cmds, $1)='' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(hardcode_libdir_separator, $1)=':' _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(file_list_spec, $1)='${wl}-f,' if test "$GCC" = yes; then case $host_os in aix4.[[012]]|aix4.[[012]].*) # We only want to do this on AIX 4.2 and lower, the check # below for broken collect2 doesn't work under 4.3+ collect2name=`${CC} -print-prog-name=collect2` if test -f "$collect2name" && strings "$collect2name" | $GREP resolve_lib_name >/dev/null then # We have reworked collect2 : else # We have old collect2 _LT_TAGVAR(hardcode_direct, $1)=unsupported # It fails to find uninstalled libraries when the uninstalled # path is not listed in the libpath. Setting hardcode_minus_L # to unsupported forces relinking _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)= fi ;; esac shared_flag='-shared' if test "$aix_use_runtimelinking" = yes; then shared_flag="$shared_flag "'${wl}-G' fi _LT_TAGVAR(link_all_deplibs, $1)=no else # not using gcc if test "$host_cpu" = ia64; then # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release # chokes on -Wl,-G. The following line is correct: shared_flag='-G' else if test "$aix_use_runtimelinking" = yes; then shared_flag='${wl}-G' else shared_flag='${wl}-bM:SRE' fi fi fi _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-bexpall' # It seems that -bexpall does not export symbols beginning with # underscore (_), so it is better to generate a list of symbols to export. _LT_TAGVAR(always_export_symbols, $1)=yes if test "$aix_use_runtimelinking" = yes; then # Warning - without using the other runtime loading flags (-brtl), # -berok will link without error, but may produce a broken library. _LT_TAGVAR(allow_undefined_flag, $1)='-berok' # Determine the default libpath from the value encoded in an # empty executable. _LT_SYS_MODULE_PATH_AIX([$1]) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then func_echo_all "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" else if test "$host_cpu" = ia64; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $libdir:/usr/lib:/lib' _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs" _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols" else # Determine the default libpath from the value encoded in an # empty executable. _LT_SYS_MODULE_PATH_AIX([$1]) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" # Warning - without using the other run time loading flags, # -berok will link without error, but may produce a broken library. _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-bernotok' _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-berok' if test "$with_gnu_ld" = yes; then # We only use this code for GNU lds that support --whole-archive. _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive$convenience ${wl}--no-whole-archive' else # Exported symbols can be pulled into shared objects from archives _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience' fi _LT_TAGVAR(archive_cmds_need_lc, $1)=yes # This is similar to how AIX traditionally builds its shared libraries. _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' fi fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='' ;; m68k) _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_minus_L, $1)=yes ;; esac ;; bsdi[[45]]*) _LT_TAGVAR(export_dynamic_flag_spec, $1)=-rdynamic ;; cygwin* | mingw* | pw32* | cegcc*) # When not using gcc, we currently assume that we are using # Microsoft Visual C++. # hardcode_libdir_flag_spec is actually meaningless, as there is # no search path for DLLs. case $cc_basename in cl*) # Native MSVC _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=yes _LT_TAGVAR(file_list_spec, $1)='@' # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. shrext_cmds=".dll" # FIXME: Setting linknames here is a bad hack. _LT_TAGVAR(archive_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-dll~linknames=' _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then sed -n -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' -e '1\\\!p' < $export_symbols > $output_objdir/$soname.exp; else sed -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' < $export_symbols > $output_objdir/$soname.exp; fi~ $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ linknames=' # The linker will not automatically build a static lib if we build a DLL. # _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes _LT_TAGVAR(exclude_expsyms, $1)='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1,DATA/'\'' | $SED -e '\''/^[[AITW]][[ ]]/s/.*[[ ]]//'\'' | sort | uniq > $export_symbols' # Don't use ranlib _LT_TAGVAR(old_postinstall_cmds, $1)='chmod 644 $oldlib' _LT_TAGVAR(postlink_cmds, $1)='lt_outputfile="@OUTPUT@"~ lt_tool_outputfile="@TOOL_OUTPUT@"~ case $lt_outputfile in *.exe|*.EXE) ;; *) lt_outputfile="$lt_outputfile.exe" lt_tool_outputfile="$lt_tool_outputfile.exe" ;; esac~ if test "$MANIFEST_TOOL" != ":" && test -f "$lt_outputfile.manifest"; then $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; $RM "$lt_outputfile.manifest"; fi' ;; *) # Assume MSVC wrapper _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. shrext_cmds=".dll" # FIXME: Setting linknames here is a bad hack. _LT_TAGVAR(archive_cmds, $1)='$CC -o $lib $libobjs $compiler_flags `func_echo_all "$deplibs" | $SED '\''s/ -lc$//'\''` -link -dll~linknames=' # The linker will automatically build a .lib file if we build a DLL. _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' # FIXME: Should let the user specify the lib program. _LT_TAGVAR(old_archive_cmds, $1)='lib -OUT:$oldlib$oldobjs$old_deplibs' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes ;; esac ;; darwin* | rhapsody*) _LT_DARWIN_LINKER_FEATURES($1) ;; dgux*) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor # support. Future versions do this automatically, but an explicit c++rt0.o # does not break anything, and helps significantly (at the cost of a little # extra space). freebsd2.2*) _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; # Unfortunately, older versions of FreeBSD 2 do not have this feature. freebsd2.*) _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; # FreeBSD 3 and greater uses gcc -shared to do shared libraries. freebsd* | dragonfly*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; hpux9*) if test "$GCC" = yes; then _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared $pic_flag ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' else _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(hardcode_direct, $1)=yes # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' ;; hpux10*) if test "$GCC" = yes && test "$with_gnu_ld" = no; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' else _LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' fi if test "$with_gnu_ld" = no; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. _LT_TAGVAR(hardcode_minus_L, $1)=yes fi ;; hpux11*) if test "$GCC" = yes && test "$with_gnu_ld" = no; then case $host_cpu in hppa*64*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' ;; ia64*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' ;; esac else case $host_cpu in hppa*64*) _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' ;; ia64*) _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' ;; *) m4_if($1, [], [ # Older versions of the 11.00 compiler do not understand -b yet # (HP92453-01 A.11.01.20 doesn't, HP92453-01 B.11.X.35175-35176.GP does) _LT_LINKER_OPTION([if $CC understands -b], _LT_TAGVAR(lt_cv_prog_compiler__b, $1), [-b], [_LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'], [_LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'])], [_LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags']) ;; esac fi if test "$with_gnu_ld" = no; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: case $host_cpu in hppa*64*|ia64*) _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *) _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. _LT_TAGVAR(hardcode_minus_L, $1)=yes ;; esac fi ;; irix5* | irix6* | nonstopux*) if test "$GCC" = yes; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' # Try to use the -exported_symbol ld option, if it does not # work, assume that -exports_file does not work either and # implicitly export all symbols. # This should be the same for all languages, so no per-tag cache variable. AC_CACHE_CHECK([whether the $host_os linker accepts -exported_symbol], [lt_cv_irix_exported_symbol], [save_LDFLAGS="$LDFLAGS" LDFLAGS="$LDFLAGS -shared ${wl}-exported_symbol ${wl}foo ${wl}-update_registry ${wl}/dev/null" AC_LINK_IFELSE( [AC_LANG_SOURCE( [AC_LANG_CASE([C], [[int foo (void) { return 0; }]], [C++], [[int foo (void) { return 0; }]], [Fortran 77], [[ subroutine foo end]], [Fortran], [[ subroutine foo end]])])], [lt_cv_irix_exported_symbol=yes], [lt_cv_irix_exported_symbol=no]) LDFLAGS="$save_LDFLAGS"]) if test "$lt_cv_irix_exported_symbol" = yes; then _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations ${wl}-exports_file ${wl}$export_symbols -o $lib' fi else _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -exports_file $export_symbols -o $lib' fi _LT_TAGVAR(archive_cmds_need_lc, $1)='no' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(inherit_rpath, $1)=yes _LT_TAGVAR(link_all_deplibs, $1)=yes ;; netbsd* | netbsdelf*-gnu) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out else _LT_TAGVAR(archive_cmds, $1)='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; newsos6) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *nto* | *qnx*) ;; openbsd*) if test -f /usr/libexec/ld.so; then _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=yes if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-retain-symbols-file,$export_symbols' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' else case $host_os in openbsd[[01]].* | openbsd2.[[0-7]] | openbsd2.[[0-7]].*) _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' ;; esac fi else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; os2*) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~echo DATA >> $output_objdir/$libname.def~echo " SINGLE NONSHARED" >> $output_objdir/$libname.def~echo EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def' _LT_TAGVAR(old_archive_from_new_cmds, $1)='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def' ;; osf3*) if test "$GCC" = yes; then _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' else _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' fi _LT_TAGVAR(archive_cmds_need_lc, $1)='no' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: ;; osf4* | osf5*) # as osf3* with the addition of -msym flag if test "$GCC" = yes; then _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $pic_flag $libobjs $deplibs $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' else _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~ $CC -shared${allow_undefined_flag} ${wl}-input ${wl}$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib~$RM $lib.exp' # Both c and cxx compiler support -rpath directly _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' fi _LT_TAGVAR(archive_cmds_need_lc, $1)='no' _LT_TAGVAR(hardcode_libdir_separator, $1)=: ;; solaris*) _LT_TAGVAR(no_undefined_flag, $1)=' -z defs' if test "$GCC" = yes; then wlarc='${wl}' _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag ${wl}-z ${wl}text ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -shared $pic_flag ${wl}-z ${wl}text ${wl}-M ${wl}$lib.exp ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' else case `$CC -V 2>&1` in *"Compilers 5.0"*) wlarc='' _LT_TAGVAR(archive_cmds, $1)='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp' ;; *) wlarc='${wl}' _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' ;; esac fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no case $host_os in solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; *) # The compiler driver will combine and reorder linker options, # but understands `-z linker_flag'. GCC discards it without `$wl', # but is careful enough not to reorder. # Supported since Solaris 2.6 (maybe 2.5.1?) if test "$GCC" = yes; then _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract' else _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract' fi ;; esac _LT_TAGVAR(link_all_deplibs, $1)=yes ;; sunos4*) if test "x$host_vendor" = xsequent; then # Use $CC to link under sequent, because it throws in some extra .o # files that make .init and .fini sections work. _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags' else _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags' fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; sysv4) case $host_vendor in sni) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_direct, $1)=yes # is this really true??? ;; siemens) ## LD is ld it makes a PLAMLIB ## CC just makes a GrossModule. _LT_TAGVAR(archive_cmds, $1)='$LD -G -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(reload_cmds, $1)='$CC -r -o $output$reload_objs' _LT_TAGVAR(hardcode_direct, $1)=no ;; motorola) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_direct, $1)=no #Motorola manual says yes, but my tests say they lie ;; esac runpath_var='LD_RUN_PATH' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; sysv4.3*) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(export_dynamic_flag_spec, $1)='-Bexport' ;; sysv4*MP*) if test -d /usr/nec; then _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no runpath_var=LD_RUN_PATH hardcode_runpath_var=yes _LT_TAGVAR(ld_shlibs, $1)=yes fi ;; sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*) _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no runpath_var='LD_RUN_PATH' if test "$GCC" = yes; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' else _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' fi ;; sysv5* | sco3.2v5* | sco5v6*) # Note: We can NOT use -z defs as we might desire, because we do not # link with -lc, and that would cause any symbols used from libc to # always be unresolved, which means just about no library would # ever link correctly. If we're not using GNU ld we use -z text # though, which does catch some bad symbols but isn't as heavy-handed # as -z defs. _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' _LT_TAGVAR(allow_undefined_flag, $1)='${wl}-z,nodefs' _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R,$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=':' _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Bexport' runpath_var='LD_RUN_PATH' if test "$GCC" = yes; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' else _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' fi ;; uts4*) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *) _LT_TAGVAR(ld_shlibs, $1)=no ;; esac if test x$host_vendor = xsni; then case $host in sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Blargedynsym' ;; esac fi fi ]) AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)]) test "$_LT_TAGVAR(ld_shlibs, $1)" = no && can_build_shared=no _LT_TAGVAR(with_gnu_ld, $1)=$with_gnu_ld _LT_DECL([], [libext], [0], [Old archive suffix (normally "a")])dnl _LT_DECL([], [shrext_cmds], [1], [Shared library suffix (normally ".so")])dnl _LT_DECL([], [extract_expsyms_cmds], [2], [The commands to extract the exported symbol list from a shared archive]) # # Do we need to explicitly link libc? # case "x$_LT_TAGVAR(archive_cmds_need_lc, $1)" in x|xyes) # Assume -lc should be added _LT_TAGVAR(archive_cmds_need_lc, $1)=yes if test "$enable_shared" = yes && test "$GCC" = yes; then case $_LT_TAGVAR(archive_cmds, $1) in *'~'*) # FIXME: we may have to deal with multi-command sequences. ;; '$CC '*) # Test whether the compiler implicitly links with -lc since on some # systems, -lgcc has to come before -lc. If gcc already passes -lc # to ld, don't add -lc before -lgcc. AC_CACHE_CHECK([whether -lc should be explicitly linked in], [lt_cv_]_LT_TAGVAR(archive_cmds_need_lc, $1), [$RM conftest* echo "$lt_simple_compile_test_code" > conftest.$ac_ext if AC_TRY_EVAL(ac_compile) 2>conftest.err; then soname=conftest lib=conftest libobjs=conftest.$ac_objext deplibs= wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1) pic_flag=$_LT_TAGVAR(lt_prog_compiler_pic, $1) compiler_flags=-v linker_flags=-v verstring= output_objdir=. libname=conftest lt_save_allow_undefined_flag=$_LT_TAGVAR(allow_undefined_flag, $1) _LT_TAGVAR(allow_undefined_flag, $1)= if AC_TRY_EVAL(_LT_TAGVAR(archive_cmds, $1) 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) then lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1)=no else lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1)=yes fi _LT_TAGVAR(allow_undefined_flag, $1)=$lt_save_allow_undefined_flag else cat conftest.err 1>&5 fi $RM conftest* ]) _LT_TAGVAR(archive_cmds_need_lc, $1)=$lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1) ;; esac fi ;; esac _LT_TAGDECL([build_libtool_need_lc], [archive_cmds_need_lc], [0], [Whether or not to add -lc for building shared libraries]) _LT_TAGDECL([allow_libtool_libs_with_static_runtimes], [enable_shared_with_static_runtimes], [0], [Whether or not to disallow shared libs when runtime libs are static]) _LT_TAGDECL([], [export_dynamic_flag_spec], [1], [Compiler flag to allow reflexive dlopens]) _LT_TAGDECL([], [whole_archive_flag_spec], [1], [Compiler flag to generate shared objects directly from archives]) _LT_TAGDECL([], [compiler_needs_object], [1], [Whether the compiler copes with passing no objects directly]) _LT_TAGDECL([], [old_archive_from_new_cmds], [2], [Create an old-style archive from a shared archive]) _LT_TAGDECL([], [old_archive_from_expsyms_cmds], [2], [Create a temporary old-style archive to link instead of a shared archive]) _LT_TAGDECL([], [archive_cmds], [2], [Commands used to build a shared archive]) _LT_TAGDECL([], [archive_expsym_cmds], [2]) _LT_TAGDECL([], [module_cmds], [2], [Commands used to build a loadable module if different from building a shared archive.]) _LT_TAGDECL([], [module_expsym_cmds], [2]) _LT_TAGDECL([], [with_gnu_ld], [1], [Whether we are building with GNU ld or not]) _LT_TAGDECL([], [allow_undefined_flag], [1], [Flag that allows shared libraries with undefined symbols to be built]) _LT_TAGDECL([], [no_undefined_flag], [1], [Flag that enforces no undefined symbols]) _LT_TAGDECL([], [hardcode_libdir_flag_spec], [1], [Flag to hardcode $libdir into a binary during linking. This must work even if $libdir does not exist]) _LT_TAGDECL([], [hardcode_libdir_separator], [1], [Whether we need a single "-rpath" flag with a separated argument]) _LT_TAGDECL([], [hardcode_direct], [0], [Set to "yes" if using DIR/libNAME${shared_ext} during linking hardcodes DIR into the resulting binary]) _LT_TAGDECL([], [hardcode_direct_absolute], [0], [Set to "yes" if using DIR/libNAME${shared_ext} during linking hardcodes DIR into the resulting binary and the resulting library dependency is "absolute", i.e impossible to change by setting ${shlibpath_var} if the library is relocated]) _LT_TAGDECL([], [hardcode_minus_L], [0], [Set to "yes" if using the -LDIR flag during linking hardcodes DIR into the resulting binary]) _LT_TAGDECL([], [hardcode_shlibpath_var], [0], [Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR into the resulting binary]) _LT_TAGDECL([], [hardcode_automatic], [0], [Set to "yes" if building a shared library automatically hardcodes DIR into the library and all subsequent libraries and executables linked against it]) _LT_TAGDECL([], [inherit_rpath], [0], [Set to yes if linker adds runtime paths of dependent libraries to runtime path list]) _LT_TAGDECL([], [link_all_deplibs], [0], [Whether libtool must link a program against all its dependency libraries]) _LT_TAGDECL([], [always_export_symbols], [0], [Set to "yes" if exported symbols are required]) _LT_TAGDECL([], [export_symbols_cmds], [2], [The commands to list exported symbols]) _LT_TAGDECL([], [exclude_expsyms], [1], [Symbols that should not be listed in the preloaded symbols]) _LT_TAGDECL([], [include_expsyms], [1], [Symbols that must always be exported]) _LT_TAGDECL([], [prelink_cmds], [2], [Commands necessary for linking programs (against libraries) with templates]) _LT_TAGDECL([], [postlink_cmds], [2], [Commands necessary for finishing linking programs]) _LT_TAGDECL([], [file_list_spec], [1], [Specify filename containing input files]) dnl FIXME: Not yet implemented dnl _LT_TAGDECL([], [thread_safe_flag_spec], [1], dnl [Compiler flag to generate thread safe objects]) ])# _LT_LINKER_SHLIBS # _LT_LANG_C_CONFIG([TAG]) # ------------------------ # Ensure that the configuration variables for a C compiler are suitably # defined. These variables are subsequently used by _LT_CONFIG to write # the compiler configuration to `libtool'. m4_defun([_LT_LANG_C_CONFIG], [m4_require([_LT_DECL_EGREP])dnl lt_save_CC="$CC" AC_LANG_PUSH(C) # Source file extension for C test sources. ac_ext=c # Object file extension for compiled C test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # Code to be used in simple compile tests lt_simple_compile_test_code="int some_variable = 0;" # Code to be used in simple link tests lt_simple_link_test_code='int main(){return(0);}' _LT_TAG_COMPILER # Save the default compiler, since it gets overwritten when the other # tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP. compiler_DEFAULT=$CC # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... if test -n "$compiler"; then _LT_COMPILER_NO_RTTI($1) _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_SYS_DYNAMIC_LINKER($1) _LT_LINKER_HARDCODE_LIBPATH($1) LT_SYS_DLOPEN_SELF _LT_CMD_STRIPLIB # Report which library types will actually be built AC_MSG_CHECKING([if libtool supports shared libraries]) AC_MSG_RESULT([$can_build_shared]) AC_MSG_CHECKING([whether to build shared libraries]) test "$can_build_shared" = "no" && enable_shared=no # On AIX, shared libraries and static libraries use the same namespace, and # are all built from PIC. case $host_os in aix3*) test "$enable_shared" = yes && enable_static=no if test -n "$RANLIB"; then archive_cmds="$archive_cmds~\$RANLIB \$lib" postinstall_cmds='$RANLIB $lib' fi ;; aix[[4-9]]*) if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then test "$enable_shared" = yes && enable_static=no fi ;; esac AC_MSG_RESULT([$enable_shared]) AC_MSG_CHECKING([whether to build static libraries]) # Make sure either enable_shared or enable_static is yes. test "$enable_shared" = yes || enable_static=yes AC_MSG_RESULT([$enable_static]) _LT_CONFIG($1) fi AC_LANG_POP CC="$lt_save_CC" ])# _LT_LANG_C_CONFIG # _LT_LANG_CXX_CONFIG([TAG]) # -------------------------- # Ensure that the configuration variables for a C++ compiler are suitably # defined. These variables are subsequently used by _LT_CONFIG to write # the compiler configuration to `libtool'. m4_defun([_LT_LANG_CXX_CONFIG], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_EGREP])dnl m4_require([_LT_PATH_MANIFEST_TOOL])dnl if test -n "$CXX" && ( test "X$CXX" != "Xno" && ( (test "X$CXX" = "Xg++" && `g++ -v >/dev/null 2>&1` ) || (test "X$CXX" != "Xg++"))) ; then AC_PROG_CXXCPP else _lt_caught_CXX_error=yes fi AC_LANG_PUSH(C++) _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(allow_undefined_flag, $1)= _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(archive_expsym_cmds, $1)= _LT_TAGVAR(compiler_needs_object, $1)=no _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(hardcode_libdir_separator, $1)= _LT_TAGVAR(hardcode_minus_L, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported _LT_TAGVAR(hardcode_automatic, $1)=no _LT_TAGVAR(inherit_rpath, $1)=no _LT_TAGVAR(module_cmds, $1)= _LT_TAGVAR(module_expsym_cmds, $1)= _LT_TAGVAR(link_all_deplibs, $1)=unknown _LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds _LT_TAGVAR(reload_flag, $1)=$reload_flag _LT_TAGVAR(reload_cmds, $1)=$reload_cmds _LT_TAGVAR(no_undefined_flag, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no # Source file extension for C++ test sources. ac_ext=cpp # Object file extension for compiled C++ test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # No sense in running all these tests if we already determined that # the CXX compiler isn't working. Some variables (like enable_shared) # are currently assumed to apply to all compilers on this platform, # and will be corrupted by setting them based on a non-working compiler. if test "$_lt_caught_CXX_error" != yes; then # Code to be used in simple compile tests lt_simple_compile_test_code="int some_variable = 0;" # Code to be used in simple link tests lt_simple_link_test_code='int main(int, char *[[]]) { return(0); }' # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_CFLAGS=$CFLAGS lt_save_LD=$LD lt_save_GCC=$GCC GCC=$GXX lt_save_with_gnu_ld=$with_gnu_ld lt_save_path_LD=$lt_cv_path_LD if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx else $as_unset lt_cv_prog_gnu_ld fi if test -n "${lt_cv_path_LDCXX+set}"; then lt_cv_path_LD=$lt_cv_path_LDCXX else $as_unset lt_cv_path_LD fi test -z "${LDCXX+set}" || LD=$LDCXX CC=${CXX-"c++"} CFLAGS=$CXXFLAGS compiler=$CC _LT_TAGVAR(compiler, $1)=$CC _LT_CC_BASENAME([$compiler]) if test -n "$compiler"; then # We don't want -fno-exception when compiling C++ code, so set the # no_builtin_flag separately if test "$GXX" = yes; then _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' else _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)= fi if test "$GXX" = yes; then # Set up default GNU C++ configuration LT_PATH_LD # Check if GNU C++ uses GNU ld as the underlying linker, since the # archiving commands below assume that GNU ld is being used. if test "$with_gnu_ld" = yes; then _LT_TAGVAR(archive_cmds, $1)='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' # If archive_cmds runs LD, not CC, wlarc should be empty # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to # investigate it a little bit more. (MM) wlarc='${wl}' # ancient GNU ld didn't support --whole-archive et. al. if eval "`$CC -print-prog-name=ld` --help 2>&1" | $GREP 'no-whole-archive' > /dev/null; then _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' else _LT_TAGVAR(whole_archive_flag_spec, $1)= fi else with_gnu_ld=no wlarc= # A generic and very simple default shared library creation # command for GNU C++ for the case where it uses the native # linker, instead of GNU ld. If possible, this setting should # overridden to take advantage of the native linker features on # the platform it is being used on. _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' fi # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' else GXX=no with_gnu_ld=no wlarc= fi # PORTME: fill in a description of your system's C++ link characteristics AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries]) _LT_TAGVAR(ld_shlibs, $1)=yes case $host_os in aix3*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; aix[[4-9]]*) if test "$host_cpu" = ia64; then # On IA64, the linker does run time linking by default, so we don't # have to do anything special. aix_use_runtimelinking=no exp_sym_flag='-Bexport' no_entry_flag="" else aix_use_runtimelinking=no # Test if we are trying to use run time linking or normal # AIX style linking. If -brtl is somewhere in LDFLAGS, we # need to do runtime linking. case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*) for ld_flag in $LDFLAGS; do case $ld_flag in *-brtl*) aix_use_runtimelinking=yes break ;; esac done ;; esac exp_sym_flag='-bexport' no_entry_flag='-bnoentry' fi # When large executables or shared objects are built, AIX ld can # have problems creating the table of contents. If linking a library # or program results in "error TOC overflow" add -mminimal-toc to # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. _LT_TAGVAR(archive_cmds, $1)='' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(hardcode_libdir_separator, $1)=':' _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(file_list_spec, $1)='${wl}-f,' if test "$GXX" = yes; then case $host_os in aix4.[[012]]|aix4.[[012]].*) # We only want to do this on AIX 4.2 and lower, the check # below for broken collect2 doesn't work under 4.3+ collect2name=`${CC} -print-prog-name=collect2` if test -f "$collect2name" && strings "$collect2name" | $GREP resolve_lib_name >/dev/null then # We have reworked collect2 : else # We have old collect2 _LT_TAGVAR(hardcode_direct, $1)=unsupported # It fails to find uninstalled libraries when the uninstalled # path is not listed in the libpath. Setting hardcode_minus_L # to unsupported forces relinking _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)= fi esac shared_flag='-shared' if test "$aix_use_runtimelinking" = yes; then shared_flag="$shared_flag "'${wl}-G' fi else # not using gcc if test "$host_cpu" = ia64; then # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release # chokes on -Wl,-G. The following line is correct: shared_flag='-G' else if test "$aix_use_runtimelinking" = yes; then shared_flag='${wl}-G' else shared_flag='${wl}-bM:SRE' fi fi fi _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-bexpall' # It seems that -bexpall does not export symbols beginning with # underscore (_), so it is better to generate a list of symbols to # export. _LT_TAGVAR(always_export_symbols, $1)=yes if test "$aix_use_runtimelinking" = yes; then # Warning - without using the other runtime loading flags (-brtl), # -berok will link without error, but may produce a broken library. _LT_TAGVAR(allow_undefined_flag, $1)='-berok' # Determine the default libpath from the value encoded in an empty # executable. _LT_SYS_MODULE_PATH_AIX([$1]) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then func_echo_all "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" else if test "$host_cpu" = ia64; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $libdir:/usr/lib:/lib' _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs" _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols" else # Determine the default libpath from the value encoded in an # empty executable. _LT_SYS_MODULE_PATH_AIX([$1]) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" # Warning - without using the other run time loading flags, # -berok will link without error, but may produce a broken library. _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-bernotok' _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-berok' if test "$with_gnu_ld" = yes; then # We only use this code for GNU lds that support --whole-archive. _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive$convenience ${wl}--no-whole-archive' else # Exported symbols can be pulled into shared objects from archives _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience' fi _LT_TAGVAR(archive_cmds_need_lc, $1)=yes # This is similar to how AIX traditionally builds its shared # libraries. _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' fi fi ;; beos*) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(allow_undefined_flag, $1)=unsupported # Joseph Beckenbach says some releases of gcc # support --undefined. This deserves some investigation. FIXME _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; chorus*) case $cc_basename in *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; cygwin* | mingw* | pw32* | cegcc*) case $GXX,$cc_basename in ,cl* | no,cl*) # Native MSVC # hardcode_libdir_flag_spec is actually meaningless, as there is # no search path for DLLs. _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=yes _LT_TAGVAR(file_list_spec, $1)='@' # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. shrext_cmds=".dll" # FIXME: Setting linknames here is a bad hack. _LT_TAGVAR(archive_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-dll~linknames=' _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then $SED -n -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' -e '1\\\!p' < $export_symbols > $output_objdir/$soname.exp; else $SED -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' < $export_symbols > $output_objdir/$soname.exp; fi~ $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ linknames=' # The linker will not automatically build a static lib if we build a DLL. # _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes # Don't use ranlib _LT_TAGVAR(old_postinstall_cmds, $1)='chmod 644 $oldlib' _LT_TAGVAR(postlink_cmds, $1)='lt_outputfile="@OUTPUT@"~ lt_tool_outputfile="@TOOL_OUTPUT@"~ case $lt_outputfile in *.exe|*.EXE) ;; *) lt_outputfile="$lt_outputfile.exe" lt_tool_outputfile="$lt_tool_outputfile.exe" ;; esac~ func_to_tool_file "$lt_outputfile"~ if test "$MANIFEST_TOOL" != ":" && test -f "$lt_outputfile.manifest"; then $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; $RM "$lt_outputfile.manifest"; fi' ;; *) # g++ # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless, # as there is no search path for DLLs. _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-all-symbols' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' # If the export-symbols file already is a .def file (1st line # is EXPORTS), use it as is; otherwise, prepend... _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then cp $export_symbols $output_objdir/$soname.def; else echo EXPORTS > $output_objdir/$soname.def; cat $export_symbols >> $output_objdir/$soname.def; fi~ $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac ;; darwin* | rhapsody*) _LT_DARWIN_LINKER_FEATURES($1) ;; dgux*) case $cc_basename in ec++*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; ghcx*) # Green Hills C++ Compiler # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; freebsd2.*) # C++ shared libraries reported to be fairly broken before # switch to ELF _LT_TAGVAR(ld_shlibs, $1)=no ;; freebsd-elf*) _LT_TAGVAR(archive_cmds_need_lc, $1)=no ;; freebsd* | dragonfly*) # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF # conventions _LT_TAGVAR(ld_shlibs, $1)=yes ;; haiku*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(link_all_deplibs, $1)=yes ;; hpux9*) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH, # but as the default # location of the library. case $cc_basename in CC*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; aCC*) _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -b ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' ;; *) if test "$GXX" = yes; then _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared -nostdlib $pic_flag ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' else # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac ;; hpux10*|hpux11*) if test $with_gnu_ld = no; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: case $host_cpu in hppa*64*|ia64*) ;; *) _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' ;; esac fi case $host_cpu in hppa*64*|ia64*) _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *) _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH, # but as the default # location of the library. ;; esac case $cc_basename in CC*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; aCC*) case $host_cpu in hppa*64*) _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; ia64*) _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; esac # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' ;; *) if test "$GXX" = yes; then if test $with_gnu_ld = no; then case $host_cpu in hppa*64*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; ia64*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $pic_flag ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; esac fi else # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac ;; interix[[3-9]]*) _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. # Instead, shared libraries are loaded at an image base (0x10000000 by # default) and relocated if they conflict, which is a slow very memory # consuming and fragmenting process. To avoid this, we pick a random, # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link # time. Moving up from 0x10000000 also allows more sbrk(2) space. _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' ;; irix5* | irix6*) case $cc_basename in CC*) # SGI C++ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' # Archives containing C++ object files must be created using # "CC -ar", where "CC" is the IRIX C++ compiler. This is # necessary to make sure instantiated templates are included # in the archive. _LT_TAGVAR(old_archive_cmds, $1)='$CC -ar -WR,-u -o $oldlib $oldobjs' ;; *) if test "$GXX" = yes; then if test "$with_gnu_ld" = no; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' else _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` -o $lib' fi fi _LT_TAGVAR(link_all_deplibs, $1)=yes ;; esac _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(inherit_rpath, $1)=yes ;; linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) case $cc_basename in KCC*) # Kuck and Associates, Inc. (KAI) C++ Compiler # KCC will only create a shared library if the output file # ends with ".so" (or ".sl" for HP-UX), so rename the library # to its proper name (with version) after linking. _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib ${wl}-retain-symbols-file,$export_symbols; mv \$templib $lib' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | $GREP "ld"`; rm -f libconftest$shared_ext; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' # Archives containing C++ object files must be created using # "CC -Bstatic", where "CC" is the KAI C++ compiler. _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' ;; icpc* | ecpc* ) # Intel C++ with_gnu_ld=yes # version 8.0 and above of icpc choke on multiply defined symbols # if we add $predep_objects and $postdep_objects, however 7.1 and # earlier do not add the objects themselves. case `$CC -V 2>&1` in *"Version 7."*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' ;; *) # Version 8.0 or newer tmp_idyn= case $host_cpu in ia64*) tmp_idyn=' -i_dynamic';; esac _LT_TAGVAR(archive_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' ;; esac _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive$convenience ${wl}--no-whole-archive' ;; pgCC* | pgcpp*) # Portland Group C++ compiler case `$CC -V` in *pgCC\ [[1-5]].* | *pgcpp\ [[1-5]].*) _LT_TAGVAR(prelink_cmds, $1)='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~ compile_command="$compile_command `find $tpldir -name \*.o | sort | $NL2SP`"' _LT_TAGVAR(old_archive_cmds, $1)='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~ $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | sort | $NL2SP`~ $RANLIB $oldlib' _LT_TAGVAR(archive_cmds, $1)='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib' ;; *) # Version 6 and above use weak symbols _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib' ;; esac _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}--rpath ${wl}$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' ;; cxx*) # Compaq C++ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib ${wl}-retain-symbols-file $wl$export_symbols' runpath_var=LD_RUN_PATH _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "X$list" | $Xsed' ;; xl* | mpixl* | bgxl*) # IBM XL 8.0 on PPC, with GNU ld _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' _LT_TAGVAR(archive_cmds, $1)='$CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' if test "x$supports_anon_versioning" = xyes; then _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib' fi ;; *) case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C++ 5.9 _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs' _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file ${wl}$export_symbols' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' _LT_TAGVAR(compiler_needs_object, $1)=yes # Not sure whether something based on # $CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 # would be better. output_verbose_link_cmd='func_echo_all' # Archives containing C++ object files must be created using # "CC -xar", where "CC" is the Sun C++ compiler. This is # necessary to make sure instantiated templates are included # in the archive. _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs' ;; esac ;; esac ;; lynxos*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; m88k*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; mvs*) case $cc_basename in cxx*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; netbsd*) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags' wlarc= _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no fi # Workaround some broken pre-1.5 toolchains output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"' ;; *nto* | *qnx*) _LT_TAGVAR(ld_shlibs, $1)=yes ;; openbsd2*) # C++ shared libraries are fairly broken _LT_TAGVAR(ld_shlibs, $1)=no ;; openbsd*) if test -f /usr/libexec/ld.so; then _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file,$export_symbols -o $lib' _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' fi output_verbose_link_cmd=func_echo_all else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; osf3* | osf4* | osf5*) case $cc_basename in KCC*) # Kuck and Associates, Inc. (KAI) C++ Compiler # KCC will only create a shared library if the output file # ends with ".so" (or ".sl" for HP-UX), so rename the library # to its proper name (with version) after linking. _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo "$lib" | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: # Archives containing C++ object files must be created using # the KAI C++ compiler. case $host in osf3*) _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' ;; *) _LT_TAGVAR(old_archive_cmds, $1)='$CC -o $oldlib $oldobjs' ;; esac ;; RCC*) # Rational C++ 2.4.1 # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; cxx*) case $host in osf3*) _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $soname `test -n "$verstring" && func_echo_all "${wl}-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' ;; *) _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~ echo "-hidden">> $lib.exp~ $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname ${wl}-input ${wl}$lib.exp `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib~ $RM $lib.exp' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' ;; esac _LT_TAGVAR(hardcode_libdir_separator, $1)=: # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld" | $GREP -v "ld:"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' ;; *) if test "$GXX" = yes && test "$with_gnu_ld" = no; then _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' case $host in osf3*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' ;; esac _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' else # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac ;; psos*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; sunos4*) case $cc_basename in CC*) # Sun C++ 4.x # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; lcc*) # Lucid # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; solaris*) case $cc_basename in CC* | sunCC*) # Sun C++ 4.2, 5.x and Centerline C++ _LT_TAGVAR(archive_cmds_need_lc,$1)=yes _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs' _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -G${allow_undefined_flag} ${wl}-M ${wl}$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no case $host_os in solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; *) # The compiler driver will combine and reorder linker options, # but understands `-z linker_flag'. # Supported since Solaris 2.6 (maybe 2.5.1?) _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract' ;; esac _LT_TAGVAR(link_all_deplibs, $1)=yes output_verbose_link_cmd='func_echo_all' # Archives containing C++ object files must be created using # "CC -xar", where "CC" is the Sun C++ compiler. This is # necessary to make sure instantiated templates are included # in the archive. _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs' ;; gcx*) # Green Hills C++ Compiler _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' # The C++ compiler must be used to create the archive. _LT_TAGVAR(old_archive_cmds, $1)='$CC $LDFLAGS -archive -o $oldlib $oldobjs' ;; *) # GNU C++ compiler with Solaris linker if test "$GXX" = yes && test "$with_gnu_ld" = no; then _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-z ${wl}defs' if $CC --version | $GREP -v '^2\.7' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -shared $pic_flag -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' else # g++ 2.7 appears to require `-G' NOT `-shared' on this # platform. _LT_TAGVAR(archive_cmds, $1)='$CC -G -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -G -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $wl$libdir' case $host_os in solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; *) _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract' ;; esac fi ;; esac ;; sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*) _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no runpath_var='LD_RUN_PATH' case $cc_basename in CC*) _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; esac ;; sysv5* | sco3.2v5* | sco5v6*) # Note: We can NOT use -z defs as we might desire, because we do not # link with -lc, and that would cause any symbols used from libc to # always be unresolved, which means just about no library would # ever link correctly. If we're not using GNU ld we use -z text # though, which does catch some bad symbols but isn't as heavy-handed # as -z defs. _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' _LT_TAGVAR(allow_undefined_flag, $1)='${wl}-z,nodefs' _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R,$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=':' _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Bexport' runpath_var='LD_RUN_PATH' case $cc_basename in CC*) _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(old_archive_cmds, $1)='$CC -Tprelink_objects $oldobjs~ '"$_LT_TAGVAR(old_archive_cmds, $1)" _LT_TAGVAR(reload_cmds, $1)='$CC -Tprelink_objects $reload_objs~ '"$_LT_TAGVAR(reload_cmds, $1)" ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; esac ;; tandem*) case $cc_basename in NCC*) # NonStop-UX NCC 3.20 # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; vxworks*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)]) test "$_LT_TAGVAR(ld_shlibs, $1)" = no && can_build_shared=no _LT_TAGVAR(GCC, $1)="$GXX" _LT_TAGVAR(LD, $1)="$LD" ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... _LT_SYS_HIDDEN_LIBDEPS($1) _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_SYS_DYNAMIC_LINKER($1) _LT_LINKER_HARDCODE_LIBPATH($1) _LT_CONFIG($1) fi # test -n "$compiler" CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS LDCXX=$LD LD=$lt_save_LD GCC=$lt_save_GCC with_gnu_ld=$lt_save_with_gnu_ld lt_cv_path_LDCXX=$lt_cv_path_LD lt_cv_path_LD=$lt_save_path_LD lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld fi # test "$_lt_caught_CXX_error" != yes AC_LANG_POP ])# _LT_LANG_CXX_CONFIG # _LT_FUNC_STRIPNAME_CNF # ---------------------- # func_stripname_cnf prefix suffix name # strip PREFIX and SUFFIX off of NAME. # PREFIX and SUFFIX must not contain globbing or regex special # characters, hashes, percent signs, but SUFFIX may contain a leading # dot (in which case that matches only a dot). # # This function is identical to the (non-XSI) version of func_stripname, # except this one can be used by m4 code that may be executed by configure, # rather than the libtool script. m4_defun([_LT_FUNC_STRIPNAME_CNF],[dnl AC_REQUIRE([_LT_DECL_SED]) AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH]) func_stripname_cnf () { case ${2} in .*) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%\\\\${2}\$%%"`;; *) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%${2}\$%%"`;; esac } # func_stripname_cnf ])# _LT_FUNC_STRIPNAME_CNF # _LT_SYS_HIDDEN_LIBDEPS([TAGNAME]) # --------------------------------- # Figure out "hidden" library dependencies from verbose # compiler output when linking a shared library. # Parse the compiler output and extract the necessary # objects, libraries and library flags. m4_defun([_LT_SYS_HIDDEN_LIBDEPS], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl AC_REQUIRE([_LT_FUNC_STRIPNAME_CNF])dnl # Dependencies to place before and after the object being linked: _LT_TAGVAR(predep_objects, $1)= _LT_TAGVAR(postdep_objects, $1)= _LT_TAGVAR(predeps, $1)= _LT_TAGVAR(postdeps, $1)= _LT_TAGVAR(compiler_lib_search_path, $1)= dnl we can't use the lt_simple_compile_test_code here, dnl because it contains code intended for an executable, dnl not a library. It's possible we should let each dnl tag define a new lt_????_link_test_code variable, dnl but it's only used here... m4_if([$1], [], [cat > conftest.$ac_ext <<_LT_EOF int a; void foo (void) { a = 0; } _LT_EOF ], [$1], [CXX], [cat > conftest.$ac_ext <<_LT_EOF class Foo { public: Foo (void) { a = 0; } private: int a; }; _LT_EOF ], [$1], [F77], [cat > conftest.$ac_ext <<_LT_EOF subroutine foo implicit none integer*4 a a=0 return end _LT_EOF ], [$1], [FC], [cat > conftest.$ac_ext <<_LT_EOF subroutine foo implicit none integer a a=0 return end _LT_EOF ], [$1], [GCJ], [cat > conftest.$ac_ext <<_LT_EOF public class foo { private int a; public void bar (void) { a = 0; } }; _LT_EOF ], [$1], [GO], [cat > conftest.$ac_ext <<_LT_EOF package foo func foo() { } _LT_EOF ]) _lt_libdeps_save_CFLAGS=$CFLAGS case "$CC $CFLAGS " in #( *\ -flto*\ *) CFLAGS="$CFLAGS -fno-lto" ;; *\ -fwhopr*\ *) CFLAGS="$CFLAGS -fno-whopr" ;; *\ -fuse-linker-plugin*\ *) CFLAGS="$CFLAGS -fno-use-linker-plugin" ;; esac dnl Parse the compiler output and extract the necessary dnl objects, libraries and library flags. if AC_TRY_EVAL(ac_compile); then # Parse the compiler output and extract the necessary # objects, libraries and library flags. # Sentinel used to keep track of whether or not we are before # the conftest object file. pre_test_object_deps_done=no for p in `eval "$output_verbose_link_cmd"`; do case ${prev}${p} in -L* | -R* | -l*) # Some compilers place space between "-{L,R}" and the path. # Remove the space. if test $p = "-L" || test $p = "-R"; then prev=$p continue fi # Expand the sysroot to ease extracting the directories later. if test -z "$prev"; then case $p in -L*) func_stripname_cnf '-L' '' "$p"; prev=-L; p=$func_stripname_result ;; -R*) func_stripname_cnf '-R' '' "$p"; prev=-R; p=$func_stripname_result ;; -l*) func_stripname_cnf '-l' '' "$p"; prev=-l; p=$func_stripname_result ;; esac fi case $p in =*) func_stripname_cnf '=' '' "$p"; p=$lt_sysroot$func_stripname_result ;; esac if test "$pre_test_object_deps_done" = no; then case ${prev} in -L | -R) # Internal compiler library paths should come after those # provided the user. The postdeps already come after the # user supplied libs so there is no need to process them. if test -z "$_LT_TAGVAR(compiler_lib_search_path, $1)"; then _LT_TAGVAR(compiler_lib_search_path, $1)="${prev}${p}" else _LT_TAGVAR(compiler_lib_search_path, $1)="${_LT_TAGVAR(compiler_lib_search_path, $1)} ${prev}${p}" fi ;; # The "-l" case would never come before the object being # linked, so don't bother handling this case. esac else if test -z "$_LT_TAGVAR(postdeps, $1)"; then _LT_TAGVAR(postdeps, $1)="${prev}${p}" else _LT_TAGVAR(postdeps, $1)="${_LT_TAGVAR(postdeps, $1)} ${prev}${p}" fi fi prev= ;; *.lto.$objext) ;; # Ignore GCC LTO objects *.$objext) # This assumes that the test object file only shows up # once in the compiler output. if test "$p" = "conftest.$objext"; then pre_test_object_deps_done=yes continue fi if test "$pre_test_object_deps_done" = no; then if test -z "$_LT_TAGVAR(predep_objects, $1)"; then _LT_TAGVAR(predep_objects, $1)="$p" else _LT_TAGVAR(predep_objects, $1)="$_LT_TAGVAR(predep_objects, $1) $p" fi else if test -z "$_LT_TAGVAR(postdep_objects, $1)"; then _LT_TAGVAR(postdep_objects, $1)="$p" else _LT_TAGVAR(postdep_objects, $1)="$_LT_TAGVAR(postdep_objects, $1) $p" fi fi ;; *) ;; # Ignore the rest. esac done # Clean up. rm -f a.out a.exe else echo "libtool.m4: error: problem compiling $1 test program" fi $RM -f confest.$objext CFLAGS=$_lt_libdeps_save_CFLAGS # PORTME: override above test on systems where it is broken m4_if([$1], [CXX], [case $host_os in interix[[3-9]]*) # Interix 3.5 installs completely hosed .la files for C++, so rather than # hack all around it, let's just trust "g++" to DTRT. _LT_TAGVAR(predep_objects,$1)= _LT_TAGVAR(postdep_objects,$1)= _LT_TAGVAR(postdeps,$1)= ;; linux*) case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C++ 5.9 # The more standards-conforming stlport4 library is # incompatible with the Cstd library. Avoid specifying # it if it's in CXXFLAGS. Ignore libCrun as # -library=stlport4 depends on it. case " $CXX $CXXFLAGS " in *" -library=stlport4 "*) solaris_use_stlport4=yes ;; esac if test "$solaris_use_stlport4" != yes; then _LT_TAGVAR(postdeps,$1)='-library=Cstd -library=Crun' fi ;; esac ;; solaris*) case $cc_basename in CC* | sunCC*) # The more standards-conforming stlport4 library is # incompatible with the Cstd library. Avoid specifying # it if it's in CXXFLAGS. Ignore libCrun as # -library=stlport4 depends on it. case " $CXX $CXXFLAGS " in *" -library=stlport4 "*) solaris_use_stlport4=yes ;; esac # Adding this requires a known-good setup of shared libraries for # Sun compiler versions before 5.6, else PIC objects from an old # archive will be linked into the output, leading to subtle bugs. if test "$solaris_use_stlport4" != yes; then _LT_TAGVAR(postdeps,$1)='-library=Cstd -library=Crun' fi ;; esac ;; esac ]) case " $_LT_TAGVAR(postdeps, $1) " in *" -lc "*) _LT_TAGVAR(archive_cmds_need_lc, $1)=no ;; esac _LT_TAGVAR(compiler_lib_search_dirs, $1)= if test -n "${_LT_TAGVAR(compiler_lib_search_path, $1)}"; then _LT_TAGVAR(compiler_lib_search_dirs, $1)=`echo " ${_LT_TAGVAR(compiler_lib_search_path, $1)}" | ${SED} -e 's! -L! !g' -e 's!^ !!'` fi _LT_TAGDECL([], [compiler_lib_search_dirs], [1], [The directories searched by this compiler when creating a shared library]) _LT_TAGDECL([], [predep_objects], [1], [Dependencies to place before and after the objects being linked to create a shared library]) _LT_TAGDECL([], [postdep_objects], [1]) _LT_TAGDECL([], [predeps], [1]) _LT_TAGDECL([], [postdeps], [1]) _LT_TAGDECL([], [compiler_lib_search_path], [1], [The library search path used internally by the compiler when linking a shared library]) ])# _LT_SYS_HIDDEN_LIBDEPS # _LT_LANG_F77_CONFIG([TAG]) # -------------------------- # Ensure that the configuration variables for a Fortran 77 compiler are # suitably defined. These variables are subsequently used by _LT_CONFIG # to write the compiler configuration to `libtool'. m4_defun([_LT_LANG_F77_CONFIG], [AC_LANG_PUSH(Fortran 77) if test -z "$F77" || test "X$F77" = "Xno"; then _lt_disable_F77=yes fi _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(allow_undefined_flag, $1)= _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(archive_expsym_cmds, $1)= _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(hardcode_libdir_separator, $1)= _LT_TAGVAR(hardcode_minus_L, $1)=no _LT_TAGVAR(hardcode_automatic, $1)=no _LT_TAGVAR(inherit_rpath, $1)=no _LT_TAGVAR(module_cmds, $1)= _LT_TAGVAR(module_expsym_cmds, $1)= _LT_TAGVAR(link_all_deplibs, $1)=unknown _LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds _LT_TAGVAR(reload_flag, $1)=$reload_flag _LT_TAGVAR(reload_cmds, $1)=$reload_cmds _LT_TAGVAR(no_undefined_flag, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no # Source file extension for f77 test sources. ac_ext=f # Object file extension for compiled f77 test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # No sense in running all these tests if we already determined that # the F77 compiler isn't working. Some variables (like enable_shared) # are currently assumed to apply to all compilers on this platform, # and will be corrupted by setting them based on a non-working compiler. if test "$_lt_disable_F77" != yes; then # Code to be used in simple compile tests lt_simple_compile_test_code="\ subroutine t return end " # Code to be used in simple link tests lt_simple_link_test_code="\ program t end " # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC="$CC" lt_save_GCC=$GCC lt_save_CFLAGS=$CFLAGS CC=${F77-"f77"} CFLAGS=$FFLAGS compiler=$CC _LT_TAGVAR(compiler, $1)=$CC _LT_CC_BASENAME([$compiler]) GCC=$G77 if test -n "$compiler"; then AC_MSG_CHECKING([if libtool supports shared libraries]) AC_MSG_RESULT([$can_build_shared]) AC_MSG_CHECKING([whether to build shared libraries]) test "$can_build_shared" = "no" && enable_shared=no # On AIX, shared libraries and static libraries use the same namespace, and # are all built from PIC. case $host_os in aix3*) test "$enable_shared" = yes && enable_static=no if test -n "$RANLIB"; then archive_cmds="$archive_cmds~\$RANLIB \$lib" postinstall_cmds='$RANLIB $lib' fi ;; aix[[4-9]]*) if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then test "$enable_shared" = yes && enable_static=no fi ;; esac AC_MSG_RESULT([$enable_shared]) AC_MSG_CHECKING([whether to build static libraries]) # Make sure either enable_shared or enable_static is yes. test "$enable_shared" = yes || enable_static=yes AC_MSG_RESULT([$enable_static]) _LT_TAGVAR(GCC, $1)="$G77" _LT_TAGVAR(LD, $1)="$LD" ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_SYS_DYNAMIC_LINKER($1) _LT_LINKER_HARDCODE_LIBPATH($1) _LT_CONFIG($1) fi # test -n "$compiler" GCC=$lt_save_GCC CC="$lt_save_CC" CFLAGS="$lt_save_CFLAGS" fi # test "$_lt_disable_F77" != yes AC_LANG_POP ])# _LT_LANG_F77_CONFIG # _LT_LANG_FC_CONFIG([TAG]) # ------------------------- # Ensure that the configuration variables for a Fortran compiler are # suitably defined. These variables are subsequently used by _LT_CONFIG # to write the compiler configuration to `libtool'. m4_defun([_LT_LANG_FC_CONFIG], [AC_LANG_PUSH(Fortran) if test -z "$FC" || test "X$FC" = "Xno"; then _lt_disable_FC=yes fi _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(allow_undefined_flag, $1)= _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(archive_expsym_cmds, $1)= _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(hardcode_libdir_separator, $1)= _LT_TAGVAR(hardcode_minus_L, $1)=no _LT_TAGVAR(hardcode_automatic, $1)=no _LT_TAGVAR(inherit_rpath, $1)=no _LT_TAGVAR(module_cmds, $1)= _LT_TAGVAR(module_expsym_cmds, $1)= _LT_TAGVAR(link_all_deplibs, $1)=unknown _LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds _LT_TAGVAR(reload_flag, $1)=$reload_flag _LT_TAGVAR(reload_cmds, $1)=$reload_cmds _LT_TAGVAR(no_undefined_flag, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no # Source file extension for fc test sources. ac_ext=${ac_fc_srcext-f} # Object file extension for compiled fc test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # No sense in running all these tests if we already determined that # the FC compiler isn't working. Some variables (like enable_shared) # are currently assumed to apply to all compilers on this platform, # and will be corrupted by setting them based on a non-working compiler. if test "$_lt_disable_FC" != yes; then # Code to be used in simple compile tests lt_simple_compile_test_code="\ subroutine t return end " # Code to be used in simple link tests lt_simple_link_test_code="\ program t end " # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC="$CC" lt_save_GCC=$GCC lt_save_CFLAGS=$CFLAGS CC=${FC-"f95"} CFLAGS=$FCFLAGS compiler=$CC GCC=$ac_cv_fc_compiler_gnu _LT_TAGVAR(compiler, $1)=$CC _LT_CC_BASENAME([$compiler]) if test -n "$compiler"; then AC_MSG_CHECKING([if libtool supports shared libraries]) AC_MSG_RESULT([$can_build_shared]) AC_MSG_CHECKING([whether to build shared libraries]) test "$can_build_shared" = "no" && enable_shared=no # On AIX, shared libraries and static libraries use the same namespace, and # are all built from PIC. case $host_os in aix3*) test "$enable_shared" = yes && enable_static=no if test -n "$RANLIB"; then archive_cmds="$archive_cmds~\$RANLIB \$lib" postinstall_cmds='$RANLIB $lib' fi ;; aix[[4-9]]*) if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then test "$enable_shared" = yes && enable_static=no fi ;; esac AC_MSG_RESULT([$enable_shared]) AC_MSG_CHECKING([whether to build static libraries]) # Make sure either enable_shared or enable_static is yes. test "$enable_shared" = yes || enable_static=yes AC_MSG_RESULT([$enable_static]) _LT_TAGVAR(GCC, $1)="$ac_cv_fc_compiler_gnu" _LT_TAGVAR(LD, $1)="$LD" ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... _LT_SYS_HIDDEN_LIBDEPS($1) _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_SYS_DYNAMIC_LINKER($1) _LT_LINKER_HARDCODE_LIBPATH($1) _LT_CONFIG($1) fi # test -n "$compiler" GCC=$lt_save_GCC CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS fi # test "$_lt_disable_FC" != yes AC_LANG_POP ])# _LT_LANG_FC_CONFIG # _LT_LANG_GCJ_CONFIG([TAG]) # -------------------------- # Ensure that the configuration variables for the GNU Java Compiler compiler # are suitably defined. These variables are subsequently used by _LT_CONFIG # to write the compiler configuration to `libtool'. m4_defun([_LT_LANG_GCJ_CONFIG], [AC_REQUIRE([LT_PROG_GCJ])dnl AC_LANG_SAVE # Source file extension for Java test sources. ac_ext=java # Object file extension for compiled Java test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # Code to be used in simple compile tests lt_simple_compile_test_code="class foo {}" # Code to be used in simple link tests lt_simple_link_test_code='public class conftest { public static void main(String[[]] argv) {}; }' # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_CFLAGS=$CFLAGS lt_save_GCC=$GCC GCC=yes CC=${GCJ-"gcj"} CFLAGS=$GCJFLAGS compiler=$CC _LT_TAGVAR(compiler, $1)=$CC _LT_TAGVAR(LD, $1)="$LD" _LT_CC_BASENAME([$compiler]) # GCJ did not exist at the time GCC didn't implicitly link libc in. _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds _LT_TAGVAR(reload_flag, $1)=$reload_flag _LT_TAGVAR(reload_cmds, $1)=$reload_cmds ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... if test -n "$compiler"; then _LT_COMPILER_NO_RTTI($1) _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_LINKER_HARDCODE_LIBPATH($1) _LT_CONFIG($1) fi AC_LANG_RESTORE GCC=$lt_save_GCC CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS ])# _LT_LANG_GCJ_CONFIG # _LT_LANG_GO_CONFIG([TAG]) # -------------------------- # Ensure that the configuration variables for the GNU Go compiler # are suitably defined. These variables are subsequently used by _LT_CONFIG # to write the compiler configuration to `libtool'. m4_defun([_LT_LANG_GO_CONFIG], [AC_REQUIRE([LT_PROG_GO])dnl AC_LANG_SAVE # Source file extension for Go test sources. ac_ext=go # Object file extension for compiled Go test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # Code to be used in simple compile tests lt_simple_compile_test_code="package main; func main() { }" # Code to be used in simple link tests lt_simple_link_test_code='package main; func main() { }' # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_CFLAGS=$CFLAGS lt_save_GCC=$GCC GCC=yes CC=${GOC-"gccgo"} CFLAGS=$GOFLAGS compiler=$CC _LT_TAGVAR(compiler, $1)=$CC _LT_TAGVAR(LD, $1)="$LD" _LT_CC_BASENAME([$compiler]) # Go did not exist at the time GCC didn't implicitly link libc in. _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds _LT_TAGVAR(reload_flag, $1)=$reload_flag _LT_TAGVAR(reload_cmds, $1)=$reload_cmds ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... if test -n "$compiler"; then _LT_COMPILER_NO_RTTI($1) _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_LINKER_HARDCODE_LIBPATH($1) _LT_CONFIG($1) fi AC_LANG_RESTORE GCC=$lt_save_GCC CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS ])# _LT_LANG_GO_CONFIG # _LT_LANG_RC_CONFIG([TAG]) # ------------------------- # Ensure that the configuration variables for the Windows resource compiler # are suitably defined. These variables are subsequently used by _LT_CONFIG # to write the compiler configuration to `libtool'. m4_defun([_LT_LANG_RC_CONFIG], [AC_REQUIRE([LT_PROG_RC])dnl AC_LANG_SAVE # Source file extension for RC test sources. ac_ext=rc # Object file extension for compiled RC test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # Code to be used in simple compile tests lt_simple_compile_test_code='sample MENU { MENUITEM "&Soup", 100, CHECKED }' # Code to be used in simple link tests lt_simple_link_test_code="$lt_simple_compile_test_code" # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC="$CC" lt_save_CFLAGS=$CFLAGS lt_save_GCC=$GCC GCC= CC=${RC-"windres"} CFLAGS= compiler=$CC _LT_TAGVAR(compiler, $1)=$CC _LT_CC_BASENAME([$compiler]) _LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes if test -n "$compiler"; then : _LT_CONFIG($1) fi GCC=$lt_save_GCC AC_LANG_RESTORE CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS ])# _LT_LANG_RC_CONFIG # LT_PROG_GCJ # ----------- AC_DEFUN([LT_PROG_GCJ], [m4_ifdef([AC_PROG_GCJ], [AC_PROG_GCJ], [m4_ifdef([A][M_PROG_GCJ], [A][M_PROG_GCJ], [AC_CHECK_TOOL(GCJ, gcj,) test "x${GCJFLAGS+set}" = xset || GCJFLAGS="-g -O2" AC_SUBST(GCJFLAGS)])])[]dnl ]) # Old name: AU_ALIAS([LT_AC_PROG_GCJ], [LT_PROG_GCJ]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([LT_AC_PROG_GCJ], []) # LT_PROG_GO # ---------- AC_DEFUN([LT_PROG_GO], [AC_CHECK_TOOL(GOC, gccgo,) ]) # LT_PROG_RC # ---------- AC_DEFUN([LT_PROG_RC], [AC_CHECK_TOOL(RC, windres,) ]) # Old name: AU_ALIAS([LT_AC_PROG_RC], [LT_PROG_RC]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([LT_AC_PROG_RC], []) # _LT_DECL_EGREP # -------------- # If we don't have a new enough Autoconf to choose the best grep # available, choose the one first in the user's PATH. m4_defun([_LT_DECL_EGREP], [AC_REQUIRE([AC_PROG_EGREP])dnl AC_REQUIRE([AC_PROG_FGREP])dnl test -z "$GREP" && GREP=grep _LT_DECL([], [GREP], [1], [A grep program that handles long lines]) _LT_DECL([], [EGREP], [1], [An ERE matcher]) _LT_DECL([], [FGREP], [1], [A literal string matcher]) dnl Non-bleeding-edge autoconf doesn't subst GREP, so do it here too AC_SUBST([GREP]) ]) # _LT_DECL_OBJDUMP # -------------- # If we don't have a new enough Autoconf to choose the best objdump # available, choose the one first in the user's PATH. m4_defun([_LT_DECL_OBJDUMP], [AC_CHECK_TOOL(OBJDUMP, objdump, false) test -z "$OBJDUMP" && OBJDUMP=objdump _LT_DECL([], [OBJDUMP], [1], [An object symbol dumper]) AC_SUBST([OBJDUMP]) ]) # _LT_DECL_DLLTOOL # ---------------- # Ensure DLLTOOL variable is set. m4_defun([_LT_DECL_DLLTOOL], [AC_CHECK_TOOL(DLLTOOL, dlltool, false) test -z "$DLLTOOL" && DLLTOOL=dlltool _LT_DECL([], [DLLTOOL], [1], [DLL creation program]) AC_SUBST([DLLTOOL]) ]) # _LT_DECL_SED # ------------ # Check for a fully-functional sed program, that truncates # as few characters as possible. Prefer GNU sed if found. m4_defun([_LT_DECL_SED], [AC_PROG_SED test -z "$SED" && SED=sed Xsed="$SED -e 1s/^X//" _LT_DECL([], [SED], [1], [A sed program that does not truncate output]) _LT_DECL([], [Xsed], ["\$SED -e 1s/^X//"], [Sed that helps us avoid accidentally triggering echo(1) options like -n]) ])# _LT_DECL_SED m4_ifndef([AC_PROG_SED], [ ############################################################ # NOTE: This macro has been submitted for inclusion into # # GNU Autoconf as AC_PROG_SED. When it is available in # # a released version of Autoconf we should remove this # # macro and use it instead. # ############################################################ m4_defun([AC_PROG_SED], [AC_MSG_CHECKING([for a sed that does not truncate output]) AC_CACHE_VAL(lt_cv_path_SED, [# Loop through the user's path and test for sed and gsed. # Then use that list of sed's as ones to test for truncation. as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for lt_ac_prog in sed gsed; do for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$lt_ac_prog$ac_exec_ext"; then lt_ac_sed_list="$lt_ac_sed_list $as_dir/$lt_ac_prog$ac_exec_ext" fi done done done IFS=$as_save_IFS lt_ac_max=0 lt_ac_count=0 # Add /usr/xpg4/bin/sed as it is typically found on Solaris # along with /bin/sed that truncates output. for lt_ac_sed in $lt_ac_sed_list /usr/xpg4/bin/sed; do test ! -f $lt_ac_sed && continue cat /dev/null > conftest.in lt_ac_count=0 echo $ECHO_N "0123456789$ECHO_C" >conftest.in # Check for GNU sed and select it if it is found. if "$lt_ac_sed" --version 2>&1 < /dev/null | grep 'GNU' > /dev/null; then lt_cv_path_SED=$lt_ac_sed break fi while true; do cat conftest.in conftest.in >conftest.tmp mv conftest.tmp conftest.in cp conftest.in conftest.nl echo >>conftest.nl $lt_ac_sed -e 's/a$//' < conftest.nl >conftest.out || break cmp -s conftest.out conftest.nl || break # 10000 chars as input seems more than enough test $lt_ac_count -gt 10 && break lt_ac_count=`expr $lt_ac_count + 1` if test $lt_ac_count -gt $lt_ac_max; then lt_ac_max=$lt_ac_count lt_cv_path_SED=$lt_ac_sed fi done done ]) SED=$lt_cv_path_SED AC_SUBST([SED]) AC_MSG_RESULT([$SED]) ])#AC_PROG_SED ])#m4_ifndef # Old name: AU_ALIAS([LT_AC_PROG_SED], [AC_PROG_SED]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([LT_AC_PROG_SED], []) # _LT_CHECK_SHELL_FEATURES # ------------------------ # Find out whether the shell is Bourne or XSI compatible, # or has some other useful features. m4_defun([_LT_CHECK_SHELL_FEATURES], [AC_MSG_CHECKING([whether the shell understands some XSI constructs]) # Try some XSI features xsi_shell=no ( _lt_dummy="a/b/c" test "${_lt_dummy##*/},${_lt_dummy%/*},${_lt_dummy#??}"${_lt_dummy%"$_lt_dummy"}, \ = c,a/b,b/c, \ && eval 'test $(( 1 + 1 )) -eq 2 \ && test "${#_lt_dummy}" -eq 5' ) >/dev/null 2>&1 \ && xsi_shell=yes AC_MSG_RESULT([$xsi_shell]) _LT_CONFIG_LIBTOOL_INIT([xsi_shell='$xsi_shell']) AC_MSG_CHECKING([whether the shell understands "+="]) lt_shell_append=no ( foo=bar; set foo baz; eval "$[1]+=\$[2]" && test "$foo" = barbaz ) \ >/dev/null 2>&1 \ && lt_shell_append=yes AC_MSG_RESULT([$lt_shell_append]) _LT_CONFIG_LIBTOOL_INIT([lt_shell_append='$lt_shell_append']) if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then lt_unset=unset else lt_unset=false fi _LT_DECL([], [lt_unset], [0], [whether the shell understands "unset"])dnl # test EBCDIC or ASCII case `echo X|tr X '\101'` in A) # ASCII based system # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr lt_SP2NL='tr \040 \012' lt_NL2SP='tr \015\012 \040\040' ;; *) # EBCDIC based system lt_SP2NL='tr \100 \n' lt_NL2SP='tr \r\n \100\100' ;; esac _LT_DECL([SP2NL], [lt_SP2NL], [1], [turn spaces into newlines])dnl _LT_DECL([NL2SP], [lt_NL2SP], [1], [turn newlines into spaces])dnl ])# _LT_CHECK_SHELL_FEATURES # _LT_PROG_FUNCTION_REPLACE (FUNCNAME, REPLACEMENT-BODY) # ------------------------------------------------------ # In `$cfgfile', look for function FUNCNAME delimited by `^FUNCNAME ()$' and # '^} FUNCNAME ', and replace its body with REPLACEMENT-BODY. m4_defun([_LT_PROG_FUNCTION_REPLACE], [dnl { sed -e '/^$1 ()$/,/^} # $1 /c\ $1 ()\ {\ m4_bpatsubsts([$2], [$], [\\], [^\([ ]\)], [\\\1]) } # Extended-shell $1 implementation' "$cfgfile" > $cfgfile.tmp \ && mv -f "$cfgfile.tmp" "$cfgfile" \ || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") test 0 -eq $? || _lt_function_replace_fail=: ]) # _LT_PROG_REPLACE_SHELLFNS # ------------------------- # Replace existing portable implementations of several shell functions with # equivalent extended shell implementations where those features are available.. m4_defun([_LT_PROG_REPLACE_SHELLFNS], [if test x"$xsi_shell" = xyes; then _LT_PROG_FUNCTION_REPLACE([func_dirname], [dnl case ${1} in */*) func_dirname_result="${1%/*}${2}" ;; * ) func_dirname_result="${3}" ;; esac]) _LT_PROG_FUNCTION_REPLACE([func_basename], [dnl func_basename_result="${1##*/}"]) _LT_PROG_FUNCTION_REPLACE([func_dirname_and_basename], [dnl case ${1} in */*) func_dirname_result="${1%/*}${2}" ;; * ) func_dirname_result="${3}" ;; esac func_basename_result="${1##*/}"]) _LT_PROG_FUNCTION_REPLACE([func_stripname], [dnl # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are # positional parameters, so assign one to ordinary parameter first. func_stripname_result=${3} func_stripname_result=${func_stripname_result#"${1}"} func_stripname_result=${func_stripname_result%"${2}"}]) _LT_PROG_FUNCTION_REPLACE([func_split_long_opt], [dnl func_split_long_opt_name=${1%%=*} func_split_long_opt_arg=${1#*=}]) _LT_PROG_FUNCTION_REPLACE([func_split_short_opt], [dnl func_split_short_opt_arg=${1#??} func_split_short_opt_name=${1%"$func_split_short_opt_arg"}]) _LT_PROG_FUNCTION_REPLACE([func_lo2o], [dnl case ${1} in *.lo) func_lo2o_result=${1%.lo}.${objext} ;; *) func_lo2o_result=${1} ;; esac]) _LT_PROG_FUNCTION_REPLACE([func_xform], [ func_xform_result=${1%.*}.lo]) _LT_PROG_FUNCTION_REPLACE([func_arith], [ func_arith_result=$(( $[*] ))]) _LT_PROG_FUNCTION_REPLACE([func_len], [ func_len_result=${#1}]) fi if test x"$lt_shell_append" = xyes; then _LT_PROG_FUNCTION_REPLACE([func_append], [ eval "${1}+=\\${2}"]) _LT_PROG_FUNCTION_REPLACE([func_append_quoted], [dnl func_quote_for_eval "${2}" dnl m4 expansion turns \\\\ into \\, and then the shell eval turns that into \ eval "${1}+=\\\\ \\$func_quote_for_eval_result"]) # Save a `func_append' function call where possible by direct use of '+=' sed -e 's%func_append \([[a-zA-Z_]]\{1,\}\) "%\1+="%g' $cfgfile > $cfgfile.tmp \ && mv -f "$cfgfile.tmp" "$cfgfile" \ || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") test 0 -eq $? || _lt_function_replace_fail=: else # Save a `func_append' function call even when '+=' is not available sed -e 's%func_append \([[a-zA-Z_]]\{1,\}\) "%\1="$\1%g' $cfgfile > $cfgfile.tmp \ && mv -f "$cfgfile.tmp" "$cfgfile" \ || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") test 0 -eq $? || _lt_function_replace_fail=: fi if test x"$_lt_function_replace_fail" = x":"; then AC_MSG_WARN([Unable to substitute extended shell functions in $ofile]) fi ]) # _LT_PATH_CONVERSION_FUNCTIONS # ----------------------------- # Determine which file name conversion functions should be used by # func_to_host_file (and, implicitly, by func_to_host_path). These are needed # for certain cross-compile configurations and native mingw. m4_defun([_LT_PATH_CONVERSION_FUNCTIONS], [AC_REQUIRE([AC_CANONICAL_HOST])dnl AC_REQUIRE([AC_CANONICAL_BUILD])dnl AC_MSG_CHECKING([how to convert $build file names to $host format]) AC_CACHE_VAL(lt_cv_to_host_file_cmd, [case $host in *-*-mingw* ) case $build in *-*-mingw* ) # actually msys lt_cv_to_host_file_cmd=func_convert_file_msys_to_w32 ;; *-*-cygwin* ) lt_cv_to_host_file_cmd=func_convert_file_cygwin_to_w32 ;; * ) # otherwise, assume *nix lt_cv_to_host_file_cmd=func_convert_file_nix_to_w32 ;; esac ;; *-*-cygwin* ) case $build in *-*-mingw* ) # actually msys lt_cv_to_host_file_cmd=func_convert_file_msys_to_cygwin ;; *-*-cygwin* ) lt_cv_to_host_file_cmd=func_convert_file_noop ;; * ) # otherwise, assume *nix lt_cv_to_host_file_cmd=func_convert_file_nix_to_cygwin ;; esac ;; * ) # unhandled hosts (and "normal" native builds) lt_cv_to_host_file_cmd=func_convert_file_noop ;; esac ]) to_host_file_cmd=$lt_cv_to_host_file_cmd AC_MSG_RESULT([$lt_cv_to_host_file_cmd]) _LT_DECL([to_host_file_cmd], [lt_cv_to_host_file_cmd], [0], [convert $build file names to $host format])dnl AC_MSG_CHECKING([how to convert $build file names to toolchain format]) AC_CACHE_VAL(lt_cv_to_tool_file_cmd, [#assume ordinary cross tools, or native build. lt_cv_to_tool_file_cmd=func_convert_file_noop case $host in *-*-mingw* ) case $build in *-*-mingw* ) # actually msys lt_cv_to_tool_file_cmd=func_convert_file_msys_to_w32 ;; esac ;; esac ]) to_tool_file_cmd=$lt_cv_to_tool_file_cmd AC_MSG_RESULT([$lt_cv_to_tool_file_cmd]) _LT_DECL([to_tool_file_cmd], [lt_cv_to_tool_file_cmd], [0], [convert $build files to toolchain format])dnl ])# _LT_PATH_CONVERSION_FUNCTIONS inn-2.6.0/m4/modes.m40000644000175200017520000000464612575023702013623 0ustar iuliusiuliusdnl modes.m4 -- Setting file and installation modes. dnl $Id: modes.m4 8495 2009-05-24 12:28:19Z iulius $ dnl dnl INN defaults to a umask of 002 for historical reasons, but offers an dnl option to change them. It also has some programs that are occasionally dnl installed with special permissions in some situations but not in others dnl (and not by default). dnl Let the user specify a new umask and also set the directory and file dnl permissions at the same time. Also let the user request inews or rnews dnl be installed with special permissions. AC_DEFUN([INN_ARG_MODES], [NEWSUMASK=02 FILEMODE=0664 DIRMODE=0775 RUNDIRMODE=0770 AC_ARG_WITH([news-umask], [AS_HELP_STRING([--with-news-umask=UMASK], [umask for news files [002]])], with_news_umask=`echo "$with_news_umask" | sed 's/^0*//'` if test "x$with_news_umask" = x22 ; then NEWSUMASK=022 FILEMODE=0644 DIRMODE=0755 RUNDIRMODE=0750 else if test "x$with_news_umask" != x2 ; then AC_MSG_ERROR([Valid umasks are 002 or 022]) fi fi) AC_SUBST([NEWSUMASK]) AC_SUBST([FILEMODE]) AC_SUBST([DIRMODE]) AC_SUBST([RUNDIRMODE]) AC_DEFINE_UNQUOTED([ARTFILE_MODE], [$FILEMODE], [Mode that incoming articles are created with.]) AC_DEFINE_UNQUOTED([BATCHFILE_MODE], [$FILEMODE], [Mode that batch files are created with.]) AC_DEFINE_UNQUOTED([GROUPDIR_MODE], [$DIRMODE], [Mode that directories are created with.]) AC_DEFINE_UNQUOTED([NEWSUMASK], [$NEWSUMASK], [The umask used by all INN programs.]) dnl inews used to be installed setgid, but may not be secure. Only do this if dnl it's explicitly requested at configure time. INEWSMODE=0550 AC_ARG_ENABLE([setgid-inews], [AS_HELP_STRING([--enable-setgid-inews], [Install inews setgid])], if test "x$enableval" = xyes ; then INEWSMODE=02555 fi) AC_SUBST([INEWSMODE]) dnl rnews used to be installed setuid root so that it could be run by the uucp dnl user for incoming batches, but this isn't necessary unless you're using dnl UUCP (which most people aren't) and setuid news is all that's needed. dnl Only do even that if it's explicitly requested at configure time. RNEWSGRP=$RUNASGROUP RNEWSMODE=0500 AC_ARG_ENABLE([uucp-rnews], [AS_HELP_STRING([--enable-uucp-rnews], [Install rnews setuid, group uucp])], if test "x$enableval" = xyes ; then RNEWSGRP=uucp RNEWSMODE=04550 fi) AC_SUBST([RNEWSGRP]) AC_SUBST([RNEWSMODE])]) inn-2.6.0/m4/vamacros.m40000644000175200017520000000434212575023702014320 0ustar iuliusiuliusdnl Check for support for variadic macros. dnl $Id: vamacros.m4 9564 2013-11-10 18:27:16Z iulius $ dnl dnl This file defines two macros for probing for compiler support for variadic dnl macros. Provided are INN_C_C99_VAMACROS, which checks for support for the dnl C99 variadic macro syntax, namely: dnl dnl #define macro(...) fprintf(stderr, __VA_ARGS__) dnl dnl and INN_C_GNU_VAMACROS, which checks for support for the older GNU dnl variadic macro syntax, namely: dnl dnl #define macro(args...) fprintf(stderr, args) dnl dnl They set HAVE_C99_VAMACROS or HAVE_GNU_VAMACROS as appropriate. dnl dnl The canonical version of this file is maintained in the rra-c-util dnl package, available at . dnl dnl Written by Russ Allbery dnl Copyright 2006, 2008, 2009 dnl The Board of Trustees of the Leland Stanford Junior University dnl dnl This file is free software; the authors give unlimited permission to copy dnl and/or distribute it, with or without modifications, as long as this dnl notice is preserved. AC_DEFUN([_INN_C_C99_VAMACROS_SOURCE], [[ #include #define error(...) fprintf(stderr, __VA_ARGS__) int main(void) { error("foo"); error("foo %d", 0); return 0; } ]]) AC_DEFUN([INN_C_C99_VAMACROS], [AC_CACHE_CHECK([for C99 variadic macros], [inn_cv_c_c99_vamacros], [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_INN_C_C99_VAMACROS_SOURCE])], [inn_cv_c_c99_vamacros=yes], [inn_cv_c_c99_vamacros=no])]) AS_IF([test x"$inn_cv_c_c99_vamacros" = xyes], [AC_DEFINE([HAVE_C99_VAMACROS], 1, [Define if the compiler supports C99 variadic macros.])])]) AC_DEFUN([_INN_C_GNU_VAMACROS_SOURCE], [[ #include #define error(args...) fprintf(stderr, args) int main(void) { error("foo"); error("foo %d", 0); return 0; } ]]) AC_DEFUN([INN_C_GNU_VAMACROS], [AC_CACHE_CHECK([for GNU-style variadic macros], [inn_cv_c_gnu_vamacros], [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_INN_C_GNU_VAMACROS_SOURCE])], [inn_cv_c_gnu_vamacros=yes], [inn_cv_c_gnu_vamacros=no])]) AS_IF([test x"$inn_cv_c_gnu_vamacros" = xyes], [AC_DEFINE([HAVE_GNU_VAMACROS], 1, [Define if the compiler supports GNU-style variadic macros.])])]) inn-2.6.0/m4/sendmail.m40000644000175200017520000000203012575023702014271 0ustar iuliusiuliusdnl sendmail.m4 -- Checks for the path to sendmail. dnl $Id: sendmail.m4 9749 2014-11-23 21:45:56Z iulius $ dnl dnl We have a custom probe for sendmail since we want to look in non-standard dnl locations for it, and another custom macro to allow users to override the dnl path to sendmail picked up by the script. dnl Allow the user to specify the path to sendmail. AC_DEFUN([INN_ARG_SENDMAIL], [AC_ARG_VAR([SENDMAIL], [Location of sendmail binary to use]) AC_ARG_WITH([sendmail], [AS_HELP_STRING([--with-sendmail=PATH], [Path to sendmail])], SENDMAIL=$with_sendmail)]) dnl Search for sendmail, honoring the path set by the user if they've done so dnl and otherwise looking only in /usr/sbin and /usr/lib. AC_DEFUN([INN_PATH_SENDMAIL], [if test "${with_sendmail+set}" = set ; then AC_MSG_CHECKING([for sendmail]) AC_MSG_RESULT([$SENDMAIL]) else AC_PATH_PROG([SENDMAIL], [sendmail], [], [/usr/sbin:/usr/lib]) if test -z "$SENDMAIL" ; then AC_MSG_ERROR([sendmail not found, re-run with --with-sendmail]) fi fi]) inn-2.6.0/m4/syslog.m40000644000175200017520000000340412575023702014023 0ustar iuliusiuliusdnl syslog.m4 -- Options and probes for syslog behavior. dnl $Id: syslog.m4 8594 2009-08-21 08:19:13Z iulius $ dnl dnl Normally, INN just logs everything to the news facility, but some systems dnl don't have that so we have to probe and see if we should use local1 dnl instead. And we also provide an option to change the facility to use. dnl dnl INN_LOG_FACILITY sets the output variable SYSLOG_FACILITY and also defines dnl LOG_INN_SERVER and LOG_INN_PROG in config.h. dnl Set up the --with-syslog-facility option. The result is stored in the dnl inn_syslog_facility variable for later use. AC_DEFUN([INN_ARG_SYSLOG], [AC_ARG_WITH([syslog-facility], [AS_HELP_STRING([--with-syslog-facility=LOG_FAC], [Syslog facility @<:@LOG_NEWS or LOG_LOCAL1@:>@])], SYSLOG_FACILITY=$with_syslog_facility, SYSLOG_FACILITY=none)]) dnl Source used by INN_LOG_FACILITY. AC_DEFUN([_INN_LOG_FACILITY], [[ #include #ifndef LOG_NEWS error: LOG_NEWS not available! #endif ]]) dnl Determine the facility for syslog messages. Default to LOG_NEWS for dnl syslog facility if it's available, but if it's not, fall back on dnl LOG_LOCAL1. Honor the existing SYSLOG_FACILITY value if already set by dnl INN_ARG_SYSLOG. AC_DEFUN([INN_LOG_FACILITY], [AC_CACHE_CHECK([log facility for news], [inn_cv_log_facility], [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_INN_LOG_FACILITY])], [inn_cv_log_facility=LOG_NEWS], [inn_cv_log_facility=LOG_LOCAL1])]) AS_IF([test x"$SYSLOG_FACILITY" = xnone], [SYSLOG_FACILITY=$inn_cv_log_facility]) AC_DEFINE_UNQUOTED([LOG_INN_SERVER], [$SYSLOG_FACILITY], [Syslog facility to use for innd logs.]) AC_DEFINE_UNQUOTED([LOG_INN_PROG], [$SYSLOG_FACILITY], [Syslog facility to use for INN program logs.]) AC_SUBST(SYSLOG_FACILITY)]) inn-2.6.0/m4/iov-max.m40000644000175200017520000000451012575023702014062 0ustar iuliusiuliusdnl iov-max.m4 -- Probe for the maximum number of iovecs accepted by writev. dnl $Id: iov-max.m4 8312 2009-01-31 20:35:04Z iulius $ dnl dnl Check for the maximum number of elements in an iovec (IOV_MAX). SVr4 dnl systems appear to use that name for this limit (checked Solaris 2.6, IRIX dnl 6.5, and HP-UX 11.00). Linux doesn't have it, but instead has UIO_MAXIOV dnl defined in or included from . dnl dnl The platforms that have IOV_MAX appear to also offer it via sysconf(3), dnl but we don't currently handle dynamic values. dnl dnl If IOV_MAX is not defined by or , probe for its dnl value by checking writev calls up to 1024 members of an iovec and set it dnl to an appropriate value. dnl Source used by INN_MACRO_IOV_MAX. define([_INN_MACRO_IOV_MAX_SOURCE], [AC_LANG_SOURCE([[ #include #include #include #include #include #ifdef HAVE_UNISTD_H # include #endif #ifdef HAVE_LIMITS_H # include #endif int main () { int fd, size; struct iovec array[1024]; char data; FILE *f = fopen ("conftestval", "w"); if (f == NULL) return 1; #ifdef IOV_MAX fprintf (f, "set in limits.h\n"); #else # ifdef UIO_MAXIOV fprintf (f, "%d\n", UIO_MAXIOV); # else fd = open ("/dev/null", O_WRONLY, 0666); if (fd < 0) return 1; for (size = 1; size <= 1024; size++) { array[size - 1].iov_base = &data; array[size - 1].iov_len = sizeof data; if (writev (fd, array, size) < 0) { if (errno != EINVAL) return 1; fprintf (f, "%d\n", size - 1); exit (0); } } fprintf (f, "1024\n"); # endif /* UIO_MAXIOV */ #endif /* IOV_MAX */ return 0; } ]])]) dnl Do the actual check. AC_DEFUN([INN_MACRO_IOV_MAX], [AC_CACHE_CHECK([value of IOV_MAX], [inn_cv_macro_iov_max], [AC_RUN_IFELSE([_INN_MACRO_IOV_MAX_SOURCE], inn_cv_macro_iov_max=`cat conftestval`, inn_cv_macro_iov_max=error, 16) if test x"$inn_cv_macro_iov_max" = xerror ; then AC_MSG_WARN([probe failure, assuming 16]) inn_cv_macro_iov_max=16 fi]) if test x"$inn_cv_macro_iov_max" != x"set in limits.h" ; then AC_DEFINE_UNQUOTED(IOV_MAX, $inn_cv_macro_iov_max, [Define to the max vectors in an iovec.]) fi]) inn-2.6.0/m4/sendfd.m40000644000175200017520000000351112575023702013745 0ustar iuliusiuliusdnl sendfd.m4 -- Check whether I_SENDFD/I_RECVFD is supported. dnl $Id: sendfd.m4 9762 2014-12-07 18:33:37Z iulius $ dnl dnl Check whether the system supports STREAMS and can send and receive file dnl descriptors via the I_SENDFD and I_RECVFD ioctls. Provides dnl INN_SYS_STREAMS_SENDFD and defines HAVE_STREAMS_SENDFD if this facility is dnl available. dnl Source used by INN_SYS_STREAMS_SENDFD. define([_INN_SYS_STREAMS_SENDFD], [AC_LANG_SOURCE([[ #include #include #include #include #include #if HAVE_UNISTD_H # include #endif int main () { int fd[2], parent; pipe(fd); if (!isastream (fd[0])) { fprintf (stderr, "%d is not a stream\n", fd[0]); return 1; } if (fork () == 0) { int child; close (fd[0]); child = socket (AF_INET, SOCK_STREAM, 0); if (child < 0) return 1; if (ioctl (fd[1], I_SENDFD, child) < 0) return 1; return 0; } else { struct strrecvfd fdrec; memset (&fdrec, 0, sizeof(fdrec)); close (fd[1]); if (ioctl (fd[0], I_RECVFD, &fdrec) < 0) { perror("ioctl"); return 1; } if (fdrec.fd < 0) { fprintf(stderr, "Bad file descriptor %d\n", fd); return 1; } return 0; } } ]])]) dnl The public macro. AC_DEFUN([INN_SYS_STREAMS_SENDFD], [AC_CACHE_CHECK([whether STREAMS fd passing is supported], [inn_cv_sys_streams_sendfd], [AC_RUN_IFELSE([_INN_SYS_STREAMS_SENDFD], [inn_cv_sys_streams_sendfd=yes], [inn_cv_sys_streams_sendfd=no], [inn_cv_sys_streams_sendfd=no])]) if test "$inn_cv_sys_streams_sendfd" = yes ; then AC_DEFINE([HAVE_STREAMS_SENDFD], 1, [Define if your system supports STREAMS file descriptor passing.]) fi]) inn-2.6.0/m4/large-fpos.m40000644000175200017520000000222312575023702014540 0ustar iuliusiuliusdnl large-fpos.m4 -- Check for an off_t-compatible fpos_t. dnl $Id: large-fpos.m4 8312 2009-01-31 20:35:04Z iulius $ dnl dnl Some operating systems (most notably BSDI) support large files but don't dnl have the fseeko and ftello functions. However, fseeko and ftello can be dnl implemented in terms of fsetpos and fgetpos if fpos_t can be cast to and dnl from off_t. This checks for that capability. dnl Source used by INN_TYPE_FPOS_T_LARGE. define([_INN_TYPE_FPOS_T_LARGE_SOURCE], [AC_LANG_SOURCE([[ #include #include int main () { fpos_t fpos = 9223372036854775807ULL; off_t off; off = fpos; exit(off == (off_t) 9223372036854775807ULL ? 0 : 1); } ]])]) dnl The user-callable macro. AC_DEFUN([INN_TYPE_FPOS_T_LARGE], [AC_CACHE_CHECK([for off_t-compatible fpos_t], [inn_cv_type_fpos_t_large], [AC_RUN_IFELSE([_INN_TYPE_FPOS_T_LARGE_SOURCE], [inn_cv_type_fpos_t_large=yes], [inn_cv_type_fpos_t_large=no], [inn_cv_type_fpos_t_large=no])]) if test "$inn_cv_type_fpos_t_large" = yes ; then AC_DEFINE([HAVE_LARGE_FPOS_T], 1, [Define if fpos_t is at least 64 bits and compatible with off_t.]) fi]) inn-2.6.0/m4/sasl.m40000644000175200017520000000636112575023702013452 0ustar iuliusiuliusdnl Find the compiler and linker flags for Cyrus SASL. dnl $Id: sasl.m4 9571 2013-11-21 15:32:04Z iulius $ dnl dnl Finds the compiler and linker flags for linking with the Cyrus SASL dnl library. Provides the --with-sasl, --with-sasl-lib, and dnl --with-sasl-include configure options to specify non-standard paths to the dnl Cyrus SASL library. dnl dnl Provides the macro INN_LIB_SASL and sets the substitution variables dnl SASL_CPPFLAGS, SASL_LDFLAGS, and SASL_LIBS. Also provides dnl INN_LIB_SASL_SWITCH to set CPPFLAGS, LDFLAGS, and LIBS to include the dnl Cyrus SASL v2 library, saving the current values first, and dnl INN_LIB_SASL_RESTORE to restore those settings to before the last dnl INN_LIB_SASL_SWITCH. Defines HAVE_SASL and sets inn_use_SASL to true if dnl the library is found and is version two. dnl dnl Provides the INN_LIB_SASL_OPTIONAL macro, which should be used if Cyrus dnl SASL support is optional. This macro will still always set the dnl substitution variables, but they'll be empty unless --with-sasl is given. dnl Defines HAVE_SASL and sets inn_use_SASL to true if the Cyrus SASL library dnl is found and is version two. dnl dnl Depends on the lib-helper.m4 framework. dnl dnl The canonical version of this file is maintained in the rra-c-util dnl package, available at . dnl dnl Written by Russ Allbery dnl Copyright 2013 dnl The Board of Trustees of the Leland Stanford Junior University dnl dnl This file is free software; the authors give unlimited permission to copy dnl and/or distribute it, with or without modifications, as long as this dnl notice is preserved. dnl Save the current CPPFLAGS, LDFLAGS, and LIBS settings and switch to dnl versions that include the Cyrus SASL flags. Used as a wrapper, with dnl INN_LIB_SASL_RESTORE, around tests. AC_DEFUN([INN_LIB_SASL_SWITCH], [INN_LIB_HELPER_SWITCH([SASL])]) dnl Restore CPPFLAGS, LDFLAGS, and LIBS to their previous values (before dnl INN_LIB_SASL_SWITCH was called). AC_DEFUN([INN_LIB_SASL_RESTORE], [INN_LIB_HELPER_RESTORE([SASL])]) dnl Checks if the Cyrus SASL library is present. The single argument, if dnl "true", says to fail if the Cyrus SASL library could not be found. AC_DEFUN([_INN_LIB_SASL_INTERNAL], [INN_LIB_HELPER_PATHS([SASL]) INN_LIB_SASL_SWITCH AC_CHECK_LIB([sasl2], [sasl_getprop], [SASL_LIBS="-lsasl2"], [AS_IF([test x"$1" = xtrue], [AC_MSG_ERROR([cannot find usable Cyrus SASL library])])]) INN_LIB_SASL_RESTORE]) dnl The main macro for packages with mandatory Cyrus SASL support. AC_DEFUN([INN_LIB_SASL], [INN_LIB_HELPER_VAR_INIT([SASL]) INN_LIB_HELPER_WITH([sasl], [Cyrus SASL], [SASL]) _INN_LIB_SASL_INTERNAL([true]) inn_use_SASL=true AC_DEFINE([HAVE_SASL], 1, [Define if libsasl2 is available.])]) dnl The main macro for packages with optional Cyrus SASL support. AC_DEFUN([INN_LIB_SASL_OPTIONAL], [INN_LIB_HELPER_VAR_INIT([SASL]) INN_LIB_HELPER_WITH_OPTIONAL([sasl], [Cyrus SASL], [SASL]) AS_IF([test x"$inn_use_SASL" != xfalse], [AS_IF([test x"$inn_use_SASL" = xtrue], [_INN_LIB_SASL_INTERNAL([true])], [_INN_LIB_SASL_INTERNAL([false])])]) AS_IF([test x"$SASL_LIBS" != x], [inn_use_SASL=true AC_DEFINE([HAVE_SASL], 1, [Define if libsasl2 is available.])])]) inn-2.6.0/m4/perl.m40000644000175200017520000001156212575023702013451 0ustar iuliusiuliusdnl perl.m4 -- Probe for the details needed to embed Perl. dnl $Id: perl.m4 9240 2011-07-12 09:51:28Z iulius $ dnl dnl Defines INN_ARG_PERL, which sets up the --with-perl command line argument dnl and also sets various flags needed for embedded Perl if it is requested dnl and ensures that Perl is the appropriate version. dnl Check for a required version of Perl. AC_DEFUN([_INN_PERL_VERSION], [AC_CACHE_CHECK([for Perl version], [inn_cv_perl_version], [if $PERL -e 'require $1;' > /dev/null 2>&1 ; then inn_cv_perl_version=`$PERL -e 'print [$]@:>@'` else AC_MSG_ERROR([Perl $1 or greater is required]) fi])]) dnl Check for Perl modules used by scripts shipped with INN. AC_DEFUN([INN_PERL_MODULE], [AC_MSG_CHECKING([for $1]) if $PERL -e 'require $1;' > /dev/null 2>&1 ; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) AC_MSG_WARN([$1 Perl module is required by $2]) inn_perl_module_warning="$inn_perl_module_warning $1 (for $2)" fi]) dnl Check to see if Perl embedding was requested. Regardless of whether it dnl was or not, determine the path to Perl. If it was requested, make sure dnl that we have the right version and then set PERL_CPPFLAGS and PERL_LIBS as dnl appropriate for embedded Perl. AC_DEFUN([INN_ARG_PERL], [AC_ARG_VAR([PERL], [Location of Perl interpreter]) AC_ARG_WITH([perl], [AS_HELP_STRING([--with-perl], [Embedded Perl script support [no]])], [case $withval in yes) DO_PERL=DO AC_DEFINE(DO_PERL, 1, [Define to compile in Perl script support.]) ;; no) DO_PERL=DONT ;; *) AC_MSG_ERROR([invalid argument to --with-perl]) ;; esac], DO_PERL=DONT) dnl Embedded Perl requires 5.004. controlchan requires 5.004_03. Other dnl things may work with 5.003, but make 5.004_03 the minimum level; anyone dnl should really have at least that these days. dnl We also check for useful Perl modules. INN_PATH_PROG_ENSURE([PERL], [perl]) _INN_PERL_VERSION(5.004_03) INN_PERL_MODULE([Encode], [controlchan]) INN_PERL_MODULE([GD], [innreport's HTML output]) INN_PERL_MODULE([MIME::Parser], [controlchan]) dnl Libraries and flags for embedded Perl. Some distributions of Linux have dnl Perl linked with gdbm but don't normally have gdbm installed, so on that dnl platform only strip -lgdbm out of the Perl libraries. Leave it in on dnl other platforms where it may be necessary (it isn't on Linux; Linux dnl shared libraries can manage their own dependencies). Strip -lc out, which dnl is added on some platforms, is unnecessary, and breaks compiles with dnl -pthread (which may be added by Python). dnl dnl If we aren't compiling with large-file support, strip out the large file dnl flags from inn_perl_core_flags; otherwise, innd/cc.c and lib/qio.c dnl disagree over the size of an off_t. Since none of our calls into Perl dnl use variables of type off_t, this should be harmless; in any event, it's dnl going to be better than the innd/cc.c breakage. dnl dnl Also check to see if the complier supports -Wno-extra and, if so, add it dnl to PERL_WARNING. This has to be conditional since -Wno-extra is only dnl supported in gcc 4.0 and later. if test x"$DO_PERL" = xDO ; then AC_MSG_CHECKING([for Perl linkage]) inn_perl_core_path=`$PERL -MConfig -e 'print $Config{archlibexp}'` inn_perl_core_flags=`$PERL -MExtUtils::Embed -e ccopts` inn_perl_core_libs=`$PERL -MExtUtils::Embed -e ldopts 2>&1 | tail -n 1` inn_perl_core_libs=" $inn_perl_core_libs " inn_perl_core_libs=`echo "$inn_perl_core_libs" | sed 's/ -lc / /'` for i in $LIBS ; do inn_perl_core_libs=`echo "$inn_perl_core_libs" | sed "s/ $i / /"` done case $host in *-linux*) inn_perl_core_libs=`echo "$inn_perl_core_libs" | sed 's/ -lgdbm / /'` ;; *-cygwin*) inn_perl_libname=`$PERL -MConfig -e 'print $Config{libperl}'` inn_perl_libname=`echo "$inn_perl_libname" | sed 's/^lib//; s/\.a$//'` inn_perl_core_libs="${inn_perl_core_libs}-l$inn_perl_libname" ;; esac inn_perl_core_libs=`echo "$inn_perl_core_libs" | sed 's/^ *//'` inn_perl_core_libs=`echo "$inn_perl_core_libs" | sed 's/ *$//'` inn_perl_core_flags=" $inn_perl_core_flags " if test x"$inn_enable_largefiles" != xyes ; then for f in -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGE_FILES ; do inn_perl_core_flags=`echo "$inn_perl_core_flags" | sed "s/ $f / /"` done fi inn_perl_core_flags=`echo "$inn_perl_core_flags" | sed 's/^ *//'` inn_perl_core_flags=`echo "$inn_perl_core_flags" | sed 's/ *$//'` PERL_CPPFLAGS="$inn_perl_core_flags" PERL_LIBS="$inn_perl_core_libs" AC_MSG_RESULT([$inn_perl_core_path]) INN_PROG_CC_FLAG([-Wno-extra], [PERL_WARNINGS=-Wno-extra], [PERL_WARNINGS='']) else PERL_CPPFLAGS='' PERL_LIBS='' PERL_WARNINGS='' fi AC_SUBST([PERL_CPPFLAGS]) AC_SUBST([PERL_LIBS]) AC_SUBST([PERL_WARNINGS])]) inn-2.6.0/m4/bdb.m40000644000175200017520000001062112575023702013231 0ustar iuliusiuliusdnl Find the compiler and linker flags for the Berkeley DB library. dnl $Id: bdb.m4 9593 2013-12-27 21:16:09Z iulius $ dnl dnl Finds the compiler and linker flags for linking with the Berkeley DB dnl library. Provides the --with-bdb, --with-bdb-lib, and --with-bdb-include dnl configure options to specify non-standard paths to the Berkeley DB dnl library. dnl dnl Provides the macro INN_LIB_BDB and sets the substitution variables dnl BDB_CPPFLAGS, BDB_LDFLAGS, and BDB_LIBS. Also provides INN_LIB_BDB_SWITCH dnl to set CPPFLAGS, LDFLAGS, and LIBS to include the Berkeley DB library, dnl saving the current values first, and INN_LIB_BDB_RESTORE to restore those dnl settings to before the last INN_LIB_BDB_SWITCH. Defines HAVE_BDB and sets dnl inn_use_BDB to true if the library is found. dnl dnl Provides the INN_LIB_BDB_OPTIONAL macro, which should be used if Berkeley dnl DB support is optional. This macro will still always set the substitution dnl variables, but they'll be empty unless --with-bdb is given. Defines dnl HAVE_BDB and sets inn_use_BDB to true if the Berkeley DB library is found. dnl dnl This file also provides INN_LIB_BDB_NDBM, which checks whether the dnl Berkeley DB library has ndbm support. It then defines HAVE_BDB_NDBM and dnl sets inn_cv_lib_bdb_ndbm to yes if ndbm compatibility layer for Berkely DB dnl is available. Either INN_LIB_BDB or INN_LIB_BDB_OPTIONAL must be called dnl before calling this macro. dnl dnl Depends on the lib-helper.m4 framework. dnl dnl The canonical version of this file is maintained in the rra-c-util dnl package, available at . dnl dnl Written by Russ Allbery dnl Copyright 2013 dnl The Board of Trustees of the Leland Stanford Junior University dnl dnl This file is free software; the authors give unlimited permission to copy dnl and/or distribute it, with or without modifications, as long as this dnl notice is preserved. dnl Save the current CPPFLAGS, LDFLAGS, and LIBS settings and switch to dnl versions that include the Berkeley DB flags. Used as a wrapper, with dnl INN_LIB_BDB_RESTORE, around tests. AC_DEFUN([INN_LIB_BDB_SWITCH], [INN_LIB_HELPER_SWITCH([BDB])]) dnl Restore CPPFLAGS, LDFLAGS, and LIBS to their previous values (before dnl INN_LIB_BDB_SWITCH was called). AC_DEFUN([INN_LIB_BDB_RESTORE], [INN_LIB_HELPER_RESTORE([BDB])]) dnl Checks if the Berkeley DB library is present. The single argument, dnl if "true", says to fail if the Berkeley DB library could not be found. AC_DEFUN([_INN_LIB_BDB_INTERNAL], [INN_LIB_HELPER_PATHS([BDB]) INN_LIB_BDB_SWITCH AC_CHECK_LIB([db], [db_create], [BDB_LIBS="-ldb"], [AS_IF([test x"$1" = xtrue], [AC_MSG_ERROR([cannot find usable Berkeley DB library])])]) INN_LIB_BDB_RESTORE]) dnl The main macro for packages with mandatory Berkeley DB support. AC_DEFUN([INN_LIB_BDB], [INN_LIB_HELPER_VAR_INIT([BDB]) INN_LIB_HELPER_WITH([bdb], [Berkeley DB], [BDB]) _INN_LIB_BDB_INTERNAL([true]) inn_use_BDB=true AC_DEFINE([HAVE_BDB], 1, [Define if libdb is available.])]) dnl The main macro for packages with optional Berkeley DB support. AC_DEFUN([INN_LIB_BDB_OPTIONAL], [INN_LIB_HELPER_VAR_INIT([BDB]) INN_LIB_HELPER_WITH_OPTIONAL([bdb], [Berkeley DB], [BDB]) AS_IF([test x"$inn_use_BDB" != xfalse], [AS_IF([test x"$inn_use_BDB" = xtrue], [_INN_LIB_BDB_INTERNAL([true])], [_INN_LIB_BDB_INTERNAL([false])])]) AS_IF([test x"$BDB_LIBS" != x], [inn_use_BDB=true AC_DEFINE([HAVE_BDB], 1, [Define if libdb is available.])])]) dnl Source used by INN_LIB_BDB_NDBM. AC_DEFUN([_INN_LIB_BDB_NDBM_SOURCE], [[ #include #define DB_DBM_HSEARCH 1 #include int main(void) { DBM *database; database = dbm_open("test", 0, 0600); dbm_close(database); return 0; } ]]) dnl Check whether Berkeley DB was compiled with ndbm compatibility layer. If dnl so, set HAVE_BDB_NDBM. Either INN_LIB_BDB or INN_LIB_BDB_OPTIONAL should dnl be called before calling this macro. AC_DEFUN([INN_LIB_BDB_NDBM], [INN_LIB_BDB_SWITCH AC_CACHE_CHECK([for working ndbm compatibility layer with Berkeley DB], [inn_cv_lib_bdb_ndbm], [AC_LINK_IFELSE([AC_LANG_SOURCE([_INN_LIB_BDB_NDBM_SOURCE])], [inn_cv_lib_bdb_ndbm=yes], [inn_cv_lib_bdb_ndbm=no])]) AS_IF([test x"$inn_cv_lib_bdb_ndbm" = xyes], [AC_DEFINE([HAVE_BDB_NDBM], 1, [Define if the Berkeley DB ndbm compatibility layer is available.])]) INN_LIB_BDB_RESTORE]) inn-2.6.0/m4/zlib.m40000644000175200017520000000614212575023702013445 0ustar iuliusiuliusdnl Find the compiler and linker flags for the zlib library. dnl $Id: zlib.m4 9593 2013-12-27 21:16:09Z iulius $ dnl dnl Finds the compiler and linker flags for linking with the zlib library. dnl Provides the --with-zlib, --with-zlib-lib, and --with-zlib-include dnl configure options to specify non-standard paths to the zlib library. dnl dnl Provides the macro INN_LIB_ZLIB and sets the substitution variables dnl ZLIB_CPPFLAGS, ZLIB_LDFLAGS, and ZLIB_LIBS. Also provides dnl INN_LIB_ZLIB_SWITCH to set CPPFLAGS, LDFLAGS, and LIBS to include the dnl zlib library, saving the current values first, and INN_LIB_ZLIB_RESTORE dnl to restore those settings to before the last INN_LIB_ZLIB_SWITCH. dnl Defines HAVE_ZLIB and sets inn_use_ZLIB to true if the library is found. dnl dnl Provides the INN_LIB_ZLIB_OPTIONAL macro, which should be used if zlib dnl support is optional. This macro will still always set the substitution dnl variables, but they'll be empty unless --with-zlib is given. dnl Defines HAVE_ZLIB and sets inn_use_ZLIB to true if the zlib library dnl is found. dnl dnl Depends on the lib-helper.m4 framework. dnl dnl The canonical version of this file is maintained in the rra-c-util dnl package, available at . dnl dnl Written by Russ Allbery dnl Copyright 2013 dnl The Board of Trustees of the Leland Stanford Junior University dnl dnl This file is free software; the authors give unlimited permission to copy dnl and/or distribute it, with or without modifications, as long as this dnl notice is preserved. dnl Save the current CPPFLAGS, LDFLAGS, and LIBS settings and switch to dnl versions that include the zlib flags. Used as a wrapper, with dnl INN_LIB_ZLIB_RESTORE, around tests. AC_DEFUN([INN_LIB_ZLIB_SWITCH], [INN_LIB_HELPER_SWITCH([ZLIB])]) dnl Restore CPPFLAGS, LDFLAGS, and LIBS to their previous values (before dnl INN_LIB_ZLIB_SWITCH was called). AC_DEFUN([INN_LIB_ZLIB_RESTORE], [INN_LIB_HELPER_RESTORE([ZLIB])]) dnl Checks if the zlib library is present. The single argument, if "true", dnl says to fail if the zlib library could not be found. AC_DEFUN([_INN_LIB_ZLIB_INTERNAL], [INN_LIB_HELPER_PATHS([ZLIB]) INN_LIB_ZLIB_SWITCH AC_CHECK_LIB([z], [compress], [ZLIB_LIBS="-lz"], [AS_IF([test x"$1" = xtrue], [AC_MSG_ERROR([cannot find usable zlib library])])]) INN_LIB_ZLIB_RESTORE]) dnl The main macro for packages with mandatory zlib support. AC_DEFUN([INN_LIB_ZLIB], [INN_LIB_HELPER_VAR_INIT([ZLIB]) INN_LIB_HELPER_WITH([zlib], [zlib], [ZLIB]) _INN_LIB_ZLIB_INTERNAL([true]) inn_use_ZLIB=true AC_DEFINE([HAVE_ZLIB], 1, [Define if libz is available.])]) dnl The main macro for packages with optional zlib support. AC_DEFUN([INN_LIB_ZLIB_OPTIONAL], [INN_LIB_HELPER_VAR_INIT([ZLIB]) INN_LIB_HELPER_WITH_OPTIONAL([zlib], [zlib], [ZLIB]) AS_IF([test x"$inn_use_ZLIB" != xfalse], [AS_IF([test x"$inn_use_ZLIB" = xtrue], [_INN_LIB_ZLIB_INTERNAL([true])], [_INN_LIB_ZLIB_INTERNAL([false])])]) AS_IF([test x"$ZLIB_LIBS" != x], [inn_use_ZLIB=true AC_DEFINE([HAVE_ZLIB], 1, [Define if libz is available.])])]) inn-2.6.0/m4/snprintf.m40000644000175200017520000000401612575023702014346 0ustar iuliusiuliusdnl Test for a working C99 snprintf. dnl $Id: snprintf.m4 9564 2013-11-10 18:27:16Z iulius $ dnl dnl Check for a working snprintf. Some systems have an snprintf that doesn't dnl nul-terminate if the buffer isn't large enough. Others return -1 if the dnl string doesn't fit into the buffer instead of returning the number of dnl characters that would have been formatted. Still others don't support dnl NULL as the buffer argument (just to get a count of the formatted length). dnl dnl Provides INN_FUNC_SNPRINTF, which adds snprintf.o to LIBOBJS unless a dnl fully working snprintf is found. dnl dnl The canonical version of this file is maintained in the rra-c-util dnl package, available at . dnl dnl Written by Russ Allbery dnl Copyright 2006, 2008, 2009 dnl The Board of Trustees of the Leland Stanford Junior University dnl dnl This file is free software; the authors give unlimited permission to copy dnl and/or distribute it, with or without modifications, as long as this dnl notice is preserved. dnl Source used by INN_FUNC_SNPRINTF. AC_DEFUN([_INN_FUNC_SNPRINTF_SOURCE], [[ #include #include char buf[2]; int test(char *format, ...) { va_list args; int count; va_start(args, format); count = vsnprintf(buf, sizeof buf, format, args); va_end(args); return count; } int main() { return ((test("%s", "abcd") == 4 && buf[0] == 'a' && buf[1] == '\0' && snprintf(NULL, 0, "%s", "abcd") == 4) ? 0 : 1); } ]]) dnl The user-callable test. AC_DEFUN([INN_FUNC_SNPRINTF], [AC_CACHE_CHECK([for working snprintf], [inn_cv_func_snprintf_works], [AC_RUN_IFELSE([AC_LANG_SOURCE([_INN_FUNC_SNPRINTF_SOURCE])], [inn_cv_func_snprintf_works=yes], [inn_cv_func_snprintf_works=no], [inn_cv_func_snprintf_works=no])]) AS_IF([test x"$inn_cv_func_snprintf_works" = xyes], [AC_DEFINE([HAVE_SNPRINTF], 1, [Define if your system has a working snprintf function.])], [AC_LIBOBJ([snprintf])])]) inn-2.6.0/m4/lib-helper.m40000644000175200017520000001326412575023702014533 0ustar iuliusiuliusdnl Helper functions to manage compiler variables. dnl $Id: lib-helper.m4 9570 2013-11-21 15:23:24Z iulius $ dnl dnl These are a wide variety of helper macros to make it easier to construct dnl standard macros to probe for a library and to set library-specific dnl CPPFLAGS, LDFLAGS, and LIBS shell substitution variables. Most of them dnl take as one of the arguments the prefix string to use for variables, which dnl is usually something like "KRB5" or "GSSAPI". dnl dnl Depends on INN_SET_LDFLAGS. dnl dnl The canonical version of this file is maintained in the rra-c-util dnl package, available at . dnl dnl Written by Russ Allbery dnl Copyright 2011, 2013 dnl The Board of Trustees of the Leland Stanford Junior University dnl dnl This file is free software; the authors give unlimited permission to copy dnl and/or distribute it, with or without modifications, as long as this dnl notice is preserved. dnl Add the library flags to the default compiler flags and then remove them. dnl dnl To use these macros, pass the prefix string used for the variables as the dnl only argument. For example, to use these for a library with KRB5 as a dnl prefix, one would use: dnl dnl AC_DEFUN([INN_LIB_KRB5_SWITCH], [INN_LIB_HELPER_SWITCH([KRB5])]) dnl AC_DEFUN([INN_LIB_KRB5_RESTORE], [INN_LIB_HELPER_RESTORE([KRB5])]) dnl dnl Then, wrap checks for library features with INN_LIB_KRB5_SWITCH and dnl INN_LIB_KRB5_RESTORE. AC_DEFUN([INN_LIB_HELPER_SWITCH], [inn_$1[]_save_CPPFLAGS="$CPPFLAGS" inn_$1[]_save_LDFLAGS="$LDFLAGS" inn_$1[]_save_LIBS="$LIBS" CPPFLAGS="$$1[]_CPPFLAGS $CPPFLAGS" LDFLAGS="$$1[]_LDFLAGS $LDFLAGS" LIBS="$$1[]_LIBS $LIBS"]) AC_DEFUN([INN_LIB_HELPER_RESTORE], [CPPFLAGS="$inn_$1[]_save_CPPFLAGS" LDFLAGS="$inn_$1[]_save_LDFLAGS" LIBS="$inn_$1[]_save_LIBS"]) dnl Given _root, _libdir, and _includedir variables set for a library (set by dnl INN_LIB_HELPER_WITH*), set the LDFLAGS and CPPFLAGS variables for that dnl library accordingly. Takes the variable prefix as the only argument. AC_DEFUN([INN_LIB_HELPER_PATHS], [AS_IF([test x"$inn_$1[]_libdir" != x], [$1[]_LDFLAGS="-L$inn_$1[]_libdir"], [AS_IF([test x"$inn_$1[]_root" != x], [INN_SET_LDFLAGS([$1][_LDFLAGS], [${inn_$1[]_root}])])]) AS_IF([test x"$inn_$1[]_includedir" != x], [$1[]_CPPFLAGS="-I$inn_$1[]_includedir"], [AS_IF([test x"$inn_$1[]_root" != x], [AS_IF([test x"$inn_$1[]_root" != x/usr], [$1[]_CPPFLAGS="-I${inn_$1[]_root}/include"])])])]) dnl Check whether a library works. This is used as a sanity check on the dnl results of *-config shell scripts. Takes four arguments; the first, if dnl "true", says that a working library is mandatory and errors out if it dnl doesn't. The second is the variable prefix. The third is a function to dnl look for that should be in the libraries. The fourth is the dnl human-readable name of the library for error messages. AC_DEFUN([INN_LIB_HELPER_CHECK], [INN_LIB_HELPER_SWITCH([$2]) AC_CHECK_FUNC([$3], [], [AS_IF([test x"$1" = xtrue], [AC_MSG_FAILURE([unable to link with $4 library])]) $2[]_CPPFLAGS= $2[]_LDFLAGS= $2[]_LIBS=]) INN_LIB_HELPER_RESTORE([$2])]) dnl Initialize the variables used by a library probe and set the appropriate dnl ones as substitution variables. Takes the library variable prefix as its dnl only argument. AC_DEFUN([INN_LIB_HELPER_VAR_INIT], [inn_$1[]_root= inn_$1[]_libdir= inn_$1[]_includedir= inn_use_$1= $1[]_CPPFLAGS= $1[]_LDFLAGS= $1[]_LIBS= AC_SUBST([$1][_CPPFLAGS]) AC_SUBST([$1][_LDFLAGS]) AC_SUBST([$1][_LIBS])]) dnl Handles --with options for a non-optional library. First argument is the dnl base for the switch names. Second argument is the short description. dnl Third argument is the variable prefix. The variables set are used by dnl INN_LIB_HELPER_PATHS. AC_DEFUN([INN_LIB_HELPER_WITH], [AC_ARG_WITH([$1], [AS_HELP_STRING([--with-][$1][=DIR], [Location of $2 headers and libraries])], [AS_IF([test x"$withval" != xyes && test x"$withval" != xno], [inn_$3[]_root="$withval"])]) AC_ARG_WITH([$1][-include], [AS_HELP_STRING([--with-][$1][-include=DIR], [Location of $2 headers])], [AS_IF([test x"$withval" != xyes && test x"$withval" != xno], [inn_$3[]_includedir="$withval"])]) AC_ARG_WITH([$1][-lib], [AS_HELP_STRING([--with-][$1][-lib=DIR], [Location of $2 libraries])], [AS_IF([test x"$withval" != xyes && test x"$withval" != xno], [inn_$3[]_libdir="$withval"])])]) dnl Handles --with options for an optional library, so --with- can dnl cause the checks to be skipped entirely or become mandatory. Sets an dnl inn_use_PREFIX variable to true or false if the library is explicitly dnl enabled or disabled. dnl dnl First argument is the base for the switch names. Second argument is the dnl short description. Third argument is the variable prefix. dnl dnl The variables set are used by INN_LIB_HELPER_PATHS. AC_DEFUN([INN_LIB_HELPER_WITH_OPTIONAL], [AC_ARG_WITH([$1], [AS_HELP_STRING([--with-][$1][@<:@=DIR@:>@], [Location of $2 headers and libraries])], [AS_IF([test x"$withval" = xno], [inn_use_$3=false], [AS_IF([test x"$withval" != xyes], [inn_$3[]_root="$withval"]) inn_use_$3=true])]) AC_ARG_WITH([$1][-include], [AS_HELP_STRING([--with-][$1][-include=DIR], [Location of $2 headers])], [AS_IF([test x"$withval" != xyes && test x"$withval" != xno], [inn_$3[]_includedir="$withval"])]) AC_ARG_WITH([$1][-lib], [AS_HELP_STRING([--with-][$1][-lib=DIR], [Location of $2 libraries])], [AS_IF([test x"$withval" != xyes && test x"$withval" != xno], [inn_$3[]_libdir="$withval"])])]) inn-2.6.0/m4/ltsugar.m40000644000175200017520000001042412575023702014164 0ustar iuliusiulius# ltsugar.m4 -- libtool m4 base layer. -*-Autoconf-*- # # Copyright (C) 2004, 2005, 2007, 2008 Free Software Foundation, Inc. # Written by Gary V. Vaughan, 2004 # # This file is free software; the Free Software Foundation gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. # serial 6 ltsugar.m4 # This is to help aclocal find these macros, as it can't see m4_define. AC_DEFUN([LTSUGAR_VERSION], [m4_if([0.1])]) # lt_join(SEP, ARG1, [ARG2...]) # ----------------------------- # Produce ARG1SEPARG2...SEPARGn, omitting [] arguments and their # associated separator. # Needed until we can rely on m4_join from Autoconf 2.62, since all earlier # versions in m4sugar had bugs. m4_define([lt_join], [m4_if([$#], [1], [], [$#], [2], [[$2]], [m4_if([$2], [], [], [[$2]_])$0([$1], m4_shift(m4_shift($@)))])]) m4_define([_lt_join], [m4_if([$#$2], [2], [], [m4_if([$2], [], [], [[$1$2]])$0([$1], m4_shift(m4_shift($@)))])]) # lt_car(LIST) # lt_cdr(LIST) # ------------ # Manipulate m4 lists. # These macros are necessary as long as will still need to support # Autoconf-2.59 which quotes differently. m4_define([lt_car], [[$1]]) m4_define([lt_cdr], [m4_if([$#], 0, [m4_fatal([$0: cannot be called without arguments])], [$#], 1, [], [m4_dquote(m4_shift($@))])]) m4_define([lt_unquote], $1) # lt_append(MACRO-NAME, STRING, [SEPARATOR]) # ------------------------------------------ # Redefine MACRO-NAME to hold its former content plus `SEPARATOR'`STRING'. # Note that neither SEPARATOR nor STRING are expanded; they are appended # to MACRO-NAME as is (leaving the expansion for when MACRO-NAME is invoked). # No SEPARATOR is output if MACRO-NAME was previously undefined (different # than defined and empty). # # This macro is needed until we can rely on Autoconf 2.62, since earlier # versions of m4sugar mistakenly expanded SEPARATOR but not STRING. m4_define([lt_append], [m4_define([$1], m4_ifdef([$1], [m4_defn([$1])[$3]])[$2])]) # lt_combine(SEP, PREFIX-LIST, INFIX, SUFFIX1, [SUFFIX2...]) # ---------------------------------------------------------- # Produce a SEP delimited list of all paired combinations of elements of # PREFIX-LIST with SUFFIX1 through SUFFIXn. Each element of the list # has the form PREFIXmINFIXSUFFIXn. # Needed until we can rely on m4_combine added in Autoconf 2.62. m4_define([lt_combine], [m4_if(m4_eval([$# > 3]), [1], [m4_pushdef([_Lt_sep], [m4_define([_Lt_sep], m4_defn([lt_car]))])]]dnl [[m4_foreach([_Lt_prefix], [$2], [m4_foreach([_Lt_suffix], ]m4_dquote(m4_dquote(m4_shift(m4_shift(m4_shift($@)))))[, [_Lt_sep([$1])[]m4_defn([_Lt_prefix])[$3]m4_defn([_Lt_suffix])])])])]) # lt_if_append_uniq(MACRO-NAME, VARNAME, [SEPARATOR], [UNIQ], [NOT-UNIQ]) # ----------------------------------------------------------------------- # Iff MACRO-NAME does not yet contain VARNAME, then append it (delimited # by SEPARATOR if supplied) and expand UNIQ, else NOT-UNIQ. m4_define([lt_if_append_uniq], [m4_ifdef([$1], [m4_if(m4_index([$3]m4_defn([$1])[$3], [$3$2$3]), [-1], [lt_append([$1], [$2], [$3])$4], [$5])], [lt_append([$1], [$2], [$3])$4])]) # lt_dict_add(DICT, KEY, VALUE) # ----------------------------- m4_define([lt_dict_add], [m4_define([$1($2)], [$3])]) # lt_dict_add_subkey(DICT, KEY, SUBKEY, VALUE) # -------------------------------------------- m4_define([lt_dict_add_subkey], [m4_define([$1($2:$3)], [$4])]) # lt_dict_fetch(DICT, KEY, [SUBKEY]) # ---------------------------------- m4_define([lt_dict_fetch], [m4_ifval([$3], m4_ifdef([$1($2:$3)], [m4_defn([$1($2:$3)])]), m4_ifdef([$1($2)], [m4_defn([$1($2)])]))]) # lt_if_dict_fetch(DICT, KEY, [SUBKEY], VALUE, IF-TRUE, [IF-FALSE]) # ----------------------------------------------------------------- m4_define([lt_if_dict_fetch], [m4_if(lt_dict_fetch([$1], [$2], [$3]), [$4], [$5], [$6])]) # lt_dict_filter(DICT, [SUBKEY], VALUE, [SEPARATOR], KEY, [...]) # -------------------------------------------------------------- m4_define([lt_dict_filter], [m4_if([$5], [], [], [lt_join(m4_quote(m4_default([$4], [[, ]])), lt_unquote(m4_split(m4_normalize(m4_foreach(_Lt_key, lt_car([m4_shiftn(4, $@)]), [lt_if_dict_fetch([$1], _Lt_key, [$2], [$3], [_Lt_key ])])))))])[]dnl ]) inn-2.6.0/m4/mmap.m40000644000175200017520000001453012575023702013437 0ustar iuliusiuliusdnl mmap.m4 -- Probe for mmap properties. dnl $Id: mmap.m4 8313 2009-01-31 21:08:29Z iulius $ dnl dnl The mmap macro that comes with Autoconf doesn't do anything useful. dnl Define a new INN_FUNC_MMAP that probes for a working mmap that supports dnl shared, non-fixed maps. This function defines HAVE_MMAP if mmap appears dnl to work, and takes an action if found argument that can be used to make dnl other probes. dnl dnl Provide INN_FUNC_MMAP_NEEDS_MSYNC, which defines MMAP_NEEDS_MSYNC if dnl reading from an open file doesn't see changes made to that file through dnl mmap without an intervening msync. dnl dnl Provide INN_FUNC_MMAP_MISSES_WRITES, which defines MMAP_MISSES_WRITES if dnl changes to a file made with write aren't seen in an mmaped region without dnl an intervening msync. dnl dnl (The above two macros together in essence probe for whether the operating dnl system has a unified page cache.) dnl dnl Finally, provide AC_FUNC_MSYNC_ARGS, which defines HAVE_MSYNC_3_ARGS if dnl msync takes three arguments (as on Solaris and Linux) rather than two dnl (most other operating systems). dnl Source used by INN_FUNC_MMAP. define([_INN_FUNC_MMAP_SOURCE], [AC_LANG_SOURCE([[ #include #include #include int main() { int *data, *data2; int i, fd; /* First, make a file with some known garbage in it. Use something larger than one page but still an odd page size. */ data = malloc (20000); if (!data) return 1; for (i = 0; i < 20000 / sizeof (int); i++) data[i] = rand(); umask (0); fd = creat ("conftestmmaps", 0600); if (fd < 0) return 1; if (write (fd, data, 20000) != 20000) return 1; close (fd); /* Next, try to mmap the file and make sure we see the same garbage. */ fd = open ("conftestmmaps", O_RDWR); if (fd < 0) return 1; data2 = mmap (0, 20000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (data2 == (int *) -1) return 1; for (i = 0; i < 20000 / sizeof (int); i++) if (data[i] != data2[i]) return 1; close (fd); unlink ("conftestmmaps"); return 0; } ]])]) dnl This portion is similar to what AC_FUNC_MMAP does, only it tests shared, dnl non-fixed mmaps. AC_DEFUN([INN_FUNC_MMAP], [AC_CACHE_CHECK([for working mmap], [inn_cv_func_mmap], [AC_RUN_IFELSE([_INN_FUNC_MMAP_SOURCE], [inn_cv_func_mmap=yes], [inn_cv_func_mmap=no], [inn_cv_func_mmap=no])]) if test $inn_cv_func_mmap = yes ; then AC_DEFINE([HAVE_MMAP], 1, [Define if mmap exists and works for shared, non-fixed maps.]) $1 else : $2 fi]) dnl Source used by INN_FUNC_MMAP_NEEDS_MSYNC. define([_INN_FUNC_MMAP_NEEDS_MSYNC_SOURCE], [AC_LANG_SOURCE([[ #include #include #include #include int main() { int *data, *data2; int i, fd; /* First, make a file with some known garbage in it. Use something larger than one page but still an odd page size. */ data = malloc (20000); if (!data) return 1; for (i = 0; i < 20000 / sizeof (int); i++) data[i] = rand(); umask (0); fd = creat ("conftestmmaps", 0600); if (fd < 0) return 1; if (write (fd, data, 20000) != 20000) return 1; close (fd); /* Next, try to mmap the file and make sure we see the same garbage. */ fd = open ("conftestmmaps", O_RDWR); if (fd < 0) return 1; data2 = mmap (0, 20000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (data2 == (int *) -1) return 1; /* Finally, see if changes made to the mmaped region propagate back to the file as seen by read (meaning that msync isn't needed). */ for (i = 0; i < 20000 / sizeof (int); i++) data2[i]++; if (read (fd, data, 20000) != 20000) return 1; for (i = 0; i < 20000 / sizeof (int); i++) if (data[i] != data2[i]) return 1; close (fd); unlink ("conftestmmapm"); return 0; } ]])]) dnl Check whether the data read from an open file sees the changes made to an dnl mmaped region, or if msync has to be called for other applications to see dnl those changes. AC_DEFUN([INN_FUNC_MMAP_NEEDS_MSYNC], [AC_CACHE_CHECK([whether msync is needed], [inn_cv_func_mmap_need_msync], [AC_RUN_IFELSE([_INN_FUNC_MMAP_NEEDS_MSYNC_SOURCE], [inn_cv_func_mmap_need_msync=no], [inn_cv_func_mmap_need_msync=yes], [inn_cv_func_mmap_need_msync=yes])]) if test $inn_cv_func_mmap_need_msync = yes ; then AC_DEFINE([MMAP_NEEDS_MSYNC], 1, [Define if you need to call msync for calls to read to see changes.]) fi]) dnl Source used by INN_FUNC_MMAP_SEES_WRITES. define([_INN_FUNC_MMAP_SEES_WRITES_SOURCE], [AC_LANG_SOURCE([[ #include #include #include #include #if HAVE_UNISTD_H # include #endif #include /* Fractional page is probably worst case. */ static char zbuff[1024]; static char fname[] = "conftestw"; int main () { char *map; int i, fd; fd = open (fname, O_RDWR | O_CREAT, 0660); if (fd < 0) return 1; unlink (fname); write (fd, zbuff, sizeof (zbuff)); lseek (fd, 0, SEEK_SET); map = mmap (0, sizeof (zbuff), PROT_READ, MAP_SHARED, fd, 0); if (map == (char *) -1) return 2; for (i = 0; fname[i]; i++) { if (write (fd, &fname[i], 1) != 1) return 3; if (map[i] != fname[i]) return 4; } return 0; } ]])]) dnl Check if an mmaped region will see writes made to the underlying file dnl without an intervening msync. AC_DEFUN([INN_FUNC_MMAP_SEES_WRITES], [AC_CACHE_CHECK([whether mmap sees writes], [inn_cv_func_mmap_sees_writes], [AC_RUN_IFELSE([_INN_FUNC_MMAP_SEES_WRITES_SOURCE], [inn_cv_func_mmap_sees_writes=yes], [inn_cv_func_mmap_sees_writes=no], [inn_cv_func_mmap_sees_writes=no])]) if test $inn_cv_func_mmap_sees_writes = no ; then AC_DEFINE([MMAP_MISSES_WRITES], 1, [Define if you need to call msync after writes.]) fi]) dnl Check whether msync takes three arguments. (It takes three arguments on dnl Solaris and Linux, two arguments on BSDI.) AC_DEFUN([INN_FUNC_MSYNC_ARGS], [AC_CACHE_CHECK([how many arguments msync takes], [inn_cv_func_msync_args], [AC_COMPILE_IFELSE( [AC_LANG_PROGRAM([[ #include #include ]], [[char *p; int psize; msync (p, psize, MS_ASYNC);]])], [inn_cv_func_msync_args=3], [inn_cv_func_msync_args=2])]) if test $inn_cv_func_msync_args = 3 ; then AC_DEFINE([HAVE_MSYNC_3_ARG], 1, [Define if your msync function takes three arguments.]) fi]) inn-2.6.0/m4/inet-ntoa.m40000644000175200017520000000335412575023702014405 0ustar iuliusiuliusdnl Check for a working inet_ntoa. dnl $Id: inet-ntoa.m4 9564 2013-11-10 18:27:16Z iulius $ dnl dnl Check whether inet_ntoa is present and working. Since calling inet_ntoa dnl involves passing small structs on the stack, present and working versions dnl may still not function with gcc on some platforms (such as IRIX). dnl Provides INN_FUNC_INET_NTOA and defines HAVE_INET_NTOA if inet_ntoa is dnl present and working. dnl dnl The canonical version of this file is maintained in the rra-c-util dnl package, available at . dnl dnl Copyright 1999, 2000, 2001, 2003 Russ Allbery dnl Copyright 2008, 2009 dnl The Board of Trustees of the Leland Stanford Junior University dnl dnl This file is free software; the authors give unlimited permission to copy dnl and/or distribute it, with or without modifications, as long as this dnl notice is preserved. dnl Source used by INN_FUNC_INET_NTOA. AC_DEFUN([_INN_FUNC_INET_NTOA_SOURCE], [[ #include #include #include #include #include int main(void) { struct in_addr in; in.s_addr = htonl(0x7f000000L); return (strcmp(inet_ntoa(in), "127.0.0.0") == 0) ? 0 : 1; } ]]) dnl The public macro. AC_DEFUN([INN_FUNC_INET_NTOA], [AC_CACHE_CHECK(for working inet_ntoa, inn_cv_func_inet_ntoa_works, [AC_RUN_IFELSE([AC_LANG_SOURCE([_INN_FUNC_INET_NTOA_SOURCE])], [inn_cv_func_inet_ntoa_works=yes], [inn_cv_func_inet_ntoa_works=no], [inn_cv_func_inet_ntoa_works=no])]) AS_IF([test x"$inn_cv_func_inet_ntoa_works" = xyes], [AC_DEFINE([HAVE_INET_NTOA], 1, [Define if your system has a working inet_ntoa function.])], [AC_LIBOBJ([inet_ntoa])])]) inn-2.6.0/m4/krb5-config.m40000644000175200017520000001113212575023702014606 0ustar iuliusiuliusdnl Use krb5-config to get link paths for Kerberos libraries. dnl $Id: krb5-config.m4 9564 2013-11-10 18:27:16Z iulius $ dnl dnl Provides one macro, INN_KRB5_CONFIG, which attempts to get compiler and dnl linker flags for a library via krb5-config and sets the appropriate shell dnl variables. Defines the Autoconf variable PATH_KRB5_CONFIG, which can be dnl used to find the default path to krb5-config. dnl dnl Depends on INN_ENABLE_REDUCED_DEPENDS. dnl dnl The canonical version of this file is maintained in the rra-c-util dnl package, available at . dnl dnl Written by Russ Allbery dnl Copyright 2011, 2012 dnl The Board of Trustees of the Leland Stanford Junior University dnl dnl This file is free software; the authors give unlimited permission to copy dnl and/or distribute it, with or without modifications, as long as this dnl notice is preserved. dnl Check for krb5-config in the user's path and set PATH_KRB5_CONFIG. This dnl is moved into a separate macro so that it can be loaded via AC_REQUIRE, dnl meaning it will only be run once even if we link with multiple krb5-config dnl libraries. AC_DEFUN([_INN_KRB5_CONFIG_PATH], [AC_ARG_VAR([PATH_KRB5_CONFIG], [Path to krb5-config]) AC_PATH_PROG([PATH_KRB5_CONFIG], [krb5-config], [], [${PATH}:/usr/kerberos/bin])]) dnl Check whether the --deps flag is supported by krb5-config. Takes the path dnl to krb5-config to use. Note that this path is not embedded in the cache dnl variable, so this macro implicitly assumes that we will always use the dnl same krb5-config program. AC_DEFUN([_INN_KRB5_CONFIG_DEPS], [AC_REQUIRE([_INN_KRB5_CONFIG_PATH]) AC_CACHE_CHECK([for --deps support in krb5-config], [inn_cv_krb5_config_deps], [AS_IF(["$1" 2>&1 | grep deps >/dev/null 2>&1], [inn_cv_krb5_config_deps=yes], [inn_cv_krb5_config_deps=no])])]) dnl Obtain the library flags for a particular library using krb5-config. dnl Takes the path to the krb5-config program to use, the argument to dnl krb5-config to use, and the variable prefix under which to store the dnl library flags. AC_DEFUN([_INN_KRB5_CONFIG_LIBS], [AC_REQUIRE([_INN_KRB5_CONFIG_PATH]) AC_REQUIRE([INN_ENABLE_REDUCED_DEPENDS]) _INN_KRB5_CONFIG_DEPS([$1]) AS_IF([test x"$inn_reduced_depends" = xfalse \ && test x"$inn_cv_krb5_config_deps" = xyes], [$3[]_LIBS=`"$1" --deps --libs $2 2>/dev/null`], [$3[]_LIBS=`"$1" --libs $2 2>/dev/null`])]) dnl Attempt to find the flags for a library using krb5-config. Takes the dnl following arguments (in order): dnl dnl 1. The root directory for the library in question, generally from an dnl Autoconf --with flag. Used by preference as the path to krb5-config. dnl dnl 2. The argument to krb5-config to retrieve flags for this particular dnl library. dnl dnl 3. The variable prefix to use when setting CPPFLAGS and LIBS variables dnl based on the result of krb5-config. dnl dnl 4. Further actions to take if krb5-config was found and supported that dnl library type. dnl dnl 5. Further actions to take if krb5-config could not be used to get flags dnl for that library type. dnl dnl Special-case a krb5-config argument of krb5 and run krb5-config without an dnl argument if that option was requested and not supported. Old versions of dnl krb5-config didn't take an argument to specify the library type, but dnl always returned the flags for libkrb5. AC_DEFUN([INN_KRB5_CONFIG], [AC_REQUIRE([_INN_KRB5_CONFIG_PATH]) inn_krb5_config_$3= inn_krb5_config_$3[]_ok= AS_IF([test x"$1" != x && test -x "$1/bin/krb5-config"], [inn_krb5_config_$3="$1/bin/krb5-config"], [inn_krb5_config_$3="$PATH_KRB5_CONFIG"]) AS_IF([test x"$inn_krb5_config_$3" != x && test -x "$inn_krb5_config_$3"], [AC_CACHE_CHECK([for $2 support in krb5-config], [inn_cv_lib_$3[]_config], [AS_IF(["$inn_krb5_config_$3" 2>&1 | grep $2 >/dev/null 2>&1], [inn_cv_lib_$3[]_config=yes], [inn_cv_lib_$3[]_config=no])]) AS_IF([test "$inn_cv_lib_$3[]_config" = yes], [$3[]_CPPFLAGS=`"$inn_krb5_config_$3" --cflags $2 2>/dev/null` _INN_KRB5_CONFIG_LIBS([$inn_krb5_config_$3], [$2], [$3]) inn_krb5_config_$3[]_ok=yes], [AS_IF([test x"$2" = xkrb5], [$3[]_CPPFLAGS=`"$inn_krb5_config_$3" --cflags 2>/dev/null` $3[]_LIBS=`"$inn_krb5_config_$3" --libs $2 2>/dev/null` inn_krb5_config_$3[]_ok=yes])])]) AS_IF([test x"$inn_krb5_config_$3[]_ok" = xyes], [$3[]_CPPFLAGS=`echo "$$3[]_CPPFLAGS" | sed 's%-I/usr/include %%'` $3[]_CPPFLAGS=`echo "$$3[]_CPPFLAGS" | sed 's%-I/usr/include$%%'` $4], [$5])]) inn-2.6.0/m4/cc-flags.m40000644000175200017520000000166612575023702014172 0ustar iuliusiuliusdnl cc-flags.m4 -- Checks whether the compiler supports a given flag. dnl $Id: cc-flags.m4 8312 2009-01-31 20:35:04Z iulius $ dnl dnl Used to check whether a compiler supports a given flag. If it does, the dnl commands in the second argument are run. If not, the commands in the dnl third argument are run. dnl Used to build the result cache name. AC_DEFUN([_INN_PROG_CC_FLAG_CACHE], [translit([inn_cv_compiler_c_$1], [-], [_])]) AC_DEFUN([INN_PROG_CC_FLAG], [AC_REQUIRE([AC_PROG_CC]) AC_MSG_CHECKING([if $CC supports $1]) AC_CACHE_VAL([_INN_PROG_CC_FLAG_CACHE([$1])], [save_CFLAGS=$CFLAGS CFLAGS="$CFLAGS $1" AC_COMPILE_IFELSE( [AC_LANG_PROGRAM([[]], [[int foo = 0;]])], [_INN_PROG_CC_FLAG_CACHE([$1])=yes], [_INN_PROG_CC_FLAG_CACHE([$1])=no]) CFLAGS=$save_CFLAGS]) AC_MSG_RESULT($_INN_PROG_CC_FLAG_CACHE([$1])) if test x"$_INN_PROG_CC_FLAG_CACHE([$1])" = xyes ; then ifelse([$2], , :, [$2]) else ifelse([$3], , :, [$3]) fi]) inn-2.6.0/m4/socket-unix.m40000644000175200017520000000626212575023702014761 0ustar iuliusiuliusdnl Various checks for UNIX domain socket support and macros. dnl $Id: socket-unix.m4 9549 2013-10-26 18:36:31Z iulius $ dnl dnl This is a collection of various Autoconf macros for checking UNIX domain dnl socket properties. The macros provided are: dnl dnl INN_MACRO_SUN_LEN dnl INN_SYS_UNIX_SOCKETS dnl dnl They use a separate internal source macro to make the code easier to read. dnl dnl The canonical version of this file is maintained in the rra-c-util dnl package, available at . dnl dnl Copyright 2009 dnl The Board of Trustees of the Leland Stanford Junior University dnl Copyright (c) 2004, 2005, 2006, 2007, 2008, 2009 dnl by Internet Systems Consortium, Inc. ("ISC") dnl Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, dnl 2002, 2003 by The Internet Software Consortium and Rich Salz dnl dnl This code is derived from software contributed to the Internet Software dnl Consortium by Rich Salz. dnl dnl Permission to use, copy, modify, and distribute this software for any dnl purpose with or without fee is hereby granted, provided that the above dnl copyright notice and this permission notice appear in all copies. dnl dnl THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH dnl REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF dnl MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY dnl SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES dnl WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN dnl ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR dnl IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. dnl Source used by INN_MACRO_SUN_LEN. AC_DEFUN([_INN_MACRO_SUN_LEN_SOURCE], [[ #include #include int main(void) { struct sockaddr_un s_un; int i = SUN_LEN(&s_un); } ]]) dnl Check for SUN_LEN, which returns the size of a struct sockaddr_un. Sets dnl HAVE_SUN_LEN if the macro is available. AC_DEFUN([INN_MACRO_SUN_LEN], [AC_CACHE_CHECK([for SUN_LEN macro], [inn_cv_sun_len_macro], [AC_LINK_IFELSE([AC_LANG_SOURCE([_INN_MACRO_SUN_LEN_SOURCE])], [inn_cv_sun_len_macro=yes], [inn_cv_sun_len_macro=no])]) AS_IF([test x"$inn_cv_sun_len_macro" = xyes], [AC_DEFINE([HAVE_SUN_LEN], 1, [Define if defines the SUN_LEN macro.])])]) dnl Source used by INN_SYS_UNIX_SOCKETS. AC_DEFUN([_INN_SYS_UNIX_SOCKETS], [[ #include #include #ifndef AF_UNIX error: No UNIX domain sockets! #endif ]]) dnl Check if UNIX domain sockets are supported. Assume that they are if dnl AF_UNIX is set in . This loses on really old versions of dnl Linux, where AF_UNIX is available but doesn't work, but we don't care dnl about Linux 1.0 any more. AC_DEFUN([INN_SYS_UNIX_SOCKETS], [AC_CACHE_CHECK([for UNIX domain sockets], [inn_cv_sys_unix_sockets], [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_INN_SYS_UNIX_SOCKETS])], [inn_cv_sys_unix_sockets=yes], [inn_cv_sys_unix_sockets=no])]) AS_IF([test x"$inn_cv_sys_unix_sockets" = xyes], [AC_DEFINE([HAVE_UNIX_DOMAIN_SOCKETS], 1, [Define if you have UNIX domain sockets.])])]) inn-2.6.0/m4/prog-ensure.m40000644000175200017520000000067012575023702014753 0ustar iuliusiuliusdnl prog-ensure.m4 -- Require that a program be found in the PATH. dnl $Id: prog-ensure.m4 8475 2009-05-17 20:30:23Z iulius $ dnl dnl This is a version of AC_PATH_PROG that requires that the program being dnl searched for is found in the user's PATH. AC_DEFUN([INN_PATH_PROG_ENSURE], [if test x"${$1}" = x ; then AC_PATH_PROG([$1], [$2]) fi if test x"${$1}" = x ; then AC_MSG_ERROR([$2 was not found in path and is required]) fi]) inn-2.6.0/m4/krb5.m40000644000175200017520000003670212575023702013355 0ustar iuliusiuliusdnl Find the compiler and linker flags for Kerberos. dnl $Id: krb5.m4 9792 2015-03-17 20:10:25Z iulius $ dnl dnl Finds the compiler and linker flags for linking with Kerberos libraries. dnl Provides the --with-krb5, --with-krb5-include, and --with-krb5-lib dnl configure options to specify non-standard paths to the Kerberos libraries. dnl Uses krb5-config where available unless reduced dependencies is requested dnl or --with-krb5-include or --with-krb5-lib are given. dnl dnl Provides the macro INN_LIB_KRB5 and sets the substitution variables dnl KRB5_CPPFLAGS, KRB5_LDFLAGS, and KRB5_LIBS. Also provides dnl INN_LIB_KRB5_SWITCH to set CPPFLAGS, LDFLAGS, and LIBS to include the dnl Kerberos libraries, saving the current values first, and dnl INN_LIB_KRB5_RESTORE to restore those settings to before the last dnl INN_LIB_KRB5_SWITCH. HAVE_KRB5 will always be defined if INN_LIB_KRB5 is dnl used. dnl dnl If KRB5_CPPFLAGS, KRB5_LDFLAGS, or KRB5_LIBS are set before calling these dnl macros, their values will be added to whatever the macros discover. dnl dnl KRB5_CPPFLAGS_GCC will be set to the same value as KRB5_CPPFLAGS but with dnl any occurrences of -I changed to -isystem. This may be useful to suppress dnl warnings from the Kerberos header files when building with GCC and dnl aggressive warning flags. Be aware that this change will change the dnl compiler header file search order as well. dnl dnl Provides the INN_LIB_KRB5_OPTIONAL macro, which should be used if Kerberos dnl support is optional. In this case, Kerberos libraries are mandatory if dnl --with-krb5 is given, and will not be probed for if --without-krb5 is dnl given. Otherwise, they'll be probed for but will not be required. dnl Defines HAVE_KRB5 and sets inn_use_KRB5 to true if the libraries are dnl found. The substitution variables will always be set, but they will be dnl empty unless Kerberos libraries are found and the user did not disable dnl Kerberos support. dnl dnl Sets the Automake conditional KRB5_USES_COM_ERR saying whether we use dnl com_err, since if we're also linking with AFS libraries, we may have to dnl change library ordering in that case. dnl dnl Depends on INN_KRB5_CONFIG, INN_ENABLE_REDUCED_DEPENDS, and dnl INN_SET_LDFLAGS. dnl dnl Also provides INN_FUNC_KRB5_GET_INIT_CREDS_OPT_FREE_ARGS, which checks dnl whether krb5_get_init_creds_opt_free takes one argument or two. Defines dnl HAVE_KRB5_GET_INIT_CREDS_OPT_FREE_2_ARGS if it takes two arguments. dnl dnl Also provides INN_INCLUDES_KRB5, which are the headers to include when dnl probing the Kerberos library properties. dnl dnl The canonical version of this file is maintained in the rra-c-util dnl package, available at . dnl dnl Written by Russ Allbery dnl Copyright 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2013, 2014 dnl The Board of Trustees of the Leland Stanford Junior University dnl dnl This file is free software; the authors give unlimited permission to copy dnl and/or distribute it, with or without modifications, as long as this dnl notice is preserved. dnl Ignore Automake conditionals if not using Automake. m4_define_default([AM_CONDITIONAL], [:]) dnl Headers to include when probing for Kerberos library properties. AC_DEFUN([INN_INCLUDES_KRB5], [[ #if HAVE_KRB5_H # include #elif HAVE_KERBEROSV5_KRB5_H # include #else # include #endif ]]) dnl Save the current CPPFLAGS, LDFLAGS, and LIBS settings and switch to dnl versions that include the Kerberos flags. Used as a wrapper, with dnl INN_LIB_KRB5_RESTORE, around tests. AC_DEFUN([INN_LIB_KRB5_SWITCH], [inn_krb5_save_CPPFLAGS="$CPPFLAGS" inn_krb5_save_LDFLAGS="$LDFLAGS" inn_krb5_save_LIBS="$LIBS" CPPFLAGS="$KRB5_CPPFLAGS $CPPFLAGS" LDFLAGS="$KRB5_LDFLAGS $LDFLAGS" LIBS="$KRB5_LIBS $LIBS"]) dnl Restore CPPFLAGS, LDFLAGS, and LIBS to their previous values (before dnl INN_LIB_KRB5_SWITCH was called). AC_DEFUN([INN_LIB_KRB5_RESTORE], [CPPFLAGS="$inn_krb5_save_CPPFLAGS" LDFLAGS="$inn_krb5_save_LDFLAGS" LIBS="$inn_krb5_save_LIBS"]) dnl Set KRB5_CPPFLAGS and KRB5_LDFLAGS based on inn_krb5_root, dnl inn_krb5_libdir, and inn_krb5_includedir. AC_DEFUN([_INN_LIB_KRB5_PATHS], [AS_IF([test x"$inn_krb5_libdir" != x], [KRB5_LDFLAGS="-L$inn_krb5_libdir"], [AS_IF([test x"$inn_krb5_root" != x], [INN_SET_LDFLAGS([KRB5_LDFLAGS], [$inn_krb5_root])])]) AS_IF([test x"$inn_krb5_includedir" != x], [KRB5_CPPFLAGS="-I$inn_krb5_includedir"], [AS_IF([test x"$inn_krb5_root" != x], [AS_IF([test x"$inn_krb5_root" != x/usr], [KRB5_CPPFLAGS="-I${inn_krb5_root}/include"])])])]) dnl Check for a header using a file existence check rather than using dnl AC_CHECK_HEADERS. This is used if there were arguments to configure dnl specifying the Kerberos header path, since we may have one header in the dnl default include path and another under our explicitly-configured Kerberos dnl location. AC_DEFUN([_INN_LIB_KRB5_CHECK_HEADER], [AC_MSG_CHECKING([for $1]) AS_IF([test -f "${inn_krb5_incroot}/$1"], [AC_DEFINE_UNQUOTED(AS_TR_CPP([HAVE_$1]), [1], [Define to 1 if you have the <$1> header file.]) AC_MSG_RESULT([yes])], [AC_MSG_RESULT([no])])]) dnl Check for the com_err header. Internal helper macro since we need dnl to do the same checks in multiple places. AC_DEFUN([_INN_LIB_KRB5_CHECK_HEADER_COM_ERR], [AS_IF([test x"$inn_krb5_incroot" = x], [AC_CHECK_HEADERS([et/com_err.h kerberosv5/com_err.h])], [_INN_LIB_KRB5_CHECK_HEADER([et/com_err.h]) _INN_LIB_KRB5_CHECK_HEADER([kerberosv5/com_err.h])])]) dnl Check for the main Kerberos header. Internal helper macro since we need dnl to do the same checks in multiple places. AC_DEFUN([_INN_LIB_KRB5_CHECK_HEADER_KRB5], [AS_IF([test x"$inn_krb5_incroot" = x], [AC_CHECK_HEADERS([krb5.h kerberosv5/krb5.h krb5/krb5.h])], [_INN_LIB_KRB5_CHECK_HEADER([krb5.h]) _INN_LIB_KRB5_CHECK_HEADER([kerberosv5/krb5.h]) _INN_LIB_KRB5_CHECK_HEADER([krb5/krb5.h])])]) dnl Does the appropriate library checks for reduced-dependency Kerberos dnl linkage. The single argument, if true, says to fail if Kerberos could not dnl be found. AC_DEFUN([_INN_LIB_KRB5_REDUCED], [INN_LIB_KRB5_SWITCH AC_CHECK_LIB([krb5], [krb5_init_context], [KRB5_LIBS="-lkrb5"], [AS_IF([test x"$1" = xtrue], [AC_MSG_ERROR([cannot find usable Kerberos library])])]) LIBS="$KRB5_LIBS $LIBS" _INN_LIB_KRB5_CHECK_HEADER_KRB5 AC_CHECK_FUNCS([krb5_get_error_message], [AC_CHECK_FUNCS([krb5_free_error_message])], [AC_CHECK_FUNCS([krb5_get_error_string], [], [AC_CHECK_FUNCS([krb5_get_err_txt], [], [AC_CHECK_LIB([ksvc], [krb5_svc_get_msg], [KRB5_LIBS="$KRB5_LIBS -lksvc" AC_DEFINE([HAVE_KRB5_SVC_GET_MSG], [1]) AC_CHECK_HEADERS([ibm_svc/krb5_svc.h], [], [], [INN_INCLUDES_KRB5])], [AC_CHECK_LIB([com_err], [com_err], [KRB5_LIBS="$KRB5_LIBS -lcom_err"], [AS_IF([test x"$1" = xtrue], [AC_MSG_ERROR([cannot find usable com_err library])], [KRB5_LIBS=""])]) _INN_LIB_KRB5_CHECK_HEADER_COM_ERR])])])]) INN_LIB_KRB5_RESTORE]) dnl Does the appropriate library checks for Kerberos linkage when we don't dnl have krb5-config or reduced dependencies. The single argument, if true, dnl says to fail if Kerberos could not be found. AC_DEFUN([_INN_LIB_KRB5_MANUAL], [INN_LIB_KRB5_SWITCH inn_krb5_extra= LIBS= AC_SEARCH_LIBS([res_search], [resolv], [], [AC_SEARCH_LIBS([__res_search], [resolv])]) AC_SEARCH_LIBS([gethostbyname], [nsl]) AC_SEARCH_LIBS([socket], [socket], [], [AC_CHECK_LIB([nsl], [socket], [LIBS="-lnsl -lsocket $LIBS"], [], [-lsocket])]) AC_SEARCH_LIBS([crypt], [crypt]) AC_SEARCH_LIBS([roken_concat], [roken]) inn_krb5_extra="$LIBS" LIBS="$inn_krb5_save_LIBS" AC_CHECK_LIB([krb5], [krb5_init_context], [KRB5_LIBS="-lkrb5 -lasn1 -lcom_err -lcrypto $inn_krb5_extra"], [AC_CHECK_LIB([krb5support], [krb5int_getspecific], [inn_krb5_extra="-lkrb5support $inn_krb5_extra"], [AC_CHECK_LIB([pthreads], [pthread_setspecific], [inn_krb5_pthread="-lpthreads"], [AC_CHECK_LIB([pthread], [pthread_setspecific], [inn_krb5_pthread="-lpthread"])]) AC_CHECK_LIB([krb5support], [krb5int_setspecific], [inn_krb5_extra="-lkrb5support $inn_krb5_extra $inn_krb5_pthread"], [], [$inn_krb5_pthread $inn_krb5_extra])], [$inn_krb5_extra]) AC_CHECK_LIB([com_err], [error_message], [inn_krb5_extra="-lcom_err $inn_krb5_extra"], [], [$inn_krb5_extra]) AC_CHECK_LIB([ksvc], [krb5_svc_get_msg], [inn_krb5_extra="-lksvc $inn_krb5_extra"], [], [$inn_krb5_extra]) AC_CHECK_LIB([k5crypto], [krb5int_hash_md5], [inn_krb5_extra="-lk5crypto $inn_krb5_extra"], [], [$inn_krb5_extra]) AC_CHECK_LIB([k5profile], [profile_get_values], [inn_krb5_extra="-lk5profile $inn_krb5_extra"], [], [$inn_krb5_extra]) AC_CHECK_LIB([krb5], [krb5_cc_default], [KRB5_LIBS="-lkrb5 $inn_krb5_extra"], [AS_IF([test x"$1" = xtrue], [AC_MSG_ERROR([cannot find usable Kerberos library])])], [$inn_krb5_extra])], [-lasn1 -lcom_err -lcrypto $inn_krb5_extra]) LIBS="$KRB5_LIBS $LIBS" _INN_LIB_KRB5_CHECK_HEADER_KRB5 AC_CHECK_FUNCS([krb5_get_error_message], [AC_CHECK_FUNCS([krb5_free_error_message])], [AC_CHECK_FUNCS([krb5_get_error_string], [], [AC_CHECK_FUNCS([krb5_get_err_txt], [], [AC_CHECK_FUNCS([krb5_svc_get_msg], [AC_CHECK_HEADERS([ibm_svc/krb5_svc.h], [], [], [INN_INCLUDES_KRB5])], [_INN_LIB_KRB5_CHECK_HEADER_COM_ERR])])])]) INN_LIB_KRB5_RESTORE]) dnl Sanity-check the results of krb5-config and be sure we can really link a dnl Kerberos program. If that fails, clear KRB5_CPPFLAGS and KRB5_LIBS so dnl that we know we don't have usable flags and fall back on the manual dnl check. AC_DEFUN([_INN_LIB_KRB5_CHECK], [INN_LIB_KRB5_SWITCH AC_CHECK_FUNC([krb5_init_context], [INN_LIB_KRB5_RESTORE], [INN_LIB_KRB5_RESTORE KRB5_CPPFLAGS= KRB5_LIBS= _INN_LIB_KRB5_PATHS _INN_LIB_KRB5_MANUAL([$1])])]) dnl Determine Kerberos compiler and linker flags from krb5-config. Does the dnl additional probing we need to do to uncover error handling features, and dnl falls back on the manual checks. AC_DEFUN([_INN_LIB_KRB5_CONFIG], [INN_KRB5_CONFIG([${inn_krb5_root}], [krb5], [KRB5], [_INN_LIB_KRB5_CHECK([$1]) INN_LIB_KRB5_SWITCH _INN_LIB_KRB5_CHECK_HEADER_KRB5 AC_CHECK_FUNCS([krb5_get_error_message], [AC_CHECK_FUNCS([krb5_free_error_message])], [AC_CHECK_FUNCS([krb5_get_error_string], [], [AC_CHECK_FUNCS([krb5_get_err_txt], [], [AC_CHECK_FUNCS([krb5_svc_get_msg], [AC_CHECK_HEADERS([ibm_svc/krb5_svc.h], [], [], [INN_INCLUDES_KRB5])], [_INN_LIB_KRB5_CHECK_HEADER_COM_ERR])])])]) INN_LIB_KRB5_RESTORE], [_INN_LIB_KRB5_PATHS _INN_LIB_KRB5_MANUAL([$1])])]) dnl The core of the library checking, shared between INN_LIB_KRB5 and dnl INN_LIB_KRB5_OPTIONAL. The single argument, if "true", says to fail if dnl Kerberos could not be found. Set up inn_krb5_incroot for later header dnl checking. AC_DEFUN([_INN_LIB_KRB5_INTERNAL], [AC_REQUIRE([INN_ENABLE_REDUCED_DEPENDS]) inn_krb5_incroot= AC_SUBST([KRB5_CPPFLAGS]) AC_SUBST([KRB5_CPPFLAGS_GCC]) AC_SUBST([KRB5_LDFLAGS]) AC_SUBST([KRB5_LIBS]) AS_IF([test x"$inn_krb5_includedir" != x], [inn_krb5_incroot="$inn_krb5_includedir"], [AS_IF([test x"$inn_krb5_root" != x], [inn_krb5_incroot="${inn_krb5_root}/include"])]) AS_IF([test x"$inn_reduced_depends" = xtrue], [_INN_LIB_KRB5_PATHS _INN_LIB_KRB5_REDUCED([$1])], [AS_IF([test x"$inn_krb5_includedir" = x && test x"$inn_krb5_libdir" = x], [_INN_LIB_KRB5_CONFIG([$1])], [_INN_LIB_KRB5_PATHS _INN_LIB_KRB5_MANUAL([$1])])]) inn_krb5_uses_com_err=false AS_CASE([$KRB5_LIBS], [*-lcom_err*], [inn_krb5_uses_com_err=true]) AM_CONDITIONAL([KRB5_USES_COM_ERR], [test x"$inn_krb5_uses_com_err" = xtrue]) KRB5_CPPFLAGS_GCC=`echo "$KRB5_CPPFLAGS" | sed -e 's/-I/-isystem /g'`]) dnl The main macro for packages with mandatory Kerberos support. AC_DEFUN([INN_LIB_KRB5], [inn_krb5_root= inn_krb5_libdir= inn_krb5_includedir= inn_use_KRB5=true AC_ARG_WITH([krb5], [AS_HELP_STRING([--with-krb5=DIR], [Location of Kerberos headers and libraries])], [AS_IF([test x"$withval" != xyes && test x"$withval" != xno], [inn_krb5_root="$withval"])]) AC_ARG_WITH([krb5-include], [AS_HELP_STRING([--with-krb5-include=DIR], [Location of Kerberos headers])], [AS_IF([test x"$withval" != xyes && test x"$withval" != xno], [inn_krb5_includedir="$withval"])]) AC_ARG_WITH([krb5-lib], [AS_HELP_STRING([--with-krb5-lib=DIR], [Location of Kerberos libraries])], [AS_IF([test x"$withval" != xyes && test x"$withval" != xno], [inn_krb5_libdir="$withval"])]) _INN_LIB_KRB5_INTERNAL([true]) AC_DEFINE([HAVE_KRB5], 1, [Define to enable Kerberos features.])]) dnl The main macro for packages with optional Kerberos support. AC_DEFUN([INN_LIB_KRB5_OPTIONAL], [inn_krb5_root= inn_krb5_libdir= inn_krb5_includedir= inn_use_KRB5= AC_ARG_WITH([krb5], [AS_HELP_STRING([--with-krb5@<:@=DIR@:>@], [Location of Kerberos headers and libraries])], [AS_IF([test x"$withval" = xno], [inn_use_KRB5=false], [AS_IF([test x"$withval" != xyes], [inn_krb5_root="$withval"]) inn_use_KRB5=true])]) AC_ARG_WITH([krb5-include], [AS_HELP_STRING([--with-krb5-include=DIR], [Location of Kerberos headers])], [AS_IF([test x"$withval" != xyes && test x"$withval" != xno], [inn_krb5_includedir="$withval"])]) AC_ARG_WITH([krb5-lib], [AS_HELP_STRING([--with-krb5-lib=DIR], [Location of Kerberos libraries])], [AS_IF([test x"$withval" != xyes && test x"$withval" != xno], [inn_krb5_libdir="$withval"])]) AS_IF([test x"$inn_use_KRB5" != xfalse], [AS_IF([test x"$inn_use_KRB5" = xtrue], [_INN_LIB_KRB5_INTERNAL([true])], [_INN_LIB_KRB5_INTERNAL([false])])], [AM_CONDITIONAL([KRB5_USES_COM_ERR], [false])]) AS_IF([test x"$KRB5_LIBS" != x], [inn_use_KRB5=true AC_DEFINE([HAVE_KRB5], 1, [Define to enable Kerberos features.])])]) dnl Source used by INN_FUNC_KRB5_GET_INIT_CREDS_OPT_FREE_ARGS. AC_DEFUN([_INN_FUNC_KRB5_OPT_FREE_ARGS_SOURCE], [INN_INCLUDES_KRB5] [[ int main(void) { krb5_get_init_creds_opt *opts; krb5_context c; krb5_get_init_creds_opt_free(c, opts); } ]]) dnl Check whether krb5_get_init_creds_opt_free takes one argument or two. dnl Early Heimdal used to take a single argument. Defines dnl HAVE_KRB5_GET_INIT_CREDS_OPT_FREE_2_ARGS if it takes two arguments. dnl dnl Should be called with INN_LIB_KRB5_SWITCH active. AC_DEFUN([INN_FUNC_KRB5_GET_INIT_CREDS_OPT_FREE_ARGS], [AC_CACHE_CHECK([if krb5_get_init_creds_opt_free takes two arguments], [inn_cv_func_krb5_get_init_creds_opt_free_args], [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_INN_FUNC_KRB5_OPT_FREE_ARGS_SOURCE])], [inn_cv_func_krb5_get_init_creds_opt_free_args=yes], [inn_cv_func_krb5_get_init_creds_opt_free_args=no])]) AS_IF([test $inn_cv_func_krb5_get_init_creds_opt_free_args = yes], [AC_DEFINE([HAVE_KRB5_GET_INIT_CREDS_OPT_FREE_2_ARGS], 1, [Define if krb5_get_init_creds_opt_free takes two arguments.])])]) inn-2.6.0/m4/pam-const.m40000644000175200017520000000460312575023702014406 0ustar iuliusiuliusdnl Determine whether PAM uses const in prototypes. dnl $Id: pam-const.m4 9792 2015-03-17 20:10:25Z iulius $ dnl dnl Linux marks several PAM arguments const, including the argument to dnl pam_get_item and some arguments to conversation functions, which Solaris dnl doesn't. Mac OS X marks the first argument to pam_strerror const, and dnl other platforms don't. This test tries to determine which style is in use dnl to select whether to declare variables const and how to prototype dnl functions in order to avoid compiler warnings. dnl dnl Since this is just for compiler warnings, it's not horribly important if dnl we guess wrong. This test is ugly, but it seems to work. dnl dnl Contributed by Markus Moeller. dnl dnl The canonical version of this file is maintained in the rra-c-util dnl package, available at . dnl dnl Copyright 2007, 2015 Russ Allbery dnl Copyright 2007, 2008 Markus Moeller dnl dnl This file is free software; the authors give unlimited permission to copy dnl and/or distribute it, with or without modifications, as long as this dnl notice is preserved. dnl Source used by INN_HEADER_PAM_CONST. AC_DEFUN([_INN_HEADER_PAM_CONST_SOURCE], [#ifdef HAVE_SECURITY_PAM_APPL_H # include #else # include #endif ]) AC_DEFUN([INN_HEADER_PAM_CONST], [AC_CACHE_CHECK([whether PAM prefers const], [inn_cv_header_pam_const], [AC_EGREP_CPP([const void \*\* *_?item], _INN_HEADER_PAM_CONST_SOURCE(), [inn_cv_header_pam_const=yes], [inn_cv_header_pam_const=no])]) AS_IF([test x"$inn_cv_header_pam_const" = xyes], [inn_header_pam_const=const], [inn_header_pam_const=]) AC_DEFINE_UNQUOTED([PAM_CONST], [$inn_header_pam_const], [Define to const if PAM uses const in pam_get_item, empty otherwise.])]) AC_DEFUN([INN_HEADER_PAM_STRERROR_CONST], [AC_CACHE_CHECK([whether pam_strerror uses const], [inn_cv_header_pam_strerror_const], [AC_EGREP_CPP([pam_strerror *\(const], _INN_HEADER_PAM_CONST_SOURCE(), [inn_cv_header_pam_strerror_const=yes], [inn_cv_header_pam_strerror_const=no])]) AS_IF([test x"$inn_cv_header_pam_strerror_const" = xyes], [inn_header_pam_strerror_const=const], [inn_header_pam_strerror_const=]) AC_DEFINE_UNQUOTED([PAM_STRERROR_CONST], [$inn_header_pam_strerror_const], [Define to const if PAM uses const in pam_strerror, empty otherwise.])]) inn-2.6.0/m4/users.m40000644000175200017520000000222712575023702013646 0ustar iuliusiuliusdnl users.m4 -- The usernames built into INN at compile time. dnl $Id: users.m4 8495 2009-05-24 12:28:19Z iulius $ dnl dnl INN allows the user and group INN will run as to be specified, as well as dnl the user to receive nightly reports and the like. dnl The settings are all fairly similar, so factor the commonality into this dnl macro. Takes the name of what we're looking for, the default, the dnl variable to set, the help string, and the comment for config.h. AC_DEFUN([_INN_ARG_USER], [AC_ARG_WITH([news-$1], [$4], [$3=$with_news_$1], [$3=$2]) AC_SUBST($3) AC_DEFINE_UNQUOTED($3, "$[$3]", [$5])]) dnl And here they are. AC_DEFUN([INN_ARG_USERS], [_INN_ARG_USER([user], [news], [RUNASUSER], [AS_HELP_STRING([--with-news-user=USER], [News user name [news]])], [The user that INN should run as.]) _INN_ARG_USER([group], [news], [RUNASGROUP], [AS_HELP_STRING([--with-news-group=GROUP], [News group name [news]])], [The group that INN should run as.]) _INN_ARG_USER([master], [usenet], [NEWSMASTER], [AS_HELP_STRING([--with-news-master=USER], [News master (address for reports) [usenet]])], [The user who gets all INN-related e-mail.])]) inn-2.6.0/m4/cc-c-o.m40000644000175200017520000000357512575023702013555 0ustar iuliusiuliusdnl cc-c-o.m4 -- Checks whether -o can be used with -c. dnl $Id: cc-c-o.m4 8407 2009-04-11 17:16:06Z iulius $ dnl dnl Used to check whether -o can be provided with -c with the chosen compiler. dnl We need this if we're not using libtool so that object files can be built dnl in subdirectories. This macro is stolen shamelessly from the libtool dnl macros. Note that we don't use the Autoconf version because it both dnl checks too much (checking cc as well as the chosen compiler) and it sets a dnl compiler #define rather than setting a variable we can use. dnl dnl $compiler_c_o is set to yes if the compiler supports this and no if not. AC_DEFUN([INN_PROG_CC_C_O], [AC_REQUIRE([AC_OBJEXT]) AC_MSG_CHECKING([if $CC supports -c -o file.$ac_objext]) AC_CACHE_VAL([inn_cv_compiler_c_o], [rm -f -r conftest 2>/dev/null mkdir conftest cd conftest echo "int some_variable = 0;" > conftest.$ac_ext mkdir out # According to Tom Tromey, Ian Lance Taylor reported there are C compilers # that will create temporary files in the current directory regardless of # the output directory. Thus, making CWD read-only will cause this test # to fail, enabling locking or at least warning the user not to do parallel # builds. chmod -w . save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -o out/conftest2.$ac_objext" compiler_c_o=no if { (eval $ac_compile) 2> out/conftest.err; } \ && test -s out/conftest2.$ac_objext; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings if test -s out/conftest.err; then inn_cv_compiler_c_o=no else inn_cv_compiler_c_o=yes fi else # Append any errors to the config.log. cat out/conftest.err 1>&AS_MESSAGE_LOG_FD inn_cv_compiler_c_o=no fi CFLAGS="$save_CFLAGS" chmod u+w . rm -f conftest* out/* rmdir out cd .. rmdir conftest rm -f -r conftest 2>/dev/null]) compiler_c_o=$inn_cv_compiler_c_o AC_MSG_RESULT([$compiler_c_o])]) inn-2.6.0/m4/ltversion.m40000644000175200017520000000126212575023702014530 0ustar iuliusiulius# ltversion.m4 -- version numbers -*- Autoconf -*- # # Copyright (C) 2004 Free Software Foundation, Inc. # Written by Scott James Remnant, 2004 # # This file is free software; the Free Software Foundation gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. # @configure_input@ # serial 3337 ltversion.m4 # This file is part of GNU Libtool m4_define([LT_PACKAGE_VERSION], [2.4.2]) m4_define([LT_PACKAGE_REVISION], [1.3337]) AC_DEFUN([LTVERSION_VERSION], [macro_version='2.4.2' macro_revision='1.3337' _LT_DECL(, macro_version, 0, [Which release of libtool.m4 was used?]) _LT_DECL(, macro_revision, 0) ]) inn-2.6.0/README0000644000175200017520000003477612575023702012621 0ustar iuliusiuliusWelcome to INN 2.6! This work is sponsored by Internet Systems Consortium. Please see INSTALL for installation instructions, NEWS for what's changed from the previous release, and LICENSE for the copyright, license, and distribution terms. What is INN? INN (InterNetNews), originally written by Rich Salz, is an extremely flexible and configurable Usenet / Netnews news server. For a complete description of the protocols behind Usenet and Netnews, see RFC 3977 (NNTP), RFC 4642 (TLS/NNTP), RFC 4643 (NNTP authentication), RFC 4644 (streaming NNTP feeds), RFC 5536 (USEFOR), RFC 5537 (USEPRO) and RFC 6048 (NNTP LIST additions) or their replacements. In brief, Netnews is a set of protocols for exchanging messages between a decentralized network of news servers. News articles are organized into newsgroups, which are themselves organized into hierarchies. Each individual news server stores locally all articles it has received for a given newsgroup, making access to stored articles extremely fast. Netnews does not require any central server; instead, each news server passes along articles it receives to all of the news servers it peers with, those servers pass the articles along to their peers, and so on, resulting in "flood fill" propagation of news articles. A news server performs three basic functions: it accepts articles from other servers and stores them on disk, sends articles it has received out to other servers, and offers stored news articles to readers on demand. It additionally has to perform some periodic maintenance tasks, such as deleting older articles to make room for new ones. Originally, a news server would just store all of the news articles it had received in a file system. Users could then read news by reading the article files on disk (or more commonly using news reading software that did this efficiently). These days, news servers are almost always stand-alone systems and news reading is supported via network connections. A user who wants to read a newsgroup opens that newsgroup in their newsreader software, which opens a network connection to the news server and sends requests for articles and related information. The protocol that a newsreader uses to talk to a news server and that a news server uses to talk to another news server over TCP/IP is called NNTP (Network News Transport Protocol). INN supports accepting articles via either NNTP connections or via UUCP. innd, the heart of INN, handles NNTP feeding connections directly; UUCP newsfeeds use rnews (included in INN) to hand articles off to innd. Other parts of INN handle feeding articles out to other news servers, most commonly innfeed (for real-time outgoing feeds) or nntpsend and innxmit (used to send batches of news created by innd to a remote site via TCP/IP). INN can also handle outgoing UUCP feeds. The part of INN that handles connections from newsreaders is nnrpd. Also included in INN are a wide variety of supporting programs to handle periodic maintenance and recovery from crashes, process special control messages, maintain the list of active newsgroups, and generate and record a staggering variety of statistics and summary information on the usage and performance of the server. INN also supports an extremely powerful filtering system that allows the server administrator to reject unwanted articles (such as spam and other abuses of Usenet). INN is free software, supported by Internet Systems Consortium and volunteers around the world. See "Supporting the INN Effort" below. Prerequisites Compiling INN requires an ANSI C compiler (gcc is recommended). INN was originally written in K&R C, but supporting pre-ANSI compilers has become enough of a headache that a lot of the newer parts of INN will no longer compile with a non-ANSI compiler. gcc itself will compile with most vendor non-ANSI compilers, however, so if you're stuck with one, installing gcc is highly recommended. Not only will it let you build INN, it will make installing lots of other software much easier. You may also need GNU make (particularly if your system make is BSD-derived), although most SysV make programs should work fine. Compiling INN also currently requires a yacc implementation (bison will do fine). INN uses GNU autoconf to probe the capabilities of your system, and therefore should compile on nearly any Unix system. It does, however, make extensive use of mmap(), which can cause problems on some older operating systems. See INSTALL for a list of systems it is known to work on. If you encounter problems compiling or running INN, or if you successfully run INN on a platform that isn't listed in INSTALL, please let us know (see "Reporting Bugs" below). Perl 5.004 or later is required to build INN and use the embedded Perl filter support (which is highly recommended; some excellent spam filters have been written for INN). Since all versions of Perl previous to 5.004 are buggy (including security problems) and have fewer features, installing Perl 5.004 or later (like at least Perl 5.8.0) is recommended. If you want to enable PGP verification of control messages (highly recommended), you will need to have a PGP implementation installed. See INSTALL for more details. Getting Started A news server can be a fairly complicated piece of software to set up just because of the wide variety of pieces that have to be configured (who is authorized to read from the server, what newsgroups it carries, and how the articles are stored on disk at a bare minimum, and if the server isn't completely stand-alone -- and very few servers are -- both incoming and outgoing feeds have to be set up and tested). Be prepared to take some time to understand what's going on and how all the pieces fit together. If you have any specific suggestions for documentation, or comments about things that are unclear, please send them to the INN maintainers (see "Reporting Bugs" below). See INSTALL for step-by-step instructions for setting up and configuring a news server. INN also comes with a very complete set of man pages; there is a man page for every configuration file and program that comes with INN. (If you find one that doesn't have a man page, that's a bug. Please do report it.) When trying to figure out some specific problem, reading the man pages for all of the configuration files involved is a very good start. Reporting Bugs We're interested in all bug reports. Not just on the programs, but on the documentation too. Please send *all* such reports to inn-workers@lists.isc.org (patches are certainly welcome, see below). Even if you post to Usenet, please CC the above address. If you have general "how do I do this" questions or problems configuring your server that you don't believe are due to a bug in INN, you should post them to news.software.nntp. A lot of experienced INN users, including several of the INN maintainers, read that newsgroup regularly. Please don't send general questions to the above addresses; those addresses are specifically for INN, and the INN maintainers usually won't have time to answer general questions. Contributing Code If you have a patch or a utility that you'd like to be considered for inclusion into INN, please mail it to inn-workers@lists.isc.org in the body of the message (not as an attachment because the mailing-list might strip it), or put it on a webpage and send a link. Patches included with a bug report as described above should follow the same procedure. Have fun! Mailing Lists There are various INN-related mailing lists you can join or send messages to if you like. Some of them you must be a member of before you can send mail to them (thank the spammers for that policy), and one of them is read-only (no postings allowed). inn-announce@lists.isc.org Where announcements about INN are set (only maintainers may post). inn-workers@lists.isc.org Discussion of INN development. It is also where to send bug reports and patches for consideration for inclusion into INN (postings by members only). If you're an INN expert and have the time to help out other users, we encourage you to join this mailing list to answer questions. (You may also want to read the newsgroup news.software.nntp, which gets a lot of INN-related questions.) inn-committers@lists.isc.org Subversion commit messages for INN are sent to this list (only the automated messages are sent here, no regular posting). inn-bugs@lists.isc.org Trac tickets for INN are sent to this list (only the automated messages are sent here, no regular posting). Bug reports should be sent to the inn-workers mailing list. To join these lists, send a subscription request to the "-request" address. The addresses for the above lists are: inn-announce-request@lists.isc.org inn-workers-request@lists.isc.org inn-committers-request@lists.isc.org inn-bugs-request@lists.isc.org Who's Responsible / Who to Thank See CONTRIBUTORS for a long list of past contributors as well as people from the inn-workers mailing list who have dedicated a lot of time and effort to getting this new version together. They deserve a big round of applause. They've certainly got our thanks. This product includes software developed by UUNET Technologies, Inc. and by the University of California, Berkeley and its contributors. Last, but certainly not least, Rich Salz, the original author of INN deserves a lion's share of the credit for writing INN in the first place and making it the most popular news server software on the planet (no NNTP yet to the moon, but we plan to be there first). Related Packages INN users may also be interested in the following software packages that work with INN or are based on it. Please note that none of this software is developed or maintained by ISC; we don't support it and generally can't answer questions about it. Cleanfeed URL: (maintained by Steve Crook) Cleanfeed is an extremely powerful spam filter, probably the most widely used spam filter on Usenet currently. It catches excessive multiposting and a host of other things, and is highly configurable. Note that it requires that INN be built with Perl support (the --with-perl option to configure). Cleanfeed was originally developed by Jeremy Nixon who maintained it until 1998. Then Marco d'Itri until 2002. Steve Crook has been maintaining it since 2007. A Python-based variant of Cleanfeed, named PyClean, also exists and can be found at . GUP (Group Update Program) URL: GUP provides a way for your peers to update their newsfeeds entries as they want without having to ask you to edit the configuration file all the time. It's useful when feeding peers take limited and very specific feeds that change periodically. innduct URL: (maintained by Ian Jackson) A possible replacement for innfeed, innxmit and nntpsend that quickly and reliably streams Usenet article to a remote site. innduct is designed to be robust and not to lose any articles (when offered to peers) in case it unexpectedly dies, contrary to innfeed. It also permits a realtime feed, contrary to innxmit or nntpsend. NewsPortal URL: A PHP-based web news reader that works as a front-end to a regular news server such as INN and lets people read and post without learning a news reader. PersonalINN URL: PersonalINN is a version of INN modified for personal use and with a friendly GUI built on top of it. It is available for NEXTSTEP or OPENSTEP only, unfortunately. suck URL: suck is a separate package for downloading a news feed via a reading connection (rather than via a direct NNTP or UUCP feed) and sending outgoing local posts via POST. It's intended primarily for personal or small-organization news servers who get their news via an ISP and are too small to warrant setting up a regular news feed. newsx URL: Serving the same purpose as suck, newsx is a separate package for downloading a news feed via a reading connection and sending outgoing local posts via POST. Some people find suck easier to configure and use, and some people find newsx easier. If you have problems with one, try the other. Supporting the INN Effort Note that INN is supported by Internet Systems Consortium, and although it is free for use and redistribution and incorporation into vendor products and export and anything else you can think of, it costs money to produce. That money comes from ISPs, hardware and software vendors, companies who make extensive use of the software, and generally kind-hearted folk such as yourself. Internet Systems Consortium has also commissioned a DHCP server implementation and handles the official support/release of BIND. You can learn more about the ISC's goals and accomplishments from the web page at . Russ Allbery Katsuhiro Kondou $Id: readme.pod 9945 2015-09-12 13:05:27Z iulius $ inn-2.6.0/ChangeLog0000644000175200017520000061405412575023702013504 0ustar iuliusiulius2015-09-12 iulius * Remove the paragraph about development versions in README * Branch 2.6.0 release. * Bump revision numbers in FAQ to reflect the new 2.6.0 release 2015-09-08 iulius * Add additional instruction for STABLE snapshots when making a release 2015-09-05 eagle * Change syslog messages used by tests/innd/chan-t.c to notice Avoids spamming syslog when running the test suite. Using notice should be equivalent for the callers of this code. (This should be done more broadly across the code base, but this change will quiet the immediate issue.) 2015-09-04 iulius * Improve documentation for RADIUS Better POD syntax, typo fix, and remove the comment in the inn-radius.conf sample file about the impossibility to use a hash tag (#) in the secret password. 2015-09-03 iulius * Typo fix * Update config.guess and config.sub to upstream versions from 2015-08-20 2015-09-02 iulius * Improve documentation about the use of htpasswd * Improve documentation about the use of port 119 with TLS * Explicitly say that the dbz config file is the .dir history file Thanks to Bernard Higonnet for the report. see #138 2015-08-28 iulius * Improve the documentation of makedbz as for the history.dir file * Silent the float-equal GCC warning for Perl XS See the thread [perl #125770] Unsafe comparison of floats in SvTRUE in perl5-porters in August 2015. * Update to latest rra-c-util release 5.8 * Update to latest C TAP Harness release 3.4 Fix segfault in runtests with empty test list. 2015-08-10 iulius * Perl XS code: Clean up the use of ERRSV along with the SvTRUE macro ERRSV macro has a getter C function internally. SvTRUE macro evaluates its arguments multiple times, leading to exponential calls of the getter function. ERRSV should be assigned to a SV* variable before passing that SV* to SvTRUE macro. Thanks to bulk88 for having pointed that out in the Perl bug tracker. 2015-08-09 iulius * innreport: Try to create HTML and IMG directories if they do not exist It can be helful for new INN installations. 2015-08-08 iulius * ninpaths: Improve logging When fopen fails, log that the error comes from ninpaths. Otherwise, the log line in the errlog file is not helpful to fix the issue. * cnfsstat: Improve logging Do not write stats until the CNFS buffer has been initialized. Also do not write double quotes twice around group patterns. * Do not add a line feed in innfeed log lines * Fix the wording of two log lines in our documentation 2015-08-04 iulius * nnrpd: improve validation of e-mail addresses Check that if an article has a From: header field beginning with '@' chars, it also has another '@' afterwards in the field. It will prevent From: header fields like "@a.b" or "@@@a.b" from being accepted. 2015-07-14 iulius * Improve a few default values for innfeed.conf - Increase max-queue-size from 5 to 20. - Enable use-mmap by default. * Improve a few default values for inn.conf - Make use of the vastly expanded storage and RAM commonly available today: datamovethreshold (from 8192 to 16384), msgidcachesize (from 16000 to 64000), overcachesize (from 64 to 128), wireformat (now enabled by default). - Enable the generation of status reports and performance timings: logstatus and nnrpdoverstats parameters, with a frequency of 10 minutes (status and timer parameters). - Fix the definition of runasgroup and runasuser: they are not necessarily "news" as they can be specified at configure time. - Say that the default value of dontrejectfiltered is false. - Say that the default value of pgpverify is false, if not present in inn.conf. - Say that the default value of logcycles should remain 3 because of privacy impact. 2015-07-12 iulius * nnrpd: fix the parsing of continuation lines in headers Empty headers were not properly parsed. Thanks to Richard Kettlewell for the bug report. * Typo * Update dependencies 2015-07-11 iulius * Add a comment to keep in mind that nnrpd must not reject proto-articles with empty headers 2015-07-07 iulius * Change Perl URL to use HTTPS to avoid a redirect * Sync with latest C TAP Harness upstream package Rebalance usage to avoid too-long strings master * Fix segfault in buffer_find_string with empty buffer Fix segfault in buffer_find_string if passed a buffer that's never had any data. Found by Richard Kettlewell. Patch from upstream rra-c-util. * Remove unneeded UNUSED from network support functions Patch from upstream rra-c-util package. Also homogenize our local network-innbind library with the upstream network one. * Clean build with -Wmissing-format-attribute Add format attributes on various internal functions and a few utility functions that take va_arg arguments so that all code builds cleanly with -Wmissing-format-attribute. 2015-07-06 eagle * Change Perl URL to use HTTPS to avoid a redirect 2015-07-04 iulius * Check for new warnings when running "make warnings" Add the following flags: -Wformat=2 -Wmissing-include-dirs -Wtrampolines -Wjump-misses-init -Winvalid-pch Using -Wformat=2 checks the use of string literals and permits to detect where security should be tightened (see ticket #136). * Update to latest C TAP Harness version from upstream Document the verbose mode. 2015-06-26 iulius * lib/timer.c: Make sure timer arithmetic is done in unsigned types Some care is required. The divison must be done in the signed type (otherwise the answer is wrong if usec<0) but any operation that can overflow (+ and *) must be done in an unsigned type. In the current code the multiplication is done in the signed type. If it overflows (as it will after a few weeks, on 32-bit platforms) then we are already into undefined behaviour (C99 s6.5#5). The subsequent conversion to an unsigned type does not make any difference - the 'working type' for the multiplication is determined by the operands alone, not by the treatment of the result. Note that overflow behaviour for unsigned types (including conversion of negative values to unsigned types) is defined: the result is the residue modulo 2^n (C99 s6.2.5#9). Thanks to Richard Kettlewell for the patch. 2015-06-25 iulius * Properly initialize innconf struct Thanks to Richard Kettlewell for the patch. * lib/hashtab.c: hash_size() would overflow if target=0 Thanks to Richard Kettlewell for the patch. 2015-06-22 iulius * Take into account union semun when dealing with semaphores Improve commit [9902]. Thanks to The Doctor for the patch. 2015-06-20 iulius * innd: mask signals when not in select This excludes the possibility of a signal handler accessing data that the main code is mutating. Rationale: When a child (say, nnrpd) terminates, innd gets SIGCHLD and immediately invokes PROCreap(). This looks for the process in PROCtable. However, if another child process has just been started then PROCwatch() will be part way through executing realloc() to expand PROCtable at the point the signal arrives. So PROCreap() may see an old and possibly corrupted version of the table. Thanks to Richard Kettlewell for the patch. * innd: don't reap the same process table entry more than once Thanks to Richard Kettlewell for the patch. * scanlogs: Report the contents of the news.err file Only errlog and news.crit were previously reported. Also fix the documentation for the max_unknown parameter in innreport.conf that applies to news.notice and not news.err. * Destroy buffindexed's semaphore as well as its shared memory segment buffindexed deletes its shared memory segment when there are no more users (i.e. when shm_nattch==0), but it fails to delete the semaphore at the same time. "make check" no longer leaks semaphores. % ./buffindexed.t cant create semaphore using ov-tmp/buffer: No space left on device failed to create semaphore for ov-tmp/buffer buffindexed: ovinitdisks: cant create shmem for ov-tmp/buffer len 16384: No space left on device Opening the overview database failed, cannot continue Thanks to Richard Kettlewell for the patch. 2015-06-14 iulius * Fix the build for Python < 2.5.0 Fix (old) commit [9297] where Py_ssize_t was set to ssize_t for Python versions anterior to 2.5.0 whereas it was an int. We therefore had an incompatible pointer type when calling PyDict_Next(). As INN is supposed to work on 2.2.0, this bug is now fixed. Also use "0" and "1" instead of "False" and "True" that did not exist in 2.2.0 for the distutils module. * Improve documentation for CNFS buffers larger than 2 GB Large-file support is useful only for 32-bit platforms (and some very rare 64-bit platforms that aren't Linux). * Python support: use the distutils.sysconfig module at configure time Drop support for Python 2.1 and before. It will allow to properly search for the python binary (in m4/python.m4) thanks to the distutils.sysconfig module which appeared in Python 2.2.0, released in December 2001! So it is no longer an issue in 2015. For the record, Python 1.5.2 was currently enough to build INN. One of the issues fixed is that on several systems, the /usr/lib/python2.7/config directory does not exist. It can for instance be /usr/lib/python2.7/config-x86_64-linux-gnu, so the build fails because "config" is hard-coded in the current way the detection is coded. see #78 * nnrpd: don't strlcpy with equal source/dest strlcpy with overlapping source & destination has undefined behavior. (INN's version of the function passes the source and destination to memcpy, which also has undefined behavior when they overlap.) Thanks to Richard Kettlewell for the patch. * innfeed, innd: fix boring uninteresting memory leaks These aren't serious leaks, but do cause noise in analysis tools. Thanks to Richard Kettlewell for the patch. * innd, expire, makehistory: fix memory leak for extraoverview Thanks to Richard Kettlewell for the bug report and the patch. * innfeed: avoid underrun of endpoint array The underrun would happen when the last endpoint is deleted. Thanks to Richard Kettlewell for the patch. * innfeed: be more conservative about article read from disk Thanks to Richard Kettlewell for the patch. * ident: avoid buffer underrun Thanks to Richard Kettlewell for the patch. * Fix ninpaths memory leak Realistically this isn't very likely to cause anyone any trouble. However, fixing it reduces noise that might obscure more serious problems. Thanks to Richard Kettlewell for the patch. 2015-06-03 iulius * Fix a Clang warning for the test of the equality between floats overview_free_space returns a percentage (positive number) or -1 when free space is irrelevant for the overview method used. * Fix Clang warning for unused macros Also clean up the code because OVERFLOW is always defined when tagged-hash is enabled. * Fix a gcc warning for an unused variable * Update NEWS to mention the upgrade to 2.6 from a version anterior to 2.5 2015-05-25 iulius * storage/ovdb/ovdb.c: Fix build issue when Berkeley DB is not available ovdb-private.h correctly undefines HAVE_BDB because db.h is not available but as portable/socket-unix.h was included just after, it was setting again HAVE_BDB owing to its definition in config.h. Rearrange the order of the inclusions. * contrib/reset-cnfs.c: The status was not correctly updated It may have returned true even though an error occurred. * contrib/expirectl.c: Remove useless line (base variable was never read) * Handle NULL argument to process_free This clears up a Clang warning. * Fix getaddrinfo test on Mac OS X Mac OS X uses higher-value flags than other platforms, so adjust the mask so that the portability test works properly there. * Add missing va_end in xasprintf implementation 2015-05-24 iulius * storage/buffindexed/buffindexed.c: fix alignment warnings Given that GROUPheader is already a pointer to a GROUPHEADER, just use &GROUPheader[1]. Thanks to Russ Allbery for the patch. These are all problems with assigning the mmap'd address to a variable that points to the first element of the structure. A better way of representing this in C would have been to have a pointer to GROUPENTRY be the last element of GROUPHEADER, although I'm not sure what other assumptions that would break, somewhere else. These alignment cast warnings are mostly because the way that mmap'd data structures are handled are probably correct in terms of code generation but aren't represented very well for the C type checker. * doc/FAQ: reflect the release of INN 2.5.5 * Use a preprocessor symbol to make the initialization of ARTHANDLE structs easier 2015-05-23 iulius * innxmit: tidy up GetMessageID buffer handling In theory, if you could get a message with an empty message-id header through before anything else, it would call memcpy(NULL, p, 0) which (surprisingly) has undefined behavior. This doesn't seem a very likely contingency but I tidied up the code to avoid it and (hopefully) be clearer anyway. Thanks to Richard Kettlewell for the patch. * storage/cnfs/cnfs.c: don't read uninitialised cycbuffname Thanks to Richard Kettlewell for the patch. * backends/actsync.c: host argument to get_active can no longer be NULL process_args() will call die() rather than let this happen. This change cleans up the code to no longer assume that a null pointer is a possibility. Thanks to Richard Kettlewell for the patch. 2015-05-17 iulius * Fixed alignment issues when storing values 2015-05-14 iulius * Add Richard Kettlewell as a contributor, and update NEWS * Correct remap check in tradindexed lookup The check was off by one; and when it happened, it invalidated the 'parent' pointer, causing a use-after-munmap (or use-after-free) condition. Thanks to Richard Kettlewell for the bug report. * Fixed Clang warning for an unused variable * Document the meaning of TDX_MAGIC (~(0xf1f0f33d)): "fifo feed". * Fixed gcc warnings for missing field 'data' initializer gcc warns about the new initialization { 0 } (-Wmissing-field-initializers). Following commit [9851]. * Verify that setuid() and setgid() actually succeed See: https://lwn.net/Articles/451985/ for a discussion of the issues in this area. The checks in newuser.c are probably unnecessary due to the subsequent tests. rnews.c is straight-up broken though. Thanks to Richard Kettlewell for the patch. 2015-05-08 iulius * Correctly flush CNFS buffers when nfswriter is true in inn.conf 2015-05-05 iulius * Correct remap check in tradindexed group lookup Previously the remap check had an off-by-one bug and moreover would never be done due to the loop condition (making the off-by-one bug moot). This one could be a problem in real life; if creating a group causes innd to expand the index then an already-running nnrpd will not automatically notice, and so won't be able to find the group. Thanks to Richard Kettlewell for the patch. * Initialize ARTHANDLE objects before passing by value Uninitialised fields have indeterminate values (6.2.4#5); this includes trap representations (3.17.2). The access to such fields in the subsequent function calls that pass the ARTHANDLE has undefined behaviour (s6.2.6.1). The fix relies on 6.7.8#21. All references to C99. Thanks to Richard Kettlewell for the patch. 2015-05-03 iulius * nnrpd/commands.c: paranoid checking of AUTHINFO GENERIC reply Check the number of arguments returned by AUTHINFO GENERIC. Thanks to Richard Kettlewell for the patch. * innfeed/connection.c: avoid violating C aliasing rules The object was written as a 'struct sockaddr' but then read as a 'struct sockaddr_storage', which violates C99 s6.5#7. The fix is to always access it as a 'struct sockaddr' and use a union to ensure enough space for any possible address type. Thanks to Richard Kettlewell for the patch. * nnrpd/commands.c: correct sense of PERMgeneric reply The comment has always been wrong, as well as the return value for ~15 years... Thanks to Richard Kettlewell for the patch. * nnrpd/perm.c: don't dereference a null pointer if there are no access groups Thanks to Richard Kettlewell for the patch. * nnrpd/article.c: A wrong variable was used for vhost feature. Since these are created by the local innd, the error should not normally occur. Thanks to Richard Kettlewell for the patch. 2015-05-02 iulius * expire/fastrm.c: Fix a dereferencing issue * tests/lib/tst-t.c: Fix encoding (a non-ASCII char was used) * tests/util/inndf.t: Add support for Mac OS X * Fix a few Clang warnings for unused variables and style improvement * configure.ac: Build fix for current Mac OS X versions The build was failing with recent versions of Mac OS X: clang: error: no such file or directory: '/usr/local/news/lib/libinn.3.dylib' make[1]: *** [libinn.la] Error 1 make: *** [all-lib] Error 2 The reason is the '-multiply_defined'-part of the command line. This switch is marked as obsolete in ld(1): -multiply_defined treatment Previously provided a way to warn or error if any of the sym- bols used from a dynamic library were also available in another linked dynamic library. This option is obsolete. Thanks to Dennis Preiser for the report. * innfeed/imap_connection.c: fix support of Cyrus SASL 2.1.25 and later Fix how sasl_callback_ft, added with Cyrus SASL 2.1.25, was handled by innfeed. See revision [9381] for more information. Thanks to Dennis Preiser for the report. * innd/art.c: Fix a dereferencing issue when parsing Injection-Info: header field Thanks to David Binderman for the patch. 2015-05-01 iulius * Document a few more steps to do when making a release (especially a major one) * Improve DESTDIR support and non-root installs DESTDIR and non-root installs are now properly supported and documented in INSTALL. The "make install", "make update" and "make cert" steps properly obey DESTDIR. Besides, it is no longer a requirement that the installation step be done by the superuser, as long as the user executing the install has supplied a DESTDIR value that points to a writable directory, *and* the person or process performing the install corrects the file ownerships when INN is installed on the system on which it's going to run. chown and chgrp commands are now executed during install only if the current user is root. INSTALL also documents how to disable them, if needed, with the CHOWNPROG and CHGRPPROG environment variables. makedbz is executed only if the current user is root or the news user. innupgrade is now executed also when DESTDIR is in use. Thanks to James Ralston for most of the patch. see #29 2015-04-23 iulius * mailpost: change the default location of the database and the temporary files Add new -t flag to specify the default directory to use for temporary files. Also change the default location of the persistent database from pathtmp to patdb. As a matter of fact, mailpost tries to store its Message-ID database in INN's pathtmp, but since it's usually running as the mail system (often daemon) rather than as news, this fails. Check that the directories are writable when mailpost is run, and otherwise die with an error. close #81 * Remove unused code (the MOST variable is no longer set) Also add quotes around echo'ed text (otherwise, two consecutives spaces are not displayed). Change how the test for the emptiness of the variable $D is done; make was exiting if the variable is set. * Fix GCC 5.1.0 warning for incompatible pointer type Rename the "U" macro used by two tests to "SUC" (casting to String of Unsigned Chars) because it otherwise conflicts with how Unicode strings are declared in ISO C11, the new default mode for the GCC 5 series. 2015-04-20 iulius * Update TODO with references to existing patches 2015-04-16 iulius * Update to latest c-tap-harness from upstream Major changes are: * Local changes for INN's docs/pod.t test are now merged into upstream. * Add a -v command-line option that enables verbose support, and rename the environment variable to C_TAP_VERBOSE. * C TAP Harness now compiles cleanly with Clang with -Weverything -Wno-padded -pedantic-errors. 2015-04-05 iulius * Mention required TLS ciphers for interoperability * Update TODO with current state of INN 2.6.0 2015-04-02 iulius * Update changelog to mention other changes for INN 2.5.5 * Use Sys::Hostname Perl core module instead of calling /bin/hostname 2015-03-25 iulius * Re-order libinnhist and libstorage on the command line used when linking Building fastrm was failing on Ubuntu 12.04.1 LTS with the following error: libstorage.so: undefined reference to `HISlookup' 2015-03-24 iulius * Mention getrra-c-util and documentation improvements in changelog for INN 2.6.0 * Update copyright years (add 2015) * Update config.guess and config.sub to upstream versions from 2015-03-08 * Update control.ctl to upstream version from 2014-06-17 2015-03-21 iulius * scanlogs: Limit the number of lines to show from error log files When lots of lines are present in error log files, they appear in the news.daily verbatim, and the resulting email is so large it is bouncing. Restrict the number of lines to 50 (the default value for unknown lines from news.notice). Thanks to Jeffrey M. Vinocur for the bug report. * nnrpd: Count write time stats when using SASL * Improve the count of sleeping channels The highest file descriptor of sleeping channels was not always properly updated. A new CHANresetlastsleeping() function now does the job when called. Also prevent innd from crashing if a channel is supposed to sleep but does not have a Waker set. Thanks to Petr Novopashenniy for the bug report. * readers.conf: improve the first example to disambiguate its effect against local connections 2015-03-18 iulius * pullnews: when giving a port along with a server name, check there is only one ":" Otherwise, it is very likely that the given server name is an IPv6 address, and therefore its end should not be interpreted as a port. * Do not build with SASL, OpenSSL or zlib support if the related headers are not found Also check for kerberosv5/com_err.h as an alternative for com_err.h. 2015-03-17 iulius * Include inn/defines.h instead of config.h in include/inn header files config.h should not be an installed header file. * Add "INN_" prefix in include/inn header files when needed * Update to latest rra-c-util Amongst the changes, the one used by INN is: - Port the Kerberos Autoconf macros and portability framework to the included Kerberos in Solaris 10 by adding some more entries to the header search paths. 2015-01-25 iulius * innreport: add a new recognized entry When using the reject_with: keyword in an access block of readers.conf, a log is generated in news.notice for readers matching the corresponding access block. This log was reported as an unknown line in daily Usenet reports. It is now filtered, and also considered as a notice level instead of an error. 2015-01-21 iulius * Fix the unsignedness of TMRgettime when printed 2015-01-10 eagle * http://www.imc.org/ietf-usefor/ appears to be gone Replace this link in HACKING with a link to the usefor mailing list archives and to my Usenet article format pages. 2015-01-10 iulius * Enable TLS compression by default As the CRIME attack is not exploitable in NNTP, do not disable TLS compression by default. No vulnerability in TLS compression is currently known as far as NNTP is concerned. * Add a cast to fix a gcc warning 2015-01-07 iulius * Cleanup in include stuff - Add missing BEGIN_DECLS/END_DECLS, and also use them instead of their expansion. - Add missing inclusion of "inn/portable-macros.h" and "inn/portable-stdbool.h". * Update dependencies * No longer include portability headers in include/inn headers Headers in include/inn can be used by other programs, and therefore should not call headers in include/portable. When building INN, copy portability headers into include/inn so that they can be included. 2015-01-06 iulius * Update to latest version of C TAP Harness The runtests harness now supports ignoring comments and blank lines in the test list specified with -l. Leading whitespace before the test name is also ignored. Also use the latest version of tests/docs/pod.t (no functional change). 2015-01-04 eagle * Remove dead link to nnrpkrb5auth 2014-12-16 iulius * nnrp.access2readers.conf: add default username when none is specified 2014-12-14 iulius * Add new contrib/nnrp.access2readers.conf.in script This script converts old-style nnrp.access to readers.conf. Thanks to Jeffrey M. Vinocur for his contribution. * Improve the sample init script for systemd 2014-12-07 iulius * Update Russ's mail address * Fix typos * Mark two tests for removal when switching confparse-t.c to new C TAP Harness syntax These tests depend on the internal implementation of the vector library. The functionality is already tested by the vector test suite. * Use system.h from rra-c-util for include/clibrary.h Keep INN-specific stuff during the synchronization from upstream. * Remove super-ancient definitions of ptrdiff_t, atexit and strtoul These functions were missing on some pre-C89 systems like SunOS. (The on_exit stuff may be for ULTRIX.) They're present on all modern systems and can just be used unconditionally. Also, string.h is now universal. One has to go back to SunOS to find a version of UNIX that doesn't have it, and it's standardized by ISO C. It can be included unconditionally. Remove checking for stddef.h, not used in the code. 2014-12-04 iulius * Include portable/socket-unix.h only when HAVE_UNIX_DOMAIN_SOCKETS is set * Rationalize the portability layer for SUN_LEN Add new portable/socket-unix.h portability layer that includes sys/un.h and defines SUN_LEN if the implementation does not do so. Remove the corresponding piece of code from clibrary.h. * Sync with latest upstream version of rra-c-util 2014-12-01 iulius * Fix a missing svn property * Move PIPE_READ and PIPE_WRITE from clibrary.h to inn/macros.h * When testing portability functions, we redefine the function to a different name to not conflict with system headers. If the function is implemented by the system as a macro, we have to undefine it first. This was being done in some places but not in others, causing compliation problems with inet_aton on FreeBSD. Do this consistently everywhere. * BSD/OS reportedly has implementations of both but doesn't prototype them in system headers, so switch the prototyping to be conditional on whether they're declared rather than whether they're available. * Windows doesn't have sys/time.h and defines struct timeval in time.h. Conditionalize the inclusion of sys/time.h for Windows and add an include of time.h where it was missing. * Add support for choosing the elliptic curve to use with TLS support The new tlseccurve parameter in inn.conf takes the name of a curve OpenSSL knows about, to use for ephemeral key exchanges. Thanks to Christian Mock for the patch. 2014-11-23 iulius * Add a missing dependency in Makefile. * m4/sendmail.m4: add missing brackets The configure script was failing when running that part of code. * Fix gcc warning on FreeBSD Casts to time_t * were causing "dereferencing type-punned pointer will break strict-aliasing rules" warnings on FreeBSD. 2014-11-12 iulius * inn.conf: Improve documentation about tlsprotocols and tlscompression 2014-11-11 iulius * Improve tuning of the SSL/TLS configuration nnrpd's TLS support is basically using OpenSSL's defaults WRT issues such as protocol support and cipher suites. In these days of POODLEs and other vulnerabilities, it should be useful to be able to have better control over what's offered. So this patch adds a few options to inn.conf: - tlsprotocols: allows to select the SSL/TLS versions that are supported - tlsciphers: allows to give an OpenSSL cipher string to tailor the cipher suites that are offered to clients - tlspreferserverciphers: switches on the server-side selection of the cipher suite (TLS default is "client chooses") - tlscompression: allows to turn off TLS compression (because of the CRIME attack) if the OpenSSL version supports this. Many thanks to Christian Mock for his patch. * Update the inn.conf sample with the new addinjectionpostingaccount parameter Following commit [9740]. 2014-11-09 iulius * Mention PyClean as a Python-based variant of Cleanfeed. * Add support for the posting-account attribute in Injection-Info: header fields Add new addinjectionpostingaccount parameter to inn.conf. When set to true, the Injection-Info: header field contains an additional posting-account attribute that mentions the username assigned to the user at connection time or after authentication. The default value for this parameter is false. closes #51 2014-11-01 iulius * current version of the script used to keep INN in sync with rra-c-util 2014-10-28 iulius * Update default paths for Debian and Fedora * Fix a dependency in a build rule * Fix incorrect sentence about the use of trailing slash for DESTDIR * Build the debugging version of buffindexed and tradindexed so that it can be included in a shared library if desired 2014-10-27 iulius * Fixed a format-nonliteral warning * Update to latest C TAP Harness upstream version Also add a missing last NULL argument to new_skip() and new_skip_block() functions, kept for compatibility with tests using the old syntax of C TAP Harness. 2014-10-03 iulius * innwatch: report an error when the control file is missing 2014-09-24 iulius * Update changelog to mention previous commit [9722] * Add two missing contrib programs in the exceptions of mkmanifest * rc.news: no longer explicitly sleep before starting innwatch and cnfsstat Instead, make these two scripts sleep by themselves. Also update documentation: improve the list of actions done by rc.news, and no longer mentions that innd should be throttled before being stopped (this is not true - the shutdown process already does the actions throttling does). * innwatch: add -i flag to specify how many seconds to sleep at startup - Also fix previous commit [9650] that did not totally fix the issue it was supposed to fix. - Fix the behaviour of the -f flag (it wasn't doing anything). - Fix how the -l flag was parsed (a space was required between -l and its argument, whils it should not have been required). - Add new POD documentation for innwatch, and update it at the same time: document new -i flag, and document already existing -f flag. * cnfsstat: add -i flag to specify how many seconds to sleep at startup Update documentation, and homogenize POD syntax at the same time. 2014-09-22 iulius * Fix build issues on AIX 7.1 mmap is redefined to mmap64 when large file support is enabled. 2014-09-21 iulius * Fixed a warning and an unnecessary sys/stropts.h header * Do not build with Berkeley DB support if db.h is not found * Typos * Clean the contrib directory along with the other directories * Fix a few warnings, and update svn:ignore for contrib * Fix build of contrib/respool.c Remove an unused variable. Add a link to libhistory. * Fix build of contrib/expirectl.c Add correct include header files, and fix a few warnings in printf() calls. Add portability code for statfs/statvfs support. * Fix build of contrib/reset-cnfs.c Add correct include header files. Use the right DO_LARGEFILES variable instead of LARGE_FILES. Reformat the code (remove tabulations). Properly exit with the right status code. * Fix build of contrib/newsresp.c Add correct include header files and prototypes. Remove useless variables. punt() now returns a bool instead of an int. ierror() now prints its second argument to stdout. * Add compilation rules for contrib/auth_pass.c Use the right socklen_t type, and add crypt.h header if available. * Improve the use of the __attribute__ GCC keyword When __attribute__ is used, be sure to include portable/macros.h so that this GCC keyword is hidden to compilers that do not recognize it. Also use double underscores around "noreturn" and "packed". * FAQ: add how to feed articles arrived between two dates to another server 2014-09-20 iulius * Fix header inclusions to match the ones from rra-c-util * Remove obsolescent AC_HEADER_SYS_WAIT macro, that defined HAVE_SYS_WAIT_H According to the Autoconf manual, this macro is obsolescent. * Remove obsolescent AC_HEADER_TIME macro, that defined TIME_WITH_SYS_TIME According to the Autoconf manual: "If a program may include both time.h and sys/time.h, define TIME_WITH_SYS_TIME. On some ancient systems, sys/time.h included time.h, but time.h was not protected against multiple inclusion, so programs could not explicitly include both files. Current systems can include both sys/time.h and time.h header files when they exist." 2014-09-17 iulius * Enable IPv6 unconditionally, if available * Add a new test for POD formatting * Add reallocarray in Makefile * update network functions to their latest upstream version Use DEFAULT_TIMEOUT (300 seconds) for getlist and ident. Entirely remove IP_OPTIONS and network_kill_options(), no longer necessary nowadays. Also remove a few parts of code related to IPV6_V6ONLY, that should no longer be a problem (AF_UNSPEC can be used instead of specifically AF_INET when IPv6 is not available). CURRENT will enable IPv6 unconditionally. Add a new network-innbind.c file, along with its headers, to deal with network connections using innbind. These functions are called by INN; in case innbind is not necessary (port >= 1024 or we are root), handle the connection to the generic functions provided by network.c (from rra-c-util). Split the network test suite into 4 specific tests (IPv4, IPv6, client and server). Changes mentioned in rra-c-util changelogs are: - When binding IPv6 sockets, restrict them to only IPv6 connections rather than also allowing IPv4 connections where possible. The default behavior, for maximum backward compatibility, is for IPv6-bound sockets to accept IPv4 connections and expose those connections as IPv4 mapped addresses. This causes various problems, however, such as with reuse of bound ports (which was causing test suite failures) and requirements to handle IPv4 mapped addresses. The network model (also used by BSD systems) where IPv6 sockets only accept IPv6 connections is cleaner, even if it requires juggling multiple sockets in some situations. - network_addr_match now always fails (returns false) if either of the strings are the empty string. AIX 7.1's inet_aton treats the empty string as equivalent to 0.0.0.0, but we want to treat it as a syntax error since it's too easy to get an empty string by accident. - The network_connect utility functions now take an optional timeout. If non-zero, a non-blocking connect is done with that timeout, rather than blocking on connect until the TCP stack gives up. The network utility code now depends on the fdflag code. - network_connect, when given a timeout, now resumes waiting for the nonblocking connect after being interrupted by a signal. This can mean that a connect can take longer than the timeout if interrupted; hopefully both timeouts and catching signals are rare enough that this won't pose a serious issue. - In network_read and network_write with a timeout, restart the I/O attempt if a system call failed with EINTR instead of aborting the operation. - When binding an IPv6-only socket with network_bind_ipv6 and not binding to all local addresses, use IP_FREEBIND if it's available. This allows binding to addresses that are not yet configured, which is much more common with IPv6 given IPv6 autoconfiguration. - Add new network_accept_any() function, which takes an array of file descriptors (similar to what's returned by network_bind_all) and blocks accepting incoming connections on any of those file descriptors. - Prefer reallocarray to realloc for multiplied sizes. - The network_bind_* functions now more reliably set the socket errno on failure and log somewhat more informative error messages with warn. - Also improve the error handling and reporting from some of the other network functions, and refactor the code to avoid more #ifdefs embedded in the middle of other code. - The network_bind_* functions now take a socket type as an additional argument so that they can be used with UDP-based services. - network_bind_all now returns a boolean, which will be false if no sockets could be bound due to some error. Callers may check this instead of checking if the socket count is zero. - Check the return status of snprintf when converting port numbers to strings in network_bind_all and network_connect_host, and use the correct format for the port number for the latter. 2014-09-15 iulius * Fix typo in INN_HAVE_SYS_BITYPES_H 2014-09-11 iulius * innupgrade: fix its execution On a few systems like AIX, innupgrade failed to run during an upgrade because "perl -T" was not explicitly called. Failure was: "-T" is on the #! line, it must also be used on the command line Thanks to The Doctor for its bug report. 2014-09-07 iulius * Sync message handling utilities for the TAP protocol with upstream version Add tap/messages.c and tap/messages.h to test message handling. Update our test suite to use these files instead of the hack done during the synchronization of C TAP Harness (with support/getc-tap-harness). Also fix a typo in MANIFEST (fdflag-t.c was mispelled) and a missing dependency in tests/Makefile for nnrpd/auth-ext.t. * No longer build a special messages library for the test suite Further to the use of the new messages test library that no longer has DEBUG parts, no longer build a special messages.o for our test suite. Besides, fix the build line for the fdflag test suite. * Add a fdflag test suite Fetched from upstream rra-c-util. * Sync the messages test suite with upstream rra-c-util - Use calloc in preference to calculating a malloc size with multiplication everywhere. In most places this caution was probably not necessary, but uniformity is easier to audit and no one will ever notice the speed difference between malloc and calloc. - Add new message_handlers_reset function to the messages utility API. This function resets all handlers to their defaults and frees any memory allocated by the message_handlers functions. This is primarily useful to allow freeing all memory when doing exhaustive memory allocation testing. - Check the return status of vsnprintf in the syslog message handlers for die, warn, and friends, and report an error with warn if vsnprintf fails. - Drop concat from the messages library. asprintf or xasprintf, provided by the portability and util libraries respectively, are entirely superior alternatives to concat. - Improve the is_function_output TAP add-on interface to take an opaque data pointer and pass it into the called function. - Add a new run_setup TAP add-on function that runs a given command and calls bail if it fails, used for doing test setup in an external command. (Some things are easier to do in shell than in C.) * Add new portable/stdbool.h header (extracted from inn/defines.h) 2014-09-06 eagle * Fix reallocarray test suite to not assume errno is preserved errno will not be preserved across calls to ok, since it uses stdio. Save errno and check the saved value instead. 2014-09-06 iulius * Sync the xwrite library with upstream rra-c-util - New inn/macros.h, inn/xwrite.h and portable/macros.h headers (extracted from inn/defines.h and inn/libinn.h). - New portable/uio.h header. - Update include headers in several files to reflect these changes. - clibrary.h now guarantees the inclusion of limits.h (to ensure the availability of SIZE_TYPE on older systems). - Switch to bmalloc where the other xmalloc infrastructure is not required. Don't bother converting tests that use xasprintf or concat for the time being. - Use calloc in preference to calculating a malloc size with multiplication everywhere. In most places this caution was probably not necessary, but uniformity is easier to audit and no one will ever notice the speed difference between malloc and calloc. - Add bail or conditionals so that clang --analyze isn't worried about NULL pointer dereferences in our tests. - Also make xwritev more robust by doing range checking on iovcnt early. It's unlikely this would have caused a real problem anywhere, but this is more obviously correct. - The number of iovs remaining can't actually be zero unless the system writev call is buggy, but the math is complex enough that clang --analyze can't figure that out. Add an assert to make the assumption explicit. * Fix the generation of MANIFEST further to the addition of the reallocarray test * Sync the vector library with latest upstream rra-c-util Use xreallocarray instead of xrealloc in the vector library. 2014-09-05 iulius * sync the xmalloc library with its latest upstream version Changes are: - Add xreallocarray to the xmalloc library, which is a checked version of reallocarray similar to what xrealloc is for realloc. - Prefer reallocarray to realloc for multiplied sizes in vector manipulations. - Also change the memory thresholds so that the xmalloc test works on amd64 Debian Linux again. - xasprintf and xvasprintf are now void functions and always call the xmalloc failure handler on any error, not just on ENOMEM errors. The faint chance that the underlying asprintf function could return some other error isn't worth the additional code complexity of still having to check the return status and then probably abort anyway. - In xasprintf and xvasprintf, distinguish between failure to allocate memory and failure to format the output. Report the latter by passing 0 to the failure handler, and special-case that in the default failure handler to report a different error message. - Avoid excessive memory allocation when duplicating short nul-terminated strings in xstrndup. 2014-09-04 iulius * sync a few tests and headers with upstream Better header inclusions, especially portable/socket.h instead of standard netinet/in.h for inet_aton and inet_ntoa tests. Also deactivate the Wformat-nonliteral warning for the snprintf test suite. * sync the getaddrinfo test suite with upstream version Changes are: - Close memory and file descriptor leaks in the test suite. - In some situations, DNS may just not work, in which case we may get different error messages than host not found when looking up invalid hosts. Allow for that and skip the test in those situations. - Also change a few network tests to use is_int for comparing IPv4 addresses rather than ok with equality. * sync the vector test suite with upstream version Add new string utilities for the TAP protocol. Update the vector test suite with latest rra-c-util version. 2014-09-03 iulius * sync the vector library with its latest rra-c-util version Changes are: - Always allocate room for at least one string to be stored in the vector. It simplifies some of the internal logic and, more to the point, unconfuses clang, which otherwise produces tons of warnings about possibly dereferencing NULL pointers since it can't follow the logic. - Update the confparse test to expect another value for the allocated size of a vector, further to the previous change. - Add __warn_unused_result__ to some vector functions that return newly-allocated memory. - Add asserts to the vector library to catch incoming NULL vectors. These will probably be optimized away by gcc, but they also help unconfuse clang. - Allow NULL to be passed to (c)vector_free. It's much more convenient for freeing data structures to be able to call free routines unconditionally without first testing for NULL. Support this in the vector interface. - Define *_split_space in terms of *_split_multi to save some code. - Check for integer overflow when determining the size of the results of vector_join and vector_cjoin. - Handle empty vectors in vector_join and cvector_join. - Don't check whether a pointer is NULL before passing it into free and instead assume free can handle NULL pointers properly. This has been true for many years. - Fix vector_free to support taking NULL pointers and doing nothing with them. - Allocate memory with calloc and assume this sets pointers to NULL instead of explicitly initializing them. We already had to assume this in various places, and architectures where the all-zero bit pattern is not the NULL pointer are exceedingly rare. * sync with latest rra-c-util Add GCC attributes to util/buffer.h functions, mostly nonnull. Clarify the documentation of some of the functions, particularly around error reporting. Fix visibility for util/buffer.c functions and for inet_aton and inet_ntoa replacements to match the default hidden visiblity of other portability and util functions. * add getc-tap-harness support script to keep INN in sync with C TAP Harness * sync the test suite driver with latest C TAP Harness 3.1 The latest version of the C TAP Harness package is now integrated into INN. The README file shipped with the test suite driver is included in the tests directory. Also keep the same name as upstream for file names in the news tests/tap directory. Update paths to header files, and keep the INN specificities for legacy tests (not yet updated to the new syntax). The math library is now linked only with the test that needs it. 2014-08-30 iulius * Sync with latest rra-c-util version Essentially wording improvement. * Do not hide functions from libinn Random internal utility functions are supposed to be exposed. Fix getrra-c-util to remove the visibility stuff when synchronizing from upstream. * Sync lib/fdflag.c and include/inn/fdflag.h with latest rra-c-util version Changes are: - Rename lib/fdflags.c to lib/fdflag.c. - Add a new header file: include/inn/fdflag.h. - Rename the close_on_exec and nonblocking functions to respectively fdflag_close_exec and fdflag_nonblocking. These functions now return a boolean; fdflag_nonblocking is now prototyped to take a socket_type instead of an int, but on UNIX this should not make a difference. - Add an implementation of fdflag_nonblocking for Windows and allow fdflag.c to be built on Windows. - Update the libinn documentation to mention these new functions (old CloseOnExec and SetNonBlocking functions were still documented). 2014-08-28 iulius * Sync lib/buffer.c and include/inn/buffer.h with latest rra-c-util version Changes are: - Remove the bool arguments to buffer_sprintf and buffer_vsprintf and instead introduce new buffer_append_sprintf and _vsprintf functions to append to the buffer, which is what the functions did with a true argument. This avoids having a bool argument to functions, the meaning of which is often obscure in the calling code. - Fix buffer_free to support taking NULL pointers and doing nothing with them. Don't check whether a pointer is NULL before passing it into free and instead assume free can handle NULL pointers properly. This has been true for many years. - Allocate memory with calloc and assume this sets pointers to NULL instead of explicitly initializing them. We already had to assume this in various places, and architectures where the all-zero bit pattern is not the NULL pointer are exceedingly rare. 2014-08-25 iulius * pullnews: new -a flag (hashfeed ability) Add a new feature to pullnews: hashfeed to split feeds. It uses MD5 and is Diablo-compatible. Thanks to Geraint Edwards for the patch. 2014-08-24 iulius * pullnews: new -B flag (header-only feeding) Add a new feature to pullnews: header-only feeding. If the article does not already have a Bytes: header field, one is added. Bodies are kept only for control articles. Thanks to Geraint Edwards for the patch. 2014-08-23 iulius * sync with latest rra-c-util Avoid strlcpy in the getnameinfo replacement (use memcpy instead) and the setenv replacement (use asprintf instead). * pullnews: bug fix to rnews when -O; improved rnews reporting Thanks to Geraint Edwards for the patch. 2014-08-22 iulius * pullnews: improve wording * When pullnews runs for the first time against a newsgroup, say "never" instead of January, 1st 1970 as the last run date. * Improve spaces, uppercase characters and singular forms when 1 article is retrieved. * Update the config file even when the group is empty. * pullnews: remove headers matching (or not) a given regexp Enable the -m flag to remove headers matching (or not) a given regexp. Thanks to Geraint Edwards for the patch. 2014-08-09 iulius * innwatch: no longer creates a child process for sleeping innwatch creates a child process only for sleeping and then waits on that process. The forked-off process is not killed by 'rc.news stop' (only its parent is), and will only die after it's done sleeping. If running under SMF on illumos/Solaris, this causes the service to likely drop into maintenance state (since not all processes die within timeout). Thanks to Lauri Tirkkonen for the patch. 2014-07-05 iulius * Sync with latest rra-c-util 5.5 KRB5_CPPFLAGS_GCC Makefile variable can now be used, if needed, in combination with gcc warnings (such as the make warnings target) to suppress warnings from Kerberos headers in non-system paths. 2014-05-17 iulius * Typos. * Bump version number in FAQ for new INN 2.5.4 release * Mention the expected rights for ncmring.gpg in perl-nocem man page * Update config.guess and config.sub to upstream versions from 2014-05-17 2014-05-14 iulius * Fix a few nits * Typo and useless new line logged in innfeed.debug * Document already existing res blocks functionality in readers.conf * Document res blocks, their log: and program: keywords, and the include directive in readers.conf man page. * Recognize res blocks and their keywords in inncheck. * Do not consider as errors in daily reports the lines logged in news.notice by the log: keyword in readers.conf. * Also teach other known lines to innreport (especially the use of "ctlinnd xexec" and normal rejection of nnrpd connections during news.daily operations). * Typo in readers.conf man page (access instead of auth). * Recognize the no-op XYZZY keyword Yep, nothing new happens with that patch. * Prevent innwatch from throttling innd when no overview directory exists Thanks to John F. Morse for the report. * Mention in news.daily documentation that lowmark requires expireover Thanks to John F. Morse for the report. 2014-04-13 iulius * Update control.ctl to upstream version from 2014-04-06 2014-03-16 iulius * Fix typo (two "d" in "address") Also remove the top1000.org domain that no longer exists. 2014-03-15 iulius * Fix clang warnings about unused variables * Handle several UIDs on PGP keys when verifying control messages Fixed a long-standing limitation on how controlchan and pgpverify were checking the signer of control messages. They now properly handle the case of several UIDs being defined on a single PGP key, as well as the presence of spaces into UIDs. In previous versions of INN, a few valid control messages got ignored because of that limitation (fido.ger.* and grisbi.* were for instance impacted). 2014-03-13 iulius * Mention that Libtool is updated from the Debian package Also remove the paragraph about INN that should not be built with libtool by default. Starting from INN 2.6, INN is always build with libtool. * Add new files to be synchronized from the upstream rra-c-util package * Update copyright years * Update control.ctl to upstream version from 2014-03-13 * Update install-sh to upstream version from 2014-03-13 * Update libtool to Debian version 2.4.2-1.7 * Update config.guess and config.sub to upstream versions from 2014-03-13 2014-02-17 iulius * improve documentation about using storeonxref with tradspool Mention in the inn.conf man page that storeonxref needs being set to true in case the tradspool article storage method is used. Though a few parts of tradspool handle both values for storeonxref, the implementation when storeonxref is false is not complete. For instance, CrackXref() is still called on the Newsgroups: header once, which obviously fails. * sync with latest rra-c-util Don't check whether a pointer is NULL before passing it into free and instead assume free can handle NULL pointers properly. This has been true for many years. * do not install motd files by default, but only samples On a fresh INN install, motd.innd and motd.nnrpd are no longer installed by default. Instead, samples for these files are provided in >pathetc>, named differently so that their default contents are not displayed to news clients before they get customised. * readers.conf: make mandatory the program: key in res blocks Fixed a segfault occurring in nnrpd when a res block was used in readers.conf without the program: key. This key is now explicitly made mandatory. * nnrpd: fixed an overlapping copy Fixed an issue where users were denied posting because of an overlapping buffer copy in a check nnrpd was doing. The error seen was "address not in Internet syntax" while using a From: address of the form . Debugging revealed that nnrpd copies a buffer into itself to look at the part behind the "@", and for very specific inputs on an old version of nnrpd that still uses strcpy, the result did not contain the dot separating the top-level domain any more. Thanks to Florian Schlichting for the patch. 2014-02-08 iulius * add a valid shell when su'ing to the news user Pass "-s /bin/sh" to "su news" to cope with the change of the news user's shell in base-passwd 3.5.30. Thanks to Colin Watson for the patch. 2013-12-27 iulius * Use $inn_use_KRB5 instead of $KRB5_LIBS. * Mention that --with-sasl requires Cyrus SASL (and not SASL). * update m4 files for Berkeley DB and zlib support bdb.m4 and zlib.m4 are now part of rra-c-util. The "--with-berkeleydb" configure flag used to add Berkeley DB support is now "--with-bdb". When building INN with Berkeley DB or zlib support, no longer add standard locations to compiler and linker include flags. Such default paths are now added only if explicitly given to one or more of the "--with-bdb", "--with-bdb-include", "--with-bdb-lib", "--with-zlib", "--with-zlib-include", or "--with-zlib-lib" configure flags. The flags ending with "-include" and "-lib" are new. If the Berkeley DB library is found at configure time, INN will now be built with support for it unless the B"--without-bdb" flag is explicitly passed to configure. Note that it was already the default behaviour for zlib support when Berkeley DB support was also enabled. Use BDB_CPPFLAGS, BDB_LDFLAGS and BDB_LIBS instead of DB_CPPFLAGS, DB_LDFLAGS and DB_LIBS. Also use HAVE_BDB instead of USE_BERKELEY_DB. 2013-12-19 iulius * rewording so as to make the documentation of noresendid clearer * mention innduct as a possible replacement for innfeed, innxmit and nntpsend 2013-12-14 iulius * ckpasswd: check for both gdbm/ndbm.h and gdbm-ndbm.h headers libgdbm-dev in Debian provides /usr/include/gdbm-ndbm.h whereas gdbm-devel in Red Hat provides /usr/include/gdbm/ndbm.h; INN then has to make sure to search for the right header file when DBM support is found. Define HAVE_GDBM_SLASH_NDBM_H and HAVE_GDBM_HYPHEN_NDBM_H instead of HAVE_GDBM_NDBM_H to properly disambiguate the two headers that are mapped to the same preprocessor name by Autoconf. Thanks to Jesse Rehmer for the bug report. * auth_krb5: do not try to build it when com_err header is not found The new krb5.m4 used by INN does not guarantee that com_err.h will exist because it's no longer part of the official Kerberos API. Therefore, add a special check to clear KRB5_AUTH, KRB5_CPPFLAGS, KRB5_LDFLAGS, and KRB5_LIBS if neither et/com_err.h nor com_err.h can be found and --with-krb5 is not given. Thanks to Jesse Rehmer for the bug report. 2013-12-12 iulius * innd: fix a segfault when running news.daily On systems where time_t is a 64-bit integer, innd segfaults on "ctlinnd name" commands (for instance when news.daily uses these commands on connected peers). Thanks to S.P. Zeidler for the patch. 2013-12-09 iulius * Fix a few useless assignments (never read) reported by clang * Properly log as notice level Perl and Python hooks by default. * innd: write the first character in uppercase for error messages * innreport: update the pattern for "strange strings" The "Including strange strings" innreport section is now back in daily Usenet reports. Fix regression introduced with commit [8389]. 2013-12-06 eagle * Update the URL for BerkeleyDB Oracle has gratuitously reorganized their web pages yet again. 2013-12-05 iulius * update (again) the link to ISC INN page 2013-11-26 iulius * update links to ISC and Trac * Russ' web site now lists the contributed links that were on the old ISC web site (the new ISC web site is only a download page of the last release). * Accessing Trac now requires a TLS connection. 2013-11-21 iulius * properly link nnrpd with libcrypto Since the separation of SSL_LIBS into OPENSSL_LIBS and CRYPTO_LIBS in openssl.m4, the crypto part has been missing for nnrpd. Now fixed. * improve support for Cyrus SASL * new --with-sasl-include and --with-sasl-lib configure flags to allow the use of non-standard paths for the Cyrus SASL library * if the Cyrus SASL library is found at configure time, INN will now be built with support for it unless the --without-sasl flag is explicitly passed to configure * sasl.m4 is now synchronized from upstream rra-c-util * sync with latest lib-helper.m4 and openssl.m4 from rra-c-util Fix a bug in the *_OPTIONAL version of all Autoconf macros using lib-helper.m4 that would add yes/include and yes/lib to the compiler and linker search paths. 2013-11-17 iulius * krb5.m4: synchronize with latest rra-c-util 4.11 release INN_LIB_KRB5_OPTIONAL now correctly configures the package to not use Kerberos instead of failing if neither the new Kerberos error APIs nor libcom_err could be found. Also fix the AM_CONDITIONAL that determines whether Kerberos is linked with libcom_err. * fix the path to simpleftp in innshellvars and INN::Config When neither wget nor ncftpget nor ncftp was found at configure time, the path to the simpleftp substitution program shipped with INN was not proprely set in innshellvars, innshellvars.pl, and the INN::Config Perl module. Thanks to Christian Garbs for the bug report. 2013-11-11 iulius * regenerate Makefile dependencies with gcc 4.7.2 Also adapt support/makedepend to keep the two leading spaces, as in previous versions of gcc. 2013-11-10 iulius * sync a few m4, lib, and include files with latest rra-c-util Changes to note: * HAVE_KRB5 is now used instead of HAVE_KERBEROS in krb5.m4 * remove NETDB_SUCCESS (no longer used) from getaddrinfo.c * define ARRAY_SIZE in getaddrinfo.c * fix two warnings of unused variable and comparison of a floating point number with 0 in snprintf.c * define UNUSED in getnameinfo.c * check the return status of snprintf instead of assuming that it will always succeed in getnameinfo.c * define SOCKLEN_T in socket.h instead of clibrary.h * improve the definition of sockaddr_storage in socket.h * improve OpenSSL support * sync with latest rra-c-util version of openssl.m4 * use HAVE_OPENSSL instead of HAVE_SSL * fix how --enable-reduced-depends works with OpenSSL support (that flag behaves the opposite it was supposed to) * search for the dl library to link with, if needed 2013-11-05 iulius * sync with rra-c-util * inet_ntop.c: sync with rra-c-util - Use socket_set_errno instead of assigning to errno. - Check the return status of snprintf in the inet_ntop replacement function instead of assuming that it will always succeed. 2013-11-03 iulius * asprintf.c: synchronize with rra-c-util In the vasprintf replacement, preserve errno if snprintf fails when formatting the string into the newly-allocated buffer. * add getrra-c-util support script to help the synchronization with upstream 2013-10-28 iulius * do not stop the build if the crypto library, when optional, is not found 2013-10-26 iulius * use at least Autoconf 2.64 to bootstrap INN AC_INIT has been supporting 5 parameters since Autoconf 2.64. Switch to this newer version. Also improve HACKING as for the files taken from other projects when releasing a new version of INN: - mention the paths of these files in the libtool tarball, - mention rra-c-util, and document how to run a single test, as well as a pointer where to find automatic build logs of INN. * separate m4 macros for networking and UNIX domain sockets Use rra-c-util socket.m4 and socket-unix.m4 files. * improve the build with Kerberos v5 and OpenSSL support * use Kerberos v5 probes from rra-c-util (new krb5-config.m4 and lib-helper.m4 files, and update of krb5.m4) * use OpenSSL probes from rra-c-util (update of openssl.m4) * KRB5_AUTH is now defined directly in configure.ac * --with-kerberos is renamed to --with-krb5 (configure flag) * --enable-reduced-depends is a new configure flag, which tries to minimize the shared library dependencies of the resulting binaries on platforms with proper shared library dependencies; this is not enabled by default, and is of interest primarily to people building packages for distributions * new --with-krb5-include, --with-krb5-lib, --with-openssl-include, and --with-openssl-lib configure flags to allow the use of non-standard paths * if the Kerberos v5, or OpenSSL SSL and crypto libraries are found at configure time, INN will now be built with support for them unless respectively the --without-krb5, or --without-openssl flags are explicitly passed to configure * update documentation and inn.conf sample file 2013-09-22 iulius * fix another link to Berkeley DB in the ovdb documentation 2013-09-22 eagle * Fix link to BerkeleyDB in the ovdb documentation * Fix link for downloading Perl in the hook documentation 2013-09-21 iulius * update to latest upstream version of rra-c-util 4.9 * typos * mention lib32 and lib64 directories when looking at Berkeley DB libraries 2013-09-20 iulius * improve building with Berkeley DB support When building INN with Berkeley DB support, no longer add standard locations to compiler and linker include flags; unconditionally adding -L/usr/lib (for instance) may break the build, especially when /usr/lib32 or /usr/lib64 are also defined. Such default paths are now added only if explicitly given to one or more of the --with-berkeleydb, --with-berkeleydb-include, or --with-berkeleydb-lib configure flags. These two last flags are new in INN 2.6.0. 2013-09-03 iulius * pullnews: improve logging when an error occurs during GROUP Also rewrite a ternary condition to improve readability. Patch from Geraint A. Edwards. 2013-08-27 iulius * add const casts * ckpasswd: use the right prototype for PAM functions On AIX 7.1, security/pam_appl.h defines struct pam_conv { int (*conv)(int, struct pam_message **, struct pam_response **, void *); void *appdata_ptr; }; so pam_message is not a const struct **. Add m4/pam-const.m4 taken from rra-c-util to detect at configure time whether pam_conv expects a const. 2013-08-25 iulius * test suite: more flexibility for inndf Increase the allowed difference in the result of the inndf test suite. It otherwise sometimes fails when files are modified on the system, outside INN. * fix the value of DBM_CPPFLAGS and DBM_LIBS It should now work (wrong fix in commit [9528]). 2013-08-24 iulius * fix the value of DBM_CPPFLAGS and DBM_LIBS Typo in a test introduced by previous commit [9525]. 2013-08-08 iulius * test suite: add libm for linking On some platforms (like AIX), linking with libm is needed as the test suite uses math functions. Though only lib/confparse-t.c currently uses is_double()/ok_double(), add libm to the whole test suite. Updating to the latest release of Russ' C TAP Harness will make it possible to link with libm only for the relevant tests. 2013-08-07 iulius * do not use yyget_leng() function as it is not defined by old flex versions Better keep using yyleng, still cast to size_t. 2013-08-06 iulius * improve the detection of ndbm compatibility layer When INN is configured with Berkeley DB support, ckpasswd tries to use the ndbm compatibility layer provided by Berkeley DB even though Berkeley DB was not built with ndbm support. This patch fixes that, by detecting at configure time that Berkeley DB really has its ndbm compatibility layer. Also add support for gdbm libraries in ckpasswd. 2013-08-01 iulius * ckpasswd: use Berkeley DB ndbm compatibility layer first On a few systems like Fedora 18 ppc64, the GNU dbm library shipped as ndbm.h is not usable. ckpasswd.c: In function 'password_dbm': ckpasswd.c:165:5: warning: passing argument 1 of 'dbm_open' discards 'const' qualifier from pointer target type [enabled by default] database = dbm_open(file, O_RDONLY, 0600); ^ In file included from ckpasswd.c:30:0: /usr/include/ndbm.h:55:14: note: expected 'char *' but argument is of type 'const char *' extern DBM *dbm_open (char *file, int flags, int mode); ^ ckpasswd.o: In function `password_dbm': /home/iulius/autobuild/inn/authprogs/ckpasswd.c:165: undefined reference to `dbm_open' /home/iulius/autobuild/inn/authprogs/ckpasswd.c:170: undefined reference to `dbm_fetch' /home/iulius/autobuild/inn/authprogs/ckpasswd.c:177: undefined reference to `dbm_close' /home/iulius/autobuild/inn/authprogs/ckpasswd.c:172: undefined reference to `dbm_close' collect2: error: ld returned 1 exit status * fix the test suite for inndf When the filesystem name is lengthy, df may output two lines. "df -k -P ." and "df -i -P ." (-P for a POSIX result) are not available on all systems, so we just rewrite the df output on one line. $ df -k . Filesystem 1K-blocks Used Available Use% Mounted on /dev/mapper/gcc12-home 546781616 413506476 133275140 76% /home 2013-07-31 iulius * fix build with flex 2.5.36+ Cast yyget_leng() to size_t because it used to be an int in flex versions anterior to 2.5.35 (not included). * fix three casts to unsigned char when using toupper() GCC complains because of char-subscripts warnings. * fix two casts to const time_t * when using ctime() * fix two casts to unsigned long * consider that NetBSD behaves like FreeBSD for "df -i" Fix the test suite for inndf on NetBSD systems. 2013-07-30 iulius * fix cast-align warnings GCC on some platforms warns about casts from struct sockaddr to one of the more specific subtypes on the grounds that the alignment requirements increase. Restructure code in getaddrinfo.c to avoid needing the casts and cast through void * everywhere else to suppress the warnings. (In all cases, we are assured that the underlying data is properly aligned.) Thanks to Russ Allbery for the patch (from rra-c-util). 2013-07-26 iulius * Update the URL for NoCeM * pullnews: fixed a Perl warning Latest Perl versions catch this use of uninitialized variable. Thanks to Tony Evans for the bug report. * pullnews: looks for the config file in the right home directory After commits [9304] and [9330], INN 2.5.3 broke the legacy behaviour of pullnews looking for its configuration file in the running user's home directory. Adding INN::Config changed the $HOME environment variable to the news user's home directory (in fact the pathnews parameter set in inn.conf). Besides, pullnews was no longer useable outside INN. Fixed these issues by: - making INN::Config and INN::Utils::Shlock optional. If these Perl modules are not present, pullnews falls back to its legacy handle of locks (that is unfortunately broken in Solaris); - setting the home directory to the one of the running user (or to pathdb if pullnews is run by the news user). Thanks to Tony Evans for the bug report. 2013-07-22 eagle * Update the URL for NoCeM * Update URL to download Perl in the Perl hook documentation 2013-07-03 iulius * disable the use of SSLv2 owing to its being unsafe 2013-07-01 iulius * clarify distribution restrictions in the ME entry of the newsfeeds sample file Note in the newsfeeds sample file that restrictions on accepted distributions will have no effect if the article has no distribution at all. Follow up commit [9499]. 2013-07-01 eagle * Clarify distribution restrictions on articles with none Note in the newsfeeds man page that restrictions on accepted distributions will have no deffect if the article has no distribution at all. 2013-06-29 eagle * Add tips for running INN on Mac OS X from Richard Tobin From in news.software.nntp. 2013-06-25 iulius * fix a few resource leaks A few files were remaining open. Thanks to David Binderman for having reported the issue. * imapfeed: fix buffer overflow Fixed a buffer overflow when using imapfeed with more than a million commands during the same IMAP session. Thanks to David Binderman for having reported the issue. * fix segfault during dereferencing Remove a useless unlink(). Thanks to David Binderman for having reported the issue. 2013-06-24 iulius * fix a Perl warning in inncheck defined(@array) has been deprecated since Perl 5.16. Thanks to Remco Rijnders for the report. 2013-06-12 iulius * document htpasswd and perl commands to generate the auth newsusers database 2013-06-08 iulius * add more gcc warnings to check (based on gcc 4.4.5) * mention in INSTALL that CNFS buffers are compatible between LFS and non-LFS versions * innfeed, scanlogs: reopen log files on sighup The recent patch [9464] to flush exploder and process channels in innd, and to send 'ctlinnd flushlogs' again after log rotation solved the problem of controlchan or ninpaths holding open an already deleted errlog file. However this does not yet solve the original issue with innfeed.log when innfeed is run as a standalone daemon via a funnel-file. This patch implements the changes suggested by Julien Elie in January 2013, to add an openLogFile() call to the sighup handler. The sigusr1 handler is not available, as it is already used to increase / decrease the debugging level along with sigusr2. Also, reopening the log after re-reading the configuration might be a good idea anyway, as the log file location may have been changed. Additionally, call sighup on the innfeed pid in scanlogs, and document the changes briefly in innfeed(8) / innfeed.conf(5). Thanks to Florian Schlichting for the patch. 2013-06-06 iulius * properly undef two hashes used in Perl hooks * add the attributes hash to nnrpd Perl posting filter The attributes hash was only created for Perl authentication and access functions. It is now accessible to Perl posting filter. Also update the sample filter_nnrpd.pl file. Thanks to Steve Crook for the patch. 2013-05-24 iulius * also remove obsolete man5/radius.conf.5 file * rename radius.conf to inn-radius.conf The name of the radius.conf configuration file shipped with INN conflicts with the libradius package. innupgrade will take care of the renaming. Reported by Jochen Schmitt. 2013-05-21 eagle * Force the actsync exit code documentation to not be a numbered list POD has changed how it disambiguates numbered lists from description lists and issues a new warning. Force the exit codes to be a description list since they don't start with 1. 2013-05-21 iulius * fix two old style definitions * remove useless code * add a comment to remember why PACKED is needed 2013-05-20 iulius * fixed the occurrence of an unexpected "cant select" error generated by innd errno was not preserved; the status hook was stuck between the select and the check of its return status, so we're getting a spurious EPERM from STATUSsummary, probably from some random glibc internal syscall involved in fopen() that isn't actually important. It only happens twice since it can only happen when select is interrupted by a signal (triggering EINTR, which then gets overwritten with EPERM), which is probably due to child processes completing, which is probably only happening during specific events on your server during expire. The solution is to rewrite this code to the order that it should have been written in the first place. Thanks to Paul Tomblin for having caught that long-standing issue. 2013-04-14 iulius * compilation with gcc 4.8.0 Explicitly give the known size of a pointer used in a snprintf() call in the OfferArticle() function. New -Wsizeof-pointer-memaccess warning introduced in gcc 4.8.0. --Cette ligne, et les suivantes ci-dessous, seront ignorées-- M post.c 2013-03-22 iulius * update documentation for the new behaviour of "ctlinnd flushlogs" Also fix a POD error introduced by the previous commit. Thanks to Florian Schlichting for having caught it. 2013-03-18 iulius * Rotate innfeed logs Exploder and process channels are now properly reopened when "ctlinnd flushlogs" is used, which is in particular the command invoked by scanlogs to rotate log files. It solves the issue that caused the patch to be reverted by commit [9449]. * Superfluous character in innfeed.log output 2013-02-23 eagle * libinn: remove HeaderFind from documentation It hasn't existed since 2002... Patch from Richard Kettlewell . * innconf: Don't test HAVE_SSL This relieves customers of /usr/include/inn from the need to guess whether INN was built with SSL support in order to get a header that matches the installed libraries. Patch from Richard Kettlewell . 2013-01-16 iulius * test suite: remove a useless wrapped test for setenv Remove a test that makes the test suite fail on a few systems. The code it is testing is obvious whereas getting the amount of memory and the data size limits right is extremely difficult in all cases. Besides, this test does not serve much purpose and is not present in rra-c-util 4.7. * Typos. Thanks to Florian Schlichting for the report. 2012-12-30 eagle * Fix the innfeed configfile parser generation rule Using $? in the rule, now that the extra dependency has been added, caused the wrong arguments to be passed to bison. Pass the correct arguments explicitly. This still seems to run bison twice for reasons that I can't figure out, but that should be harmless. 2012-12-29 eagle * Add additional dependency to de-parallelize innfeed yacc rule With a highly parallel make, yacc could be invoked on the innfeed configuration parser twice, once to build the *.c file and once to build the *.h file, leading to one of the mv commands failing. Follow the recommendation in the Automake manual and add an additional dependency from the *.h file to the *.c file, forcing the rule to be run once. Reported by Remco Rijnders. 2012-12-23 eagle * Do not attempt to rotate innfeed logs innfeed with funnel feeds will hold its log files open forever, so those log files cannot be safely rotated with scanlogs. There's no way to signal innfeed to close the log file and open a new one. Remove the code in scanlogs that attempts to deal with them. Patch from Florian Schlichting . 2012-12-07 eagle * Remove reference to reap from prunehistory man page The FTP site from which this program was available seems to have disappeared, and it was already marked as obsolete. 2012-11-03 eagle * Remove link to Elena Samsonova's introduction This web site appears to be defunct. 2012-09-04 iulius * Mention CVE-2012-3523 in the NEWS file (though posterior to the 2.5.3 release) 2012-07-08 iulius * update INSTALL to mention that shared libraries are no longer backed up 2012-07-07 eagle * Update URL for NewsPortal to one that works 2012-07-06 eagle * Fix the backup extensions referenced library Makefile comments * Stop creating backups for shared libraries Introduce a new LI_LPUB command and use it for shared library installation. Document in Makefile.global.in why shared libraries can't use the normal backup mechanism. * Update HACKING release instructions for signing procedure ISC no longer signs the releases. We do that ourselves. We also now need to contact ISC to update ftp.isc.org. * Document library versioning in HACKING * Reformat HACKING POD source for consistent margins * Bump shared library versions Increase the shared library version for all the shared libraries on the grounds that we've almost certainly had ABI-breaking changes since 2.5 (which was a long time ago). Move the shard library versions to near the top of the relevant Makefiles so that the version is more prominant and document how the version should be maintained. * Reformat file lists in lib/Makefile 2012-06-15 iulius * typos 2012-06-14 iulius * prepare the 2.5.3 release * Typo fixed in NEWS. * Add the 2012 year to LICENSE. * Add a contributor of a recent bug fix in CONTRIBUTORS. (Sorry for having forgotten to commit the file!) * Bump the version of the latest release in FAQ. * inncheck: skip warning when expected .OLD backup files are found Improve commit 9418 by allowing only expected .OLD backup files (and not all files whose name ends with ".OLD"). * update to latest config.guess and config.sub files 2012-06-12 iulius * auth_init() should be optional in perl_auth The documentation (doc/hook-perl) for nnrpd auth hook says: If a Perl function auth_init() is defined by that file, it is called immediately after the file is loaded. And later: Provided the file loads without errors, auth_init() (if present) runs without fatal errors, and a Perl function authenticate() is defined, authenticate() will then be called This suggests that auth_init() is optional, but it actually isn't. When it's not defined, the auth hook will fail: 200 isis.rt.uk.eu.org InterNetNews NNRP server INN 2.5.2 ready (posting ok) authinfo user test 381 Enter password authinfo pass test 400 Internal error (3). Goodbye! It seems more sensible to not require an auth_init() function, as most hooks probably won't need it. Thanks to River Tarnell for the bug report. * inncheck: skip warning when .OLD backup files are found * ctlinnd: check the syntax of the newsfeeds file on reload Add a check of the syntax of the newsfeeds file when trying to reload "all", "active" and "newsfeeds". Otherwise, innd may crash on reload. Thanks to Tim Fardell for the bug report. * innreport: ignore invalid dates in daily HTML reports Lines containing invalid dates are now skipped by innreport. The behaviour of innreport was changed by the previous commit because the use of Time::Local::timelocal() validates the date and fails when the date is invalid. We now properly skip it. Thanks to Petr Novopashenniy for the bug report. 2012-06-11 iulius * fix the generation of HTML pages by innreport during leap years An illegal division by zero occurred during the generation of HTML pages by innreport. The problem comes from the parsing of innreport.db. If you have in this file a change of year for which the hours are the same between Dec 31th and Jan 1st, then this error is generated. For instance: news-notice.2010.12.31-04.15.02.html|Dec 31 04:15:02 -- Jan 1 04:15:02|28079|2791|7.9 MB|34320|1500|4.6 MB There is an issue in how innreport translates these dates. The ConvDate() function assumes the dates are relative to the *current* year. So it thinks it has been between Dec 31th 2012 and Jan 1st 2012 (whereas it was Dec 31th 2010 and Jan 1st 2011). As 2012 is a leap year, and Dec 31th is after Feb 28th, the translated date contains an unexpected "+1 day". Thus, the main page of innreport HTML generation has not been updated since the beginning of the year. Note that no HTML report is lost. They are properly generated. Now, innreport makes use of Time::Local to convert dates to seconds since epoch, thus modifying the behaviour of ConvDate() which was converting dates to seconds since January, 1st. I bet innreport now runs slower, but I think it is better to achieve robustness (the code using Time::Local seems easier to read and maintain). 2012-05-29 iulius * add a sample systemd-like init script Include in the contrib directory a systemd-like init script for INN. Thanks to Jochen Schmitt (co-maintainer of the inn package of the Fedora project) for the proposal. 2012-05-28 iulius * add a releaselocks() function to INN::Utils::Shlock Improve how to release existing locks. lock() now keeps a trace of acquired locks; releaselocks() releases all these locks. Previously, unlock() had to be called on all locks. * add a hint to easily find out the modified line for INN in ltmain.sh 2012-05-03 iulius * improve documentation for innflags Thanks to John F. Morse for the report. 2012-04-04 eagle * Remove old install documentation for --enable-libtool Keep the bits about the shared library API and move it to somewhere else in the installation instructions. 2012-04-03 eagle * Fix a mangled comment in the configure libtool changes * Re-enable our special support for -B in libtool * Update libtool to Debian version 2.4.2-1 * Make libtool support unconditional The days when this was too slow on common systems have long past, and it makes the build system unnecessarily complex to support having it be optional, particularly as we move forward with Automake. 2012-03-04 iulius * fixed a trailing extra junk byte in innd answers When an error occurs when innd writes on a channel, a trailing extra junk byte is added to the reply. If the write() function fails, "i" is negative and we substract "i" to cp->Out->left anyway. So if for instance i=-1, we end up in having cp->Out->left increased by one. Which would add an extra character after a complete response. (So it appears in fact before a new response.) Issue introduced in changeset [7418]. Thanks to River Tarnell for the bug report. 2012-01-14 iulius * fix a segfault when a message does not contain a Newsgroups: header field A bug was introduced by [9121] (also in STABLE, but not in a release). The Newsgroups: header field was strdup'ed before having checked the validity of all the header fields. It could therefore be unset, leading to a segfault. The check is now done. If the Newsgroups: header field is missing, this error will still be reported afterwards. And in case it is set, the presence of a moderated newsgroup is properly checked, and nnrpd acts accordingly (it does not add or update Injection-Date:, Injection-Info: and Path: header fields in this case). 2012-01-01 iulius * fix invalid 431 response when server is paused When innd is paused, its 431 response code does not comply with RFC 4644. The Message-ID is missing. It is now properly present as first argument to the response code. Thanks to River Tarnell for the bug report. 2011-12-26 iulius * build INN before running the test suite * test suite: allow the biff service to be named comsat On some systems, the comsat alias for biff is first, so a lookup of UDP port 512 returns it instead. Thanks to Jeffrey Vinocur for the report. 2011-12-26 eagle * Remove the Linux PAM link from the ckpasswd man page Remove link to the Linux PAM System Administrator's Guide from the ckpasswd man page, since unfortunately it appears to not be available on-line any more (except from various hosts that just re-export the Debian /usr/share/doc tree, but those aren't likely to be stable). * Fix a stray space in the FAQ so that it formats properly 2011-12-25 iulius * update sendinpaths to use the current top1000 addresses As the top1000.org domain name has been discontinued for the TOP 1000 Usenet stats, update our documentation and sendinpaths to use top1000.anthologeek.net. 2011-10-01 iulius * add paths to external libraries in CFLAGS and LDFLAGS for the test suite A few tests are failing (or not built) because of paths to external headers or libraries given at configure time unset during the build of the test suite. 2011-09-20 iulius * fix gcc warning with new Cyrus SASL version Cyrus SASL 2.1.25 modified the prototype of SASL callback functions to fix the "function declaration isn't a prototype" warning. : @@ -33,9 +33,10 @@ * SASL_FAIL -- unable to find a callback of the requested type * SASL_INTERACT -- caller must use interaction to get data */ +typedef int (*sasl_callback_ft)(void); typedef int sasl_getcallback_t(sasl_conn_t *conn, unsigned long callbackid, - int (**pproc)(), + sasl_callback_ft * pproc, void **pcontext); When using callbacks, we now cast the argument: (sasl_callback_ft) &getsimple to silent "initialization from incompatible pointer type" warnings, and define sasl_callback_ft for older Cyrus SASL versions. 2011-09-19 eagle * Stop using INADDR_LOOPBACK It looks like the byte order of INADDR_LOOPBACK is indeterminate and may require htonl on some systems and not on others. Avoid the issue by not using it and instead just using htonl(0x7f000001UL). 2011-09-13 iulius * skip leading whitespace in Xref: header fields when running as a slave Fix a regression that occurred in INN 2.5.0 when leading whitespace have been made significant in header field bodies. It could lead INN to drop articles and throttle itself when running as a slave because Xref: header fields generated by other news servers, or even INN 2.4.6, could contain (valid) leading whitespace. Thanks to Matija Nalis for having caught this bug. 2011-09-08 iulius * fix signedness issues in the results of ternary constructions * fix the return type of main() Use an int for main() return type. * fix the end of the header in archive (it was 1 byte too long) 2011-09-04 iulius * add documentation for procbatch New documentation written by Florian Schlichting, with the help of a memo by Russ Allbery. Add a link to this new documentation in innfeed and news.daily documentation. Use "$INN::Config::pathtmp" instead of "INN::Config::$spooltmp" in procbatch. (The two variables are the same; the first one is more wide-spread.) Add a warning if -s is used without -c, and if -t is used without -m. 2011-08-31 iulius * silent a log message about FD_SETSIZE As rlimitnofile is set to "-1" by default, innd should handle silently a decrease of available file descriptors to FD_SETSIZE minus 1. 2011-08-22 iulius * Update changelog * Cast the argument to toupper() Following the previous commit dealing with tolower(), cast the argument to an unsigned char. 2011-08-21 eagle * Cast the argument to tolower() Like the other ctype.h functions, tolower takes an int, which on systems with signed chars can result in unexpected type promotion and broken behavior. Follow best practice of always casting the argument to an unsigned char. Remove some dead code in the innfeed config parser. 2011-08-21 iulius * update MANIFEST after the removal of hstrerror 2011-08-21 eagle * Fix warning suppression in getnameinfo replacement When lengths that may be 0 are cast to unsigned before subtracting one, we may get underflow. Move the subtraction to an addition on the other side of the comparison. Use const void * as the cast for calling gethostbyaddr, since it's more general than char *. * Work around gcc 4.4 misdiagnosis of strict aliasing violation gcc 4.4 thinks part of the network test suite is a strict aliasing violation because it declares a local struct sockaddr_storage and then casts it to other structs. Allocate it from the heap instead to work around this bug. 2011-08-20 eagle * Move netdb constants from portable/socket.h to lib/getaddrinfo.c Nothing outside of the getaddrinfo replacement function now needs any of the netdb.h constants or h_errno, so remove them from portable/socket.h and move them to lib/getaddrinfo.c. * Remove hstrerror replacement This is no longer used anywhere in the source tree since all callers now use getaddrinfo and its corresponding error message function instead. * Remove obsolete h_errno reference in innfeed 2011-08-20 iulius * strictly check the syntax of a token * Check that the last argument to "ctlinnd addhist" is either '' or a valid token; * Use @000000000000000000000000000000000000@ when the token is empty ('') for "ctlinnd addhist", or badly formatted for any other function calling TextToToken(); * Fix the result of the hexadecimal conversion when a lowercase character is used. Transform it to an uppercase character. * Reject invalid tokens containing '@' or lowercase characters. (They were previously wrongly accepted.) Previously: % ctlinnd addhist '' 1312575175 1312575175 1312575175 '' Ok % grephistory -e '' @95A9ADC6403A29596359BECC38B651C17020@ % ctlinnd addhist '' 1312575175 1312575175 1312575175 '@aaaaaaaaaaaaaaaaaaaaa@' Ok % grephistory -e '' @CACACACACACACACACACAA9BA95191402003F@ % ctlinnd addhist '' 1312575175 1312575175 1312575175 '@a' Ok % grephistory -e '' @CAA95191C66E8A89FC1B8B495EFE9EE9301E@ Memory was not properly initialized. Now, the first command uses @000000000000000000000000000000000000@ and the others are rejected, as invalid. 2011-08-18 iulius * synchronize our libraries with rra-c-util 3.7 * Update to the latest release of rra-c-util (3.7). Keep the specific part for setenv-t.c with the run of a shell script wrapper. * Add support for "make tests" in the tests directory. * Add support for $SOURCE and $BUILD in runtests. * mkstemp.c: add a check for (errno != EISDIR). * snprintf.c: remove the HAVE_LONG_DOUBLE check. All modern compilers have "long double". * fix the libtest support files to make the new libtest format work * update the messages library from rra-c-util 3.7 * New prototypes for message_handlers_* and message_log_* functions: size_t for the count/len argument, instead of a signed int. * Add a flush at the end of message_log_stdout. * update the vector library from rra-c-util 3.7 * Add new vector_addn, vector_split_multi and cvector_split_multi functions. (Still unused.) * Update to the new test suite for vector.c. * update xmalloc librairies from rra-c-util 3.7 * Switch the xmalloc_fail prototype from lib/xmalloc.c to include/inn/libinn.h. * Update tests/lib/xmalloc.t to the new shell syntax with libtest.sh for the test suite. * Disable the check for RRA_MAINTAINER_TESTS. We'll see whether we should keep it later. * Use 3.5MB instead of 96KB for the tests/lib/xmalloc.t failing test. * Use RLIMIT_AS instead of RLIMIT_DATA in tests/lib/xmalloc.c. * update network librairies from rra-c-util 3.7 * Change the prototype of network_bind_all to use (unsigned int *) instead of (signed int *) for the count argument. * Adapt nnrpd to use unsigned int for lfdcount, and innd to use unsigned int for chanlimit. * Add the network_accept_any function. (Currently unused.) * Define the socket_type type and INVALID_SOCKET. * Update dependencies. * Fix a typo in a comment in lib/network.c. * Update the test suite to use network-t.c from rra-c-util 3.7. 2011-08-16 iulius * docheckgroups: no longer use awk On a few systems, docheckgroups fails because of the use of an old version of awk. A few awk implementations have a limit in the size of the input they can process; it then cause issues with large newsgroups files. Use Perl instead of awk for the time being. It solves these issues. Successfully tested on a system where docheckgroups was previously failing. Thanks to John F. Morse for the bug report. The next step will be a total rewrite of docheckgroups. It will be much more maintainable as a Perl script. * cnfsstat: reload configuration files when modified cnfsstat now reloads storage.conf and cycbuff.conf if they have been modified since the last iteration of the script (started with the "-l" flag). Otherwise, the new configuration is not taken into account by a "ctlinnd xexec" command; only rc.news starts cnfsstat. Also change the "Cannot open CycBuff Conffile" message because it is not always true. The message can be logged when an error occurs during the parsing of the file. Sometimes, even when the parsing was incorrect, the file was accepted. This behaviour is also fixed, and the file is refused at cnfsstat startup. * fix an error in rc.news documentation about ovmethod and docnfsstat 2011-08-10 iulius * improve the sample init script Based on the Debian package. (Current maintainer: Marco d'Itri.) * inncheck: allow to skip the check for certain files Setting an empty value to a file to check permits to skip the actual check. It is useful for people who are not running all the programs shipped with INN (for instance innfeed, in which case innfeed.conf is unnecessary). Thanks to Florian Schlichting for the patch. * inncheck: mention that the run-away quote warning may be a false positive Note: In cases where an option value spans more than two lines, i.e. there is at least one line that doesn't even contain a single double-quote, there will also be an error messages about an invalid option name as well as one about a missing colon... Thanks to Jeffrey M. Vinocur and Florian Schlichting for this suggestion. * pullnews: no longer modify invalid empty header fields pullnews MUST NOT modify header lines by default. Remove the part of the code that was removing empty header lines. According to RFC 5537: 3.6. Duties of a Relaying Agent Relaying agents MUST NOT alter, delete, or rearrange any part of an article except for the Path and Xref header fields. They MUST NOT modify the body of articles in any way. If an article is not acceptable as is, the article MUST be rejected rather than modified. Also, fix the first lines of the script to no longer say that pullnews can be used without INN. (We use shlock.) 2011-08-06 iulius * upgrade to latest control.ctl file * confusion in the name of the "die" function * fix a cast to signed, and not unsigned * temporarily revert commit 9308 because of an unexpected "sed" error appearing at configure time Errors appear at the end of configure: checking log facility for news... LOG_NEWS configure: creating ./config.status Usage: /bin/sed [OPTION]... {script-only-if-no-other-script} [input-file]... [...] GNU sed home page: . General help using GNU software: . ./configure: line 20063: /bin/sed -e 1s/^X//: No such file or directory ./configure: line 20063: : command not found ./configure: line 20063: : command not found ./configure: line 20063: : command not found ./configure: line 20063: : command not found [...] * update our support files to the latest libtool version (2.4) * update our support files to the latest automake version (HEAD) * update our support files to the latest autoconf version (HEAD) * fix compilation of tagged-hash history format when warnings are on Variables names have changed since the last time a build in debug mode was done. 2011-08-04 iulius * add an INN::Utils::Shlock module to wrap shlock in Perl scripts Calling shlock (a program shipped with INN) is more portable than using flock(2) and its corresponding Perl function because this function does not work as expected on all existing systems (for instance on Solaris). This patch: * contains POD documentation for INN::Utils::Shlock; * adds support for INN::Utils::Shlock instead of flock(2) in pullnews and mailpost. * uses INN::Utils::Shlock instead of a subfunction calling shlock(1) in controlchan, thdexpire and send-uucp. Thanks to Dennis Davis for the bug report. * add POD documentation for scanspool Many thanks to Florian Schlichting for the creation of the POD documentation. 2011-08-02 iulius * check for wrong leading characters in passwd (for instance a commented line with //) Thanks to Florian Schlichting for the patch. * remove inncheck warning about an empty newsfeeds ME entry INN works without problems when the newsfeeds ME entry is empty. inncheck no longer warns about an empty ME entry. Thanks to Florian Schlichting for the patch. 2011-08-01 iulius * fix an error in the order of typedef arguments in commit 9291 * fix unsigned/signed conversions 2011-07-31 iulius * fix Makefile inclusion variable for Berkeley DB Use the right DB_CPPFLAGS variable instead of BERKELEY_DB_CFLAGS. * fix gcc 4.6.1 warnings about unused variables 2011-07-27 iulius * add missing types (used in innfeed/config_l.c) * fix an autoconf issue with missing brackets * fix issues with calls to getgroups() Properly check that the getgroups() functions exists and works before trying to use it. Also use GETGROUPS_T set by configure to be whichever of gid_t or int is the base type of the array argument to getgroups(). * use new AC_CHECK_TYPE autoconf syntax Autoconf versions prior to 2.13 provide a now deprecated version of the AC_CHECK_TYPE macro. It was doing more than a CHECK, and was also defining missing types with #define instead of typedef. Swith to the preferred syntax with AC_CHECK_TYPES and typedef. 2011-07-25 iulius * include if available Do not include only when is available. It is required by the XSI extensions to POSIX and may be the only place that strncasecmp, for instance, is prototyped on some platforms like FreeBSD. 2011-07-24 iulius * sendinpaths: add a pattern for the dump filenames Only process files whose name begin with "inpaths." for legacy reasons (behaviour of the previous sendinpaths shell script). It will prevent issues on /path being symlinked to another directory. Also abort the run of sendinpaths if we cannot chdir() to the log directory. Thanks to The Doctor for having reported the problem. 2011-07-22 iulius * better documentation of the available variables in INN::Config Document how to obtain the existing INN::Config variables; provide a Perl script that gives the list. Add support for the "undef" value in "innconfval -p" (Perl output) so that the list could be exhaustive. Add a way to retrieve the real short version ("x.y.z") instead of a long string ("INN x.y.z (prerelease 20110622)"). $INN::Config::VERSION now contains the short version. 2011-07-20 iulius * sendinpaths: complete rewrite in Perl Switch to a new sendinpaths program written in Perl. Based on Mohan Kokal's work. No longer explicitly check for the 'inpath.*' pattern in file names. New -c, -d and -h flags for a copy of the e-mail to the newsmaster, a debug mode, and a usage information. close #22 2011-07-18 iulius * improve scripts to send Path: statistics Add two flags to sendinpaths: -k and -r permit to control the interval of days for processing dump files. It will allow a proper generation of daily statistics. Also fixed an issue with statistics that could be missing for a couple of days when monthly sent. Improve our documentation. 2011-07-17 iulius * Update our changelog for INN 2.5.3. 2011-07-12 iulius * add a missing dependency to time.h for "struct timeval" Thanks to The Doctor for having reported the issue. * add a pure attribute, a noreturn attribute, and fix a typo * fix a compilation warning Make sure FD_SETSIZE is strictly positive. Cast for an unsigned comparison. Fix a potential bug of affecting -1 to i in case FD_SETSIZE was 0. * synchronize the test suite with C TAP Harness 1.7 * homogeneize the CleanupAndExit() function of fakeinnd with the real one * controlchan: use convdate instead of DateTime::Format::Mail * Adapt commit [9238] to no longer have a dependency to the Perl module DateTime::Format::Mail. Use convdate instead. * On a date parsing failure, controlchan no longer dies and rejects the message (which will be actually process only with the "-c" flag disactivating the cutoff check). * Add a "-c" flag to controlchan to disable the cutoff check. * Also add a "-h" flag to print basic usage information. * Convert controlchan man page into POD: - mention control.ctl.local; - document the "-c" and "-h" flags; - document the need for the Perl modules Encode and MIME::Parser; - add an example of how to manually invoke controlchan. 2011-07-11 iulius * tradindexed remap of index files when writes out of order The overview IDX files aren't being re-read after an article is written to them -- the failing tests (in xref.t, in the test suite) are the places where a stored overview line is read back to check that it was stored correctly, except for the first time each group is read (tests 2-5, 11-18) and when the high water mark is advanced (7-10), which work correctly. There isn't any mechanism in the source code for the files to be refreshed except in the case that the high water mark has been advanced. On systems which don't have MMAP_MISSES_WRITES, perhaps the underlying write shows up automatically (?) Thanks to Wim Lewis for the bug report. * controlchan: impose a date cutoff, block the replay of old control articles controlchan now requires DateTime::Format::Mail to parse dates. Otherwise, with the new Injection-Date: header field, old control articles could be maliciously reinjected into Usenet, and replayed. The Injection-Date: header of old control articles is not always signed... controlchan now imposes a date cutoff. According to RFC 5537, a relaying agent processes an article as follows: 2. It MUST examine the Injection-Date header field or, if absent, the Date header field, and reject the article if that date is more than 24 hours into the future. It MAY reject articles with dates in the future with a smaller margin than 24 hours. That is to say that the Date: header field can be set to anything when an Injection-Date header field exists. * add a default timeout on outgoing sockets (NNTPconnect) For a long time, there have been occasional problems with actsync syncing from certain servers: instead of erroring out and exiting (with "cannot connect to server: Connection timed out" or alternatively "cannot connect to server: Success"), actsync would hang until manually killed, sometimes for days. gdb shows actsync is hanging in a call to fgets in NNTPconnect() (from actsync.c:get_active()). innxmit and innxbatch can use alarm() to set a timeout but other users of NNTPconnect, such as nntpget, rnews, clientlib.c and nnrpd/post.c apparently don't. Implement SO_RCVTIMEO with the default timeout of 300 seconds (set in include/inn/options.h). The raised error will be: "actsync: cannot connect to server: Resource temporarily unavailable" Thanks to Florian Schlichting for the patch. * inncheck: INN 2.6.0 uses addinjectiondate and addinjectionpostinghost for readers.conf The two addnntppostingdate and addnntppostinghost parameters in inn.conf have been respectively renamed to addinjectiondate and addinjectionpostinghost in the 2.6 branch. * inncheck: properly find the boundaries of substituted variables in newsfeeds Thanks to Alexander Bartolich for the bug report, and also the patch. 2011-07-10 iulius * check of global values vs per-peer values in innfeed.conf * As no keys are actually required in innfeed.conf, update inncheck accordingly. * Update inncheck, the man page and the innfeed.conf sample file to correctly mention that deliver-* keys for imapfeed must be specified at global scope. * initial-sleep is a global value; close-period is a per-peer value. Update inncheck accordingly. 2011-07-09 iulius * check all the default values in innfeed.conf (documentation, sample, source code) * Change the default dynamic method from 0 to 3. It already was the recommended value in the documentation, and the default value in the sample innfeed.conf file. * Change the default use-mmap setting from true to false. It already was the recommended value in the documentation. The value of this parameter is only used when innfeed is given file names to send instead of storage API tokens, which is a fairly rare use case. * No longer generate an error message (logged in news.err) when a parameter is not defined in innfeed.conf. It has a default value, so no need to warn the user. (Also updated in innreport.) * Add a "noreturn" hint to the compiler for the dump_core() function. * Homogeneize tape.c with host.c for the default values of backlog-limit, backlog-factor and backlog-limit-highwater. * Log a few more parameters in innfeed.status: "Stats period", "Stats reset", "Debug shrinking", "Fast exit", "stdio-fdmax", "reconnection time", "max reconnection time", "DNS retry period", "DNS expire period", "port num", "force IPv4", "backlog highwater", "highwater queue". * Documentation fixes: - "pid-file" is relative to "pathrun", and not "backlog-directory"; - "log-file" is relative to "pathlog", and not "backlog-directory"; - "initial-sleep" is a global value, and not per-peer; - "close-period" is a per-peer value, and not a global value; - "max-reconnect-time" is mispelled once in the documentation; - specify that *no* keys are mandatory. They are all optional, and have a default value (all of them are now mentioned in the documentation). * Documentation additions: - If a file named "innfeed.debug" exists in the "pathlog" directory, then "debug-level" is automatically set to 1. This is a cheap way of avoiding continual reloading of the "newsfeeds" file when debugging. Note that debug messages still go to "log-file"; - "backlog-directory" is relative to "pathspool"; - "status-file" can be set to an absolute path. * inncheck: eliminate perl4-style subroutine calls with &function Patch from Florian Schlichting. * inncheck: improve how wildmats are recognized Thanks to Florian Schlichting for the patch. 2011-07-08 iulius * inncheck: improvements in checks * Allow uppercase characters in IPv6 addresses. RFC 5952 section 4 / 4.3 says that all characters in an IPv6 address MUST be textually represented in lowercase and advises that humans should do so, too. At the same time, all implementations must be able to accept both lowercase and uppercase. * Suppress (useless and misleading) error messages after an error is detected, until the parser is back in sync. The drawback is that it will hide two consecutive errors until the first one gets fixed. * Allow not to specify the maximum size in storage.conf. "size: 16384," was treated as an error though it was totally valid and meant "no upper limit". * Recognize inclusions in readers.conf and innfeed.conf. inncheck will ignore the include statement. The drawback is that every file must be self-contained on its own: a file must be a "complete" set of blocks and options. In order to check the included file, inncheck has to be run again on it (with for instance the option "readers.conf=/path/to/included/file"). Thanks to Florian Schlichting for the patch. 2011-07-07 iulius * improve checks on passwd.nntp Improve the error message when the parsed lined is in wrong format: "Argument list too long" (E2BIG) instead of "Numerical argument out of domain" (EDOM). Also catch a new kind of error in passwd.nntp: any line prior to a matching , which is not empty or starts with a hash, but contains a third colon somewhere that is not followed by "authinfo". Thanks to Florian Schlichting for the patch. * let innd -S check incoming.conf The fully-useful check will have to wait for the new config parser being used... Thanks to Florian Schlichting for this new -S flag. * inncheck: use Getopt::Long Improve the way inncheck parses the command line arguments. The last matching argument is taken into account (consequently, "--noperm --perm --noperm" is valid, as well as "-q -v"). As -perm is not just the opposite of -noperm, $perms is handled differently. So 1 is -perm, 0 is -noperm, and -1 is neither. Use --fix, --perms, etc. in our documentation. Keep -fix, -perms, etc. for backward compatibility. Thanks to Florian Schlichting for this patch. * inncheck: check for require_ssl and CIDR notations inncheck was looking for require-ssl instead of require_ssl; and it wasn't accepting IPv6 addresses in CIDR notation at all. Patch from Florian Schlichting. 2011-07-06 iulius * inncheck: generate a proper exit value and allow quiet (-q) operation Add support for a quiet mode (with the -q flag). This flag cannot be used along with the exiting -v flag (verbose mode). Also add a non-zero exit value when errors are found. Thanks to Florian Schlichting for this patch. * innfeed.conf: fix documentation wrt. floating-point and optional values Move bindaddress and bindaddress6 to the (newly created) optional section. dynamic-backlog-low and dynamic-backlog-high expect floating-point values, and not integer values. All of the special keys for imapfeed are not required. Thanks to Florian Schlichting for the main part of this patch. * inncheck: actually check incoming.conf/readers.conf, as well as innfeed.conf and storage.conf (generic config-syntax parser) IDEA: Be more restrictive than current parsers and config-syntax / lib/confparse.c combined, so that anything that passes inncheck will be OK both now and in the future. A few notes: - cuddled braces: config-syntax says there doesn't need to be white-space inside, but incoming.conf parser in innd/rc.c requires it (at least after "{") => DO REQUIRE WHITESPACE, as it will work everywhere - double quotes: config-syntax says strings can be continued on multiple lines by means of a backslash; incoming.conf parser in innd/rc.c does not know about backslashes, but will continue over newlines until matching " is found or 32K exceeded => DO REQUIRE strings to stay on one line - comments: config-syntax says "comments at the end of lines aren't permitted", but this seems to be standard practice... Weird things can happen if a "#" is encountered inside a string - multiple variables: config-syntax says multiple variable settings can be on one line when separated by semicolon; incoming.conf parser knows nothing about this - double assignments: in practice, the latter takes precedence, says config-syntax, though I wouldn't bet on it for current incoming.conf... Many thanks to Florian Schlichting for this pretty useful patch. * inncheck: make perlcritic happy (a little bit at a time...) One of these is a bug: an error message was not shown (for passwd.nntp). Thanks to Florian Schlichting for this patch. * clean up innd code Add the "noreturn" attribute to two functions. Fix a typo, add a comment, add an explicit comparison to NULL. * implement an upper limit to the number of file descriptors select() only handles (FD_SETSIZE-1) file descriptors at most. Otherwise, macros like FD_SET have an unexpected behaviour. Force this upper limit right now (usually 1023). In future versions of INN, hopefully libevent will deal with a larger number of file descriptors. Thanks to Steve Crook for the bug report. * better check of the rights on the private key for TLS connections Use stat() instead of lstat(). The permissions of the symlink are irrelevant; we only care about the permissions of the underlying file. Use getegid() instead of getgid(). The news group is fairly trusted already by INN. Ensure that if the mode is 440 or 640, the group owner is the news group (to prevent the failure case of having news:users as the owner and group). In the error message, a wrong path to the key was put (cert_file instead of key_file). Thanks to Florian Schlichting for having reported the issue, and to Russ Allbery for his help on the fix. 2011-07-05 iulius * add 'use strict' mode to signcontrol Clean up these Perl scripts. Use LOCK_EX instead of the value 2. Thanks to Florian Schlichting for this patch. * STARTTLS / AUTHINFO SASL plaintext command injection Fixed a possible plaintext command injection during the negotiation of a TLS layer. The vulnerability detailed in CVE-2011-0411 affects the STARTTLS and AUTHINFO SASL commands. nnrpd now resets its read buffer upon a successful negotiation of a TLS layer. It prevents malicious commands, sent unencrypted, from being executed in the new encrypted state of the session. The issue has been present since INN 2.3.0 (when STARTTLS was implemented). Confirmed when sending "STARTTLS\r\nDATE\r\n" with openssl: 17:04 news@trigo ~/work/openssl/openssl-1.0.0d% apps/openssl s_client -quiet -starttls smtp -connect news.trigofacile.com:119 didn't found starttls in server response, try anyway... depth=0 C = FR, ST = news.trigofacile.com, O = news.trigofacile.com, CN = news.trigofacile.com verify error:num=18:self signed certificate verify return:1 depth=0 C = FR, ST = news.trigofacile.com, O = news.trigofacile.com, CN = news.trigofacile.com verify return:1 500 What? 111 20110403150413 QUIT 205 Bye! The 500 artefact is because of the EHLO sent by openssl before STARTTLS. We see here that the DATE command is answered. With this patch, here is a new attempt with openssl: 17:06 news@trigo ~/work/openssl/openssl-1.0.0d% apps/openssl s_client -quiet -starttls smtp -connect news.trigofacile.com:119 didn't found starttls in server response, try anyway... depth=0 C = FR, ST = news.trigofacile.com, O = news.trigofacile.com, CN = news.trigofacile.com verify error:num=18:self signed certificate verify return:1 depth=0 C = FR, ST = news.trigofacile.com, O = news.trigofacile.com, CN = news.trigofacile.com verify return:1 500 What? DATE 111 20110403150638 QUIT 205 Bye! The answer to DATE is not given after "500 What?". We had to explicitly ask for it. The issue is therefore solved. * add 'use strict' mode to cnfsheadconf, cnfsstat, innmail and procbatch Clean up these Perl scripts. Thanks to Florian Schlichting for this patch. * add 'use strict' mode to mod-active Clean up the Perl script (especially a rewrite of the eval() call). Restart INN automatically before (and if) dying, when the old active file is still present. Thanks to Florian Schlichting for this patch. * add support for wire-formatted articles in scanspool Remove \r from the Newsgroups: header field when parsing it. Otherwise, scanspool can not properly parse articles stored in wire-format, and wrongly report that the article does not belong in its newsgroup. Also initialize two variables, and make sure a hash value exists, to silent three warnings during the execution of scanspool. * add 'use strict' mode to scanspool Clean up the Perl script. Thanks to Florian Schlichting for this patch. 2011-06-24 iulius * fix compilation issues and useless prototypes * Remove the useless timerCallback() function. * Add a missing argument in a call to prepareWrite(). * "len" should be a socklen_t and not an int. * "program" was not declared as a char*. * Better names for variables. * Remove useless prototypes, and make a few functions as static. * Typos in comments. Thanks for Florian Schlichting for the main part of the patch. * fix a bug in the frequency innfeed logs its status innfeed was printing lots of useless lines to news.notice. TMRnow() doesn't return the current time, as innfeed was assuming in the code, but the readily usable number of milliseconds since the last call to TMRsummary() or TMRinit(). Thanks to Florian Schlichting for the patch. 2011-06-14 iulius * Build INN with the new "-Wunused-but-set-variable" GCC 4.6.0 warning. Remove unused variables. At the same time, do a bit of cleaning! (Useless whitespaces, useful (for reading) whitespaces, one-line if conditions, "conn_ret" type instead of "int".) Also add checks to WriteToWire_lmtpstr() results, so as to really return the result (and properly disconnect the session on error), instead of returning RET_OK as though everything were OK. Same thing for WriteToWire_imapstr() at a few places. No need to be in any auth step when we are about to close the IMAP session after a failure of WriteToWire_imapstr(). * Build INN with the new "-Wunused-but-set-variable" GCC 4.6.0 warning. Remove unused variable "amt", and at the same time two useless TRUST_FPRINTF and NO_TRUST_STRLEN branches. * Build INN with the new "-Wunused-but-set-variable" and "-Wunused-but-set-parameter" GCC 4.6.0 warnings. Remove unused variables. Also fix a potential issue with the incorrect handling of structs when passed by value instead of by reference. Maybe a compiler-specific behaviour. * Build INN with the new "-Wunused-but-set-variable" GCC 4.6.0 warning. Remove unused "c" variable. Well, better rewrite the function at the same time. Thanks to Russ Allbery for the new array parsing code. * Build INN with the new "-Wunused-but-set-variable" and "-Wunused-but-set-parameter" GCC 4.6.0 warnings. Remove unused variables. In innd/python.c, also use "wasspace" as a boolean. In tests/overview/api-t.c, keep the "expires" variable because it is set afterwards in the code to the right expected value. Though unused after being set, it is worth keeping it for the day when the overview API uses the value. 2011-06-13 iulius * Build INN with the new "-Wunused-but-set-variable" GCC 4.6.0 warning. It was complaining about an unused "sv" variable. This commit fixes the following issues: * innd and nnrpd segfaults because of a corrupted Perl stack. A copy of the Perl stack pointer is saved at several places in the code but not always restored the way it should be. For instance, PerlFilter() calls could reallocate the Perl stack and subsequently cause failures when the stack is used afterwards. Make sure that the Perl stack could not be modified twice in the row without being properly updated at global scope. * use ENTER-SAVETMPS-PUSHMARK-PUTBACK and SPAGAIN-PUTBACK-FREETMPS-LEAVE macros whenever they are required. Note that call_argv() does not need PUSHMARK-PUTBACK. * fixed two memory leaks because of a xasprintf() which was not free'd. * documentation of the filter_end() Perl routine that is called, if defined, prior to the deactivation of Perl filters. Both used by innd and nnrpd when Perl filtering is turned off (either with "ctlinnd perl n" or when a Perl function dies at run time). 2011-06-11 iulius * Use "localhost" instead of "stdin" in injection fields when an article is posted from the local system. Suggestion from Dieter Stussy and Paolo Amoroso. * Fix the build on MacOS because of common variables. We just declare them as non-common by adding initialisations. Root cause seems to be that ranlib behaves different on MacOS X. By default, ranlib (actual libtool, ranlib is a symbolic link to libtool) doesn't put common symbols in the index. From ranlib(1): -c Include common symbols as definitions with respect to the table of contents. This is seldom the intended behavior for linking from a library, as it forces the linking of a library member just because it uses an uninitialized global that is undefined at that point in the linking. This option is included only because this was the original behavior of ranlib. This option is not the default. Thanks to Dennis Preiser and vi Bo Lindbergh for the patch. Also remove the three now unnecessary mentions to shmem.o, expire.o and ov.o in ovmethod.mk. 2011-05-11 eagle * Fix broken or obsolete links in install documentation Update the link for installing Perl to the result of the current permanent redirect. Remove the paragraph about installing the commercial version of PGP, since www.pgp.com now redirects to the Symantec product page, which makes no mention of the PGP that would be of interest to an INN installer. 2011-04-16 iulius * Document the format of ninpaths dump files. Add a symbolic link for inpaths(8) to ninpaths(8). Mention that INN 1.7 supports the WP newsfeeds flag. * The simpleftp script does not switch to binary mode before transferring files. If it is used by actsyncd to get active.gz from ftp.isc.org, the transferred archive is corrupted. Thanks to Matthias Meyser for the bug report. * OVrealnow was declared twice in storage/overinterface.h. Thanks to Dennis Preiser for the report. * On some OS (like FreeBSD), renice emits a status report to STDERR. When using sendinpaths, which uses renice, in a cronjob, an email is sent every time with that report. STDERR is now redirected to /dev/null. Thanks to Matthias Meyser for having reported the issue. * Update changelog after revision 9189. 2011-03-02 iulius * Convert the innfeed.conf man page to POD. * Add backlog-highwater, news-spool and input-file in the innfeed.conf sample file. * Add fast-exit, debug-shrinking and initial-sleep in both the documentation and the sample file. * To match what innfeed does by default : - change host-queue-highwater from 200 to 10 in the sample file - change max-connections from 5 to 2 in the sample file - change dynamic-backlog-low from 25.0 to 20.0 in the sample file * innfeed/host.c was expecting "backlog-limit-high" whereas innfeed/tape.c was properly using "backlog-limit-highwater" (which is the one mentioned in the innfeed.conf configuration file and the documentation). It then appears that innfeed was not taking the value into account and always using the default value of 0. * Fix the documentation for the path to innfeed.status. It is either pathlog or pathhttp, and not the backlog directory. * Fix the paragraph about imapfeed: "deliver" is not a parameter. * Remove the notion of version 0.9.3 and 1.0 for innfeed. * Remove the mention that "in previous versions of innfeed, a value of 1 had a special meaning for . This is no longer the case, 1 means a maximum of 1 connection." * Remove the mention that "in innfeed 0.9.3 and earlier, was in the range [0.0, 9.0]". * Remove the sample innfeed.conf file from the documentation (no need to duplicate it). * Typos. 2011-02-12 iulius * Add a missing semi-colon. * Fix spaces. * Convert inncheck man page to POD. Update the help usage. Fix a few typos, and add the control.ctl.local file. 2011-02-12 eagle * Initialize socket structures and use SUN_LEN properly From Bo Lindbergh: innd failed to bind to an unprivileged socket. I found the problem in lib/network.c (a sockaddr_in with uninitialised spare bits in it). Then I grepped around and found more instances of the same problem. I also found a sockaddr_un problem (SUN_LEN not used). 2011-02-06 eagle * Add imapfeed to NAME section of innfeed man page 2011-02-01 eagle * Ensure flag is declared in innbind for IPV6_V6ONLY If SO_REUSEADDR were not defined but IPV6_V6ONLY were defined, innbind would fail to compile because a flag variable would not be declared. This is an unlikely set of supported options for a network stack, but let's be fully correct. Thanks, Florian Schlichting. 2011-01-30 iulius * Use cat, grep -v and echo instead of sed. The sed syntax is too different depending on the platform. * Add the clibrary.h header to properly define va_list. Thanks to The Doctor for having pointed out the issue on FreeBSD. 2011-01-28 iulius * Re-add the legacy *errors to the libtest header. It was removed by mistake in the last commit. (gcc 4.3 complains about it, whereas gcc 4.1 does not) * Update our test suite to use the official C TAP Harness release (version 1.6). Well, for the time being, use the legacy syntax for the test suite. A variable named LIBTEST_NEW_FORMAT can be set at the beginning of a test so as to use the new syntax. 2011-01-25 iulius * inncheck: run under 'use strict'. Also don't use hashes with package names (e.g., %control'messages) as it's an ancient form and prevents syntax checking. Thanks to Florian Schlichting for the improvement. * inncheck: delete long-unsused cruft. innlog.pl was the precursor to innreport... Thanks to Florian Schlichting for the clean up. 2011-01-24 iulius * Mention that SASL is also used by nnrpd. Typo, and improve the changelog for INN 2.5.3. * Add parenthesis for Python print() calls. It otherwise does not work with Python 3. * Convert innfeed man page to POD. Improve the help usage (notably, adding a mention of the -o flag). Move innfeed(1) to innfeed(8). Add a symbolic link for imapfeed(8) to innfeed(8). Fixes during the process: * Document the new logging (notably, "accsize", "rejsize" and the "%d/%.1f" syntax for "deferred" were missing). * Document the -s flag in the man page. Thanks to Florian Schlichting for having found it was missing and provided a wording for the documentation. * Typos. 2011-01-23 iulius * Add the htmlstatus parameter to inn.conf to determine whether the status file that innd can write out (depending on the value of the status parameter) is plain text or wrapped in HTML. It previously only was a compile-time option, set to true by default. The behaviour was therefore impossible to modify without compiling again INN. Thanks to Florian Schlichting for the patch. Also fix the syntax of the generated HTML file. The refresh time should not end with a semi-colon. Add the English language, and validate HTML 5. * Missing commit, related to revision 9135. I forgot to commit the new documentation: * Add authentication to the downstream server. * Improve documentation (especially the example for the "-m" flag that should be quoted for the shell). * Improve the logging of Perl and Python access hooks. The origin wasn't previously mentioned: "syntax error in (null)(2), Expected value." Now, nnrpd will log: "syntax error in perl_access-block(2), Expected value." 2011-01-20 iulius * Improve the wording of the action taken after a newgroup/rmgroup control article. Always use a verb. Thanks to Florian Schlichting for the patch. 2010-12-25 eagle * Fix wrapping of readers.conf parameters inherited by inn.conf * Document the post saving done by readertrack = true If readertrack is set to true, in addition to logging client activity, every post made is also saved in a directory under the log directory in a file named by message ID. Document this in inn.conf. 2010-11-17 iulius * The -F flag was not working when the -P flag was not also set with pullnews. The two flags can now be used separately. * The "-k n" flag was not working the way it should in pullnews. The configuration file was not saved after every n articles (right behaviour) but after every n articles in one newsgroup. Thanks to Edmund H. Ramm for the bug report. * pullnews was not properly counting the number of bytes downloaded. It was not resetting the count between newsgroups; consequently, the bytes count was only right for the first newsgroup. Thanks to Edmund H. Ramm for the bug report. 2010-11-01 iulius * Fixed an issue in the Python access hook for nnrpd: it has not been working since Python 2.5 on 64-bit platforms, owing to a change to Python's C API, using a new Py_ssize_t type definition instead of int. Thanks to Raphael Barrois for the patch. Define Py_ssize_t as an int for Python versions < 2.5.0. Also include Python.h before any standard headers are included, because "Python may define some pre-processor definitions which affect the standard headers on some systems". 2010-10-30 iulius * * Advertise the COUNTS, DISTRIBUTIONS, MODERATORS, MOTD and SUBSCRIPTIONS keywords in the LIST capability for nnrpd. Compliance to RFC 6048. * Implement the message of the day in innd, and advertise it in its LIST capability. * The motd.news file (previously used only by nnrpd) is renamed to motd.nnrpd during innupgrade. * A new motd.innd file is created for innd. * In the 501 error code for an unexpected third argument to a LIST command, mention it is not necessarily a wildmat. * Better wording for the 503 error code in response to LIST. * Documentation update. * Symbolic links are created for motd.innd.5 and motd.nnrpd.5 (pointing to the legacy motd.news.5 man page). 2010-10-29 iulius * Before passing the submission template to snprintf(), check that it is a valid one with "%" followed by only another "%" or a "s" (but once only). * When "cnfsstat -a" is used, a Perl warning occurs if the CNFS buffer has not been initialized yet or received an article. The time of the oldest article is unset. We now properly take it into account. Thanks to Florian Schlichting for the bug report. * Check MAXARTLINELENGTH in nnrpd, not innd. This patch means reverting the previous "CRwithoutLF" patch [9086] and also ripping out all length checking and both unsigned long length as well as data->LastCRLF. Thanks to Florian Schlichting for the patch. * Update our documentation to mention the fact that RFC 6048 now exists and is implemented in INN. 2010-10-24 iulius * * Add authentication to the downstream server. * Improve documentation (especially the example for the "-m" flag that should be quoted for the shell). * Do not connect to an upstream server when no newsgroups should be fetched. * Do not stop processing newsgroups when an error occur (for instance when a newsgroup mentioned in the configuration file has been removed from the upstream server). Thanks to Edmund H. Ramm for the bug reports. 2010-10-03 iulius * Mention in the logs of innfeed that a warning may be fine when a new CNFS buffer has just been set up. Thanks to Michael Grimm for having reported the issue. * Ignore in innreport when innfeed rolls its funnel file. Otherwise, it shows up as "unknown entry" in the report. Thanks to Florian Schlichting for the patch. 2010-09-28 iulius * datebuff was no longer initialized... Fix commit 9127. 2010-09-26 iulius * Realign our inn.conf sample (because "extraoverviewadvertised" and "addinjectionpostinghost" are a bit too long for the current alignment). * Do not add an Injection-Date: header when a message-ID and a posting date are already present. RFC 5537: If the proto-article had both a Message-ID header field and a Date header field, an Injection-Date header field MUST NOT be added, since the proto- article may have been multiply injected by a posting agent that predates this standard. Otherwise, the injecting agent MUST add an Injection-Date header field containing the current date and time. * Do not forget to free a pointer. * Better check for the syntax of the posting-host attribute of the Injection-Info: header field. "stdin:" without any IP was seen in the wild. Fix it to only print "stdin". * Fix changelog from previous commit. (The issue was only in CURRENT 2.6.0, not STABLE INN 2.5.3.) * nnrpd no longer adds or updates Injection-Date:, Injection-Info: and Path: header fields when the article is forwarded to a moderator. It could otherwise lead to rejects at injection time when the article was approved by the moderator because these fields show a previous injection (which did not occur). Note that if strippath is true, we will still remove the Path: header. No harm done with that removal. Excerpt from RFC 5537: 3.5. Duties of an Injecting Agent 7. If the Newsgroups header contains one or more moderated groups and the proto-article does not contain an Approved header field, the injecting agent MUST either forward it to a moderator as specified in Section 3.5.1 or, if that is not possible, reject it. This forwarding MUST be done after adding the Message-ID and Date headers if required, and before adding the Injection- Info and Injection-Date headers. 8. Otherwise, a Path header field with a MUST be added if not already present. 9. The injecting agent MUST then update the Path header field as described in Section 3.2.1. * Go on checking the syntax of the Date: header field, even though it is not used by innd when an Injection-Date: header field exists. 2010-09-25 iulius * No impact because the NeedHeadername member is not used in this case. Yet, better put it right. * innd, as a relaying agent, now examines the Injection-Date: header, when present. It contains the posting date (used for history). makehistory now also uses it, when present. Otherwise, the Date: header is used. nnrpd, as an injecting agent, checks whether the Date: header field is not too far in the past. It is a recommendation of RFC 5537 since not all news servers support the Injection-Date: header. (The article would then suffer from poorer propagation.) 2010-09-24 iulius * Update the manifest and Subversion properties after commit 9111. * inncheck was not substituting variables in the newsfeeds configuration file. It therefore wrongly reported errors. For instance: $limitHI=131072 example/example.com:*:Ajp,Tm,<$limitHI:innfeed! Now fixed. This example is properly parsed by inncheck. Thanks to Michael Grimm for the bug report. * Add the possibility to run local innshellvars scripts. innshellvars.local, innshellvars.pl.local and innshellvars.tcl.local are now executed at the end of the run of the corresponding innshellvars script. A typical use is to add or override variables. For instance HOME or GNUPGHOME. These local scripts have to be executable; otherwise, they are not taken into account. Thanks to Matthew Vernon for the suggestion. 2010-09-10 eagle * Yet another update to the Berkeley DB URL 2010-09-10 iulius * INN thinks that OpenBSD (4.6) doesn't support Unix-domain sockets. The problem is that doesn't include cleanly unless you include , and the AF_UNIX test in m4/socket.m4 doesn't include . It is also added to the test program for AI_ADDRCONFIG, even though (a) including stdio.h happens to pull in the necessary headers and (b) OpenBSD doesn't have AI_ADDRCONFIG anyway. Note: m4/sendfd.m4 can be left without the patch. This will always fail on OpenBSD anyway, so it shouldn't matter. This will only successfully compile and run on a STREAMS-based system, so only SysV. It is the same for include/portable/getnameinfo.h and include/portable/getaddrinfo.h: none of those currently cause problems because these headers are only included by files which have already included sys/types.h, and OpenBSD doesn't support STREAMS at all. Thanks to Wim Lewis for the patch. 2010-08-11 iulius * Fix the Tcl innshellvars script (which has not worked since INN 2.3). A variable was unset, and the evaluation of innconfval was not working. Also add a catch block in case the Tcl interpreter does not recognize the umask command. 2010-08-10 iulius * Revert commit 7405 (for Debian bug #307765) to resolve another Debian bug: #584234. We started setting HOME to try to get pgpverify to look in the correct place for the keyring. Current versions of pgpverify now deal with this by setting the keyring location explicitly. Overriding HOME breaks things. Most notably, it means that gpg will try and create files in /.gnupg so if pathnews is not the same as ~news, things like buildinnkeyring (Debian specific) and gpgverify won't work. Thanks to Matthew Vernon for the bug report. We also change where pullnews looks for its configuration file. It is now pullnews.marks in , instead of .pullnews in HOME. 2010-08-05 iulius * Use a variable for the width within printf. Following commit 9092. Thanks to Urs Janssen for the hint. 2010-08-03 iulius * Use a constant (ARTNUMPRINTSIZE) for the length of a water mark. It will be of help if we ever switch from 32-bit to 64-bit article numbers. 2010-08-01 iulius * Assignment to $[ is deprecated in Perl 5.12.1. Anyway, the default is 0; so we can remove our assignments. Thanks to Dieter Stussy for having noticed. 2010-07-30 iulius * Fix the name of the variable containing the information about required headers. Following commit 9087. * Mention that Perl and Python filters for innd return the *first* occurrence of header fields in the %hdr variable. Whereas it is the *last* occurrence for the Perl filter for nnrpd. * When a header field appeared more than once in an article, it was missing from the overview data. OVER/XOVER, as well as HDR/XHDR/XPAT using the overview, were therefore returning an empty field. The content of the first occurrence is now returned, in accordance with RFC 3977. 2010-07-17 iulius * This patch causes both CRwithoutLF (\r) and LFwithoutCR (\n) to be considered the same as CRLF (\r\n) for purposes of checking allowable header line length. It appears that some people like to grace their articles with Face: or X-Face: headers of typically 4-6k binary data. While usually put into nice continuation lines of around 80 characters, a few clients fail to create proper line breaks for this header, inserting only a single \r or \n instead. The nnrpd in INN 2.3 doesn't reject or correct these mistakes for non-system headers, and offending articles are still "in the wild". innd currently counts, logs and ignores such imperfect line breaks. Alas, when checking for the maximum allowable 'physical' line length, single \r or \n are not recognized, thus making the whole 'virtual' (continued) header appear as a single line, easily exceeding MAXARTLINELENGTH and thus causing the article to be rejected. Thanks to Florian Schlichting for the patch. * Confusion between CR and LF when innd parses a body and counts missing LFs. Thanks to Florian Schlichting for having noticed it. * A single header field line is limited to 998 bytes, per RFC 5536. innd was previously accepting, and also generating Xref: header field lines, up to 1022 bytes. Use new MAXARTLINELENGTH and MED_BUFFER constants instead of MAXHEADERSIZE in the code. Update the test suite. Meanwhile, fix two bugs in the generation of the Xref: header field: * When resizing the buffer, extra place for a CRLF that may be added by a continuation line is not taken into account. Consequently, when p[0] = '\r' and p[1] = '\n' are used, a segfault may occur. * When comparing to MAXARTLINELENGTH, the final CRLF is not taken into account ("+2" is missing in the count), so the generated Xref: header field might end up with 1002 bytes! 2010-07-11 iulius * Remove the mention of an unnecessary (and weird) include file. (See commit 8871.) 2010-07-03 eagle * remove broken tags / ctags make targets These targets have been broken for some time and wouldn't work with GNU ctags anyway. Just do 'ctags -R' in the toplevel source directory and put 'set tags=./tags,tags,/path/to/source/tags' in your .vimrc if you want to use tags. Patch from Florian Schlichting. 2010-06-28 iulius * Mention in the documentation that e-mail addresses can be used as arguments to sendinpaths. Thanks to Ian Jackson for the bug report. 2010-06-05 iulius * Fix the use of uninitialized values: storage classes and ARGV. oldart was not declared as a local variable and its value was then wrongly reused. * Allow to *not* compress logs. --with-log-compress=cat will do that at configure time. Idea from Florian Schlichting. 2010-05-31 iulius * Use pre-formatted text for a better HTML and *nroff output. * Fix a character badly encoded. 2010-05-30 iulius * Convert libstorage to POD. Fixes during the process: * ARTHANDLE contains two different ways of representing article data: an iovec array, and a char*/length pair. One is used by SMstore and the other by SMretrieve, but this is not documented. Patch from Ian Jackson to fix that. * OVadd has a time_t expires parameter. * OVexpiregroup has a struct history *h parameter. * OVgetartinfo does not have char **data and int *len parameters. * SMexplaintoken(const TOKEN token) was not documented. * Dot-stuffing was not clearly documented. * Structs and enums were not all mentioned. * EXPENSIVESTAT was not documented. * OVCACHEKEEP and OVCACHEFREE were not documented. * Put variables in italics (POD documentation). 2010-05-20 iulius * Emitting an error if nntpsend is unable to get the lock is better than silently failing. Thanks to Matthew Vernon for his suggestion. * Fix the documentation of the -n flag for nntpsend. * No upper case to innwatch, innxmit and innreport. * Convert nntpsend and nntpsend.ctl documentation to POD. Better nntpsend.ctl sample configuration file. Fixes: * The -a flag is given only when there is no size limit. * The -d and -D flags were incorrectly described (reported by Matthew Vernon). * The -S flag does not exist for nntpsend. * When the -n flag is used, nntpsend does not sleep before spawning each innxmit child. * In nntpsend.ctl documentation, trunc(1) does not exist; it is shrinkfile(1). * Remove trailing spaces. 2010-05-08 iulius * The order of CNFS buffers in a metacycbuff is now properly read and written by cnfsheadconf. There previoulsy was a confusion between hexadecimal and decimal values. Thanks to John F. Morse for the bug report. * When HDR/XHDR/XPAT was used on a new article coming into a newsgroup, requesting a header not present in the overview database, the first subsequent OVER/XOVER command did not show that article. A remap of the overview data file was missing in nnrpd. Thanks to Sam Varshavchik for the bug report. The cached index and the cached data files were not immediately remapped to reflect the change. When doing an overview search with (X)OVER, both the index and data files are remapped, if need be. The same for (X)HDR or XPAT *when* using the overview. Here, as "(from articles)" is replied by the server, the search is done in the article itself and does not call an OVopensearch(). The code uses OVgetartinfo() which calls tdx_article_entry() which remaps the index file but *not* the data file. Therefore, the high water mark in the cached data file is still mapped to high-1 after (X)HDR but tdx_article_entry() updates data->high to be the new high water mark... Then (X)OVER is used. It believes the cached data information is right. But obviously does not find the last article and the cached data file then is marked as to-be-remapped. Which is done when doing the subsequent overview search with (X)OVER. See also [8451]. * cnfsheadconf now properly recognizes continuation lines in cycbuff.conf, that is to say lines ending with a backslash (\). Thanks to John F. Morse for the bug report. 2010-04-12 eagle * Update the URL to Berkeley DB * Update URL for svn2cl * Remove the README reference to inflow The URL appears to be dead. * Fix URL for the SCO UNIX tutorial to avoid a redirect 2010-03-24 eagle * Rewording and editing of changes for 2.5.2 2010-03-23 iulius * Bump revision number. * Fix a call to an uninitialized value. Thanks to John F. Morse for the bug report. 2010-03-21 iulius * Remove a superfluous space. * Mention that UTF-8 is needed for LIST DISTRIBUTIONS and LIST MOTD. * Do not allow local postings to junk groups, as it has always been documented in the active man page (before commit [8735]). Posting will be allowed in configured to in readers.conf (local postings). * Enforce UTF-8 for LIST MOTD and LIST DISTRIBUTIONS responses. 2010-03-19 iulius * mailpost should trim the References: header field when it is too long. Otherwise, inews rejects the article. RFC 5537 explains how to trim it. Original patch from Harald Dunkel. * Assume STDC_HEADERS is always true nowadays. CTYPE is no longer useful; yet, as Russ Allbery hints at, we keep the cast because it remains the case that passing signed characters into is*() functions can cause problems on some platforms. Some implementations do straight table lookups and, if given what they think is a negative number, will happily dereference memory off the beginning of the table. * actsync rejects syncing newsgroups whose components start with '+', '-' or '_'. RFC 5536 in section 3.1.4 states that these MUST be accepted by news servers (and innd does so), even though their use is reserved. actsync now supports them, as well as a hierarchy name that starts with one of these characters. Thanks to Florian Schlichting for the patch. * Remove obsolete AC_HEADER_DIRENT and (unused) AC_STRUCT_TM. * Change how innfeed logs its article counts. A checkpoint is added just before a connection is closed. Checkpoints contain values accumulated since the last checkpoint. This way, innreport can rely on checkpoints only, so as to produce more accurate stats. See also commit [9002] for innd. close #7 2010-03-16 iulius * Properly show in innreport news clients that failed to post articles. * Prevent negative values from being reported in nnrpd for the number of groups read by a client. * Silent two warnings (gethostbyaddr and getpeername) that used not to be reported. A change in nnrpd previously broke that. * Properly count the number of groups opened during a session, and which contained at least an article. Previously, the count was not taken into account when groups were opened with no new articles in any of them. And it was taken into account for groups with no articles, provided that at least one contained an article. The result is now more coherent. * innreport was not properly summing post errors: the domain count was not done (though configured in innreport.conf), and the reader count was not working. * Better report with innreport: NoCeM notices with only bad messages will now be shown, innxmit feeds with no accepted articles will also be shown, as well as news readers which never read any article (for instance those that only post). * Change how innd logs its article counts. A checkpoint is added just before a connection is closed. Checkpoints contain values accumulated since the last checkpoint. This way, innreport can rely on checkpoints only, so as to produce more accurate stats. see #7 2010-03-14 iulius * Remove the check for newsgroups containing only ASCII characters when receiving a newgroup control article. Otherwise, UTF-8 characters would cause a reject of the control article. * Better wording, and a POD typo. * Fix a bug in tradindexed cache entries which made innd throttle because the oldest cache entry couldn't be found. It thought the cache was empty, kept opening copies of the same overview files, and eventually ran out of available system open file descriptors. * Do not truncate to 9 characters the name of a host when unspooling files with rnews. * Properly create datarootdir and pathhttp with the rights associated to the news user and the news group. * When answering to IHAVE, innd was sending a checkpoint to the remote server in case it was time to write a checkpoint to syslog. 2010-02-16 iulius * Update the size limit of block devices. It has changed since the time our documentation was written. Thanks to Miquel van Smoorenburg and Russ Allbery. 2010-02-15 iulius * Update to latest control.ctl file from ISC. * Update to latest version of config.guess and config.sub. * Update to libtool 2.2.6b. 2010-02-12 iulius * scanlogs was expecting the default innfeed.conf file to look for the log file to rotate. Also fixed a bug in news.daily: procbatch was called without specifying the innfeed backlog directory. See commit [8978]. 2010-02-09 iulius * Add a new keyword to news.daily: "procbatchdir" specifies the backlog directories of innfeed when several instances of innfeed are running or when its configuration file is not the default one. * Regular expression should only match at the beginning of the line. Otherwise "./configure --prefix=/opt/portable" will fail on test 8 because additional lines matches "port". Test 8 has to be adjusted to not count and depend on comment lines. Besides, fix an issue of undefined domain or fromhost being unset. Reported by Heiko Schlichting. 2010-02-08 iulius * tdx-util manpage does not know of -F option for correcting errors. Patch from Heiko Schlichting. Also fix a few typos. 2010-02-07 iulius * Update the comment of the Python filter sample to mention that TAKETHIS is taken into account by the message-ID filter. See commit [8949]. * Variable ARTpathme is filled but not used anywhere. It was used in INN 2.3 so as not to process ihave and sendme control messages we sent ourselves. Patch from Heiko Schlichting. * Comment corrected: arguments of ok_double() are double, not integer. Patch from Heiko Schlichting. * Comment corrected: ok_article produces five test results, not three. Patch from Heiko Schlichting. * expire.ctl documentation should use examples with reasonable limits for min/max of Expire: headers. Patch from Heiko Schlichting. * dbzstore does return DBZSTORE_RESULT, not bool. Patch from Heiko Schlichting. * Use Perl and Python filters on message-ID when articles are given to innd via TAKETHIS. The new RFC-3977 structure of the TAKETHIS command easily allows such a check. 2010-02-06 iulius * Log the port number of a connection (nnrpd). * When using tradindexed, the overview data for a cancelled article is now properly immediately removed from the overview. OVcancel was failing because the article number could be higher than the cached high water mark. The overview data is now reopened so as to check the actual high water mark. The article number was not always parsed without error (a trailing "\r\n" may be present). Thanks to Lars Magne Ingebrigtsen for the patch. * When a user is allowed to locally post articles, then LIST COUNTS will return the right "y" flag (instead of "n" or "x"). Also see commit [8945] for LIST ACTIVE. 2010-02-04 iulius * When a user is allowed to locally post articles, then LIST ACTIVE will return the right "y" flag (instead of "n" or "x"). * Do not hide "j" and "x" status in LIST ACTIVE answers. Use NF_FLAG_* macro instead of hard-coded chars for the status of a newsgroup. * Allow "Aj" in newsfeeds (new in INN 2.5.2). * Fixed a bug in the newsfeeds "C" flag: the count of followup groups was one less than the real number. When the value of the Followup-To: header is "poster", it is no longer considered to be a followup. Also fixed the default value of the "C" flag: 2 instead of 1. Thanks to Dieter Stussy for the patch. 2010-01-28 iulius * Revert commit [8932]. No longer check how many headers an article has when posted via inews. * Fixed a bug in how mailpost handles cross-posting feature. Without this patch, mailpost does not detach from sendmail, i.e. sendmail starts one mailpost after the other, instead of running all mailposts for a new message in parallel. Thanks to Harald Dunkel for the patch. 2010-01-26 iulius * Fix a typo in the name of a variable. Thanks to Manuel Pégourié-Gonnard for the bug report. * Mention that when logtrash is set to false, unwanted.log is no longer generated. * Better explanation of why SASL_CONTINUE must be changed to SASL_BADPROT. Approved by Ken. * Clarify how posting backoff is activated. Thanks to Manuel Pégourié-Gonnard for the report. * The PID is written in nnrpd.pid when the default port is used. Thanks to Benoit Izac for having reported that. 2010-01-24 iulius * A new option has been added to inews. When the "-m" flag is specified, the corresponding value will be used to determine the maximum number of header fields that are accepted for an article. The default was C50 and it is now configurable. Thanks to Torsten Jerzembeck for the bug report. 2010-01-23 iulius * Updated changelog for INN 2.5.2 and INN 2.6.0. * Properly return 501 instead of 502 when a peer is already authenticated and sends a syntactically invalid AUTHINFO command. 2010-01-22 iulius * Optimize how ovgrouppat is handled. Thanks to D. Stussy for the suggestion. 2010-01-21 iulius * Add a new parameter to inn.conf: logtrash, used to add a line in the news log file to report unwanted newsgroups. * Increase the allowed difference in the result of the inndf test suite. (It periodically fails for an unkwnown reason...) 2010-01-20 iulius * Though it currently does no harm to use ngp here, it is better to have another variable (in case the logic of the code changes in the future, ngp may be used for another purpose and should not be initialized here). * Set a value to INN_HISTORY_H. * See commit [8889]. Change "feedtrash" (inn.conf) to use "Aj" (newsfeeds) so that we can have a finer configuration of that behaviour, per feed. Thanks to D. Stussy for the idea. 2010-01-17 iulius * Check whether a newsgroup has been selected, before asking for authentication. Answer to retry later when POST is not allowed due to backoff limits. * Fix indentation. * When a newsgroup is empty, use 0 for the current article number (instead of the last known low water mark). * Update all INN code to use include/inn/nntp.h for NNTP codes and eliminate include/nntp.h. * Revert commit 8869 (423 is the right response code to send when the current article number is valid but the article no longer exists). Besides, fix HDR and OVER to also use 423 in that case. See erratum 2004 on RFC 3977. * Add complete support for uwildmats in ovgrouppat: negated patterns "!" are now taken into account (and not considered as poisoned patterns "@"). Also fix a wrong memchr length. 2010-01-16 iulius * Add a new parameter to inn.conf: feedtrash, set to false by default (which was the historic behaviour of INN), adds the capability to feed articles accepted and filed in junk (due to wanttrash) to peers based on their feed patterns (applied to the Newsgroups: header as though the article were accepted and all those groups were locally carried). This is useful if you want to run INN with a minimal active file and propagate all posts. Thanks to Andrew Gierth for the patch. * No longer check for Comments: and Original-Sender: in nnrpd. Otherwise, multiple headers for them were rejected by nnrpd, which violated RFCs 5536 and 5537. Only CURRENT was affected. * innconfval no longer maps NULL string or list values to an empty string or list. These values should really be undefined. In particular, it fixes an issue reported by Kamil Jonca about nnrpd inserting an empty Organization: header when the organization: parameter in inn.conf was unset. * Fixed a bug in the parsing of the ovgrouppat: wildmat in inn.conf that prevented overview data from being generated when poisoned groups were specified but a latter sub-pattern made the group wanted. Thanks to D. Stussy for the bug report. Also improve the documentation of the ovgrouppat: parameter. * Fixed a bug in nnrpd Perl filter: a header field whose name begins with the name of a standardized header field was not properly taken into account. (For instance "Organization-Test".) * When an unauthenticated user tried to post an article, nnrpd replied 440 (posting not allowed) instead of the right 480 (authentication required) when the user might be able to post after authentication. Thanks to Daniel Weber for the bug report. * Typos. 2010-01-04 iulius * Fix a segfault when virtualhosting is used in readers.conf. The new Path: header was generated with an invalid pointer. * Typo: "correspondance" -> "correspondence". 2009-12-31 iulius * Injection-Info: header body is improperly folded with CRLF when created. At this point in the nnrpd code, a bare LF must be used. It is converted to CRLF afterwards. It was breaking mail moderation (two new lines were added, so the end of the headers appeared in the mail body). Thanks to Ray Banana for the bug report. Also fix how the Path: header body should be scanned for ".POSTED". Same bug as above: LF begins a FWS, and not CR. 2009-12-30 iulius * Commit 8765 (mandatory AUTHINFO USER for authenticated peers) will be only in INN 2.6.0 -- not INN 2.5.2. * Fix a mistake in commit 8873 to handle IHAVE like POST. Sorry! 2009-12-29 iulius * When adding an overview line with OVadd(), check that Xref: is at the beginning of an overview field (that is to say that a tabulation precedes it). Otherwise, if for instance one has User-Agent:full in LIST OVERVIEW.FMT and an article is received with the following header field: User-Agent: test Xref: server test:50 then the overview data for article 50 in newsgroup test is replaced by the one of this article. And the real article number of this article is unknown for the overview. * Do not differentiate IHAVE from POST in nnrpd. Articles injected with IHAVE should be treated the same way as if they were injected with POST. * Add support for the "POSTED" diag-keyword in nnrpd, as described in RFC 5537. In particular, nnrpd checks that the article does not already contain a "POSTED" diag-keyword. * The O flag in newsfeeds now relies on the contents of the Injection-Info: header (or X-Trace: header if there is no Injection-Info: header) to determine the origin of an article. Make public the skip_cfws() function so as to skip CFWS defined in RFC 5322. * Use ":" with chown, and not ".", as separator for group name. 2009-12-28 iulius * Return 420 instead of 423 when current article number is used with ARTICLE, BODY, HEAD and STAT. 2009-12-27 iulius * Use case-insensitive matches for: - distributions, - path identities, - message-IDs (except for history hashes), - IMAP commands, - header names, - control commands. Note that newsgroups are still matched case-sensitively. * Contrary to RFC 1036, the presence of a Subject: header field starting with the string "cmsg " MUST NOT cause an article to be interpreted as a control message. (RFC 5537) * sendsys, senduuname and version control messages are obsolete (see RFC 5537) and can no longer be sent via nnrpd or inews. * Mark a few headers as obsolete in nnrpd (therefore, they cannot be present in posts -- it was already the case for the first four headers): NNTP-Posting-Date, NNTP-Posting-Host, X-Complaints-To, X-Trace, Also-Control, Article-Names, Article-Updates, See-Also. Also mark these two headers as obsolete in innd (it does not change anything for innd): X-No-Archive, NNTP-Posting-Path. 2009-12-26 iulius * Fix a bug in keyword generation code: an initial comma was still added at the beginning of the Keywords: header (when the first word was a noised one). * Fixed a bug when HDR, XHDR and XPAT are used when I is set to true in F. The Xref: header of articles posted to only one newsgroup appears empty. The offset count is too large by one in virtual hosting. * Adjust the length of retlen when we have to realloc retval. Fix after commit 8861. * Use "!!" as diag-match in the Path: header after the name of the virtualhost when virtualhost: is set to true in readers.conf and it is added at the beginning of the header. 2009-12-25 iulius * Fixed a bug to control-only feeds: junked non-control articles were being fed down control-only feeds). Thanks to Andrew Gierth for the patch. 2009-12-24 iulius * * Update protocol differences for innd, now that RFCs 3977, 4643 and 4644 have been implemented. * Mention that the -X flag is unset by default. * The Lines: header is not added by innd. * Other typos. * Mention new RFCs references: 974 -> 5321 (SMTP) 822 and 2822 -> 5322 (mail) 2373 -> 4291 (IPv6) 2553 -> 3493 (IPv6 socket) 2060 -> 3501 (IMAP) 2279 -> 3629 (UTF-8) * Use of Injection-Info: headers. * nnrpd no longer generates NNTP-Posting-Date:, NNTP-Posting-Host:, X-Trace: and X-Complaints-To: headers. Instead, Injection-Date: and Injection-Info: are used. * Rename addnntppostinghost and addnntppostingdate parameters in inn.conf to respectively addinjectiondate and addinjectionpostinghost. innupgrade deals with that only for inn.conf; a manual change will be needed for readers.conf, if these parameters are overriden in this file. * Update the test suite, documentation and the FAQ. * Typos. * Note that the "L" reading/posting access in readers.conf is also in effect for groups with status "x" (and not only "n"). A secure layer can also be negotiated with AUTHINFO SASL. 2009-12-22 iulius * * Add checks for Archive:, Archived-At:, Comments:, Injection-Date:, Injection-Info:, Original-Sender: and User-Agent: headers. * No longer check for the Injector-Info: one (it was standardized under Injection-Info:). * No longer generate a Lines: header (deprecated by RFC 5536). * Generate the Injection-Date: header. * Add access to the new header field Comments: within Perl and Python hooks for innd. Also update the POD documentation and the Python sample. 2009-12-21 iulius * Typo. * Search for gpgv2 (besides gpgv). Otherwise, GnuPG cannot be found on a few platforms like FreeBSD. 2009-12-16 iulius * Fix a bug in the daily Usenet report: a few lines did not appear in the report whereas the total still took them into account. These lines do not appear when the peer does not respond during the whole day. They now appear (and every article is marked as spooled). * Do not overwrite LOCKS from innshellvars in case someone in the future expects it to have the value in innshellvars in the entire script. Also prevent a call to LOCK afterwards in the script from failing. Thanks to S.P.Zeidler for the patch. 2009-12-07 iulius * Fix the use of an uninitialized value in mailpost. Patch from Harald Dunkel. 2009-12-04 iulius * Add access to three new headers within Perl and Python hooks for innd: * Archive [RFC 5536] * Archived-At [RFC 5064] * Summary [RFC 5536] Also update the POD documentation and the Python sample. 2009-12-03 iulius * Fix a typo in the name of the news.lists.filters newsgroup. Thanks to John Marshall for having noticed it. 2009-12-01 iulius * Add dependencies for tinyleaf compilation. It was not recompiled when changes were made to its dependencies. 2009-12-01 eagle * Send a space rather than a blank line in tinyleaf HELP output GCC format checking doesn't permit an empty format (""), which meant that tinyleaf failed to compile since format checking was added to nntp_send_line_noflush. Work around this for the time being by sending a single space on that line. 2009-11-30 eagle * Fix printf attribute for x_asprintf in the non-vamacros case 2009-11-30 iulius * Improve gcc warnings on printf-like function calls. * Implement LIST COUNTS. It is a combination of LIST ACTIVE and GROUP. It returns the same result as LIST ACTIVE except that the number of articles in a newsgroup is inserted before its status. LIST COUNTS was first implemented in Highwinds products (e.g. Cyclone, Tornado, Typhoon, Twister -- when they support readers). 2009-11-29 iulius * Typos. * LIST SUBSCRIPTIONS recognizes an optional argument: a wildmat can now be specified to restrict the results of this command to specific newsgroups. Also change the comment returned after the 215 code (the newsgroups are now "recommended"). Even though a user does not have any right to read groups, let him see a possible message of the day (LIST MOTD). 2009-11-28 iulius * tdx-util was improperly considering empty overview fields as malformed overview data. 2009-11-27 iulius * * nnrpd now checks the syntax of the Message-ID: header. * Fixed a bug when nnrpd sends "IHAVE \r\n" to innd. Arbitrary commands could be passed to innd because nnrpd did not ensure the validity of the syntax of the message-ID. * The permanent variable was not properly set on IHAVE rejects and defers. * innd now accepts commands ending with only "\n". 2009-11-20 iulius * * Mention that the distributions file should be encoded in UTF-8. * Mention that the motd.news file should be encoded in UTF-8, and dot-stuffed. * Typos. 2009-11-17 iulius * When processing a checkgroups, do not take into account duplicated lines with localgroups. * Integrate showtoken into the main distribution. "sm -c" will now show clear, decoded information about storage API tokens. A new SMexplaintoken() function has been added to the storage manager. 2009-11-15 iulius * Add two log messages for bad commands (too long NNTP command or argument) in innd. * No need to treat expire.log differently than other log files during its rotation. Patch from Florian Schlichting. * When "lowmark" was used without "expireover" in news.daily, the warning was never shown (the file it was written into was rotated before being treated). * Update the changelog for INN 2.5.2. 2009-11-14 iulius * Check LIST output for correct dot-stuffing. 2009-11-11 iulius * news.daily: don't send superfluous mails when "nomail" is given Make sure that when news.daily is called with "nomail", mail is only sent when there is real output. Previously, there would always be headings and empty lines useful to structure the full report, which are now ommitted. Also, postexec has been moved up to be included in the regular mail, and the "nomail" definition of ${MAIL} is no longer overwritten with the default. Thanks to Florian Schlichting for this patch. * This patch cleans up a little cruft (${T}, ${PROBS}) left in scanlogs since its rewrite by Gary Palmer in September 1997. Thanks to Florian Schlichting for this patch. 2009-11-10 iulius * Documentation fix: a group with status "j" can receive local articles. Mention that groups with status "n" or "x" can receive local posts, if configured to. * Wording: "flag" -> "status" for the last field of the active field. Also fix a typo in hook-python. * Confusion between DISTRIB.PATS and DISTRIBUTIONS. LIST ACTIVE.TIMES now accepts a third argument (newsgroups wildmat). Mention LIST HEADERS [MSGID|RANGE]. * It is a POST capability, not a READER one. * Wording of LIST answers. * "all" and "world" distributions should not be used according to USEFOR (RFC 5536). Fix the doc: when the distributions file is missing, 503 is returned, not an empty answer. 2009-11-08 iulius * innxmit currently requires the message-ID after 501. * RFC 3977 compliance: * innxbatch and innxmit now recognize 403 for a problem preventing the action from being taken. * innxmit now also recognizes 501 when there is a syntax error and considers it as a reject (IHAVE/CHECK/TAKETHIS). * A password is not mandatory with AUTHINFO USER/PASS. A username may be enough to authenticate according to RFC 4644. * With AUTHINFO USER/PASS, the username is mandatory. * Use uppercase letters. * tinyleaf should use 201 and not 200 as initial greeting. Using POST is not possible. * Streaming commands were still allowed when streaming was deactivated. * * Fix a long-standing bug in TAKETHIS: when authentication was required, a 480 code was answered before having received the whole multi-data block, which broke the NNTP protocol. * Implement RFC 3977 and 4644 for streaming. TAKETHIS is now totally RFC-compliant and checks the syntax. (However, 501 is still not sent for interoperability reasons). * Better response when an article is cancelled. * When XBATCH answers 501, it is because of an invalid size, not a missing size (the new parser takes care of it before calling XBATCH). * HDR, XHDR and XPAT were not properly showing leading spaces in header values. Besides, CR and LF were both changed to spaces. It is not what should be done: CRLF should be removed and CR, LF, NUL, TAB should be changed to spaces after the removal of CRLF. * * Add a parameter to wire_findheader() so as to keep initial whitespaces, if needed. It is USEFOR-compliant (RFC 5536). * archive, makehistory and tdx-util now generate valid overview data (with leading spaces). * Fix a bug in archive which did not change LF (withouth CR) into spaces when generating overview data. * Update the test suite. 2009-11-07 iulius * In streaming mode, there is no code to defer an article sent via TAKETHIS. When the server was paused, we used 403 (temporary failure). However, RFC 4644 mentions that we MUST send 400 here and close the connection so as not to reject the article. * Fix a bug introduced by commit 8437. If the Message-ID: or the Supersedes: headers contains trailing white spaces, the article is stored corrupted (we have a '\r' inside the header). Introduce more intelligence in the parsing (see a wish explained in commit 6921) and retain the removed character to properly put it again after the parsing. * If keyword generation is set to true in inn.conf but the Keywords: header is not stored in the overview, warn the news administrator and deactive keyword generation (because it is useless and eats resources for nothing). * Improve syntax checks on message-IDs: nnrpd was only based on RFC 3977, and innd on RFC 1036. They are now both based on RFC 5536 (USEFOR). 2009-11-06 iulius * Record the extra persistant state of NNTP commands per channel instead of a static global variable. * Fix two gcc 4.4 cast warnings. * Add two new files for the test suite in the MANIFEST file. * Update the prototype of the function for keyword generation code when INN is compiled without that support. * * Add a new "unsigned long" type to the configuration parser. It will properly warn the user when a variable contains a negative integer. It will prevent INN from crashing at several places (like a xmalloc(-5) which in fact allocates 4GB of memory on a 32-bit architecture). * Modify lots of casts (some are now useless, others needed). * Also update the test suite, adding case tests for unsigned integers and lists. * * The keyword generation code now generates a Keywords: header only if the original article does not already have one. * Fixed a segfault because of a possible invalid pointer beyond the allocated Keywords: header. * In case the generated Keywords: header is empty, nothing will be retained (because "Keywords: \r\n" is an invalid header). * The generated Keywords: header now does not begin with a comma. 2009-11-04 iulius * Fix a memory leak in the LISTGROUP command when the provided range is invalid. And in GROUP/LISTGROUP commands when the client does not have read access. * Fix a memory leak in the POST command when the Newsgroups: header is invalid. * When INN is compiled without SASL support, AUTHINFO SASL is an unkwnown AUTHINFO command. It should return 501 (syntax error) instead of 503 (not mentioned in RFC for that case). 2009-11-03 iulius * Fix a bug in cvtbatch which was returning only the size of the headers of an article with the "b" flag. It now correctly returns the size of the whole article (which is what "b" is supposed to do). Also add a missing free() call. 2009-11-01 iulius * Change a few log lines. * Remove dead code in batcher (it is no longer possible to use file names). * Convert the documentation for innxmit into POD. * * New "t" flag for "-w" with cvtbatch, so as to retrieve the arrival time of an article. * Convert the man page to POD. Mention the default value for "-w"; it is no longer possible to use file names with cvtbatch. * When keyword generation was done on an article which has a Keywords: header whose length is greater than the keylimit: parameter, innd crashed. Change memcpy to strlcpy. * innreport was not correctly summing innd stats when the hostname was an IPv6 address instead of a fully qualified domain name. 2009-10-28 iulius * Fix two gcc warnings caused by (size_t *) and (unsigned int *) casts. 2009-10-25 iulius * After successful authentication, MODE-READER is not an available capability. * Call the function for MODE CANCEL before the check on the length of the arguments. * When the argument to CHECK is not valid, make sure we properly answer 438 with the whole given argument. * * Add support for whitespaces in username/password for AUTHINFO USER/PASS commands. * Properly retain the whole (possibly syntactically wrong) message-ID of CHECK/TAKETHIS commands. * Implement CAPABILITIES in innd. * Improve authentication in innd and implement RFC 4643 (AUTHINFO USER/PASS): * If a feeder will not be able to authenticate (because it already has access to every feeder commands), 502 is returned instead of letting it authenticate [and fail to]. * Do not allow AUTHINFO PASS before having sent AUTHINFO USER. * Document how nnrpd differs in the implementation of RFC 3977. 2009-10-24 iulius * Fix the test suite because of a change of return value when an article is totally empty. * Add support for the third wildmat argument to LIST commands. Also check that the output does not contain a dot on a single line. * If ".\r\n" was put into motd.news, the result of LIST MOTD was breaking the NNTP protocol. * When innd cannot provide information for LIST NEWSGROUPS and LIST ACTIVE.TIMES, it now returns 503. It was giving an invalid result (".\r\n" without any response code!). * Return 502 (permission denied) or 401 MODE-READER to LIST commands when the feeder cannot use LIST commands. 2009-10-23 iulius * Fix a bug when articles were fed to innd via IHAVE or TAKETHIS with an empty article: the article terminator was not recognized. Therefore, subsequent NNTP commands were eaten inside the article... See also commit 8149 for the same issue with an empty body. * * Return 435/438 instead of 501 to IHAVE/CHECK commands for compatibility reasons. * When a CHECK/TAKETHIS command line is too long and no streaming is allowed, 500 must be returned (these commands are unknown). * Remove dead code. * Missing to deallocate grouplist when an error occurs when accessing the active file. * Return 412 (no group selected) instead of 501 (syntax error) when using HEAD/STAT with an article number. 2009-10-21 iulius * * Improve logging of MODE commands in news.notice. * Specify that MODE READER cannot be sent after a successful authentication because the client is known as a reader. * Improve the greeting message and say we are in transit mode with innd. * * Add a flag to each NNTP command so as to mention whether it can be used without being authenticated (AUTHINFO, HELP, MODE and QUIT). * Eliminate the special casing of the state CSgetauth. Whether the peer has authenticated is now stored in the channel rather than in the channel state. It simplifies a lot the code. * Fix a bug in the response for reader commands: innd was answering with a superfluous blank line when readers were not allowed. close #99 * Add support for whitespaces in usernames/passwords provided with AUTHINFO USER/PASS. nnrpd was previously considering as invalid passwords like "a b" (two arguments "a" and "b" when only one was expected) or parsing " ab" as "ab" (stripping the leading space). nnrpd now treats everything after the first whitespace character following AUTHINFO USER/PASS, up to, but not including, the CRLF, as the username/password. Thanks to Jeffrey M. Vinocur for the initial patch. close #30 2009-10-20 iulius * Mention in the documentation that when "nnrpd -i" is used (for an initial command), the connection is closed after the response. 2009-10-17 iulius * * Add a decent parser to innd for NNTP commands. It now puts into a table the given arguments. It permits to have a far better grammar parser (commands like "IHAVE" are no longer valid, and commands like " IHAVE " are now valid). * The length of each NNTP command is no longer necessary to be kept in memory by the command handler. see #99 * Typos, and mark MAXHEADERSIZE to be fixed in innd code. * Do not take into account leading white spaces in NNTP commands in the RTlong case. * Do not close the connection upon receiving an NNTP command containing only spaces! * Ignore lines smaller than 2 bytes (that is to say lines containing only "\r\n" or something else like "a\n"). * * If the length of NNTP arguments is too long (> 497 bytes), a 501 error is now returned. * If a command is given less (or more) arguments than expected, a 501 error is now returned. * Improve the HELP output for innd, specifying expected arguments. * HEAD answers 412 (not in a newsgroup) when requesting an article number. It was previously sending 501 (syntax error in mesasge-ID). 2009-10-16 iulius * For unimplemented NNTP commands like SLAVE, do not return 501 when the command line is too long, but 500. * NNTP compliance (RFC 3977): * Check whether the NNTP command sent by a news client is too large (> 512 bytes). Return an error (500 or 501, depending on the base command) if it is the case. * Send 400 instead of 500 when innd closes the connection after 10 (BAD_COMMAND_COUNT) unrecognized commands. * Use integers instead of strings for NNTP response codes (HEAD and STAT). * Return a comment for 205 (QUIT), 235 (IHAVE OK) and 400 (failure and exit) response codes. Do not use strings from include/nntp.h. 2009-10-10 iulius * * Mention --enable-keywords and --with-openssl in the default inn.conf configuration file. * No need to regenerate the overview when the keyword generation code is used. * * Mention the OVER command instead of XOVER. * Mention the --enable-keywords configure option in inn.conf for keyword generation. * * The "will" keyword was checked twice. * Fix a gcc warning (a signed/unsigned cast). * Fix a segfault in the keyword generation code for articles already containing a Keywords: header. Thanks to Nix for the patch. 2009-10-09 iulius * Remove duplicate contents in news.daily, as for the run of expire and expireover. Also add a warning when the "lowmark" keyword is used without "expireover". Thanks to D. Stussy for the patch. 2009-10-03 iulius * Mention the bug-fix in the keyword generation code. * inndstart is no longer used (it was removed in INN 2.5.0) so we mention it in our FAQ. * Fix a GNUism in news.daily (a directory and "-print" are needed). Thanks to S.P. Zeidler for the patch. 2009-10-01 eagle * Update keyword code to match current article handling We no longer copy the article out of the channel and nul-terminate it, but the keyword generation code was assuming that the article was nul-terminated. Modify KEYgenerate to take an article length and use xmalloc/memcpy to make a copy rather than strdup. Thanks to Nix for the report. 2009-09-29 iulius * Bump version to INN 2.5.1. * Mention in Perl and Python hooks that UTF-8 should be used for reject messages. 2009-09-28 iulius * Mention that active.times and distrib.pats should be encoded in UTF-8, as well as ctlinnd and nnrpd arguments. * Add a function to validate the encoding of UTF-8 strings. "ctlinnd pause", "ctlinnd readers", "ctlinnd reject", "ctlinnd throttle" and "nnrpd -r" commands now expect a properly encoded reason. 2009-09-11 iulius * Update control.ctl to its last version. * Update support files for autoconf to their last stable version. 2009-09-09 iulius * Improve the changelog wording. compress does not have patent issues any longer. * Use new Autoconf 2.60 macros AC_TYPE_UINT16_T, AC_TYPE_UINT32_T, AC_TYPE_LONG_LONG_INT and AC_TYPE_SSIZE_T. We do not need m4/int32.m4 any longer to define uint32_t (int32_t is not used). 2009-09-07 iulius * Check that a CAF header contains at least 128 bytes for the free bitmap header. Otherwise, increase the blocksize. On 64-bit systems, a CAF header can exceed the size of the default bitmap, which prevents timecaf from working. 2009-09-05 iulius * Update the maximum size of a CAF. Limits are 3.5GB or 262144 articles in a CAF. * Remove trailing spaces. * Fix a documentation error about timecaf: it does not work per newsgroup (though it used to). FreeZoneIndexBytes does not exist; it is FreeZoneIndexSize. Also remove trailing spaces. * Support for sequence numbers higher than 65535 (2^16-1) in the timecaf storage method. We can now integrate up to (2^32-1) articles in a mere CAF. A CAF contains all the articles that arrive during 256 seconds. Thanks to Kamil Jonca for the bug report and his patch. Also explain how to decode timecaf tokens and paths. And use fixed 16-bit and 32-bit numbers for tokens; timecaf did not work on systems where short ints were not 16-bit integers. * Missing explanation for the hexadecimal value of the timehash storage class. * Missing explanation for the hexadecimal value of the tradspool storage class. * Explain how to decode cnfs tokens. Also remove trailing spaces. * Explain how to decode tradspool tokens and paths. Also remove trailing spaces. * ts.ng.db no longer exists; it is tradspool.map. Also remove trailing spaces. * Convert tabs to spaces. * Remove trailing spaces. * Explain how to decode timehash tokens and paths. Also use fixed 16-bit and 32-bit numbers for tokens; timehash did not work on systems where short ints were not 16-bit integers. 2009-09-02 iulius * Do not send 205 on exit when the client does not send QUIT. 2009-08-29 iulius * Increase the CNFS blocksize from 512 bytes to 4096 bytes and the size limit for a buffer from 1 TB to 16 TB. Currently the CNFS storage method uses a 512 byte (1 sector)- granularity for its "filesystem". That was great in the nineties, but nowadays that is very limiting: - most filesystems use 4K blocks, so a write to a 512 byte- CNFS block can result in a read-modify-write cycle, slowing down writes enormously (effectively making them synchronous) - With larger devices, the block-bitmap at the start balloons in size - The size limit of a CNFS file/partition is 2^31 * 512 = 1 TB. (the block-offset is stored in the CNFS token as a signed integer..) So I have updated storage/cnfs/ to use 4K blocks. This introduces a new CNFS version in the CNFS header, version 4. The header now includes a blocksize member, which is 4K by default. The block offset is now encoded in the CNFS token as a unsigned int. CNFSv4 supports files/partitions up to 16 TB with a 4K blocksize. If we want to support > 16TB with 4K blocks, that is doable by stealing a few bits from the 'cycnum' value in the CNFS token. The code was updated so that for CNFSv4 and up the cyclenumber wraps on 2^24 instead of 2^32 (with one wrap per day, that's good for 45000 years, so there is no problems there). So we have 8 bits for that, but the rest of the code has not been written yet. The code works fine with existing CNFSv3 files/partitions. cnfsstat and cnfsheadconf have also been updated to understand CNFSv4. Right now a new CNFS file/device is always initialized with 4K blocksize, but it would be trivial to make that configurable. With larger blocksizes we might want to look at the CNFS write padding though it is perhaps not useful to pad CNFS writes to larger blocks than 4K. It doesn't do any harm though. Thanks to Miquel van Smoorenburg for the CNFSv4 patch. 2009-08-22 iulius * Silent gcc warnings for a possibly uninitialized variable. 2009-08-21 iulius * Use Autoconf quadrigraphs to properly encode brackets. * Mention ckpasswd(8) in the "see also" section of the documentation for auth_krb5. 2009-08-20 eagle * Avoid using markup in prunehistory NAME section Avoid POD markup in the prunehistory NAME section. Not all versions of pod2man will strip this markup when generating the man page, and *roff markup in NAME confuses some man implementations. * Recommend against using auth_krb5 In the auth_krb5 man page, recommend using ckpasswd with PAM support and a Kerberos PAM module instead of this authenticator. * Rewrite auth_krb5 to use modern Kerberos APIs Use krb5_get_init_creds_password and the current APIs and remove a bunch of cruft that's unnecessary and uninteresting. The library defaults will take care of things like building the correct service principal. Add a call to krb5_verify_init_creds, although this will only work if nnrpd is pointed to a keytab that it can read or if it's run as root. Looking in a different keytab for a local key with which to validate the credentials is left as future work and requires additional configuration to point to the correct keytab. * Strict aliasing cleanups in innd network code gcc 4.4 is now stricter about aliasing checks and doesn't like taking variables of type struct sockaddr_storage and casting them or assigning pointers to them to other struct types and then dereferencing or storing through those other pointers. It may optimize the stores away, which would be bad. The primary affected code is the inetd query code. There, allocate memory from the heap instead of the stack and use a variable of type struct sockaddr *, which is cast to other pointer types. gcc knows how to deal with that. Elsewhere, eliminate RCaddressmatch in favor of network_sockaddr_equal, which does the same thing but is aliasing-clean. Stop using SA_LEN to get the length of an address for memcpy and instead just copy the full size of a sockaddr_storage, which given that both the source and the destination are sockaddr_storage variables will be safe. 2009-08-20 iulius * Convert makedbz(8) and prunehistory(8) documentation to POD. 2009-08-19 iulius * Add examples in grephistory documentation. 2009-08-18 iulius * Changelog for previous commit 8578. * * Return the contents of the expires history field when "grephistory -l" is used. It was always empty. * Mention in documentation that "grephistory -l" returns nothing when the storage API token does not exist. * Fixed "grephistory -v" which does not work when the storage API token does not exist. * Convert expire(8) and expirerm(8) documentation to POD. * Various fixes and homogenizations. 2009-08-17 iulius * * History retention is now done, when possible, on posting times and not arrival times. Otherwise, articles may be kept too long in history. * HISremember in history API now has a fourth argument (the article posting date). * The default value for /remember/ is now 11 (artcutoff + 1 in order to take into account articles posted one day into the future). * Documentation rewording and improvements. * Various homogenizations. 2009-08-15 iulius * Typo for logstatus, an inn.conf parameter. close #84 (which was in fact already committed in INN 2.5.0, see revision [7633]) * Typo: occurance -> occurrence. 2009-08-14 iulius * A patch from Christopher Biedl to alter ARTcancelverify to check whether at least one group in the cancel message can be found in the article to be cancelled. The check for matching Sender: and From: headers is useless and removed. close #38 * Add a restrictive umask before generating TLS certificates. * Typo: interpretor -> interpreter. 2009-08-05 iulius * The README files provided with snapshots were still mentioning the inn-bugs mailing-list instead of inn-workers. 2009-07-30 iulius * A wrong source was logged to the news log for local postings, especially when the local server was not listed in incoming.conf (RChostname() then returned a wrong static buffer and ARTlog() also did not handle the case of a local connection). Also fix the initialization of data->FeedsiteLength when needed. close #15 2009-07-29 iulius * Move dbz.h into the public inn directory (it is for instance used by suck). However, it should be noted that if another history file format is implemented in the future, it might cause problems to external programs. 2009-07-18 iulius * Log a checkgroups only once in innreport. Now that all control.ctl matching lines are used, they were all logged by innreport as a checkgroups. We only count for several doit actions. * Do not act on a checkgroups when there isn't any matching newsgroup. 2009-07-08 iulius * If innfeed is not used, news.daily reports an error message when it tries to find out the pid of all the running instances of innfeed. We now check for that. Thanks to Andreas M. Kirchwitz for the patch. 2009-07-03 iulius * Convert the cnfsheadconf man page into POD. * When "-c" is not specified, cnfsheadconf exits. It now displays the status of all CNFS buffers, as written in its documentation. * * Use Math::BigInt rather than bigint.pl. * Use Math::BigFloat not to wrongly truncate numbers. 2009-06-28 iulius * * Add a "-v" flag to cnfsstat to see the output of consistency checks for article storage and history. * Convert the cnfsstat man page into POD. * Update the NoCeM sample file. * Use gzip instead of compress as the default UUCP batcher. 2009-06-23 iulius * Add the -L flag to makehistory in order to specify a maximum load average. If exceeded, the rebuild is paused until it decreases below the specified load. Patch by Jonathan Kamens. 2009-06-21 iulius * Regenerate dependencies after commit 8530. 2009-06-21 eagle * inn/storage.h depends on inn/defines.h and inn/options.h. 2009-06-20 iulius * Fix a bug in how perl-nocem parses its configuration file. * sasl.conf does not exist any longer. * * Change the default value for TLS certificates from to . * Add a default value to tlscapath: because it is required by nnrpd when TLS is used. * Improve the TLS documentation. * * Fixed a segfault in imap_connection which can occur when SASL is used (overflow in strlcpy). * Owing to the US-CERT vulnerability note VU#238019, Cyrus SASL library has slightly changed. imap_connection and nnrpd now handle that change. Otherwise, some answers are too long to be properly computed during SASL exchanges (because of a new null character added by sasl_encode64). * The distributions file was not installed. It was an oversight. 2009-06-17 iulius * * Fixed a segfault when retrieving via HDR/XHDR/XPAT the contents of an extra overview field absent from the headers of an article. The NEWNEWS command was also affected on very rare cases. Thanks to Tim Woodall for the bug report. * HDR/XHDR/XPAT answers are now robust when the overview database is inconsistent. When the overview schema was modified without the overview database being rebuilt, wrong results could be returned for extra fields (especially a random portion of some other header). The desired header name is now explicitly searched in the overview information. In order to achieve that, the overview_getheader() function was split into overview_get_standard_header() and overview_get_extra_header(). It allows to search by index for standard headers and to walk extra overview fields. * Fixed a memory leak when requesting metadata information (:bytes and :lines). * Typo: "to precede" instead of "to preceede". * Mention to update install-sh to its last version. * Typos. * * Fix an inaccuracy in the count of overview stats. * Also fix a potential segfault in case overview_getheader() returns a NULL pointer (though it should not for the Message-ID). 2009-06-11 eagle * Link tinyleaf with $(LIBS), needed for networking libraries on Solaris. 2009-06-09 iulius * Update the release date of INN 2.5.0. 2009-06-06 iulius * Clarify a sentence about the difference between doit and mail for a checkgroups. * * Import new versions of a few m4 files provided by rra-c-util 1.0. * getaddrinfo.m4 is merged into socket.m4. * inncheck now recognizes the new "/maxdocheckgroups/" keyword in control.ctl. 2009-05-24 iulius * Use AS_HELP_STRING instead of obsolete AC_HELP_STRING. * * Use AC_COMPILE_IFELSE instead of AC_EGREP_CPP. * Use AC_CACHE_CHECK instead of AC_MSG_CHECKING, AC_CACHE_VAL and AC_MSG_RESULT. * Better wording for the creation of /pgp/ncmring.gpg. * Bump version numbers of TRUNK (2.5.0 -> 2.6.0). inn-2.6.0/control/0000755000175200017520000000000012575023677013413 5ustar iuliusiuliusinn-2.6.0/control/signcontrol.in0000644000175200017520000005437612575023702016310 0ustar iuliusiulius#! /usr/bin/perl -w # written April 1996, (David C Lawrence) # Currently maintained by Russ Allbery # Version 1.8, 2003-07-06 # # Changes from 1.6 -> 1.8 # -- Added support for GnuPG. # -- Replace signing code with code from PGP::Sign that generates detached # signatures instead. Otherwise, GnuPG signatures with DSA keys could # not be verified. Should still work the same as before with RSA keys. # -- Thanks to new signing code, no longer uses a temporary file. # -- Only lock when using PGP; GnuPG shouldn't need it. # # Changes from 1.5 -> 1.6 # -- eliminated subprocess use (except pgp, of course). # -- interlock against competing signing processes. # -- allow optional headers; see $use_or_add. # -- added simple comments about why particular headers are signed. # -- made error messages a tad more helpful for situations when it is hard # to know what message was trying to be signed (such as via an "at" # job). # -- set $action, $group, $moderated to "" to prevent unusued variable # warnings in the event a Control header can't be parsed. # -- moved assignment of $pgpend out of loop. # # Changes from 1.4 -> 1.5 # -- need to require Text::Tabs to get 'expand' for tabs in checkgroups. # # Changes from 1.3 -> 1.4 # -- added checkgroups checking. # -- added group name in several error messages (for help w/batch # processing). # -- disabled moderator address checking. # -- adjusted newsgroups line (ie, tabbing fixed) now correctly # substituted into control message. # # Changes from 1.2.3 -> 1.3 # -- skip minor pgp signature headers like "charset:" after "version:" # header and until the empty line that starts the base64 signature block. use strict; # CONFIGURATION # PGP variables. # # $pgp can be set to the path to GnuPG to use GnuPG instead. The program # name needs to end in gpg so that signcontrol knows GnuPG is being used. # # STORING YOUR PASSPHRASE IN A FILE IS A POTENTIAL SECURITY HOLE. # Make sure you know what you're doing if you do it. # If you don't use $pgppassfile, you can only use this script interactively. # If you DO use $pgppassfile, it is possible that someone could steal # your passphrase either by gaining access to the file or by seeing # the environment of a running pgpverify program. # # $pgplock is used because pgp does not guard itself against concurrent # read/write access to its randseed.bin file. A writable file is needed; # The default value is to use the .pgp/config.txt file in the home # directory of the user running the program. Note that this will only # work to lock against other instances of signcontrol, not all pgp uses. # $pgplock is not used if $pgp ends in 'gpg' since GnuPG doesn't need # this. my $pgpsigner = 'INSERT_YOUR_PGP_USERID'; my $pgppassfile = ''; # file with pass phrase for $pgpsigner my $pgp = "/usr/local/bin/pgp"; my $pgpheader = "X-PGP-Sig"; my $pgplock = (getpwuid($<))[7] . '/.pgp/config.txt'; # this program is strict about always wanting to be consistent about what # headers appear in the control messages. the defaults for the # @... arrays are reasonable, but you should edit the force values. # these headers are acceptable in input, but they will be overwritten with # these values. no sanity checking is done on what you put here. also, # Subject: is forced to be the Control header prepending by "cmsg". also, # Newsgroups: is forced to be just the group being added/removed. # (but is taken as-is for checkgroups) my %force; $force{'Path'} = 'bounce-back'; $force{'From'} = 'YOUR_ADDRESS_AND_NAME'; $force{'Approved'} = 'ADDRESS_FOR_Approved_HEADER'; $force{'X-Info'}='ftp://ftp.isc.org/pub/pgpcontrol/README.html' . "\n\t" . 'ftp://ftp.isc.org/pub/pgpcontrol/README'; # these headers are acceptable in input, or if not present then will be # created with the given value. None are enabled by default, because they # should not be necessary. Setting one to a null string will pass through # any instance of it found in the input, but not generate one if it is # missing. If you set any $use_or_add{} variables, you must also put it in # @orderheaders below. # # Note that Distribution nearly never works correctly, so use it only if # you are really sure the propagation of the article will be limited as # you intend. This normally means that you control all servers the # distribution will go to with an iron fist. # my %use_or_add; # $use_or_add{'Reply-To'} = 'YOUR_REPLY_ADDRESS'; # $use_or_add{'Oranization'} = 'YOUR_ORGANIZATION'; # $use_or_add{'Distribution'} = 'MESSAGE_DISTRIBUTION'; # host for message-id; this could be determined automatically based on # where it is run, but consistency is the goal here my $id_host = 'FULL_HOST_NAME'; # headers to sign. Sender is included because non-PGP authentication uses # it. The following should always be signed: # Subject -- some older news systems use it to identify the control action. # Control -- most news systems use this to determine what to do. # Message-ID -- guards against replay attacks. # Date -- guards against replay attacks. # From -- used by news systems as part of authenticating the message. # Sender -- used by news systems as part of authenticating the message. my @signheaders = ('Subject', 'Control', 'Message-ID', 'Date', 'From', 'Sender'); # headers to remove from real headers of final message. # If it is a signed header, it is signed with an empty value. # set to () if you do not want any headers removed. my @ignoreheaders = ('Sender'); # headers that will appear in final message, and their order of # appearance. all _must_ be set, either in input or via the $force{} and # $use_or_add{} variables above. # (exceptions: Date, Lines, Message-ID are computed by this program) # if header is in use_or_add with a null value, it will not appear in output. # several are required by the news article format standard; if you remove # these, your article will not propagate: # Path, From, Newsgroups, Subject, Message-ID, Date # if you take out these, your control message is not very useful: # Control, Approved # any headers in @ignoreheaders also in @orderheaders are silently dropped. # any non-null header in the input but not in @orderheaders or @ignoreheaders # is an error. # null headers are silently dropped. my @orderheaders = ('Path', 'From', 'Newsgroups', 'Subject', 'Control', 'Approved', 'Message-ID', 'Date', 'Lines', 'X-Info', $pgpheader); # this program tries to help you out by not letting you sign erroneous # names, especially ones that are so erroneous they run afoul of naming # standards. # # set to match only hierarchies you will use it on # include no '|' for a single hierarchy (eg, "$hierarchies = 'uk';"). my $hierarchies = 'HIERARCHIES'; # the draft news article format standard says: # "subsequent components SHOULD begin with a letter" # where "SHOULD" means: # means that the item is a strong recommendation: there may be # valid reasons to ignore it in unusual circumstances, but # this should be done only after careful study of the full # implications and a firm conclusion that it is necessary, # because there are serious disadvantages to doing so. # as opposed to "MUST" which means: # means that the item is an absolute requirement of the specification # MUST is preferred, but might not be acceptable if you have legacy # newsgroups that have name components that begin with a letter, like # news.announce.newgroups does with comp.sys.3b1 and 17 other groups. my $start_component_with_letter = 'MUST'; ## END CONFIGURATION use Fcntl qw(F_SETFD LOCK_EX); use FileHandle; use IPC::Open3 qw(open3); use POSIX qw(setlocale strftime LC_TIME); use Text::Tabs; # to get 'expand' for tabs in checkgroups $0 =~ s#^.*/##; die "Usage: $0 < message\n" if @ARGV > 0; my $LOCK; umask(0022); # flock needs a writable file, if we create it if ($pgp !~ /gpg$/) { open ($LOCK, '>>', $pgplock) || die "$0: open $pgplock: $!, exiting\n"; flock ($LOCK, LOCK_EX); # block until locked } my ($die, $group, $action, $grouppat, %header, $moderated, $body, @ERROR); # Initialize the $die variable here (we use it to concatenate possible # error messages during the run of the following functions). $die = ''; setgrouppat(); readhead(); readbody(); if ($die) { if ($group) { die "$0: ERROR PROCESSING ${action}group $group:\n", $die; } elsif ($action eq 'check') { die "$0: ERROR PROCESSING checkgroups:\n", $die; } elsif ($header{'Subject'}) { die "$0: ERROR PROCESSING Subject: $header{'Subject'}\n", $die; } else { die $die; } } signit(); if ($pgp !~ /gpg$/) { close ($LOCK) || warn "$0: close $pgplock: $!\n"; } exit 0; sub setgrouppat { my ($plain_component, $no_component); my ($must_start_letter, $should_start_letter); # newsgroup name checks based on RFC 1036bis (not including encodings) rules: # "component MUST contain at least one letter" # "[component] MUST not contain uppercase letters" # "[component] MUST begin with a letter or digit" # "[component] MUST not be longer than 14 characters" # "sequences 'all' and 'ctl' MUST not be used as components" # "first component MUST begin with a letter" # and enforcing "subsequent components SHOULD begin with a letter" as MUST # and enforcing at least a 2nd level group (can't use to newgroup "general") # # DO NOT COPY THIS PATTERN BLINDLY TO OTHER APPLICATIONS! # It has special construction based on the pattern it is finally used in. $plain_component = '[a-z][-+_a-z\d]{0,13}'; $no_component = '(.*\.)?(all|ctl)(\.|$)'; $must_start_letter = '(\.' . $plain_component . ')+'; $should_start_letter = '(\.(?=\d*[a-z])[a-z\d]+[-+_a-z\d]{0,13})+'; $grouppat = "(?!$no_component)($hierarchies)"; if ($start_component_with_letter eq 'SHOULD') { $grouppat .= $should_start_letter; } elsif ($start_component_with_letter eq 'MUST') { $grouppat .= $must_start_letter; } else { die "$0: unknown value configured for \$start_component_with_letter\n"; } foreach my $hierarchy (split /\|/, $hierarchies) { die "$0: hierarchy name $hierarchy not standards-compliant\n" if $hierarchy !~ /^$plain_component$/o; } eval { 'test' =~ /$grouppat/ }; die "$0: bad regexp for matching group names:\n $@" if $@; return; } sub readhead { my ($head, $label, $value); local $/ = ""; $head = ; # get the whole news header $die .= "$0: continuation lines in headers not allowed\n" if $head =~ s/\n[ \t]+/ /g; # rejoin continued lines foreach (split /\n/, $head) { if (/^(\S+): (.*)/) { $label = $1; $value = $2; $die .= "$0: duplicate header $label\n" if $header{$label}; $header{$label} = $value; $header{$label} =~ s/^\s+//; $header{$label} =~ s/\s+$//; } elsif (/^$/) { ; # the empty line separator(s) } else { $die .= "$0: non-header line:\n $_\n"; } } $header{'Message-ID'} = '<' . time . ".$$\@$id_host>"; setlocale(LC_TIME, "C"); $header{'Date'} = strftime("%a, %d %h %Y %T -0000", gmtime); for (@ignoreheaders) { $die .= "ignored header $_ also has forced value set\n" if $force{$_}; $header{$_} = ''; } for (@orderheaders) { $header{$_} = $force{$_} if defined($force{$_}); next if /^(Lines|\Q$pgpheader\E)$/; # these are set later unless ($header{$_}) { if (defined($use_or_add{$_})) { $header{$_} = $use_or_add{$_} if $use_or_add{$_} ne ''; } else { $die .= "$0: missing $_ header\n"; } } } $action = $group = $moderated = ""; if ($header{'Control'}) { if ($header{'Control'} =~ /^(new)group (\S+)( moderated)?$/o || $header{'Control'} =~ /^(rm)group (\S+)()$/o || $header{'Control'} =~ /^(check)groups()()$/o) { ($action, $group, $moderated) = ($1, $2, $3); $die .= "$0: group name $group is not standards-compliant\n" if $group !~ /^$grouppat$/ && $action eq 'new'; $die .= "$0: no group to rmgroup on Control: line\n" if ! $group && $action eq 'rm'; $header{'Subject'} = "cmsg $header{'Control'}"; $header{'Newsgroups'} = $group unless $action eq 'check'; } else { $die .= "$0: bad Control format: $header{'Control'}\n"; } } else { $die .= "$0: can't verify message content; missing Control header\n"; } return; } sub readbody { my ($status, $ngline, $fixline, $used, $desc, $mods); local $/ = undef; $body = ; # slurp the rest of the article $header{'Lines'} = $body =~ tr/\n/\n/ if $body; # the following tests are based on the structure of a # news.announce.newgroups newgroup message; even if you comment out the # "first line" test, please leave the newsgroups line and moderators # checks if ($action eq 'new') { $status = $moderated ? 'a\smoderated' : 'an\sunmoderated'; $die .= "$0: nonstandard first line in body for $group\n" if $body !~ /^\Q$group\E\sis\s$status\snewsgroup\b/; my $intro = "For your newsgroups file:\n"; $ngline = ($body =~ /^$intro\Q$group\E[ \t]+(.+)\n(\n|\Z(?!\n))/mi)[0]; if ($ngline) { $_ = $group; $desc = $1; $fixline = $_; $fixline .= "\t" x ((length) > 23 ? 1 : (4 - ((length) + 1) / 8)); $used = (length) < 24 ? 24 : (length) + (8 - (length) % 8); $used--; $desc =~ s/ \(Moderated\)//i; $desc =~ s/\s+$//; $desc =~ s/\w$/$&./; $die .= "$0: $group description too long\n" if $used + length($desc) > 80; $fixline .= $desc; $fixline .= ' (Moderated)' if $moderated; $body =~ s/^$intro(.+)/$intro$fixline/mi; } else { $die .= "$0: $group newsgroup line not formatted correctly\n"; } # moderator checks are disabled; some sites were trying to # automatically maintain aliases based on this, which is bad policy. if (0 && $moderated) { $die .= "$0: $group submission address not formatted correctly\n" if $body !~ /\nGroup submission address: ?\S+@\S+\.\S+\n/m; $mods = "( |\n[ \t]+)\\([^)]+\\)\n\n"; $die .= "$0: $group contact address not formatted correctly\n" if $body !~ /\nModerator contact address: ?\S+@\S+\.\S+$mods/m; } } # rmgroups have freeform bodies # checkgroups have structured bodies if ($action eq 'check') { for (split /\n/, $body) { my ($group, $description) = /^(\S+)\t+(.+)/; $die .= "$0: no group:\n $_\n" unless $group; $die .= "$0: no description:\n $_\n" unless $description; $die .= "$0: bad group name \"$group\"\n" if $group !~ /^$grouppat$/; $die .= "$0: tab in description\n" if $description =~ /\t/; s/ \(Moderated\)$//; $die .= "$0: $group line too long\n" if length(expand($_)) > 80; } } return; } # Create a detached signature for the given data. The first argument # should be a key id, the second argument the PGP passphrase (which may be # null, in which case PGP will prompt for it), and the third argument # should be the complete message to sign. # # In a scalar context, the signature is returned as an ASCII-armored block # with embedded newlines. In array context, a list consisting of the # signature and the PGP version number is returned. Returns undef in the # event of an error, and the error text is then stored in @ERROR. # # This function is taken almost verbatim from PGP::Sign except the PGP # style is determined from the name of the program used. sub pgp_sign { my ($keyid, $passphrase, $message) = @_; # Ignore SIGPIPE, since we're going to be talking to PGP. local $SIG{PIPE} = 'IGNORE'; # Determine the PGP style. my $pgpstyle = 'PGP2'; if ($pgp =~ /pgps$/) { $pgpstyle = 'PGP5' } elsif ($pgp =~ /gpg$/) { $pgpstyle = 'GPG' } # Figure out what command line we'll be using. PGP v6 and PGP v2 use # compatible syntaxes for what we're trying to do. PGP v5 would have, # except that the -s option isn't valid when you call pgps. *sigh* my @command; if ($pgpstyle eq 'PGP5') { @command = ($pgp, qw/-baft -u/, $keyid); } elsif ($pgpstyle eq 'GPG') { @command = ($pgp, qw/--detach-sign --armor --textmode -u/, $keyid, qw/--force-v3-sigs --pgp2/); } else { @command = ($pgp, qw/-sbaft -u/, $keyid); } # We need to send the password to PGP, but we don't want to use either # the command line or an environment variable, since both may expose us # to snoopers on the system. So we create a pipe, stick the password in # it, and then pass the file descriptor to PGP. PGP wants to know about # this in an environment variable; GPG uses a command-line flag. # 5.005_03 started setting close-on-exec on file handles > $^F, so we # need to clear that here (but ignore errors on platforms where fcntl or # F_SETFD doesn't exist, if any). # # Make sure that the file handles are created outside of the if # statement, since otherwise they leave scope at the end of the if # statement and are automatically closed by Perl. my $passfh = new FileHandle; my $writefh = new FileHandle; local $ENV{PGPPASSFD}; if ($passphrase) { pipe ($passfh, $writefh); eval { fcntl ($passfh, F_SETFD, 0) }; print $writefh $passphrase; close $writefh; if ($pgpstyle eq 'GPG') { push (@command, '--batch', '--passphrase-fd', $passfh->fileno); } else { push (@command, '+batchmode'); $ENV{PGPPASSFD} = $passfh->fileno; } } # Fork off a pgp process that we're going to be feeding data to, and tell # it to just generate a signature using the given key id and pass phrase. my $pgp = new FileHandle; my $signature = new FileHandle; my $errors = new FileHandle; my $pid = eval { open3 ($pgp, $signature, $errors, @command) }; if ($@) { @ERROR = ($@, "Execution of $command[0] failed.\n"); return undef; } # Write the message to the PGP process. Strip all trailing whitespace # for compatibility with older pgpverify and attached signature # verification. $message =~ s/[ \t]+\n/\n/g; print $pgp $message; # All done. Close the pipe to PGP, clean up, and see if we succeeded. # If not, save the error output and return undef. close $pgp; local $/ = "\n"; my @errors = <$errors>; my @signature = <$signature>; close $signature; close $errors; close $passfh if $passphrase; waitpid ($pid, 0); if ($? != 0) { @ERROR = (@errors, "$command[0] returned exit status $?\n"); return undef; } # Now, clean up the returned signature and return it, along with the # version number if desired. PGP v2 calls this a PGP MESSAGE, whereas # PGP v5 and v6 and GPG both (more correctly) call it a PGP SIGNATURE, # so accept either. while ((shift @signature) !~ /-----BEGIN PGP \S+-----\n/) { unless (@signature) { @ERROR = ("No signature from PGP (command not found?)\n"); return undef; } } my $version; while ($signature[0] ne "\n" && @signature) { $version = $1 if ((shift @signature) =~ /^Version:\s+(.*?)\s*$/); } shift @signature; pop @signature; $signature = join '', @signature; chomp $signature; undef @ERROR; return wantarray ? ($signature, $version) : $signature; } sub signit { my ($head, $signheaders); # Form the message to be signed. $signheaders = join ",", @signheaders; $head = "X-Signed-Headers: $signheaders\n"; foreach my $header (@signheaders) { $head .= "$header: $header{$header}\n"; } my $message = "$head\n$body"; # Get the passphrase if available. my $passphrase; if ($pgppassfile && -f $pgppassfile) { $pgppassfile =~ s%^(\s)%./$1%; if (open my $PGPPASS, '<', $pgppassfile) { $passphrase = <$PGPPASS>; close $PGPPASS; chomp $passphrase; } } # Sign the message, getting the signature and PGP version number. my ($signature, $version) = pgp_sign($pgpsigner, $passphrase, $message); unless ($signature) { die "@ERROR\n$0: could not generate signature\n"; } # GnuPG has version numbers containing spaces, which breaks our header # format. Find just some portion that contains a digit. ($version) = ($version =~ /(\S*\d\S*)/); # Put the signature into the headers. $signature =~ s/^/\t/mg; $header{$pgpheader} = "$version $signheaders\n$signature"; for (@ignoreheaders) { delete $header{$_} if defined $header{$_}; } $head = ''; foreach my $header (@orderheaders) { $head .= "$header: $header{$header}\n" if $header{$header}; delete $header{$header}; } foreach my $header (keys %header) { die "$0: unexpected header $header left in header array\n"; } print STDOUT $head; print STDOUT "\n"; print STDOUT $body; return; } # Our lawyer told me to include the following. The upshot of it is that # you can use the software for free as much as you like. # Copyright (c) 1996 UUNET Technologies, Inc. # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. All advertising materials mentioning features or use of this software # must display the following acknowledgement: # This product includes software developed by UUNET Technologies, Inc. # 4. The name of UUNET Technologies ("UUNET") may not be used to endorse or # promote products derived from this software without specific prior # written permission. # # THIS SOFTWARE IS PROVIDED BY UUNET ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL UUNET BE LIABLE FOR ANY DIRECT, # INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED # OF THE POSSIBILITY OF SUCH DAMAGE. # Local variables: # cperl-indent-level: 2 # fill-column: 74 # End: inn-2.6.0/control/controlbatch.in0000644000175200017520000000503012575023702016410 0ustar iuliusiulius#! /bin/sh # fixscript will replace this line with code to load innshellvars ######################################################################## # controlbatch - Run controlchan against a batch file. # # Command usage: controlbatch [feedsite batchfile] # Defaults are feedsite: controlchan!, batchfile: ${BATCH}/controlchan! ######################################################################## # # This script will run controlchan against a batch file. You can use # it to clear occasional backlogs while running controls from a # channel, or even skip the channel and run control messages as a file # feed. # ######################################################################## # # If you're doing the channel thing, you might want to put something # like this in your crontab to do a cleanup in the wee hours: # # 00 04 * * * /controlbatch # ######################################################################## # # If you would rather skip the channel and just process controls each # hour in a batch, use this newsfeeds entry instead of the "stock" # version: # # controlchan!\ # :!*,control,control.*,!control.cancel\ # :Tf,Wnsm: # # And, a crontab entry something like this: # # 30 * * * * /controlbatch # ######################################################################## batchlock="${LOCKS}/LOCK.controlbatch" mypid=$$ # A concession to INN 1.x if [ me${PATHBIN}ow = meow ] ; then PATHBIN=${NEWSBIN} export PATHBIN fi # See if we have no arguments and should use the defaults. If there are # arguments, make sure we have enough to attempt something useful. if [ me${1}ow != meow ] ; then if [ me${2}ow = meow ] ; then echo "Usage: ${0} [feedsite batchfile]" >&2 exit 0 else feedsite=${1} batchfile=${2} fi else feedsite=controlchan\! batchfile=controlchan\! fi # Check if any other copies of controlbatch are running. If we are not # alone, give up here and now. ${PATHBIN}/shlock -p $mypid -f ${batchlock} || exit 0 cd ${BATCH} if [ -s ${batchfile}.work ] ; then cat ${batchfile}.work >>${batchfile}.doit rm -f ${batchfile}.work fi if [ -s ${batchfile} ] ; then mv ${batchfile} ${batchfile}.work if ${PATHBIN}/ctlinnd -s -t30 flush ${feedsite} ; then cat ${batchfile}.work >>${batchfile}.doit rm -f ${batchfile}.work fi fi if [ -s ${batchfile}.doit ] ; then ${PATHBIN}/controlchan \ < ${batchfile}.doit >> ${MOST_LOGS}/controlbatch.log 2>&1 # if you want extra assurance that nothing gets lost... # cat ${batchfile}.doit >> ${batchfile}.done rm -f ${batchfile}.doit fi rm -f ${batchlock} inn-2.6.0/control/Makefile0000644000175200017520000000230712575023702015042 0ustar iuliusiulius## $Id: Makefile 8247 2008-12-21 22:28:19Z iulius $ include ../Makefile.global top = .. ALL = controlbatch controlchan docheckgroups perl-nocem \ pgpverify signcontrol MAN = ../doc/man/perl-nocem.8 ../doc/man/pgpverify.1 all: $(ALL) $(MAN) install: all for F in $(ALL) ; do \ $(CP_XPUB) $$F $D$(PATHBIN)/$$F ; \ done for M in modules/*.pl ; do \ $(CP_RPUB) $$M $D$(PATHCONTROL)/`basename $$M` ; \ done bootstrap: $(MAN) clean clobber distclean: rm -f $(ALL) maintclean: distclean rm -f $(MAN) profiled: all depend: $(FIXSCRIPT): @echo Run configure before running make. See INSTALL for details. @exit 1 ## Build rules. FIX = $(FIXSCRIPT) controlbatch: controlbatch.in $(FIX) ; $(FIX) controlbatch.in controlchan: controlchan.in $(FIX) ; $(FIX) controlchan.in docheckgroups: docheckgroups.in $(FIX) ; $(FIX) docheckgroups.in perl-nocem: perl-nocem.in $(FIX) ; $(FIX) perl-nocem.in pgpverify: pgpverify.in $(FIX) ; $(FIX) pgpverify.in signcontrol: signcontrol.in $(FIX) ; $(FIX) -i signcontrol.in ../doc/man/perl-nocem.8: perl-nocem.in $(POD2MAN) -s 8 -n "PERL-NOCEM" $? > $@ ../doc/man/pgpverify.1: pgpverify.in $(POD2MAN) -s 1 -n "PGPVERIFY" $? > $@ inn-2.6.0/control/pgpverify.in0000644000175200017520000010205312575023702015744 0ustar iuliusiulius#! /usr/bin/perl -w # use lib '@LIBPERLDIR@'; use INN::Config; # If running inside INN, uncomment the above and point to INN::Config. # # Written April 1996, (David C Lawrence) # Currently maintained by Russ Allbery # Version 1.29, 2014-04-15 # # NOTICE TO INN MAINTAINERS: The version that is shipped with INN is the # same as the version that I make available to the rest of the world # (including non-INN sites), so please make all changes through me. # # This program requires Perl 5, probably at least about Perl 5.003 since # that's when FileHandle was introduced. If you want to use this program # and your Perl is too old, please contact me (eagle@eyrie.org) and tell # me about it; I want to know what old versions of Perl are still used in # practice. # # Changes from 1.28 -> 1.29 # -- Disambiguate numbered lists from description lists in POD to silent # a pod2man warning. # -- Add a --findid= flag to explicitly search for in the # output from PGP's analysis of the message. In case the signature is # valid but does not contain , pgpverify exits with the new # exit status 4. # # Changes from 1.27 -> 1.28 # -- Use the INN::Config Perl module instead of innshellvars.pl to # accomodate the new build process of INN 2.5. # # Changes from 1.26 -> 1.27 # -- Default to pubring.gpg when trustedkeys.gpg is not found in the # default key location, for backward compatibility. # # Changes from 1.25 -> 1.26 # -- Return the correct status code when the message isn't verified # instead of always returning 255. # # Changes from 1.24 -> 1.25 # -- Fix the -test switch to actually do something. # -- Improve date generation when logging to standard output. # # Changes from 1.23 -> 1.24 # -- Fix bug in the recognition of wire-format articles. # # Changes from 1.15 -> 1.23 # -- Bump version number to match CVS revision number. # -- Replaced all signature verification code with code that uses detached # signatures. Signatures generated by GnuPG couldn't be verified using # attached signatures without adding a Hash: header, and this was the # path of least resistance plus avoids munging problems in the future. # Code taken from PGP::Sign. # # Changes from 1.14 -> 1.15 # -- Added POD documentation. # -- Fixed the -test switch so that it works again. # -- Dropped Perl 4 compatibility and reformatted. Now passes use strict. # # Changes from 1.13.1 -> 1.14 # -- Native support for GnuPG without the pgpgpg wrapper, using GnuPG's # program interface by Marco d'Itri. # -- Always use Sys::Syslog without any setlogsock call for Perl 5.6.0 or # later, since Sys::Syslog in those versions of Perl uses the C library # interface and is now portable. # -- Default to expecting the key ring in $inn'newsetc/pgp if it exists. # -- Fix a portability problem for Perl 4 introduced in 1.12. # # Changes from 1.13 -> 1.13.1 # -- Nothing functional, just moved the innshellvars.pl line to the head of # the script, to accomodate the build process of INN. # # Changes from 1.12 -> 1.13 # -- Use INN's syslog_facility if available. # # Changes from 1.11 -> 1.12 # -- Support for GnuPG. # -- Use /usr/ucb/logger, if present, instead of /usr/bin/logger (the latter # of which, on Solaris at least, is some sort of brain damaged POSIX.2 # command which doesn't use syslog). # -- Made syslog work for dec_osf (version 4, at least). # -- Fixed up priority of '.' operator vs bitwise operators. # # Changes from 1.10 -> 1.11 # -- Code to log error messages to syslog. # See $syslog and $syslog_method configurable variables. # -- Configurably allow date stamp on stderr error messages. # -- Added locking for multiple concurrent pgp instances. # -- More clear error message if pgp exits abnormally. # -- Identify PGP 5 "BAD signature" string. # -- Minor diddling for INN (path to innshellvars.pl changed). # # Changes from 1.9 -> 1.10 # -- Minor diddling for INN 2.0: use $inn'pathtmp if it exists, and # work with the new subst method to find innshellvars.pl. # -- Do not truncate the tmp file when opening, in case it is really # linked to another file. # # Changes from 1.8 -> 1.9 # -- Match 'Bad signature' pgp output to return exit status 3 by removing # '^' in regexp matched on multiline string. # # Changes from 1.7 -> 1.8 # -- Ignore final dot-CRLF if article is in NNTP format. # # Changes from 1.6 -> 1.7 # -- Parse PGP 5.0 'good signature' lines. # -- Allow -test switch; prints pgp input and output. # -- Look for pgp in INN's innshellvars.pl. # -- Changed regexp delimiters for stripping $0 to be compatible with old # Perl. # # Changes from 1.5 -> 1.6 # -- Handle articles encoded in NNTP format ('.' starting line is doubled, # \r\n at line end) by stripping NNTP encoding. # -- Exit 255 with pointer to $HOME or $PGPPATH if pgp can't find key # ring. (It probably doesn't match the necessary error message with # ViaCrypt PGP.) # -- Failures also report Message-ID so the article can be looked up to # retry. # # Changes from 1.4 -> 1.5 # -- Force English language for 'Good signature from user' by passing # +language=en on pgp command line, rather than setting the # environment variable LANGUAGE to 'en'. # # Changes from 1.3 -> 1.4 # -- Now handles wrapped headers that have been unfolded. # (Though I do believe news software oughtn't be unfolding them.) # -- Checks to ensure that the temporary file is really a file, and # not a link or some other weirdness. # Path to the GnuPG gpgv binary, if you have GnuPG. If you do, this will # be used in preference to PGP. For most current control messages, you # need a version of GnuPG that can handle RSA signatures. If you have INN # and the script is able to successfully include your INN::Config module, # the value of $INN::Config::gpgv will override this. # $gpgv = '/usr/local/bin/gpgv'; # Path to pgp binary; for PGP 5.0, set the path to the pgpv binary. If # you have INN and the script is able to successfully include your # INN::Config module, the value of $INN::Config::pgp will override this. $pgp = '/usr/local/bin/pgp'; # If you keep your keyring somewhere that is not the default used by pgp, # uncomment the next line and set appropriately. If you have INN and the # script is able to successfully include your INN::Config module, this # will be set to $INN::Config::newsetc/pgp if that directory exists unless # you set it explicitly. GnuPG will use a file named pubring.gpg in this # directory. # $keyring = '/path/to/your/pgp/config'; # If you have INN and the script is able to successfully include your # INN::Config module, the value of $INN::Config::pathtmp and # $INN::Config::locks will override these. $tmpdir = "/tmp"; $lockdir = $tmpdir; # How should syslog be accessed? # # As it turns out, syslogging is very hard to do portably in versions of # Perl prior to 5.6.0. Sys::Syslog should work without difficulty in # 5.6.0 or later and will be used automatically for those versions of Perl # (unless $syslog_method is ''). For earlier versions of Perl, 'inet' is # all that's available up to version 5.004_03. If your syslog does not # accept UDP log packets, such as when syslogd runs with the -l flag, # 'inet' will not work. A value of 'unix' will try to contact syslogd # directly over a Unix domain socket built entirely in Perl code (no # subprocesses). If that is not working for you, and you have the # 'logger' program on your system, set this variable to its full path name # to have a subprocess contact syslogd. If the method is just "logger", # the script will search some known directories for that program. If it # can't be found & used, everything falls back on stderr logging. # # You can test the script's syslogging by running "pgpverify < # /some/text/file" on a file that is not a valid news article. The # "non-header at line #" error should be syslogged. # # $syslog_method = 'unix'; # Unix doman socket, Perl 5.004_03 or higher. # $syslog_method = 'inet'; # UDP to port 514 of localhost. # $syslog_method = ''; # Don't ever try to do syslogging. $syslog_method = 'logger'; # Search for the logger program. # The next two variables are the values to be used for syslog's facility # and level to use, as would be found in syslog.conf. For various # reasons, it is impossible to economically have the script figure out how # to do syslogging correctly on the machine. If you have INN and the # script is able to successfully include you INN::Config module, then # the value of $INN::Config::syslog_facility will override this value of # $syslog_facility; $syslog_level is unaffected. $syslog_facility = 'news'; $syslog_level = 'err'; # Prepend the error message with a timestamp? This is only relevant if # not syslogging, when errors go to stderr. # # $log_date = 0; # Zero means don't do it. # $log_date = 1; # Non-zero means do it. $log_date = -t STDOUT; # Do it if STDOUT is to a terminal. # End of configuration section. require 5; use strict; use vars qw($gpgv $pgp $keyring $tmp $tmpdir $lockdir $syslog_method $syslog_facility $syslog_level $log_date $findid $test $messageid); use Fcntl qw(O_WRONLY O_CREAT O_EXCL); use FileHandle; use IPC::Open3 qw(open3); use POSIX qw(strftime); use Getopt::Long; # Check the arguments passed to pgpverify. # If a syntax error occurs, do not syslog it: such an error is almost # certainly from someone running the script manually. Getopt::Long::Configure('bundling_override'); GetOptions( 'test' => sub { $test = 1 }, 'findid=s' => \$findid ) or die "Usage: $0 [--findid='string'] [--test] < message\n"; # Grab various defaults from INN::Config if running inside INN. $pgp = $INN::Config::pgp if $INN::Config::pgp && $INN::Config::pgp ne "no-pgp-found-during-configure"; $gpgv = $INN::Config::gpgv if $INN::Config::gpgv; $tmp = ($INN::Config::pathtmp ? $INN::Config::pathtmp : $tmpdir) . "/pgp$$"; $lockdir = $INN::Config::locks if $INN::Config::locks; $syslog_facility = $INN::Config::syslog_facility if $INN::Config::syslog_facility; if (! $keyring && $INN::Config::newsetc) { $keyring = $INN::Config::newsetc . '/pgp' if -d $INN::Config::newsetc . '/pgp'; } # Trim /path/to/prog to prog for error messages. $0 =~ s%^.*/%%; # Make sure that the signature verification program can be executed. if ($gpgv) { if (! -x $gpgv) { &fail("$0: $gpgv: " . (-e _ ? "cannot execute" : "no such file") . "\n"); } } elsif (! -x $pgp) { &fail("$0: $pgp: " . (-e _ ? "cannot execute" : "no such file") . "\n"); } # Parse the article headers and generate the PGP message. my ($nntp_format, $header, $dup) = &parse_header(); exit 1 unless $$header{'X-PGP-Sig'}; my ($message, $signature, $version) = &generate_message($nntp_format, $header, $dup); if ($test) { print "-----MESSAGE-----\n$message\n-----END MESSAGE-----\n\n"; print "-----SIGNATURE-----\n$signature\n-----SIGNATURE-----\n\n"; } # The call to pgp needs to be locked because it tries to both read and # write a file named randseed.bin but doesn't do its own locking as it # should, and the consequences of a multiprocess conflict is failure to # verify. my $lock; unless ($gpgv) { $lock = "$lockdir/LOCK.$0"; until (&shlock($lock) > 0) { sleep(2); } } # Verify the message. my ($ok, $signer) = pgp_verify($signature, $version, $message); unless ($gpgv) { unlink ($lock) or &errmsg("$0: unlink $lock: $!\n"); } print "$signer\n" if $signer; unless ($ok == 0) { &errmsg("$0: verification failed\n"); } exit $ok; # Parse the article headers and return a flag saying whether the message # is in NNTP format and then two references to hashes. The first hash # contains all the header/value pairs, and the second contains entries for # every header that's duplicated. This is, by design, case-sensitive with # regards to the headers it checks. It's also insistent about the # colon-space rule. sub parse_header { my (%header, %dup, $label, $value, $nntp_format); while (<>) { # If the first header line ends with \r\n, this article is in the # encoding it would be in during an NNTP session. Some article # storage managers keep them this way for efficiency. $nntp_format = /\r\n$/ if $. == 1; s/\r?\n$//; last if /^$/; if (/^(\S+):[ \t](.+)/) { ($label, $value) = ($1, $2); $dup{$label} = 1 if $header{$label}; $header{$label} = $value; } elsif (/^\s/) { &fail("$0: non-header at line $.: $_\n") unless $label; $header{$label} .= "\n$_"; } else { &fail("$0: non-header at line $.: $_\n"); } } $messageid = $header{'Message-ID'}; return ($nntp_format, \%header, \%dup); } # Generate the PGP message to verify. Takes a flag indicating wire # format, the hash of headers and header duplicates returned by # parse_header and returns a list of three elements. The first is the # message to verify, the second is the signature, and the third is the # version number. sub generate_message { my ($nntp_format, $header, $dup) = @_; # The regexp below might be too strict about the structure of PGP # signature lines. # The $sep value means the separator between the radix64 signature lines # can have any amount of spaces or tabs, but must have at least one # space or tab; if there is a newline then the space or tab has to # follow the newline. Any number of newlines can appear as long as each # is followed by at least one space or tab. *phew* my $sep = "[ \t]*(\n?[ \t]+)+"; # Match all of the characters in a radix64 string. my $r64 = '[a-zA-Z0-9+/]'; local $_ = $$header{'X-PGP-Sig'}; &fail("$0: X-PGP-Sig not in expected format\n") unless /^(\S+)$sep(\S+)(($sep$r64{64})+$sep$r64+=?=?$sep=$r64{4})$/; my ($version, $signed_headers, $signature) = ($1, $3, $4); $signature =~ s/$sep/\n/g; $signature =~ s/^\s+//; my $message = "X-Signed-Headers: $signed_headers\n"; my $label; foreach $label (split(",", $signed_headers)) { &fail("$0: duplicate signed $label header, can't verify\n") if $$dup{$label}; $message .= "$label: "; $message .= "$$header{$label}" if $$header{$label}; $message .= "\n"; } $message .= "\n"; # end of headers while (<>) { # read body lines if ($nntp_format) { # Check for end of article; some news servers (eg, Highwind's # "Breeze") include the dot-CRLF of the NNTP protocol in the article # data passed to this script. last if $_ eq ".\r\n"; # Remove NNTP encoding. s/^\.\./\./; s/\r\n$/\n/; } $message .= $_; } # Strip off all trailing whitespaces for compatibility with the way that # pgpverify used to work, using attached signatures. $message =~ s/[ \t]+\n/\n/g; return ($message, $signature, $version); } # Check a detached signature for given data. Takes a signature block (in # the form of an ASCII-armored string with embedded newlines), a version # number (which may be undef), and the message. We return an exit status # and the key id if the signature is verified. 0 means good signature, 1 # means bad data, 2 means an unknown signer, 3 means a bad signature, and # 4 means good signature without having found the argument given to the # --findid flag. # In the event of an error, we report with errmsg. # # This code is taken almost verbatim from PGP::Sign except for the code to # figure out the PGP style. sub pgp_verify { my ($signature, $version, $message) = @_; chomp $signature; # Ignore SIGPIPE, since we're going to be talking to PGP. local $SIG{PIPE} = 'IGNORE'; # Set the PGP style based on whether $gpgv is set. my $pgpstyle = ($gpgv ? 'GPG' : 'PGP2'); # Because this is a detached signature, we actually need to save both # the signature and the data to files and then run PGP on the signature # file to make it verify the signature. Because this is a detached # signature, though, we don't have to do any data mangling, which makes # our lives much easier. It would be nice to do this without having to # use temporary files, but I don't see any way to do so without running # into mangling problems. # # PGP v5 *requires* there be some subheader or another. *sigh*. So we # supply one if Version isn't given. :) my $umask = umask 077; my $filename = $tmpdir . '/pgp' . time . '.' . $$; my $sigfile = new FileHandle "$filename.asc", O_WRONLY|O_EXCL|O_CREAT; unless ($sigfile) { &errmsg ("Unable to open temp file $filename.asc: $!\n"); return (255, undef); } if ($pgpstyle eq 'PGP2') { print $sigfile "-----BEGIN PGP MESSAGE-----\n"; } else { print $sigfile "-----BEGIN PGP SIGNATURE-----\n"; } if (defined $version) { print $sigfile "Version: $version\n"; } elsif ($pgpstyle ne 'GPG') { print $sigfile "Comment: Use GnuPG; it's better :)\n"; } print $sigfile "\n", $signature; if ($pgpstyle eq 'PGP2') { print $sigfile "\n-----END PGP MESSAGE-----\n"; } else { print $sigfile "\n-----END PGP SIGNATURE-----\n"; } close $sigfile; # Signature saved. Now save the actual message. my $datafile = new FileHandle "$filename", O_WRONLY|O_EXCL|O_CREAT; unless ($datafile) { &errmsg ("Unable to open temp file $filename: $!\n"); unlink "$filename.asc"; return (255, undef); } print $datafile $message; close $datafile; # Figure out what command line we'll be using. my @command; if ($pgpstyle eq 'GPG') { @command = ($gpgv, qw/--quiet --status-fd=1 --logger-fd=1/); } else { @command = ($pgp, '+batchmode', '+language=en'); } # Now, call PGP to check the signature. Because we've written # everything out to a file, this is actually fairly simple; all we need # to do is grab stdout. PGP prints its banner information to stderr, so # just ignore stderr. Set PGPPATH if desired. # # For GnuPG, use pubring.gpg if an explicit keyring was configured or # found. Otherwise, use trustedkeys.gpg in the default keyring location # if found and non-zero, or fall back on pubring.gpg. This is # definitely not the logic that I would use if writing this from # scratch, but it has the most backward compatibility. local $ENV{PGPPATH} = $keyring if ($keyring && $pgpstyle ne 'GPG'); if ($pgpstyle eq 'GPG') { if ($keyring) { push (@command, "--keyring=$keyring/pubring.gpg"); } else { my $home = $ENV{GNUPGHOME} || $ENV{HOME}; $home .= '/.gnupg' if $home; if ($home && ! -s "$home/trustedkeys.gpg" && -f "$home/pubring.gpg") { push (@command, "--keyring=pubring.gpg"); } } } push (@command, "$filename.asc"); push (@command, $filename); my $input = new FileHandle; my $output = new FileHandle; my $pid = eval { open3 ($input, $output, $output, @command) }; if ($@) { &errmsg ($@); &errmsg ("Execution of $command[0] failed.\n"); unlink ($filename, "$filename.asc"); return (255, undef); } close $input; # Check for the message that gives us the key status and return the # appropriate thing to our caller. This part is a zoo due to all of the # different formats used. GPG has finally done the right thing and # implemented a separate status stream with parseable data. # # MIT PGP 2.6.2 and PGP 6.5.2: # Good signature from user "Russ Allbery ". # ViaCrypt PGP 4.0: # Good signature from user: Russ Allbery # PGP 5.0: # Good signature made 1999-02-10 03:29 GMT by key: # 1024 bits, Key ID 0AFC7476, Created 1999-02-10 # "Russ Allbery " # # Also, PGP v2 prints out "Bad signature" while PGP v5 uses "BAD # signature", and PGP v6 reverts back to "Bad signature". local $_; local $/ = ''; my $signer; my $ok = 255; while (<$output>) { print if $test; if ($pgpstyle eq 'GPG') { if (/\[GNUPG:\]\s+GOODSIG\s+\S+\s+(\S+)/) { $ok = 0; $signer = $1; } elsif (/\[GNUPG:\]\s+NODATA/ || /\[GNUPG:\]\s+UNEXPECTED/) { $ok = 1; last; } elsif (/\[GNUPG:\]\s+NO_PUBKEY/) { $ok = 2; last; } elsif (/\[GNUPG:\]\s+BADSIG\s+/) { $ok = 3; last; } } else { if (/^Good signature from user(?::\s+(.*)|\s+\"(.*)\"\.)$/m) { $signer = $+; $ok = 0; } elsif (/^Good signature made .* by key:\n.+\n\s+\"(.*)\"/m) { $signer = $1; $ok = 0; } elsif (/^\S+: Good signature from \"(.*)\"/m) { $signer = $1; $ok = 0; } elsif (/^(?:\S+: )?Bad signature /im) { $ok = 3; last; } } # If the --findid flag is used, and the signature is good, # override the value of the signer with the string specified in # the --findid flag. if (defined ($findid) and $ok eq 0) { $signer = $findid if (/$findid/); } } close $input; waitpid ($pid, 0); unlink ($filename, "$filename.asc"); umask $umask; if (defined ($findid) and $ok eq 0 and $signer ne $findid) { $ok = 4; } return ($ok, $signer || ''); } # Log an error message, attempting syslog first based on $syslog_method # and falling back on stderr. sub errmsg { my ($message) = @_; $message =~ s/\n$//; my $date = ''; if ($log_date) { $date = strftime ('%Y-%m-%d %T ', localtime); } if ($syslog_method && $] >= 5.006) { eval "use Sys::Syslog"; $syslog_method = 'internal'; } if ($syslog_method eq "logger") { my @loggers = ('/usr/ucb/logger', '/usr/bin/logger', '/usr/local/bin/logger'); my $try; foreach $try (@loggers) { if (-x $try) { $syslog_method = $try; last; } } $syslog_method = '' if $syslog_method eq 'logger'; } if ($syslog_method ne '' && $syslog_method !~ m%/logger$%) { eval "use Sys::Syslog"; } if ($@ || $syslog_method eq '') { warn $date, "$0: trying to use Perl's syslog: $@\n" if $@; warn $date, $message, "\n"; warn $date, "... while processing $messageid\n" if $messageid; } else { $message .= " processing $messageid" if $messageid; if ($syslog_method =~ m%/logger$%) { unless (system($syslog_method, "-i", "-p", "$syslog_facility.$syslog_level", $message) == 0) { if ($? >> 8) { warn $date, "$0: $syslog_method exited status ", $? >> 8, "\n"; } else { warn $date, "$0: $syslog_method died on signal ", $? & 255, "\n"; } $syslog_method = ''; &errmsg($message); } } else { # setlogsock arrived in Perl 5.004_03 to enable Sys::Syslog to use a # Unix domain socket to talk to syslogd, which is the only way to do # it when syslog runs with the -l switch. if ($syslog_method eq "unix") { if ($^O eq "dec_osf" && $] >= 5) { eval 'sub Sys::Syslog::_PATH_LOG { "/dev/log" }'; } if ($] <= 5.00403 || ! eval "setlogsock('unix')") { warn $date, "$0: cannot use syslog_method 'unix' on this system\n"; $syslog_method = ''; &errmsg($message); return; } } # Unfortunately, there is no way to definitively know in this # program if the message was logged. I wish there were a way to # send a message to stderr if and only if the syslog attempt failed. &openlog($0, 'pid', $syslog_facility); &syslog($syslog_level, $_[0]); &closelog(); } } } sub fail { &errmsg($_[0]); exit 255; } # Get a lock in essentially the same fashion as INN's shlock. return 1 on # success, 0 for normal failure, -1 for abnormal failure. "normal # failure" is that a lock is apparently in use by someone else. sub shlock { my ($file) = @_; my ($ltmp, $pid); unless (defined(&ENOENT)) { eval "require POSIX qw(:errno_h)"; if ($@) { # values taken from BSD/OS 3.1 sub ENOENT { 2 } sub ESRCH { 3 } sub EEXIST { 17 } } } $ltmp = ($file =~ m%(.*/)%)[0] . "shlock$$"; # This should really attempt to use another temp name. -e $ltmp && (unlink($ltmp) || return -1); open(LTMP, ">$ltmp") || return -1; print LTMP "$$\n" || (unlink($ltmp), return -1); close(LTMP) || (unlink($ltmp), return -1); if (!link($ltmp, $file)) { if ($! == &EEXIST) { if (open(LOCK, "<$file")) { $pid = ; if ($pid =~ /^\d+$/ && (kill(0, $pid) == 1 || $! != &ESRCH)) { unlink($ltmp); return 0; } # OK, the pid in the lockfile is not a number or no longer exists. close(LOCK); # silent failure is ok here # Unlink failed. if (unlink($file) != 1 && $! != &ENOENT) { unlink($ltmp); return 0; } # Check if open failed for reason other than file no longer present. } elsif ($! != &ENOENT) { unlink($ltmp); return -1; } # Either this process unlinked the lockfile because it was bogus, or # between this process's link() and open() the other process holding # the lock unlinked it. This process can now try to acquire. if (! link($ltmp, $file)) { unlink($ltmp); return $! == &EEXIST ? 0 : -1; # Maybe another proc grabbed the lock. } } else { # First attempt to link failed. unlink($ltmp); return 0; } } unlink($ltmp); return 1; } =head1 NAME pgpverify - Cryptographically verify Usenet control messages =head1 SYNOPSIS B [B<--findid>=I] [B<--test>] < I =head1 DESCRIPTION The B program reads (on standard input) a Usenet control message that has been cryptographically signed using the B program (or some other program that produces a compatible format). B then uses a PGP implementation to determine who signed the control message. If the control message has a valid signature, B prints (to stdout) the user ID of the key that signed the message. Otherwise, it exits with a non-zero exit status. If B is installed as part of INN, it uses INN's configuration to determine what signature verification program to use, how to log errors, what temporary directory to use, and what keyring to use. Otherwise, all of those parameters can be set by editing the beginning of this script. By default, when running as part of INN, B expects the PGP key ring to be found in I/pgp (as either F or F depending on whether PGP or GnuPG is used to verify signatures). If that directory doesn't exist, it will fall back on using the default key ring, which is in a F<.pgp> or F<.gnupg> subdirectory of the running user's home directory. INN, when using GnuPG, configures B to use B, which by default expects keys to be in a keyring named F, since it doesn't implement trust checking directly. B uses that file if present but falls back to F if it's not found. This bypasses the trust model for checking keys, but is compatible with the way that B used to behave. Of course, if a keyring is found in I/pgp or configured at the top of the script, that overrides all of this behavior. =head1 OPTIONS =over 4 =item B<--findid>=I The B<--findid> flag causes B to explicitly search for I in the output from PGP's analysis of the message. This option is useful when several UIDs are defined on a single PGP key, and the caller to B needs checking whether a given one is defined on this key. In case the signature is valid but does not contain I, B exits with exit status 4. =item B<--test> The B<--test> flag causes B to print out the input that it is passing to PGP (which is a reconstructed version of the input that supposedly created the control message) as well as the output from PGP's analysis of the message. =back =head1 EXIT STATUS B may exit with the following statuses: =over 4 =item 0Z<> The control message had a good PGP signature. =item 1Z<> The control message had no PGP signature. =item 2Z<> The control message had an unknown PGP signature. =item 3Z<> The control message had a bad PGP signature. =item 4Z<> The control message had a good PGP signature but the argument given to the B<--findid> flag had non been found in the output from PGP's analysis of the message. =item 255Z<> A problem occurred not directly related to PGP analysis of signature. =back =head1 ENVIRONMENT B does not modify or otherwise alter the environment before invoking the B or B program. It is the responsibility of the person who installs B to ensure that when B or B runs, it has the ability to locate and read a PGP key file that contains the PGP public keys for the appropriate Usenet hierarchy administrators. B can be pointed to an appropriate key ring by editing variables at the beginning of this script. =head1 NOTES Historically, Usenet news server administrators have configured their news servers to automatically honor Usenet control messages based on the originator of the control messages and the hierarchies for which the control messages applied. For example, in the past, David Lawrence always issued control messages for the S<"Big 8"> hierarchies (comp, humanities, misc, news, rec, sci, soc, talk). Usenet news administrators would configure their news server software to automatically honor newgroup and rmgroup control messages that originated from David Lawrence and applied to any of the S hierarchies. Unfortunately, Usenet news articles (including control messages) are notoriously easy to forge. Soon, malicious users realized they could create or remove (at least temporarily) any S newsgroup they wanted by simply forging an appropriate control message in David Lawrence's name. As Usenet became more widely used, forgeries became more common. The B program was designed to allow Usenet news administrators to configure their servers to cryptographically verify control messages before automatically acting on them. Under the B system, a Usenet hierarchy maintainer creates a PGP public/private key pair and disseminates the public key. Whenever the hierarchy maintainer issues a control message, he uses the B program to sign the control message with the PGP private key. Usenet news administrators configure their news servers to run the B program on the appropriate control messages, and take action based on the PGP key User ID that signed the control message, not the name and address that appear in the control message's From: or Sender: headers. Thus, appropriate use of the B and B programs essentially eliminates the possibility of malicious users forging Usenet control messages that sites will act upon, as such users would have to obtain the PGP private key in order to forge a control message that would pass the cryptographic verification step. If the hierarchy administrators properly protect their PGP private keys, the only way a malicious user could forge a validly-signed control message would be by breaking the public key encryption algorithm, which (at least at this time) is believed to be prohibitively difficult for PGP keys of a sufficient bit length. =head1 HISTORY B was written by David C Lawrence . Manual page provided by James Ralston. It is currently maintained by Russ Allbery . =head1 COPYRIGHT AND LICENSE David Lawrence wrote: "Our lawyer told me to include the following. The upshot of it is that you can use the software for free as much as you like." Copyright (c) 1996 UUNET Technologies, Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: =over 4 =item 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. =item 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. =item 3. All advertising materials mentioning features or use of this software must display the following acknowledgement: This product includes software developed by UUNET Technologies, Inc. =item 4. The name of UUNET Technologies ("UUNET") may not be used to endorse or promote products derived from this software without specific prior written permission. =back THIS SOFTWARE IS PROVIDED BY UUNET "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL UUNET BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. =head1 SEE ALSO gpgv(1), pgp(1). L is where the most recent versions of B and B live, along with PGP public keys used for hierarchy administration. =cut # Local variables: # cperl-indent-level: 2 # fill-column: 74 # End: inn-2.6.0/control/modules/0000755000175200017520000000000012575023677015063 5ustar iuliusiuliusinn-2.6.0/control/modules/sendme.pl0000644000175200017520000000371012575023702016661 0ustar iuliusiulius## $Id: sendme.pl 8281 2009-01-10 11:52:16Z iulius $ ## ## sendme control message handler. ## ## Copyright 2001 by Marco d'Itri ## ## Redistribution and use in source and binary forms, with or without ## modification, are permitted provided that the following conditions ## are met: ## ## 1. Redistributions of source code must retain the above copyright ## notice, this list of conditions and the following disclaimer. ## ## 2. Redistributions in binary form must reproduce the above copyright ## notice, this list of conditions and the following disclaimer in the ## documentation and/or other materials provided with the distribution. use strict; sub control_sendme { my ($par, $sender, $replyto, $site, $action, $log, $approved, $article) = @_; my @body = split(/\r?\n/, $article->stringify_body); if ($action eq 'mail') { my $mail = sendmail("sendme by $sender"); print $mail map { s/^~/~~/; "$_\n" } @body; close $mail or logdie('Cannot send mail: ' . $!); } elsif ($action eq 'log') { if ($log) { logger($log, "sendme $sender", $article); } else { logmsg("sendme from $sender"); } } elsif ($action eq 'doit') { my $tempfile = "$INN::Config::tmpdir/sendme.$$"; open(GREPHIST, "| $INN::Config::newsbin/grephistory -s > $tempfile") or logdie("Cannot run grephistory: $!"); foreach (@body) { print GREPHIST "$_\n"; } close GREPHIST or logdie("Cannot run grephistory: $!"); if (-s $tempfile and $site =~ /^[a-zA-Z0-9.-_]+$/) { open(TEMPFILE, $tempfile) or logdie("Cannot open $tempfile: $!"); open(BATCH, ">>$INN::Config::batch/$site.work") or logdie("Cannot open $INN::Config::batch/$site.work: $!"); print BATCH $_ while ; close BATCH; close TEMPFILE; } unlink $tempfile; } } 1; inn-2.6.0/control/modules/senduuname.pl0000644000175200017520000000432512575023702017555 0ustar iuliusiulius## $Id: senduuname.pl 8281 2009-01-10 11:52:16Z iulius $ ## ## senduuname control message handler. ## ## Copyright 2001 by Marco d'Itri ## ## Redistribution and use in source and binary forms, with or without ## modification, are permitted provided that the following conditions ## are met: ## ## 1. Redistributions of source code must retain the above copyright ## notice, this list of conditions and the following disclaimer. ## ## 2. Redistributions in binary form must reproduce the above copyright ## notice, this list of conditions and the following disclaimer in the ## documentation and/or other materials provided with the distribution. use strict; sub control_senduuname { my ($par, $sender, $replyto, $site, $action, $log, $approved, $article) = @_; my ($where) = @$par; my $head = $article->head; my @headers = split(/\r?\n/, $head->stringify); my @body = split(/\r?\n/, $article->stringify_body); if ($action eq 'mail') { my $mail = sendmail("senduuname $sender"); print $mail <; close UUNAME or logdie("Cannot run uuname: $!"); close $mail or logdie("Cannot send mail: $!"); logger($log, "senduuname $sender to $replyto", $article) if $log; } } 1; inn-2.6.0/control/modules/newgroup.pl0000644000175200017520000002453512575023702017264 0ustar iuliusiulius## $Id: newgroup.pl 9303 2011-08-04 22:09:57Z iulius $ ## ## newgroup control message handler. ## ## Copyright 2001 by Marco d'Itri ## ## Redistribution and use in source and binary forms, with or without ## modification, are permitted provided that the following conditions ## are met: ## ## 1. Redistributions of source code must retain the above copyright ## notice, this list of conditions and the following disclaimer. ## ## 2. Redistributions in binary form must reproduce the above copyright ## notice, this list of conditions and the following disclaimer in the ## documentation and/or other materials provided with the distribution. use strict; sub control_newgroup { my ($par, $sender, $replyto, $site, $action, $log, $approved, $article, $charset_from, $charset_to) = @_; my ($groupname, $modflag) = @$par; my $head = $article->head; my @headers = split(/\r?\n/, $head->stringify); my @fullbody = split(/\r?\n/, $article->stringify_body); my (@body, $part, $part_head); my $mimegroupinfo = 0; my $charset_message; if (defined $head->mime_attr('Content-Type.charset')) { $charset_message = $head->mime_attr('Content-Type.charset'); } # Check if it is a multipart message. The body is restricted to # the application/news-groupinfo part, if any. if ($article->parts > 0) { foreach $part ($article->parts) { $part_head = $part->head; if ($part_head->mime_type eq 'application/news-groupinfo') { @body = split(/\r?\n/, $part->stringify_body); if (defined $part_head->mime_attr('Content-Type.charset')) { $charset_message = $part_head->mime_attr('Content-Type.charset'); } $mimegroupinfo = 1; } } } # The newgroup control message can be an application/news-groupinfo # entity itself. if ($head->mime_type eq 'application/news-groupinfo') { @body = @fullbody; $mimegroupinfo = 1; } @body = @fullbody if not $mimegroupinfo; # Find the right charset if absent or forced by control.ctl. foreach (@$charset_from) { my ($group, $charset) = split /:/; if ($groupname =~ /$group/) { if (not defined $charset_message or $charset =~ /=force/) { $charset_message = $charset; $charset_message =~ s/\^(.+)\$/$1/; $charset_message =~ s/\\//g; $charset_message =~ s/=force//; } last; } } if (not defined $charset_message or not defined Encode::find_encoding($charset_message)) { $charset_message = "cp1252"; # Default charset, when undefined. } $modflag ||= ''; my $modcmd = $modflag eq 'moderated' ? 'm' : 'y'; # Scan active to see what sort of change we are making. open(ACTIVE, $INN::Config::active) or logdie("Cannot open $INN::Config::active: $!"); my @oldgroup; while () { next unless /^(\Q$groupname\E)\s\d+\s\d+\s(\w)/; @oldgroup = split /\s+/; last; } close ACTIVE; my $status; my $ngdesc = ''; my $olddesc = ''; my $ngname = $groupname; # If there is a tag line, search whether the description has changed. my $found = 0; my $ngline = ''; foreach (@body) { $found = 1 if (($mimegroupinfo) and ($_ !~ /^For your newsgroups file:\s*$/)); if ($found) { # It is the line which contains the description. $ngline = $_; last; } $found = 1 if $_ =~ /^For your newsgroups file:\s*$/; } if ($found) { ($ngname, $ngdesc) = split(/\s+/, $ngline, 2); # Scan newsgroups to see the previous description, if any. open(NEWSGROUPS, $INN::Config::newsgroups) or logdie("Cannot open $INN::Config::newsgroups: $!"); while () { if (/^\Q$groupname\E\s+(.*)/) { $olddesc = $1; last; } } close NEWSGROUPS; } # Properly encode the newsgroup description. Encode::from_to($ngdesc, $charset_message, $charset_to); if (@oldgroup) { if ($oldgroup[3] eq 'm' and $modflag ne 'moderated') { $status = 'be made unmoderated'; } elsif ($oldgroup[3] ne 'm' and $modflag eq 'moderated') { $status = 'be made moderated'; } else { if ($ngdesc eq $olddesc) { $status = 'not change'; } else { $status = 'have a new description'; } } } elsif (not $approved) { $status = 'be unapproved'; } else { $status = 'be created'; } # Check whether the group name and the description are not junk. my $errmsg; if (defined &local_checkgroupname) { $errmsg = local_checkgroupname($groupname); } else { $errmsg = checkgroupname($groupname); } if (! $errmsg) { if (defined &local_checkdescription) { $errmsg = local_checkdescription($ngdesc, $modcmd); } else { $errmsg = checkdescription($ngdesc, $modcmd); } } if ($errmsg) { if ($log) { logger($log, "skipping newgroup $groupname $modcmd" . " $sender (would $status): $errmsg", $article); } else { logmsg("skipping newgroup $groupname $modcmd $sender" . " (would $status): $errmsg"); } return; } if ($action eq 'mail' and $status !~ /(not change|be unapproved)/) { my $mail = sendmail("newgroup $groupname $modcmd $sender"); print $mail <$tempfile") or logdie("Cannot open $tempfile: $!"); while () { next if (/^\Q$name\E\s+(.*)/); print TEMPFILE $_; } # We now write a pretty line for the description. if (length $name < 8) { print TEMPFILE "$name\t\t\t$desc\n"; } elsif (length $name < 16) { print TEMPFILE "$name\t\t$desc\n"; } else { print TEMPFILE "$name\t$desc\n"; } close TEMPFILE; close NEWSGROUPS; rename($tempfile, $INN::Config::newsgroups) or logdie("Cannot rename $tempfile: $!"); unlink($tempfile); # Unlock. INN::Utils::Shlock::unlock($lockfile); } # Check the group name. This is partially derived from C News. # Some checks are commented out if I think they're too strict or # language-dependent. Your mileage may vary. sub checkgroupname { local $_ = shift; # Whole-name checking. return 'Empty group name' if (! $_); return 'Whitespace in group name' if /\s/; # return 'Unsafe group name' if /[\`\/:;]/; return 'Bad dots in group name' if /^\./ or /\.$/ or /\.\./; # return 'Group name does not begin/end with alphanumeric' # if (/^[a-zA-Z0-9].+[a-zA-Z0-9]$/; return 'Group name begins in control., example. or to.' if /^(?:control|example|to)\./; return 'Group name is control, example, junk, poster or to' if /^(?:control|example|junk|poster|to)$/; # return 'Group name too long' if length $_ > 128; my @components = split(/\./); # Prevent alt.a.b.c.d.e.f.g.w.x.y.z... return 'Too many components' if $#components > 9; # Per-component checking. for (my $i = 0; $i <= $#components; $i++) { local $_ = $components[$i]; return 'All-numeric name component' if /^[0-9]+$/; # return 'Name component starts with non-alphanumeric' if /^[a-zA-Z0-9]/; # return 'Name component does not contain letter' if not /[a-zA-Z]/; return "`all' or `ctl' used as name component" if /^(?:all|ctl)$/; # return 'Name component longer than 30 characters' if length $_ > 30; return 'Uppercase letter(s) in name' if /[A-Z]/; # return 'Illegal character(s) in name' if /[^a-z0-9+_\-]/; # Sigh, c++ etc. must be allowed. # return 'Repeated punctuation in name' if /--|__|\+\+./; # return 'Repeated component(s) in name' if ($i + 2 <= $#components # and $_ eq $components[$i + 1] and $_ eq $components[$i + 2]); } return ''; } # Check the description. sub checkdescription { my ($desc, $flag) = @_; # Whole-name checking. return 'Empty description' if (! $desc); return 'Moderation status mismatch' if ($desc =~ / \(Moderated\)$/) and $flag eq 'y'; return 'Moderation status mismatch' if ($desc !~ / \(Moderated\)$/) and $flag eq 'm'; return 'Reserved "(Moderated)" substring used' if ($desc =~ /\(Moderated\).+$/); return ''; } 1; inn-2.6.0/control/modules/checkgroups.pl0000644000175200017520000001607212575023702017730 0ustar iuliusiulius## $Id: checkgroups.pl 8550 2009-07-18 13:07:05Z iulius $ ## ## checkgroups control message handler. ## ## Copyright 2001 by Marco d'Itri ## ## Redistribution and use in source and binary forms, with or without ## modification, are permitted provided that the following conditions ## are met: ## ## 1. Redistributions of source code must retain the above copyright ## notice, this list of conditions and the following disclaimer. ## ## 2. Redistributions in binary form must reproduce the above copyright ## notice, this list of conditions and the following disclaimer in the ## documentation and/or other materials provided with the distribution. use strict; sub control_checkgroups { my ($par, $sender, $replyto, $site, $action, $log, $approved, $article, $charset_from, $charset_to, $exclusionpats, $droppats, $maxchanges) = @_; my ($newsgrouppats) = @$par; my $head = $article->head; my @headers = split(/\r?\n/, $head->stringify); my @body = split(/\r?\n/, $article->stringify_body); my @newbody; my $charset_message; if (defined $head->mime_attr('Content-Type.charset')) { $charset_message = $head->mime_attr('Content-Type.charset'); } foreach (@body) { my ($ngname, $ngdesc) = split(/\s+/, $_, 2); my $charset_newsgroup = $charset_message; next if ($ngname !~ /$newsgrouppats/); next if ($exclusionpats and $ngname =~ /$exclusionpats/); next if ($droppats and $ngname =~ /$droppats/); # Find the right charset if absent or forced by control.ctl. foreach (@$charset_from) { my ($group, $charset) = split /:/; if ($ngname =~ /$group/) { if (not defined $charset_newsgroup or $charset =~ /=force/) { $charset_newsgroup = $charset; $charset_newsgroup =~ s/\^(.+)\$/$1/; $charset_newsgroup =~ s/\\//g; $charset_newsgroup =~ s/=force//; } last; } } if (not defined $charset_newsgroup or not defined Encode::find_encoding($charset_newsgroup)) { $charset_newsgroup = "cp1252"; # Default charset, when undefined. } # Properly encode the newsgroup description. Encode::from_to($ngdesc, $charset_newsgroup, $charset_to); push(@newbody, $ngname."\t".$ngdesc); } # We do not go on if there is no changes to do. return if ($#newbody < 0); if ($action eq 'mail') { my $mail = sendmail("checkgroups by $sender"); print $mail "$sender posted the following checkgroups message:\n\n"; print $mail map { s/^~/~~/; "$_\n" } @headers; print $mail <$tempfile.art") or logdie("Cannot open $tempfile.art: $!"); print TEMPART map { s/^~/~~/; "$_\n" } @$body; close TEMPART; open(OLDIN, '<&STDIN') or die $!; open(OLDOUT, '>&STDOUT') or die $!; open(STDIN, "$tempfile.art") or die $!; open(STDOUT, ">$tempfile") or die $!; my $st = system("$INN::Config::pathbin/docheckgroups", "-u", $newsgrouppats, $exclusionpats); logdie('Cannot run docheckgroups: ' . $!) if $st == -1; logdie('docheckgroups returned status ' . ($st & 255)) if $st > 0; close(STDIN); close(STDOUT); open(STDIN, '<&OLDIN') or die $!; open(STDOUT, '>&OLDOUT') or die $!; open(TEMPFILE, $tempfile) or logdie("Cannot open $tempfile: $!"); my @output = ; chop @output; # There is no need to send an empty mail. if ($#output > 0) { my $dochanges = 1; my @newmaxchanges = @$maxchanges; foreach my $line (@output) { last if !$dochanges; if ($line =~ /^\s*\S*ctlinnd \S+ (\S+)/) { my $ngname = $1; foreach my $i (0..$#newmaxchanges) { my ($group, $value) = split (/:/, $newmaxchanges[$i]); if ($ngname =~ /$group/) { $value--; if ($value < 0) { $dochanges = 0; } $newmaxchanges[$i] = "$group:$value"; last; } } } } if ($dochanges) { open(OLDIN, '<&STDIN') or die $!; open(OLDOUT, '>&STDOUT') or die $!; open(STDIN, "$tempfile") or die $!; open(STDOUT, ">$tempfile.modact") or die $!; my $st = system("$INN::Config::pathbin/mod-active"); logdie('Cannot run mod-active: ' . $!) if $st == -1; logdie('mod-active returned status ' . ($st & 255)) if $st > 0; close(STDIN); close(STDOUT); open(STDIN, '<&OLDIN') or die $!; open(STDOUT, '>&OLDOUT') or die $!; if ($log) { unshift(@output, ''); unshift(@output, '######################################################################'); unshift(@output, '# This script has already been successfully executed by controlchan. #'); unshift(@output, '######################################################################'); logger($log, "checkgroups by $sender processed (changes applied)", \@output); } } else { unshift(@output, ''); unshift(@output, '################################################'); unshift(@output, '# This script was NOT executed by controlchan. #'); unshift(@output, '################################################'); logger($log || 'mail', "checkgroups by $sender *not* processed (too many changes)", \@output); } } else { logmsg("checkgroups by $sender processed (no change)"); } close TEMPFILE; unlink($tempfile, "$tempfile.art", "$tempfile.modact"); } 1; inn-2.6.0/control/modules/version.pl0000644000175200017520000000413712575023702017077 0ustar iuliusiulius## $Id: version.pl 8281 2009-01-10 11:52:16Z iulius $ ## ## version control message handler. ## ## Copyright 2001 by Marco d'Itri ## ## Redistribution and use in source and binary forms, with or without ## modification, are permitted provided that the following conditions ## are met: ## ## 1. Redistributions of source code must retain the above copyright ## notice, this list of conditions and the following disclaimer. ## ## 2. Redistributions in binary form must reproduce the above copyright ## notice, this list of conditions and the following disclaimer in the ## documentation and/or other materials provided with the distribution. use strict; sub control_version { my ($par, $sender, $replyto, $site, $action, $log, $approved, $article) = @_; my ($where) = @$par; my $head = $article->head; my @headers = split(/\r?\n/, $head->stringify); my @body = split(/\r?\n/, $article->stringify_body); my $version = $INN::Config::version || '(unknown version)'; if ($action eq 'mail') { my $mail = sendmail("version $sender"); print $mail < ## ## Redistribution and use in source and binary forms, with or without ## modification, are permitted provided that the following conditions ## are met: ## ## 1. Redistributions of source code must retain the above copyright ## notice, this list of conditions and the following disclaimer. ## ## 2. Redistributions in binary form must reproduce the above copyright ## notice, this list of conditions and the following disclaimer in the ## documentation and/or other materials provided with the distribution. use strict; sub control_sendsys { my ($par, $sender, $replyto, $site, $action, $log, $approved, $article) = @_; my ($where) = @$par; my $head = $article->head; my @headers = split(/\r?\n/, $head->stringify); my @body = split(/\r?\n/, $article->stringify_body); if ($action eq 'mail') { my $mail = sendmail("sendsys $sender"); print $mail <; print $mail "\n"; close NEWSFEEDS; close $mail or logdie("Cannot send mail: $!"); logger($log, "sendsys $sender to $replyto", $article) if $log; } } 1; inn-2.6.0/control/modules/ihave.pl0000644000175200017520000000403012575023702016476 0ustar iuliusiulius## $Id: ihave.pl 8285 2009-01-11 12:06:55Z iulius $ ## ## ihave control message handler. ## ## Copyright 2001 by Marco d'Itri ## ## Redistribution and use in source and binary forms, with or without ## modification, are permitted provided that the following conditions ## are met: ## ## 1. Redistributions of source code must retain the above copyright ## notice, this list of conditions and the following disclaimer. ## ## 2. Redistributions in binary form must reproduce the above copyright ## notice, this list of conditions and the following disclaimer in the ## documentation and/or other materials provided with the distribution. use strict; sub control_ihave { my ($par, $sender, $replyto, $site, $action, $log, $approved, $article) = @_; my @body = split(/\r?\n/, $article->stringify_body); if ($action eq 'mail') { my $mail = sendmail("ihave by $sender"); print $mail map { s/^~/~~/; "$_\n" } @body; close $mail or logdie('Cannot send mail: ' . $!); } elsif ($action eq 'log') { if ($log) { logger($log, "ihave $sender", $article); } else { logmsg("ihave $sender"); } } elsif ($action eq 'doit') { my $tempfile = "$INN::Config::tmpdir/ihave.$$"; open(GREPHIST, "| $INN::Config::newsbin/grephistory -i > $tempfile") or logdie('Cannot run grephistory: ' . $!); foreach (@body) { print GREPHIST "$_\n"; } close GREPHIST; if (-s $tempfile) { open(INEWS, "| $INN::Config::inews -h") or logdie('Cannot run inews: ' . $!); print INEWS "Newsgroups: to.$site\n" . "Subject: cmsg sendme $INN::Config::pathhost\n" . "Control: sendme $INN::Config::pathhost\n\n"; open(TEMPFILE, $tempfile) or logdie("Cannot open $tempfile: $!"); print INEWS $_ while ; close INEWS or die $!; close TEMPFILE; } unlink $tempfile; } } 1; inn-2.6.0/control/modules/rmgroup.pl0000644000175200017520000000651612575023702017110 0ustar iuliusiulius## $Id: rmgroup.pl 9303 2011-08-04 22:09:57Z iulius $ ## ## rmgroup control message handler. ## ## Copyright 2001 by Marco d'Itri ## ## Redistribution and use in source and binary forms, with or without ## modification, are permitted provided that the following conditions ## are met: ## ## 1. Redistributions of source code must retain the above copyright ## notice, this list of conditions and the following disclaimer. ## ## 2. Redistributions in binary form must reproduce the above copyright ## notice, this list of conditions and the following disclaimer in the ## documentation and/or other materials provided with the distribution. use strict; sub control_rmgroup { my ($par, $sender, $replyto, $site, $action, $log, $approved, $article) = @_; my ($groupname) = @$par; my $head = $article->head; my @headers = split(/\r?\n/, $head->stringify); my @body = split(/\r?\n/, $article->stringify_body); # Scan active to see what sort of change we are making. open(ACTIVE, $INN::Config::active) or logdie("Cannot open $INN::Config::active: $!"); my @oldgroup; while () { next unless /^(\Q$groupname\E)\s\d+\s\d+\s(\w)/; @oldgroup = split /\s+/; last; } close ACTIVE; my $status; if (not @oldgroup) { $status = 'not change'; } elsif (not $approved) { $status = 'be unapproved'; } else { $status = 'be removed'; } if ($action eq 'mail' and $status !~ /(not change|be unapproved)/) { my $mail = sendmail("rmgroup $groupname $sender"); print $mail <$tempfile") or logdie("Cannot open $tempfile: $!"); while () { print TEMPFILE $_ if not /^\Q$groupname\E\s/; } close TEMPFILE; close NEWSGROUPS; rename($tempfile, $INN::Config::newsgroups) or logdie("Cannot rename $tempfile: $!"); unlink $tempfile; # Unlock. INN::Utils::Shlock::unlock($lockfile); logger($log, "rmgroup $groupname $status $sender", $article) if $log; } } 1; inn-2.6.0/control/docheckgroups.in0000644000175200017520000002040612575023702016572 0ustar iuliusiulius#! /bin/sh # fixscript will replace this line with code to load innshellvars ## $Id: docheckgroups.in 9343 2011-08-16 14:28:07Z iulius $ ## Script to execute checkgroups text; results to stdout. ## ## Usage: docheckgroups [-u] [include-pattern [exclude-pattern]] < message ## ## If the -u flag is given, the newsgroups descriptions are automatically ## updated. T=${TMPDIR} UPDATEDESC=false cat /dev/null >${T}/$$out ## Parse arguments. if [ $# -gt 0 ]; then case $1 in -u) shift; UPDATEDESC=true;; esac fi ## Copy the message without excluded newsgroups and append local newsgroups. cat | ${PERL} -e 'while () { my @fields = split(); print $_ if ((scalar(@fields) > 0) && ($fields[0] !~ /'${2:-^#}'/) && ($fields[0] !~ /^#/)); }' > ${T}/$$msg test -f ${LOCALGROUPS} && cat ${LOCALGROUPS} | ${EGREP} -v "^#" >> ${T}/$$msg ## Exit if there is no matching newsgroup (otherwise docheckgroups is eager ## to delete everything). test -s ${T}/$$msg || { rm -f ${T}/$$* exit 0 } ## Make sure we do not have duplicates in the resulting concatenation of ## the checkgroups and the localgroups file. ${SORT} -u ${T}/$$msg > ${T}/$$msg2 mv -f ${T}/$$msg2 ${T}/$$msg ## Get the top-level newsgroup names from the message and turn it into ## a regexp. PATS=`${SED} <${T}/$$msg \ -e 's/[ ].*//' -e 's/\..*//' \ -e 's/^!//' -e '/^$/d' \ -e 's/^/^/' -e 's/$/[\.\w]/' \ | ${SORT} -u \ | (tr '\012' '|' ; echo '' ) \ | ${SED} -e 's/|$//'` ## Check for missing and obsolete newsgroups in active. cat ${ACTIVE} | ${PERL} -e 'while () { my @fields = split(); print $fields[0]."\n" if ((scalar(@fields) > 0) && ($fields[0] =~ /'${1:-.}'/) && ($fields[0] =~ /'${PATS}'/)); }' | ${SORT} > ${T}/$$active cat ${T}/$$msg | ${PERL} -e 'while () { my @fields = split(); print $fields[0]."\n" if ((scalar(@fields) > 0) && ($fields[0] =~ /'${1:-.}'/) && ($fields[0] =~ /'${PATS}'/)); }' | ${SORT} > ${T}/$$newsgrps comm -13 ${T}/$$active ${T}/$$newsgrps >${T}/$$missing comm -23 ${T}/$$active ${T}/$$newsgrps >${T}/$$remove ## Check for proper moderation flags in active (we need to be careful ## when dealing with wire-formatted articles manually fed from the spool). cat ${ACTIVE} | ${PERL} -e 'while () { my @fields = split(); print $fields[0]."\n" if ((scalar(@fields) > 0) && ($fields[0] =~ /'${1:-.}'/) && ($fields[0] =~ /'${PATS}'/) && ($_ =~ / m$/)); }' | ${SORT} > ${T}/$$amod.all cat ${T}/$$msg | ${PERL} -e 'while () { my @fields = split(); print $fields[0]."\n" if ((scalar(@fields) > 0) && ($fields[0] =~ /'${1:-.}'/) && ($fields[0] =~ /'${PATS}'/) && ($_ =~ / \(Moderated\)\r?$/)); }' \ | ${SORT} > ${T}/$$ng.mod comm -12 ${T}/$$missing ${T}/$$ng.mod >${T}/$$add.mod comm -23 ${T}/$$missing ${T}/$$ng.mod >${T}/$$add.unmod cat ${T}/$$add.mod ${T}/$$add.unmod >>${T}/$$add comm -23 ${T}/$$amod.all ${T}/$$remove >${T}/$$amod comm -13 ${T}/$$ng.mod ${T}/$$amod >${T}/$$ismod comm -23 ${T}/$$ng.mod ${T}/$$amod >${T}/$$nm.all comm -23 ${T}/$$nm.all ${T}/$$add >${T}/$$notmod ## Check for missing and obsolete newsgroups descriptions (possibly ## in wire format). A few sed implementations do not recognize ## "[ ]\+", so we use " [ ]*" instead. cat ${NEWSGROUPS} | ${PERL} -e 'while () { my @fields = split(); print $_ if ((scalar(@fields) > 0) && ($fields[0] =~ /'${1:-.}'/) && ($fields[0] =~ /'${PATS}'/)); }' \ | ${SED} 's/ [ ]*/ /' | ${SORT} > ${T}/$$localdesc cat ${T}/$$msg | ${PERL} -e 'while () { my @fields = split(); print $_ if ((scalar(@fields) > 0) && ($fields[0] =~ /'${1:-.}'/) && ($fields[0] =~ /'${PATS}'/)); }' \ | ${SED} 's/\r\?$//' \ | ${SED} 's/ [ ]*/ /' | ${SORT} > ${T}/$$newdesc comm -13 ${T}/$$localdesc ${T}/$$newdesc >${T}/$$missingdesc comm -23 ${T}/$$localdesc ${T}/$$newdesc >${T}/$$removedesc ## If the -u flag is given, update the newsgroups descriptions. if [ "${UPDATEDESC}" = "true" ] ; then cat ${NEWSGROUPS} | ${PERL} -e 'while () { my @fields = split(); print $_ if ((scalar(@fields) > 0) && ($fields[0] !~ /'${PATS}'/)); }' \ > ${T}/$$updatednewsgroups cat ${NEWSGROUPS} | ${PERL} -e 'while () { my @fields = split(); print $_ if ((scalar(@fields) > 0) && ($fields[0] !~ /'${1:-.}'/) && ($fields[0] =~ /'${PATS}'/)); }' \ >> ${T}/$$updatednewsgroups cat ${T}/$$newdesc >>${T}/$$updatednewsgroups mv -f ${NEWSGROUPS} ${NEWSGROUPS}.old ${SORT} ${T}/$$updatednewsgroups | ${SED} 's/ [ ]*/ /' \ | ${PERL} -e 'while () { my @fields = split("\t", $_, 2); next if (scalar(@fields) == 0); my $length = length("$fields[0]"); my $desc; if (scalar(@fields) == 2) { $desc = "$fields[1]"; } else { $desc = ""; } if ($length < 8) { print $fields[0]."\t\t\t".$desc; } elsif ($length < 16) { print $fields[0]."\t\t".$desc; } else { print $fields[0]."\t".$desc; } }' > ${NEWSGROUPS} chmod 0664 ${NEWSGROUPS} ${NEWSGROUPS}.old fi ## Display information on newsgroups which need to be removed/added/changed. if [ -s ${T}/$$remove ] ; then ( echo "# The following newsgroups are non-standard and should be removed:" echo "#" ${SED} "s/^/# /" ${T}/$$remove echo "#" echo "# You can remove them by executing the command(s):" echo "" for i in `cat ${T}/$$remove` ; do echo " ${PATHBIN}/ctlinnd rmgroup $i" done echo "" ) >>${T}/$$out fi if [ -s ${T}/$$add ] ; then ( echo "# The following newsgroups are missing and should be added:" echo "#" ${SED} "s/^/# /" ${T}/$$add echo "#" echo "# You can add them by executing the command(s):" echo "" for i in `cat ${T}/$$add.unmod` ; do echo " ${PATHBIN}/ctlinnd newgroup $i y ${FROM}" done for i in `cat ${T}/$$add.mod` ; do echo " ${PATHBIN}/ctlinnd newgroup $i m ${FROM}" done echo "" ) >>${T}/$$out fi if [ -s ${T}/$$ismod ] ; then ( echo "# The following newsgroups are incorrectly marked as moderated" echo "# and should have their status changed:" echo "#" ${SED} "s/^/# /" ${T}/$$ismod echo "#" echo "# You can correct this by executing the command(s):" echo "" for i in `cat ${T}/$$ismod` ; do echo " ${PATHBIN}/ctlinnd changegroup $i y" done echo "" ) >>${T}/$$out fi if [ -s ${T}/$$notmod ] ; then ( echo "# The following newsgroups are incorrectly marked as unmoderated" echo "# and should have their status changed:" echo "#" ${SED} "s/^/# /" ${T}/$$notmod echo "#" echo "# You can correct this by executing the command(s):" echo "" for i in `cat ${T}/$$notmod` ; do echo " ${PATHBIN}/ctlinnd changegroup $i m" done echo "" ) >>${T}/$$out fi ## Display information on descriptions which need to be removed/added. if [ -s ${T}/$$removedesc ] ; then ( echo "# The following newsgroups descriptions are obsolete and should be removed:" echo "#" ${SED} "s/^/# /" ${T}/$$removedesc echo "#" if [ "${UPDATEDESC}" = "true" ] ; then echo "# The file ${NEWSGROUPS} has just been updated accordingly." else echo "# You can remove them by editing ${NEWSGROUPS}" echo "# or by using the -u flag with docheckgroups." fi echo "" ) >>${T}/$$out fi if [ -s ${T}/$$missingdesc ] ; then ( echo "# The following newsgroups descriptions are missing and should be added:" echo "#" ${SED} "s/^/# /" ${T}/$$missingdesc echo "#" if [ "${UPDATEDESC}" = "true" ] ; then echo "# The file ${NEWSGROUPS} has just been updated accordingly." else echo "# You can add them by editing ${NEWSGROUPS}" echo "# or by using the -u flag with docheckgroups." fi echo "" ) >>${T}/$$out fi ## We're done. test -s ${T}/$$out && { cat ${T}/$$out echo "exit # so you can feed this message into the shell (as well as mod-active)." echo "" } rm -f ${T}/$$* inn-2.6.0/control/controlchan.in0000644000175200017520000004727212575023702016256 0ustar iuliusiulius#! /usr/bin/perl -w # fixscript will replace this line with code to load INN::Config ## $Id: controlchan.in 9620 2014-03-15 16:49:11Z iulius $ ## ## Channel feed program to route control messages to an appropriate handler. ## ## Copyright 2001 by Marco d'Itri ## ## Redistribution and use in source and binary forms, with or without ## modification, are permitted provided that the following conditions ## are met: ## ## 1. Redistributions of source code must retain the above copyright ## notice, this list of conditions and the following disclaimer. ## ## 2. Redistributions in binary form must reproduce the above copyright ## notice, this list of conditions and the following disclaimer in the ## documentation and/or other materials provided with the distribution. ## ## Give this program its own newsfeed. Make sure that you've created ## the newsgroup control.cancel so that you don't have to scan through ## cancels, which this program won't process anyway. ## ## Make a newsfeeds entry like this: ## ## controlchan!\ ## :!*,control,control.*,!control.cancel\ ## :AC,Tc,Wnsm\ ## :/controlchan require 5.004_03; use Encode; use Getopt::Std; use MIME::Parser; use INN::Utils::Shlock; use strict; delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'}; $0 =~ s!.*/!!; # globals my ($cachedctl, $curmsgid); my $lastctl = 0; my $use_syslog = 0; my $debug = 0; END { # In case we bail out, while holding a lock. INN::Utils::Shlock::releaselocks(); } my $usage = "usage: $0 [-ch]\n\n" . "Reads stdin for file names or tokens.\n\n" . " -c Disables cutoff on dates.\n" . " -h Giveis this usage information.\n"; my %opt; getopts("ch", \%opt) || die $usage; die $usage if defined $opt{'h'}; # setup logging ########################################################### # do not log to syslog if stderr is connected to a console if (not -t 2) { eval { require INN::Syslog; import INN::Syslog; $use_syslog = 1; }; eval { require Sys::Syslog; import Sys::Syslog; $use_syslog = 1; } unless $use_syslog; } if ($use_syslog) { if ($Sys::Syslog::VERSION < 0.15) { eval "sub Sys::Syslog::_PATH_LOG { '/dev/log' }" if $^O eq 'dec_osf'; Sys::Syslog::setlogsock('unix') if $^O =~ /linux|dec_osf|freebsd|darwin/; } openlog('controlchan', 'pid', $INN::Config::syslog_facility); } logmsg('starting'); # load modules from the control directory ################################# opendir(CTL, $INN::Config::controlprogs) or logdie("Cannot open $INN::Config::controlprogs: $!", 'crit'); foreach (readdir CTL) { next if not /^([a-z\.]+\.pl)$/ or not -f "$INN::Config::controlprogs/$_"; eval { require "$INN::Config::controlprogs/$1" }; if ($@) { $@ =~ s/\n/ /g; logdie($@, 'crit'); } logmsg("loaded $INN::Config::controlprogs/$1", 'debug'); } closedir CTL; # main loop ############################################################### while () { chop; my ($token, $sitepath, $msgid) = split(/\s+/, $_); next if not defined $token; $sitepath ||= ''; $curmsgid = $msgid || ''; my $artfh = open_article($token); next if not defined $artfh; my $article_string = ''; while (<$artfh>) { $article_string .= $_; } close $artfh or logdie('sm died with status ' . ($? >> 8)); my %hdr; my $parser = new MIME::Parser; $parser->output_dir("$INN::Config::pathtmp"); my $article = $parser->parse_data($article_string); if (not parse_article($article, \%hdr)) { $parser->filer->purge; next; } if (not $hdr{'control'}) { $parser->filer->purge; next; } if (not defined $opt{'c'}) { # Make sure old control articles are not replayed. my ($postingdate, $injectiondate); # Parse date header fields. We will take the oldest date. if ($hdr{'date'}) { $postingdate = int(convdate('-n', "$hdr{'date'}")); } else { logmsg('Missing Date: header field'); $parser->filer->purge; next; } if ($hdr{'injection-date'}) { $injectiondate = int(convdate('-n', "$hdr{'injection-date'}")); } else { $injectiondate = $postingdate; } my $articledate = ($postingdate < $injectiondate) ? $postingdate : $injectiondate; # Compute the allowed interval of time: # artcutoff days in the past, one day in the future. my $currentdate = int(convdate('-n')); my $mindate = $currentdate - $INN::Config::artcutoff * 86400; my $maxdate = $currentdate + 86400; if ($articledate > $maxdate) { logmsg('Control article injected or posted in the future (' . convdate('-d', '-c', "$articledate") . ')'); $parser->filer->purge; next; } if ($INN::Config::artcutoff > 0 && $articledate < $mindate) { logmsg('Control article too old (' . convdate('-d', '-c', "$articledate") . '), artcutoff set to ' . $INN::Config::artcutoff . ' days'); $parser->filer->purge; next; } } $curmsgid = $hdr{'message-id'}; my $sender = cleanaddr($hdr{'sender'} || $hdr{'from'}); my $replyto = cleanaddr($hdr{'reply-to'} || $hdr{'from'}); my (@progparams, $progname); if ($hdr{'control'} =~ /\s/) { $hdr{'control'} =~ /^(\S+)\s+(.+)?/; $progname = lc $1; # Newsgroups names are case-sensitive. @progparams = split(/\s+/, $2) if $2; if ($progname eq 'newgroup' and defined $progparams[1]) { $progparams[1] = lc $progparams[1]; } } else { $progname = lc $hdr{'control'}; } if ($progname eq 'cancel') { $parser->filer->purge; next; } if ($progname !~ /^([a-z]+)$/) { logmsg("Naughty control in article $curmsgid ($progname)"); $parser->filer->purge; next; } $progname = $1; # Do we want to process the message? Let's check the permissions. my (@charset_from, @maxchanges, @matches); my $exclusionpats = ''; my $droppats = ''; my $charset_to = ctlperm($progname, $sender, $progparams[0], $token, $article, \@charset_from, \@maxchanges, \@matches); foreach my $i (0..$#matches) { my $action = $matches[$i][0] if $matches[$i][0]; my $logname = $matches[$i][1] if $matches[$i][1]; $progparams[0] = $matches[$i][2] if $matches[$i][2]; if ($action eq '_pgpfail') { my $type = ''; if ($progname and $progname eq 'newgroup') { if ($progparams[1] and $progparams[1] eq 'moderated') { $type = 'm '; } else { $type = 'y '; } } logmsg("skipping $progname $type$sender" . " (pgpverify failed) in $curmsgid"); next; } # Find the appropriate module and call it. my $subname = "control_$progname"; my $subfind = \&$subname; if (not defined &$subfind) { if ($logname) { logger($logname, "Unknown control message by $sender", $article); } else { logmsg("Unknown \"$progname\" control by $sender"); } next; } # Count a control article only once. my $approved = $hdr{'approved'} ? 1 : 0; $approved = -1 if $i > 0; logmsg("$subname, " . join(' ', @progparams) . " $sender $replyto $token, $exclusionpats, $droppats," . " $charset_to, $sitepath, $action" . ($logname ? "=$logname" : '') .", $approved"); if ($action ne 'drop') { &$subfind(\@progparams, $sender, $replyto, $sitepath, $action, $logname, $approved, $article, \@charset_from, $charset_to, $exclusionpats, $droppats, \@maxchanges); $exclusionpats .= '|' if $exclusionpats; $exclusionpats .= $progparams[0]; } else { $droppats .= '|' if $droppats; $droppats .= $progparams[0]; } } $parser->filer->purge; } closelog() if $use_syslog; exit 0; # misc functions ########################################################## sub parse_article { my ($article, $hdr) = @_; my ($h, $buffer); my %uniquehdr = map { $_ => 1 } qw(approved control date followup-to from injection-date message-id newsgroups path reply-to sender subject); my $head = $article->head; foreach $h (%uniquehdr) { $hdr->{$h} = ''; $buffer = $head->get($h); if ($buffer) { $buffer =~ s/\r?\n$//; $buffer =~ s/\r?\n\s+/ /g; $hdr->{$h} = $buffer; } if ($head->count($h) > 1) { logmsg("Multiple $h headers in article $curmsgid"); return 0; } } # Article is empty or does not exist. return 0 if not $head->stringify; return 1; } # Strip a mail address, innd-style. sub cleanaddr { local $_ = shift; s/(\s+)?\(.*\)(\s+)?//g; s/.*<(.*)>.*/$1/; s/[^-a-zA-Z0-9+_.@%]/_/g; # protect MTA s/^-/_/; # protect MTA return $_; } # Read and cache control.ctl and control.ctl.local. sub readctlfile { if (! -e $INN::Config::ctlfile) { logdie("Cannot open $INN::Config::ctlfile: $!", 'crit'); } my $localfile = (-e $INN::Config::ctlfile . '.local'); my @files = ($INN::Config::ctlfile); my $mtime = (stat($INN::Config::ctlfile))[9]; if ($localfile) { push(@files, $INN::Config::ctlfile . '.local'); my $mtimelocalfile = (stat($INN::Config::ctlfile . '.local'))[9]; if ($mtimelocalfile > $mtime) { $mtime = $mtimelocalfile; } } return $cachedctl if $lastctl == $mtime; # mtime has not changed. $lastctl = $mtime; my @ctllist; foreach my $file (@files) { open(CTLFILE, $file) or logdie("Cannot open $file: $!", 'crit'); while () { chop; # Not a comment or blank? Convert wildmat to regex. next if not /^(\s+)?[^\#]/ or /^$/; if (not /^\/(?:local)?encoding\/:/ and not /^\/maxdocheckgroups\/:/ and not /:(?:doit|doifarg|drop|log|mail|verify-.*)(?:=.*)?$/) { s/.*://; logmsg("$_ is not a valid action for control.ctl", 'err'); next; } # Convert to a ':'-separated list of regexps. s/^all:/*:/i; s/([\$\+\.])/\\$1/g; s/\*/.*/g; s/\?/./g; s/(.*)/^$1\$/; s/:/\$:^/g; s/\|/\$|^/g; s/\//\\\//g; push(@ctllist, $_); } close(CTLFILE); } logmsg('warning: control.ctl is empty!', 'err') if not @ctllist; return $cachedctl = [ reverse @ctllist ]; } # Parse a control message's permissions. sub ctlperm { my ($type, $sender, $newsgroup, $token, $article, $charset_from, $maxchanges, $matches) = @_; my %keyresults = (); my ($action, $maxdocheckgroups, $logname, $charset_to); # newgroup and rmgroup require newsgroup names; check explicitly for that # here and return drop if the newsgroup is missing (to avoid a bunch of # warnings from undefined values later on in permission checking). if ($type eq 'newgroup' or $type eq 'rmgroup') { unless ($newsgroup) { push (@$matches, [ 'drop', undef, undef ]); return ('UTF-8'); } } my $ctllist = readctlfile(); my $matchedaction = 0; my $matchedencoding = 0; foreach (@$ctllist) { my @ctlline = split /:/; # 0: type 1: from@addr 2: group.* 3: action if ($type =~ /$ctlline[0]/ and $sender =~ /$ctlline[1]/i and ($type !~ /(?:new|rm)group/ or $newsgroup =~ /$ctlline[2]/)) { if (not $matchedaction) { $action = $ctlline[3]; $action =~ s/\^(.+)\$/$1/; $action =~ s/\\//g; ($action, $logname) = split(/=/, $action); if ($type eq 'checkgroups') { push (@$matches, [ $action, $logname, $ctlline[2] ]); } else { push (@$matches, [ $action, $logname, undef ]); # @ctllist is a reversed list so the first match is the last # one in control.ctl followed by control.ctl.local. $matchedaction = 1; } } } # 0: /localencoding/ 1: encoding if ($ctlline[0] eq '^\/localencoding\/$') { if (not $matchedencoding) { $charset_to = $ctlline[1]; $charset_to =~ s/\^(.+)\$/$1/; $charset_to =~ s/\\//g; $matchedencoding = 1; } } # 0: /encoding/ 1: from@addr 2: group.* 3: encoding[=force] if ($ctlline[0] eq '^\/encoding\/$') { if ($sender =~ /$ctlline[1]/i) { push (@$charset_from, $ctlline[2].':'.$ctlline[3]); } } # 0: /maxdocheckgroups/ 1: from@addr 2: group.* 3: number if ($ctlline[0] eq '^\/maxdocheckgroups\/$') { if ($sender =~ /$ctlline[1]/i) { $maxdocheckgroups = $ctlline[3]; $maxdocheckgroups =~ s/\^(.+)\$/$1/; push (@$maxchanges, $ctlline[2].':'.$maxdocheckgroups); } } } if (not defined $charset_to or not defined Encode::find_encoding($charset_to)) { $charset_to = 'UTF-8'; # Default local encoding. } # Default value when nothing matches the control article. if ($#$matches < 0) { push (@$matches, [ 'drop', undef, undef ]); } # Default value appended to the end of @maxchanges. push (@$maxchanges, '^.*$:10'); foreach my $i (0..$#$matches) { if ($$matches[$i][0] =~ /^verify-(.+)/) { my $keyowner = $1; if (!exists($keyresults{$keyowner})) { if ($INN::Config::pgpverify and $INN::Config::pgpverify =~ /^(?:true|on|yes)$/i) { my $pgpresult = defined &local_pgpverify ? local_pgpverify($token, $article, $keyowner) : pgpverify($token, $keyowner); if ($keyowner eq $pgpresult) { $keyresults{$keyowner} = 'doit'; } else { $keyresults{$keyowner} = '_pgpfail'; } } else { $keyresults{$keyowner} = 'mail'; } } $$matches[$i][0] = $keyresults{$keyowner}; } } return ($charset_to); } # Write stuff to a log or send mail to the news admin. sub logger { my ($logfile, $message, $article) = @_; my (@headers, @body); if (ref $article eq 'ARRAY') { @headers = @$article; } else { my $head = $article->head; @headers = split(/\r?\n/, $head->stringify); @body = split(/\r?\n/, $article->stringify_body); } if ($logfile eq 'mail') { my $mail = sendmail($message); print $mail map { s/^~/~~/; "$_\n" } @headers; print $mail "\n" . join ('', map { s/^~/~~/; "$_\n" } @body) if @body; close $mail or logdie("Cannot send mail: $!"); return; } if ($logfile =~ /^([^.\/].*)/) { $logfile = $1; } else { logmsg("Invalid log file: $logfile", 'err'); $logfile = 'control'; } $logfile = "$INN::Config::most_logs/$logfile.log" unless $logfile =~ /^\//; my $lockfile = $logfile; $lockfile =~ s#.*/##; $lockfile = "$INN::Config::locks/LOCK.$lockfile"; # Acquire a lock. INN::Utils::Shlock::lock($lockfile, 60) or logdie("Cannot create lockfile $lockfile"); open(LOGFILE, ">>$logfile") or logdie("Cannot open $logfile: $!"); print LOGFILE "$message\n"; foreach (@headers, '', @body, '') { print LOGFILE " $_\n"; } close LOGFILE; # Unlock. INN::Utils::Shlock::unlock($lockfile); } # write to syslog or errlog sub logmsg { my ($msg, $lvl) = @_; return if $lvl and $lvl eq 'debug' and not $debug; if ($use_syslog) { syslog($lvl || 'notice', '%s', $msg); } else { print STDERR (scalar localtime) . ": $msg\n"; } } # log a message and then die sub logdie { my ($msg, $lvl) = @_; $msg .= " ($curmsgid)" if $curmsgid; logmsg($msg, $lvl || 'err'); exit 1; } # wrappers executing external programs #################################### # Open an article appropriately to our storage method (or lack thereof). sub open_article { my $token = shift; if ($token =~ /^\@.+\@$/) { my $pid = open(ART, '-|'); logdie('Cannot fork: ' . $!) if $pid < 0; if ($pid == 0) { exec("$INN::Config::newsbin/sm", '-q', $token) or logdie("Cannot exec sm: $!"); } return *ART; } else { return *ART if open(ART, $token); logmsg("Cannot open article $token: $!"); } return undef; } sub pgpverify { my $token = shift; my $keyid = shift; if ($token =~ /^\@.+\@$/) { open(PGPCHECK, "$INN::Config::newsbin/sm -q $token " . "| $INN::Config::newsbin/pgpverify --findid='$keyid' |") or goto ERROR; } else { open(PGPCHECK, "$INN::Config::newsbin/pgpverify --findid='$keyid' < $token |") or goto ERROR; } my $pgpresult = ; close PGPCHECK or goto ERROR; $pgpresult ||= ''; chop $pgpresult; return $pgpresult; ERROR: logmsg("pgpverify failed: $!", 'debug'); return ''; } sub ctlinnd { my ($cmd, @args) = @_; my $st = system("$INN::Config::newsbin/ctlinnd", '-s', $cmd, @args); logdie('Cannot run ctlinnd: ' . $!) if $st == -1; logdie('ctlinnd returned status ' . ($st & 255)) if $st > 0; } # Run convdate on the given arguments. # The result can be numeric (with -n) or a string (with -d). sub convdate { my (@args) = @_; my $result = 0; my $pid = open(my $CONVDATE, '-|'); logdie("Cannot fork: $!") if $pid < 0; if ($pid == 0) { # exec() properly escapes its arguments to prevent shell injection. exec("$INN::Config::pathbin/convdate", @args) or logdie("Cannot exec convdate: $!"); } else { $result = <$CONVDATE>; close($CONVDATE); } $result = 0 if not $result; chomp $result; return $result; } # If $body is not defined, returns a file handle which must be closed. # Don't forget checking the return value of close(). # $addresses may be a scalar or a reference to a list of addresses. # If not defined, $INN::Config::newsmaster is the default. # parts of this code stolen from innmail.pl sub sendmail { my ($subject, $addresses, $body) = @_; $addresses = [ $addresses || $INN::Config::newsmaster ] if not ref $addresses; $subject ||= '(no subject)'; # fix up all addresses my @addrs = map { s#[^-a-zA-Z0-9+_.@%]##g; $_ } @$addresses; my $sm = $INN::Config::mta; if ($sm =~ /%s/) { $sm = sprintf($sm, join(' ', @addrs)); } else { $sm .= ' ' . join(' ', @addrs); } # fork and spawn the MTA without using the shell my $pid = open(MTA, '|-'); logdie('Cannot fork: ' . $!) if $pid < 0; if ($pid == 0) { exec(split(/\s+/, $sm)) or logdie("Cannot exec $sm: $!"); } print MTA 'To: ' . join(",\n\t", @addrs) . "\nSubject: $subject\n\n"; return *MTA if not defined $body; $body = join("\n", @$body) if ref $body eq 'ARRAY'; print MTA $body . "\n"; close MTA or logdie("Execution of $sm failed: $!"); return 1; } inn-2.6.0/control/perl-nocem.in0000644000175200017520000004366712575023702016011 0ustar iuliusiulius#! /usr/bin/perl -w # fixscript will replace this line with code to load INN::Config ############################################################################## # perl-nocem - a NoCeM-on-spool implementation for INN 2.x. # Copyright 2000 by Miquel van Smoorenburg # Copyright 2001 by Marco d'Itri # This program is licensed under the terms of the GNU General Public License. # # List of changes: # # 2002: Patch by Steven M. Christey for untrusted printf input. # 2007: Patch by Christoph Biedl for checking a timeout. # Documentation improved by Jeffrey M. Vinocur (2002), Russ Allbery (2006) # and Julien Elie (2007). # ############################################################################## require 5.00403; use strict; # XXX FIXME I haven't been able to load it only when installed. # If nobody can't fix it just ship the program with this line commented. #use Time::HiRes qw(time); my $keyring = $INN::Config::pathetc . '/pgp/ncmring.gpg'; # XXX To be moved to a config file. #sub local_want_cancel_id { # my ($group, $hdrs) = @_; # ## Hippo has too many false positives to be useful outside of pr0n groups # if ($hdrs->{issuer} =~ /(?:Ultra|Spam)Hippo/) { # foreach (split(/,/, $group)) { # return 1 if /^alt\.(?:binar|sex)/; # } # return 0; # } # return 1; #} # no user serviceable parts below this line ################################### # global variables my ($working, $got_sighup, $got_sigterm, @ncmperm, $cancel); my $use_syslog = 0; my $log_open = 0; my $nntp_open = 0; my $last_cancel = 0; my $socket_timeout = $INN::Config::peertimeout - 100; my $logfile = $INN::Config::pathlog . '/perl-nocem.log'; # initialization and main loop ############################################### eval { require Sys::Syslog; import Sys::Syslog; $use_syslog = 1; }; if ($use_syslog) { if ($Sys::Syslog::VERSION < 0.15) { eval "sub Sys::Syslog::_PATH_LOG { '/dev/log' }" if $^O eq 'dec_osf'; Sys::Syslog::setlogsock('unix') if $^O =~ /linux|dec_osf|freebsd|darwin/; } openlog('nocem', '', $INN::Config::syslog_facility); } if (not $INN::Config::gpgv) { logmsg('cannot find the gpgv binary', 'err'); sleep 5; exit 1; } if ($INN::Config::version and not $INN::Config::version =~ /^INN 2\.[0123]\./) { $cancel = \&cancel_nntp; } else { $cancel = \&cancel_ctlinnd; } $SIG{HUP} = \&hup_handler; $SIG{INT} = \&term_handler; $SIG{TERM} = \&term_handler; $SIG{PIPE} = \&term_handler; logmsg('starting up'); unless (read_ctlfile()) { sleep 5; exit 1; } while () { chop; $working = 1; do_nocem($_); $working = 0; term_handler() if $got_sigterm; hup_handler() if $got_sighup; } logmsg('exiting because of EOF', 'debug'); exit 0; ############################################################################## # Process one NoCeM notice. sub do_nocem { my $token = shift; my $start = time; # open the article and verify the notice my $artfh = open_article($token); return if not defined $artfh; my ($msgid, $nid, $issuer, $nocems) = read_nocem($artfh); close $artfh; return unless $nocems; &$cancel($nocems); logmsg("Articles cancelled: " . join(' ', @$nocems), 'debug'); my $diff = (time - $start) || 0.01; my $nr = scalar @$nocems; logmsg(sprintf("processed notice %s by %s (%d ids, %.5f s, %.1f/s)", $nid, $issuer, $nr, $diff, $nr / $diff)); } # - Check if it is a PGP signed NoCeM notice # - See if we want it # - Then check PGP signature sub read_nocem { my $artfh = shift; # Examine the first 200 lines to see if it is a PGP signed NoCeM. my $ispgp = 0; my $isncm = 0; my $inhdr = 1; my $i = 0; my $body = ''; my $from = ''; my $msgid = '<>'; while (<$artfh>) { last if $i++ > 200; s/\r\n$/\n/; if ($inhdr) { if (/^$/) { $inhdr = 0; } elsif (/^From:\s+(.*)\s*$/i) { $from = $1; } elsif (/^Message-ID:\s+(<.*>)/i) { $msgid = $1; } } else { $body .= $_; $ispgp = 1 if /^-----BEGIN PGP SIGNED MESSAGE-----/; if (/^\@\@BEGIN NCM HEADERS/) { $isncm = 1; last; } } } # must be a PGP signed NoCeM. if (not $ispgp) { logmsg("Article $msgid: not PGP signed", 'debug'); return; } if (not $isncm) { logmsg("Article $msgid: not a NoCeM", 'debug'); return; } # read the headers of this NoCeM, and check if it's supported. my %hdrs; while (<$artfh>) { s/\r\n/\n/; $body .= $_; last if /^\@\@BEGIN NCM BODY/; my ($key, $val) = /^([^:]+)\s*:\s*(.*)$/; $hdrs{lc $key} = $val; } foreach (qw(action issuer notice-id type version)) { next if $hdrs{$_}; logmsg("Article $msgid: missing $_ pseudo header", 'debug'); return; } return if not supported_nocem($msgid, \%hdrs); # decide if we want it. if (not want_nocem(\%hdrs)) { logmsg("Article $msgid: unwanted ($hdrs{issuer}/$hdrs{type})", 'debug'); return; } # XXX want_hier() not implemented # if ($hdrs{hierarchies} and not want_hier($hdrs{hierarchies})) { # logmsg("Article $msgid: unwanted hierarchy ($hdrs{hierarchies})", # 'debug'); # return; # } # We do want it, so read the entire article. Also copy it to # a temp file so that we can check the PGP signature when done. my $tmpfile = "$INN::Config::pathtmp/nocem.$$"; if (not open(OFD, ">$tmpfile")) { logmsg("cannot open temp file $tmpfile: $!", 'err'); return; } print OFD $body; undef $body; # process NoCeM body. my $inbody = 1; my @nocems; my ($lastid, $lastgrp); while (<$artfh>) { s/\r\n$/\n/; print OFD; $inbody = 0 if /^\@\@END NCM BODY/; next if not $inbody or /^#/; my ($id, $grp) = /^(\S*)\s+(\S+)/; next if not $grp; if ($id) { push @nocems, $lastid if $lastid and want_cancel_id($lastgrp, \%hdrs); $lastid = $id; $lastgrp = $grp; } else { $lastgrp .= ',' . $grp; } } push @nocems, $lastid if $lastid and want_cancel_id($lastgrp, \%hdrs); close OFD; # at this point we need to verify the PGP signature. return if not @nocems; my $e = pgp_check($hdrs{issuer}, $msgid, $tmpfile); unlink $tmpfile; return if not $e; return ($msgid, $hdrs{'notice-id'}, $hdrs{issuer}, \@nocems); } # XXX not implemented: code to discard notices for groups we don't carry sub want_cancel_id { my ($group, $hdrs) = @_; return local_want_cancel_id(@_) if defined &local_want_cancel_id; 1; } # Do we actually want this NoCeM? sub want_nocem { my $hdrs = shift; foreach (@ncmperm) { my ($issuer, $type) = split(/\001/); if ($hdrs->{issuer} =~ /$issuer/i) { return 1 if '*' eq $type or lc $hdrs->{type} eq $type; } } return 0; } sub supported_nocem { my ($msgid, $hdrs) = @_; if ($hdrs->{version} !~ /^0\.9[0-9]?$/) { logmsg("Article $msgid: version $hdrs->{version} not supported", 'debug'); return 0; } if ($hdrs->{action} ne 'hide') { logmsg("Article $msgid: action $hdrs->{action} not supported", 'debug'); return 0; } return 1; } # Check the PGP signature on an article. sub pgp_check { my ($issuer, $msgid, $art) = @_; # fork and spawn a child my $pid = open(PFD, '-|'); if (not defined $pid) { logmsg("pgp_check: cannot fork: $!", 'err'); return 0; } if ($pid == 0) { open(STDERR, '>&STDOUT'); exec($INN::Config::gpgv, '--status-fd=1', $keyring ? '--keyring=' . $keyring : '', $art); exit 126; } # Read the result and check status code. local $_ = join('', ); my $status = 0; if (not close PFD) { if ($? >> 8) { $status = $? >> 8; } else { logmsg("Article $msgid: $INN::Config::gpgv killed by signal " . ($? & 255)); return 0; } } # logmsg("Command line was: $INN::Config::gpgv --status-fd=1" # . ($keyring ? ' --keyring=' . $keyring : '') . " $art", 'debug'); # logmsg("Full PGP output: >>>$_<<<", 'debug'); if (/^\[GNUPG:\]\s+GOODSIG\s+\S+\s+(.*)/m) { return 1 if $1 =~ /\Q$issuer\E/; logmsg("Article $msgid: signed by $1 instead of $issuer"); } elsif (/^\[GNUPG:\]\s+NO_PUBKEY\s+(\S+)/m) { logmsg("Article $msgid: $issuer (ID $1) not in keyring"); } elsif (/^\[GNUPG:\]\s+BADSIG\s+\S+\s+(.*)/m) { logmsg("Article $msgid: bad signature from $1"); } elsif (/^\[GNUPG:\]\s+BADARMOR/m or /^\[GNUPG:\]\s+UNEXPECTED/m) { logmsg("Article $msgid: malformed signature"); } elsif (/^\[GNUPG:\]\s+ERRSIG\s+(\S+)/m) { # safety net: we get there if we don't know about some token logmsg("Article $msgid: unknown error (ID $1)"); } else { # some other error we don't know about happened. # 126 is returned by the child if exec fails. s/ at \S+ line \d+\.\n$//; s/\n/_/; logmsg("Article $msgid: $INN::Config::gpgv exited " . (($status == 126) ? "($_)" : "with status $status"), 'err'); } return 0; } # Read article. sub open_article { my $token = shift; if ($token =~ /^\@.+\@$/) { my $pid = open(ART, '-|'); if ($pid < 0) { logmsg('Cannot fork: ' . $!, 'err'); return undef; } if ($pid == 0) { exec("$INN::Config::newsbin/sm", '-q', $token) or logmsg("Cannot exec sm: $!", 'err'); return undef; } return *ART; } else { return *ART if open(ART, $token); logmsg("Cannot open article $token: $!", 'err'); } return undef; } # Cancel a number of Message-IDs. We use ctlinnd to do this, # and we run up to 15 of them at the same time (10 usually). sub cancel_ctlinnd { my @ids = @{$_[0]}; while (@ids > 0) { my $max = @ids <= 15 ? @ids : 10; for (my $i = 1; $i <= $max; $i++) { my $msgid = shift @ids; my $pid; sleep 5 until (defined ($pid = fork)); if ($pid == 0) { exec "$INN::Config::pathbin/ctlinnd", '-s', '-t', '180', 'cancel', $msgid; exit 126; } # logmsg("cancelled: $msgid [$i/$max]", 'debug'); } # Now wait for all children. while ((my $pid = wait) > 0) { next unless $?; if ($? >> 8) { logmsg("Child $pid died with status " . ($? >> 8), 'err'); } else { logmsg("Child $pid killed by signal " . ($? & 255), 'err'); } } } } sub cancel_nntp { my $ids = shift; my $r; if ($nntp_open and time - $socket_timeout > $last_cancel) { logmsg('Close socket for timeout'); close (NNTP); $nntp_open = 0; } if (not $nntp_open) { use Socket; if (not socket(NNTP, PF_UNIX, SOCK_STREAM, 0)) { logmsg("socket: $!", 'err'); goto ERR; } if (not connect(NNTP, sockaddr_un($INN::Config::pathrun . '/nntpin'))) { logmsg("connect: $!", 'err'); goto ERR; } if (($r = ) !~ /^200 /) { $r =~ s/\r\n$//; logmsg("bad reply from server: $r", 'err'); goto ERR; } select NNTP; $| = 1; select STDOUT; print NNTP "MODE CANCEL\r\n"; if (($r = ) !~ /^284 /) { $r =~ s/\r\n$//; logmsg("MODE CANCEL not supported: $r", 'err'); goto ERR; } $nntp_open = 1; } foreach (@$ids) { print NNTP "$_\r\n"; if (($r = ) !~ /^289/) { $r =~ s/\r\n$//; logmsg("cannot cancel $_: $r", 'err'); goto ERR; } } $last_cancel = time; return; ERR: # discard unusable socket close (NNTP); logmsg('Switching to ctlinnd...', 'err'); cancel_ctlinnd($ids); $cancel = \&cancel_ctlinnd; } sub read_ctlfile { my $permfile = $INN::Config::pathetc . '/nocem.ctl'; unless (open(CTLFILE, $permfile)) { logmsg("Cannot open $permfile: $!", 'err'); return 0; } while () { chop; s/^\s+//; s/\s+$//; next if /^#/ or /^$/; my ($issuer, $type) = split(/:/, lc $_); if (not (defined($issuer) and defined($type))) { logmsg("Cannot parse nocem.ctl line <<$_>>; syntax is <>.", 'err'); next; } $type =~ s/\s//g; foreach (split(/,/, $type)) { push(@ncmperm, "$issuer\001$_"); } } close CTLFILE; return 1; } sub logmsg { my ($msg, $lvl) = @_; if (not $use_syslog) { if ($log_open == 0) { open(LOG, ">>$logfile") or die "Cannot open log: $!"; $log_open = 1; select LOG; $| = 1; select STDOUT; } $lvl ||= 'notice'; print LOG "$lvl: $msg\n"; return; } syslog($lvl || 'notice', '%s', $msg); } sub hup_handler { $got_sighup = 1; return if $working; close LOG; $log_open = 0; } sub term_handler { $got_sigterm = 1; return if $working; logmsg('exiting because of signal'); exit 1; } __END__ =head1 NAME perl-nocem - A NoCeM-on-spool implementation for S =head1 SYNOPSIS perl-nocem =head1 DESCRIPTION NoCeM, which is pronounced I, is a protocol enabling authenticated third-parties to issue notices which can be used to cancel unwanted articles (like spam and articles in moderated newsgroups which were not approved by their moderators). It can also be used by readers as a I. It is intended to eventually replace the protocol for third-party cancel messages. B processes third-party, PGP-signed article cancellation notices. It is possible not to honour all NoCeM notices but only those which are sent by people whom you trust (that is to say if you trust the PGP key they use to sign their NoCeM notices). Indeed, it is up to you to decide whether you wish to honour their notices, depending on the criteria they use. Processing NoCeM notices is easy to set up: =over 4 =item 1. Import the keys of the NoCeM issuers you trust in order to check the authenticity of their notices. You can do: gpg --no-default-keyring --primary-keyring /pgp/ncmring.gpg --import chmod 644 /pgp/ncmring.gpg where is the value of the I parameter set in F and the file containing the key(s) to import. The keyring must be located in /pgp/ncmring.gpg; you only have to create the directory /pgp before using B (it will automatically generate the F file) and make sure the news user can read this file, once generated. For old PGP-generated keys, you may have to use B<--allow-non-selfsigned-uid> if they are not properly self-signed, but anyone creating a key really should self-sign the key. Current PGP implementations do this automatically. The keys of NoCeM issuers can be found in the web site of I: L. You can even download there a unique file which contains all the keys. =item 2. Create a F config file in I indicating the NoCeM issuers and notices you want to follow. This permission file contains lines like: bleachbot@httrack.com:spam,site pgpmoose@killfile.org:pgpmoose-forged-moderation This will remove all articles for which the issuer (first part of the line, before the colon C<:>) has issued NoCeM notices corresponding to the criteria specified after the colon. You will also find information about that on the web site of I. =item 3. Add to the F file an entry like this one in order to feed B the NoCeM notices posted to alt.nocem.misc and news.lists.filters: nocem!\ :!*,alt.nocem.misc,news.lists.filters\ :Tc,Wf,Ap:/perl-nocem with the correct path to B, located in . Then, reload the F file (C). Note that you should at least carry news.lists.filters on your news server (or other newsgroups where NoCeM notices are sent) if you wish to process them. =item 4. Everything should now work. However, do not hesitate to manually test B with a NoCeM notice, using: grephistory '' | perl-nocem Indeed, B expects tokens on its standard input, and B can easily give it the token of a known article, thanks to its Message-ID. =back When you have verified that everything works, you can eventually turn off regular spam cancels, if you want, not processing any longer cancels containing C in the Path: header (see the I parameter in F). =head1 FILES =over 4 =item I/perl-nocem The Perl script itself used to process NoCeM notices. =item I/nocem.ctl The configuration file which specifies the NoCeM notices to be processed. =item I/pgp/ncmring.gpg The keyring which contains the public keys of trusted NoCeM issuers. =back =head1 BUGS The Subject: header is not checked for the @@NCM string and there is no check for the presence of the References: header. The Newsgroups: pseudo header is not checked, but this can be done in local_want_cancel_id(). The Hierarchies: header is ignored. =head1 HISTORY Copyright 2000 by Miquel van Smoorenburg . Copyright 2001 by Marco d'Itri . $Id: perl-nocem.in 9639 2014-05-17 06:24:44Z iulius $ =head1 SEE ALSO gpgv(1), grephistory(1), inn.conf(5), newsfeeds(5), pgp(1). =cut inn-2.6.0/samples/0000755000175200017520000000000012575023701013363 5ustar iuliusiuliusinn-2.6.0/samples/nnrpd_auth_wrapper.pl.in0000644000175200017520000000251012575023702020226 0ustar iuliusiulius#! /usr/bin/perl -w # fixscript will replace this line with code to load INN::Config # Example wrapper nnrpd_auth.pl for support of old perl authentication # scripts, by Erik Klavon. # This file contains a sample perl script which can be used to # duplicate the behavior of the old nnrpperlauth functionality. This # script only supports authentication. # How to use this wrapper: # - append your old script to this file with two changes: # - rename the old "auth_init" sub to "old_auth_init" # - rename the old "authenticate" sub to "old_authenticate" use vars qw(%attributes); # auth_init # This sub simply calls old_auth_init # Comment this out if you don't need auth_init sub auth_init { old_auth_init(); } # authenticate # This sub modifies the global hash attributes so that it has all the # entries required in the old way of doing things, calls # old_authenticate, and transforms the return array into the new # format. sub authenticate { $attributes{type} = "authenticate"; my @auth_array = old_authenticate(); my @return_array; # copy return code $return_array[0] = $auth_array[0]; # simple error report if ($auth_array[0] != 281) { $return_array[1] = "Perl authentication error!"; return @return_array; } else { $return_array[1] = ""; } return @return_array; } inn-2.6.0/samples/startup_innd.pl0000644000175200017520000000245612575023702016442 0ustar iuliusiulius# # RCSId: $Id: startup_innd.pl 42 1997-08-04 04:03:43Z gpalmer $ # Description: Sample startup code for Perl hooks in INN. This file, after # it's installed in the right spot, will be loaded when # innd starts up. The following functions should be defined # by it (they don't have to be, in fact this file can be # empty, but it must exist if you've compiled in Perl support). # # sub filter_before_reload { ... } # Called before the filter definition file filter_innd.pl # is loaded (every time). # sub filter_after_reload { ... } # Called after the filter definition file filter_innd.pl # is loaded (every time). # # See the sample file filter_innd.pl for details on what it does. my $before_count = 1 ; # Gets no arguments, and its caller expects no return value. sub filter_before_reload { if ($before_count == 1) { # Do one thing # print "First time (before)\n" ; $before_count++ ; } else { # Do something else # print "Time number $before_count (before)\n" ; $before_count++ ; } } my $after_count = 1 ; # Gets no arguments, and its caller expects no return value. sub filter_after_reload { if ($after_count == 1) { # Do one thing # print "First time (after)\n" ; $after_count++ ; } else { # Do another # print "Time number $after_count (after)\n" ; $after_count++ ; } } inn-2.6.0/samples/cycbuff.conf0000644000175200017520000000350612575023702015660 0ustar iuliusiulius## $Id: cycbuff.conf 7651 2007-08-20 10:28:34Z iulius $ ## ## Configuration file for INN CNFS storage method. ## ## This file defines the cyclical buffers that make up the storage pools ## for CNFS (Cyclic News File System). For information about how to ## configure INN to use CNFS, see the storage.conf man page; and for ## information about how to create the CNFS buffers, see the cycbuff.conf ## man page. ## ## The order of lines in this file is not important among the same item. ## But all cycbuff item should be presented before any metacycbuff item. ## Number of articles written before the cycbuff header is ## written back to disk to (25 by default). cycbuffupdate:25 ## Interval (in seconds) between re-reads of the cycbuff (30 by default). refreshinterval:30 ## 1. Cyclic buffers ## Format: ## "cycbuff" (literally) : symbolic buffer name (less than 7 characters) : ## path to buffer file : length of symbolic buffer in kilobytes in decimal ## (1KB = 1024 bytes) ## ## If you're trying to stay under 2 GB, keep your sizes below 2097152. cycbuff:ONE:/export/cycbuffs/one:512000 cycbuff:TWO:/export/cycbuffs/two:512000 cycbuff:THREE:/export/cycbuffs/three:512000 ## 2. Meta-cyclic buffers ## Format: ## "metacycbuff" (literally) : symbolic meta-cyclic buffer name (less than ## 8 characters) : comma separated list of cyclic buffer symbolic names ## [:INTERLEAVE|SEQUENTIAL] ## ## With the default INTERLEAVE mode, articles are stored in each cycbuff ## in a round-robin fashion, one article per cycbuff in the order listed. ## With the SEQUENTIAL mode, each cycbuff is written in turn until that ## cycbuff is full and then moves on to the next one. ## ## Symbolic meta-cyclic buffer names are used in storage.conf in the ## options: field. metacycbuff:BIG:ONE,TWO:SEQUENTIAL metacycbuff:SMALL:THREE inn-2.6.0/samples/innshellvars.local0000755000175200017520000000070112575023702017111 0ustar iuliusiulius## $Id: innshellvars.local 9109 2010-09-24 17:16:09Z iulius $ ## ## Sample innshellvars.local script. ## ## It permits to add or override variables defined by ## the innshellvars shell script. ## This script has to be executable in order to be included ## during the run of innshellvars. ## Defining or redefining VARIABLE will allow to use ## $VARIABLE with the corresponding value in a script ## calling innshellvars. #VARIABLE = 'test' inn-2.6.0/samples/nnrpd_auth_wrapper.py0000644000175200017520000000432412575023702017643 0ustar iuliusiulius## $Id: nnrpd_auth_wrapper.py 8251 2008-12-23 12:22:03Z iulius $ ## ## Example wrapper for support of old Python authentication scripts, ## by Erik Klavon. ## ## This file contains a sample Python script which can be used to ## duplicate the behaviour of the old nnrppythonauth functionality. ## This script only supports authentication. ## ## How to use this wrapper: ## - insert your authentication class into this file; ## - rename your authentication class OLDAUTH. ## ## See the INN Python Filtering and Authentication Hooks documentation ## for more information. ## The use of this file is *discouraged*. ## Old AUTH class. ## Insert your old auth class here. ## Do not include the code which sets the hook. ## Wrapper AUTH class. It creates an instance of the old class and ## calls its methods. Arguments and return values are munged as ## needed to fit the new way of doing things. class MYAUTH: """Provide auth callbacks to nnrpd.""" def authen_init(self): self.old = OLDAUTH() def authenticate(self, attributes): # Python 3.x uses memoryview(b'authinfo') because buffers # do not exist any longer. Note that the argument is # a bytes object. # attributes['type'] = memoryview(b'authinfo') attributes['type'] = buffer('authinfo') perm = (self.old).authenticate(attributes) err_str = "No error" if perm[0] == 481: err_str = "Python authentication error!" return (perm[0], err_str) def authen_close(self): (self.old).close() ## The rest is used to hook up the auth module on nnrpd. It is unlikely ## you will ever need to modify this. ## Import functions exposed by nnrpd. This import must succeed, or nothing ## will work! from nnrpd import * ## Create a class instance. myauth = MYAUTH() ## ...and try to hook up on nnrpd. This would make auth object methods visible ## to nnrpd. try: set_auth_hook(myauth) syslog('notice', "authentication module successfully hooked into nnrpd") except Exception, errmsg: # Syntax for Python 2.x. #except Exception as errmsg: # Syntax for Python 3.x. syslog('error', "Cannot obtain nnrpd hook for authentication method: %s" % errmsg[0]) inn-2.6.0/samples/send-uucp.cf0000644000175200017520000000222312575023702015600 0ustar iuliusiulius## $Id: send-uucp.cf 7650 2007-08-20 10:08:33Z iulius $ ## ## Configuration file for send-uucp(8). ## ## It specifies to which remote UUCP sites news batches from the outgoing ## files should be sent. ## ## Format: ## site[:host[:funnel]] [compressor [maxsize [batchtime]]] ## ## compressor, maxsize and batchtime can be left out and will then use the ## default values. You can't leave out the second field (compressor) and ## still use the third (maxsize) or the fourth (batchtime). So if you want ## to set a maxsize, you HAVE to add a compression method. ## ## compressor values can be one of: "bzip2", "compress", "gzip", "none". ## ## You can use flags with your compressor, just add them with a '_' character ## instead of a space. For example, "compress_-b13" for 13 bits compression. ## ## Remember that the size you set is the size *before* compression! ## ## See the send-uucp man page for more information. #zoetermeer gzip 1048576 5,18,22 #hoofddorp gzip 1048576 5,18,22 #pa3ebv gzip 1048576 5,18,22 #drinkel bzip2 1048576 5,6,18,20,22,0,2 #manhole compress 1048576 5,18,22 #owl compress 1048576 #able #pern::MYFUNNEL! inn-2.6.0/samples/distributions0000644000175200017520000000060012575023702016205 0ustar iuliusiulius## $Id: distributions 9028 2010-03-21 20:10:46Z iulius $ ## ## Sample distributions configuration file. ## ## It contains recommended values for the Distribution: header. ## It is encoded in UTF-8. ## See the distributions man page for more information. #fr Local to France. #local Local to this news server. #nj Local to New Jersey. #usa Local to the United States of America. inn-2.6.0/samples/actsync.ign0000644000175200017520000000111512575023702015525 0ustar iuliusiulius## $Id: actsync.ign 7664 2007-09-02 12:58:07Z iulius $ ## ## Sample actsync ignore file. ## ## It defines synchronization rules (which newsgroups will be ## checked and which will be ignored). ## See the actsync man page for more information. ## For now by default do not sync. i * ## Sync on the 8 majors. c comp.* c humanities.* c misc.* c news.* c rec.* c sci.* c soc.* c talk.* ## Sync only moderated gnu.* groups. #c gnu.* m ## Don't compare to.* groups as they will differ. i to.* ## We always want our special top level groups. i control i general i junk i test i to inn-2.6.0/samples/nnrpd_access.py0000644000175200017520000000760412575023702016407 0ustar iuliusiulius## $Id: nnrpd_access.py 8250 2008-12-23 12:00:46Z iulius $ ## ## This is a sample access module for the Python nnrpd hook. ## ## See the INN Python Filtering and Authentication Hooks documentation ## for more information. ## The perl_access: parameter in readers.conf is used to load this script. ## ## An instance of ACCESS class is passed to nnrpd via the set_auth_hook() ## function imported from nnrpd. The following methods of that class ## are known to nnrpd: ## ## __init__() - Use this method to initialize your ## general variables or open a common ## database connection. May be omitted. ## access_init() - Init function specific to access ## control. May be omitted. ## access(attributes) - Called when a python_access ## statement is reached in the ## processing of readers.conf. Returns ## a dictionary of values representing ## statements to be included in an ## access group. ## access_close() - Called on nnrpd termination. Save ## your state variables or close a ## database connection. May be omitted. ## ## If there is a problem with return codes from any of these methods, then nnrpd ## will die and syslog the exact reason. ## ## There are also a few Python functions defined in nnrpd: ## ## set_auth_hook() - Called by nnrpd as this module is loaded. ## It is used to pass a reference to an ## instance of authentication class to nnrpd. ## syslog() - An equivalent replacement for regular syslog. ## One consideration for using it is to ## uniform nnrpd logging. ## Sample access class. It defines all access methods known to nnrpd. class ACCESS: """Provide access callbacks to nnrpd.""" def __init__(self): """This is a good place to initialize variables or open a database connection.""" syslog('notice', 'nnrpd access class instance created') def access_init(self): """Called when this script is initialized.""" pass def access(self, attributes): """Called when python_access: is encountered in readers.conf.""" # Just for debugging purposes. syslog('notice', 'n_a access() invoked: hostname %s, ipaddress %s, interface %s, user %s' % ( \ attributes['hostname'], \ attributes['ipaddress'], \ attributes['interface'], \ attributes['user'])) # Allow newsreading from specific host only. if '127.0.0.1' == str(attributes['ipaddress']): syslog('notice', 'authentication access by IP address succeeded') return {'read':'*', 'post':'*'} else: syslog('notice', 'authentication access by IP address failed') return {'read':'!*', 'post':'!*'} def access_close(self): """Called on nnrpd termination.""" pass ## The rest is used to hook up the access module on nnrpd. It is unlikely ## you will ever need to modify this. ## Import functions exposed by nnrpd. This import must succeed, or nothing ## will work! from nnrpd import * ## Create a class instance. myaccess = ACCESS() ## ...and try to hook up on nnrpd. This would make auth object methods visible ## to nnrpd. try: set_auth_hook(myaccess) syslog('notice', "access module successfully hooked into nnrpd") except Exception, errmsg: # Syntax for Python 2.x. #except Exception as errmsg: # Syntax for Python 3.x. syslog('error', "Cannot obtain nnrpd hook for access method: %s" % errmsg[0]) inn-2.6.0/samples/innshellvars.pl.local0000755000175200017520000000100312575023702017517 0ustar iuliusiulius## $Id: innshellvars.pl.local 9109 2010-09-24 17:16:09Z iulius $ ## ## Sample innshellvars.pl.local script. ## ## It permits to add or override variables defined by ## the INN::Config Perl module (or the legacy innshellvars.pl ## Perl script). ## This script has to be executable in order to be included ## during the run of INN::Config. ## Defining or redefining $variable will allow to use ## $INN::Config::variable with the corresponding value ## in a script calling INN::Config. #$variable = 'test'; inn-2.6.0/samples/filter_innd.py0000644000175200017520000002756412575023702016251 0ustar iuliusiulius## $Id: filter_innd.py 8955 2010-02-07 20:05:39Z iulius $ ## ## This is a sample filter for the Python innd hook. ## ## See the INN Python Filtering and Authentication Hooks documentation ## for more information. ## ## You have access to the following methods from the module INN: ## - addhist(message-id) ## - article(message-id) ## - cancel(message-id) ## - havehist(message-id) ## - hashstring(string) ## - head(message-id) ## - newsgroup(groupname) ## - set_filter_hook(instance) ## - syslog(level, message) import re from string import * ## The built-in intern() method has been in the sys module ## since Python 3.0. import sys if sys.version_info[0] >= 3: def intern(headerName): return sys.intern(headerName) ## This looks weird, but creating and interning these strings should ## let us get faster access to header keys (which innd also interns) by ## losing some strcmps under the covers. Also_Control = intern("Also-Control") Approved = intern("Approved") Archive = intern("Archive") Archived_At = intern("Archived-At") Bytes = intern("Bytes") Cancel_Key = intern("Cancel-Key") Cancel_Lock = intern("Cancel-Lock") Comments = intern("Comments") Content_Base = intern("Content-Base") Content_Disposition = intern("Content-Disposition") Content_Transfer_Encoding = intern("Content-Transfer-Encoding") Content_Type = intern("Content-Type") Control = intern("Control") Date = intern("Date") Date_Received = intern("Date-Received") Distribution = intern("Distribution") Expires = intern("Expires") Face = intern("Face") Followup_To = intern("Followup-To") From = intern("From") In_Reply_To = intern("In-Reply-To") Injection_Date = intern("Injection-Date") Injection_Info = intern("Injection-Info") Keywords = intern("Keywords") Lines = intern("Lines") List_ID = intern("List-ID") Message_ID = intern("Message-ID") MIME_Version = intern("MIME-Version") Newsgroups = intern("Newsgroups") NNTP_Posting_Date = intern("NNTP-Posting-Date") NNTP_Posting_Host = intern("NNTP-Posting-Host") NNTP_Posting_Path = intern("NNTP-Posting-Path") Organization = intern("Organization") Original_Sender = intern("Original-Sender") Originator = intern("Originator") Path = intern("Path") Posted = intern("Posted") Posting_Version = intern("Posting-Version") Received = intern("Received") References = intern("References") Relay_Version = intern("Relay-Version") Reply_To = intern("Reply-To") Sender = intern("Sender") Subject = intern("Subject") Summary = intern("Summary") Supersedes = intern("Supersedes") User_Agent = intern("User-Agent") X_Auth = intern("X-Auth") X_Auth_Sender = intern("X-Auth-Sender") X_Canceled_By = intern("X-Canceled-By") X_Cancelled_By = intern("X-Cancelled-By") X_Complaints_To = intern("X-Complaints-To") X_Face = intern("X-Face") X_HTTP_UserAgent = intern("X-HTTP-UserAgent") X_HTTP_Via = intern("X-HTTP-Via") X_Mailer = intern("X-Mailer") X_Modbot = intern("X-Modbot") X_Modtrace = intern("X-Modtrace") X_Newsposter = intern("X-Newsposter") X_Newsreader = intern("X-Newsreader") X_No_Archive = intern("X-No-Archive") X_Original_Message_ID = intern("X-Original-Message-ID") X_Original_NNTP_Posting_Host = intern("X-Original-NNTP-Posting-Host") X_Original_Trace = intern("X-Original-Trace") X_Originating_IP = intern("X-Originating-IP") X_PGP_Key = intern("X-PGP-Key") X_PGP_Sig = intern("X-PGP-Sig") X_Poster_Trace = intern("X-Poster-Trace") X_Postfilter = intern("X-Postfilter") X_Proxy_User = intern("X-Proxy-User") X_Submissions_To = intern("X-Submissions-To") X_Trace = intern("X-Trace") X_Usenet_Provider = intern("X-Usenet-Provider") X_User_ID = intern("X-User-ID") Xref = intern("Xref") __BODY__ = intern("__BODY__") __LINES__ = intern("__LINES__") class InndFilter: """Provide filtering callbacks to innd.""" def __init__(self): """This runs every time the filter is loaded or reloaded. This is a good place to initialize variables and precompile regular expressions, or maybe reload stats from disk. """ self.re_newrmgroup = re.compile('(?:new|rm)group\s') self.re_obsctl = re.compile('(?:sendsys|version|uuname)') # Message-ID pattern from a once-common spambot. self.re_none44 = re.compile('none\d+\.yet>') # There is a mad newgrouper who likes to meow. self.re_meow = re.compile("^Meow\!", re.M) # One of my silly addresses. self.re_fluffymorph = re.compile("andruQ@myremarQ.coM", re.I) def filter_before_reload(self): """Runs just before the filter gets reloaded. You can use this method to save state information to be restored by the __init__() method or down in the main module. """ syslog('notice', "filter_before_reload executing...") def filter_close(self): """Runs when innd exits. You can use this method to save state information to be restored by the __init__() method or down in the main module. """ syslog('notice', "filter_close running, bye!") def filter_messageid(self, msgid): """Filter articles just by their Message-IDs. This method interacts with the CHECK, IHAVE and TAKETHIS NNTP commands. If you return a non-empty string here, the offered article will be refused before you ever have to waste any bandwidth looking at it (unless TAKETHIS is used before an earlier CHECK). Make sure that such a message is properly encoded in UTF-8 so as to comply with the NNTP protocol. """ return "" # Deactivate the samples. if self.re_none44.search(msgid): return "But I don't like spam!" if msgid[0:8] == ': ## Where: ## Wildcard name or IP address ## String to be displayed in the logs ## ## By adding a host to this file, it will be tracked using the ## nnrpd tracking system if enabled in inn.conf(5). Each read/post ## will have an entry logged with the in the log message ## # nasty.foo.com:nasty@foo.com # *.bar.com:VeryNastyClient inn-2.6.0/samples/newsfeeds.in0000644000175200017520000001355012575023702015703 0ustar iuliusiulius## $Id: newsfeeds.in 9501 2013-07-01 17:32:45Z iulius $ ## ## newsfeeds - determine where Usenet articles get sent ## ## Format: ## site[/exclude,exclude...]\ ## :pattern,pattern...[/distrib,distrib...]\ ## :flag,flag...\ ## :param ## ## This file is complicated -- see newsfeeds(5) for full details. ## The ME feed entry is special magic. ## ## "/exclude" entries for this feed entry will cause INN to reject all ## articles that have passed through those listed sites (by matching ## Path: entries). There are some "pseudo-sites" in general use that can ## be listed as exclusions to reject specific types of 3rd-party cancel ## messages (see the "Cancel FAQ" in news.admin.net-abuse.usenet): ## ## cyberspam Cancels for spam, munged articles, binaries ## spewcancel Cancels for munged articles and runaway gateways ## bincancel Cancels for binary postings to non-binary groups ## udpcancel Cancels to force sites to enforce antispam policies ## ## The "pattern" field for this feed entry gives the initial subscription ## list for all other feeds specified in this file. These patterns are ## *prepended* to all other feed patterns. Using this feature is ## confusing and mildly discouraged; make sure you understand the man ## page before using it. ## ## "/distrib" for this feed entry specifies what distributions the server ## will accept. If any distributions are listed there, the server will ## accept only articles with those distributions and articles that do not ## have a Distribution: header field. If all the distributions listed are ## negated (starting with "!"), then the server will only accept articles ## without those distributions and articles that do not have a Distribution: ## header field. ## ## For the ME line (and the ME line *only*), patterns affect *outgoing* ## feeds and distributions affect *incoming* feeds (including local posts). # Empty default subscription list, reject all incoming articles (including # locally posted articles) with a distribution of "local" or # "collabra-internal", accept all others (including articles that do not # have a Distribution: header field). ME:!*/!local,!collabra-internal:: # The same as the above, but would reject all posts that have # news.example.com in the path (posts passing through that site). #ME/news.example.com:!*/!local,!collabra-internal:: # An empty ME entry is possible, in which case no exclusion patterns # will be defined. #ME::: # The special feed that handles all newsgroup control messages. Only # disable this if you want to ignore all newsgroup control messages; INN # no longer handles any control messages except cancel internally. controlchan!\ :!*,control,control.*,!control.cancel\ :AC,Tc,Wnsm:@bindir@/controlchan ## Uncomment if you're using innfeed. This feed tells INN how to run ## innfeed, and then every site you're feeding with innfeed has a ## flag of Tm and an argument of "innfeed!" to funnel into this feed. ## The feed pattern for innfeed should *always* be "!*"; don't ever ## feed articles directly into innfeed. ## ## Add "-y" as an option to innfeed to use the name of each feed as the ## name of the host to feed articles to; without "-y" an innfeed.conf ## file is needed. # innfeed funnel master. #innfeed!\ # :!*\ # :Tc,Wnm*:@bindir@/innfeed ## Only uncomment this feed if both enableoverview and useoverchan are ## set to true in inn.conf. By default, innd will write out overview ## internally and doesn't need or want this feed, but useoverchan can ## optionally be set to true and this feed uncommented to move those ## operations out of innd's main loop. # News overview. #overview!:*:Tc,WnteO:@bindir@/overchan ## OUTGOING NORMAL FEED EXAMPLES # A real-time feed through innfeed. Don't send articles with a distribution # of "foo", since those articles are internal. # Note that control messages will be sent even though "!control,!control.*" # is specified. It is useful not to forget that pattern since control # messages for local.* would still be sent with "*,@local.*" only. #news.uu.net/uunet\ # :*,!junk,!control,!control.*/!foo\ # :Tm:innfeed! # Create a batch file in @SPOOLDIR@/outgoing for all articles # that haven't already passed through nic.near.net. The batch file will # be named nic.near.net, the default file name, and either nntpsend or # send-nntp can send articles from that spool file. #nic.near.net\ # :*,!junk,!control,!control.*/!foo\ # :Tf,Wnm: # A UUCP feed, where we try to keep the "batching" between 4 KB and 1 KB. # You can use send-uucp(8) to process these batch files. #ihnp4\ # :*,!junk,!control,!control.*/!foo\ # :Tf,Wnb,B4096/1024: ## OUTGOING SPECIAL FEED EXAMPLES # Accumulate Path: header statistics. See ninpaths(8) for more details on # how to set this up. #inpaths!:*:Tc,WP:@bindir@/ninpaths -p -d @LOGDIR@/path/inpaths.%d # Feed all moderated source postings to an archiver. #source-archive!:!*,*sources*,!*wanted*,!*.d\ # :Tc,Wn:@bindir@/archive -f -i @SPOOLDIR@/archive/INDEX # Feed NoCeM notices to perl-nocem in order to process third-party, # PGP-signed article cancellation notices. See perl-nocem(8) for more # details on how to set this up. #nocem!:!*,alt.nocem.misc,news.lists.filters\ # :Tc,Wf,Ap:@bindir@/perl-nocem # News to mail gateway. Similar to innfeed, this uses a master feed and # then individual feeds for every separate address that news is being # gated to. This sends all posts to rec.pets.red-ants to the address # listed in @sysconfdir@/news2mail.cf for list-big-red-ants. # Posts from the domain list owner are excluded (path for that example: # lists.ucsd.edu). #news2mail!:!*:Ac,Tc,Wn*:@bindir@/news2mail #list-big-red-ants/lists.ucsd.edu:!*,rec.pets.red-ants:Ap,Tm:news2mail! # Capture all local postings (with a distribution of "foo" and no more # than two sites in the Path: header) using a local program (that doesn't # come with INN). #capture!\ # :*/foo\ # :Tp,H2:/usr/local/bin/capture %s inn-2.6.0/samples/active.minimal0000644000175200017520000000034112575023702016205 0ustar iuliusiuliuscontrol 0000000000 0000000001 n control.cancel 0000000000 0000000001 n control.checkgroups 0000000000 0000000001 n control.newgroup 0000000000 0000000001 n control.rmgroup 0000000000 0000000001 n junk 0000000000 0000000001 n inn-2.6.0/samples/nnrpd_dynamic_wrapper.py0000644000175200017520000000357712575023702020337 0ustar iuliusiulius## $Id: nnrpd_dynamic_wrapper.py 8250 2008-12-23 12:00:46Z iulius $ ## ## Example wrapper for support of old Python authentication scripts, ## by Erik Klavon. ## ## This file contains a sample Python script which can be used to ## duplicate the behaviour of the old nnrppythonauth functionality. ## This script only supports dynamic access control by group. ## ## How to use this wrapper: ## - insert your authentication class into this file; ## - rename your authentication class OLDAUTH. ## ## See the INN Python Filtering and Authentication Hooks documentation ## for more information. ## The use of this file is *discouraged*. ## Old AUTH class. ## Insert your old auth class here. ## Do not include the code which sets the hook. ## Wrapper DYNACCESS class. It creates an instance of the old class and ## calls its methods. Arguments and return values are munged as ## needed to fit the new way of doing things. class MYDYNACCESS: """Provide dynamic access callbacks to nnrpd.""" def dynamic_init(self): self.old = OLDAUTH() def dynamic(self, attributes): return (self.old).authorize(attributes) def dynamic_close(self): (self.old).close() ## The rest is used to hook up the dynamic access module on nnrpd. It is unlikely ## you will ever need to modify this. ## Import functions exposed by nnrpd. This import must succeed, or nothing ## will work! from nnrpd import * ## Create a class instance. mydynaccess = MYDYNACCESS() ## ...and try to hook up on nnrpd. This would make auth object methods visible ## to nnrpd. try: set_auth_hook(mydynaccess) syslog('notice', "dynamic access module successfully hooked into nnrpd") except Exception, errmsg: # Syntax for Python 2.x. #except Exception as errmsg: # Syntax for Python 3.x. syslog('error', "Cannot obtain nnrpd hook for dynamic access method: %s" % errmsg[0]) inn-2.6.0/samples/inn.conf.in0000644000175200017520000001406212575023702015427 0ustar iuliusiulius## $Id: inn.conf.in 9922 2015-07-14 16:43:55Z iulius $ ## ## inn.conf -- INN configuration data ## ## Format: ## : ## ## Blank values are allowed for certain parameters. ## ## See the inn.conf(5) man page for a full description of each of these ## options. This sample file is divided into two sections; first, there ## are the parameters that must be set (or should be set in nearly all ## cases), and then all parameters are given with their defaults for ## reference in the same order and with the same organization as the ## inn.conf(5) documentation. # The following parameters are most likely to need setting, although the # defaults generated by configure may be reasonable. mta: "@SENDMAIL@ -oi -oem %s" organization: "A poorly-installed InterNetNews site" ovmethod: tradindexed hismethod: hisv6 pathhost: @HOSTNAME@ pathnews: @prefix@ #runasuser: #runasgroup: # General Settings #domain: #innflags: mailcmd: @bindir@/innmail #server: # Feed Configuration artcutoff: 10 #bindaddress: #bindaddress6: dontrejectfiltered: false hiscachesize: 256 ignorenewsgroups: false immediatecancel: false linecountfuzz: 0 maxartsize: 1000000 maxconnections: 50 #pathalias: #pathcluster: pgpverify: @DO_PGPVERIFY@ port: 119 refusecybercancels: false remembertrash: true #sourceaddress: #sourceaddress6: verifycancels: false verifygroups: false wanttrash: false wipcheck: 5 wipexpire: 10 # Article Storage cnfscheckfudgesize: 0 enableoverview: true extraoverviewadvertised: [ ] extraoverviewhidden: [ ] groupbaseexpiry: true mergetogroups: false nfswriter: false overcachesize: 128 #ovgrouppat: storeonxref: true useoverchan: false wireformat: true xrefslave: false # Reading allownewnews: true articlemmap: true clienttimeout: 1800 initialtimeout: 10 msgidcachesize: 64000 nfsreader: false nfsreaderdelay: 60 nnrpdcheckart: true nnrpdflags: "" nnrpdloadlimit: 16 noreader: false readerswhenstopped: false readertrack: false tradindexedmmap: true # Reading -- Keyword Support # # You should add "keywords" to extraoverviewadvertised or extraoverviewhidden # if you enable this feature. You must have compiled this support in too # with --enable-keywords at configure time. keywords: false keyartlimit: 100000 keylimit: 512 keymaxwords: 250 # Posting addinjectiondate: true addinjectionpostingaccount: false addinjectionpostinghost: true checkincludedtext: false #complaints: #fromhost: localmaxartsize: 1000000 #moderatormailer: nnrpdauthsender: false #nnrpdposthost: nnrpdpostport: 119 spoolfirst: false strippostcc: false # Posting -- Exponential Backoff backoffauth: false #backoffdb: backoffk: 1 backoffpostfast: 0 backoffpostslow: 1 backofftrigger: 10000 # Reading and Posting -- TLS/SSL Support # # The OpenSSL SSL and crypto libraries must have been found # at configure time to have this support, or you must have # compiled this support in with --with-openssl at configure time. #tlscafile: #tlscapath: @sysconfdir@ #tlscertfile: @sysconfdir@/cert.pem #tlskeyfile: @sysconfdir@/key.pem #tlsciphers: #tlscompression: true #tlseccurve: #tlspreferserverciphers: true #tlsprotocols: [ TLSv1 TLSv1.1 TLSv1.2 ] # Monitoring doinnwatch: true innwatchbatchspace: 4000 innwatchlibspace: 25000 innwatchloload: 1000 innwatchhiload: 2000 innwatchpauseload: 1500 innwatchsleeptime: 600 innwatchspoolnodes: 200 innwatchspoolspace: 25000 # Logging docnfsstat: false htmlstatus: true incominglogfrequency: 200 logartsize: true logcancelcomm: false logcycles: 3 logipaddr: true logsitename: true logstatus: true logtrash: true nnrpdoverstats: true nntplinklog: false #stathist: status: 600 timer: 600 # System Tuning badiocount: 5 blockbackoff: 120 chaninacttime: 600 chanretrytime: 300 datamovethreshold: 16384 icdsynccount: 10 keepmmappedthreshold: 1024 #maxcmdreadsize: maxforks: 10 nicekids: 4 nicenewnews: 0 nicennrpd: 0 pauseretrytime: 300 peertimeout: 3600 rlimitnofile: -1 # Paths patharchive: @SPOOLDIR@/archive patharticles: @SPOOLDIR@/articles pathbin: @bindir@ pathcontrol: @CONTROLDIR@ pathdb: @DBDIR@ pathetc: @sysconfdir@ pathfilter: @FILTERDIR@ pathhttp: @HTTPDIR@ pathincoming: @SPOOLDIR@/incoming pathlog: @LOGDIR@ pathoutgoing: @SPOOLDIR@/outgoing pathoverview: @SPOOLDIR@/overview pathrun: @RUNDIR@ pathspool: @SPOOLDIR@ pathtmp: @tmpdir@ inn-2.6.0/samples/incoming.conf0000644000175200017520000001372212575023702016043 0ustar iuliusiulius## $Id: incoming.conf 8439 2009-05-01 09:20:24Z iulius $ ## ## incoming.conf -- Configuration of incoming news feeds ## ## This file consists of three types of entries: key/value, peer and group. ## Comments are taken from the hash character "#" to the end of the line. ## Blank lines are ignored. ## ## Key/value entries are a keyword immediately followed by a colon, at least ## one blank and a value. For example: ## ## max-connections: 10 ## ## A legal key contains neither blanks, nor colon, nor "#". ## There are three different types of values: integers, booleans, and strings. ## Integers are as to be expected. A boolean value is either "true" or ## "false" (case is significant). A string value is any other sequence of ## characters. If the string needs to contain whitespace, then it must be ## quoted with double quotes. ## ## Peer entries look like: ## ## peer { ## # body ## } ## ## The word "peer" is required. is a label for this peer. It is ## any string valid as a key. The body of a peer entry contains some number ## of key/value entries. ## ## Group entries look like: ## ## group { ## # body ## } ## ## The word "group" is required. is any string valid as a key. ## The body of a group entry contains any number of the three types of ## entries. So key/value pairs can be defined inside a group, and peers can ## be nested inside a group, and other groups can be nested inside a group. ## ## Key/value entries that are defined outside of all peer and group entries ## are said to be at "global scope". Global key/value entries act as ## defaults for peers. When innd looks for a specific value in a peer entry ## (for example, the maximum number of connections to allow), if the value ## is not defined in the peer entry, then the enclosing groups are examined ## for the entry (starting at the closest enclosing group). If there are no ## enclosing groups, or the enclosing groups don't define the key/value, ## then the value at global scope is used. ## ## A small example could be: ## ## # Global value applied to all peers that have no value of their own. ## max-connections: 5 ## ## # A peer definition. ## peer uunet { ## hostname: usenet1.uu.net ## } ## ## peer vixie { ## hostname: gw.home.vix.com ## max-connections: 10 # Override global value. ## } ## ## # A group of two peers which can open more connections than normal. ## group fast-sites { ## max-connections: 15 ## ## # Another peer. The max-connections: value from the ## # fast-sites group scope is used. ## peer data.ramona.vix.com { ## hostname: data.ramona.vix.com ## } ## ## peer bb.home.vix.com { ## hostname: bb.home.vix.com ## max-connections: 20 # He can really cook. ## } ## } ## ## Given the above configuration file, the defined peers would have the ## following values for the max-connections: key. ## ## uunet 5 ## vixie 10 ## data.ramona.vix.com 15 ## bb.home.vix.com 20 ## ## The following keys are allowed: ## ## hostname: ## This key is mandatory in a peer block. The value is a string representing ## a list of hostnames separated by a comma. A hostname is the host's FQDN, ## or the dotted-quad IP address of the peer for IPv4, or the colon-separated ## IP address of the peer for IPv6. ## ## streaming: ## This key requires a boolean value. It defines whether streaming commands ## are allowed from this peer. (default=true) ## ## max-connections: ## This key requires a positive integer value. It defines the maximum number ## of connections allowed. A value of zero specifies an unlimited number ## of maximum connections ("unlimited" or "none" can be used as synonyms). ## (default=0) ## ## hold-time: ## This key requires a positive integer value. It defines the hold time before ## close, if the connection is over the max-connections: value. A value of zero ## specifies immediate close. (default=0) ## ## identd: ## This key requires a string value. It is used if you wish to require a ## peer's user name retrieved through identd match the specified string. ## (unset by default, that is to say no identd) ## ## password: ## This key requires a string value. It is used if you wish to require a peer ## to supply a password. (unset by default, that is to say no password) ## ## patterns: ## This key requires a string value. It is a list of newsfeeds(5)-style list ## of newsgroups which are to be accepted from this host. (default="*") ## ## email: ## This key requires a string value. Reserved for future use. (empty by default) ## ## comment: ## This key requires a string value. Reserved for future use. (empty by default) ## ## skip: ## This key requires a boolean value. Setting this entry causes this peer ## to be skipped. (default=false) ## ## ignore: ## This key requires a boolean value. Setting this entry causes innd to ## refuse every article sent via CHECK or IHAVE by this peer. (default=false) ## ## noresendid: ## This key requires a boolean value. It defines whether innd should send ## "431" (response to CHECK, in streaming mode) or "436" (response to IHAVE ## in non-streaming mode) responses instead of "438" (response to CHECK) ## or "435" (response to IHAVE) if a message is offered that is already ## received from another peer. This can be useful for peers that resend ## messages right away, as innfeed does. (default=false) ## ## nolist: ## This key requires a boolean value. It defines whether a peer is allowed ## to issue list command. (default=false, that is to say it can) ## streaming: true # Streaming allowed by default. max-connections: 8 # Per feed. peer ME { hostname: "localhost, 127.0.0.1, ::1" } inn-2.6.0/samples/localgroups0000644000175200017520000000131212575023702015636 0ustar iuliusiulius## $Id: localgroups 8441 2009-05-03 22:12:50Z iulius $ ## ## Put your local newsgroups, along with their descriptions, ## in this file (formatted like the newsgroups file) if you want ## them to be recognized by docheckgroups. They would otherwise ## be mentioned for removal. ## ## There is no need to put here local newsgroups of hierarchies ## for which no checkgroups control messages are sent (unless ## you manually process checkgroups texts for them). ## ## Lines beginning with a hash sign (#) are not taken into account. ## ## See the docheckgroups man page for more information. #comp.group.to.add An additional local newsgroup in comp.*. #comp.other.group Another local newsgroup in comp.*. inn-2.6.0/samples/nnrpd_access_wrapper.pl.in0000644000175200017520000000267212575023702020537 0ustar iuliusiulius#! /usr/bin/perl -w # fixscript will replace this line with code to load INN::Config # Example wrapper nnrpd_access.pl for support of old perl authentication # scripts, by Erik Klavon. # This file contains a sample perl script which can be used to # duplicate the behavior of the old nnrpperlauth functionality. This # script only supports access control. # How to use this wrapper: # - append your old script to this file with two changes: # - rename the old "auth_init" sub to "old_auth_init" # - rename the old "authenticate" sub to "old_authenticate" use vars qw(%attributes); # access # This sub modifies the global hash attributes so that it has all the # entries required in the old way of doing things, calls # old_authenticate, and creates a return hash with the right attributes. sub access { # Comment this out if you don't need auth_init. old_auth_init(); $attributes{type} = "connect"; my @connect_array = old_authenticate(); my %hash; # handle max rate if ($connect_array[4]) { # Force perl to make a C string out of this integer, # or else bad things will happen. Sigh. $hash{"max_rate"} = $connect_array[4] . "\0"; } # handle read boolean, set to wildmat if ($connect_array[1]) { $hash{"read"} = $connect_array[3]; } # handle post boolean, set to wildmat if ($connect_array[2]) { $hash{"post"} = $connect_array[3]; } return %hash; } inn-2.6.0/samples/inn-radius.conf0000644000175200017520000000340212575023702016303 0ustar iuliusiulius# $Id: inn-radius.conf 9940 2015-09-04 12:58:15Z iulius $ # # Sample RADIUS configuration file for the RADIUS readers.conf # authenticator. If you're not using that authenticator, this file is not # used. server radius { # Hostname of the RADIUS server. #radhost: radius-server.example.com # Port to query on the RADIUS server. radport: 1645 # Local hostname or IP address making the request. # # The RADIUS server expects an IP address; a hostname will be translated # into an IP address with gethostbyname(). If not given, this information # is not included in the request (not all RADIUS setups need this information). #lochost: news.example.com # Local port of connection. # # The port the client being authenticated is connecting to. If not # given, defaults to 119. This doesn't need to be set unless readers are # connecting to a non-standard port. #locport: 119 # Shared secret with the RADIUS server. # # If your secret includes spaces, tabs, or #, be sure to include it # in double quotes. #secret: SECRET-WORD # Prefix for username. # # Before being given to the RADIUS server, usernames will be rewritten by # prepending the prefix, if given, and then appending the suffix, if # given. #prefix: news- # Suffix for username. #suffix: @example.com # Whether to ignore bad reply IP. # # If set to false, the RADIUS authenticator will check to ensure that the # response it receives is from the same IP address as it sent the request # to (for some added security). If set to true, it will skip this # verification check (if your RADIUS server has multiple IP addresses or # if other odd things are going on, it may be perfectly normal for the # response to come from a different IP address). ignore-source: false } inn-2.6.0/samples/INN.py0000644000175200017520000000202712575023702014363 0ustar iuliusiulius## $Id: INN.py 8250 2008-12-23 12:00:46Z iulius $ ## ## This module supplies stub Python functions corresponding to the ones ## provided by innd. It is not used by the server; it is only here so ## that you can test your filter scripts before loading. ## See the INN Python Filtering and Authentication Hooks documentation ## for more information. from types import * def set_filter_hook(anObject): print("** set_filter_hook for " + repr(anObject)) def addhist(messageid): print("** addhist Message-ID: " + messageid) def havehist(messageid): print("** havehist Message-ID: " + messageid) def cancel(messageid): print("** cancel Message-ID: " + messageid) def newsgroup(groupname): print("** newsgroup: " + groupname) def head(messageid): print("** head Message-ID: " + messageid) def article(messageid): print("** article Message-ID: " + messageid) def hashstring(mystring): print("** hash: " + mystring) def syslog(level, message): print("-- syslog level: %s message: %s" % (level, message)) inn-2.6.0/samples/Makefile0000644000175200017520000000277612575023702015040 0ustar iuliusiulius## $Id: Makefile 7650 2007-08-20 10:08:33Z iulius $ ## ## All the actual installation work of any files in the samples directory ## is done via the site directory, so that one can maintain one's news ## configuration in the site directory and use make commands to update the ## server automatically. All this Makefile does is run fixconfig or ## fixscript on a few files that need configure results (and remove them on ## make clean). include ../Makefile.global top = .. ALL = buffindexed.conf inn.conf innreport.conf newsfeeds \ nnrpd_auth.pl nnrpd_access.pl nnrpd_auth_wrapper.pl \ nnrpd_access_wrapper.pl all: $(ALL) install: bootstrap: clean clobber distclean maintclean: rm -f $(ALL) depend: profiled: all $(FIXCONFIG) $(FIXSCRIPT): @echo Run configure before running make. See INSTALL for details. @exit 1 ## Build rules. FIXC = $(FIXCONFIG) FIXS = $(FIXSCRIPT) buffindexed.conf: buffindexed.conf.in $(FIXC) ; $(FIXC) $@.in inn.conf: inn.conf.in $(FIXC) ; $(FIXC) $@.in innreport.conf: innreport.conf.in $(FIXC) ; $(FIXC) $@.in newsfeeds: newsfeeds.in $(FIXC) ; $(FIXC) $@.in nnrpd_auth.pl: nnrpd_auth.pl.in $(FIXS) ; $(FIXS) $@.in nnrpd_access.pl: nnrpd_access.pl.in $(FIXS) ; $(FIXS) $@.in nnrpd_auth_wrapper.pl: nnrpd_auth_wrapper.pl.in $(FIXS) ; $(FIXS) $@.in nnrpd_access_wrapper.pl: nnrpd_access_wrapper.pl.in $(FIXS) ; $(FIXS) $@.in inn-2.6.0/samples/storage.conf0000644000175200017520000000276012575023702015704 0ustar iuliusiulius## $Id: storage.conf 7651 2007-08-20 10:28:34Z iulius $ ## ## Rules for where INN should store incoming articles. ## ## This file is used to determine which storage method articles are sent ## to be stored and which storage class they are stored as. Each ## method is described as follows: ## ## method { ## newsgroups: ## class: ## size: [,] ## expires: [,] ## options: ## exactmatch: ## } ## ## See the storage.conf man page for more information. ## ## Only newsgroups, class, and (for CNFS, to specify the metacycbuff) ## options are required; the other keys are optional. If any CNFS ## methods are configured, you will also need to set up cycbuff.conf. ## By default, store everything in tradspool. method tradspool { newsgroups: * class: 0 } ## Here are some samples for a CNFS configuration. This assumes that you ## have two metacycbuffs configured, one for text newsgroups and one for ## binaries. Cancel messages, which tend to be very high-volume, are ## stored in the binary metacycbuff as well. This assumes storeonxref is ## set to true in inn.conf. ## Pick off the binary newsgroups first. #method cnfs { # newsgroups: *.bina*,control.cancel # class: 1 # options: BINARY #} ## Put the remaining (text) groups in the other cycbuff. #method cnfs { # newsgroups: * # class: 2 # options: TEXT #} inn-2.6.0/samples/nnrpd.py0000644000175200017520000000105212575023702015055 0ustar iuliusiulius## $Id: nnrpd.py 8250 2008-12-23 12:00:46Z iulius $ ## ## This module supplies stub Python functions corresponding to the ones ## provided by nnrpd. It is not used by the server; it is only here so ## that you can test your filter scripts before loading. ## See the INN Python Filtering and Authentication Hooks documentation ## for more information. from types import * def set_auth_hook(anObject): print("** set_auth_hook for " + repr(anObject)) def syslog(level, message): print("-- syslog level: %s message: %s" % (level, message)) inn-2.6.0/samples/innshellvars.tcl.local0000755000175200017520000000071712575023702017701 0ustar iuliusiulius## $Id: innshellvars.tcl.local 9109 2010-09-24 17:16:09Z iulius $ ## ## Sample innshellvars.tcl.local script. ## ## It permits to add or override variables defined by ## the innshellvars.tcl script. ## This script has to be executable in order to be included ## during the run of innshellvars.tcl. ## Defining or redefining variable will allow to use ## $variable with the corresponding value during the run ## of innshellvars.tcl. #set variable "test" inn-2.6.0/samples/newsgroups.minimal0000644000175200017520000000051512575023702017151 0ustar iuliusiuliuscontrol Various control messages (no posting). control.cancel Cancel messages (no posting). control.checkgroups Hierarchy check control messages (no posting). control.newgroup Newsgroup creation control messages (no posting). control.rmgroup Newsgroup removal control messages (no posting). junk Unfiled articles (no posting). inn-2.6.0/samples/readers.conf0000644000175200017520000001121312575023702015656 0ustar iuliusiulius## $Id: readers.conf 7828 2008-05-07 07:58:22Z iulius $ ## ## readers.conf - Access control and configuration for nnrpd ## ## Format: ## auth "" { ## hosts: "" ## auth: "" ## res: "" ## default: "" ## default-domain: "" ## } ## access "" { ## users: "" ## newsgroups: "" ## read: "" ## post: "" ## access: "" ## } ## ## Other parameters are possible. See readers.conf(5) for all the ## details. Only one of newsgroups or read/post may be used in a single ## access group. ## ## If the connecting host is not matched by any hosts: parameter of any ## auth group, it will be denied access. auth groups assign an identity ## string to connections, access groups grant privileges to identity ## strings matched by their users: parameters. ## ## In all cases, the last match found is used, so put defaults first. ## ## For a news server that allows connections from anyone within a ## particular domain or IP address range, just uncomment the "local" auth ## group and the "local" access group below and adjust the hosts: and ## default: parameters of the auth group and the users: parameter of the ## access group for your local network and domain name. That's all there ## is to it. ## ## For more complicated configurations, read the comments on the examples ## and also see the examples and explanations in readers.conf(5). The ## examples in readers.conf(5) include setups that require the user to ## log in with a username and password (the example in this file only ## uses simple host-based authentication). ## ## NOTE: Unlike in previous versions of INN, nnrpd will now refuse any ## post from anyone to a moderated newsgroup that contains an Approved: ## header unless their access block has an access: key containing the ## "A" flag. This is to prevent abuse of moderated groups, but it means ## that if you support any newsgroup moderators, you need to make sure ## to add such a line to the access group that affects them. See the ## access group for localhost below for an example. # The only groups enabled by default (the rest of this file is # commented-out examples). This assigns the identity of to # the local machine auth "localhost" { hosts: "localhost, 127.0.0.1, ::1, stdin" default: "" } # Grant that specific identity access to read and post to any newsgroup # and allow it to post articles with Approved: headers to moderated # groups. access "localhost" { users: "" newsgroups: "*" access: RPA } # This auth group matches all connections from example.com or machines in # the example.com domain and gives them the identity @example.com. # Instead of using wildmat patterns to match machine names, you could also # put a wildmat pattern matching IP addresses or an IP range specified # using CIDR notation (like 10.10.10.0/24) here. #auth "local" { # hosts: "*.example.com, example.com" # default: "@example.com" #} # This auth group matches a subset of machines and assigns connections # from there an identity of "@example.com"; these systems should # only have read access, no posting privileges. #auth "read-only" { # hosts: "*.newuser.example.com" # default: "@example.com" #} # This auth group matches the systems at a guest institution that should # be allowed to read the example.events.* hierarchy but nothing else. #auth "events-only" { # hosts: "*.example.org" # default: "@example.org" #} # Finally, this auth group matches some particular systems which have been # abusing the server. Note that it doesn't assign them an identity at # all; the "empty" identity created in this fashion won't match any users: # parameters. Note also that it's last, so anything matching this entry # will take precedent over everything above it. #auth "abusers" { # hosts: "badguy-dsl.example.com, kiosk.public-access.example.com" #} # Now for the access groups. All of our access groups should have users: # parameters so there are no access groups that match connections without # an identity (such as are generated by the "abusers" entry above). # First, the default case of local users, who get to read and post to # everything. #access "local" { # users: "@example.com" # newsgroups: "*" #} # Now, the read-only folks, who only get to read everything. #access "read-only" { # users: "@example.com" # read: "*" #} # Finally, the events-only people who get to read and post but only to a # specific hierarchy. #access "events-only" { # users: "@example.org" # newsgroups: "example.events.*" #} inn-2.6.0/samples/buffindexed.conf.in0000644000175200017520000000116312575023702017124 0ustar iuliusiulius## $Id: buffindexed.conf.in 7651 2007-08-20 10:28:34Z iulius $ ## ## Configuration for the buffindexed overview method. ## ## It specifies the buffers that the buffindexed overview method ## should use. It is required if the server uses buffindexed (as ## configured by the ovmethod: parameter in inn.conf). ## See the buffindexed.conf man page for more information. ## ## The order of lines in this file is not important. ## Format: ## index(0-65535) : path to buffer file : ## length of buffer in kilobytes in decimal (1KB = 1024 bytes) 0:@SPOOLDIR@/overview/OV1:1536000 1:@SPOOLDIR@/overview/OV2:1536000 inn-2.6.0/samples/nnrpd_access.pl.in0000644000175200017520000001016412575023702016772 0ustar iuliusiulius#! /usr/bin/perl -w # fixscript will replace this line with code to load INN::Config ## ## Sample code for the nnrpd Perl access hooks. ## This file is loaded when a perl_access: parameter is reached in ## readers.conf. If it defines a sub named access, which will be ## called during processing of a perl_access: parameter. Attributes ## about the connection are passed to the program in the %attributes ## global variable. It should return a hash containing ## parameter-value pairs for the access group. If there is a problem, ## nnrpd will die and syslog the exact error. ## The default behavior of the following code is to look for nnrp.access ## in INN's configuration file directory and to attempt to implement about ## the same host-based access control as the previous nnrp.access code in ## earlier versions of INN. This may be useful for backward compatibility. ## This file cannot be run as a standalone script, although it would be ## worthwhile to add some code so that it could so that one could test the ## results of various authentication and connection queries from the ## command line. The #! line at the top is just so that fixscript will ## work. # This function is called when perl_access: is reached in readers.conf. # For details on all the information passed to it, see # ~news/doc/hook-perl. sub access { &loadnnrp($INN::Config::newsetc . '/nnrp.access'); return &checkhost($attributes{hostname}, $attributes{ipaddress}); } # Called at startup, this loads the nnrp.access file and converts it into a # convenient internal format for later queries. sub loadnnrp { my $file = shift; my ($block, $perm, $user, $pass); open (ACCESS, $file) or die "Could not open $file: $!\n"; local $_; while () { my %tmp; chomp; s/\#.*//; ($block, $perm, $user, $pass, $tmp{groups}) = split /:/; next unless (defined $tmp{groups}); # We don't support username/password entries, so be safe. next if ($user || $pass); # Change the wildmat pattern to a regex (this isn't thorough, as # some ranges won't be converted properly, but it should be good # enough for this purpose). if ($block !~ m%^(?:\d+\.){3}\d+/\d+$%) { $block =~ s/\./\\./g; $block =~ s/\?/./g; $block =~ s/\*/.*/g; } $tmp{block} = $block; $tmp{canread} = ($perm =~ /r/i); $tmp{canpost} = ($perm =~ /p/i); unshift(@hosts, { %tmp }); } close ACCESS; } # Given the hostname and IP address of a connecting host, use our @hosts # array constructed from nnrp.access and see what permissions that host has. sub checkhost { my ($host, $ip) = @_; my %return_hash; my $key; for $key (@hosts) { my ($read, $post) = ($key->{canread}, $key->{canpost}); # First check for CIDR-style blocks. if ($key->{block} =~ m%^(\d+\.\d+\.\d+\.\d+)/(\d+)$%) { my $block = unpack('N', pack('C4', split(/\./, $1))); my $mask = (0xffffffff << (32 - $2)) & 0xffffffff; $block = $block & $mask; my $packedip = unpack('N', pack('C4', split(/\./, $ip))); if (($packedip & $mask) == $block) { if ($read) { $return_hash{"read"} = $key->{groups}; } if ($post) { $return_hash{"post"} = $key->{groups}; } return %return_hash; } } if ($ip =~ /^$key->{block}$/) { if ($read) { $return_hash{"read"} = $key->{groups}; } if ($post) { $return_hash{"post"} = $key->{groups}; } return %return_hash; } if ($host =~ /^$key->{block}$/) { if ($read) { $return_hash{"read"} = $key->{groups}; } if ($post) { $return_hash{"post"} = $key->{groups}; } return %return_hash; } } # If we fell through to here, nothing matched, so we should deny # permissions. return %return_hash; } inn-2.6.0/samples/subscriptions0000644000175200017520000000016212575023702016215 0ustar iuliusiuliusnews.announce.newusers news.newusers.questions misc.test misc.test.moderated news.announce.newgroups news.answers inn-2.6.0/samples/innwatch.ctl0000644000175200017520000000427212575023702015710 0ustar iuliusiulius## $Id: innwatch.ctl 9629 2014-05-14 17:23:39Z iulius $ ## ## Sample control file for innwatch. ## ## Indicates what to run to test the state of the news system, and what ## to do about it. Format: ## !state!when!command!test!limit!command!reason/comment ## where ## Delimiter; pick from [,:@;?!]. ## State to enter if true. ## States we must be in to match. ## Command to run to test condition. ## Operator to use in test(1) command. ## Value to test against. ## Command for innwatch to perform; use exit, ## flush, go, pause, shutdown, skip, or throttle. ## Used in ctlinnd command (if needed). ## ## See the innwatch.ctl man page for more information. ## First, just exit innwatch if innd has gone away. !!! test -f ${LOCKS}/innd.pid && echo 0 || echo 1 ! eq ! 1 ! exit ! innd dead ## If another innwatch has started, exit. !!! cat ${LOCKS}/LOCK.${PROGNAME} ! ne ! $$ ! exit ! innwatch replaced ## Next test the load average. Above first threshold pause, above higher ## threshold throttle, below restart limit undo whatever was done. !load!load hiload! uptime | tr -d ,. | awk '{ print $(NF - 2) }' ! lt ! ${INNWATCHLOLOAD} ! go ! loadav !hiload!+ load! uptime | tr -d ,. | awk '{ print $(NF - 2) }' ! gt ! ${INNWATCHHILOAD} ! throttle ! loadav !load!+! uptime | tr -d ,. | awk '{ print $(NF - 2) }' ! gt ! ${INNWATCHPAUSELOAD} ! pause ! loadav ## Uncomment these to keep overchan backlog in check. Assumes your overchan ## feed is named 'overview!'. #::overblog:ctlinnd feedinfo overview!|awk 'NR==1{print $7}':lt:100000:go:overviewbacklog #:overblog:+:ctlinnd feedinfo overview!|awk 'NR==1{print $7}':gt:400000:throttle:overviewbacklog ## If load is OK, check space (and inodes) on various filesystems !!! ${INNDF} . ! lt ! ${INNWATCHSPOOLSPACE} ! throttle ! No space (spool) !!! ${INNDF} ${BATCH} ! lt ! ${INNWATCHBATCHSPACE} ! throttle ! No space (newsq) !!! ${INNDF} ${PATHDB} ! lt ! ${INNWATCHLIBSPACE} ! throttle ! No space (newslib) !!! ${INNDF} -i . ! lt ! ${INNWATCHSPOOLNODES} ! throttle ! No space (spool inodes) !!! test -d ${OVERVIEWDIR} && ${INNDF} ${OVERVIEWDIR} ! lt ! ${INNWATCHSPOOLSPACE} ! throttle ! No space (overview) inn-2.6.0/samples/innfeed.conf0000644000175200017520000001002312575023702015637 0ustar iuliusiulius## $Id: innfeed.conf 9923 2015-07-14 16:48:11Z iulius $ ## ## innfeed.conf - Configuration file for innfeed ## ## Format: ## key: value ## ## Values may be an integer, a floating-point number, ## C-style single-quoted characters, boolean, and strings. ## ## If a string value contains whitespace, or embedded quotes, ## or the comment character ("#"), then the whole string must ## be quoted with double quotes. Inside the quotes, standard ## C-escape sequences can be used (\t, \n, \r, \f, \v, \", \'). ## ## Blank lines are ignored. Everything after a '#' is ignored too. ## ## ## See the innfeed.conf(5) man page for a full description ## of the format. ## Global values. Not specific to any peer. These are optional, ## but if used, will override the compiled-in values. #news-spool: # Default is . #input-file: # Default is unset (channel mode). #pid-file: innfeed.pid # Relative to . #debug-level: 0 #debug-shrinking: false #fast-exit: false #use-mmap: true #log-file: innfeed.log # Relative to . #stdio-fdmax: 0 #log-time-format: "%a %b %d %H:%M:%S %Y" ## Uncomment the next line to include the contents ## of "testfile" at this point. #$INCLUDE testfile ## Other global parameters. #backlog-directory: innfeed # Relative to . #backlog-highwater: 5 #backlog-rotate-period: 60 #backlog-ckpt-period: 30 #backlog-newfile-period: 600 #dns-retry: 900 #dns-expire: 86400 #close-period: 86400 #gen-html: false # If true, status-file is relative to ; #status-file: innfeed.status # otherwise, it is relative to . #connection-stats: false #host-queue-highwater: 10 #stats-period: 600 #stats-reset: 43200 #initial-sleep: 2 #initial-reconnect-time: 30 #max-reconnect-time: 3600 ## Defaults for all peers. These all exist at ## global scope. Any of them can be redefined ## inside a peer or group definition. #article-timeout: 600 #response-timeout: 300 #initial-connections: 1 #max-connections: 2 #max-queue-size: 20 #streaming: true #no-check-high: 95.0 #no-check-low: 90.0 #no-check-filter: 50.0 #bindaddress: #bindaddress6: #port-number: 119 #force-ipv4: false #drop-deferred: false #min-queue-connection: false #backlog-limit: 0 #backlog-factor: 1.10 #backlog-limit-highwater: 0 #dynamic-method: 3 #dynamic-backlog-filter: 0.7 #dynamic-backlog-low: 20.0 #dynamic-backlog-high: 50.0 #no-backlog: false #backlog-feed-first: false ## Peers. #peer decwrl { # ip-name: news1.pa.dec.com #} #peer uunet { # ip-name: news.uunet.uu.net # max-connections: 10 #} ## Group peers together to give second level defaults. #group fast-sites { # max-connections: 7 # # peer data.ramona.vix.com { # # ip-name defaults to "data.ramona.vix.com". # streaming: false # } # # peer bb.home.vix.com { # ip-name: 192.5.5.33 # username: john # password: myPassword # } #} ## For imapfeed. # If imapfeed is used, the following parameters can # be set at global scope: # deliver-authname, deliver-password, deliver-username, # deliver-realm, deliver-rcpt-to, deliver-to-header. inn-2.6.0/samples/moderators0000644000175200017520000000241412575023702015467 0ustar iuliusiulius## $Id: moderators 8211 2008-12-06 12:56:46Z iulius $ ## ## moderators - Mailing addresses for moderators. ## ## Whenever possible, the master moderator database at moderators.isc.org ## should be used rather than adding specific entries to this file. The ## master database will list any publically propagated moderated group; ## changes should be sent to . ## ## Exceptions listed in this file are mostly hierarchies for which the ## master database isn't accurate or updated quickly enough. Local ## moderated newsgroups can also be added to this file. ## ## Format: ## : ## ## Shell-style newsgroup pattern or specific newsgroup. ## Mail address, "%s" becomes newgroup name with dots ## changed to dashes. ## ## The first matching entry is used. ## Public hierarchies with exceptions. aioe.*:%s-newsgroup@aioe.org fido7.*:%s@fido7.ru ffm.*:%s@moderators.arcornews.de fj.*:%s@moderators.fj-news.org medlux.*:%s@news.medlux.ru nl.*:%s@nl.news-admin.org perl.*:news-moderator-%s@perl.org relcom.*:%s@moderators.relcom.ru si.*:%s@arnes.si ukr.*:%s@sita.kiev.ua ## Direct all other public hierarchies to the master moderator database. *:%s@moderators.isc.org inn-2.6.0/samples/nnrpd_dynamic.py0000644000175200017520000001047212575023702016567 0ustar iuliusiulius## $Id: nnrpd_dynamic.py 8250 2008-12-23 12:00:46Z iulius $ ## ## This is a sample dynamic access module for the Python nnrpd hook. ## ## See the INN Python Filtering and Authentication Hooks documentation ## for more information. ## The perl_dynamic: parameter in readers.conf is used to load this script. ## ## An instance of DYNACCESS class is passed to nnrpd via the set_auth_hook() ## function imported from nnrpd. The following methods of that class ## are known to nnrpd: ## ## __init__() - Use this method to initialize your ## general variables or open a common ## database connection. May be omitted. ## dynamic_init() - Init function specific to ## authentication. May be omitted. ## dynamic(attributes) - Called whenever a reader requests either ## read or post access to a ## newsgroup. Returns None to grant ## access, or a non-empty string (which ## will be reported back to reader) ## otherwise. ## dynamic_close() - Called on nnrpd termination. Save ## your state variables or close a database ## connection. May be omitted. ## ## If there is a problem with return codes from any of these methods, then nnrpd ## will die and syslog the exact reason. ## ## There are also a few Python functions defined in nnrpd: ## ## set_auth_hook() - Called by nnrpd as this module is loaded. ## It is used to pass a reference to an ## instance of authentication class to nnrpd. ## syslog() - An equivalent replacement for regular syslog. ## One consideration for using it is to ## uniform nnrpd logging. ## Sample dynamic access class. It defines all dynamic access methods known ## to nnrpd. class DYNACCESS: """Provide dynamic access callbacks to nnrpd.""" def __init__(self): """This is a good place to initialize variables or open a database connection.""" syslog('notice', 'nnrpd dynamic access class instance created') def dynamic_init(self): """Called when this script is initialized.""" pass def dynamic(self, attributes): """Called when python_dynamic: is reached in the processing of readers.conf and a reader requests either read or post permission for particular newsgroup.""" # Just for debugging purposes. syslog('notice', 'n_a dynamic() invoked against type %s, hostname %s, ipaddress %s, interface %s, user %s' % ( \ attributes['type'], \ attributes['hostname'], \ attributes['ipaddress'], \ attributes['interface'], \ attributes['user'])) # Allow reading of any newsgroup but not posting. if 'post' == str(attributes['type']): syslog('notice', 'dynamic authorization access for post access denied') return "no posting for you" elif 'read' == str(attributes['type']): syslog('notice', 'dynamic authorization access for read access granted') return None else: syslog('notice', 'dynamic authorization access type is not known: %s' % attributes['type']) return "Internal error"; def dynamic_close(self): """Called on nnrpd termination.""" pass ## The rest is used to hook up the dynamic access module on nnrpd. It is unlikely ## you will ever need to modify this. ## Import functions exposed by nnrpd. This import must succeed, or nothing ## will work! from nnrpd import * ## Create a class instance. mydynaccess = DYNACCESS() ## ...and try to hook up on nnrpd. This would make auth object methods visible ## to nnrpd. try: set_auth_hook(mydynaccess) syslog('notice', "dynamic access module successfully hooked into nnrpd") except Exception, errmsg: # Syntax for Python 2.x. #except Exception as errmsg: # Syntax for Python 3.x. syslog('error', "Cannot obtain nnrpd hook for dynamic access method: %s" % errmsg[0]) inn-2.6.0/samples/news2mail.cf0000644000175200017520000000203112575023702015573 0ustar iuliusiulius## $Id: news2mail.cf 8198 2008-11-30 12:37:18Z iulius $ ## ## Sample news2mail configuration file. ## ## Format is: ## ## Newsfeeds-name List-to-address [List-sender-address] ## ## Newsfeeds-name: Name used in the newsfeeds file for the gateway feed ## (required). ## List-to-address: List address to send articles to (required). ## List-sender-address: Envelope-from address to use (a list member's ## address; optional). ## ## In newsfeeds, put an entry like: ## ## news2mail!:!*:Ac,Tc,Wn*:/news2mail ## ## and for each mailing-list, have an entry list like: ## ## list-big-red-ants/lists.ucsd.edu:!*,rec.pets.red-ants:Ap,Tm:news2mail! ## ## The site name used in the newfeeds entry for a mailing-list (above ## "list-big-red-ants") must be the same as the first field in an entry in ## this file. ## ## See the news2mail man page for more information. #list-big-red-ants big-red-ants@lists.ucsd.edu news+big-red-ants@local.news.server.org inn-2.6.0/samples/nnrpd_access_wrapper.py0000644000175200017520000000436612575023702020151 0ustar iuliusiulius## $Id: nnrpd_access_wrapper.py 8251 2008-12-23 12:22:03Z iulius $ ## ## Example wrapper for support of old Python authentication scripts, ## by Erik Klavon. ## ## This file contains a sample Python script which can be used to ## duplicate the behaviour of the old nnrppythonauth functionality. ## This script only supports access control. ## ## How to use this wrapper: ## - insert your authentication class into this file; ## - rename your authentication class OLDAUTH. ## ## See the INN Python Filtering and Authentication Hooks documentation ## for more information. ## The use of this file is *discouraged*. ## Old AUTH class. ## Insert your old auth class here. ## Do not include the code which sets the hook. ## Wrapper ACCESS class. It creates an instance of the old class and ## calls its methods. Arguments and return values are munged as ## needed to fit the new way of doing things. class MYACCESS: """Provide access callbacks to nnrpd.""" def access_init(self): self.old = OLDAUTH() def access(self, attributes): # Python 3.x uses memoryview(b'connect') because buffers # do not exist any longer. Note that the argument is # a bytes object. # attributes['type'] = memoryview(b'connect') attributes['type'] = buffer('connect') perm = (self.old).authenticate(attributes) result = dict({'users': '*'}) if perm[1] == 1: result['read'] = perm[3] if perm[2] == 1: result['post'] = perm[3] return result def access_close(self): (self.old).close() ## The rest is used to hook up the access module on nnrpd. It is unlikely ## you will ever need to modify this. ## Import functions exposed by nnrpd. This import must succeed, or nothing ## will work! from nnrpd import * ## Create a class instance. myaccess = MYACCESS() ## ...and try to hook up on nnrpd. This would make access object methods visible ## to nnrpd. try: set_auth_hook(myaccess) syslog('notice', "access module successfully hooked into nnrpd") except Exception, errmsg: # Syntax for Python 2.x. #except Exception as errmsg: # Syntax for Python 3.x. syslog('error', "Cannot obtain nnrpd hook for access method: %s" % errmsg[0]) inn-2.6.0/samples/nnrpd_auth.py0000644000175200017520000001062012575023702016077 0ustar iuliusiulius## $Id: nnrpd_auth.py 8639 2009-09-29 20:12:25Z iulius $ ## ## This is a sample authentication module for the Python nnrpd hook. ## ## See the INN Python Filtering and Authentication Hooks documentation ## for more information. ## The perl_auth: parameter in readers.conf is used to load this script. ## ## An instance of AUTH class is passed to nnrpd via the set_auth_hook() ## function imported from nnrpd. The following methods of that class ## are known to nnrpd: ## ## __init__() - Use this method to initialize your ## general variables or open a common ## database connection. May be omitted. ## authen_init() - Init function specific to ## authentication. May be omitted. ## authenticate(attributes) - Called when a python_auth statement ## is reached in the processing of ## readers.conf. Returns a response ## code, an error string and an ## optional string to appear in the ## logs as the username (make sure that ## such a message is properly encoded ## in UTF-8 so as to comply with the ## NNTP protocol). ## authen_close() - Called on nnrpd termination. Save ## your state variables or close a database ## connection. May be omitted. ## ## If there is a problem with return codes from any of these methods, then nnrpd ## will die and syslog the exact reason. ## ## There are also a few Python functions defined in nnrpd: ## ## set_auth_hook() - Called by nnrpd as this module is loaded. ## It is used to pass a reference to an ## instance of authentication class to nnrpd. ## syslog() - An equivalent replacement for regular syslog. ## One consideration for using it is to ## uniform nnrpd logging. ## Sample authentication class. It defines all auth methods known to nnrpd. class AUTH: """Provide authentication callbacks to nnrpd.""" def __init__(self): """This is a good place to initialize variables or open a database connection.""" # Create a list of NNTP codes to respond on authentication. self.authcodes = { 'ALLOWED': 281, 'DENIED': 481, 'ERROR': 403 } syslog('notice', 'nnrpd authentication class instance created') def authen_init(self): """Called when this script is initialized.""" pass def authenticate(self, attributes): """Called when python_auth: is encountered in readers.conf.""" # Just for debugging purposes. syslog('notice', 'n_a authenticate() invoked: hostname %s, ipaddress %s, interface %s, user %s' % ( \ attributes['hostname'], \ attributes['ipaddress'], \ attributes['interface'], \ attributes['user'])) # Do username password authentication. if 'foo' == str(attributes['user']) \ and 'foo' == str(attributes['pass']): syslog('notice', 'authentication by username succeeded') return (self.authcodes['ALLOWED'], 'No error', 'default_user') else: syslog('notice', 'authentication by username failed') return (self.authcodes['DENIED'], 'Access Denied!') def authen_close(self): """Called on nnrpd termination.""" pass ## The rest is used to hook up the auth module on nnrpd. It is unlikely ## you will ever need to modify this. ## Import functions exposed by nnrpd. This import must succeed, or nothing ## will work! from nnrpd import * ## Create a class instance. myauth = AUTH() ## ...and try to hook up on nnrpd. This would make auth object methods visible ## to nnrpd. try: set_auth_hook(myauth) syslog('notice', "authentication module successfully hooked into nnrpd") except Exception, errmsg: # Syntax for Python 2.x. #except Exception as errmsg: # Syntax for Python 3.x. syslog('error', "Cannot obtain nnrpd hook for authentication method: %s" % errmsg[0]) inn-2.6.0/samples/nocem.ctl0000644000175200017520000000152312575023702015172 0ustar iuliusiulius## $Id: nocem.ctl 9513 2013-07-26 20:30:29Z iulius $ ## ## Configuration file for perl-nocem(8). ## This file specifies the NoCeM issuers and notices you want to follow. ## ## Based on Rosalind Hengeveld's NoCeM Registry: ## ## ## ## Format: ## issuer:notice1,notice2 ## ## You also have to properly configure your newsfeeds file and ## your keyring situated in /pgp/ncmring.gpg. ## You may wish to review and change the policy below. ## ## See the perl-nocem man page for more information. alba-nocem@albasani.net:spam,Content-Based.Detlef-B,Content-Based.Selzer-M bleachbot@httrack.com:spam,site news@uni-berlin.de:Admincancel nl-cancel@a3.xs4all.nl:spam,mmf,binary nocem@aioe.org:aioe-spam nocem@arcor.de:spam pgpmoose@killfile.org:pgpmoose-forged-moderation inn-2.6.0/samples/motd.nnrpd0000644000175200017520000000323312575023702015373 0ustar iuliusiuliusHere are a few suggestions of messages to display to news readers accessing the server. The contents of this file is displayed in reply to the LIST MOTD command from readers. Just rename this file to "motd.nnrpd" and adjust its contents to whatever message of the day (encoded in UTF-8) you want to communicate. It is not an error if this file does not exist nor if it is empty. For more information, please have a look at the generic motd.news man page. Example 1 --------- Attention all users, This server will be down for scheduled upgrades on February 1st. It should be back up by 8:00 a.m. February 2nd. Any questions should be e-mailed to . Apologies for the disturbance. Example 2 --------- Dear customers, We are pleased to announce the creation of a support newsgroup, named "our.company.support". It was created on February 1st and is intended to receive your questions about the use of our services. Our support team will usually be responding within a couple of days. Please use this new newsgroup as the preferable means to contact us. Example 3 --------- This news server now supports TLS connections. Please configure your news reader to use TLS when connecting or authenticating. A tutorial is available in the "our.company.tutorials" newsgroup to help you do the change. Do not hesitate to ask for help in the "our.company.support" newsgroup if you encounter issues during the change. Starting from February 1st, unencrypted authentications (that is to say not using TLS) will be rejected. Example 4 --------- Support for a new SASL mechanism has been added: OPENID20 can now be used to authenticate. Feel free to use it! inn-2.6.0/samples/control.ctl0000644000175200017520000031453312575023702015561 0ustar iuliusiulius## control.ctl - Access control for control messages. ## Last modified: 2014-06-17 ## ## Based on rone's unified control.ctl file. ## ## For a web presentation of the information recorded here, as well as ## other useful information about Usenet hierarchies, please see: ## ## ## ## Please copy usenet-config@isc.org on any updates to this file so that ## it can be updated in the INN Subversion repository and on ftp.isc.org. ## For changes to a public hierarchy, please also post the changes to ## news.admin.hierarchies. ## ## The canonical version of this file can be found in the latest INN ## release and at ; these ## two files will be kept in sync. Please refer to the latest version of ## this file for the most up-to-date hierarchy control information and ## please use the latest version if you intend to carry all hierarchies. ## ## You may wish to review and change the policy for alt.*, free.*, ## it-alt.*, and oesterreich.* below before using this file on your ## server. ## ## Format: ## ::: ## ## Control message or "all" if it applies to all control ## messages. ## Pattern that must match the From line. ## Pattern that must match the newsgroup being newgroup'd ## or rmgroup'd (ignored for other messages). ## What to do: ## doit Perform action ## drop Ignore message ## log One line to error log ## mail Send mail to admin ## verify-pgp_userid Do PGP verification on user. ## All actions except drop and mail can be given a log ## location by following the action with an = and the ## log ("mail" says to mail the admin, an empty location ## tosses the log information, and a relative path xxx ## logs to $LOG/xxx.log). ## ## The *last* matching entry is used. See the expire.ctl(5) man page for ## complete information. ## ## This file follows the following policies: ## ## * Most unknown or invalid control messages no longer result in mail. ## This is due to the proliferation of forged control messages filling ## up mailboxes. Some news servers even get overwhelmed with trying to ## log failure, so unsigned control messages for hierarchies that use ## PGP are simply dropped. ## ## * The assumption is that you now have PGP on your system. If you ## don't, you should get it to help protect yourself against all the ## control message forgeries. See . ## PGP control message verification comes with all versions of INN since ## 1.5, but you will need to install either PGP or GnuPG; see the ## installation instructions for your news server. ## ## If for some reason you can't use PGP, search for the *PGP* comments ## and modify the control lines to change "verify-..." in the action ## field to "mail" or "doit=mail" or "doit=" or whatever you ## prefer (replacing with the name of an appropriate log ## file). ## ## * A number of hierarchies are for local use only but have leaked out ## into the general stream. In this config file, they are set so that ## the groups will be easy to remove, and are marked with a comment of ## *LOCAL* (for use by that organization only, not generally ## distributed), *DEFUNCT* (a hierarchy that's no longer used), or ## *PRIVATE* (should only be carried after making arrangements with the ## given contact address). Please delete all groups in those ## hierarchies from your server if you carry them, unless you've ## contacted the listed contact address and arranged a feed. ## ## If you have permission to carry any of the hierarchies so listed in ## this file, you should change the entries for those hierarchies ## below. ## ## * Some hierarchies are marked as *HISTORIC*. These hierarchies ## aren't entirely defunct, but they are very low-traffic, not widely ## read or carried, and may not be worth carrying. If you don't intend ## to carry them, comment out their entries. ## ## The comments of this file aren't in any formal or well-defined syntax, ## but they are meant to use a consistent syntax to allow eventual parsing ## by scripts into a better database format. Please follow the syntax of ## existing entries when providing new ones. The recognized "fields" are ## Contact (contact e-mail address), Admin group (the administrative group ## for the hierarchy), URL, Key URL (URL for PGP key), Key fingerprint, Key ## mail (address to mail for PGP key), and Syncable server (for actsync or ## a similar tool). ## ## Names used in this file that cannot be encoded in 7bit ASCII are in ## UTF-8. The only non-7bit-ASCII content is in comments. ## ------------------------------------------------------------------------- ## DEFAULT ## ------------------------------------------------------------------------- # Default to dropping control messages that aren't recognized to allow # people to experiment without inadvertently mailbombing news admins. all:*:*:drop ## ------------------------------------------------------------------------- ## CHECKGROUPS MESSAGES ## ------------------------------------------------------------------------- # Default to mailing all checkgroups messages to the administrator. checkgroups:*:*:mail ## ------------------------------------------------------------------------- ## MISCELLANEOUS CONTROL MESSAGES ## ------------------------------------------------------------------------- # Mostly only used for UUCP feeds, very rarely used these days. ihave:*:*:drop sendme:*:*:drop # Request to send a copy of the newsfeeds file, intended for mapping # projects. Almost never used for anything other than mailbombing now. sendsys:*:*:log=sendsys # Request to send the server's path entry. Not particularly useful. senduuname:*:*:log=senduuname # Request to send the server's version number. version:*:*:log=version ## ------------------------------------------------------------------------- ## NEWGROUP/RMGROUP MESSAGES ## ------------------------------------------------------------------------- ## Default (for any group) newgroup:*:*:mail rmgroup:*:*:mail ## A.BSU (*DEFUNCT* -- Ball State University, USA) # This hierarchy is defunct. Please remove it. newgroup:*:a.bsu.*:mail rmgroup:*:a.bsu.*:doit ## ACS & OSU (*LOCAL* -- Ohio State University, USA) # Contact: Albert J. School # Contact: Harpal Chohan # For local use only, contact the above address for information. newgroup:*:acs.*|osu.*:mail rmgroup:*:acs.*|osu.*:doit ## ADASS (Astronomical Data Analysis Software and Systems) # URL: http://iraf.noao.edu/iraf/web/adass_news.html checkgroups:news@iraf.noao.edu:adass.*:doit newgroup:news@iraf.noao.edu:adass.*:doit rmgroup:news@iraf.noao.edu:adass.*:doit ## AHN (Athens-Clarke County, Georgia, USA) checkgroups:greg@*.ucns.uga.edu:ahn.*:doit newgroup:greg@*.ucns.uga.edu:ahn.*:doit rmgroup:greg@*.ucns.uga.edu:ahn.*:doit ## AIOE (Aioe.org) # Contact: usenet@aioe.org # URL: http://news.aioe.org/hierarchy/ # Admin group: aioe.system # Key URL: http://news.aioe.org/hierarchy/aioe.txt # Key fingerprint: 2203 1AAC 51E7 C7FD 664F 1D80 90DF 6C71 2322 A7F8 # Syncable server: nntp.aioe.org # *PGP* See comment at top of file. newgroup:*:aioe.*:drop rmgroup:*:aioe.*:drop checkgroups:usenet@aioe.org:aioe.*:verify-usenet@aioe.org newgroup:usenet@aioe.org:aioe.*:verify-usenet@aioe.org rmgroup:usenet@aioe.org:aioe.*:verify-usenet@aioe.org ## AIR (*DEFUNCT* -- Stanford University, USA) # Contact: news@news.stanford.edu # This hierarchy is defunct. Please remove it. newgroup:*:air.*:mail rmgroup:*:air.*:doit ## AKR (Akron, Ohio, USA) checkgroups:red@redpoll.mrfs.oh.us:akr.*:doit newgroup:red@redpoll.mrfs.oh.us:akr.*:doit rmgroup:red@redpoll.mrfs.oh.us:akr.*:doit ## ALABAMA & HSV (Huntsville, Alabama, USA) # Contact: jpc@suespammers.org # Admin group: alabama.config # *PGP* See comment at top of file. newgroup:*:alabama.*|hsv.*:drop rmgroup:*:alabama.*|hsv.*:drop checkgroups:jpc@suespammers.org:alabama.*|hsv.*:verify-alabama-group-admin newgroup:jpc@suespammers.org:alabama.*|hsv.*:verify-alabama-group-admin rmgroup:jpc@suespammers.org:alabama.*|hsv.*:verify-alabama-group-admin ## ALIVE (*DEFUNCT* -- ?) # Contact: thijs@kink.xs4all.nl # This hierarchy is defunct. Please remove it. newgroup:*:alive.*:mail rmgroup:*:alive.*:doit ## ALT # # Accept all newgroups (except ones forged from Big 8 newgroup issuers, # who never issue alt.* control messages) and silently ignore all # rmgroups. # # What policy to use for alt.* groups varies widely from site to site. # For a small site, it is strongly recommended that this policy be changed # to drop all newgroups and rmgroups for alt.*. The local news admin can # then add new alt.* groups only on user request. Tons of alt.* newgroups # are sent out regularly with the intent more to create nonsense entries # in active files than to actually create a useable newsgroup. The admin # may still want to check the control message archive, as described below. # # Quality, user-desirable new groups can often be discovered by a quick # perusal of recent alt.* newgroup messages after discarding obvious junk # groups. One good initial filter is to check the archive of control # messages for a requested group to see if a syntactically valid newgroup # message was issued. Many of the junk control messages are invalid and # won't be archived, and many sites will only add alt.* groups with valid # control messages. To check the archive, see if: # # ftp://ftp.isc.org/pub/usenet/control/alt/.gz # # exists (replacing with the name of the group) and read the # first and last few control messages to see if the newsgroup should be # moderated. (Some alt.* groups that should be moderated are created # unmoderated by hijackers to try to damage the newsgroup.) # # Be aware that there is no official, generally accepted alt.* policy and # all information about alt.* groups available is essentially someone's # opinion, including these comments. There are nearly as many different # policies with regard to alt.* groups as there are Usenet sites. # newgroup:*:alt.*:doit newgroup:group-admin@isc.org:alt.*:drop newgroup:tale@*uu.net:alt.*:drop rmgroup:*:alt.*:drop ## AR (Argentina) checkgroups:jorge_f@nodens.fisica.unlp.edu.ar:ar.*:doit newgroup:jorge_f@nodens.fisica.unlp.edu.ar:ar.*:doit rmgroup:jorge_f@nodens.fisica.unlp.edu.ar:ar.*:doit ## ARC (*LOCAL* -- NASA Ames Research Center, USA) # Contact: news@arc.nasa.gov # For local use only, contact the above address for information. newgroup:*:arc.*:mail rmgroup:*:arc.*:doit ## ARKANE (Arkane Systems, UK) # Contact: newsbastard@arkane.demon.co.uk checkgroups:newsbastard@arkane.demon.co.uk:arkane.*:doit newgroup:newsbastard@arkane.demon.co.uk:arkane.*:doit rmgroup:newsbastard@arkane.demon.co.uk:arkane.*:doit ## AT (Austria) # URL: http://www.usenet.at/ # Admin group: at.usenet.gruppen # Key URL: http://www.usenet.at/pgpkey.asc # *PGP* See comment at top of file. newgroup:*:at.*:drop rmgroup:*:at.*:drop checkgroups:control@usenet.backbone.at:at.*:verify-control@usenet.backbone.at newgroup:control@usenet.backbone.at:at.*:verify-control@usenet.backbone.at rmgroup:control@usenet.backbone.at:at.*:verify-control@usenet.backbone.at ## AUS (Australia) # Contact: ausadmin@aus.news-admin.org # URL: http://aus.news-admin.org/ # Admin group: aus.net.news # Key URL: http://aus.news-admin.org/ausadmin.asc # *PGP* See comment at top of file. newgroup:*:aus.*:drop rmgroup:*:aus.*:drop checkgroups:ausadmin@aus.news-admin.org:aus.*:verify-ausadmin@aus.news-admin.org newgroup:ausadmin@aus.news-admin.org:aus.*:verify-ausadmin@aus.news-admin.org rmgroup:ausadmin@aus.news-admin.org:aus.*:verify-ausadmin@aus.news-admin.org ## AUSTIN (Austin, Texas, USA) # URL: http://frenzy.austin.tx.us/austin/ # Admin group: austin.usenet.config checkgroups:chip@unicom.com:austin.*:doit checkgroups:fletcher@cs.utexas.edu:austin.*:doit checkgroups:pug@pug.net:austin.*:doit newgroup:chip@unicom.com:austin.*:doit newgroup:fletcher@cs.utexas.edu:austin.*:doit newgroup:pug@pug.net:austin.*:doit rmgroup:chip@unicom.com:austin.*:doit rmgroup:fletcher@cs.utexas.edu:austin.*:doit rmgroup:pug@pug.net:austin.*:doit ## AZ (Arizona, USA) checkgroups:system@asuvax.eas.asu.edu:az.*:doit newgroup:system@asuvax.eas.asu.edu:az.*:doit rmgroup:system@asuvax.eas.asu.edu:az.*:doit ## BA (San Francisco Bay Area, USA) # Contact: ba-mod@panix.com # URL: http://www.panix.com/~babm/ba/ # Admin group: ba.config # Key URL: http://www.panix.com/~babm/ba/ba-mod.asc # Key fingerprint: 3C B6 7A 3B 34 53 C0 58 D2 FB 65 E8 E9 6F CD 46 # *PGP* See comment at top of file. newgroup:*:ba.*:drop rmgroup:*:ba.*:drop checkgroups:ba-mod@panix.com:ba.*:verify-ba.config newgroup:ba-mod@panix.com:ba.*:verify-ba.config rmgroup:ba-mod@panix.com:ba.*:verify-ba.config ## BACKBONE (*LOCAL* -- ruhr.de/ruhrgebiet.individual.net in Germany) # Contact: admin@ruhr.de # For local use only, contact the above address for information. newgroup:*:backbone.*:mail rmgroup:*:backbone.*:doit ## BC (British Columbia, Canada) checkgroups:bc_van_usenet@fastmail.ca:bc.*:doit newgroup:bc_van_usenet@fastmail.ca:bc.*:doit rmgroup:bc_van_usenet@fastmail.ca:bc.*:doit ## BDA (German groups?) checkgroups:news@*netuse.de:bda.*:doit newgroup:news@*netuse.de:bda.*:doit rmgroup:news@*netuse.de:bda.*:doit ## BE (Belgique/Belgie/Belgien/Belgium) # Contact: be-hierarchy-admin@usenet.be # URL: http://usenet.be/ # Admin group: be.announce # Key URL: http://usenet.be/be.announce.newgroups.asc # Key fingerprint: 30 2A 45 94 70 DE 1F D5 81 8C 58 64 D2 F7 08 71 # *PGP* See comment at top of file. newgroup:*:be.*:drop rmgroup:*:be.*:drop checkgroups:group-admin@usenet.be:be.*:verify-be.announce.newgroups newgroup:group-admin@usenet.be:be.*:verify-be.announce.newgroups rmgroup:group-admin@usenet.be:be.*:verify-be.announce.newgroups ## BELWUE (Landeshochschulnetz Baden-Wuerttemberg, Germany) # Contact: news@belwue.de # URL: http://www.belwue.de/produkte/dienste/dienste-usenet1.html # Admin group: belwue.infos # Key URL: http://www.belwue.de/fileadmin/belwue/Dokumente/Newsserver/belwue-hir-control.asc # Key fingerprint: 2E FF 2B 8A 89 1F E5 AA 6F 89 67 24 50 B5 42 D9 # *PGP* See comment at top of file. newgroup:*:belwue.*:drop rmgroup:*:belwue.*:drop checkgroups:news@news.belwue.de:belwue.*:verify-belwue-hir-control newgroup:news@news.belwue.de:belwue.*:verify-belwue-hir-control rmgroup:news@news.belwue.de:belwue.*:verify-belwue-hir-control ## BERMUDA (Bermuda) checkgroups:news@*ibl.bm:bermuda.*:doit newgroup:news@*ibl.bm:bermuda.*:doit rmgroup:news@*ibl.bm:bermuda.*:doit ## BEST (*LOCAL* -- Best Internet Communications, Inc.) # Contact: news@best.net # For local use only, contact the above address for information. newgroup:*:best.*:mail rmgroup:*:best.*:doit ## BIONET (Biology Network) # URL: http://www.bio.net/ # Admin group: bionet.general # Key fingerprint: EB C0 F1 BA 26 0B C6 D6 FB 8D ED C4 AE 5D 10 54 # *PGP* See comment at top of file. newgroup:*:bionet.*:drop rmgroup:*:bionet.*:drop checkgroups:Biosci-control-key@net.bio.net:bionet.*:verify-Biosci-control-key@net.bio.net newgroup:Biosci-control-key@net.bio.net:bionet.*:verify-Biosci-control-key@net.bio.net rmgroup:Biosci-control-key@net.bio.net:bionet.*:verify-Biosci-control-key@net.bio.net ## BIRK (*LOCAL* -- University of Oslo, Norway) # Contact: birk-admin@ping.uio.no # For local use only, contact the above address for information. newgroup:*:birk.*:mail rmgroup:*:birk.*:doit ## BIT (Gatewayed Mailing lists) # URL: http://www.newsadmin.com/bit/bit.htm # Admin group: bit.admin # *PGP* See comment at top of file. newgroup:*:bit.*:drop rmgroup:*:bit.*:drop checkgroups:bit@newsadmin.com:bit.*:verify-bit@newsadmin.com newgroup:bit@newsadmin.com:bit.*:verify-bit@newsadmin.com rmgroup:bit@newsadmin.com:bit.*:verify-bit@newsadmin.com ## BIZ (Business Groups) checkgroups:edhew@xenitec.on.ca:biz.*:doit newgroup:edhew@xenitec.on.ca:biz.*:doit rmgroup:edhew@xenitec.on.ca:biz.*:doit ## BLGTN (Bloomington, In, USA) checkgroups:control@news.bloomington.in.us:blgtn.*:doit newgroup:control@news.bloomington.in.us:blgtn.*:doit rmgroup:control@news.bloomington.in.us:blgtn.*:doit ## BLN (Berlin, Germany) # Contact: news@fu-berlin.de # URL: ftp://ftp.fu-berlin.de/doc/news/bln/bln # Admin group: bln.net.news checkgroups:news@*fu-berlin.de:bln.*:doit newgroup:news@*fu-berlin.de:bln.*:doit rmgroup:news@*fu-berlin.de:bln.*:doit ## BNE (Brisbane, Australia) # Contact: ausadmin@aus.news-admin.org # URL: http://bne.news-admin.org/ # Key URL: http://aus.news-admin.org/ausadmin.asc # *PGP* See comment at top of file. newgroup:*:bne.*:drop rmgroup:*:bne.*:drop checkgroups:ausadmin@aus.news-admin.org:bne.*:verify-ausadmin@aus.news-admin.org newgroup:ausadmin@aus.news-admin.org:bne.*:verify-ausadmin@aus.news-admin.org rmgroup:ausadmin@aus.news-admin.org:bne.*:verify-ausadmin@aus.news-admin.org ## BOFH (*PRIVATE* -- Bastard Operator From Hell) # Contact: bofh-control@lists.killfile.org # Key fingerprint: 40B5 8A56 1E00 6152 083E 38B3 CEF5 6980 7DC1 A266 # For private use only, contact the above address for information. # *PGP* See comment at top of file. newgroup:*:bofh.*:drop rmgroup:*:bofh.*:drop # The following three lines are only for authorized bofh.* sites. #checkgroups:bofh-control@killfile.org:bofh.*:verify-bofh-control@lists.killfile.org #newgroup:bofh-control@killfile.org:bofh.*:verify-bofh-control@lists.killfile.org #rmgroup:bofh-control@killfile.org:bofh.*:verify-bofh-control@lists.killfile.org ## CA (California, USA) # Contact: ikluft@thunder.sbay.org # URL: http://www.sbay.org/ca/ checkgroups:ikluft@thunder.sbay.org:ca.*:doit newgroup:ikluft@thunder.sbay.org:ca.*:doit rmgroup:ikluft@thunder.sbay.org:ca.*:doit ## CAIS (*LOCAL* -- Capital Area Internet Services) # Contact: news@cais.com # For local use only, contact the above address for information. newgroup:*:cais.*:mail rmgroup:*:cais.*:doit ## CALSTATE (California State University) checkgroups:*@*calstate.edu:calstate.*:doit newgroup:*@*calstate.edu:calstate.*:doit rmgroup:*@*calstate.edu:calstate.*:doit ## CANB (Canberra, Australia) # Contact: ausadmin@aus.news-admin.org # URL: http://canb.news-admin.org/ # Key URL: http://aus.news-admin.org/ausadmin.asc # *PGP* See comment at top of file. newgroup:*:canb.*:drop rmgroup:*:canb.*:drop checkgroups:ausadmin@aus.news-admin.org:canb.*:verify-ausadmin@aus.news-admin.org newgroup:ausadmin@aus.news-admin.org:canb.*:verify-ausadmin@aus.news-admin.org rmgroup:ausadmin@aus.news-admin.org:canb.*:verify-ausadmin@aus.news-admin.org ## CAPDIST (Albany, The Capital District, New York, USA) checkgroups:danorton@albany.net:capdist.*:doit newgroup:danorton@albany.net:capdist.*:doit rmgroup:danorton@albany.net:capdist.*:doit ## CARLETON (Carleton University, Canada) newgroup:news@cunews.carleton.ca:carleton.*:doit newgroup:news@cunews.carleton.ca:carleton*class.*:mail rmgroup:news@cunews.carleton.ca:carleton.*:doit ## CD-ONLINE (*LOCAL* -- ?) # Contact: newsmaster@worldonline.nl # For local use only, contact the above address for information. newgroup:*:cd-online.*:mail rmgroup:*:cd-online.*:doit ## CENTRAL (*LOCAL* -- The Internet Company of New Zealand, Wellington, NZ) # Contact: usenet@iconz.co.nz # For local use only, contact the above address for information. newgroup:*:central.*:mail rmgroup:*:central.*:doit ## CERN (*PRIVATE* -- CERN European Laboratory for Particle Physics) # Contact: Dietrich Wiegandt # For private use only, contact the above address for information. newgroup:News.Support@cern.ch:cern.*:mail rmgroup:News.Support@cern.ch:cern.*:doit ## CH (Switzerland) # Contact: ch-news-admin@use-net.ch # URL: http://www.use-net.ch/Usenet/ # Key URL: http://www.use-net.ch/Usenet/adminkey.html # Key fingerprint: 71 80 D6 8C A7 DE 2C 70 62 4A 48 6E D9 96 02 DF # *PGP* See comment at top of file. newgroup:*:ch.*:drop rmgroup:*:ch.*:drop checkgroups:felix.rauch@nice.ch:ch.*:verify-ch-news-admin@use-net.ch newgroup:felix.rauch@nice.ch:ch.*:verify-ch-news-admin@use-net.ch rmgroup:felix.rauch@nice.ch:ch.*:verify-ch-news-admin@use-net.ch ## CHAVEN (*LOCAL* -- Celestian Haven ISP, Midwest, USA) # Contact: news@chaven.com # For local use only, contact the above address for information. newgroup:*:chaven.*:mail rmgroup:*:chaven.*:doit ## CHI (Chicago, USA) # URL: http://lull.org/pub/chi-newsgroup-faq checkgroups:lisbon@*chi.il.us:chi.*:doit newgroup:lisbon@*chi.il.us:chi.*:doit rmgroup:lisbon@*chi.il.us:chi.*:doit ## CHILE (Chile and Chilean affairs) # Contact: mod-cga@usenet.cl # URL: http://www.usenet.cl/ # Admin group: chile.grupos.anuncios checkgroups:mod-cga@*usenet.cl:chile.*:doit checkgroups:mod-cga@*farah.cl:chile.*:doit newgroup:mod-cga@*usenet.cl:chile.*:doit newgroup:mod-cga@*farah.cl:chile.*:doit rmgroup:mod-cga@*usenet.cl:chile.*:doit rmgroup:mod-cga@*farah.cl:chile.*:doit ## CHINESE (China and Chinese language groups) checkgroups:pinghua@stat.berkeley.edu:chinese.*:doit newgroup:pinghua@stat.berkeley.edu:chinese.*:doit rmgroup:pinghua@stat.berkeley.edu:chinese.*:doit ## CHRISTNET (Christian Discussion) checkgroups:news@fdma.com:christnet.*:doit newgroup:news@fdma.com:christnet.*:doit rmgroup:news@fdma.com:christnet.*:doit ## CL (*PRIVATE* -- CL-Netz, German) # Contact: koordination@cl-netz.de # URL: http://www.cl-netz.de/ # Key URL: http://www.cl-netz.de/control.txt # For private use only, contact above address for questions. # *PGP* See comment at top of file. newgroup:*:cl.*:drop rmgroup:*:cl.*:doit # The following three lines are only for authorized cl.* sites. #checkgroups:koordination@cl-netz.de:cl.*:verify-cl.netz.infos #newgroup:koordination@cl-netz.de:cl.*:verify-cl.netz.infos #rmgroup:koordination@cl-netz.de:cl.*:verify-cl.netz.infos ## CLARI (*PRIVATE* -- Features and News, available on a commercial basis) # Contact: support@clari.net # Admin group: clari.net.admin # Key URL: http://www.clari.net/tech/clarikey.txt # For private use only, contact the above address for information. # *PGP* See comment at top of file. newgroup:*:clari.*:drop rmgroup:*:clari.*:drop newgroup:cl*@clarinet.com:clari.*:mail rmgroup:cl*@clarinet.com:clari.*:verify-ClariNet.Group ## CMI (*LOCAL* -- Champaign County, IL, USA) # Contact: news@ks.uiuc.edu # For local use only, contact the above address for information. newgroup:*:cmi.*:mail rmgroup:*:cmi.*:doit ## CMU (*LOCAL* -- Carnegie-Mellon University, Pennsylvania, USA) # Contact: Daniel Edward Lovinger # For local use only, contact the above address for information. newgroup:*:cmu.*:mail rmgroup:*:cmu.*:doit ## CN (China) # URL: http://news.yaako.com/ # Admin group: cn.announce # Key fingerprint: 62 97 EE 33 F7 16 25 C1 A4 9E 47 BA C5 3E 5E 9E # *PGP* See comment at top of file. newgroup:*:cn.*:drop rmgroup:*:cn.*:drop checkgroups:control@bentium.com:cn.*:verify-cn.admin.news.announce newgroup:control@bentium.com:cn.*:verify-cn.admin.news.announce rmgroup:control@bentium.com:cn.*:verify-cn.admin.news.announce ## CN.BBS (China) # URL: http://bbs.cn.news-admin.org/ # Admin group: cn.bbs.admin.announce # *PGP* See comment at top of file. newgroup:*:cn.bbs.*:drop rmgroup:*:cn.bbs.*:drop checkgroups:control@cn-bbs.org:cn.bbs.*:verify-cn.bbs.admin.announce newgroup:control@cn-bbs.org:cn.bbs.*:verify-cn.bbs.admin.announce rmgroup:control@cn-bbs.org:cn.bbs.*:verify-cn.bbs.admin.announce ## CO (Colorado, USA) # Contact: coadmin@boyznoyz.com (Bill of Denver) checkgroups:coadmin@boyznoyz.com:co.*:doit newgroup:coadmin@boyznoyz.com:co.*:doit rmgroup:coadmin@boyznoyz.com:co.*:doit ## CODEWARRIOR (CodeWarrior discussion) checkgroups:news@supernews.net:codewarrior.*:doit newgroup:news@supernews.net:codewarrior.*:doit rmgroup:news@supernews.net:codewarrior.*:doit ## COMP, HUMANITIES, MISC, NEWS, REC, SCI, SOC, TALK (The Big Eight) # Contact: board@big-8.org # URL: http://www.big-8.org/ # Admin group: news.announce.newgroups # Key fingerprint: F5 35 58 D3 55 64 10 14 07 C6 95 53 13 6F D4 07 # *PGP* See comment at top of file. newgroup:*:comp.*|humanities.*|misc.*|news.*|rec.*|sci.*|soc.*|talk.*:drop rmgroup:*:comp.*|humanities.*|misc.*|news.*|rec.*|sci.*|soc.*|talk.*:drop checkgroups:group-admin@isc.org:comp.*|humanities.*|misc.*|news.*|rec.*|sci.*|soc.*|talk.*:verify-news.announce.newgroups newgroup:group-admin@isc.org:comp.*|humanities.*|misc.*|news.*|rec.*|sci.*|soc.*|talk.*:verify-news.announce.newgroups rmgroup:group-admin@isc.org:comp.*|humanities.*|misc.*|news.*|rec.*|sci.*|soc.*|talk.*:verify-news.announce.newgroups ## COMPUTER42 (Computer 42, Germany) # Contact: Dirk Schmitt checkgroups:news@computer42.org:computer42.*:doit newgroup:news@computer42.org:computer42.*:doit rmgroup:news@computer42.org:computer42.*:doit ## CONCORDIA (Concordia University, Montreal, Canada) # Contact: newsmaster@concordia.ca # URL: General University info at http://www.concordia.ca/ checkgroups:news@newsflash.concordia.ca:concordia.*:doit newgroup:news@newsflash.concordia.ca:concordia.*:doit rmgroup:news@newsflash.concordia.ca:concordia.*:doit ## COURTS (*DEFUNCT* -- Court discussion) # Contact: trier@ins.cwru.edu # This hierarchy is defunct. Please remove it. newgroup:*:courts.*:mail rmgroup:*:courts.*:doit ## CPCUIIA (Chartered Prop. Casulty Underwriter/Insurance Institute of America) # Contact: miller@cpcuiia.org # URL: http://www.aicpcu.org/ checkgroups:miller@cpcuiia.org:cpcuiia.*:doit newgroup:miller@cpcuiia.org:cpcuiia.*:doit rmgroup:miller@cpcuiia.org:cpcuiia.*:doit ## CU (*LOCAL* -- University of Colorado) # Contact: Doreen Petersen # For local use only, contact the above address for information. newgroup:*:cu.*:mail rmgroup:*:cu.*:doit ## CUHK (*LOCAL* -- Chinese University of Hong Kong) # Contact: shlam@ie.cuhk.edu.hk (Alan S H Lam) # For local use only, contact the above address for information. newgroup:*:cuhk.*:mail rmgroup:*:cuhk.*:doit ## CZ (Czech Republic) # URL: ftp://ftp.vslib.cz/pub/news/config/cz/newsgroups (text) checkgroups:petr.kolar@vslib.cz:cz.*:doit newgroup:petr.kolar@vslib.cz:cz.*:doit rmgroup:petr.kolar@vslib.cz:cz.*:doit ## DC (Washington, D.C., USA) checkgroups:news@mattress.atww.org:dc.*:doit newgroup:news@mattress.atww.org:dc.*:doit rmgroup:news@mattress.atww.org:dc.*:doit ## DE (German language) # Contact: moderator@dana.de # URL: http://www.dana.de/ # Admin group: de.admin.news.announce # Key URL: http://www.dana.de/pgp/dana.txt # Key fingerprint: 5B B0 52 88 BF 55 19 4F 66 7D C2 AE 16 26 28 25 # *PGP* See comment at top of file. newgroup:*:de.*:drop rmgroup:*:de.*:drop checkgroups:moderator@dana.de:de.*:verify-de.admin.news.announce newgroup:moderator@dana.de:de.*:verify-de.admin.news.announce rmgroup:moderator@dana.de:de.*:verify-de.admin.news.announce ## DE.ALT (German language alternative hierarchy) # *PGP* See comment at top of file. newgroup:*:de.alt.*:doit rmgroup:moderator@dana.de:de.alt.*:verify-de.admin.news.announce ## DEMON (Demon Internet, UK) # Contact: newsmaster@demon.net # Admin group: demon.news # Key URL: ftp://ftp.demon.co.uk/pub/news/doc/demon.news.txt # *PGP* See comment at top of file. newgroup:*:demon.*:drop rmgroup:*:demon.*:drop checkgroups:newsmaster@demon.net:demon.*:verify-demon.news newgroup:newsmaster@demon.net:demon.*:verify-demon.news rmgroup:newsmaster@demon.net:demon.*:verify-demon.news ## DFW (Dallas/Fort Worth, Texas, USA) # URL: http://www.cirr.com/dfw/ # Admin group: dfw.usenet.config checkgroups:eric@*cirr.com:dfw.*:doit newgroup:eric@*cirr.com:dfw.*:doit rmgroup:eric@*cirr.com:dfw.*:doit ## DICTATOR (Dictator's Handbook) # Contact: news@dictatorshandbook.net # URL: http://dictatorshandbook.net/usenet/usenetadmin.html # Admin group: dictator.announce # Key URL: http://www.dictatorshandbook.net/usenet/news-public.key # Key fingerprint: 5C0A 741A F931 D79A D9E9 BBB7 4406 4481 91ED C5F2 # *PGP* See comment at top of file. newgroup:*:dictator.*:drop rmgroup:*:dictator.*:drop checkgroups:news@dictatorshandbook.net:dictator.*:verify-news@dictatorshandbook.net newgroup:news@dictatorshandbook.net:dictator.*:verify-news@dictatorshandbook.net rmgroup:news@dictatorshandbook.net:dictator.*:verify-news@dictatorshandbook.net ## DK (Denmark) # URL: http://www.usenet.dk/dk-admin/ # Key URL: http://www.usenet.dk/dk-admin/pubkey.html # Key fingerprint: 7C B2 C7 50 F3 7D 5D 73 8C EE 2E 3F 55 80 72 FF # *PGP* See comment at top of file. newgroup:*:dk.*:drop rmgroup:*:dk.*:drop checkgroups:news@news.dknet.dk:dk.*:verify-news@news.dknet.dk newgroup:news@news.dknet.dk:dk.*:verify-news@news.dknet.dk rmgroup:news@news.dknet.dk:dk.*:verify-news@news.dknet.dk ## DUKE (*LOCAL* -- Duke University, USA) # Contact: news@newsgate.duke.edu # For local use only, contact the above address for information. newgroup:*:duke.*:mail rmgroup:*:duke.*:doit ## EASYNET (*HISTORIC* -- Easynet PLC, UK) # # This hierarchy is not entirely defunct, but it receives very little # traffic and is included primarily for the sake of completeness. # # Contact: Christiaan Keet # Admin group: easynet.support # *PGP* See comment at top of file. newgroup:*:easynet.*:drop rmgroup:*:easynet.*:drop checkgroups:newsmaster@easynet.net:easynet.*:verify-easynet.news newgroup:newsmaster@easynet.net:easynet.*:verify-easynet.news rmgroup:newsmaster@easynet.net:easynet.*:verify-easynet.news ## EE (Estonia) # Contact: usenet@news.ut.ee # URL: http://news.ut.ee/ # Key URL: http://news.ut.ee/pubkey.asc # *PGP* See comment at top of file. newgroup:*:ee.*:drop rmgroup:*:ee.*:drop checkgroups:news@news.ut.ee:ee.*:verify-ee.news newgroup:news@news.ut.ee:ee.*:verify-ee.news rmgroup:news@news.ut.ee:ee.*:verify-ee.news ## EFN & EUG (*HISTORIC* -- Eugene Free Computer Network, Eugene/Springfield, Oregon, USA) # # This hierarchy is not entirely defunct, but it receives very little # traffic and is included primarily for the sake of completeness. # # Admin group: eug.config # *PGP* See comment at top of file. newgroup:*:efn.*|eug.*:drop rmgroup:*:efn.*|eug.*:drop checkgroups:newsadmin@efn.org:efn.*|eug.*:verify-eug.config newgroup:newsadmin@efn.org:efn.*|eug.*:verify-eug.config rmgroup:newsadmin@efn.org:efn.*|eug.*:verify-eug.config ## EHIME-U (? University, Japan ?) checkgroups:news@cc.nias.ac.jp:ehime-u.*:doit checkgroups:news@doc.dpc.ehime-u.ac.jp:ehime-u.*:doit newgroup:news@cc.nias.ac.jp:ehime-u.*:doit newgroup:news@doc.dpc.ehime-u.ac.jp:ehime-u.*:doit rmgroup:news@cc.nias.ac.jp:ehime-u.*:doit rmgroup:news@doc.dpc.ehime-u.ac.jp:ehime-u.*:doit ## ENGLAND (England) # Contact: admin@england.news-admin.org # Admin group: england.news.policy # Key fingerprint: DA 3E C2 01 46 E5 61 CB A2 43 09 CA 13 6D 31 1F # *PGP* See comment at top of file. newgroup:*:england.*:drop rmgroup:*:england.*:drop checkgroups:admin@england.news-admin.org:england.*:verify-england-usenet newgroup:admin@england.news-admin.org:england.*:verify-england-usenet rmgroup:admin@england.news-admin.org:england.*:verify-england-usenet ## ES (Spain) # Contact: moderador@corus-es.org # URL: http://www.corus-es.org/docs/es_newsadmins_faq.txt # Admin group: es.news.anuncios # Key URL: http://www.corus-es.org/docs/esnews.asc # *PGP* See comment at top of file. newgroup:*:es.*:drop rmgroup:*:es.*:drop checkgroups:moderador@corus-es.org:es.*:verify-es.news newgroup:moderador@corus-es.org:es.*:verify-es.news rmgroup:moderador@corus-es.org:es.*:verify-es.news ## ESP (Spanish-language newsgroups) # Contact: # URL: http://ennui.org/esp/ # Key URL: http://ennui.org/esp/mod-ena.asc # *PGP* See comment at top of file. newgroup:*:esp.*:drop rmgroup:*:esp.*:drop checkgroups:mod-ena@ennui.org:esp.*:verify-esp.news.administracion newgroup:mod-ena@ennui.org:esp.*:verify-esp.news.administracion rmgroup:mod-ena@ennui.org:esp.*:verify-esp.news.administracion ## ETERNAL-SEPTEMBER (Eternal-September Project) # Contact: news@eternal-september.org # URL: http://www.eternal-september.org/ # Key URL: http://www.eternal-september.org/control/pgpkey.txt # Key fingerprint: B7 9A 2B C6 7D 5A FF 79 18 E2 AC 91 4B C1 25 F1 # *PGP* See comment at top of file. newgroup:*:eternal-september.*:drop rmgroup:*:eternal-september.*:drop checkgroups:news@eternal-september.org:eternal-september.*:verify-news@eternal-september.org newgroup:news@eternal-september.org:eternal-september.*:verify-news@eternal-september.org rmgroup:news@eternal-september.org:eternal-september.*:verify-news@eternal-september.org ## EUNET (Europe) checkgroups:news@noc.eu.net:eunet.*:doit newgroup:news@noc.eu.net:eunet.*:doit rmgroup:news@noc.eu.net:eunet.*:doit ## EUROPA (Europe) # URL: http://www.europa.usenet.eu.org/ # Admin group: europa.usenet.admin # Key URL: http://www.europa.usenet.eu.org/pgp/index.html # Key fingerprint: 3A 05 A8 49 FB 16 29 25 75 E3 DE BB 69 E0 1D B4 # *PGP* See comment at top of file. newgroup:*:europa.*:drop rmgroup:*:europa.*:drop checkgroups:group-admin@usenet.eu.org:europa.*:verify-group-admin@usenet.eu.org newgroup:group-admin@usenet.eu.org:europa.*:verify-group-admin@usenet.eu.org rmgroup:group-admin@usenet.eu.org:europa.*:verify-group-admin@usenet.eu.org ## EXAMPLE (*RESERVED* -- For use in examples) # # The example.* hierarchy is reserved by RFC 5536 and MUST NOT be used # for regular newsgroups. It is intended for use in examples, standards # documents, and similar places to avoid clashes with real newsgroup # names. # newgroup:*:example.*:drop rmgroup:*:example.*:drop ## FA (Gated mailing lists) # # This hierarchy was removed in the Great Renaming of 1988. # # A site in Norway is currently (as of 2002) gatewaying various mailing # lists into fa.* newsgroups, but that site does not appear to be issuing # any control messages for those groups. # # This hierarchy is still in use, but it has no active maintainer. # Control messages for this hierarchy should not be honored without # confirming that the sender is the new hierarchy maintainer. ## FFM (Frankfurt/M., Germany) # URL: http://ffm.arcornews.de/ # Admin group: ffm.admin # Key URL: ftp://ftp.arcor-online.net/pub/news/PGPKEY.FFM # Key fingerprint: 53 A0 82 62 6F C7 81 C9 CF 53 AB 00 A3 F8 C2 11 # *PGP* See comment at top of file. newgroup:*:ffm.*:drop rmgroup:*:ffm.*:drop checkgroups:ffm.admin@arcor.de:ffm.*:verify-ffm.admin newgroup:ffm.admin@arcor.de:ffm.*:verify-ffm.admin rmgroup:ffm.admin@arcor.de:ffm.*:verify-ffm.admin ## FIDO (FidoNet) # # This hierarchy is still in use, but it has no active maintainer. # Control messages for this hierarchy should not be honored without # confirming that the sender is the new hierarchy maintainer. ## FIDO.BELG (Belgian FidoNet) # Admin group: fido.belg.news # *PGP* See comment at top of file. newgroup:*:fido.belg.*:drop rmgroup:*:fido.belg.*:drop checkgroups:fidobelg@mail.z2.fidonet.org:fido.belg.*:verify-fido.belg.news newgroup:fidobelg@mail.z2.fidonet.org:fido.belg.*:verify-fido.belg.news rmgroup:fidobelg@mail.z2.fidonet.org:fido.belg.*:verify-fido.belg.news ## FIDO.GER (German FIDO Net Echos) # URL: ftp://ftp.fu-berlin.de/doc/news/fido.ger/fido.ger-info.english # Key URL: ftp://ftp.fu-berlin.de/doc/news/fido.ger/PGP-Key # *PGP* See comment at top of file. newgroup:*:fido.ger.*:drop rmgroup:*:fido.ger.*:drop checkgroups:fido.ger@news.fu-berlin.de:fido.ger.*:verify-fido.ger@news.fu-berlin.de newgroup:fido.ger@news.fu-berlin.de:fido.ger.*:verify-fido.ger@news.fu-berlin.de rmgroup:fido.ger@news.fu-berlin.de:fido.ger.*:verify-fido.ger@news.fu-berlin.de ## FIDO7 (Russian FidoNet) # URL: http://www.fido7.ru/ # Admin group: fido7.postmasters # Key URL: http://www.fido7.ru/pgpcontrol.html # *PGP* See comment at top of file. newgroup:*:fido7.*:drop rmgroup:*:fido7.*:drop checkgroups:newgroups-request@fido7.ru:fido7.*:verify-fido7.announce.newgroups newgroup:newgroups-request@fido7.ru:fido7.*:verify-fido7.announce.newgroups rmgroup:newgroups-request@fido7.ru:fido7.*:verify-fido7.announce.newgroups ## FINET (Finland and Finnish language alternative newsgroups) checkgroups:*@*.fi:finet.*:doit newgroup:*@*.fi:finet.*:doit rmgroup:*@*.fi:finet.*:doit ## FJ (Japan and Japanese language) # Contact: committee@fj-news.org # URL: http://www.fj-news.org/index.html.en # Admin group: fj.news.announce # Key URL: http://www.is.tsukuba.ac.jp/~yas/fj/fj.asc # *PGP* See comment at top of file. newgroup:*:fj.*:drop rmgroup:*:fj.*:drop checkgroups:committee@fj-news.org:fj.*:verify-fj.news.announce newgroup:committee@fj-news.org:fj.*:verify-fj.news.announce rmgroup:committee@fj-news.org:fj.*:verify-fj.news.announce ## FL (Florida, USA) checkgroups:hgoldste@news1.mpcs.com:fl.*:doit checkgroups:scheidell@fdma.fdma.com:fl.*:doit newgroup:hgoldste@news1.mpcs.com:fl.*:doit newgroup:scheidell@fdma.fdma.com:fl.*:doit rmgroup:hgoldste@news1.mpcs.com:fl.*:doit rmgroup:scheidell@fdma.fdma.com:fl.*:doit ## FLORA (FLORA Community WEB, Canada) # Contact: russell@flora.org # Admin group: flora.general # *PGP* See comment at top of file. newgroup:*:flora.*:drop rmgroup:*:flora.*:drop checkgroups:news@flora.ottawa.on.ca:flora.*:verify-flora-news newgroup:news@flora.ottawa.on.ca:flora.*:verify-flora-news rmgroup:news@flora.ottawa.on.ca:flora.*:verify-flora-news ## FR (French language) # URL: http://www.usenet-fr.net/ # Admin group: fr.usenet.forums.annonces # Key URL: http://www.usenet-fr.net/fur/usenet/presentation-fr.html # *PGP* See comment at top of file. newgroup:*:fr.*:drop rmgroup:*:fr.*:drop checkgroups:control@usenet-fr.news.eu.org:fr.*:verify-control@usenet-fr.news.eu.org newgroup:control@usenet-fr.news.eu.org:fr.*:verify-control@usenet-fr.news.eu.org rmgroup:control@usenet-fr.news.eu.org:fr.*:verify-control@usenet-fr.news.eu.org ## FRANCE (France) # Contact: control@usenet-france.news.eu.org # Admin group: france.admin.evolutions # *PGP* See comment at top of file. newgroup:*:france.*:drop rmgroup:*:france.*:drop checkgroups:control@usenet-france.news.eu.org:france.*:verify-control@usenet-france.news.eu.org newgroup:control@usenet-france.news.eu.org:france.*:verify-control@usenet-france.news.eu.org rmgroup:control@usenet-france.news.eu.org:france.*:verify-control@usenet-france.news.eu.org ## FREE (Open Hierarchy where anyone can create a group) newgroup:*:free.*:doit newgroup:group-admin@isc.org:free.*:drop newgroup:tale@*uu.net:free.*:drop rmgroup:*:free.*:drop ## FUDAI (Japanese ?) checkgroups:news@picard.cs.osakafu-u.ac.jp:fudai.*:doit newgroup:news@picard.cs.osakafu-u.ac.jp:fudai.*:doit rmgroup:news@picard.cs.osakafu-u.ac.jp:fudai.*:doit ## FUR (*PRIVATE* -- furrynet) # Contact: fur-config@news.furry.net # For private use only, contact the above address for information. newgroup:*:fur.*:mail rmgroup:*:fur.*:doit ## GER & HANNOVER & HANNET & HILDESHEIM & HISS (Hannover, Germany) checkgroups:fifi@hiss.han.de:ger.*|hannover.*|hannet.*|hildesheim.*|hiss.*:doit newgroup:fifi@hiss.han.de:ger.*|hannover.*|hannet.*|hildesheim.*|hiss.*:doit rmgroup:fifi@hiss.han.de:ger.*|hannover.*|hannet.*|hildesheim.*|hiss.*:doit ## GIT (Georgia Institute of Technology, USA) newgroup:news@news.gatech.edu:git.*:doit newgroup:news@news.gatech.edu:git*class.*:mail rmgroup:news@news.gatech.edu:git.*:doit ## GNU (Free Software Foundation) # URL: http://www.gnu.org/usenet/usenet.html # Admin group: gnu.gnusenet.config # Key URL: http://www.gnu.org/usenet/usenet-pgp-key.txt # *PGP* See comment at top of file. newgroup:*:gnu.*:drop rmgroup:*:gnu.*:drop checkgroups:usenet@gnu.org:gnu.*:verify-usenet@gnu.org newgroup:usenet@gnu.org:gnu.*:verify-usenet@gnu.org rmgroup:usenet@gnu.org:gnu.*:verify-usenet@gnu.org ## GNUU (*PRIVATE* -- GNUU e.V., Oberursel, Germany) # URL: http://www.gnuu.de/ # Key URL: http://www.gnuu.de/config/PGPKEY.GNUU # For private use only. # *PGP* See comment at top of file. newgroup:*:gnuu.*:drop rmgroup:*:gnuu.*:drop newgroup:news@gnuu.de:gnuu.*:mail rmgroup:news@gnuu.de:gnuu.*:verify-news@gnuu.de ## GOV (Government Information) # Admin group: gov.usenet.announce # *PGP* See comment at top of file. newgroup:*:gov.*:drop rmgroup:*:gov.*:drop checkgroups:gov-usenet-announce-moderator@govnews.org:gov.*:verify-gov.usenet.announce newgroup:gov-usenet-announce-moderator@govnews.org:gov.*:verify-gov.usenet.announce rmgroup:gov-usenet-announce-moderator@govnews.org:gov.*:verify-gov.usenet.announce ## GRISBI (Grisbi Personal Finance Manager software) # Contact: newsmaster@grisbi.org # URL: http://news.grisbi.org/ # Admin group: grisbi.admin # Key URL: http://news.grisbi.org/public-key.asc # Key fingerprint: EB35 0C03 0080 BD2A 7E0C A4C9 F2C6 2A3D C86C C6E1 # *PGP* See comment at top of file. newgroup:*:grisbi.*:drop rmgroup:*:grisbi.*:drop checkgroups:grisbi-control@grisbi.org:grisbi.*:verify-grisbi-control@grisbi.org newgroup:grisbi-control@grisbi.org:grisbi.*:verify-grisbi-control@grisbi.org rmgroup:grisbi-control@grisbi.org:grisbi.*:verify-grisbi-control@grisbi.org ## GWU (George Washington University, Washington, DC) # Contact: Sweth Chandramouli checkgroups:news@nit.gwu.edu:gwu.*:doit newgroup:news@nit.gwu.edu:gwu.*:doit rmgroup:news@nit.gwu.edu:gwu.*:doit ## HACKTIC (*HISTORIC* -- XS4ALL, Netherlands) # # This hierarchy is not entirely defunct, but it receives very little # traffic and is included primarily for the sake of completeness. # # *PGP* See comment at top of file. newgroup:*:hacktic.*:drop rmgroup:*:hacktic.*:drop checkgroups:news@zhaum.xs4all.nl:hacktic.*:verify-news@zhaum.xs4all.nl newgroup:news@zhaum.xs4all.nl:hacktic.*:verify-news@zhaum.xs4all.nl rmgroup:news@zhaum.xs4all.nl:hacktic.*:verify-news@zhaum.xs4all.nl ## HAMBURG (City of Hamburg, Germany) # Contact: hamburg@steering-group.net # URL: http://www.steering-group.net/hamburg/ # Admin group: hamburg.koordination # Key URL: http://www.steering-group.net/hamburg/hamburg.koordination.txt # Key fingerprint: 3E E7 0C BB 6E 01 94 EE 45 6F C5 57 F4 B9 54 8E # *PGP* See comment at top of file. newgroup:*:hamburg.*:drop rmgroup:*:hamburg.*:drop checkgroups:hamburg@steering-group.net:hamburg.*:verify-hamburg.koordination newgroup:hamburg@steering-group.net:hamburg.*:verify-hamburg.koordination rmgroup:hamburg@steering-group.net:hamburg.*:verify-hamburg.koordination ## HAMILTON (Canadian) checkgroups:news@*dcss.mcmaster.ca:hamilton.*:doit newgroup:news@*dcss.mcmaster.ca:hamilton.*:doit rmgroup:news@*dcss.mcmaster.ca:hamilton.*:doit ## HAMSTER (Hamster, a Win32 news and mail proxy server) # Contact: hamster-contact@snafu.de # Admin group: hamster.de.config # Key fingerprint: 12 75 A9 42 8A D6 1F 77 6A CF B4 0C 79 15 5F 93 # *PGP* See comment at top of file. newgroup:*:hamster.*:drop rmgroup:*:hamster.*:drop checkgroups:hamster-control@snafu.de:hamster.*:verify-hamster-control@snafu.de newgroup:hamster-control@snafu.de:hamster.*:verify-hamster-control@snafu.de rmgroup:hamster-control@snafu.de:hamster.*:verify-hamster-control@snafu.de ## HAN (Korean Hangul) # Contact: newgroups-request@usenet.or.kr # Admin group: han.news.admin # Key URL: ftp://ftp.usenet.or.kr/pub/korea/usenet/pgp/PGPKEY.han # *PGP* See comment at top of file. newgroup:*:han.*:drop rmgroup:*:han.*:drop checkgroups:newgroups-request@usenet.or.kr:han.*:verify-han.news.admin newgroup:newgroups-request@usenet.or.kr:han.*:verify-han.news.admin rmgroup:newgroups-request@usenet.or.kr:han.*:verify-han.news.admin ## HARVARD (*LOCAL* -- Harvard University, Cambridge, MA) # For local use only. newgroup:*@*.harvard.edu:harvard.*:mail rmgroup:*@*.harvard.edu:harvard.*:doit ## HAWAII (Hawaii, USA) checkgroups:news@lava.net:hawaii.*:doit newgroup:news@lava.net:hawaii.*:doit rmgroup:news@lava.net:hawaii.*:doit ## HFX (Halifax, Nova Scotia) checkgroups:stevemackie@gmail.com:hfx.*:doit newgroup:stevemackie@gmail.com:hfx.*:doit rmgroup:stevemackie@gmail.com:hfx.*:doit ## HIV (HIVNET Foundation, for HIV+/AIDS information) # Contact: news@hivnet.org # Admin group: hiv.config # Key fingerprint: 5D D6 0E DC 1E 2D EA 0B B0 56 4D D6 52 53 D7 A4 # *PGP* See comment at top of file. newgroup:*:hiv.*:drop rmgroup:*:hiv.*:drop checkgroups:news@hivnet.org:hiv.*:verify-news@hivnet.org newgroup:news@hivnet.org:hiv.*:verify-news@hivnet.org rmgroup:news@hivnet.org:hiv.*:verify-news@hivnet.org ## HK (Hong Kong) checkgroups:hknews@comp.hkbu.edu.hk:hk.*:doit newgroup:hknews@comp.hkbu.edu.hk:hk.*:doit rmgroup:hknews@comp.hkbu.edu.hk:hk.*:doit ## HOUSTON (Houston, Texas, USA) # Admin group: houston.usenet.config # *PGP* See comment at top of file. newgroup:*:houston.*:drop rmgroup:*:houston.*:drop checkgroups:news@academ.com:houston.*:verify-houston.usenet.config newgroup:news@academ.com:houston.*:verify-houston.usenet.config rmgroup:news@academ.com:houston.*:verify-houston.usenet.config ## HR (Croatian language) # Contact: newsmaster@carnet.hr # URL: http://newsfeed.carnet.hr/control/ # Admin group: hr.news.admin # Key URL: http://newsfeed.carnet.hr/control/key.txt # Key fingerprint: 0EE5 74FB 1C40 7ADB 0AAC A52F 7192 1BA3 ED63 AD9A # Syncable server: news.carnet.hr # *PGP* See comment at top of file. newgroup:*:hr.*:drop rmgroup:*:hr.*:drop checkgroups:newsmaster@carnet.hr:hr.*:verify-newsmaster@carnet.hr newgroup:newsmaster@carnet.hr:hr.*:verify-newsmaster@carnet.hr rmgroup:newsmaster@carnet.hr:hr.*:verify-newsmaster@carnet.hr ## HUMANITYQUEST (*HISTORIC* -- Humanities discussion) # # This hierarchy is not entirely defunct, but it receives very little # traffic and is included primarily for the sake of completeness. # # Contact: news-admin@humanityquest.com # URL: http://www.humanityquest.com/projects/newsgroups/ # Key URL: http://www.humanityquest.com/projects/newsgroups/PGP.htm # Key fingerprint: BA3D B306 B6F5 52AA BA8F 32F0 8C4F 5040 16F9 C046 # *PGP* See comment at top of file. newgroup:*:humanityquest.*:drop rmgroup:*:humanityquest.*:drop checkgroups:news-admin@humanityquest.com:humanityquest.*:verify-humanityquest.admin.config newgroup:news-admin@humanityquest.com:humanityquest.*:verify-humanityquest.admin.config rmgroup:news-admin@humanityquest.com:humanityquest.*:verify-humanityquest.admin.config ## HUN (Hungary) # URL: http://gatling.ikk.sztaki.hu/~kissg/news/ # Admin group: hun.admin.news # Key URL: http://gatling.ikk.sztaki.hu/~kissg/news/hun.admin.news.asc # *PGP* See comment at top of file. newgroup:*:hun.*:drop rmgroup:*:hun.*:drop checkgroups:hun-mnt@news.sztaki.hu:hun.*:verify-hun.admin.news newgroup:hun-mnt@news.sztaki.hu:hun.*:verify-hun.admin.news rmgroup:hun-mnt@news.sztaki.hu:hun.*:verify-hun.admin.news ## IA (Iowa, USA) checkgroups:skunz@iastate.edu:ia.*:doit newgroup:skunz@iastate.edu:ia.*:doit rmgroup:skunz@iastate.edu:ia.*:doit ## IBMNET (*LOCAL* -- ?) # Contact: news@ibm.net # For local use only, contact the above address for information. newgroup:*:ibmnet.*:mail rmgroup:*:ibmnet.*:doit ## ICONZ (*LOCAL* -- The Internet Company of New Zealand, New Zealand) # Contact: usenet@iconz.co.nz # For local use only, contact the above address for information. newgroup:*:iconz.*:mail rmgroup:*:iconz.*:doit ## IDOCTRA (Idoctra Translation Software, Translation Discussion) # Contact: support@idoctra.com checkgroups:support@idoctra.com:idoctra.*:doit newgroup:support@idoctra.com:idoctra.*:doit rmgroup:support@idoctra.com:idoctra.*:doit ## IE (Ireland) # Contact: control@usenet.ie # Admin group: ie.news.group # *PGP* See comment at top of file. newgroup:*:ie.*:drop rmgroup:*:ie.*:drop checkgroups:control@usenet.ie:ie.*:verify-control@usenet.ie newgroup:control@usenet.ie:ie.*:verify-control@usenet.ie rmgroup:control@usenet.ie:ie.*:verify-control@usenet.ie ## IEEE (*DEFUNCT* -- Institute of Electrical and Electronic Engineers) # Contact: postoffice@ieee.org # This hierarchy is defunct. Please remove it. newgroup:*:ieee.*:mail rmgroup:*:ieee.*:doit ## INFO (*DEFUNCT* -- Gatewayed mailing lists) # This hierarchy is defunct. Please remove it. newgroup:rjoyner@uiuc.edu:info.*:mail rmgroup:rjoyner@uiuc.edu:info.*:doit ## IS (Iceland) # Contact: IS Group Admins # URL: http://www.usenet.is/ # Admin group: is.isnet # Key URL: http://www.usenet.is/group-admin.asc # Key fingerprint: 33 32 8D 46 1E 5E 1C 7F 48 60 8E 72 E5 3E CA EA # *PGP* See comment at top of file. newgroup:*:is.*:drop rmgroup:*:is.*:drop checkgroups:group-admin@usenet.is:is.*:verify-group-admin@usenet.is newgroup:group-admin@usenet.is:is.*:verify-group-admin@usenet.is rmgroup:group-admin@usenet.is:is.*:verify-group-admin@usenet.is ## ISC (Japanese ?) checkgroups:news@sally.isc.chubu.ac.jp:isc.*:doit newgroup:news@sally.isc.chubu.ac.jp:isc.*:doit rmgroup:news@sally.isc.chubu.ac.jp:isc.*:doit ## ISRAEL & IL (Israel) newgroup:news@news.biu.ac.il:israel.*:doit rmgroup:news@news.biu.ac.il:israel.*|il.*:doit ## ISU (I-Shou University, Taiwan) # Contact: news@news.isu.edu.tw # URL: http://news.isu.edu.tw/ # Admin group: isu.newgroups # Key URL: http://news.isu.edu.tw/isu.asc # *PGP* See comment at top of file. newgroup:*:isu.*:drop rmgroup:*:isu.*:drop checkgroups:news@news.isu.edu.tw:isu.*:verify-news@news.isu.edu.tw newgroup:news@news.isu.edu.tw:isu.*:verify-news@news.isu.edu.tw rmgroup:news@news.isu.edu.tw:isu.*:verify-news@news.isu.edu.tw ## IT (Italian) # Contact: gcn@news.nic.it # URL: http://www.news.nic.it/ # Admin group: it.news.annunci # Key URL: http://www.news.nic.it/pgp.txt # Key fingerprint: 94 A4 F7 B5 46 96 D6 C7 A6 73 F2 98 C4 8C D0 E0 # *PGP* See comment at top of file. newgroup:*:it.*:drop rmgroup:*:it.*:drop checkgroups:gcn@news.nic.it:it.*:verify-gcn@news.nic.it newgroup:gcn@news.nic.it:it.*:verify-gcn@news.nic.it rmgroup:gcn@news.nic.it:it.*:verify-gcn@news.nic.it ## IT-ALT (Alternate Italian) # # There is no one official control message issuer for the it-alt.* # hierarchy, so this file doesn't choose any particular one. Several # different people issue control messages for this hierarchy, which may # or may not agree, and sites carrying this hierarchy are encouraged to # pick one and add it below. # # Newgroup and removal requests are to be posted to it-alt.config. A list # of people issuing PGP/GPG signed control messages is available in a # periodic posting to news.admin.hierarchies and it-alt.config. # newgroup:*:it-alt.*:drop rmgroup:*:it-alt.*:drop ## ITALIA (Italy) # Contact: news@news.cineca.it # URL: http://news.cineca.it/italia/ # Admin group: italia.announce.newgroups # Key URL: http://news.cineca.it/italia/italia-pgp.txt # Key fingerprint: 0F BB 71 62 DA 5D 5D B8 D5 86 FC 28 02 67 1A 6B # *PGP* See comment at top of file. newgroup:*:italia.*:drop rmgroup:*:italia.*:drop checkgroups:news@news.cineca.it:italia.*:verify-italia.announce.newgroups newgroup:news@news.cineca.it:italia.*:verify-italia.announce.newgroups rmgroup:news@news.cineca.it:italia.*:verify-italia.announce.newgroups ## IU (Indiana University) newgroup:news@usenet.ucs.indiana.edu:iu.*:doit newgroup:root@usenet.ucs.indiana.edu:iu.*:doit newgroup:*@usenet.ucs.indiana.edu:iu*class.*:mail rmgroup:news@usenet.ucs.indiana.edu:iu.*:doit rmgroup:root@usenet.ucs.indiana.edu:iu.*:doit ## JAPAN (Japan) # Contact: Tsuneo Tanaka # URL: http://www.asahi-net.or.jp/~AE5T-KSN/japan-e.html # Admin group: japan.admin.announce # Key URL: http://grex.cyberspace.org/~tt/japan.admin.announce.asc # Key fingerprint: 6A FA 19 47 69 1B 10 74 38 53 4B 1B D8 BA 3E 85 # *PGP* See comment at top of file. newgroup:*:japan.*:drop rmgroup:*:japan.*:drop checkgroups:japan.admin.announce@news.efnet.com:japan.*:verify-japan.admin.announce@news.efnet.com newgroup:japan.admin.announce@news.efnet.com:japan.*:verify-japan.admin.announce@news.efnet.com rmgroup:japan.admin.announce@news.efnet.com:japan.*:verify-japan.admin.announce@news.efnet.com ## JLUG (Japan Linux Users Group) # Contact: news@linux.or.jp # URL: http://www.linux.or.jp/community/news/index.html # Admin group: jlug.config # Key URL: http://www.linux.or.jp/pgpkey/news # *PGP* See comment at top of file. newgroup:*:jlug.*:drop rmgroup:*:jlug.*:drop checkgroups:news@linux.or.jp:jlug.*:verify-news@linux.or.jp newgroup:news@linux.or.jp:jlug.*:verify-news@linux.or.jp rmgroup:news@linux.or.jp:jlug.*:verify-news@linux.or.jp ## K12 (US Educational Network) # URL: http://www.k12groups.org/ checkgroups:braultr@*csmanoirs.qc.ca:k12.*:doit newgroup:braultr@*csmanoirs.qc.ca:k12.*:doit rmgroup:braultr@*csmanoirs.qc.ca:k12.*:doit ## KA (*PRIVATE* -- Karlsruhe, Germany) # Contact: usenet@karlsruhe.org # URL: http://www.karlsruhe.org/ # Admin group: ka.admin # Key URL: http://www.karlsruhe.org/pubkey-news.karlsruhe.org.asc # Key fingerprint: DE 19 BB 25 76 19 81 17 F0 67 D2 23 E8 C8 7C 90 # For private use only, contact the above address for information. # *PGP* See comment at top of file. newgroup:*:ka.*:drop rmgroup:*:ka.*:drop # The following three lines are only for authorized ka.* sites. #checkgroups:usenet@karlsruhe.org:ka.*:verify-usenet@karlsruhe.org #newgroup:usenet@karlsruhe.org:ka.*:verify-usenet@karlsruhe.org #rmgroup:usenet@karlsruhe.org:ka.*:verify-usenet@karlsruhe.org ## KANTO (?) # *PGP* See comment at top of file. rmgroup:*:kanto.*:drop checkgroups:ty@kamoi.imasy.or.jp:kanto.*:verify-kanto.news.network # NOTE: newgroups aren't verified... newgroup:*@*.jp:kanto.*:doit rmgroup:ty@kamoi.imasy.or.jp:kanto.*:verify-kanto.news.network ## KASSEL (Kassel, Germany) # Admin group: kassel.admin # *PGP* See comment at top of file. newgroup:*:kassel.*:drop rmgroup:*:kassel.*:drop checkgroups:dirk.meyer@dinoex.sub.org:kassel.*:verify-kassel-admin newgroup:dirk.meyer@dinoex.sub.org:kassel.*:verify-kassel-admin rmgroup:dirk.meyer@dinoex.sub.org:kassel.*:verify-kassel-admin ## KC (Kansas City, Kansas/Missouri, USA) checkgroups:dan@sky.net:kc.*:doit newgroup:dan@sky.net:kc.*:doit rmgroup:dan@sky.net:kc.*:doit ## KGK (Administered by KGK, Japan) # Contact: Keiji KOSAKA # URL: http://film.rlss.okayama-u.ac.jp/~kgk/kgk/index.html # Admin group: kgk.admin checkgroups:usenet@film.rlss.okayama-u.ac.jp:kgk.*:doit newgroup:usenet@film.rlss.okayama-u.ac.jp:kgk.*:doit rmgroup:usenet@film.rlss.okayama-u.ac.jp:kgk.*:doit ## KIEL (Kiel, Germany) # # This hierarchy is still in use, but it has no active maintainer. # Control messages for this hierarchy should not be honored without # confirming that the sender is the new hierarchy maintainer. # # Admin group: kiel.sysop ## KRST (*LOCAL* -- University of Oslo, Norway) # Contact: jani@ifi.uio.no # For local use only, contact the above address for information. newgroup:*:krst.*:mail rmgroup:*:krst.*:doit ## KWNET (*LOCAL* -- Kitchener-Waterloo?) # Contact: Ed Hew # For local use only, contact the above address for information. newgroup:*:kwnet.*:mail rmgroup:*:kwnet.*:doit ## LAW (?) # Contact: Jim Burke checkgroups:*@*.kentlaw.edu:law.*:doit checkgroups:*@*.law.vill.edu:law.*:doit newgroup:*@*.kentlaw.edu:law.*:doit newgroup:*@*.law.vill.edu:law.*:doit rmgroup:*@*.kentlaw.edu:law.*:doit rmgroup:*@*.law.vill.edu:law.*:doit ## LINUX (Gated Linux mailing lists) # Contact: Marco d'Itri # Admin group: linux.admin.news # Key fingerprint: 81 B3 27 99 4F CE 32 D1 1B C9 01 0D BB B3 2E 41 # *PGP* See comment at top of file. newgroup:*:linux.*:drop rmgroup:*:linux.*:drop checkgroups:linux-admin@bofh.it:linux.*:verify-linux-admin@bofh.it newgroup:linux-admin@bofh.it:linux.*:verify-linux-admin@bofh.it rmgroup:linux-admin@bofh.it:linux.*:verify-linux-admin@bofh.it ## LOCAL (*RESERVED* -- Local-only groups) # # Historically reserved for local groups and occasionally configured for # such groups in news software. It is not really a good idea for sites # to use this hierarchy for local groups, since they may occur on many # unconnected sites and may confuse news readers that read at multiple # sites. # newgroup:*:local.*:drop rmgroup:*:local.*:drop ## LUEBECK (Luebeck, Germany) # Contact: usenet@zybrkat.org # Admin group: luebeck.admin checkgroups:usenet@zybrkat.org:luebeck.*:doit newgroup:usenet@zybrkat.org:luebeck.*:doit rmgroup:usenet@zybrkat.org:luebeck.*:doit ## MALTA (Nation of Malta) # Contact: cmeli@cis.um.edu.mt # URL: http://www.malta.news-admin.org/ # Admin group: malta.config # Key URL: http://www.cis.um.edu.mt/news-malta/PGP.PUBLICKEY # Key fingerprint: 20 17 01 5C F0 D0 1A 42 E4 13 30 58 0B 14 48 A6 # *PGP* See comment at top of file. newgroup:*:malta.*:drop rmgroup:*:malta.*:drop checkgroups:cmeli@cis.um.edu.mt:malta.*:verify-malta.config newgroup:cmeli@cis.um.edu.mt:malta.*:verify-malta.config rmgroup:cmeli@cis.um.edu.mt:malta.*:verify-malta.config ## MANAWATU (*LOCAL* -- Manawatu district, New Zealand) # Contact: alan@manawatu.gen.nz or news@manawatu.gen.nz # For local use only, contact the above address for information. newgroup:*:manawatu.*:mail rmgroup:*:manawatu.*:doit ## MAUS (MausNet, Germany) # Admin group: maus.info # Key fingerprint: 82 52 C7 70 26 B9 72 A1 37 98 55 98 3F 26 62 3E # *PGP* See comment at top of file. newgroup:*:maus.*:drop rmgroup:*:maus.*:drop checkgroups:guenter@gst0hb.hb.provi.de:maus.*:verify-maus-info checkgroups:guenter@gst0hb.north.de:maus.*:verify-maus-info newgroup:guenter@gst0hb.hb.provi.de:maus.*:verify-maus-info newgroup:guenter@gst0hb.north.de:maus.*:verify-maus-info rmgroup:guenter@gst0hb.hb.provi.de:maus.*:verify-maus-info rmgroup:guenter@gst0hb.north.de:maus.*:verify-maus-info ## MCMASTER (*LOCAL* -- McMaster University, Ontario) # Contact: Brian Beckberger # For local use only, contact the above address for information. newgroup:*:mcmaster.*:mail rmgroup:*:mcmaster.*:doit ## MCOM (*LOCAL* -- Netscape Inc, USA) # For local use only. newgroup:*:mcom.*:mail rmgroup:*:mcom.*:doit ## ME (Maine, USA) checkgroups:kerry@maine.maine.edu:me.*:doit newgroup:kerry@maine.maine.edu:me.*:doit rmgroup:kerry@maine.maine.edu:me.*:doit ## MEDLUX (All-Russia medical teleconferences) # URL: ftp://ftp.medlux.ru/pub/news/medlux.grp checkgroups:neil@new*.medlux.ru:medlux.*:doit newgroup:neil@new*.medlux.ru:medlux.*:doit rmgroup:neil@new*.medlux.ru:medlux.*:doit ## MELB (Melbourne, Australia) # Contact: ausadmin@aus.news-admin.org # URL: http://melb.news-admin.org/ # Key URL: http://aus.news-admin.org/ausadmin.asc # *PGP* See comment at top of file. newgroup:*:melb.*:drop rmgroup:*:melb.*:drop checkgroups:ausadmin@aus.news-admin.org:melb.*:verify-ausadmin@aus.news-admin.org newgroup:ausadmin@aus.news-admin.org:melb.*:verify-ausadmin@aus.news-admin.org rmgroup:ausadmin@aus.news-admin.org:melb.*:verify-ausadmin@aus.news-admin.org ## MENSA (The Mensa Organisation) # Contact: usenet@newsgate.mensa.org # Admin group: mensa.config # Key fingerprint: 52B9 3963 85D9 0806 8E19 7344 973C 5005 DC7D B7A7 # *PGP* See comment at top of file. newgroup:*:mensa.*:drop rmgroup:*:mensa.*:drop checkgroups:usenet@newsgate.mensa.org:mensa.*:verify-mensa.config newgroup:usenet@newsgate.mensa.org:mensa.*:verify-mensa.config rmgroup:usenet@newsgate.mensa.org:mensa.*:verify-mensa.config ## METOCEAN (ISP in Japan) checkgroups:fwataru@*.metocean.co.jp:metocean.*:doit newgroup:fwataru@*.metocean.co.jp:metocean.*:doit rmgroup:fwataru@*.metocean.co.jp:metocean.*:doit ## METROPOLIS (*LOCAL* -- ?) # Contact: newsmaster@worldonline.nl # For local use only, contact the above address for information. newgroup:*:metropolis.*:mail rmgroup:*:metropolis.*:doit ## MI (Michigan, USA) # Contact: Steve Simmons checkgroups:scs@lokkur.dexter.mi.us:mi.*:doit newgroup:scs@lokkur.dexter.mi.us:mi.*:doit rmgroup:scs@lokkur.dexter.mi.us:mi.*:doit ## MICROSOFT (Microsoft Corporation, USA) # # Control articles for that hierarchy are not issued by Microsoft itself # but by a Usenet active participant in order to improve the quality of # the propagation of Microsoft newsgroups. Their official URL is: # http://www.microsoft.com/communities/newsgroups/list/en-us/default.aspx # # Contact: control-microsoft@trigofacile.com # URL: http://www.trigofacile.com/divers/usenet/clefs/index.htm # Admin group: microsoft.public.news.server # Key URL: http://www.trigofacile.com/divers/usenet/clefs/pgpkey-microsoft.asc # Key fingerprint: DF70 5FC9 F615 D52E 02DB A3CB 63A9 8D13 E60E 2FAA # Syncable server: msnews.microsoft.com # *PGP* See comment at top of file. newgroup:*:microsoft.*:drop rmgroup:*:microsoft.*:drop checkgroups:control-microsoft@trigofacile.com:microsoft.*:verify-control-microsoft@trigofacile.com newgroup:control-microsoft@trigofacile.com:microsoft.*:verify-control-microsoft@trigofacile.com rmgroup:control-microsoft@trigofacile.com:microsoft.*:verify-control-microsoft@trigofacile.com ## MILW (Milwaukee, Wisconsin, USA) # Contact: milw@usenet.mil.wi.us # URL: http://usenet.mil.wi.us/ # Admin group: milw.config # Key URL: http://usenet.mil.wi.us/pgpkey # Key fingerprint: 6E 9B 9F 70 98 AB 9C E5 C3 C0 05 82 21 5B F4 9E # *PGP* See comment at top of file. newgroup:*:milw.*:drop rmgroup:*:milw.*:drop checkgroups:milw@usenet.mil.wi.us:milw.*:verify-milw.config newgroup:milw@usenet.mil.wi.us:milw.*:verify-milw.config rmgroup:milw@usenet.mil.wi.us:milw.*:verify-milw.config ## MOD (*DEFUNCT* -- Original top level moderated hierarchy) # This hierarchy is defunct. Please remove it. newgroup:*:mod.*:mail rmgroup:*:mod.*:doit ## MUC (Munchen [Munich], Germany) # Admin group: muc.admin # Key URL: http://home.arcor.de/andreas-barth/muc-admin.html # Key fingerprint: 43 C7 0E 7C 45 C7 06 E0 BD 6F 76 CE 07 39 5E 66 # *PGP* See comment at top of file. newgroup:*:muc.*:drop rmgroup:*:muc.*:drop checkgroups:muc-cmsg@muenchen.pro-bahn.org:muc.*:verify-muc.admin newgroup:muc-cmsg@muenchen.pro-bahn.org:muc.*:verify-muc.admin rmgroup:muc-cmsg@muenchen.pro-bahn.org:muc.*:verify-muc.admin ## NAGASAKI-U (Nagasaki University, Japan ?) checkgroups:root@*nagasaki-u.ac.jp:nagasaki-u.*:doit newgroup:root@*nagasaki-u.ac.jp:nagasaki-u.*:doit rmgroup:root@*nagasaki-u.ac.jp:nagasaki-u.*:doit ## NAS (*LOCAL* -- NAS, NASA Ames Research Center, USA) # Contact: news@nas.nasa.gov # For local use only, contact the above address for information. newgroup:*:nas.*:mail rmgroup:*:nas.*:doit ## NASA (*LOCAL* -- National Aeronautics and Space Administration, USA) # Contact: news@nas.nasa.gov # For local use only, contact the above address for information. newgroup:*:nasa.*:mail rmgroup:*:nasa.*:doit ## NC (North Carolina, USA) # # This hierarchy is still in use, but it has no active maintainer. # Control messages for this hierarchy should not be honored without # confirming that the sender is the new hierarchy maintainer. ## NCF (*LOCAL* -- National Capital Freenet, Ottawa, Ontario, Canada) # Contact: news@freenet.carleton.ca # For local use only, contact the above address for information. newgroup:*:ncf.*:mail rmgroup:*:ncf.*:doit ## NCTU (Taiwan) checkgroups:chen@cc.nctu.edu.tw:nctu.*:doit newgroup:chen@cc.nctu.edu.tw:nctu.*:doit rmgroup:chen@cc.nctu.edu.tw:nctu.*:doit ## NCU (*LOCAL* -- National Central University, Taiwan) # Contact: Ying-Hao Chang # Contact: # For local use only, contact the above address for information. newgroup:*:ncu.*:mail rmgroup:*:ncu.*:doit ## NERSC (National Energy Research Scientific Computing Center) # # This hierarchy is still in use, but it has no active maintainer. # Control messages for this hierarchy should not be honored without # confirming that the sender is the new hierarchy maintainer. # # Contact: usenet@nersc.gov ## NET (*HISTORIC* -- Usenet 2) # # This was a failed experiment in a different newsgroup creation policy and # administrative policy which has now been almost entirely abandoned. The # information is retained here for the few sites still using it, but sites # not already carrying the groups probably won't be interested. # # (This was also the original unmoderated Usenet hierarchy from before the # Great Renaming. The groups that used to be in net.* in the 1980s are now # in the Big Eight hierarchies.) # # URL: http://www.usenet2.org # Admin group: net.config # Key URL: http://www.usenet2.org/control@usenet2.org.asc # Key fingerprint: D7 D3 5C DB 18 6A 29 79 BF 74 D4 58 A3 78 9D 22 # *PGP* See comment at top of file. newgroup:*:net.*:drop rmgroup:*:net.*:drop #checkgroups:control@usenet2.org:net.*:verify-control@usenet2.org #newgroup:control@usenet2.org:net.*:verify-control@usenet2.org #rmgroup:control@usenet2.org:net.*:verify-control@usenet2.org ## NETINS (*LOCAL* -- netINS, Inc) # Contact: news@netins.net # For local use only, contact the above address for information. newgroup:*:netins.*:mail rmgroup:*:netins.*:doit ## NETSCAPE (Netscape Communications Corp) # Contact: news@netscape.com # URL: http://www.mozilla.org/community.html # Admin group: netscape.public.admin # Key fingerprint: B7 80 55 12 1F 9C 17 0B 86 66 AD 3B DB 68 35 EC # *PGP* See comment at top of file. newgroup:*:netscape.*:drop rmgroup:*:netscape.*:drop checkgroups:news@netscape.com:netscape.*:verify-netscape.public.admin newgroup:news@netscape.com:netscape.*:verify-netscape.public.admin rmgroup:news@netscape.com:netscape.*:verify-netscape.public.admin ## NEWS4US (*LOCAL* -- NEWS4US dot NL, Netherlands) # Contact: info@news4us.nl # For local use only, contact the above address for information. newgroup:*:news4us.*:mail rmgroup:*:news4us.*:doit ## NF (Newfoundland and Labrador, Canada) # Contact: randy@mun.ca checkgroups:randy@mun.ca:nf.*:doit newgroup:randy@mun.ca:nf.*:doit rmgroup:randy@mun.ca:nf.*:doit ## NIAGARA (Niagara Peninsula, USA/Canada) checkgroups:news@niagara.com:niagara.*:doit newgroup:news@niagara.com:niagara.*:doit rmgroup:news@niagara.com:niagara.*:doit ## NIAS (Japanese ?) checkgroups:news@cc.nias.ac.jp:nias.*:doit newgroup:news@cc.nias.ac.jp:nias.*:doit rmgroup:news@cc.nias.ac.jp:nias.*:doit ## NIGERIA (Nigeria) checkgroups:news@easnet.net:nigeria.*:doit newgroup:news@easnet.net:nigeria.*:doit rmgroup:news@easnet.net:nigeria.*:doit ## NIHON (Japan) checkgroups:ktomita@jade.dti.ne.jp:nihon.*:doit newgroup:ktomita@jade.dti.ne.jp:nihon.*:doit rmgroup:ktomita@jade.dti.ne.jp:nihon.*:doit ## NIPPON (*PRIVATE* -- Japan) # URL: http://www.gcd.org/news/nippon/ # Admin group: nippon.news.group # Key URL: http://www.gcd.org/news/nippon/ # Key fingerprint: BC CF 15 CD B1 3C DF B3 C3 DE 35 6F 2F F7 46 DB # For private use only. # *PGP* See comment at top of file. newgroup:*:nippon.*:drop rmgroup:*:nippon.*:drop newgroup:news@gcd.org:nippon.*:mail rmgroup:news@gcd.org:nippon.*:verify-nippon.news.group ## NJ (New Jersey, USA) # Contact: nj-admin@gunslinger.net # URL: http://www.exit109.com/~jeremy/nj/ checkgroups:nj-admin@gunslinger.net:nj.*:doit newgroup:nj-admin@gunslinger.net:nj.*:doit rmgroup:nj-admin@gunslinger.net:nj.*:doit ## NL (Netherlands) # Contact: nl-admin@nic.surfnet.nl # URL: http://nl.news-admin.org/info/nladmin.html # Admin group: nl.newsgroups # Key fingerprint: 45 20 0B D5 A1 21 EA 7C EF B2 95 6C 25 75 4D 27 # *PGP* See comment at top of file. newgroup:*:nl.*:drop rmgroup:*:nl.*:drop checkgroups:nl-admin@nic.surfnet.nl:nl.*:verify-nl.newsgroups newgroup:nl-admin@nic.surfnet.nl:nl.*:verify-nl.newsgroups rmgroup:nl-admin@nic.surfnet.nl:nl.*:verify-nl.newsgroups ## NL-ALT (Alternative Netherlands groups) # Key fingerprint: 6B 62 EB 53 4D 5D 2F 96 35 D9 C8 9C B0 65 0E 4C # *PGP* See comment at top of file. checkgroups:nl-alt-janitor@surfer.xs4all.nl:nl-alt.*:verify-nl-alt.config.admin newgroup:*:nl-alt.*:doit rmgroup:nl-alt-janitor@surfer.xs4all.nl:nl-alt.*:verify-nl-alt.config.admin rmgroup:news@kink.xs4all.nl:nl-alt.*:verify-nl-alt.config.admin ## NLO (Open Source / Free Software, hosted by nl.linux.org) # URL: http://mail.nl.linux.org/ # Key fingerprint: 63 DC B2 51 0A F3 DD 72 C2 BD C6 FD C1 C5 44 CF # Syncable server: news.nl.linux.org # *PGP* See comment at top of file. newgroup:*:nlo.*:drop rmgroup:*:nlo.*:drop checkgroups:news@nl.linux.org:nlo.*:verify-nlo.newsgroups newgroup:news@nl.linux.org:nlo.*:verify-nlo.newsgroups rmgroup:news@nl.linux.org:nlo.*:verify-nlo.newsgroups ## NM (New Mexico, USA) checkgroups:news@tesuque.cs.sandia.gov:nm.*:doit newgroup:news@tesuque.cs.sandia.gov:nm.*:doit rmgroup:news@tesuque.cs.sandia.gov:nm.*:doit ## NNTPWORLD (General discussion) # Contact: usenet@nntpworld.net # URL: http://www.nntpworld.net/ # Admin group: nntpworld.config # Key URL: http://www.nntpworld.net/nntpworld.asc # Key fingerprint: C7 BB E0 56 7A 8A 5A 86 09 B5 E3 29 E1 BB 8C E1 # *PGP* See comment at top of file. newgroup:*:nntpworld.*:drop rmgroup:*:nntpworld.*:drop checkgroups:usenet@nntpworld.net:nntpworld.*:verify-usenet@nntpworld.net newgroup:usenet@nntpworld.net:nntpworld.*:verify-usenet@nntpworld.net rmgroup:usenet@nntpworld.net:nntpworld.*:verify-usenet@nntpworld.net ## NO (Norway) # URL: http://www.usenet.no/ # Admin group: no.usenet.admin # Key URL: http://www.usenet.no/pgp-key.txt # *PGP* See comment at top of file. newgroup:*:no.*:drop rmgroup:*:no.*:drop checkgroups:control@usenet.no:no.*:verify-no-hir-control newgroup:control@usenet.no:no.*:verify-no-hir-control rmgroup:control@usenet.no:no.*:verify-no-hir-control ## NO.ALT (Norway alternative hierarchy) # *PGP* See comment at top of file. newgroup:*:no.alt.*:drop rmgroup:*:no.alt.*:drop newgroup:*@*.no:no.alt.*:doit rmgroup:control@usenet.no:no.alt.*:verify-no-hir-control ## NORD (Northern Germany) # # This hierarchy is still in use, but it has no active maintainer. # Control messages for this hierarchy should not be honored without # confirming that the sender is the new hierarchy maintainer. ## NRW (Northrine-Westfalia, Germany) # Contact: moderator@nrw.usenetverwaltung.de # URL: http://nrw.usenetverwaltung.de/ # Admin group: nrw.admin.announce # Key URL: http://nrw.usenetverwaltung.de/pgp/nrw.asc # Key fingerprint: 13 4A 80 FE D6 34 B4 64 AF 32 08 3F 62 0E B1 E2 # *PGP* See comment at top of file. newgroup:*:nrw.*:drop rmgroup:*:nrw.*:drop checkgroups:moderator@nrw.usenetverwaltung.de:nrw.*:verify-moderator@nrw.usenetverwaltung.de newgroup:moderator@nrw.usenetverwaltung.de:nrw.*:verify-moderator@nrw.usenetverwaltung.de rmgroup:moderator@nrw.usenetverwaltung.de:nrw.*:verify-moderator@nrw.usenetverwaltung.de ## NV (Nevada) checkgroups:cshapiro@netcom.com:nv.*:doit checkgroups:doctor@netcom.com:nv.*:doit newgroup:cshapiro@netcom.com:nv.*:doit newgroup:doctor@netcom.com:nv.*:doit rmgroup:cshapiro@netcom.com:nv.*:doit rmgroup:doctor@netcom.com:nv.*:doit ## NY (New York State, USA) checkgroups:root@ny.psca.com:ny.*:doit newgroup:root@ny.psca.com:ny.*:doit rmgroup:root@ny.psca.com:ny.*:doit ## NYC (New York City) # Contact: Perry E. Metzger checkgroups:perry@piermont.com:nyc.*:doit newgroup:perry@piermont.com:nyc.*:doit rmgroup:perry@piermont.com:nyc.*:doit ## NZ (New Zealand) # Contact: root@usenet.net.nz # URL: http://www.faqs.org/faqs/usenet/nz-news-hierarchy # Admin group: nz.net.announce # Key fingerprint: 07 DF 48 AA D0 ED AA 88 16 70 C5 91 65 3D 1A 28 # *PGP* See comment at top of file. newgroup:*:nz.*:drop rmgroup:*:nz.*:drop checkgroups:root@usenet.net.nz:nz.*:verify-nz-hir-control newgroup:root@usenet.net.nz:nz.*:verify-nz-hir-control rmgroup:root@usenet.net.nz:nz.*:verify-nz-hir-control ## OC (Orange County, California, USA) checkgroups:bob@tsunami.sugarland.unocal.com:oc.*:doit newgroup:bob@tsunami.sugarland.unocal.com:oc.*:doit rmgroup:bob@tsunami.sugarland.unocal.com:oc.*:doit ## OESTERREICH (Free Austria) # # This is apparently another alt.* or free.* but specific to Austria. # Currently, the ftp.isc.org list doesn't honor newgroup messages in the # hierarchy due to lack of requests, but here is the information in case # any news admin wishes to carry it. # # URL: http://www.tahina.priv.at/~cm/oe/index.en.html #newgroup:*:oesterreich.*:doit #newgroup:group-admin@isc.org:oesterreich.*:drop #newgroup:tale@*uu.net:oesterreich.*:drop #rmgroup:*:oesterreich.*:drop ## OH (Ohio, USA) checkgroups:trier@ins.cwru.edu:oh.*:doit newgroup:trier@ins.cwru.edu:oh.*:doit rmgroup:trier@ins.cwru.edu:oh.*:doit ## OK (Oklahoma, USA) checkgroups:quentin@*qns.com:ok.*:doit newgroup:quentin@*qns.com:ok.*:doit rmgroup:quentin@*qns.com:ok.*:doit ## OKINAWA (Okinawa, Japan) checkgroups:news@opus.or.jp:okinawa.*:doit newgroup:news@opus.or.jp:okinawa.*:doit rmgroup:news@opus.or.jp:okinawa.*:doit ## ONT (Ontario, Canada) checkgroups:pkern@gpu.utcc.utoronto.ca:ont.*:doit newgroup:pkern@gpu.utcc.utoronto.ca:ont.*:doit rmgroup:pkern@gpu.utcc.utoronto.ca:ont.*:doit ## OPENNEWS (Open News Network) # URL: http://www.open-news-network.org/ # *PGP* See comment at top of file. newgroup:*:opennews.*:drop rmgroup:*:opennews.*:drop checkgroups:news@news2.open-news-network.org:opennews.*:verify-news@news2.open-news-network.org newgroup:news@news2.open-news-network.org:opennews.*:verify-news@news2.open-news-network.org rmgroup:news@news2.open-news-network.org:opennews.*:verify-news@news2.open-news-network.org ## OPENWATCOM (Open Watcom compilers) # Contact: admin@openwatcom.news-admin.org # URL: http://www.openwatcom.org/ # Admin group: openwatcom.contributors # Key URL: http://cmeerw.org/files/openwatcom/pgp-openwatcom.asc # Syncable server: news.openwatcom.org # *PGP* See comment at top of file. newgroup:*:openwatcom.*:drop rmgroup:*:openwatcom.*:drop checkgroups:admin@openwatcom.news-admin.org:openwatcom.*:verify-admin@openwatcom.news-admin.org newgroup:admin@openwatcom.news-admin.org:openwatcom.*:verify-admin@openwatcom.news-admin.org rmgroup:admin@openwatcom.news-admin.org:openwatcom.*:verify-admin@openwatcom.news-admin.org ## OPERA (Opera Software, Oslo, Norway) # Contact: usenet@opera.com # Syncable server: news.opera.com # *PGP* See comment at top of file. newgroup:*:opera.*:drop rmgroup:*:opera.*:drop checkgroups:*@opera.com:opera.*:verify-opera-group-admin newgroup:*@opera.com:opera.*:verify-opera-group-admin rmgroup:*@opera.com:opera.*:verify-opera-group-admin ## OTT (Ottawa, Ontario, Canada) # Contact: onag@pinetree.org # URL: http://www.pinetree.org/ONAG/ checkgroups:clewis@ferret.ocunix.on.ca:ott.*:doit checkgroups:dave@revcan.ca:ott.*:doit checkgroups:gordon@*pinetree.org:ott.*:doit checkgroups:news@*pinetree.org:ott.*:doit checkgroups:news@bnr.ca:ott.*:doit checkgroups:news@ferret.ocunix.on.ca:ott.*:doit checkgroups:news@nortel.ca:ott.*:doit newgroup:clewis@ferret.ocunix.on.ca:ott.*:doit newgroup:dave@revcan.ca:ott.*:doit newgroup:gordon@*pinetree.org:ott.*:doit newgroup:news@*pinetree.org:ott.*:doit newgroup:news@bnr.ca:ott.*:doit newgroup:news@ferret.ocunix.on.ca:ott.*:doit newgroup:news@nortel.ca:ott.*:doit rmgroup:clewis@ferret.ocunix.on.ca:ott.*:doit rmgroup:dave@revcan.ca:ott.*:doit rmgroup:gordon@*pinetree.org:ott.*:doit rmgroup:news@*pinetree.org:ott.*:doit rmgroup:news@bnr.ca:ott.*:doit rmgroup:news@ferret.ocunix.on.ca:ott.*:doit rmgroup:news@nortel.ca:ott.*:doit ## OWL (Ostwestfalen-Lippe, Germany) # # This hierarchy is still in use, but it has no active maintainer. # Control messages for this hierarchy should not be honored without # confirming that the sender is the new hierarchy maintainer. # # Contact: news@owl.de # Syncable server: news.owl.de ## PA (Pennsylvania, USA) # URL: http://www.netcom.com/~rb1000/pa_hierarchy/ checkgroups:fxp@epix.net:pa.*:doit newgroup:fxp@epix.net:pa.*:doit rmgroup:fxp@epix.net:pa.*:doit ## PBINFO (Paderborn, Germany) # Contact: news@uni-paderborn.de # *PGP* See comment at top of file. newgroup:*:pbinfo.*:drop rmgroup:*:pbinfo.*:drop checkgroups:postmaster@upb.de:pbinfo.*:verify-news@uni-paderborn.de newgroup:postmaster@upb.de:pbinfo.*:verify-news@uni-paderborn.de rmgroup:postmaster@upb.de:pbinfo.*:verify-news@uni-paderborn.de ## PERL (Perl Programming Language) # Contact: newsadmin@perl.org # URL: http://www.nntp.perl.org/about/ # Key URL: http://www.nntp.perl.org/about/newsadmin@perl.org.pgp # Key fingerprint: 438F D1BA 4DCC 3B1A BED8 2BCC 3298 8A7D 8B2A CFBB # *PGP* See comment at top of file. newgroup:*:perl.*:drop rmgroup:*:perl.*:drop checkgroups:newsadmin@perl.org:perl.*:verify-newsadmin@perl.org newgroup:newsadmin@perl.org:perl.*:verify-newsadmin@perl.org rmgroup:newsadmin@perl.org:perl.*:verify-newsadmin@perl.org ## PGH (Pittsburgh, Pennsylvania, USA) # Admin group: pgh.config # *PGP* See comment at top of file. newgroup:*:pgh.*:drop rmgroup:*:pgh.*:drop checkgroups:pgh-config@psc.edu:pgh.*:verify-pgh.config newgroup:pgh-config@psc.edu:pgh.*:verify-pgh.config rmgroup:pgh-config@psc.edu:pgh.*:verify-pgh.config ## PGSQL (Gated PostgreSQL mailing lists) # Contact: news@postgresql.org # URL: http://news.hub.org/gpg_public_keys.html # Key URL: http://news.hub.org/gpg_public_keys.html # *PGP* See comment at top of file. newgroup:*:pgsql.*:drop rmgroup:*:pgsql.*:drop checkgroups:news@postgresql.org:pgsql.*:verify-news@postgresql.org newgroup:news@postgresql.org:pgsql.*:verify-news@postgresql.org rmgroup:news@postgresql.org:pgsql.*:verify-news@postgresql.org ## PHL (Philadelphia, Pennsylvania, USA) checkgroups:news@vfl.paramax.com:phl.*:doit newgroup:news@vfl.paramax.com:phl.*:doit rmgroup:news@vfl.paramax.com:phl.*:doit ## PIN (Personal Internauts' NetNews) checkgroups:pin-admin@forus.or.jp:pin.*:doit newgroup:pin-admin@forus.or.jp:pin.*:doit rmgroup:pin-admin@forus.or.jp:pin.*:doit ## PIPEX (UUNET WorldCom UK) # Contact: Russell Vincent checkgroups:news-control@ops.pipex.net:pipex.*:doit newgroup:news-control@ops.pipex.net:pipex.*:doit rmgroup:news-control@ops.pipex.net:pipex.*:doit ## PITT (University of Pittsburgh, PA) checkgroups:news+@pitt.edu:pitt.*:doit checkgroups:news@toads.pgh.pa.us:pitt.*:doit newgroup:news+@pitt.edu:pitt.*:doit newgroup:news@toads.pgh.pa.us:pitt.*:doit rmgroup:news+@pitt.edu:pitt.*:doit rmgroup:news@toads.pgh.pa.us:pitt.*:doit ## PL (Poland and Polish language) # URL: http://www.usenet.pl/doc/news-pl-new-site-faq.html # Admin group: pl.news.admin # Key URL: http://www.usenet.pl/doc/news-pl-new-site-faq.html#pgp # *PGP* See comment at top of file. newgroup:*:pl.*:drop rmgroup:*:pl.*:drop checkgroups:michalj@*fuw.edu.pl:pl.*:verify-pl.announce.newgroups checkgroups:newgroup@usenet.pl:pl.*:verify-pl.announce.newgroups newgroup:michalj@*fuw.edu.pl:pl.*:verify-pl.announce.newgroups newgroup:newgroup@usenet.pl:pl.*:verify-pl.announce.newgroups rmgroup:michalj@*fuw.edu.pl:pl.*:verify-pl.announce.newgroups rmgroup:newgroup@usenet.pl:pl.*:verify-pl.announce.newgroups ## PLANET (*LOCAL* -- PlaNet FreeNZ co-operative, New Zealand) # Contact: office@pl.net # For local use only, contact the above address for information. newgroup:*:planet.*:mail rmgroup:*:planet.*:doit ## PRIMA (*LOCAL* -- prima.ruhr.de/Prima e.V. in Germany) # Contact: admin@prima.ruhr.de # For local use only, contact the above address for information. newgroup:*:prima.*:mail rmgroup:*:prima.*:doit ## PSU (*LOCAL* -- Penn State University, USA) # Contact: Dave Barr (barr@math.psu.edu) # For local use only, contact the above address for information. newgroup:*:psu.*:mail rmgroup:*:psu.*:doit ## PT (Portugal and Portuguese language) # URL: http://www.usenet-pt.org/ # Admin group: pt.internet.usenet # Key URL: http://www.usenet-pt.org/control@usenet-pt.org.asc # *PGP* See comment at top of file. newgroup:*:pt.*:drop rmgroup:*:pt.*:drop checkgroups:pmelo@*.inescc.pt:pt.*:verify-control@usenet-pt.org newgroup:pmelo@*.inescc.pt:pt.*:verify-control@usenet-pt.org rmgroup:pmelo@*.inescc.pt:pt.*:verify-control@usenet-pt.org ## PUBNET (*DEFUNCT* -- ?) # URL: ftp://ftp.isc.org/pub/usenet/control/pubnet/pubnet.config.gz # This hierarchy is defunct. Please remove it. newgroup:*:pubnet.*:mail rmgroup:*:pubnet.*:doit ## RELCOM (Commonwealth of Independent States) # URL: ftp://ftp.relcom.ru/pub/relcom/netinfo/ # Admin group: relcom.netnews # Key URL: ftp://ftp.relcom.ru/pub/relcom/netinfo/coordpubkey.txt # *PGP* See comment at top of file. newgroup:*:relcom.*:drop rmgroup:*:relcom.*:drop checkgroups:coord@*.relcom.ru:relcom.*:verify-relcom.newsgroups newgroup:coord@*.relcom.ru:relcom.*:verify-relcom.newsgroups rmgroup:coord@*.relcom.ru:relcom.*:verify-relcom.newsgroups ## RPI (*LOCAL* -- Rensselaer Polytechnic Institute, Troy, NY, USA) # Contact: sofkam@rpi.edu # For local use only, contact the above address for information. newgroup:*:rpi.*:mail rmgroup:*:rpi.*:doit ## SAAR (Saarland Region, Germany) # URL: http://www.saar-admin-news.de/ # Admin group: saar.admin.news # Key URL: http://www.saar-admin-news.de/saar-control.asc # *PGP* See comment at top of file. newgroup:*:saar.*:drop rmgroup:*:saar.*:drop checkgroups:control@saar-admin-news.de:saar.*:verify-saar-control newgroup:control@saar-admin-news.de:saar.*:verify-saar-control rmgroup:control@saar-admin-news.de:saar.*:verify-saar-control ## SACHSNET (Sachsen [Saxony], Germany) checkgroups:root@lusatia.de:sachsnet.*:doit newgroup:root@lusatia.de:sachsnet.*:doit rmgroup:root@lusatia.de:sachsnet.*:doit ## SAT (San Antonio, Texas, USA) # Contact: satgroup@endicor.com # Admin group: sat.usenet.config # *PGP* See comment at top of file. newgroup:*:sat.*:drop rmgroup:*:sat.*:drop checkgroups:satgroup@endicor.com:sat.*:verify-satgroup@endicor.com newgroup:satgroup@endicor.com:sat.*:verify-satgroup@endicor.com rmgroup:satgroup@endicor.com:sat.*:verify-satgroup@endicor.com ## SBAY (South Bay/Silicon Valley, California) # URL: http://www.sbay.org/sbay-newsgroups.html checkgroups:ikluft@thunder.sbay.org:sbay.*:doit checkgroups:steveh@grafex.sbay.org:sbay.*:doit newgroup:ikluft@thunder.sbay.org:sbay.*:doit newgroup:steveh@grafex.sbay.org:sbay.*:doit rmgroup:ikluft@thunder.sbay.org:sbay.*:doit rmgroup:steveh@grafex.sbay.org:sbay.*:doit ## SCHULE (?) # Contact: schule-admin@roxel.ms.sub.org # URL: http://home.pages.de/~schule-admin/ # Admin group: schule.admin # Key URL: http://www.afaik.de/usenet/admin/schule/control/schule.asc # Key fingerprint: 64 06 F0 AE E1 46 85 0C BD CA 0E 53 8B 1E 73 D2 # *PGP* See comment at top of file. newgroup:*:schule.*:drop rmgroup:*:schule.*:drop checkgroups:newsctrl@schule.de:schule.*:verify-schule.konfig newgroup:newsctrl@schule.de:schule.*:verify-schule.konfig rmgroup:newsctrl@schule.de:schule.*:verify-schule.konfig ## SCOT (Scotland) # URL: http://scot.news-admin.org/ # Admin group: scot.newsgroups.discuss # Key URL: http://scot.news-admin.org/signature.html # *PGP* See comment at top of file. newgroup:*:scot.*:drop rmgroup:*:scot.*:drop checkgroups:control@scot.news-admin.org:scot.*:verify-control@scot.news-admin.org newgroup:control@scot.news-admin.org:scot.*:verify-control@scot.news-admin.org rmgroup:control@scot.news-admin.org:scot.*:verify-control@scot.news-admin.org ## SCOUT (Scouts and guides) # URL: http://news.scoutnet.org/ # Admin group: scout.admin # Key URL: http://news.scoutnet.org/scout-pgpkey.asc # *PGP* See comment at top of file. newgroup:*:scout.*:drop rmgroup:*:scout.*:drop checkgroups:control@news.scoutnet.org:scout.*:verify-control@news.scoutnet.org newgroup:control@news.scoutnet.org:scout.*:verify-control@news.scoutnet.org rmgroup:control@news.scoutnet.org:scout.*:verify-control@news.scoutnet.org ## SDSU (*LOCAL* -- San Diego State University, CA) # Contact: Craig R. Sadler # For local use only, contact the above address for information. newgroup:*:sdsu.*:mail rmgroup:*:sdsu.*:doit ## SE (Sweden) # Contact: usenet@usenet-se.net # Admin group: se.internet.news.meddelanden # Key fingerprint: 68 03 F0 FD 0C C3 4E 69 6F 0D 0C 60 3C 58 63 96 # *PGP* See comment at top of file. newgroup:*:se.*:drop rmgroup:*:se.*:drop checkgroups:usenet@usenet-se.net:se.*:verify-usenet-se newgroup:usenet@usenet-se.net:se.*:verify-usenet-se rmgroup:usenet@usenet-se.net:se.*:verify-usenet-se ## SEATTLE (Seattle, Washington, USA) checkgroups:billmcc@akita.com:seattle.*:doit checkgroups:graham@ee.washington.edu:seattle.*:doit newgroup:billmcc@akita.com:seattle.*:doit newgroup:graham@ee.washington.edu:seattle.*:doit rmgroup:billmcc@akita.com:seattle.*:doit rmgroup:graham@ee.washington.edu:seattle.*:doit ## SFNET (Finland) # Contact: sfnet@cs.tut.fi # URL: http://www.cs.tut.fi/sfnet/ # Admin group: sfnet.ryhmat+listat # Key fingerprint: DE79 33C2 D359 D128 44E5 6A0C B6E3 0E53 6933 A636 # *PGP* See comment at top of file. newgroup:*:sfnet.*:drop rmgroup:*:sfnet.*:drop checkgroups:sfnet@*cs.tut.fi:sfnet.*:verify-sfnet@cs.tut.fi newgroup:sfnet@*cs.tut.fi:sfnet.*:verify-sfnet@cs.tut.fi rmgroup:sfnet@*cs.tut.fi:sfnet.*:verify-sfnet@cs.tut.fi ## SHAMASH (Jewish) checkgroups:archives@israel.nysernet.org:shamash.*:doit newgroup:archives@israel.nysernet.org:shamash.*:doit rmgroup:archives@israel.nysernet.org:shamash.*:doit ## SI (The Republic of Slovenia) # URL: http://www.arnes.si/news/config/ # Admin group: si.news.announce.newsgroups # Key URL: http://www.arnes.si/news/config/ # *PGP* See comment at top of file. newgroup:*:si.*:drop rmgroup:*:si.*:drop checkgroups:news-admin@arnes.si:si.*:verify-si.news.announce.newsgroups newgroup:news-admin@arnes.si:si.*:verify-si.news.announce.newsgroups rmgroup:news-admin@arnes.si:si.*:verify-si.news.announce.newsgroups ## SJ (St. John's, Newfoundland and Labrador, Canada) # Contact: randy@mun.ca checkgroups:randy@mun.ca:sj.*:doit newgroup:randy@mun.ca:sj.*:doit rmgroup:randy@mun.ca:sj.*:doit ## SK (Slovakia) checkgroups:uhlar@ccnews.ke.sanet.sk:sk.*:doit newgroup:uhlar@ccnews.ke.sanet.sk:sk.*:doit rmgroup:uhlar@ccnews.ke.sanet.sk:sk.*:doit ## SLO (San Luis Obispo, CA) checkgroups:news@punk.net:slo.*:doit newgroup:news@punk.net:slo.*:doit rmgroup:news@punk.net:slo.*:doit ## SOLENT (Solent region, England) checkgroups:news@tcp.co.uk:solent.*:doit newgroup:news@tcp.co.uk:solent.*:doit rmgroup:news@tcp.co.uk:solent.*:doit ## SPOKANE (Spokane, Washington, USA) checkgroups:usenet@news.spokane.wa.us:spokane.*:doit newgroup:usenet@news.spokane.wa.us:spokane.*:doit rmgroup:usenet@news.spokane.wa.us:spokane.*:doit ## SSLUG (*PRIVATE* -- SkÃ¥ne Sjælland Linux User Group) # URL: http://en.sslug.dk/ # For private use only. newgroup:sparre@sslug.se:sslug.*:mail rmgroup:sparre@sslug.se:sslug.*:doit ## STAROFFICE (StarOffice business suite, Sun Microsystems, Inc.) # Contact: news@starnews.sun.com # Admin group: staroffice.admin # Key fingerprint: C6 3E 81 6F 2A 19 D3 84 72 51 F9 1B E3 B9 B2 C9 # Syncable server: starnews.sun.com # *PGP* See comment at top of file. newgroup:*:staroffice.*:drop rmgroup:*:staroffice.*:drop checkgroups:news@stardivision.de:staroffice.*:verify-staroffice.admin newgroup:news@stardivision.de:staroffice.*:verify-staroffice.admin rmgroup:news@stardivision.de:staroffice.*:verify-staroffice.admin ## STGT (Stuttgart, Germany) # Contact: news@news.uni-stuttgart.de # URL: http://news.uni-stuttgart.de/hierarchie/stgt/ # Admin group: stgt.net # Key URL: http://news.uni-stuttgart.de/hierarchie/stgt/stgt-control.txt # Key fingerprint: BA A4 0A 54 8F F5 F5 1E 72 48 51 AE 09 51 DE 44 # *PGP* See comment at top of file. newgroup:*:stgt.*:drop rmgroup:*:stgt.*:drop checkgroups:stgt-control@news.uni-stuttgart.de:stgt.*:verify-stgt-control newgroup:stgt-control@news.uni-stuttgart.de:stgt.*:verify-stgt-control rmgroup:stgt-control@news.uni-stuttgart.de:stgt.*:verify-stgt-control ## STL (Saint Louis, Missouri, USA) checkgroups:news@icon-stl.net:stl.*:doit newgroup:news@icon-stl.net:stl.*:doit rmgroup:news@icon-stl.net:stl.*:doit ## SU (*LOCAL* -- Stanford University, USA) # Contact: news@news.stanford.edu # For local use only, contact the above address for information. newgroup:*:su.*:mail rmgroup:*:su.*:doit ## SUNET (Swedish University Network) checkgroups:ber@*.sunet.se:sunet.*:doit newgroup:ber@*.sunet.se:sunet.*:doit rmgroup:ber@*.sunet.se:sunet.*:doit ## SURFNET (Dutch Universities network) checkgroups:news@info.nic.surfnet.nl:surfnet.*:doit newgroup:news@info.nic.surfnet.nl:surfnet.*:doit rmgroup:news@info.nic.surfnet.nl:surfnet.*:doit ## SWNET (Sverige, Sweden) checkgroups:ber@sunic.sunet.se:swnet.*:doit newgroup:ber@sunic.sunet.se:swnet.*:doit rmgroup:ber@sunic.sunet.se:swnet.*:doit ## SYD (Sydney, Australia) # Contact: ausadmin@aus.news-admin.org # URL: http://syd.news-admin.org/ # Key URL: http://aus.news-admin.org/ausadmin.asc # *PGP* See comment at top of file. newgroup:*:syd.*:drop rmgroup:*:syd.*:drop checkgroups:ausadmin@aus.news-admin.org:syd.*:verify-ausadmin@aus.news-admin.org newgroup:ausadmin@aus.news-admin.org:syd.*:verify-ausadmin@aus.news-admin.org rmgroup:ausadmin@aus.news-admin.org:syd.*:verify-ausadmin@aus.news-admin.org ## SZAF (*PRIVATE* -- Szafe im Netz) # Contact: hirtenrat@szaf.org # Admin group: szaf.admin # Key URL: http://news.szaf.org/szaf/szaf-key.txt # For private use only, contact the above address for information. # *PGP* See comment at top of file. newgroup:*:szaf.*:drop rmgroup:*:szaf.*:drop newgroup:hirtenrat@szaf.org:szaf.*:mail rmgroup:hirtenrat@szaf.org:szaf.*:verify-Hirtenrat ## T-NETZ (*DEFUNCT* -- Germany) # This hierarchy is defunct. Please remove it. newgroup:*:t-netz.*:mail rmgroup:*:t-netz.*:doit ## TAMU (Texas A&M University) # Contact: Philip Kizer checkgroups:news@tamsun.tamu.edu:tamu.*:doit newgroup:news@tamsun.tamu.edu:tamu.*:doit rmgroup:news@tamsun.tamu.edu:tamu.*:doit ## TAOS (Taos, New Mexico, USA) # Contact: Chris Gunn checkgroups:cgunn@laplaza.org:taos.*:doit newgroup:cgunn@laplaza.org:taos.*:doit rmgroup:cgunn@laplaza.org:taos.*:doit ## TCFN (Toronto Free Community Network, Canada) checkgroups:news@t-fcn.net:tcfn.*:doit newgroup:news@t-fcn.net:tcfn.*:doit rmgroup:news@t-fcn.net:tcfn.*:doit ## TELE (*LOCAL* -- Tele Danmark Internet) # Contact: usenet@tdk.net # For local use only, contact the above address for information. newgroup:*:tele.*:mail rmgroup:*:tele.*:doit ## TERMVAKT (*LOCAL* -- University of Oslo, Norway) # Contact: jani@ifi.uio.no # For local use only, contact the above address for information. newgroup:*:termvakt.*:mail rmgroup:*:termvakt.*:doit ## TEST (*RESERVED* -- Local test hierarchy) # # Historically used as a local test hierarchy. It is not a good idea to # use this hierarchy name on any production server since they may occur # on many unconnected sites. # newgroup:*:test.*:drop rmgroup:*:test.*:drop ## THUR (Thuringia, Germany) # Contact: usenet@thur.de # URL: http://www.thur.de/thurnet/old/thurnews.html # Admin group: thur.net.news.groups # Key fingerprint: 7E 3D 73 13 93 D4 CA 78 39 DE 3C E7 37 EE 22 F1 # Syncable server: news.thur.de # *PGP* See comment at top of file. newgroup:*:thur.*:drop rmgroup:*:thur.*:drop checkgroups:usenet@thur.de:thur.*:verify-thur.net.news.groups newgroup:usenet@thur.de:thur.*:verify-thur.net.news.groups rmgroup:usenet@thur.de:thur.*:verify-thur.net.news.groups ## TNN (*DEFUNCT* -- The Network News, Japan) # This hierarchy is defunct. Please remove it. newgroup:netnews@news.iij.ad.jp:tnn.*:mail newgroup:tnn@iij-mc.co.jp:tnn.*:mail rmgroup:netnews@news.iij.ad.jp:tnn.*:doit rmgroup:tnn@iij-mc.co.jp:tnn.*:doit ## TRIANGLE (Research Triangle, Central North Carolina, USA) checkgroups:jfurr@acpub.duke.edu:triangle.*:doit checkgroups:news@news.duke.edu:triangle.*:doit checkgroups:tas@concert.net:triangle.*:doit newgroup:jfurr@acpub.duke.edu:triangle.*:doit newgroup:news@news.duke.edu:triangle.*:doit newgroup:tas@concert.net:triangle.*:doit rmgroup:jfurr@acpub.duke.edu:triangle.*:doit rmgroup:news@news.duke.edu:triangle.*:doit rmgroup:tas@concert.net:triangle.*:doit ## TUM (Technische Universitaet Muenchen) checkgroups:news@informatik.tu-muenchen.de:tum.*:doit newgroup:news@informatik.tu-muenchen.de:tum.*:doit rmgroup:news@informatik.tu-muenchen.de:tum.*:doit ## TW (Taiwan) checkgroups:ltc@news.cc.nctu.edu.tw:tw.*:doit newgroup:ltc@news.cc.nctu.edu.tw:tw.*:doit rmgroup:ltc@news.cc.nctu.edu.tw:tw.*:doit ## TW.K-12 (Taiwan K-12 Discussion) checkgroups:k-12@news.nchu.edu.tw:tw.k-12.*:doit newgroup:k-12@news.nchu.edu.tw:tw.k-12.*:doit rmgroup:k-12@news.nchu.edu.tw:tw.k-12.*:doit ## TX (Texas, USA) checkgroups:eric@cirr.com:tx.*:doit checkgroups:fletcher@cs.utexas.edu:tx.*:doit checkgroups:usenet@academ.com:tx.*:doit newgroup:eric@cirr.com:tx.*:doit newgroup:fletcher@cs.utexas.edu:tx.*:doit newgroup:usenet@academ.com:tx.*:doit rmgroup:eric@cirr.com:tx.*:doit rmgroup:fletcher@cs.utexas.edu:tx.*:doit rmgroup:usenet@academ.com:tx.*:doit ## UCB (University of California Berkeley, USA) # Contact: Chris van den Berg # URL: http://www.net.berkeley.edu/usenet/ # Key URL: http://www.net.berkeley.edu/usenet/usenet.asc # Key fingerprint: 96 B8 8E 9A 98 09 37 7D 0E EC 81 88 DB 90 29 BF # *PGP* See comment at top of file. newgroup:*:ucb.*:drop rmgroup:*:ucb.*:drop checkgroups:usenet@agate.berkeley.edu:ucb.*:verify-ucb.news newgroup:usenet@agate.berkeley.edu:ucb.*:verify-ucb.news rmgroup:usenet@agate.berkeley.edu:ucb.*:verify-ucb.news ## UCD (University of California Davis, USA) checkgroups:usenet@mark.ucdavis.edu:ucd.*:doit checkgroups:usenet@rocky.ucdavis.edu:ucd.*:doit newgroup:usenet@mark.ucdavis.edu:ucd.*:doit newgroup:usenet@rocky.ucdavis.edu:ucd.*:doit rmgroup:usenet@mark.ucdavis.edu:ucd.*:doit rmgroup:usenet@rocky.ucdavis.edu:ucd.*:doit ## UFRA (Unterfranken, Deutschland) # # This hierarchy is still in use, but it has no active maintainer. # Control messages for this hierarchy should not be honored without # confirming that the sender is the new hierarchy maintainer. # # Admin group: ufra.admin # Key fingerprint: F7 AD 96 D8 7A 3F 7E 84 02 0C 83 9A DB 8F EB B8 # *PGP* See comment at top of file. newgroup:*:ufra.*:drop rmgroup:*:ufra.*:drop ## UIUC (*DEFUNCT* -- University of Illinois at Urbana-Champaign, USA) # Contact: news@ks.uiuc.edu # This hierarchy is defunct. Please remove it. newgroup:*:uiuc.*:mail rmgroup:*:uiuc.*:doit ## UK (United Kingdom of Great Britain and Northern Ireland) # URL: http://www.usenet.org.uk/ # Admin group: uk.net.news.announce # Key URL: http://www.usenet.org.uk/newsadmins.html # *PGP* See comment at top of file. newgroup:*:uk.*:drop rmgroup:*:uk.*:drop checkgroups:control@usenet.org.uk:uk.*:verify-uk.net.news.announce newgroup:control@usenet.org.uk:uk.*:verify-uk.net.news.announce rmgroup:control@usenet.org.uk:uk.*:verify-uk.net.news.announce ## UKR (Ukraine) checkgroups:ay@sita.kiev.ua:ukr.*:doit newgroup:ay@sita.kiev.ua:ukr.*:doit rmgroup:ay@sita.kiev.ua:ukr.*:doit ## ULM (*PRIVATE* -- Ulm, Germany) # Admin group: ulm.misc # Syncable server: news.in-ulm.de # For private use only. newgroup:*:ulm.*:mail rmgroup:*:ulm.*:doit ## UMICH (University of Michigan, USA) checkgroups:*@*.umich.edu:umich.*:doit newgroup:*@*.umich.edu:umich.*:doit rmgroup:*@*.umich.edu:umich.*:doit ## UMN (University of Minnesota, USA) newgroup:edh@*.tc.umn.edu:umn.*:doit newgroup:news@*.tc.umn.edu:umn.*:doit newgroup:Michael.E.Hedman-1@umn.edu:umn.*:doit newgroup:edh@*.tc.umn.edu:umn*class.*:mail newgroup:news@*.tc.umn.edu:umn*class.*:mail newgroup:Michael.E.Hedman-1@umn.edu:umn*class.*:mail rmgroup:news@*.tc.umn.edu:umn.*:doit rmgroup:edh@*.tc.umn.edu:umn.*:doit rmgroup:Michael.E.Hedman-1@umn.edu:umn.*:doit ## UN (*HISTORIC* -- The United Nations) # # This hierarchy is not entirely defunct, but it receives very little # traffic and is included primarily for the sake of completeness. # # Admin group: un.public.usenet.admin # *PGP* See comment at top of file. newgroup:*:un.*:drop rmgroup:*:un.*:drop checkgroups:news@news.itu.int:un.*:verify-ungroups@news.itu.int newgroup:news@news.itu.int:un.*:verify-ungroups@news.itu.int rmgroup:news@news.itu.int:un.*:verify-ungroups@news.itu.int ## UO (University of Oregon, Eugene, Oregon, USA) checkgroups:newsadmin@news.uoregon.edu:uo.*:doit newgroup:newsadmin@news.uoregon.edu:uo.*:doit rmgroup:newsadmin@news.uoregon.edu:uo.*:doit ## US (United States of America) # Contact: us-control@lists.killfile.org # Admin group: us.config # Key fingerprint: BB96 EB2C CFD0 75C8 E9DE C2C2 1DA2 9D87 B73C AF1B # *PGP* See comment at top of file. newgroup:*:us.*:drop rmgroup:*:us.*:drop checkgroups:us-control@lists.killfile.org:us.*:verify-us-control@lists.killfile.org newgroup:us-control@lists.killfile.org:us.*:verify-us-control@lists.killfile.org rmgroup:us-control@lists.killfile.org:us.*:verify-us-control@lists.killfile.org ## UT (*LOCAL* -- University of Toronto, Canada) # URL: http://www.utoronto.ca/ns/utornews/ #newgroup:news@ecf.toronto.edu:ut.*:doit #newgroup:news@ecf.toronto.edu:ut.class.*:mail #rmgroup:news@ecf.toronto.edu:ut.*:doit ## UTA (Finnish) checkgroups:news@news.cc.tut.fi:uta.*:doit newgroup:news@news.cc.tut.fi:uta.*:doit rmgroup:news@news.cc.tut.fi:uta.*:doit ## UTEXAS (*LOCAL* -- University of Texas, USA) # URL: http://www.utexas.edu/its/usenet/index.php newgroup:fletcher@cs.utexas.edu:utexas.*:doit newgroup:news@geraldo.cc.utexas.edu:utexas.*:doit newgroup:fletcher@cs.utexas.edu:utexas*class.*:mail newgroup:news@geraldo.cc.utexas.edu:utexas*class.*:mail rmgroup:fletcher@cs.utexas.edu:utexas.*:doit rmgroup:news@geraldo.cc.utexas.edu:utexas.*:doit ## UTWENTE (*LOCAL* -- University of Twente, Netherlands) # Contact: newsmaster@utwente.nl # For local use only, contact the above address for information. newgroup:*:utwente.*:mail rmgroup:*:utwente.*:doit ## UVA (*LOCAL* -- University of Virginia, USA) # Contact: usenet@virginia.edu # For local use only, contact the above address for information. newgroup:*:uva.*:mail rmgroup:*:uva.*:doit ## UW (University of Waterloo, Canada) # Admin group: uw.newsgroups # Syncable server: news.uwaterloo.ca # *PGP* See comment at top of file. newgroup:*:uw.*:drop rmgroup:*:uw.*:drop checkgroups:newsgroups@news.uwaterloo.ca:uw.*:verify-uw.newsgroups newgroup:newsgroups@news.uwaterloo.ca:uw.*:verify-uw.newsgroups rmgroup:newsgroups@news.uwaterloo.ca:uw.*:verify-uw.newsgroups ## UWARWICK (*LOCAL* -- University of Warwick, UK) # Contact: Jon Harley # For local use only, contact the above address for information. newgroup:*:uwarwick.*:mail rmgroup:*:uwarwick.*:doit ## UWO (University of Western Ontario, London, Canada) # URL: http://www.uwo.ca/its/news/groups.uwo.html checkgroups:reggers@julian.uwo.ca:uwo.*:doit newgroup:reggers@julian.uwo.ca:uwo.*:doit rmgroup:reggers@julian.uwo.ca:uwo.*:doit ## VAN (Vancouver, British Columbia, Canada) checkgroups:bc_van_usenet@fastmail.ca:van.*:doit newgroup:bc_van_usenet@fastmail.ca:van.*:doit rmgroup:bc_van_usenet@fastmail.ca:van.*:doit ## VEGAS (Las Vegas, Nevada, USA) checkgroups:cshapiro@netcom.com:vegas.*:doit checkgroups:doctor@netcom.com:vegas.*:doit newgroup:cshapiro@netcom.com:vegas.*:doit newgroup:doctor@netcom.com:vegas.*:doit rmgroup:cshapiro@netcom.com:vegas.*:doit rmgroup:doctor@netcom.com:vegas.*:doit ## VGC (Japan groups?) checkgroups:news@isl.melco.co.jp:vgc.*:doit newgroup:news@isl.melco.co.jp:vgc.*:doit rmgroup:news@isl.melco.co.jp:vgc.*:doit ## VMSNET (VMS Operating System) checkgroups:cts@dragon.com:vmsnet.*:doit newgroup:cts@dragon.com:vmsnet.*:doit rmgroup:cts@dragon.com:vmsnet.*:doit ## WA (Western Australia) # Contact: ausadmin@aus.news-admin.org # URL: http://wa.news-admin.org/ # Key URL: http://aus.news-admin.org/ausadmin.asc # *PGP* See comment at top of file. newgroup:*:wa.*:drop rmgroup:*:wa.*:drop checkgroups:ausadmin@aus.news-admin.org:wa.*:verify-ausadmin@aus.news-admin.org newgroup:ausadmin@aus.news-admin.org:wa.*:verify-ausadmin@aus.news-admin.org rmgroup:ausadmin@aus.news-admin.org:wa.*:verify-ausadmin@aus.news-admin.org ## WADAI (Japanese ?) checkgroups:kohe-t@*wakayama-u.ac.jp:wadai.*:doit newgroup:kohe-t@*wakayama-u.ac.jp:wadai.*:doit rmgroup:kohe-t@*wakayama-u.ac.jp:wadai.*:doit ## WALES (Wales) # Contact: committee@wales-usenet.org # URL: http://www.wales-usenet.org/ # Admin group: wales.usenet.config # Key URL: http://www.wales-usenet.org/english/newsadmin.txt # Key fingerprint: 2D 9E DE DF 12 DA 34 5C 49 E1 EE 28 E3 AB 0D AD # *PGP* See comment at top of file. newgroup:*:wales.*:drop rmgroup:*:wales.*:drop checkgroups:control@wales-usenet.org:wales.*:verify-wales-usenet newgroup:control@wales-usenet.org:wales.*:verify-wales-usenet rmgroup:control@wales-usenet.org:wales.*:verify-wales-usenet ## WASH (Washington State, USA) checkgroups:graham@ee.washington.edu:wash.*:doit newgroup:graham@ee.washington.edu:wash.*:doit rmgroup:graham@ee.washington.edu:wash.*:doit ## WEST-VIRGINIA (West Virginia, USA) checkgroups:bryan27@hgo.net:west-virginia.*:doit newgroup:mark@bluefield.net:west-virginia.*:doit newgroup:bryan27@hgo.net:west-virginia.*:doit rmgroup:mark@bluefield.net:west-virginia.*:doit rmgroup:bryan27@hgo.net:west-virginia.*:doit ## WORLDONLINE (*LOCAL* -- ?) # Contact: newsmaster@worldonline.nl # For local use only, contact the above address for information. newgroup:*:worldonline.*:mail rmgroup:*:worldonline.*:doit ## WPG (Winnipeg, Manitoba, Canada) # # This hierarchy is still in use, but it has no active maintainer. # Control messages for this hierarchy should not be honored without # confirming that the sender is the new hierarchy maintainer. ## WPI (*LOCAL* -- Worcester Polytechnic Institute, Worcester, MA) # For local use only. newgroup:aej@*.wpi.edu:wpi.*:mail rmgroup:aej@*.wpi.edu:wpi.*:doit ## WU (Washington University at St. Louis, MO) checkgroups:*@*.wustl.edu:wu.*:doit newgroup:*@*.wustl.edu:wu.*:doit rmgroup:*@*.wustl.edu:wu.*:doit ## X-PRIVAT (Italian) # Contact: dmitry@x-privat.org # URL: http://www.x-privat.org/ # Admin group: x-privat.info # Key URL: http://www.x-privat.org/dmitry.asc # Key fingerprint: 9B 0A 7E 68 27 80 C7 96 47 6B 03 90 51 05 68 43 # *PGP* See comment at top of file. newgroup:*:x-privat.*:drop rmgroup:*:x-privat.*:drop checkgroups:dmitry@x-privat.org:x-privat.*:verify-dmitry@x-privat.org newgroup:dmitry@x-privat.org:x-privat.*:verify-dmitry@x-privat.org rmgroup:dmitry@x-privat.org:x-privat.*:verify-dmitry@x-privat.org ## XS4ALL (XS4ALL, Netherlands) # Contact: XS4ALL Newsmaster checkgroups:news@xs4all.nl:xs4all.*:doit newgroup:news@xs4all.nl:xs4all.*:doit rmgroup:news@xs4all.nl:xs4all.*:doit ## YORK (*LOCAL* -- York University, Toronto, ON) # Contact: Peter Marques # For local use only, contact the above address for information. newgroup:*:york.*:mail rmgroup:*:york.*:doit ## Z-NETZ (German non-Internet based network) # Contact: teko@dinoex.sub.org # Admin group: z-netz.koordination.user+sysops # Key URL: ftp://ftp.dinoex.de/pub/keys/z-netz.koordination.user+sysops.asc # *PGP* See comment at top of file. newgroup:*:z-netz.*:drop rmgroup:*:z-netz.*:drop checkgroups:teko@dinoex.sub.org:z-netz.*:verify-z-netz.koordination.user+sysops newgroup:teko@dinoex.sub.org:z-netz.*:verify-z-netz.koordination.user+sysops rmgroup:teko@dinoex.sub.org:z-netz.*:verify-z-netz.koordination.user+sysops ## ZA (South Africa) checkgroups:ccfj@hippo.ru.ac.za:za.*:doit checkgroups:root@duvi.eskom.co.za:za.*:doit newgroup:ccfj@hippo.ru.ac.za:za.*:doit newgroup:root@duvi.eskom.co.za:za.*:doit rmgroup:ccfj@hippo.ru.ac.za:za.*:doit rmgroup:root@duvi.eskom.co.za:za.*:doit ## ZER (*DEFUNCT* -- Germany) # This hierarchy is defunct. Please remove it. newgroup:*:zer.*:mail rmgroup:*:zer.*:doit ## ------------------------------------------------------------------------- ## CONTROL.CTL ADDITIONS ## ------------------------------------------------------------------------- # Incoming encodings in newgroup and checkgroups control articles. # These lines are additions to the official control.ctl file. # Default (for any description). /encoding/:*:*:cp1252 /encoding/:*:cn.*:gb18030 /encoding/:*:han.*:gb18030 /encoding/:*:fido7.*:koi8-r /encoding/:*:medlux.*:koi8-r /encoding/:*:relcom.*:koi8-r /encoding/:*:ukr.*:koi8-u /encoding/:*:fr.*:iso-8859-15 /encoding/:*:nctu.*:big5 /encoding/:*:ncu.*:big5 /encoding/:*:tw.*:big5 /encoding/:*:scout.forum.chinese:big5 /encoding/:*:scout.forum.korean:big5 /encoding/:*:fido.*:utf-8 inn-2.6.0/samples/filter_innd.pl0000644000175200017520000001477512575023702016234 0ustar iuliusiulius# # $Id: filter_innd.pl 9205 2011-06-13 15:08:31Z iulius $ # # Sample Perl filtering file for the innd hooks. # # This file gets loaded at innd process startup, and everytime a # "ctlinnd reload filter.perl 'reason'" or a # "ctlinnd reload all 'reason'" is done. # # Before this file is loaded, the perl routine `filter_before_reload' is # called, and after it's finished loading, the perl routine # `filter_after_reload' is called. See startup_innd.pl for more details. # # When filtering is disabled, the filter_end() Perl routine is called, # if defined, prior to the deactivation of the filter. # # The following routines can be defined here for use by innd: # # sub filter_art { ... } # # This routine is called before every article is accepted for # posting. Is is called with no arguments, but has access to # all the non-empty standard headers of the article via the # global associative array `%hdr.' If it returns the empty # string ("") then the article is accepted. If it returns any # non-null string value, then the article is rejected and the # returned string value is logged as the reason why (make sure # that such a message is properly encoded in UTF-8 so as to comply # with the NNTP protocol). # # The standard headers are: # # Approved, Control, Date, Distribution, Expires, # From, Lines, Message-ID, Newsgroups, Path, # Reply-To, Sender, Subject, Supersedes, Bytes, # Also-Control, References # # sub filter_mode { ... } # # This routine is called every time `go', `pause', or # `throttle' is called. It is called with no arguments and # returns no value. The global associative array `%mode' has # three keyed values stored in it: # # 'Mode' The current mode # ("running", "paused", "throttled") # 'NewMode" The new mode # 'reason' The reason given. # # For example: %mode = ('Mode', 'running', # 'NewMode', 'throttled', # 'reason', 'doing nightly backups') # # If filter_art is not defined when this file is done loading, then # filtering is disabled. If any syntax error occurs when loading the file, # then filtering is disabled. # # sub filter_messageid { ... } # # This routine is called when each article (in streaming # mode only) is checked to see if INN wants to accept the # article. If it returns the empty string, the article # is accepted. If it returns a non-empty value, the # article is refused (make sure that such a message is # properly encoded in UTF-8 so as to comply with the # NNTP protocol). It is called with one argument, # the message-id to check. # # Called on each article innd receives from a peer. Return "" to accept, # and any other non-null string to reject. If rejecting the string returned # will be part of the logged reason. # sub filter_art { my $rval = "" ; # Assume we'll accept. Cannot be `0' ### Remove two leading '##' from the following section (and then ### "ctlinnd reload filter.perl 'reason'" and the filter will reject articles that ### have "make money" in the subject, or are posted to more than 10 ### newsgroups. ## my ($maxgroups) = 10 ; ## ### Normally this output would be lost, but if you run innd with '-d -f' you ### can see what's going on. ### ### foreach $key (sort keys %hdr) { ### print "Header:\t$key Value:\t $hdr{$key}\n" ; ### } ## ## if ($hdr{"Subject"} =~ /\$*make.*money.*\$*/i ) { ## $rval = "no money requests here" ## } elsif ( ( @_ = split(",",$hdr{'Newsgroups'}) ) > $maxgroups ) { ## $rval = "too many groups" ; ### Kill article with "Re: " but no References: ## } elsif ($hdr{'Subject'} =~ /^Re: /o and $hdr{'References'} eq "") { ## $rval = "Followup without References:"; ### Kill article with invalid From: ## } elsif ($hdr{'From'} =~ /^\w*$/o or ## $hdr{'From'} !~ /^(.+?)\@([-\w\d]+\.)*([-\w\d]+)\.([-\w\d]{2,})$/o) { ## $rval = "From: is invalid, must be user\@[host.]domain.tld"; ## } ### ### print "Accepting\n" if ! $rval ; $rval ; } sub filter_mode { if ($mode{'NewMode'} eq "throttled" || $mode{'NewMode'} eq "paused") { # print "Closing spam database\n" ; # won't kill server. # &close_spam_database ; } else { # print "Opening spam database\n" ; # won't kill server # &open_spam_database ; } } sub filter_messageid { my ($messageid) = @_; $rval = ''; # $rval = 'No' if ($messageid =~ /a\.spam\.domain>?/i); $rval; } sub filter_end { # Do whatever you want to clean up things when Perl filtering is disabled. } ########################################################################### ## ## Another sample. More elaborate, but cleaner... from Christophe ## Wolfhugel . ## #### Regular expressions we reject. #### Format : Header => regexp => reason ##%reject = ( ## 'Subject' => { ## 'make.*money.*fast' => 'MMF rejected', ## 'cash.*cash.*cash' => 'Cash rejected' ## }, ##); ## ##sub filter_art { ## my($rval) = ''; ## my(@ng, $i, $j, $k, $l); ## ## if ($hdr{'From'} !~ /\@/o) { ## $rval = 'Invalid From'; ## } else { ## while (($i, $j) = each %reject) { ## while (($k, $l) = each %{$j}) { ## if ($hdr{$i} =~ /$k/i) { ## $rval = $l; ## goto the_end; ## } ## } ## } ## } ## @ng = split(/,/, $hdr{'Newsgroups'}); ## if ($#ng > 10) { ## $rval = 'ECP rejected'; ## } ##the_end: ## undef %hdr; ## return $rval ##} ## ##sub filter_mode { ##} ## ###%hdr = ( ### 'Subject' => 'Make money fast', ### 'From' => 'bozo@gov.org' ###); ###&filter_art; ########################################################################### ## ## From Chrisophe Wolfhugel again (wolf@pasteur.fr). This is not ## standalone code. ## ##Just for the fun, I've added following code to filter_innd.pl : ## ## ## Keep track of the From and subject. ## $i = "$hdr{'From'} $hdr{'Subject'}"; ## push(@history, $i); ## $history{$i}++; ## ## ## Reject the EMP. ## if ($history{$i} > 10) { ## $rval = "EMP rejected (appeared $history{$i} times): $i"; ## } ## ## ## Remove too old things. ## while ($#history > 1000) { ## delete($history{shift(@history)}); ## } ## ##It is pretty successfull in detecting and refusing excessive multi-posting. ##Same sender, same subject, appearing more than 10 times without the last ##1000 articles gets junked. ## ##Already catched a few hundreds : ## ##Nov 20 08:27:23.175 - vishnu.jussieu.fr <3292ac9a.4064710@nntp.cts.com> 437 EMP rejected (btr@trenet.com Be a Beta Tester!) ## ##That was just for the pleasure. It is still sucking a non significant CPU ##time on my slow Alpha. inn-2.6.0/samples/innreport.css0000644000175200017520000000316412575023702016122 0ustar iuliusiulius/* ** $Id: innreport.css 8170 2008-11-17 19:14:16Z iulius $ ** ** innreport.css -- for parameter html_css_url in innreport.conf ** ** Style created by Alexander Bartolich, 2008. */ div.ir-pageTitle { border-bottom:4px double black; border-top:4px double black; margin-bottom:2ex; margin-top:2ex; text-align:center; } div.ir-feedTotals { margin-bottom:1ex; margin-left:auto; margin-right:auto; text-align:center; } table.ir-archives, table.ir-report { border-collapse:collapse; margin-left:auto; margin-right:auto; margin-top:1ex; margin-bottom:1ex; } table.ir-archives td, table.ir-archives th, table.ir-report td, table.ir-report th { border:1px solid black; empty-cells:show; padding:0.3ex 0.3em 0.3ex 0.3em; } table.ir-archives th, table.ir-report th { font-weight:bold; background-color:#D3D3D3; } table.ir-archives th, table.ir-report th, tr.ir-totalRow td, tr.ir-headerRow th { border-bottom:2px solid black; border-top:2px solid black; } div.ir-pageFooter { border-top:4px double black; padding-top:1ex; margin-top:1ex; vertical-align:top; } div.ir-pageFooter img { border:0; float:left; margin-right:1em; } div.ir-versionNotice { font-size:small; } div.ir-section { border-top:1px solid black; } p.ir-sectionTitle { font-weight:bold; } div.ir-logFileLines { font-family:monospace; } div.ir-reportGraph { margin-left:auto; margin-right:auto; margin-top:1ex; margin-bottom:1ex; text-align:center; } td.ir-totalColumn { text-align:left; font-weight:bold; } tr.ir-oddRow td, td.ir-primaryKey { background-color:#F8E0E0; } inn-2.6.0/samples/nnrpd_auth.pl.in0000644000175200017520000000540412575023702016473 0ustar iuliusiulius#! /usr/bin/perl -w # fixscript will replace this line with code to load INN::Config ## ## Sample code for the nnrpd Perl authentication hooks. ## ## This file is loaded when a perl_auth: parameter is reached in ## readers.conf. If it defines a sub named authenticate, that ## function will be called during processing of a perl_auth: ## parameter. Attributes about the connection are passed to the ## program in the %attributes global variable. It should return an ## array with two elements: ## ## 1) NNTP response code. Should be one of the codes from %authcodes ## below to not risk violating the protocol. ## 2) An error string to be passed to the client (make sure that ## such a message is properly encoded in UTF-8 so as to comply with the ## NNTP protocol). ## Both elements are required. If there is a problem, nnrpd will die ## and syslog the exact error. ## The code below uses a user database based on CDB_File. It is ## provided here as an example of an authentication script. ## This file cannot be run as a standalone script, although it would be ## worthwhile to add some code so that it could so that one could test the ## results of various authentication and connection queries from the ## command line. The #! line at the top is just so that fixscript will ## work. use strict; use vars qw(%attributes %authcodes %users); # These codes are a widely implemented de facto standard. %authcodes = ('allowed' => 281, 'denied' => 481, 'error' => 403); # This sub should perform any initialization work that the # authentication stuff needs. sub auth_init { require CDB_File; tie (%users, 'CDB_File', $INN::Config::pathdb . '/users.cdb') or warn "Could not open $INN::Config::pathdb/users.cdb for users: $!\n"; } # This function is called for authentication requests. For details on # all the information passed to it, see ~news/doc/hook-perl. sub authenticate { return &checkuser(); } # This function assumes that there's a database tied as %users that # contains, keyed by users, a tab-separated list of the password (in # crypt format), whether they can post, a wildmat matching what # newsgroups they have access to, and the number of bytes per second # they're allowed to use. This section of the code only accesses the # username and password fields. See the file nnrpd_access.pl for # access rights based on the other fields. sub checkuser { my $user = $attributes{'username'}; my $pass = $attributes{'password'}; return ($authcodes{denied}, "No username given.") unless defined $users{$user}; my ($password, $post, $speed, $subscription) = split(/\t/, $users{$user}); return ($authcodes{denied}, "Incorrect password.") if (crypt($pass, $password) ne $password); return ($authcodes{allowed}, ""); } inn-2.6.0/samples/actsync.cfg0000644000175200017520000000060312575023702015510 0ustar iuliusiulius## $Id: actsync.cfg 7651 2007-08-20 10:28:34Z iulius $ ## ## Sample actsync configuration file. ## ## It permits to synchronize, compare, or merge active files. ## It is useful for keeping the list of carried newsgroups ## up to date. ## See the actsync man page for more information. host=ftp.isc.org ftppath=/pub/usenet/CONFIG/active.gz flags=-v 0 -p 80 ignore_file=actsync.ign inn-2.6.0/samples/passwd.nntp0000644000175200017520000000156712575023702015577 0ustar iuliusiulius## $Id: passwd.nntp 8726 2009-11-08 18:29:57Z iulius $ ## ## Sample passwd.nntp configuration file. ## ## It permits to authenticate on remote NNTP servers. ## ## Format: ## ::[: EOF } my $body = ''; { my $v = $output{'default'}{'html_body'}; if (defined($v)) { $v = &GetValue($v); $v =~ s/\\\"/\"/go; $body = ' ' . $v; } } return <$xsl $title $style_sheet $HTML_header EOF } sub GetHTMLFooter() { my $footer = ''; my $v = $output{'default'}{'footer'}; if (defined($v)) { $v = &GetValue($v); $v =~ s/\\\"/\"/go; $footer = '
' . $v; } my $xhtml11_icon = 'http://www.w3.org/Icons/valid-xhtml11'; $v = $output{'default'}{'html_xhtml11_icon'}; if (defined($v)) { $xhtml11_icon = &GetValue($v); } my $vcss_icon = 'http://jigsaw.w3.org/css-validator/images/vcss'; $v = $output{'default'}{'html_vcss_icon'}; if (defined($v)) { $vcss_icon = &GetValue($v); } my $time = second2time(time - $start_time); return < Valid XHTML 1.1 Strict CSS is valid!
innreport $version (c) 1996-1999 by Fabien Tassin <fta\@sofaraway.org>$footer
$HTML_footer EOF } # make an index for archive pages sub Make_Index($$$$) { my ($rep, $index, $filename, $data) = @_; my %output = %$data; $index =~ s/^\"\s*(.*?)\s*\"$/$1/o; # add requested data at the end of the database. open (DATA, ">> $rep/innreport.db") || die "can't open $rep/innreport.db\n"; my $i = 0; my $res = "$filename"; while (defined ${${$output{'index'}{'column'}}[$i]}{'value'}) { my $data = &GetValue (${${$output{'index'}{'column'}}[$i]}{'value'}); $data =~ s/\n//sog; my @list = split /\|/, $data; my $val; foreach $val (@list) { $res .= ($val eq 'date' ? "|$first_date -- $last_date" : "|" . &EvalExpr($val)); } $i++; } print DATA "$res\n"; close DATA; # sort the database (reverse order), remove duplicates. open (DATA, "$rep/innreport.db") || die "can't open $rep/innreport.db\n"; my %data; while () { m/^([^\|]+)\|(.*)$/o; $data{$1} = $2; } close DATA; open (DATA, "> $rep/innreport.db") || die "can't open $rep/innreport.db\n"; $i = 0; foreach (sort {$b cmp $a} (keys %data)) { print DATA "$_|$data{$_}\n" if $CYCLE == 0 || $i < $CYCLE; $i++; } close DATA; my $title = "Daily Usenet report"; $title = &GetValue ($output{'default'}{'title'}) if defined $output{'default'}{'title'}; $title =~ s/\\\"/\"/g; my $Title = $title; $Title =~ s/<.*?>//g; my $result = GetHTMLHeader($Title . ': index') . "

$title - archives

\n"; if ($GRAPH) { my $i = 0; $result .= "
\n"; while (defined ${${$output{'index'}{'graph'}}[$i]}{'title'}) { my $title = &GetValue (${${$output{'index'}{'graph'}}[$i]}{'title'}); my $filename = "index$i.$GD_FORMAT"; my $color_bg = &GetValue (${${$output{'index'}{'graph'}}[$i]}{'color'}); my $unit = &GetValue (${${$output{'index'}{'graph'}}[$i]}{'unit'}); my $date_idx = &GetValue (${${$output{'index'}{'graph'}}[$i]}{'value'}); $date_idx =~ s/^val(\d+)$/$1/o; my @c = @{${${$output{'index'}{'graph'}}[$i]}{'data'}}; my $label_in = &GetValue (${$c[0]}{'name'}); my $color_in = &GetValue (${$c[0]}{'color'}); my $value_in = &GetValue (${$c[0]}{'value'}); my $type_in = 0; $type_in = $value_in =~ s/^byte\((.*?)\)$/$1/o; $value_in =~ s/^val(\d+)$/$1/o; my $label_out = &GetValue (${$c[1]}{'name'}); my $color_out = &GetValue (${$c[1]}{'color'}); my $value_out = &GetValue (${$c[1]}{'value'}); my $type_out = 0; $type_out = $value_out =~ s/^byte\((.*?)\)$/$1/o; $value_out =~ s/^val(\d+)$/$1/o; my (%in, %out, %dates, $k); foreach $k (keys (%data)) { my @res = split /\|/, $data{$k}; my ($year) = $k =~ m/^news-notice\.(\d+)\.\d+\.\d+-\d+.\d+.\d+$HTML_EXTENSION/; next unless $year; # bad filename.. strange. my ($start, $end) = $res[$date_idx - 1] =~ m/^(\w+\s+\d+ \S+) -- (\w+\s+\d+ \S+)$/o; if (!defined($start)) { warn "Invalid line in DB file ignored: $k" if ($DEBUG); next; } my $start_sec = &ConvDate ($year . ' ' . $start, 0); my $end_sec = &ConvDate ($year . ' ' . $end, 0); if (!defined($start_sec) or !defined($end_sec)) { warn "Invalid date in DB file ignored: $k" if ($DEBUG); next; } if ($start_sec - $end_sec == 0) { warn "Time range 0 in DB file ignored: $k" if ($DEBUG); next; } # 31/12 - 1/1 ? if ($end_sec < $start_sec) { $end_sec = &ConvDate ($year+1 . ' ' . $end, 0); } $in{$start_sec} = $type_in ? &kb2i($res[$value_in - 1]) : $res[$value_in - 1]; $out{$start_sec} = $type_out ? &kb2i($res[$value_out - 1]) : $res[$value_out - 1]; $dates{$start_sec} = $end_sec; } my ($xmax, $ymax) = (500, 170); &Chrono ("$IMG_dir/$filename", $title, $color_bg, $xmax, $ymax, \%in, \%out, \%dates, $label_in, $label_out, $color_in, $color_out, $unit); $result .= "\"Graph\"/\n"; $i++; } $result .= "
\n"; } $i = 0; $result .= "\n"; $result .= ""; my $temp = ''; while (defined ${${$output{'index'}{'column'}}[$i]}{'title'}) { my $title = &GetValue (${${$output{'index'}{'column'}}[$i]}{'title'}); my $name = ''; $name = &GetValue (${${$output{'index'}{'column'}}[$i]}{'name'}) if defined ${${$output{'index'}{'column'}}[$i]}{'name'}; my @list = split /\|/, $name; if ($name) { $result .= sprintf "", $#list + 1; } else { $result .= ""; } foreach (@list) { $temp .= ""; } $i++; } $result .= "\n$temp\n"; $i = 0; foreach (sort {$b cmp $a} (keys %data)) { if ($CYCLE == 0 || $i < $CYCLE) { my @list = split /\|/, $data{$_}; my $class = $i % 2 ? 'ir-oddRow' : 'ir-evenRow'; my $str = ""; while (@list) { $str .= ""; } $str .= "\n"; $result .= "$str"; } $i++; } $result .= "
$title$title$_
"; $str .= "" if -e "$rep/$_"; $str .= shift @list; $str .= "" if -e "$rep/$_";; $str .= ""; my $t = shift @list; $t =~ s/^\0+//o; # remove garbage, if any. $str .= "$t
\n"; $result .= GetHTMLFooter(); my $name = $rep . "/" . $index; while ($name =~ m/\/\.\.\//o) { $name =~ s|^\./||o; # ^./xxx => ^xxx $name =~ s|/\./|/|go; # xxx/./yyy => xxx/yyy $name =~ s|/+|/|go; # xxx//yyy => xxx/yyy $name =~ s|^/\.\./|/|o; # ^/../xxx => ^/xxx $name =~ s|^[^/]+/\.\./||o; # ^xxx/../ => ^nothing $name =~ s|/[^/]+/\.\./|/|go; # /yyy/../ => / } open (INDEX, "> $name") || die "Error: Unable to create $name\n"; print INDEX $result; close INDEX; 1; } sub Graph3d { my $filename = shift; # filename my $title = shift; # title my $xmax = shift; # width my $n = shift; # Number of hash code tables use strict; my ($i, $k, $t); my @val; for $i (0 .. $n - 1) { push @val, shift; # hash code table } my $colors = shift; # colors table my $labels = shift; # labels my $max = 0; my $max_size = 0; my $size = 0; foreach $k (sort keys (%{$val[0]})) { $t = 0; $size++; for $i (0 .. $n - 1) { $t += ${$val[$i]}{$k} if defined ${$val[$i]}{$k}; } $max = $t if $max < $t; $t = length($k); $max_size = $t if $max_size < $t; } $max = 1 unless $max; $max_size *= gdSmallFont()->width; # relief my ($rx, $ry) = (15, 5); # margins my ($mt, $mb) = (40, 40); my $ml = $max_size > 30 ? $max_size + 8 : 30; my $mr = 7 + length($max) * gdSmallFont()->width; $mr = 30 if $mr < 30; # height of each bar my $h = 12; # difference between 2 bars my $d = 25; my $ymax = $size * $d + $mt + $mb; my $image = new GD::Image ($xmax, $ymax); my ($white, $black); if (defined $output{'default'}{'graph_fg'}) { my $t = $output{'default'}{'graph_fg'}; $t =~ s/^\"\s*\#(.*?)\s*\"$/$1/o; $t =~ m/^[\da-fA-F]{6}$/o || die "Error in section 'default' section 'graph_fg'. Bad color.\n"; my @c = map { hex ($_) } ($t =~ m/^(..)(..)(..)$/); $black = $image->colorAllocate (@c); } else { $black = $image->colorAllocate ( 0, 0, 0); } if (defined $output{'default'}{'graph_bg'}) { my $t = $output{'default'}{'graph_bg'}; $t =~ s/^\"\s*\#(.*?)\s*\"$/$1/o; $t =~ m/^[\da-fA-F]{6}$/o || die "Error in section 'default' section 'graph_bg'. Bad color.\n"; my @c = map { hex ($_) } ($t =~ m/^(..)(..)(..)$/); $white = $image->colorAllocate (@c); } else { $white = $image->colorAllocate (255, 255, 255); } $image->filledRectangle (0, 0, $xmax, $ymax, $white); my @col; for $i (0 .. $n - 1) { $col[$i][0] = $image->colorAllocate ($$colors[$i][0], $$colors[$i][1], $$colors[$i][2]); $col[$i][1] = $image->colorAllocate ($$colors[$i][0] * 3 / 4, $$colors[$i][1] * 3 / 4, $$colors[$i][2] * 3 / 4); $col[$i][2] = $image->colorAllocate ($$colors[$i][0] * 2 / 3, $$colors[$i][1] * 2 / 3, $$colors[$i][2] * 2 / 3); } $image->transparent ($white) if $transparent; $image->rectangle (0, 0, $xmax - 1, $size * $d + $mt + $mb - 1, $black); $image->line (0, $mt - 5, $xmax - 1, $mt - 5, $black); for $i (0 .. $n - 1) { $image->string (gdSmallFont(), $i * $xmax / $n + $mt - 10 + $rx, ($mt - gdSmallFont()->height) / 2, "$$labels[$i]", $black); $image->filledRectangle ($i * $xmax / $n + 10, 8 + $ry / 2, $i * $xmax / $n + $mt - 10, $mt - 12, $col[$i][0]); $image->rectangle ($i * $xmax / $n + 10, 8 + $ry / 2, $i * $xmax / $n + $mt - 10, $mt - 12, $black); { my $poly = new GD::Polygon; $poly->addPt($i * $xmax / $n + 10, 8 + $ry / 2); $poly->addPt($i * $xmax / $n + 10 + $rx / 2, 8); $poly->addPt($i * $xmax / $n + $mt - 10 + $rx / 2, 8); $poly->addPt($i * $xmax / $n + $mt - 10, 8 + $ry / 2); $image->filledPolygon($poly, $col[$i][1]); $image->polygon($poly, $black); } { my $poly = new GD::Polygon; $poly->addPt($i * $xmax / $n + $mt - 10 + $rx / 2, 8); $poly->addPt($i * $xmax / $n + $mt - 10, 8 + $ry / 2); $poly->addPt($i * $xmax / $n + $mt - 10, $mt - 12); $poly->addPt($i * $xmax / $n + $mt - 10 + $rx / 2, $mt - 12 - $ry / 2); $image->filledPolygon($poly, $col[$i][2]); $image->polygon($poly, $black); } } # Title $image->string (gdMediumBoldFont(), ($xmax - gdMediumBoldFont()->width * length($title)) / 2, $ymax - gdMediumBoldFont()->height - 7, "$title", $black); my $e = $mt - $h + $d; my $r = ($xmax - $ml - $mr - $rx) / $max; # Axe Oz $image->line ($ml + $rx, $mt, $ml + $rx, $size * $d + $mt - $ry, $black); $image->line ($ml + $rx + $max * $r, $mt, $ml + $rx + $max * $r, $size * $d + $mt - $ry, $black); $image->line ($ml, $mt + $ry, $ml, $size * $d + $mt, $black); # Axe Ox $image->line ($ml + $rx, $size * $d + $mt - $ry, $ml + $rx - 2 * $rx, $size * $d + $mt + $ry, $black); # Axe Oy $image->line ($ml + $rx, $size * $d + $mt - $ry, $xmax - $mr / 2, $size * $d + $mt - $ry, $black); $image->line ($ml, $size * $d + $mt, $xmax - $mr - $rx, $size * $d + $mt, $black); # Graduations.. my $nn = 10; for $k (1 .. ($nn - 1)) { $image->dashedLine ($ml + $rx + $k * ($xmax - $ml - $mr - $rx) / $nn, $mt + 10, $ml + $rx + $k * ($xmax - $ml - $mr - $rx) / $nn, $size * $d + $mt - $ry, $black); $image->dashedLine ($ml + $rx + $k * ($xmax - $ml - $mr - $rx) / $nn, $size * $d + $mt - $ry, $ml + $k * ($xmax - $ml - $mr - $rx) / $nn, $size * $d + $mt, $black); $image->line ($ml + $k * ($xmax - $ml - $mr - $rx) / $nn, $size * $d + $mt, $ml + $k * ($xmax - $ml - $mr - $rx) / $nn, $size * $d + $mt + 5, $black); my $t = sprintf "%d%%", $k * 10; $image->string (gdSmallFont(), $ml + $k * ($xmax - $ml - $mr - $rx) / $nn - length($t) * gdSmallFont()->width / 2, $size * $d + $mt + 6, "$t", $black); } { my $t = sprintf "%d%%", 0; $image->line ($ml, $size * $d + $mt, $ml, $size * $d + $mt + 5, $black); $image->string (gdSmallFont(), $ml - length($t) * gdSmallFont()->width / 2, $size * $d + $mt + 6, "$t", $black); $image->line ($xmax - $mr, $size * $d + $mt - $ry, $xmax - $mr - $rx, $size * $d + $mt, $black); $image->line ($xmax - $mr - $rx, $size * $d + $mt, $xmax - $mr - $rx, $size * $d + $mt + 5, $black); $t = sprintf "%d%%", 100; $image->string (gdSmallFont(), $xmax - $mr - $rx - length($t) * gdSmallFont()->width / 2, $size * $d + $mt + 6, "$t", $black); } foreach $k (sort {${$val[0]}{$b} <=> ${$val[0]}{$a}} keys (%{$val[0]})) { $image->string (gdSmallFont(), $ml - length($k) * gdSmallFont()->width - 3, $e + $h / 2 - gdSmallFont()->height / 2, "$k", $black); my $t = 0; $image->line ($ml + ($t + ${$val[0]}{$k}) * $r + $rx - $rx, $e + $h, $ml + ($t + ${$val[0]}{$k}) * $r + $rx, $e - $ry + $h, $black); for $i (0 .. $n - 1) { next unless defined ${$val[$i]}{$k}; { my $poly = new GD::Polygon; $poly->addPt($ml + $t * $r, $e); $poly->addPt($ml + $t * $r + $rx, $e - $ry); $poly->addPt($ml + ($t + ${$val[$i]}{$k}) * $r + $rx, $e - $ry); $poly->addPt($ml + ($t + ${$val[$i]}{$k}) * $r, $e); $image->filledPolygon($poly, $col[$i][1]); $image->polygon($poly, $black); } unless (${$val[$i + 1]}{$k} || ${$val[$i]}{$k} == 0) { my $poly = new GD::Polygon; $poly->addPt($ml + ($t + ${$val[$i]}{$k}) * $r + $rx, $e - $ry); $poly->addPt($ml + ($t + ${$val[$i]}{$k}) * $r + $rx - $rx, $e); $poly->addPt($ml + ($t + ${$val[$i]}{$k}) * $r + $rx - $rx, $e + $h); $poly->addPt($ml + ($t + ${$val[$i]}{$k}) * $r + $rx, $e - $ry + $h); $image->filledPolygon($poly, $col[$i][2]); $image->polygon($poly, $black); } $image->filledRectangle ($ml + $t * $r, $e, $ml + ($t + ${$val[$i]}{$k}) * $r, $e + $h, $col[$i][0]); $image->rectangle ($ml + $t * $r, $e, $ml + ($t + ${$val[$i]}{$k}) * $r, $e + $h, $black); $t += ${$val[$i]}{$k}; } # total length (offered) $image->filledRectangle ($ml + $t * $r + $rx + 3, $e - 2 - gdSmallFont()->height / 2, $ml + $t * $r + $rx + 4 + gdSmallFont()->width * length $t, $e - 6 + gdSmallFont()->height / 2, $white); $image->string (gdSmallFont(), $ml + $t * $r + $rx + 5, $e - 3 - gdSmallFont()->height / 2, "$t", $black); # first value (accepted) $image->filledRectangle ($ml + $t * $r + $rx + 3, $e - 4 + gdSmallFont()->height / 2, $ml + $t * $r + $rx + 4 + gdSmallFont()->width * length(${$val[0]}{$k}), $e - 2 + gdSmallFont()->height, $white); $image->string (gdSmallFont(), $ml + $t * $r + $rx + 5, $e - 5 + gdSmallFont()->height / 2, ${$val[0]}{$k}, $black); $e += $d; } open (IMG, "> $filename") || die "Error: Can't open \"$filename\": $!\n"; if ($GD_FORMAT eq 'png') { print IMG $image->png; } else { print IMG $image->gif; } close IMG; $ymax; } sub Histo($$$$$$$$) { my ($filename, $title, $xmax, $factor, $labelx, $labely, $val1, $labels1) = @_; use strict; my $max = 0; my $ymax = 300; my $nb = 0; # A hugly hack to convert hashes to lists.. # and to adjust the first and the last value... # this function should be rewritten.. my (@a, @b, $kk); foreach $kk (sort keys (%$val1)) { if (defined $$val1{$kk}) { $nb++; # Arg... the following MUST be removed !!!!!!!!! $$val1{$kk} = $$val1{$kk} / $innreport_inn::inn_flow_time{$kk} * 3600 if ($innreport_inn::inn_flow_time{$kk} != 3600) && ($innreport_inn::inn_flow_time{$kk} != 0); push @a, $$val1{$kk}; $max = $$val1{$kk} if $$val1{$kk} > $max; push @b, $$labels1{$kk}; } } return 0 unless $nb; # strange, no data. my $val = \@a; my $labels = \@b; my ($i, $j); my ($marginl, $marginr, $margint, $marginb, $shx, $shy); my $image = new GD::Image($xmax, $ymax); my ($white, $black); if (defined $output{'default'}{'graph_fg'}) { my $t = $output{'default'}{'graph_fg'}; $t =~ s/^\"\s*\#(.*?)\s*\"$/$1/o; $t =~ m/^[\da-fA-F]{6}$/o || die "Error in section 'default' section 'graph_fg'. Bad color.\n"; my @c = map { hex ($_) } ($t =~ m/^(..)(..)(..)$/); $black = $image->colorAllocate (@c); } else { $black = $image->colorAllocate ( 0, 0, 0); } if (defined $output{'default'}{'graph_bg'}) { my $t = $output{'default'}{'graph_bg'}; $t =~ s/^\"\s*\#(.*?)\s*\"$/$1/o; $t =~ m/^[\da-fA-F]{6}$/o || die "Error in section 'default' section 'graph_bg'. Bad color.\n"; my @c = map { hex $_ } ($t =~ m/^(..)(..)(..)$/); $white = $image->colorAllocate (@c); } else { $white = $image->colorAllocate (255, 255, 255); } $image->filledRectangle (0, 0, $xmax, $ymax, $white); my $gray = $image->colorAllocate (128, 128, 128); my $red = $image->colorAllocate (255, 0, 0); my $red2 = $image->colorAllocate (189, 0, 0); my $red3 = $image->colorAllocate (127, 0, 0); my $coltxt = $black; $image->transparent ($white) if $transparent; my $FontWidth = gdSmallFont()->width; my $FontHeight = gdSmallFont()->height; $marginl = 60; $marginr = 30; $margint = 60; $marginb = 30; $shx = 7; $shy = 7; $max = 1 unless $max; my $part = 8; $max /= $factor; my $old_max = $max; { my $t = log ($max) / log 10; $t = sprintf "%.0f", $t - 1; $t = exp ($t * log 10); $max = sprintf "%.0f", $max / $t * 10 + 0.4; my $t2 = sprintf "%.0f", $max / $part; unless ($part * $t2 == $max) { while ($part * $t2 != $max) { $max++; $t2 = sprintf "%d", $max / $part; } } $max = $max * $t / 10; } # Title $image->string (gdMediumBoldFont(), ($xmax - length ($title) * gdMediumBoldFont()->width) / 2, ($margint - $shy - gdMediumBoldFont()->height) / 2, $title, $coltxt); # Labels $image->string (gdSmallFont(), $marginl / 2, $margint / 2, $labely, $coltxt); $image->string (gdSmallFont(), $xmax - $marginr / 2 - $FontWidth * length ($labelx), $ymax - $marginb / 2, $labelx, $coltxt); # Max $image->line ($marginl, $ymax - $marginb - $shy - $old_max * ($ymax - $marginb - $margint - $shy) / $max, $xmax - $marginr, $ymax - $marginb - $shy - $old_max * ($ymax - $marginb - $margint - $shy) / $max, $red); $image->line ($marginl, $ymax - $marginb - $shy - $old_max * ($ymax - $marginb - $margint - $shy) / $max, $marginl - $shx, $ymax - $marginb - $old_max * ($ymax - $marginb - $margint - $shy) / $max, $red); # Left $image->line ($marginl - $shx, $margint + $shy, $marginl - $shx, $ymax - $marginb, $coltxt); $image->line ($marginl, $margint, $marginl, $ymax - $marginb - $shy, $coltxt); $image->line ($marginl, $margint, $marginl - $shx, $margint + $shy, $coltxt); $image->line ($marginl - $shx, $ymax - $marginb, $marginl, $ymax - $marginb - $shy, $coltxt); # Right $image->line ($xmax - $marginr, $margint, $xmax - $marginr, $ymax - $marginb - $shy, $coltxt); $image->line ($xmax - $marginr - $shx, $ymax - $marginb, $xmax - $marginr, $ymax - $marginb - $shy, $coltxt); # Bottom $image->line ($marginl - $shx, $ymax - $marginb, $xmax - $marginr - $shx, $ymax - $marginb, $coltxt); $image->line ($marginl, $ymax - $marginb - $shy, $xmax - $marginr, $ymax - $marginb - $shy, $coltxt); $image->fill ($xmax / 2, $ymax - $marginb - $shy / 2, $gray); # Top $image->line ($marginl, $margint, $xmax - $marginr, $margint, $coltxt); $image->setStyle ($coltxt, $coltxt, &GD::gdTransparent, &GD::gdTransparent, &GD::gdTransparent); # Graduations for ($i = 0; $i <= $part; $i++) { $j = $max * $i / $part ; # Warning to floor # $j = ($max / $part) * ($i / 10000); # $j *= 10000; # Little hack... $j = sprintf "%d", $j if $j > 100; $image->line ($marginl - $shx - 3, $ymax - $marginb - $i * ($ymax - $marginb - $margint - $shy) / $part, $marginl - $shx, $ymax - $marginb - $i * ($ymax - $marginb - $margint - $shy) / $part, $coltxt); $image->line ($marginl - $shx, $ymax - $marginb - $i * ($ymax - $marginb - $margint - $shy) / $part, $marginl, $ymax - $marginb - $shy - $i * ($ymax - $marginb - $margint - $shy) / $part, gdStyled()); $image->line ($marginl, $ymax - $marginb - $shy - $i * ($ymax - $marginb - $margint - $shy) / $part, $xmax - $marginr, $ymax - $marginb - $shy - $i * ($ymax - $marginb - $margint - $shy) / $part, gdStyled()); $image->string (gdSmallFont(), $marginl - $shx - $FontWidth * length ("$j") - 7, $ymax - $marginb - ($i) * ($ymax - $marginb - $margint - $shy) / ($part) - $FontHeight / 2, "$j", $coltxt); } # Graduation (right bottom corner) $image->line ($xmax - $marginr - $shx, $ymax - $marginb, $xmax - $marginr - $shx, $ymax - $marginb + 3, $coltxt); # Bars $i = 0; my $w = ($xmax - $marginl - $marginr) / $nb; my $k = $w / 5; $$val[$nb - 1] = 0 unless $$val[$nb - 1]; foreach $j (@$val) { my $MAX = 1; if ($i++ <= $nb) { # Graduation $image->line ($marginl + ($i - 1) * $w - $shx, $ymax - $marginb, $marginl + ($i - 1) * $w - $shx, $ymax - $marginb + 3, $coltxt); my $ii = sprintf "%d", $i / $MAX; $image->string (gdSmallFont(), $marginl + ($i - 0.5) * $w + 1 - ($FontWidth * length ($$labels[$i-1])) / 2 - $shx, $ymax - $marginb + 3, $$labels[$i-1], $coltxt) unless ($w < $FontWidth * length ($$labels[$i-1])) && ($i != $MAX * $ii); # Right my $poly = new GD::Polygon; $poly->addPt($marginl + ($i) * $w - $k, $ymax - $marginb - $shy - $j / $factor * ($ymax - $marginb - $margint - $shy) / $max); $poly->addPt($marginl + ($i) * $w - $k, $ymax - $marginb - $shy); $poly->addPt($marginl + ($i) * $w - $k - $shx, $ymax - $marginb); $poly->addPt($marginl + ($i) * $w - $k - $shx, $ymax - $marginb - $j / $factor * ($ymax - $marginb - $margint - $shy) / $max); $image->filledPolygon($poly, $red3); $image->polygon($poly, $coltxt); # Front $image->filledRectangle ($marginl + ($i - 1) * $w + $k - $shx, $ymax - $marginb - $j / $factor * ($ymax - $marginb - $margint - $shy) / $max, $marginl + ($i) * $w - $k - $shx, $ymax - $marginb, $red); $image->rectangle ($marginl + ($i - 1) * $w + $k - $shx, $ymax - $marginb - $j / $factor * ($ymax - $marginb - $margint - $shy) / $max, $marginl + ($i) * $w - $k - $shx, $ymax - $marginb, $coltxt); # Top my $poly2 = new GD::Polygon; $poly2->addPt($marginl + ($i - 1) * $w + $k, $ymax - $marginb - $shy - $j / $factor * ($ymax - $marginb - $margint - $shy) / $max); $poly2->addPt($marginl + ($i) * $w - $k, $ymax - $marginb - $shy - $j / $factor * ($ymax - $marginb - $margint - $shy) / $max); $poly2->addPt($marginl + ($i) * $w - $k - $shx, $ymax - $marginb - $j / $factor * ($ymax - $marginb - $margint - $shy) / $max); $poly2->addPt($marginl + ($i - 1) * $w + $k - $shx, $ymax - $marginb - $j / $factor * ($ymax - $marginb - $margint - $shy) / $max); $image->rectangle (0, 0, $xmax - 1, $ymax - 1, $coltxt); $image->filledPolygon($poly2, $red2); $image->polygon($poly2, $coltxt); } } open (IMG, "> $filename") || die "Can't create '$filename'\n"; if ($GD_FORMAT eq 'png') { print IMG $image->png; } else { print IMG $image->gif; } close IMG; 1; } sub Chrono { my $filename = shift; # filename my $title = shift; # title my $color_bg = shift; # background color my $xmax = shift; # width my $ymax = shift; # height my $in = shift; my $out = shift; my $dates = shift; my $legend_in = shift; my $legend_out = shift; my $color_in = shift; my $color_out = shift; my $unit = shift; my $key; my $x_min = 1E30; my $x_max = 0; my $y_min = 0; my $y_max; my $y_max_in = 0; my $y_max_out = 0; foreach $key (sort keys %$dates) { $x_min = $key if $x_min > $key; $x_max = $$dates{$key} if $x_max < $$dates{$key}; my $delta = $dates->{$key} - $key; my $t = $out->{$key} / $delta; $y_max_out = $t if $y_max_out < $t; $t = $in->{$key} / $delta; $y_max_in = $t if $y_max_in < $t; } $y_max = $y_max_out > $y_max_in ? $y_max_out : $y_max_in; my $factor = 1; if ($y_max < 1) { $factor = 60; if ($y_max < 4 / 60) { $y_max = 4 / 60; } else { $y_max = int ($y_max * $factor) + 1; $y_max += (4 - ($y_max % 4)) % 4; $y_max /= $factor; } } else { $y_max = int ($y_max) + 1; $y_max += (4 - ($y_max % 4)) % 4; } $unit .= "/" . ($factor == 60 ? "min" : "sec"); # min range is 4 weeks. my $delta = $x_max - $x_min; $x_min = $x_max - 3024000 if $delta < 3024000; # between 4 weeks and one year, range is a year. $x_min = $x_max - 31536000 if ($delta < 31536000 && $delta > 3024000); # max range is 13 months $x_min = $x_max - 34128000 if $delta > 34128000; my $image = new GD::Image ($xmax, $ymax); my ($white, $black); if (defined $output{'default'}{'graph_fg'}) { my $t = $output{'default'}{'graph_fg'}; $t =~ s/^\"\s*\#(.*?)\s*\"$/$1/o; $t =~ m/^[\da-fA-F]{6}$/o || die "Error in section 'default' section 'graph_fg'. Bad color.\n"; my @c = map { hex $_ } ($t =~ m/^(..)(..)(..)$/); $black = $image->colorAllocate (@c); } else { $black = $image->colorAllocate ( 0, 0, 0); } if (defined $output{'default'}{'graph_bg'}) { my $t = $output{'default'}{'graph_bg'}; $t =~ s/^\"\s*\#(.*?)\s*\"$/$1/o; $t =~ m/^[\da-fA-F]{6}$/o || die "Error in section 'default' section 'graph_bg'. Bad color.\n"; my @c = map { hex $_ } ($t =~ m/^(..)(..)(..)$/); $white = $image->colorAllocate (@c); } else { $white = $image->colorAllocate (255, 255, 255); } my $bg; if (defined $color_bg) { $color_bg =~ m/^\#[\da-fA-F]{6}$/o || die "Error in section 'index'. Bad color $color_bg.\n"; my @c = map { hex $_ } ($color_bg =~ m/^\#(..)(..)(..)$/); $bg = $image->colorAllocate (@c); } else { $bg = $image->colorAllocate (255, 255, 206); } my $col_in; if (defined $color_in) { $color_in =~ m/^\#[\da-fA-F]{6}$/o || die "Error in section 'index'. Bad color $color_in.\n"; my @c = map { hex $_ } ($color_in =~ m/^\#(..)(..)(..)$/); $col_in = $image->colorAllocate (@c); } else { $col_in = $image->colorAllocate ( 80, 159, 207); } my $col_out; my @col_out = ( 0, 0, 255); if (defined $color_out) { $color_out =~ m/^\#[\da-fA-F]{6}$/o || die "Error in section 'index'. Bad color $color_out.\n"; my @c = map { hex $_ } ($color_out =~ m/^\#(..)(..)(..)$/); $col_out = $image->colorAllocate (@c); @col_out = @c; } else { $col_out = $image->colorAllocate (@col_out); } my $white2 = $image->colorAllocate (255, 255, 255); my $gray = $image->colorAllocate (192, 192, 192); my $red = $image->colorAllocate (255, 0, 0); my $coltxt = $black; my $size = 22; # legend # legend statistics my ($max_in, $max_out) = (0, 0); # min my ($min_in, $min_out) = (1E10, 1E10); # max my ($t_in, $t_out) = (0, 0); # time my ($s_in, $s_out) = (0, 0); # sum $image->filledRectangle (0, 0, $xmax, $ymax, $gray); $image->transparent ($gray) if $transparent; my $FontWidth = gdSmallFont()->width; my $FontHeight = gdSmallFont()->height; $image->setStyle ($black, &GD::gdTransparent, &GD::gdTransparent); my $marginl = 13 + $FontWidth * length (sprintf "%d", $y_max * $factor); my $marginr = 15 + 4 * $FontWidth; # "100%" my $margint = 2 * $FontHeight + gdMediumBoldFont()->height; my $marginb = 2 * $FontHeight + $size; my $xratio = ($xmax - $marginl - $marginr) / ($x_max - $x_min); my $yratio = ($ymax - $margint - $marginb) / ($y_max - $y_min); my $frame = new GD::Polygon; $frame->addPt(2, $margint - $FontHeight -3); $frame->addPt($xmax - 2, $margint - $FontHeight -3); $frame->addPt($xmax - 2, $ymax - 3); $frame->addPt(2, $ymax - 3); $image->filledPolygon($frame, $white2); $image->polygon($frame, $black); $image->filledRectangle ($marginl, $margint, $xmax - $marginr, $ymax - $marginb, $bg); my $brush = new GD::Image(1, 2); my $b_col = $brush->colorAllocate(@col_out); $brush->line(0, 0, 0, 1, $b_col); $image->setBrush($brush); my ($old_x, $old_y_in, $old_y_out); foreach $key (sort keys %$dates) { next if $key < $x_min; my $delta = $$dates{$key} - $key; $min_in = $$in{$key} / $delta if $min_in > $$in{$key} / $delta; $max_in = $$in{$key} / $delta if $max_in < $$in{$key} / $delta; $min_out = $$out{$key} / $delta if $min_out > $$out{$key} / $delta; $max_out = $$out{$key} / $delta if $max_out < $$out{$key} / $delta; $t_in += $delta; $s_in += $$in{$key}; $s_out += $$out{$key}; my $tt_in = $$in{$key} / ($$dates{$key} - $key) * $yratio; my $tt_out = $$out{$key} / ($$dates{$key} - $key) * $yratio; my $new_x = $marginl + ($key - $x_min) * $xratio; $image->filledRectangle ($marginl + ($key - $x_min) * $xratio, $ymax - $marginb - $tt_in, $marginl + ($$dates{$key} - $x_min) * $xratio, $ymax - $marginb, $col_in); if (defined $old_x) { $old_x = $new_x if $old_x > $new_x; my $poly = new GD::Polygon; $poly->addPt($old_x, $old_y_in); $poly->addPt($new_x, $ymax - $marginb - $tt_in); $poly->addPt($new_x, $ymax - $marginb); $poly->addPt($old_x, $ymax - $marginb); $image->filledPolygon($poly, $col_in); } $image->line ($marginl + ($key - $x_min) * $xratio, $ymax - $marginb - $tt_out, $marginl + ($$dates{$key} - $x_min) * $xratio, $ymax - $marginb - $tt_out, &GD::gdBrushed); $image->line ($old_x, $old_y_out, $new_x, $ymax - $marginb - $tt_out, $col_out) if defined $old_x; $old_x = $marginl + ($$dates{$key} - $x_min) * $xratio; $old_y_in = $ymax - $marginb - $tt_in; $old_y_out = $ymax - $marginb - $tt_out; } $t_out = $t_in; # main frame $image->rectangle ($marginl, $margint, $xmax - $marginr, $ymax - $marginb, $black); # graduations my $i; foreach $i (0, 25, 50, 75, 100) { my $t = $ymax - $margint - $marginb; $image->line ($marginl, $ymax - $marginb - $i / 100 * $t, $xmax - $marginr, $ymax - $marginb - $i / 100 * $t, gdStyled()); $image->line ($xmax - $marginr, $ymax - $marginb - $i / 100 * $t, $xmax - $marginr + 3, $ymax - $marginb - $i / 100 * $t, $black); $image->line ($marginl - 3, $ymax - $marginb - $i / 100 * $t, $marginl, $ymax - $marginb - $i / 100 * $t, $black); $image->string (gdSmallFont(), $xmax - $marginr + 8, - $FontHeight / 2 + $ymax - $marginb - $i / 100 * $t, "$i%", $black); my $s = sprintf "%d", $y_max * $i / 100 * $factor; $image->string (gdSmallFont(), $marginl - 5 - $FontWidth * length $s, - $FontHeight / 2 + $ymax - $marginb - $i / 100 * $t, $s, $black); } ## my $w = 604800; # number of seconds in a week my $y = 31536000; # number of seconds in a 365 days year my $mm = 2592000; # number of seconds in a 30 days month if ($x_max - $x_min <= 3024000) { # less than five weeks # unit is a week # 1/1/1990 is a monday. Use this as a basis. my $d = 631152000; # number of seconds between 1/1/1970 and 1/1/1990 my $n = int ($x_min / $y); my $t = $x_min - $n * $y - int (($n - 2) / 4) * 24 * 3600; my $f = int ($t / $w); $n = $d + int (($x_min - $d) / $w) * $w; while ($n < $x_max) { $t = $marginl + ($n - $x_min) * $xratio; if ($n > $x_min) { $image->line ($t, $margint, $t, $ymax - $marginb, gdStyled()); $image->line ($t, $ymax - $marginb, $t, $ymax - $marginb + 2, $black); } $image->string (gdSmallFont(), $FontWidth * 7 / 2 + $t, $ymax - $marginb + 4, (sprintf "Week %02d", $f), $black) if ($n + $w / 2 > $x_min) && ($n + $w / 2 < $x_max); $f++; $n += $w; $t = int ($n / $y); $f = 0 if $n - $y * $t - int (($t - 2) / 4) * 24 * 3600 < $w && $f > 50; } $d = 86400; # 1 day $n = int ($x_min / $y); $t = $n * $y + int (($n - 2) / 4) * 24 * 3600; $i = 0; my $x; while ($t < $x_max) { $x = $marginl + ($t - $x_min) * $xratio; $image->line ($x, $margint, $x, $ymax - $marginb + 2, $red) if $t > $x_min; $t += $mm; $t += $d if $i == 0 || $i == 2 || $i == 4 || $i == 6 || $i == 7 || $i == 9 || $i == 11; # 31 days months if ($i == 1) { # february ? $t -= 2 * $d; $t += $d unless (1970 + int ($t / $y)) % 4; } $i++; $i = 0 if $i == 12; # Happy New Year !! } } else { # unit is a month my $n = int ($x_min / $y); my $t = $n * $y + int (($n - 2) / 4) * 24 * 3600; my @m = ("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"); my $d = 86400; # 1 day my $i = 0; my $x; while ($t < $x_max) { $x = $marginl + ($t - $x_min) * $xratio; if ($t > $x_min) { $image->line ($x, $margint, $x, $ymax - $marginb, gdStyled()); $image->line ($x, $ymax - $marginb, $x, $ymax - $marginb + 2, $black); $image->line ($x, $margint, $x, $ymax - $marginb, $red) unless $i; } $image->string (gdSmallFont(), $mm * $xratio / 2 - $FontWidth * 3 / 2 + $x, $ymax - $marginb + 4, (sprintf "%s", $m[$i]), $black) if ($t + 2 * $w > $x_min) && ($x_max > 2 * $w + $t); $t += $mm; $t += $d if ($i == 0 || $i == 2 || $i == 4 || $i == 6 || $i == 7 || $i == 9 || $i == 11); # 31 days months if ($i == 1) { # february ? $t -= 2 * $d; $t += $d unless (1970 + int ($t / $y)) % 4; } $i++; $i = 0 if $i == 12; # Happy New Year !! } } # Add the little red arrow my $poly = new GD::Polygon; $poly->addPt($xmax - $marginr - 2, $ymax - $marginb - 3); $poly->addPt($xmax - $marginr + 4, $ymax - $marginb); $poly->addPt($xmax - $marginr - 2, $ymax - $marginb + 3); $image->filledPolygon($poly, $red); # Title $image->string (gdMediumBoldFont(), $xmax / 2 - $FontWidth * length ($title) / 2, 4, $title, $black); # Legend my $y_in = $ymax - $size - $FontHeight + 5; $image->string (gdSmallFont(), $marginl, $y_in, $legend_in, $col_in); $image->string (gdSmallFont(), $xmax / 4, $y_in, (sprintf "Min: %5.1f $unit", $min_in * $factor), $black); $image->string (gdSmallFont(), $xmax / 2, $y_in, (sprintf "Avg: %5.1f $unit", $s_in / $t_in * $factor), $black); $image->string (gdSmallFont(), 3 * $xmax / 4, $y_in, (sprintf "Max: %5.1f $unit", $max_in * $factor), $black); my $y_out = $ymax - $size + 5; $image->string (gdSmallFont(), $marginl, $y_out, $legend_out, $col_out); $image->string (gdSmallFont(), $xmax / 4, $y_out, (sprintf "Min: %5.1f $unit", $min_out * $factor), $black); $image->string (gdSmallFont(), $xmax / 2, $y_out, (sprintf "Avg: %5.1f $unit", $s_out / $t_out * $factor), $black); $image->string (gdSmallFont(), 3 * $xmax / 4, $y_out, (sprintf "Max: %5.1f $unit", $max_out * $factor), $black); open (IMG, "> $filename") || die "Error: Can't open \"$filename\": $!\n"; if ($GD_FORMAT eq 'png') { print IMG $image->png; } else { print IMG $image->gif; } close IMG; return $ymax; } sub Write_all_results { my $HTML_output = shift; my $h = shift; my $k; my $title = $$h{'default'}{'title'} ? $$h{'default'}{'title'} : "Daily Usenet report"; $title =~ s/^\"\s*(.*?)\s*\"$/$1/o; $title =~ s/\\\"/\"/go; my $Title = $title; $Title =~ s/<.*?>//go; { my $Title = $Title; $Title =~ s/\&/&/go; $Title =~ s/\<//go; print "$Title from $first_date to $last_date\n\n" if ((defined $$h{'default'}{'text'}) and ($$h{'default'}{'text'} =~ m/^true$/io)); } if ($HTML) { open (HTML, "> $HTML_output") || die "Error: cant open $HTML_output\n"; print HTML GetHTMLHeader("$Title: $first_date"); print HTML "
\n" . "

$title

\n" . "

$first_date -- $last_date

\n" . "
\n"; # Index print HTML "
    \n"; foreach $k (@{$$h{'_order_'}}) { next if $k =~ m/^(default|index)$/; my $r_data = EvalHash( $h->{$k}{'data'} ); my ($string) = $$h{$k}{'title'} =~ m/^\"\s*(.*?)\s*\"$/o; $string =~ s/\s*:$//o; my $want = 1; ($want) = $$h{$k}{'skip'} =~ m/^\"?\s*(.*?)\s*\"?$/o if defined $$h{$k}{'skip'}; $want = $want eq 'true' ? 0 : 1; if (%$r_data && $want) { printf HTML "
  • %s
  • \n", $k, $string; } } print HTML "
\n"; } if (@unrecognize && $WANT_UNKNOWN) { my $mm = $#unrecognize; print "Unknown entries from news log file:\n"; if ($HTML && $WANT_HTML_UNKNOWN) { print HTML "
", "

", "Unknown entries from news log file:

\n"; } $mm = $MAX_UNRECOGNIZED - 1 if $MAX_UNRECOGNIZED > 0 && $mm > $MAX_UNRECOGNIZED - 1; if ($mm < $unrecognize_max && $unrecognize_max > 0) { printf HTML "

First %d / $unrecognize_max lines (%3.1f%%)

\n", $mm + 1, ($mm + 1) / $unrecognize_max * 100 if $HTML && $WANT_HTML_UNKNOWN; printf "First %d / $unrecognize_max lines (%3.1f%%)\n", $mm + 1, ($mm + 1) / $unrecognize_max * 100; } print HTML '
' if ($HTML); for my $l (0 .. $mm) { chomp $unrecognize[$l]; # sometimes, the last line need a CR print "$unrecognize[$l]\n"; # so, we always add one if ($HTML && $WANT_HTML_UNKNOWN) { print HTML EscapeHTML($unrecognize[$l]), "
\n"; } } print "\n"; if ($HTML) { print HTML "
\n", "
\n"; } } close HTML if $HTML; foreach $k (@{$$h{'_order_'}}) { next if $k =~ m/^(default|index)$/; &Write_Results($HTML_output, $k, $h); } if ($HTML) { open (HTML, ">> $HTML_output") || die "Error: cant open $HTML_output\n"; print HTML GetHTMLFooter(); close HTML; } } sub Write_Results { my $HTML_output = shift; my $report = shift; my $data = shift; my %output = %$data; return 0 unless defined $output{$report}; # no data to write return 0 if defined $output{$report}{'skip'} && $output{$report}{'skip'} =~ m/^true$/io; my ($TEXT, $HTML, $DOUBLE); # Need a text report ? $TEXT = defined $output{$report}{'text'} ? $output{$report}{'text'} : (defined $output{'default'}{'text'} ? $output{'default'}{'text'} : ''); die "Error in config file. Field 'text' is mandatory.\n" unless $TEXT; $TEXT = ($TEXT =~ m/^true$/io) ? 1 : 0; # Need an html report ? if ($HTML_output) { $HTML = defined $output{$report}{'html'} ? $output{$report}{'html'} : (defined $output{'default'}{'html'} ? $output{'default'}{'html'} : ''); die "Error in config file. Field 'html' is mandatory.\n" unless $HTML; $HTML = ($HTML =~ m/^true$/io) ? 1 : 0; } # Double table ? $DOUBLE = defined $output{$report}{'double'} ? $output{$report}{'double'} : 0; $DOUBLE = ($DOUBLE =~ m/^true$/io) ? 1 : 0; # Want to truncate the report ? my $TOP = defined $output{$report}{'top'} ? $output{$report}{'top'} : -1; my $TOP_HTML = defined $output{$report}{'top_html'} ? $output{$report}{'top_html'} : $TOP; my $TOP_TEXT = defined $output{$report}{'top_text'} ? $output{$report}{'top_text'} : $TOP; my (%h, $r_data, @keys, $h); { my $t = $output{$report}{'data'} || die "Error in section $report. Need a 'data' field.\n"; $r_data = EvalHash($t); @keys = keys (%$r_data); return unless @keys; # nothing to report. exit. } { my $t = $output{$report}{'sort'}; ( $h ) = defined($t) ? PrepareEval($t) : sub { $a cmp $b }; } if ($HTML) { open (HTML, ">> $HTML_output") || die "Error: cant open $HTML_output\n"; } print "\n" if $TEXT; my ($key, $key1, $key2); if (defined $output{$report}{'title'}) { my $t = $output{$report}{'title'}; $t =~ s/^\"\s*(.*?)\s*\"$/$1/o; if ($HTML) { my $html = $t; $html =~ s/(:?)$/ [Top $TOP_HTML]$1/o if $TOP_HTML > 0; print HTML "
\n", "

", $html, "

\n\n"; } $t =~ s/(:?)$/ [Top $TOP_TEXT]$1/o if $TOP_TEXT > 0; print $t, "\n" if $TEXT; } my $numbering = 0; $numbering = 1 if defined $output{$report}{'numbering'} && $output{$report}{'numbering'} =~ m/^true$/o; my $i; my $s = ''; my $html = ''; my $first = 0; foreach $i (@{$output{$report}{'column'}}) { my ($v1, $v2); my $wtext = defined $$i{'text'} ? $$i{'text'} : 1; $wtext = $wtext =~ m/^(1|true)$/io ? 1 : 0; my $whtml = defined $$i{'html'} ? $$i{'html'} : 1; $whtml = $whtml =~ m/^(1|true)$/io ? 1 : 0; $v1 = defined ($$i{'format_name'}) ? $$i{'format_name'} : (defined ($$i{'format'}) ? $$i{'format'} : "%s"); $v1 =~ s/^\"\s*(.*?)\s*\"$/$1/o; $v2 = $$i{'name'}; $v2 =~ s/^\"\s*(.*?)\s*\"$/$1/o; $s .= sprintf $v1 . " ", $v2 if $wtext && !($DOUBLE && $first == 1); if ($HTML && $whtml) { my $v1 = $v1; $v1 =~ s/\%-?(?:\d+(?:\.\d+)?)?(\w)/\%$1/g; my $temp = $first ? "center" : "left"; $temp .= "\" colspan=\"2" if $numbering && !$first; $html .= sprintf "", $v2; } $first++; } $s =~ s/\s*$//; print "$s\n" if $TEXT; $s = ''; if ($HTML) { print HTML "$html\n"; $html = ''; } my $num = 0; my $done; if ($DOUBLE) { my $num_d = 0; foreach $key1 (sort @keys) { $done = 0; $num = 0; $num_d++; $s = ''; $html = ''; my @res; foreach $key2 (sort {$r_data->{$key1}{$b} <=> $r_data->{$key1}{$a}} keys (%{$r_data->{$key1}})) { my $first = 0; $num++; foreach $i (@{$output{$report}{'column'}}) { my ($v1, $v2, $p); my $wtext = defined $$i{'text'} ? $$i{'text'} : 1; $wtext = $wtext =~ m/^(1|true)$/io ? 1 : 0; my $whtml = defined $$i{'html'} ? $$i{'html'} : 1; $whtml = $whtml =~ m/^(1|true)$/io ? 1 : 0; # is it the primary key ? $p = 0; $p = 1 if defined $$i{'primary'} && $$i{'primary'} =~ m/true/; # format $v1 = defined ($$i{'format'}) ? $$i{'format'} : "%s"; $v1 =~ s/^\"\s*(.*?)\s*\"$/$1/o; # value $v2 = $$i{'value'}; $v2 =~ s/^\"\s*(.*?)\s*\"$/$1/o; my $r =''; if ($v2) { $r = &EvalExpr ($v2, $key2, $num, $key1); die "Error in section $report column $$i{'name'}. " . "Invalid 'value' value.\n" unless defined $r; } $res[$first] += $r if $v1 =~ m/\%-?(?:\d+(?:\.\d+)?)?d/o; if ($p) { $s .= sprintf $v1. "\n", EscapeHTML($r) unless $done || !$wtext; if ($HTML && $whtml) { if ($done) { $html .= ""; } else { $v1 =~ s/\%-?(?:\d+(?:\.\d+)?)?s/\%s/g; $html .= $numbering ? "" : ''; # Hardcoded colspan=3 works for "Miscellaneous innd statistics:". $html .= "\n"; } } } else { if ($wtext) { $s .= " " if $first == 1; $s .= sprintf $v1 . " ", $r; } if ($HTML && $whtml) { $html .= $numbering ? '' : '' if $first == 1; $v1 =~ s/\%-?(?:\d+(?:\.\d+)?)?s/\%s/g; my $temp = $first > 1 ? "right" : "left"; $html .= sprintf "", $temp, EscapeHTML($r); } } $done = 1 if $p; $first++; } $s =~ s/\s*$//; $s =~ s/\\n/\n/g; print "$s\n" if $TEXT && ($num <= $TOP_TEXT || $TOP_TEXT == -1); if ($HTML && ($num <= $TOP_HTML || $TOP_HTML == -1)) { $html =~ s/\\n//g; print HTML "$html\n"; } $s = ''; $html = ''; } $first = 0; $s = ''; $html = ''; if ($TOP_TEXT != -1 && $TOP_HTML != -1) { foreach $i (@{$output{$report}{'column'}}) { if (defined $$i{'primary'} && $$i{'primary'} =~ m/true/o) { $first++; $s .= ' '; $html .= "" if $HTML; $html .= "" if $HTML && $numbering; next; } my ($v1, $v2); $v1 = defined ($$i{'format_total'}) ? $$i{'format_total'} : (defined ($$i{'format'}) ? $$i{'format'} : "%s"); $v1 =~ s/^\"\s*(.*?)\s*\"$/$1/o; my $r = $first == 1 ? $num : $res[$first]; $s .= sprintf $v1 . ' ', $r; if ($HTML) { my $temp = $first > 1 ? 'align="right"' : 'class="ir-totalColumn"'; $v1 =~ s/\%-?(?:\d+(?:\.\d+)?)?s/\%s/g; $html .= sprintf "", $temp, EscapeHTML($r); } $first++; } $s =~ s/\s*$//; $s =~ s/\\n//g; print "$s\n" if $TEXT; print HTML "$html\n" if $HTML; } } print "\n" if $TEXT; $first = 0; $num = $num_d; $s = ''; $html = ''; foreach $i (@{$output{$report}{'column'}}) { my $wtext = defined $$i{'text'} ? $$i{'text'} : 1; $wtext = $wtext =~ m/^(1|true)$/io ? 1 : 0; my $whtml = defined $$i{'html'} ? $$i{'html'} : 1; $whtml = $whtml =~ m/^(1|true)$/io ? 1 : 0; my ($v1, $v2); $v1 = defined $$i{'format_total'} ? $$i{'format_total'} : (defined $$i{'format'} ? $$i{'format'} : "%s"); $v1 =~ s/^\"\s*(.*?)\s*\"$/$1/o; $v2 = $$i{'total'} || die "Error in section $report column $$i{'name'}. " . "Need a 'total' field.\n"; $v2 =~ s/^\"\s*(.*?)\s*\"$/$1/o; my $r = ''; if ($v2) { $r = &EvalExpr ($v2, $key2, $num, 1); die "Error in section $report column $$i{'name'}. " . "Invalid 'total' value.\n" unless defined $r; } $s .= sprintf $v1 . " ", $r if $wtext && $first != 1; if ($HTML && $whtml) { my $temp = $first ? 'align="right"' : 'class="ir-totalColumn"'; $temp .= ' colspan="2"' if $numbering && !$first; $v1 =~ s/\%-?(?:\d+(?:\.\d+)?)?s/\%s/g; $html .= $first == 1 ? "" : sprintf "", $temp, EscapeHTML($r); } $first++; } $s =~ s/\s*$//; $s =~ s/\\n//g; print "$s\n" if $TEXT; if ($HTML) { print HTML "$html\n", "
$v1
$num_d"; $html .= sprintf($v1, EscapeHTML($r)); $html .= "
$v1
$v1
$v1
\n", "
\n"; } } else { foreach $key (sort $h @keys) { next unless defined $key; next unless defined $r_data->{$key}; # to avoid problems after some undef() $num++; next unless $num <= $TOP_HTML || $TOP_HTML == -1 || $num <= $TOP_TEXT || $TOP_TEXT == -1; my $first = 0; foreach $i (@{$output{$report}{'column'}}) { my $wtext = defined $$i{'text'} ? $$i{'text'} : 1; $wtext = $wtext =~ m/^(1|true)$/io ? 1 : 0; my $whtml = defined $$i{'html'} ? $$i{'html'} : 1; $whtml = $whtml =~ m/^(1|true)$/io ? 1 : 0; my ($v1, $v2); $v1 = defined ($$i{'format'}) ? $$i{'format'} : "%s"; $v1 =~ s/^\"\s*(.*?)\s*\"$/$1/o; $v2 = $$i{'value'}; $v2 =~ s/^\"\s*(.*?)\s*\"$/$1/o; my $r =''; if ($v2) { $r = &EvalExpr ($v2, $key, $num); die "Error in section $report column $$i{'name'}. " . "Invalid 'value' value.\n" unless defined $r; } $s .= sprintf $v1 . " ", $r if $wtext && (($num <= $TOP_TEXT) || ($TOP_TEXT == -1)); if ($HTML && $whtml && ($num <= $TOP_HTML || $TOP_HTML == -1)) { # substitute full fledged "%s" specifiers (alignment, width # and precision) with a plain "%s" $v1 =~ s/\%-?(?:\d+(?:\.\d+)?)?s/\%s/g; $html .= "$num" if $numbering && !$first; my $temp = $first ? "right" : "left"; $html .= sprintf "$v1", EscapeHTML($r); } $first++; } $s =~ s/\s*$//; print "$s\n" if $TEXT && ($num <= $TOP_TEXT || $TOP_TEXT == -1); $s = ''; if ($HTML && ($num <= $TOP_HTML || $TOP_HTML == -1)) { my $class = $num % 2 ? 'ir-oddRow' : 'ir-evenRow'; print HTML "$html\n"; $html = ''; } } print "\n" if $TEXT; $first = 0; foreach $i (@{$output{$report}{'column'}}) { my $wtext = defined $$i{'text'} ? $$i{'text'} : 1; $wtext = $wtext =~ m/^(1|true)$/io ? 1 : 0; my $whtml = defined $$i{'html'} ? $$i{'html'} : 1; $whtml = $whtml =~ m/^(1|true)$/io ? 1 : 0; my ($v1, $v2); $v1 = defined ($$i{'format_total'}) ? $$i{'format_total'} : (defined ($$i{'format'}) ? $$i{'format'} : "%s"); $v1 =~ s/^\"\s*(.*?)\s*\"$/$1/o; $v2 = $$i{'total'} || die "Error in section $report column $$i{'name'}. " . "Need a 'total' field.\n"; $v2 =~ s/^\"\s*(.*?)\s*\"$/$1/o; my $r = ''; if ($v2) { $r = &EvalExpr ($v2, $key, $num); die "Error in section $report column $$i{'name'}. " . "Invalid 'total' value.\n" unless defined $r; } $s .= sprintf $v1 . " ", $r if $wtext; if ($HTML && $whtml) { $v1 =~ s/\%-?(?:\d+(?:\.\d+)?)?s/\%s/g; my $temp = $first ? 'align="right"' : 'class="ir-totalColumn"'; $temp .= ' colspan="2"' if $numbering && !$first; $html .= sprintf "$v1", EscapeHTML($r); } $first++; } $s =~ s/\s*$//; print "$s\n" if $TEXT; if ($HTML) { print HTML "$html\n", "\n", "\n"; my $i = 0; while ($GRAPH && defined ${${$output{$report}{'graph'}}[$i]}{'type'}) { my $type = ${${$output{$report}{'graph'}}[$i]}{'type'}; my ($title) = ${${$output{$report}{'graph'}}[$i]}{'title'} =~ m/^\"\s*(.*?)\s*\"$/o; if ($type eq 'histo3d') { my (@values, @colors, @labels); my $num = 0; my $j; foreach $j (@{${${$output{$report}{'graph'}}[$i]}{'data'}}) { $num++; push @values, EvalHash( $j->{'value'} ); my ($t) = $$j{'name'} =~ m/^\"\s*(.*?)\s*\"$/o; push @labels, $t; $t = $$j{'color'} || die "Error in section $report section 'graph'. " . "No color specified for 'value' $$j{'value'}.\n"; $t =~ s/^\"\s*\#(.*?)\s*\"$/$1/o; $t =~ m/^[\da-fA-F]{6}$/o || die "Error in section $report section 'graph'. " . "Bad color for 'value' $$j{'value'}.\n"; my @c = map { hex $_ } ($t =~ m/^(..)(..)(..)$/); push @colors, \@c; } $suffix = '' unless defined $suffix; my $s = ($i ? $i : '') . $suffix; print HTML "
\"$title\"> $HTML_output") || die "Error: cant open $HTML_output\n"; print HTML "width=\"$xmax\" height=\"$y\" "; print HTML "src=\"$IMG_pth$report$s.$GD_FORMAT\"/>
\n"; } elsif ($type eq 'histo') { my $factor = ${${${${$output{$report}{'graph'}}[$i]}{'data'}}[1]}{'factor'} || die "Error in section $report section 'graph'. " . "No factor specified for 'value' " . ${${${${$output{$report}{'graph'}}[$i]}{'data'}}[1]}{'name'} . ".\n"; $factor =~ s/^\"\s*(.*?)\s*\"$/$1/o; my $labelx = ${${${${$output{$report}{'graph'}}[$i]}{'data'}}[0]}{'name'} || die "Error in section $report section 'graph'. " . "No name specified for value.\n"; $labelx =~ s/^\"\s*(.*?)\s*\"$/$1/o; my $labely = ${${${${$output{$report}{'graph'}}[$i]}{'data'}}[1]}{'name'} || die "Error in section $report section 'graph'. " . "No name specified for value.\n"; $labely =~ s/^\"\s*(.*?)\s*\"$/$1/o; my $t = ${${${${$output{$report}{'graph'}}[$i]}{'data'}}[0]}{'value'} || die "Error in section $report section 'graph'. " . "No 'value' specified for " . ${${${${$output{$report}{'graph'}}[$i]}{'data'}}[0]}{'name'} . ".\n"; my $r_labels = EvalHash( $t ); $t = ${${${${$output{$report}{'graph'}}[$i]}{'data'}}[1]}{'value'} || die "Error in section $report section 'graph'. " . "No 'value' specified for " . ${${${${$output{$report}{'graph'}}[$i]}{'data'}}[1]}{'name'} . ".\n"; my $r_values = EvalHash( $t ); my $s = ($i ? $i : '') . $suffix; { my $r; close HTML; # # 6th argument of function Histo is a reference to a hash, # but it is modified, so pass it a copy # my %values = %$r_values; $r = &Histo ("$IMG_dir/$report$s.$GD_FORMAT", $title, $xmax, $factor, $labelx, $labely, \%values, $r_labels); open (HTML, ">> $HTML_output") || die "Error: cant open $HTML_output\n"; if ($r) { print HTML "
", "\"$title\"
\n"; } } } elsif ($type eq 'piechart') { print "Sorry, graph type 'piechart' not supported yet..\n"; } else { die "Error in section $report section 'graph'. " . "Invalid 'type' value.\n" } $i++; } } } close HTML if $HTML; } sub PrepareEval($;$) { my $string = shift; my $double = shift; # reduce white space (including line feeds) to single space $string =~ s/\s+/ /smog; # remove surrounding double quotes, if any $string =~ s/^\"\s*(.*?)\s*\"$/$1/o; # convert "%innd_his" to "%innreport_inn::innd_his" $string =~ s/([\$\%\@])([^{\s\d])/$1${CLASS}\:\:$2/og; # However, a few variables are defined through the enclosure. # $a and $b are provided by function sort. # So convert "%innreport_inn::prog_type" back to "%prog_type" $string =~ s/([\$\%\@])${CLASS}\:\:( a| b| key\d*| num| prog_size| prog_type| sec_glob )\b/$1$2/xog; # If expression consists of a single hash then just return a # reference to it. if ($string !~ s/^\%/\\%/) { # otherwise convert pseudo-functions to real Perl code my %Func = ( 'bytes' => '&NiceByte(', 'div0' => '&Divide0(', 'time_ms' => '&ms2time(', 'time' => '&second2time(', 'total%' => ( $double ? '&ComputeTotalDouble(\\%' : '&ComputeTotal(\\%' ) ); my $i = 0; do { if ($DEBUG) { printf STDERR "PrepareEval %d [%s]\n", $i++, $string; } } while( $string =~ s/ (^|[^&\w]) ([a-z][a-z_0-9]+) \s* \( \s* (%)? /$1 . $Func{$2 . ($3||'')} /xoge ); } if ($DEBUG) { printf STDERR "PrepareEval - [%s]\n", $string; } # These variables are provided to the function inside the closure. # PrepareEval returns references to these variables. my $sub; my $num; my $key; my $key1; my $key2; # man perlvar # $^W ... The current value of the warning switch, initially # true if -w was used. { local $^W = $DEBUG; $sub = eval "sub { $string; }"; } if ($@) { confess "PrepareEval($string) raises $@"; } return ( $sub, \$num, \$key, \$key1, \$key2 ); } sub EvalHash($) { my $v = shift; my ( $sub ) = PrepareEval($v); my $result; eval { local $^W = $DEBUG; $result = &$sub(); }; if ($@ && $DEBUG > 1) { confess "EvalHash($v) raises $@"; } if (ref($result) ne 'HASH') { confess "EvalHash($v) does not return reference to hash."; } return $result; } sub EvalExpr { my ( $v, $key, $num, $key1 ) = @_; my ( $sub, $r_num, $r_key, $r_key1, $r_key2 ) = PrepareEval($v, $key1); $$r_num = $num; $$r_key = $key; $$r_key1 = $key1; $$r_key2 = $key1 ? $key : undef; my $r; eval { local $^W = $DEBUG; ($r) = &$sub(); }; if ($@ && $DEBUG > 1) { confess "EvalExpr($v) raises $@"; } return ($r || 0); } sub Divide0(@) { my $dividend = shift; return 0 unless $dividend; for my $divisor(@_) { return 0 unless $divisor; $dividend /= $divisor; } return $dividend; } sub NiceByte(;$) { my $size = shift() || 0; my $t = $size / 1024 / 1024 / 1024 > 1 ? sprintf "%.1f GB", $size / 1024 / 1024 / 1024 : ($size / 1024 / 1024 > 1 ? sprintf "%.1f MB", $size / 1024 / 1024 : sprintf "%.1f KB", $size / 1024); return $t; } sub kb2i { my $s = shift; my ($i, $u) = $s =~ m/^(\S+) (\S+)$/; $i *= 1024 * 8 if $u =~ m/MB/o; $i *= 1024 * 1024 * 8 if $u =~ m/GB/o; return $i; } sub Decode_Config_File { my $file = shift; my ($line, $section); my $linenum = 0; my $info; my @list; open (FILE, "$file") || die "Can\'t open config file \"$file\". Abort.\n"; while (defined ($line = )) { $linenum++; last if eof (FILE); ($info, $linenum, $line) = &read_conf ($linenum, $line, \*FILE); die "Error in $file line $linenum: must be 'section' instead of '$info'\n" unless ($info eq 'section'); ($info, $linenum, $line) = &read_conf ($linenum, $line, \*FILE); die "Error in $file line $linenum: invalid section name '$info'\n" unless $info =~ /^\w+$/; print "section $info {\n" if $DEBUG; $section = $info; ($info, $linenum, $line) = &read_conf ($linenum, $line, \*FILE); die "Error in $file line $linenum: must be a '{' instead of '$info'\n" unless ($info eq '{'); ($info, $linenum, $line) = &read_conf ($linenum, $line, \*FILE); push @list, $section; while ($info ne '}') { # it is a block last if eof (FILE); my $keyword = $info; ($info, $linenum, $line) = &read_conf ($linenum, $line, \*FILE); my $value = $info; if ($info eq '{') { # it is a sub-block my @a; $output{$section}{$keyword} = \@a unless $output{$section}{$keyword}; my %hash; print "\t$keyword {\n" if $DEBUG; ($info, $linenum, $line) = &read_conf ($linenum, $line, \*FILE); my @sublist; # to store the "data" blocks while ($info ne '}') { last if eof (FILE); my $subkeyword = $info; ($info, $linenum, $line) = &read_conf ($linenum, $line, \*FILE); my $subvalue = $info; if ($info eq '{') { # it is a sub-sub-block my %subhash; print "\t\t$subkeyword {\n" if $DEBUG; my @b; $hash{$subkeyword} = \@b unless ${hash}{$subkeyword}; ($info, $linenum, $line) = &read_conf ($linenum, $line, \*FILE); while ($info ne '}') { last if eof (FILE); my $subsubkeyword = $info; ($info, $linenum, $line) = &read_conf ($linenum, $line, \*FILE); my $subsubvalue = $info; if ($info eq '{') { die "Error in $file line $linenum: too many blocks.\n"; } else { ($info, $linenum, $line) = &read_conf ($linenum, $line, \*FILE); die "Error in $file line $linenum: must be a ';' instead " . "of '$info'\n" unless ($info eq ';'); print "\t\t\t$subsubkeyword\t$subsubvalue;\n" if $DEBUG; $subhash{$subsubkeyword} = $subsubvalue; ($info, $linenum, $line) = &read_conf ($linenum, $line, \*FILE); } } ($info, $linenum, $line) = &read_conf ($linenum, $line, \*FILE); die "Error in $file line $linenum: must be a ';' instead of " . "'$info'\n" unless $info eq ';'; push @{$hash{$subkeyword}} , \%subhash; ($info, $linenum, $line) = &read_conf ($linenum, $line, \*FILE); print "\t\t};\n" if $DEBUG; } else { ($info, $linenum, $line) = &read_conf ($linenum, $line, \*FILE); die "Error in $file line $linenum: must be a ';' instead " . "of '$info'\n" unless $info eq ';'; print "\t\t$subkeyword\t$subvalue;\n" if $DEBUG; $hash{$subkeyword} = $subvalue; ($info, $linenum, $line) = &read_conf ($linenum, $line, \*FILE); } } ($info, $linenum, $line) = &read_conf ($linenum, $line, \*FILE); die "Error in $file line $linenum: must be a ';' instead of '$info'\n" unless $info eq ';'; push @{$output{$section}{$keyword}}, \%hash; ($info, $linenum, $line) = &read_conf ($linenum, $line, \*FILE); print "\t};\n" if $DEBUG; } else { ($info, $linenum, $line) = &read_conf ($linenum, $line, \*FILE); die "Error in $file line $linenum: must be a ';' instead of '$info'\n" unless $info eq ';'; print "\t$keyword\t$value;\n" if $DEBUG; $output{$section}{$keyword} = $value; ($info, $linenum, $line) = &read_conf ($linenum, $line, \*FILE); } } die "Error in $file line $linenum: must be a '}' instead of '$info'\n" unless $info eq '}'; ($info, $linenum, $line) = &read_conf ($linenum, $line, \*FILE); die "Error in $file line $linenum: must be a ';' instead of '$info'\n" unless $info eq ';'; print "};\n\n" if $DEBUG; } close FILE; die "Configuration file $file is empty." if ($linenum < 1); $output{'_order_'} = \@list; } sub read_conf { my ($linenum, $line, $file) = @_; *FILE = *$file; $line =~ s,^\s+,,o; # remove useless blanks $line =~ s,^(\#|//).*$,,o; # remove comments (at the beginning) while (($line =~ m/^$/o || $line =~ m/^\"[^\"]*$/o) && !(eof (FILE))) { $line .= ; # read one line $linenum++; $line =~ s,^\s*,,om; # remove useless blanks $line =~ s,^(\#|//).*$,,om; # remove comments (at the beginning) } $line =~ s/^( # at the beginning [{};] # match '{', '}', or ';' | # OR \" # a double quoted string (?:\\.|[^\"\\])* \" | # OR [^{};\"\s]+ # a word )\s*//mox; my $info = $1; if (defined $info && $info) { chomp $info; } else { warn "Syntax error in conf file line $linenum.\n"; } return ($info, $linenum, $line); } sub GetValue { my $v = shift; my ($r) = $v =~ m/^(?:\"\s*)?(.*?)(?:\s*\")?$/so; return $r; } sub Usage { my ($base) = $0 =~ /([^\/]+)$/; print "Usage: $base -f innreport.conf [-[no]options]\n"; print " where options are:\n"; print " -h (or -help) this help page\n"; print " -v display the version number of innreport\n"; print " -config print innreport configuration information\n"; print " -html HTML output"; print " [default]" if ($HTML); print "\n"; print " -g want graphs"; print " [default]" if ($GRAPH); print "\n"; print " -graph an alias for option -g\n"; print " -d directory directory for Web pages"; print "\n [default=$HTML_dir]" if (defined ($HTML_dir)); print "\n"; print " -dir directory an alias for option -d\n"; print " -p directory pictures path (file space)"; print "\n [default=$IMG_dir]" if (defined ($IMG_dir)); print "\n"; print " -path directory an alias for option -p\n"; print " -w directory pictures path (web space)"; print " [default=$IMG_pth]" if (defined ($IMG_pth)); print "\n"; print " -webpath directory an alias for option -w\n"; print "\n"; print " -i file Name of index file"; print " [default=$index]" if (defined ($index)); print "\n"; print " -index file an alias for option -i\n"; print " -a want to archive HTML results"; print " [default]" if ($ARCHIVE); print "\n"; print " -archive an alias for option -a\n"; print " -c number how many report files to keep (0 = all)\n"; print " [default=$CYCLE]" if (defined ($CYCLE)); print "\n"; print " -cycle number an alias for option -c\n"; print " -s char separator for filename"; print " [default=\"$SEPARATOR\"]\n"; print " -separator char an alias for option -s\n"; print " -unknown \"Unknown entries from news log file\"\n"; print " report"; print " [default]" if ($WANT_UNKNOWN); print "\n"; print " -html-unknown Same as above, but in generated HTML output."; print " [default]" if ($WANT_UNKNOWN); print "\n"; print " -maxunrec Max number of unrecognized lines to display\n"; print " [default=$MAX_UNRECOGNIZED]" if (defined ($MAX_UNRECOGNIZED)); print "\n"; print " -notdaily Never perform daily actions"; print " [default]" if $NOT_DAILY; print "\n"; print " -casesensitive Case sensitive"; print " [default]" if ($CASE_SENSITIVE); print "\n\n"; print "Use \"no\" in front of boolean options to unset them.\n"; print "For example, \"-graph\" is set by default. Use \"-nograph\" to remove this\n"; print "feature.\n"; exit 0; } sub Version { print "\nThis is innreport version $version\n\n"; print "Copyright 1996-1999, Fabien Tassin \n"; exit 0; } sub Summary { use Config; # Convert empty arguments into null string ("") my $i = 0; foreach (@old_argv) { $old_argv[$i] = '""' if $_ eq ''; $i++; } # Display the summary print "\nSummary of my innreport (version $version) configuration:\n"; print " General options:\n"; print " command line='@old_argv' (please, check this value)\n"; print " html=" . ($HTML?"yes":"no") . ", graph=" . ($GRAPH?"yes":"no") . ", haveGD=" . ($::HAVE_GD?"yes":"no") . "\n"; print " archive=" . ($ARCHIVE?"yes":"no") . ", cycle=$CYCLE, separator=\"" . $SEPARATOR . "\"\n"; print " case_sensitive=" . ($CASE_SENSITIVE?"yes":"no") . ", want_unknown=" . ($WANT_UNKNOWN?"yes":"no") . ", max_unrecog=$MAX_UNRECOGNIZED\n"; print " Paths:\n"; print " html_dir=$HTML_dir\n"; print " img_dir=$IMG_dir\n"; print " img_pth=$IMG_pth\n"; print " index=$index\n"; print " Platform:\n"; print " perl version $::Config{baserev} " . "patchlevel $::Config{patchlevel} " . "subversion $::Config{subversion}\n"; print " libperl=$::Config{libperl}, useshrplib=$::Config{useshrplib}, " . "bincompat3=$::Config{bincompat3}\n"; print " osname=$::Config{osname}, osvers=$::Config{osvers}, " . "archname=$::Config{archname}\n"; print " uname=$::Config{myuname}\n\n"; exit 0; } ######################### End of File ########################## inn-2.6.0/scripts/scanlogs.in0000644000175200017520000002276112575023702015560 0ustar iuliusiulius#! /bin/sh # fixscript will replace this line with code to load innshellvars ## $Id: scanlogs.in 9903 2015-06-20 17:20:46Z iulius $ ## ## Summarize INN log files and rotate them. ## Used by news.daily. ## ## Optional arguments: ## norotate Do not rotate logfiles. ## Directory where old log files are kept. OLD=${MOST_LOGS}/OLD ## Files defined in innshellvars. We repeat them for clarity. ERRLOG=${MOST_LOGS}/errlog LOG=${MOST_LOGS}/news ## If you want to archive the active file, enable this line. ACTIVEFILE=${ACTIVE} ## Maximum number of lines to show from error log files. MAXERRLINES=50 ## Where these programs, if used, write their logs. ## We also have to find innfeed's log file. CONTROLBATCH=${MOST_LOGS}/controlbatch.log INNFEEDCONF=${PATHETC}/innfeed.conf if [ -f "${INNFEEDCONF}" ]; then INNFEEDLOG=`${AWK} '{gsub(/:|#/, " & ")} {if ($1 == "log-file" && $2 == ":") print $3}' ${INNFEEDCONF}` INNFEEDPIDFILE=`${AWK} '{gsub(/:|#/, " & ")} {if ($1 == "pid-file" && $2 == ":") print $3}' ${INNFEEDCONF}` fi INNFEED= for F in "${INNFEEDLOG}" ; do test -f "${MOST_LOGS}/${F}" && INNFEED="${INNFEED} ${MOST_LOGS}/${F}" done test -z "${INNFEED}" && test -f "${MOST_LOGS}/innfeed.log" && INNFEED="${MOST_LOGS}/innfeed.log" NNTPSEND=${MOST_LOGS}/nntpsend.log PERLNOCEM=${MOST_LOGS}/perl-nocem.log SENDIHAVE=${MOST_LOGS}/send-ihave.log SENDNNTP=${MOST_LOGS}/send-nntp.log SENDUUCP=${MOST_LOGS}/send-uucp.log LIVEFILES="${CONTROLBATCH} ${INNFEED} ${NNTPSEND} ${PERLNOCEM} ${SENDIHAVE} ${SENDNNTP} ${SENDUUCP}" ## Where news.daily places expire output, unless noexplog was used. EXPLOG=${MOST_LOGS}/expire.log ## If you divide your news syslog into separate files, list them here. SYSLOG_CRIT=${MOST_LOGS}/news.crit SYSLOG_ERR=${MOST_LOGS}/news.err SYSLOG_NOTICE=${MOST_LOGS}/news.notice SYSLOGS="${SYSLOG_CRIT} ${SYSLOG_ERR} ${SYSLOG_NOTICE}" ## Where tally control processor is found. TALLY_CONTROL=${PATHBIN}/tally.control UNWANTED_LOG=${MOST_LOGS}/unwanted.log CONTROL_LOG=${MOST_LOGS}/control.log CONTROL_DATA= test -f ${MOST_LOGS}/newgroup.log && CONTROL_DATA=${MOST_LOGS}/newgroup.log test -f ${MOST_LOGS}/rmgroup.log \ && CONTROL_DATA="${CONTROL_DATA} ${MOST_LOGS}/rmgroup.log" ## Build up the list of log files to process. LOGS="${ERRLOG} ${LOG} ${ACTIVEFILE} ${EXPLOG} ${SYSLOGS} ${UNWANTED_LOG}" for F in ${LIVEFILES} ; do test -n "${F}" -a -f "${F}" && LOGS="${LOGS} ${F}" done test -n "${CONTROL_DATA}" && LOGS="${LOGS} ${CONTROL_LOG}" for F in checkgroups default ihave newgroup rmgroup sendme sendsys \ senduuname version miscctl badcontrol failedpgp badpgp; do test -f ${MOST_LOGS}/${F}.log && LOGS="${LOGS} ${MOST_LOGS}/${F}.log" done PROGNAME=scanlogs LOCK=${LOCKS}/LOCK.${PROGNAME} ## Set defaults. ROTATE=true ## Parse JCL. for I do case "X${I}" in Xnorotate) ROTATE=false ;; *) echo "Unknown flag ${I}" 1>&2 exit 1 ;; esac done ## Make sure every log exists. for F in ${LOGS} ; do test ! -f ${F} && touch ${F} done ## Rotate the logs? if ${ROTATE} ; then ## Lock out others. shlock -p $$ -f ${LOCK} || { echo "$0: Locked by `cat ${LOCK}`" exit 1 } trap "rm -f ${LOCK}; exit 0" 1 2 3 15 HERE=`pwd` cd ${MOST_LOGS} test ! -d ${OLD} && mkdir ${OLD} ctlinnd -s logmode PAUSED=false ctlinnd -s pause "Flushing log and syslog files" 2>&1 && PAUSED=true ## First, flush log files to be sure everything has been recorded ## before rotating them. OUTPUT=`ctlinnd flushlogs 2>&1` if [ "$OUTPUT" != "Ok" -a "$OUTPUT" != "In debug mode" ]; then echo "$OUTPUT" echo 'Cannot flush logs.' rm -f ${LOCK} exit 1 fi ## Make sure these .old files exist, in case innd is down. for F in ${LOG} ${ERRLOG} ; do if [ ! -f ${F}.old ]; then rm -f ${F}.old cp ${F} ${F}.old cat /dev/null >${F} fi done ## Copy syslog files, truncating old inode since syslog has it open. for F in ${SYSLOGS}; do rm -f ${F}.old cp ${F} ${F}.old cat /dev/null >${F} done ctlinnd -s logmode ## Make a copy of the active file. if [ -n ${ACTIVEFILE} ] ; then BASE=`basename ${ACTIVEFILE}` rm -f ${OLD}/${BASE}.old cp ${ACTIVEFILE} ${OLD}/${BASE}.old fi ## These are live files, so use link rather than copy. for F in ${LIVEFILES} ; do if [ -f ${F} ]; then rm -f ${F}.old ${F}.new ln ${F} ${F}.old touch ${F}.new chmod 0660 ${F}.new mv ${F}.new ${F} fi done ## Tally control messages if we logged them. test -n "${CONTROL_DATA}" && cat ${CONTROL_DATA} | ${TALLY_CONTROL} ## Find out the PID of innfeed now and not at the beginning of scanlogs ## because it is restarted when running ctlinnd flushlogs. INNFEEDPID= for F in "${INNFEEDPIDFILE}" ; do test -f "${PATHRUN}/${F}" && INNFEEDPID=`cat "${PATHRUN}/${F}"` done test -z "${INNFEEDPID}" && test -f "${PATHRUN}/innfeed.pid" && INNFEEDPID=`cat "${PATHRUN}/innfeed.pid"` ## Send a HUP signal to innfeed so that it reopens its log files. if [ ! -z ${INNFEEDPID} ]; then kill -HUP ${INNFEEDPID} > /dev/null 2>&1 fi ${PAUSED} && ctlinnd -s go "Flushing log and syslog files" 2>&1 cd ${OLD} for F in ${LOGS}; do ## Process the current (just-flushed) log. BASE=`basename ${F}` rm -f ${OLD}/${BASE} case ${F} in ${SYSLOG_CRIT}|${SYSLOG_ERR}|${ERRLOG}|${LOG}|${SYSLOG_NOTICE}) ## Make a link that can be deleted (since if not rotating ## we delete the copy that is made in ${TMPDIR}). mv ${F}.old ${OLD}/${BASE} rm -f ${OLD}/${BASE}.0 ln ${OLD}/${BASE} ${OLD}/${BASE}.0 ;; ${ACTIVEFILE}) mv ${BASE}.old ${OLD}/${BASE} ;; ${UNWANTED_LOG}) ## Rotate and compress the file. BASE=`basename ${F}` if [ ! -f ${BASE} -a -f ../${BASE} ]; then cp ../${BASE} ${BASE} chmod 0440 ${BASE} fi if [ -f ${BASE} ]; then ${LOG_COMPRESS} <${BASE} >${BASE}.0${Z} && rm -f ${BASE} chmod 0440 ${BASE}.0${Z} ## Do rotation. if [ X${LOGCYCLES} = X ]; then LOGCYCLES=3 fi EXT=${LOGCYCLES} rm -f ${BASE}.${LOGCYCLES}${Z} while [ ${EXT} -gt 0 ] ; do NEXT=${EXT} EXT=`expr ${EXT} - 1` test -f ${BASE}.${EXT}${Z} \ && rm -f ${BASE}.${NEXT}${Z} \ && mv ${BASE}.${EXT}${Z} ${BASE}.${NEXT}${Z} done fi ## innreport assumes where unwanted.log exists, so leave it ## and process later. ;; *) if [ -f ${F}.old ]; then mv ${F}.old ${OLD}/${BASE} else rm -f ${OLD}/${BASE} ${F}.new touch ${F}.new chmod 0660 ${F}.new ln ${F} ${F}.old mv ${F}.new ${F} mv ${F}.old ${OLD}/${BASE} fi ;; esac done cd ${HERE} else ## Don't use the real OLD directory, instead use TMPDIR. OLD=${TMPDIR} ## Make a snapshot of what we need for below. ctlinnd -s pause "Snapshot log and syslog files" 2>&1 for F in ${SYSLOG_CRIT} ${SYSLOG_ERR} ${ERRLOG} ${LOG} ${SYSLOG_NOTICE} ; do BASE=`basename ${F}` rm -f ${OLD}/${BASE}.0 cp ${F} ${OLD}/${BASE}.0 done ctlinnd -s go "Snapshot log and syslog files" 2>&1 fi ## ## We now (finally!) have copies of the log files where we need them. ## ## Display syslog critical messages. BASE=`basename ${SYSLOG_CRIT}` OLD_SYSLOG=${OLD}/${BASE}.0 if [ -s ${OLD_SYSLOG} ] ; then echo Syslog critical messages: cat ${OLD_SYSLOG} | head -n ${MAXERRLINES} echo --------- echo '' fi rm -f ${OLD_SYSLOG} ## Display syslog error messages. BASE=`basename ${SYSLOG_ERR}` OLD_SYSLOG=${OLD}/${BASE}.0 if [ -s ${OLD_SYSLOG} ] ; then echo Syslog error messages: cat ${OLD_SYSLOG} | head -n ${MAXERRLINES} echo --------- echo '' fi rm -f ${OLD_SYSLOG} ## Display error log. BASE=`basename ${ERRLOG}` OLD_ERRLOG=${OLD}/${BASE}.0 if [ -s ${OLD_ERRLOG} ] ; then echo Error log: cat ${OLD_ERRLOG} | head -n ${MAXERRLINES} echo --------- echo '' fi rm -f ${OLD_ERRLOG} ## Scan for various problems in articles we were offered or sent... BASE=`basename ${LOG}` OLD_LOG=${OLD}/${BASE}.0 ## and summarize syslog information. BASE=`basename ${SYSLOG_NOTICE}` OLD_SYSLOG=${OLD}/${BASE}.0 INNREPORT=${TMPDIR}/innreport$$ if [ -s ${OLD_SYSLOG} -o -s ${OLD_LOG} ] ; then ${PATHBIN}/innreport -f ${PATHETC}/innreport.conf ${OLD_SYSLOG} ${OLD_LOG} > ${INNREPORT} if [ -s ${INNREPORT} ] ; then cat ${INNREPORT} echo --------- echo '' fi fi rm -f ${OLD_LOG} ${OLD_SYSLOG} ${INNREPORT} ## Now that innreport has finished, we can move unwanted.log. This ## file is not reset because it keeps the count of unwanted newsgroups. if ${ROTATE} ; then BASE=`basename ${UNWANTED_LOG}` if [ -f ${UNWANTED_LOG}.old ]; then mv ${UNWANTED_LOG}.old ${OLD}/${BASE} else rm -f ${OLD}/${BASE} cp ${UNWANTED_LOG} ${OLD}/${BASE} chmod 0660 ${OLD}/${BASE} fi fi ## Compress and rotate the logs. if ${ROTATE} ; then cd ${OLD} if [ X${LOGCYCLES} = X ]; then LOGCYCLES=3 fi for F in ${LOGS} ; do ## Skip if it's unwanted.log, since it's already rotated. if [ ${F} = ${UNWANTED_LOG} ]; then continue fi ## Skip if file doesn't exist. BASE=`basename ${F}` test -f ${BASE} || continue ## Compress the file. ${LOG_COMPRESS} <${BASE} >${BASE}.0${Z} && rm -f ${BASE} chmod 0440 ${BASE}.0${Z} ## Do rotation. EXT=${LOGCYCLES} rm -f ${BASE}.${LOGCYCLES}${Z} while [ ${EXT} -gt 0 ] ; do NEXT=${EXT} EXT=`expr ${EXT} - 1` test -f ${BASE}.${EXT}${Z} \ && rm -f ${BASE}.${NEXT}${Z} \ && mv ${BASE}.${EXT}${Z} ${BASE}.${NEXT}${Z} done done ## Remove lock. rm -f ${LOCK} fi ## All done. exit 0 inn-2.6.0/scripts/simpleftp.in0000644000175200017520000000352112575023702015743 0ustar iuliusiulius#! /usr/bin/perl -w # simpleftp - Rudimentary FTP client. # # $Id: simpleftp.in 9196 2011-04-16 08:21:13Z iulius $ # # Author: David Lawrence . # Rewritten by Julien Elie to use Net::FTP. # # Fetch files to the local machine based on URLs on the command line. # INN's configure searches for ncftp, wget, linx, et caetera, # but they're all system add-ons, so this is provided. # # Perl 5 is already required by other parts of INN; it only took a half hour # to write this, so this was the easiest way to go for a backup plan. # # This script is nowhere near as flexible as libwww. If you really need # that kind of power, get libwww and use it. This is just sufficient for what # INN needed. use strict; use Net::FTP; use Sys::Hostname; $0 =~ s(.*/)(); my $usage = "Usage: $0 ftp-URL ...\n"; @ARGV or die $usage; for (@ARGV) { m(^ftp://) or die $usage; } my ($lasthost, $ftp); # This will keep track of how many _failed_. my $exit = @ARGV; for (@ARGV) { my ($host, $path) = m%^ftp://([^/]+)(/.+)%; my $user = 'anonymous'; my $pass = (getpwuid($<))[0] . '@' . hostname; my $port = 21; unless (defined $host && defined $path) { warn "$0: bad URL: $_\n"; next; } if ($host =~ /(.*):(.*)\@(.*)/) { $user = $1; $pass = $2; $host = $3; } if ($host =~ /(.*):(.*)/) { $port = $1; $host = $2; } if (defined $lasthost && $host ne $lasthost) { $ftp->quit; $lasthost = undef; } unless (defined $lasthost) { $ftp = Net::FTP->new($host, Port => $port) or next; $ftp->login($user, $pass) or next; } my $localfile = $path; $path =~ s([^/]+$)(); $localfile =~ s(.*/)(); $ftp->binary or next; $ftp->cwd($path) or next; $ftp->get($localfile) or next; $exit--; $lasthost = $host; } $ftp->quit if defined $lasthost; exit $exit; inn-2.6.0/scripts/innreport_inn.pm0000644000175200017520000024166312575023702016645 0ustar iuliusiulius########################################################################## # INN module for innreport (3.*). # # $Id: innreport_inn.pm 9920 2015-07-12 13:48:04Z iulius $ # # Sample file tested with INN 2.5, 2.4, 2.3, 2.2, 1.7.2 and 1.5.1. # # (c) 1997-2001 by Fabien Tassin . # See innreport for more information. # # Version 3.1.0. # ########################################################################## # TODO: add the map file. package innreport_inn; use strict; my $MIN = 1E10; my $MAX = -1; my %ctlinnd = ('a', 'addhist', 'D', 'allow', 'b', 'begin', 'c', 'cancel', 'u', 'changegroup', 'd', 'checkfile', 'e', 'drop', 'f', 'flush', 'g', 'flushlogs', 'h', 'go', 'i', 'hangup', 's', 'mode', 'j', 'name', 'k', 'newgroup', 'l', 'param', 'm', 'pause', 'v', 'readers', 't', 'refile', 'C', 'reject', 'o', 'reload', 'n', 'renumber', 'z', 'reserve', 'p', 'rmgroup', 'A', 'send', 'q', 'shutdown', 'B', 'signal', 'r', 'throttle', 'w', 'trace', 'x', 'xabort', 'y', 'xexec', 'E', 'logmode', 'F', 'feedinfo', 'T', 'filter', 'P', 'perl',); my %timer_names = (idle => 'idle', hishave => 'history lookup', hisgrep => 'history grep', hiswrite => 'history write', hissync => 'history sync', nntpread => 'nntp read', artparse => 'article parse', artclean => 'article cleanup', artwrite => 'article write', artcncl => 'article cancel', artlog => 'article logging', sitesend => 'site send', overv => 'overview write', perl => 'perl filter', python => 'python filter', datamove => 'data move' ); my %innfeed_timer_names = ( 'idle' => 'idle', 'blstats' => 'backlog stats', 'stsfile' => 'status file', 'newart' => 'article new', 'prepart' => 'article prepare', 'readart' => 'article read', 'read' => 'data read', 'write' => 'data write', 'cb' => 'callbacks', ); my %nnrpd_timer_names = ( 'idle' => 'idle', 'newnews' => 'newnews', ); our %batcher_articles; our %batcher_bytes; our %batcher_elapsed; our %batcher_offered; our %cnfsstat; our %cnfsstat_cycles; our %cnfsstat_rate; our %cnfsstat_samples; our %cnfsstat_size; our %cnfsstat_time; our %cnfsstat_used; our %controlchan_doit; our %controlchan_ihave_site; our %controlchan_new; our %controlchan_ok; our %controlchan_other; our %controlchan_rm; our %controlchan_sendme_site; our %controlchan_skippgp; our %controlchan_who; our %inn_badart; our %innd_accepted; our %innd_accepted_sum; our %innd_bad_command; our %innd_bad_ihave; our %innd_bad_msgid; our %innd_bad_newsgroup; our %innd_bad_sendme; our %innd_blocked; our %innd_cache; our %innd_changegroup; our %innd_connect; our %innd_connect_sum; our %innd_control; our %innd_duplicated_size; our %innd_duplicated_size_sum; our %innd_filter_perl; our %innd_filter_python; our %innd_his; our %innd_huge; our %innd_max_conn; our %innd_misc; our %innd_misc_stat; our %innd_newgroup; our %innd_no_colon_space; our %innd_no_permission; our %innd_offered; our %innd_offered_size; our %innd_offered_size_sum; our %innd_offered_sum; our %innd_others; our %innd_posted_future; our %innd_refused; our %innd_refused_sum; our %innd_rejected; our %innd_rejected_size; our %innd_rejected_size_sum; our %innd_rejected_sum; our %innd_rmgroup; our %innd_seconds; our %innd_seconds_sum; our %innd_stored_size; our %innd_stored_size_sum; our %innd_strange_strings; our %innd_time_max; our %innd_time_min; our %innd_time_num; our %innd_time_time; our $innd_time_times; our %innd_too_many_connects_per_minute; our %inn_duplicate; our %innfeed_accepted; our %innfeed_accepted_size; our %innfeed_connect; our %innfeed_missing; our %innfeed_offered; our %innfeed_refused; our %innfeed_rejected; our %innfeed_rejected_size; our %innfeed_seconds; our %innfeed_shrunk; our %innfeed_spooled; our %innfeed_time_max; our %innfeed_time_min; our %innfeed_time_num; our %innfeed_time_time; our $innfeed_time_times; our %inn_flow; our %inn_flow_labels; our %inn_flow_size; our $inn_flow_size_total; our %inn_flow_time; our $inn_flow_total; our %inn_linecount; our %inn_site_path; our %inn_tooold; our %inn_unapproved; our %inn_unapproved_g; our %inn_uw_dist; our %inn_uw_dist_s; our %inn_uw_ng; our %inn_uw_ng_s; our %inn_uw_site; our %innxmit_accepted; our %innxmit_accepted_size; our %innxmit_afail_host; our %innxmit_badart; our %innxmit_cfail_host; our %innxmit_crefused; our %innxmit_duplicate; our %innxmit_expire; our %innxmit_hiload; our %innxmit_ihfail; our %innxmit_linecount; our %innxmit_missing; our %innxmit_nospace; our %innxmit_offered; our %innxmit_others; our %innxmit_refused; our %innxmit_rejected; our %innxmit_rejected_size; our %innxmit_site; our %innxmit_times; our %innxmit_tooold; our %innxmit_unapproved; our %innxmit_unapproved_g; our %innxmit_uw_dist; our %innxmit_uw_dist_s; our %innxmit_uw_ng; our %innxmit_uw_ng_s; our %innxmit_uw_site; our %nnrpd_articles; our %nnrpd_auth; our %nnrpd_bytes; our %nnrpd_connect; our %nnrpd_curious; our %nnrpd_dom_articles; our %nnrpd_dom_bytes; our %nnrpd_dom_connect; our %nnrpd_dom_groups; our %nnrpd_dom_no_permission; our %nnrpd_dom_post_error; our %nnrpd_dom_post_ok; our %nnrpd_dom_post_rej; our %nnrpd_dom_reset_peer; our %nnrpd_dom_timeout; our %nnrpd_dom_times; our %nnrpd_dom_unrecognized; our %nnrpd_gethostbyaddr; our %nnrpd_group; our %nnrpd_groups; our %nnrpd_hierarchy; our %nnrpd_no_permission; our %nnrpd_post_error; our %nnrpd_post_ok; our %nnrpd_post_rej; our %nnrpd_reset_peer; our %nnrpd_resource_elapsed; our %nnrpd_resource_idle; our %nnrpd_resource_system; our %nnrpd_resource_user; our %nnrpd_sys_times; our %nnrpd_time_max; our %nnrpd_time_min; our %nnrpd_time_num; our %nnrpd_timeout; our %nnrpd_times; our %nnrpd_time_time; our $nnrpd_time_times; our %nnrpd_unrecogn_cmd; our %nnrpd_unrecognized; our %nnrpd_usr_times; our %nntplink_accepted; our %nntplink_auth; our %nntplink_bpipe; our %nntplink_connects; our %nntplink_eof; our %nntplink_expire; our %nntplink_fail; our %nntplink_failed; our %nntplink_fake_connects; our %nntplink_hiload; our %nntplink_ihfail; our %nntplink_nospace; our %nntplink_offered; our %nntplink_rejected; our %nntplink_selecterr; our %nntplink_site; our %nntplink_sockerr; our %nntplink_times; our %nocem_badsigs; our %nocem_goodsigs; our $nocem_lastid; our $nocem_newids; our %nocem_newids; our $nocem_totalbad; our $nocem_totalgood; our $nocem_totalids; our %nocem_totalids; our %rnews_bogus_date; our %rnews_bogus_dist; our %rnews_bogus_ng; our $rnews_duplicate; our %rnews_host; our $rnews_linecount; our %rnews_misc; our $rnews_no_colon_space; our %rnews_rejected; our $rnews_too_old; our %rnews_unapproved; our $server; # init innd timer foreach (values %timer_names) { $innd_time_min{$_} = $MIN; $innd_time_max{$_} = $MAX; $innd_time_time{$_} = 0; # to avoid a warning... Perl < 5.004 $innd_time_num{$_} = 0; # ... } $innd_time_times = 0; # ... # init innfeed timer foreach (values %innfeed_timer_names) { $innfeed_time_min{$_} = $MIN; $innfeed_time_max{$_} = $MAX; $innfeed_time_time{$_} = 0; # to avoid a warning... Perl < 5.004 $innfeed_time_num{$_} = 0; # ... } $innfeed_time_times = 0; # ... # init nnrpd timer foreach (values %nnrpd_timer_names) { $nnrpd_time_min{$_} = $MIN; $nnrpd_time_max{$_} = $MAX; $nnrpd_time_time{$_} = 0; # to avoid a warning... Perl < 5.004 $nnrpd_time_num{$_} = 0; # ... } $nnrpd_time_times = 0; # ... # collect: Used to collect the data. sub collect($$$$$$) { my ($day, $hour, $prog, $res, $left, $CASE_SENSITIVE) = @_; return 1 if $left =~ /Reading config from (\S+)$/o; ######## ## inn (from the "news" log file - not from "news.notice") ## if ($prog eq "inn") { # accepted article if ($res =~ m/[\+j]/o) { $hour =~ s/:.*$//o; $inn_flow{"$day $hour"}++; $inn_flow_total++; # Memorize the size. This can only be done with INN >= 1.5xx and # DO_LOG_SIZE = DO. # server size [feeds] # or # server (filename) size [feeds] my ($s) = $left =~ /^\S+ \S+ (?:\(\S+\) )?(\d+)(?: |$)/o; if ($s) { $inn_flow_size{"$day $hour"} += $s; $inn_flow_size_total += $s; } return 1; } # 437 Duplicate article if ($left =~ /(\S+) <[^>]+> (?:437|439) Duplicate(?: article)?$/o) { my $server = $1; $server = lc $server unless $CASE_SENSITIVE; $inn_badart{$server}++; $inn_duplicate{$server}++; return 1; } # 437 Unapproved for if ($left =~ /(\S+) <[^>]+> (?:437|439) Unapproved for \"([^\"]+)\"$/o) { my ($server, $group) = ($1, $2); $server = lc $server unless $CASE_SENSITIVE; $inn_badart{$server}++; $inn_unapproved{$server}++; $inn_unapproved_g{$group}++; return 1; } # 437 Too old -- ... if ($left =~ /(\S+) <[^>]+> (?:437|439) Too old -- /o) { my $server = $1; $server = lc $server unless $CASE_SENSITIVE; $inn_badart{$server}++; $inn_tooold{$server}++; return 1; } # 437 Unwanted site ... in path if ($left =~ /(\S+) <[^>]+> (?:437|439) Unwanted site (\S+) in path$/o) { my ($server, $site) = ($1, $2); $server = lc $server unless $CASE_SENSITIVE; $inn_badart{$server}++; $inn_uw_site{$server}++; $inn_site_path{$site}++; return 1; } # 437 Unwanted newsgroup "..." if ($left =~ /(\S+) <[^>]+> (?:437|439) Unwanted newsgroup \"(\S+)\"$/o) { my ($server, $group) = ($1, $2); ($group) = split(/,/, $group); $server = lc $server unless $CASE_SENSITIVE; $inn_badart{$server}++; $inn_uw_ng_s{$server}++; $inn_uw_ng{$group}++; return 1; } # 437 Unwanted distribution "..." if ($left =~ /(\S+) <[^>]+> (?:437|439) Unwanted distribution \"(\S+)\"$/o) { my ($server, $dist) = ($1, $2); $server = lc $server unless $CASE_SENSITIVE; $inn_badart{$server}++; $inn_uw_dist_s{$server}++; $inn_uw_dist{$dist}++; return 1; } # 437 Linecount x != y +- z if ($left =~ /(\S+) <[^>]+> (?:437|439) Linecount/o) { my $server = $1; $server = lc $server unless $CASE_SENSITIVE; $inn_badart{$server}++; $inn_linecount{$server}++; return 1; } # 437 No colon-space in "xxxx" header if ($left =~ /(\S+) <[^>]+> (?:437|439) No colon-space in \"[^\"]+\" header/o) { my $server = $1; $server = lc $server unless $CASE_SENSITIVE; $inn_badart{$server}++; $innd_others{$server}++; $innd_no_colon_space{$server}++; return 1; } # 437 Article injected or posted in the future -- "xxxxx" if ($left =~ /(\S+) <[^>]+> (?:437|439) Article injected or posted in the future -- \"[^\"]+\"/o) { my $server = $1; $server = lc $server unless $CASE_SENSITIVE; $innd_posted_future{$server}++; $innd_others{$server}++; $inn_badart{$server}++; return 1; } # Article accepted but includes "....." if ($left =~ /(\S+) <[^>]+> Article accepted but includes/o) { my $server = $1; $server = lc $server unless $CASE_SENSITIVE; $innd_strange_strings{$server}++; $innd_others{$server}++; $inn_badart{$server}++; return 1; } # Cancelling <...> if ($left =~ /(\S+) <[^>]+> Cancelling/o) { return 1; } # all others are just counted as "Other" if ($left =~ /(\S+) /o) { my $server = $1; $server = lc $server unless $CASE_SENSITIVE; $inn_badart{$server}++; $innd_others{$server}++; return 1; } } ######## ## innd if ($prog eq "innd") { ## Note for innd logs: ## there's a lot of entries detected but still not used ## (because of a lack of interest). # think it's a dotquad return 1 if $left =~ /^think it\'s a dotquad$/o; if ($left =~ /^SERVER /o) { # SERVER perl filtering enabled return 1 if $left =~ /^SERVER perl filtering enabled$/o; # SERVER perl filtering disabled return 1 if $left =~ /^SERVER perl filtering disabled$/o; # SERVER Python filtering enabled return 1 if $left =~ /^SERVER Python filtering enabled$/o; # SERVER Python filtering disabled return 1 if $left =~ /^SERVER Python filtering disabled$/o; # SERVER cancelled +id return 1 if $left =~ /^SERVER cancelled /o; } # Python filter return 1 if $left =~ /^defined python methods$/o; return 1 if $left =~ /^reloading pyfilter$/o; return 1 if $left =~ /^reloaded pyfilter OK$/o; return 1 if $left =~ /^python interpreter initialized OK$/o; return 1 if $left =~ /^python method \w+ not found$/o; return 1 if $left =~ /^python: First load, so I can do initialization stuff\.$/o; return 1 if $left =~ /^python: filter_before_reload executing\.\.\.$/o; return 1 if $left =~ /^python: I\'m just reloading, so skip the formalities\.$/o; return 1 if $left =~ /^python: spamfilter successfully hooked into INN$/o; return 1 if $left =~ /^python: state change from \w+ to \w+ - /o; return 1 if $left =~ /^python: filter_close running, bye!$/o; # rejecting[perl] if ($left =~ /^rejecting\[perl\] <[^>]+> \d+ (.*)/o) { $innd_filter_perl{$1}++; return 1; } # rejecting[python] if ($left =~ /^rejecting\[python\] <[^>]+> \d+ (.*)/o) { $innd_filter_python{$1}++; return 1; } # closed lost return 1 if $left =~ /^\S+ closed lost \d+/o; # new control command if ($left =~ /^ctlinnd command (\w)(:.*)?/o) { my $command = $1; my $cmd = $ctlinnd{$command}; $cmd = $command unless $cmd; return 1 if $cmd eq 'flush'; # to avoid a double count $innd_control{"$cmd"}++; return 1; } # old control command (by letter) if ($left =~ /^(\w)$/o) { my $command = $1; my $cmd = $ctlinnd{$command}; $cmd = $command unless $cmd; return 1 if $cmd eq 'flush'; # to avoid a double count $innd_control{"$cmd"}++; return 1; } # old control command (letter + reason) if ($left =~ /^(\w):.*$/o) { my $command = $1; my $cmd = $ctlinnd{$command}; $cmd = $command unless $cmd; return 1 if $cmd eq 'flush'; # to avoid a double count $innd_control{"$cmd"}++; return 1; } # opened return 1 if $left =~ /\S+ opened \S+:\d+:file$/o; # buffered return 1 if $left =~ /\S+ buffered$/o; # spawned return 1 if $left =~ /\S+ spawned \S+:\d+:proc:\d+$/o; return 1 if $left =~ /\S+ spawned \S+:\d+:file$/o; # running return 1 if $left =~ /\S+ running$/o; # sleeping if ($left =~ /(\S+):\d+:proc:\d+ sleeping$/o) { my $server = $1; $server = lc $server unless $CASE_SENSITIVE; $innd_blocked{$server}++; return 1; } # blocked sleeping if ($left =~ /(\S+):\d+:proc:\d+ blocked sleeping/o) { my $server = $1; $server = lc $server unless $CASE_SENSITIVE; $innd_blocked{$server}++; return 1; } if ($left =~ /(\S+):\d+ blocked sleeping/o) { my $server = $1; $server = lc $server unless $CASE_SENSITIVE; $innd_blocked{$server}++; return 1; } # restarted return 1 if $left =~ m/^\S+ restarted$/o; # starting return 1 if $left =~ m/^\S+ starting$/o; # readclose return 1 if $left =~ m/^\S+:\d+ readclose+$/o; # rejected 502 if ($left =~ m/^(\S+) rejected 502$/) { my $server = $1; $server = lc $server unless $CASE_SENSITIVE; $innd_no_permission{$server}++; return 1; } # rejected 505 if ($left =~ m/^(\S+) rejected 505$/) { my $server = $1; $server = lc $server unless $CASE_SENSITIVE; $innd_too_many_connects_per_minute{$server}++; return 1; } # connected # # Record : instead of just as otherwise we may # miss some persistent connection newsfeeds that in any given innreport # reporting period may not record any connect entries. We'll accumulate # these into totals at the end of processing. if ($left =~ /^(\S+) connected (\d+)/o) { my $server = "$1:$2"; $server = lc $server unless $CASE_SENSITIVE; $innd_connect{$server}++; return 1; } # logstatus (information written in inn_status.html) return 1 if ($left =~ /\S+ status seconds \d+ accepted \d+ refused \d+ rejected \d+ duplicate \d+ accepted size \d+ duplicate size \d+(?: rejected size \d+)?$/o); # closed/checkpoint (with times) # # Add all checkpoints. They contain stats generated since the last # checkpoint. # On a closed, a checkpoint is generated by innd. It is therefore # useless to take into account a line corresponding to a closed status. if ($left =~ /(\S+:\d+) (checkpoint|closed) seconds (\d+) accepted (\d+) refused (\d+) rejected (\d+) duplicate (\d+) accepted size (\d+) duplicate size (\d+)(?: rejected size (\d+))?$/o) { my ($server, $status, $seconds, $accepted, $refused, $rejected, $duplicate, $accptsize, $dupsize, $rjctsize) = ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10); $server = lc $server unless $CASE_SENSITIVE; if ($status eq 'checkpoint') { $innd_seconds{$server} += $seconds; $innd_accepted{$server} += $accepted; $innd_refused{$server} += $refused; $innd_rejected{$server} += $rejected; $innd_stored_size{$server} += $accptsize; $innd_duplicated_size{$server} += $dupsize; $innd_rejected_size{$server} += ($rjctsize || 0); } return 1; } # closed (without times (?)) return 1 if $left =~ m/\S+ closed$/o; # closed (for a cancel feed - MODE CANCEL) return 1 if $left =~ m/localhost:\d+ closed seconds \d+ cancels \d+$/o; # flush if ($left =~ /(\S+) flush$/o) { $innd_control{"flush"}++; return 1; } # flush-file if ($left =~ /flush_file/) { $innd_control{"flush_file"}++; return 1; } # too many connections from site if ($left =~ /too many connections from (\S+)/o) { $innd_max_conn{$1}++; return 1; } # overview exit 0 elapsed 23 pid 28461 return 1 if $left =~ m/\S+ exit \d+ .*$/o; # internal rejecting huge article if ($left =~ /(\S+) internal rejecting huge article/o) { my $server = $1; $server =~ s/:\d+$//o; $server = lc $server unless $CASE_SENSITIVE; $innd_huge{$server}++; return 1; } # internal closing free channel if ($left =~ /(\S+) internal closing free channel/o) { $innd_misc{"Free channel"}++; return 1; } # internal (other) return 1 if $left =~ /\S+ internal/o; # wakeup return 1 if $left =~ /\S+ wakeup$/o; # throttle if ($left =~ /(\S+) throttled? /) { $innd_control{"throttle"}++; return 1; } # profile timer # ME time X nnnn X(X) [...] # The exact timers change from various versions of INN, so try to deal # with this in a general fashion. if ($left =~ m/^\S+\s+ # ME time\s(\d+)\s+ # time ((?:\S+\s\d+\(\d+\)\s*)+) # timer values $/ox) { $innd_time_times += $1; my $timers = $2; while ($timers =~ /(\S+) (\d+)\((\d+)\)\s*/g) { my $name = $timer_names{$1} || $1; $innd_time_time{$name} += $2; if ($3) { my $average = $2 / $3; $innd_time_num{$name} += $3; my $min = $innd_time_min{$name}; $innd_time_min{$name} = $average if (!defined($min) || $min > $average); my $max = $innd_time_max{$name}; $innd_time_max{$name} = $average if (!defined($max) || $max < $average); } } return 1; } # ME time xx idle xx(xx) [ bug ? a part of timer ?] return 1 if $left =~ m/^ME time \d+ idle \d+\(\d+\)\s*$/o; # ME HISstats x hitpos x hitneg x missed x dne # # from innd/his.c: # HIShitpos: the entry existed in the cache and in history. # HIShitneg: the entry existed in the cache but not in history. # HISmisses: the entry was not in the cache, but was in the history file. # HISdne: the entry was not in cache or history. if ($left =~ m/^ME\ HISstats # ME HISstats \ (\d+)\s+hitpos # hitpos \ (\d+)\s+hitneg # hitneg \ (\d+)\s+missed # missed \ (\d+)\s+dne # dne $/ox) { $innd_his{'Positive hits'} += $1; $innd_his{'Negative hits'} += $2; $innd_his{'Cache misses'} += $3; $innd_his{'Do not exist'} += $4; return 1; } # SERVER history cache final: 388656 lookups, 1360 hits if ($left =~ m/^SERVER history cache final: (\d+) lookups, (\d+) hits$/) { $innd_cache{'Lookups'} += $1; $innd_cache{'Hits'} += $2; return 1; } # bad_hosts (appears after a "cant gesthostbyname" from a feed) return 1 if $left =~ m/\S+ bad_hosts /o; # cant read return 1 if $left =~ m/\S+ cant read/o; # cant write return 1 if $left =~ m/\S+ cant write/o; # cant flush return 1 if $left =~ m/\S+ cant flush/o; # spoolwake return 1 if $left =~ m/\S+ spoolwake$/o; # spooling return 1 if $left =~ m/\S+ spooling/o; # DEBUG return 1 if $left =~ m/^DEBUG /o; # NCmode return 1 if $left =~ m/\S+ NCmode /o; # outgoing return 1 if $left =~ m/\S+ outgoing/o; # inactive return 1 if $left =~ m/\S+ inactive/o; # timeout return 1 if $left =~ m/\S+ timeout/o; # lcsetup return 1 if $left =~ m/\S+ lcsetup/o; # rcsetup return 1 if $left =~ m/\S+ rcsetup/o; # flush_all return 1 if $left =~ m/\S+ flush_all/o; # buffered return 1 if $left =~ m/\S+ buffered$/o; # descriptors return 1 if $left =~ m/\S+ descriptors/o; # ccsetup return 1 if $left =~ m/\S+ ccsetup/o; # renumbering return 1 if $left =~ m/\S+ renumbering/o; # renumber return 1 if $left =~ m/\S+ renumber /o; # ihave from me if ($left =~ m/\S+ ihave_from_me /o) { $controlchan_ihave_site{'ME'}++; return 1; } # sendme from me if ($left =~ m/\S+ sendme_from_me /o) { $controlchan_sendme_site{'ME'}++; return 1; } # newgroup if ($left =~ m/\S+ newgroup (\S+) as (\S)/o) { $innd_newgroup{$1} = $2; return 1; } # rmgroup if ($left =~ m/\S+ rmgroup (\S+)$/o) { $innd_rmgroup{$1}++; return 1; } # changegroup if ($left =~ m/\S+ change_group (\S+) to (\S)/o) { $innd_changegroup{$1} = $2; return 1; } # paused if ($left =~ m/(\S+) paused /o) { $innd_control{"paused"}++; return 1; } # throttled return 1 if $left =~ m/\S+ throttled/o; # reload if ($left =~ m/(\S+) reload/o) { $innd_control{"reload"}++; return 1; } # shutdown if ($left =~ m/(\S+) shutdown/o) { $innd_control{"shutdown"}++; return 1; } # re-executed return 1 if $left =~ m/^SERVER execv /o; # SERVER servermode paused return 1 if ($left =~ /(\S+) servermode paused$/o); # SERVER servermode running return 1 if ($left =~ /(\S+) servermode running$/o); # SERVER flushlogs paused if ($left =~ /(\S+) flushlogs /) { $innd_control{"flushlogs"}++; return 1; } # think it's a dotquad return 1 if $left =~ /think it\'s a dotquad: /o; # bad_ihave if ($left =~ /(\S+) bad_ihave /) { my $server = $1; $server =~ s/:\d+$//o; $server = lc $server unless $CASE_SENSITIVE; $innd_bad_ihave{$server}++; return 1; } # bad_messageid if ($left =~ /(\S+) bad_messageid/o) { my $server = $1; $server =~ s/:\d+$//o; $server = lc $server unless $CASE_SENSITIVE; $innd_bad_msgid{$server}++; return 1; } # bad_sendme if ($left =~ /(\S+) bad_sendme /o) { my $server = $1; $server =~ s/:\d+$//o; $server = lc $server unless $CASE_SENSITIVE; $innd_bad_sendme{$server}++; return 1; } # bad_command if ($left =~ /(\S+) bad_command /o) { my $server = $1; $server =~ s/:\d+$//o; $server = lc $server unless $CASE_SENSITIVE; $innd_bad_command{$server}++; return 1; } # bad_newsgroup if ($left =~ /(\S+) bad_newsgroup /o) { my $server = $1; $server =~ s/:\d+$//o; $innd_bad_newsgroup{$server}++; $server = lc $server unless $CASE_SENSITIVE; return 1; } if ($left =~ m/ cant /o) { # cant select Bad file number if ($left =~ / cant select Bad file number/o) { $innd_misc{"Bad file number"}++; return 1; } # cant gethostbyname if ($left =~ / cant gethostbyname/o) { $innd_misc{"gethostbyname error"}++; return 1; } # cant accept RCreader if ($left =~ / cant accept RCreader /o) { $innd_misc{"RCreader"}++; return 1; } # cant sendto CCreader if ($left =~ / cant sendto CCreader /o) { $innd_misc{"CCreader"}++; return 1; } # cant (other) skipped - not particularly interesting return 1; } # bad_newsfeeds no feeding sites return 1 if $left =~ /\S+ bad_newsfeeds no feeding sites/o; # CNFS: cycbuff rollover - possibly interesting return 1 if $left =~ /CNFS(?:-sm)?: cycbuff \S+ rollover to cycle/o; # CNFS: CNFSflushallheads: flushing - possibly interesting return 1 if $left =~ /CNFS(?:-sm)?: CNFSflushallheads: flushing /o; # CNFS: metacycbuff rollover with SEQUENTIAL return 1 if $left =~ /CNFS(?:-sm)?: metacycbuff \S+ cycbuff is moved to /o; # Cleanfeed status reports return 1 if $left =~ /^filter: status/o; return 1 if $left =~ /^filter: Reloading bad files/o; return 1 if $left =~ /^filter: Saved EMP database/o; return 1 if $left =~ /^filter: Restored EMP database/o; } ######## ## innfeed if ($prog eq "innfeed") { # connected if ($left =~ /(\S+):\d+ connected$/) { my $server = $1; $server = lc $server unless $CASE_SENSITIVE; $innfeed_connect{$server}++; return 1; } # closed periodic return 1 if $left =~ m/\S+:\d+ closed periodic$/o; # periodic close return 1 if $left =~ m/\S+:\d+ periodic close$/o; # checkpoint (child) return 1 if $left =~ m/\S+:\d+ checkpoint seconds \d+ offered \d+ accepted \d+ refused \d+ rejected \d+/o; # final (child) return 1 if $left =~ m/\S+:\d+ final seconds \d+ offered \d+ accepted \d+ refused \d+ rejected \d+/o; # global (real) return 1 if $left =~ m/\S+ global seconds \d+ offered \d+ accepted \d+ refused \d+ rejected \d+ missing \d+/o; # checkpoint (real) (new format) if ($left =~ /(\S+) checkpoint seconds (\d+) offered (\d+) accepted (\d+) refused (\d+) rejected (\d+) missing (\d+) accsize (\d+) rejsize (\d+) spooled (\d+)/o) { my ($server, $seconds, $offered, $accepted, $refused, $rejected, $missing, $accepted_size, $rejected_size, $spooled) = ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10); $server = lc $server unless $CASE_SENSITIVE; $innfeed_seconds{$server} += $seconds; $innfeed_offered{$server} += $offered; $innfeed_accepted{$server} += $accepted; $innfeed_refused{$server} += $refused; $innfeed_rejected{$server} += $rejected; $innfeed_missing{$server} += $missing; $innfeed_spooled{$server} += $spooled; $innfeed_accepted_size{$server} += $accepted_size; $innfeed_rejected_size{$server} += $rejected_size; return 1; } elsif ($left =~ /(\S+) checkpoint seconds (\d+) offered (\d+) accepted (\d+) refused (\d+) rejected (\d+) missing (\d+) spooled (\d+)/o) { my ($server, $seconds, $offered, $accepted, $refused, $rejected, $missing, $spooled) = ($1, $2, $3, $4, $5, $6, $7, $8); $server = lc $server unless $CASE_SENSITIVE; $innfeed_seconds{$server} += $seconds; $innfeed_offered{$server} += $offered; $innfeed_accepted{$server} += $accepted; $innfeed_refused{$server} += $refused; $innfeed_rejected{$server} += $rejected; $innfeed_missing{$server} += $missing; $innfeed_spooled{$server} += $spooled; return 1; } # checkpoint (only seconds & spooled) if ($left =~ /(\S+) checkpoint seconds (\d+) spooled (\d+)/o) { my ($server, $seconds, $spooled) = ($1, $2, $3); $server = lc $server unless $CASE_SENSITIVE; # Initialize a value for that key (otherwise, it does not appear in the # report, and the total line is wrong). $innfeed_offered{$server} += 0; $innfeed_seconds{$server} += $seconds; $innfeed_spooled{$server} += $spooled; return 1; } # final return 1 if $left =~ m/\S+ final seconds/o; # ME file xxxx shrunk from yyyy to zzz if ($left =~ /^ME file (.*)\.output shrunk from (\d+) to (\d+)$/) { my ($file, $s1, $s2) = ($1, $2, $3); $file =~ s|^.*/([^/]+)$|$1|; # keep only the server name $innfeed_shrunk{$file} += $s1 - $s2; return 1; } # profile timer # ME time X nnnn X(X) [...] return 1 if $left =~ m/backlogstats/; if ($left =~ m/^\S+\s+ # ME time\s(\d+)\s+ # time ((?:\S+\s\d+\(\d+\)\s*)+) # timer values $/ox) { $innfeed_time_times += $1; my $timers = $2; while ($timers =~ /(\S+) (\d+)\((\d+)\)\s*/g) { my $name = $innfeed_timer_names{$1} || $1; $innfeed_time_time{$name} += $2; if ($3) { $innfeed_time_num{$name} += $3; my $average = $2 / $3; my $min = $innfeed_time_min{$name}; $innfeed_time_min{$name} = $average if (!defined($min) || $min > $average); my $max = $innfeed_time_max{$name}; $innfeed_time_max{$name} = $average if (!defined($max) || $max < $average); } } return 1; } # xxx grabbing external tape file return 1 if $left =~ m/ grabbing external tape file/o; # hostChkCxns - maxConnections was return 1 if $left =~ m/hostChkCxns - maxConnections was /o; # cxnsleep return 1 if $left =~ m/\S+ cxnsleep .*$/o; # idle return 1 if $left =~ m/\S+ idle tearing down connection$/o; # remote return 1 if $left =~ m/\S+ remote .*$/o; # spooling return 1 if $left =~ m/\S+ spooling no active connections$/o; # ME articles total return 1 if $left =~ m/(?:SERVER|ME) articles total \d+ bytes \d+/o; # ME articles active return 1 if $left =~ m/(?:SERVER|ME) articles active \d+ bytes \d+/o; # connect : Connection refused return 1 if $left =~ m/connect : Connection refused/o; # connect : Network is unreachable return 1 if $left =~ m/connect : Network is unreachable/o; # connect : Address family not supported by protocol return 1 if $left =~ m/connect : Address family not supported by protocol/o; # connect : No route to host return 1 if $left =~ m/connect : No route to host/o; # connection vanishing return 1 if $left =~ m/connection vanishing/o; # can't resolve hostname return 1 if $left =~ m/can\'t resolve hostname/o; # new hand-prepared backlog file return 1 if $left =~ m/new hand-prepared backlog file/o; # flush re-connect failed return 1 if $left =~ m/flush re-connect failed/o; # internal QUIT while write pending return 1 if $left =~ m/internal QUIT while write pending/o; # ME source lost . Exiting return 1 if $left =~ m/(?:SERVER|ME) source lost . Exiting/o; # ME starting innfeed (+version & date) return 1 if $left =~ m/(?:SERVER|ME) starting (?:innfeed|at)/o; # ME finishing at (date) return 1 if $left =~ m/(?:SERVER|ME) finishing at /o; # mode no-CHECK entered return 1 if $left =~ m/mode no-CHECK entered/o; # mode no-CHECK exited return 1 if $left =~ m/mode no-CHECK exited/o; # closed return 1 if $left =~ m/^(\S+) closed$/o; # global (+ seconds offered accepted refused rejected missing) return 1 if $left =~ m/^(\S+) global/o; # idle connection still has articles return 1 if $left =~ m/^(\S+) idle connection still has articles$/o; # missing article for IHAVE-body return 1 if $left =~ m/^(\S+) missing article for IHAVE-body$/o; # cannot continue return 1 if $left =~ m/^cannot continue/o; if ($left =~ /^(?:SERVER|ME)/o) { # ME dropping articles into ... return 1 if $left =~ / dropping articles into /o; # ME dropped ... return 1 if $left =~ / dropped /o; # ME internal bad data in checkpoint file return 1 if $left =~ m/ internal bad data in checkpoint/o; # ME two filenames for same article return 1 if $left =~ m/ two filenames for same article/o; # ME unconfigured peer return 1 if $left =~ m/ unconfigured peer/o; # exceeding maximum article size return 1 if $left =~ m/ exceeding maximum article byte/o; # no space left on device errors return 1 if $left =~ m/ ioerr fclose/o; return 1 if $left =~ m/ lock failed for host/o; return 1 if $left =~ m/ lock file pid-write/o; return 1 if $left =~ m/ locked cannot setup peer/o; return 1 if $left =~ m/ received shutdown signal/o; # unconfigured peer return 1 if $left =~ m/ unconfigured peer/o; # ME lock return 1 if $left =~ m/ lock/o; # ME exception: getsockopt (0): Socket operation on non-socket return 1 if $left =~ m/ exception: getsockopt /o; # ME config aborting fopen (...) Permission denied return 1 if $left =~ m/ config aborting fopen /o; # ME cant chmod innfeed.pid.... return 1 if $left =~ m/ cant chmod \S+\/innfeed.pid/o; return 1 if $left =~ m/ tape open failed /o; return 1 if $left =~ m/ oserr open checkpoint file:/o; # ME finishing (quickly) return 1 if $left =~ m/\(quickly\) /o; # ME config: value of streaming is not a boolean return 1 if $left =~ m/config: value of \S+ is not/o; # innfeed rolling funnel file return 1 if $left =~ m/ preparing to roll /o; return 1 if $left =~ m/ reached EOF in /o; return 1 if $left =~ m/ opened /o; # when optional parameters are not present in innfeed.conf return 1 if $left =~ m/ config: adding default value for key /o; } # hostChkCxn - now: x.xx, prev: x.xx, abs: xx, curr: x return 1 if $left =~ m/ hostChkCxn - now/o; # loading path_to_config_file/innfeed.conf return 1 if $left =~ m/loading /o; # Finally, to avoid problems with strange error lines, ignore them. #return 1 if ($left =~ /ME /); } ######## ## innxmit if ($prog eq "innxmit") { # 437 Duplicate article if ($left =~ /(\S+) rejected [^\s]+ \(.*?\) (?:437|439) Duplicate article$/o) { my $server = $1; $server = lc $server unless $CASE_SENSITIVE; $innxmit_badart{$server}++; $innxmit_duplicate{$server}++; return 1; } # 437 Unapproved for if ($left =~ /(\S+) rejected [^\s]+ \(.*\) (?:437|439) Unapproved for \"(.*?)\"$/o) { my ($server, $group) = ($1, $2); $server = lc $server unless $CASE_SENSITIVE; $innxmit_badart{$server}++; $innxmit_unapproved{$server}++; $innxmit_unapproved_g{$group}++; return 1; } # 437 Too old -- ... if ($left =~ /(\S+) rejected [^\s]+ \(.*\) (?:437|439) Too old -- \".*?\"$/o) { my $server = $1; $server = lc $server unless $CASE_SENSITIVE; $innxmit_badart{$server}++; $innxmit_tooold{$server}++; return 1; } # 437 Unwanted site ... in path if ($left =~ /(\S+) rejected [^\s]+ \(.*?\) (?:437|439) Unwanted site (\S+) in path$/o) { my ($server, $site) = ($1, $2); $server = lc $server unless $CASE_SENSITIVE; $innxmit_badart{$server}++; $innxmit_uw_site{$server}++; # $innxmit_site_path{$site}++; return 1; } # 437 Unwanted newsgroup "..." if ($left =~ /(\S+) rejected [^\s]+ \(.*?\) (?:437|439) Unwanted newsgroup \"(\S+)\"$/o) { my ($server, $group) = ($1, $2); $server = lc $server unless $CASE_SENSITIVE; $innxmit_badart{$server}++; $innxmit_uw_ng_s{$server}++; $innxmit_uw_ng{$group}++; return 1; } # 437 Unwanted distribution "..." if ($left =~ /(\S+) rejected [^\s]+ \(.*?\) (?:437|439) Unwanted distribution \"(\S+)\"$/o) { my ($server, $dist) = ($1, $2); $server = lc $server unless $CASE_SENSITIVE; $innxmit_badart{$server}++; $innxmit_uw_dist_s{$server}++; $innxmit_uw_dist{$dist}++; return 1; } # xx rejected foo.bar/12345 (foo/bar/12345) 437 Unwanted distribution "..." if ($left =~ /^(\S+) rejected .* (?:437|439) Unwanted distribution \"(\S+)\"$/o) { my ($server, $dist) = ($1, $2); $server = lc $server unless $CASE_SENSITIVE; $innxmit_badart{$server}++; $innxmit_uw_dist_s{$server}++; $innxmit_uw_dist{$dist}++; return 1; } # 437 Linecount x != y +- z if ($left =~ /(\S+) rejected [^\s]+ \(.*?\) (?:437|439) Linecount/o) { my $server = $1; $server = lc $server unless $CASE_SENSITIVE; $innxmit_badart{$server}++; $innxmit_linecount{$server}++; return 1; } # 437 Newsgroup name illegal -- "xxx" if ($left =~ /(\S+) rejected .* (?:437|439) Newsgroup name illegal -- "[^\"]*"$/) { my $server = $1; $server = lc $server unless $CASE_SENSITIVE; $innxmit_others{$server}++; $innxmit_badart{$server}++; return 1; } # Streaming retries return 1 if ($left =~ /\d+ Streaming retries$/o); # ihave failed if ($left =~ /(\S+) ihave failed/o) { my $server = $1; $server = lc $server unless $CASE_SENSITIVE; $innxmit_ihfail{$server} = 1; if ($left = /436 \S+ NNTP \S+ out of space/o) { $innxmit_nospace{$server}++; return 1; } if ($left = /400 \S+ space/o) { $innxmit_nospace{$server}++; return 1; } if ($left = /400 Bad file/o) { $innxmit_crefused{$server}++; return 1; } if ($left = /480 Transfer permission denied/o) { $innxmit_crefused{$server}++; return 1; } } # stats (new format) if ($left =~ /(\S+) stats offered (\d+) accepted (\d+) refused (\d+) rejected (\d+) missing (\d+) accsize (\d+) rejsize (\d+)$/o) { my ($server, $offered, $accepted, $refused, $rejected, $missing, $accbytes, $rejbytes) = ($1, $2, $3, $4, $5, $6, $7, $8); $server = lc $server unless $CASE_SENSITIVE; $innxmit_offered{$server} += $offered; $innxmit_offered{$server} -= $innxmit_ihfail{$server} if ($innxmit_ihfail{$server}); $innxmit_accepted{$server} += $accepted; $innxmit_refused{$server} += $refused; $innxmit_rejected{$server} += $rejected; $innxmit_missing{$server} += $missing; $innxmit_accepted_size{$server} += $accbytes; $innxmit_rejected_size{$server} += $rejbytes; $innxmit_site{$server}++; $innxmit_ihfail{$server} = 0; return 1; } # stats if ($left =~ /(\S+) stats offered (\d+) accepted (\d+) refused (\d+) rejected (\d+)$/o) { my ($server, $offered, $accepted, $refused, $rejected) = ($1, $2, $3, $4, $5); $server = lc $server unless $CASE_SENSITIVE; $innxmit_offered{$server} += $offered; $innxmit_offered{$server} -= $innxmit_ihfail{$server} if ($innxmit_ihfail{$server}); $innxmit_accepted{$server} += $accepted; $innxmit_refused{$server} += $refused; $innxmit_rejected{$server} += $rejected; $innxmit_site{$server}++; $innxmit_ihfail{$server} = 0; return 1; } # times if ($left =~ /(\S+) times user (.+) system (\S+) elapsed (\S+)$/o) { my ($server, $user, $system, $elapsed) = ($1, $2, $3, $4); $server = lc $server unless $CASE_SENSITIVE; $innxmit_times{$server} += $elapsed; return 1; } # connect & no space if ($left =~ /(\S+) connect \S+ 400 No space/o) { my $server = $1; $server = lc $server unless $CASE_SENSITIVE; $innxmit_nospace{$server}++; $innxmit_site{$server}++; return 1; } # connect & NNTP no space if ($left =~ /(\S+) connect \S+ 400 \S+ out of space/o) { my $server = $1; $server = lc $server unless $CASE_SENSITIVE; $innxmit_nospace{$server}++; $innxmit_site{$server}++; return 1; } # connect & loadav if ($left =~ /(\S+) connect \S+ 400 loadav/o) { my $server = $1; if ($left =~ /expir/i) { $server = lc $server unless $CASE_SENSITIVE; $innxmit_expire{$server}++; $innxmit_site{$server}++; return 1; } } # connect 400 (other) if ($left =~ /(\S+) connect \S+ 400/o) { my $server = $1; $server = lc $server unless $CASE_SENSITIVE; $innxmit_crefused{$server}++; $innxmit_site{$server}++; return 1; } # connect failed if ($left =~ /(\S+) connect failed/o) { my $server = $1; $server = lc $server unless $CASE_SENSITIVE; $innxmit_cfail_host{$server}++; $innxmit_site{$server}++; return 1; } # authenticate failed if ($left =~ /(\S+) authenticate failed/o) { my $server = $1; $server = lc $server unless $CASE_SENSITIVE; $innxmit_afail_host{$server}++; $innxmit_site{$server}++; return 1; } # xxx ihave failed 400 loadav [innwatch:hiload] yyy gt zzz if ($left =~ /^(\S+) ihave failed 400 loadav/o) { my $server = $1; $server = lc $server unless $CASE_SENSITIVE; $innxmit_hiload{$server}++; return 1; } # ihave failed return 1 if ($left =~ /\S+ ihave failed/o); # requeued (....) 436 No space return 1 if ($left =~ /\S+ requeued \S+ 436 No space/o); # requeued (....) 400 No space return 1 if ($left =~ /\S+ requeued \S+ 400 No space/o); # requeued (....) 436 Can't write history return 1 if ($left =~ /\S+ requeued \S+ 436 Can\'t write history/o); # unexpected response code return 1 if ($left =~ /unexpected response code /o); } ######## ## nntplink if ($prog eq "nntplink") { $left =~ s/^(\S+):/$1/; # EOF if ($left =~ /(\S+) EOF /o) { my $server = $1; $server = lc $server unless $CASE_SENSITIVE; $nntplink_site{$server}++; $nntplink_eof{$server}++; return 1; } # Broken pipe if ($left =~ /(\S+) Broken pipe$/o) { my $server = $1; $server = lc $server unless $CASE_SENSITIVE; $nntplink_site{$server}++; $nntplink_bpipe{$server}++; return 1; } # already running - won't die return 1 if $left =~ /\S+ nntplink.* already running /o; # connection timed out if ($left =~ /(\S+) connection timed out/o) { my $server = $1; $server = lc $server unless $CASE_SENSITIVE; $nntplink_site{$server}++; $nntplink_bpipe{$server}++; return 1; } # greeted us with 400 No space if ($left =~ /(\S+) greeted us with 400 No space/o) { my $server = $1; $server = lc $server unless $CASE_SENSITIVE; $nntplink_site{$server}++; $nntplink_nospace{$server}++; return 1; } # greeted us with 400 loadav if ($left =~ /(\S+) greeted us with 400 loadav/o) { my $server = $1; $server = lc $server unless $CASE_SENSITIVE; $nntplink_site{$server}++; $nntplink_hiload{$server}++; return 1; } # greeted us with 400 (other) if ($left =~ /(\S+) greeted us with 400/o) { my $server = $1; $server = lc $server unless $CASE_SENSITIVE; $nntplink_site{$server}++; if ($left =~ /expir/i) { $nntplink_expire{$server}++; } else { $nntplink_fail{$server}++; } return 1; } # greeted us with 502 if ($left =~ /(\S+) greeted us with 502/o) { my $server = $1; $server = lc $server unless $CASE_SENSITIVE; $nntplink_site{$server}++; $nntplink_auth{$server}++; return 1; } # sent authinfo if ($left =~ /(\S+) sent authinfo/o) { my $server = $1; $server = lc $server unless $CASE_SENSITIVE; $nntplink_site{$server}++; $nntplink_auth{$server}++; return 1; } # socket() if ($left =~ /(\S+) socket\(\): /o) { my $server = $1; $server = lc $server unless $CASE_SENSITIVE; $nntplink_site{$server}++; $nntplink_sockerr{$server}++; return 1; } # select() if ($left =~ /(\S+) select\(\) /o) { my $server = $1; $server = lc $server unless $CASE_SENSITIVE; $nntplink_site{$server}++; $nntplink_selecterr{$server}++; return 1; } # sent IHAVE if ($left =~ /(\S+) sent IHAVE/o) { my $server = $1; $server = lc $server unless $CASE_SENSITIVE; $nntplink_ihfail{$server}++; if (($left =~ / 436 /) && ($left =~ / out of space /)) { $nntplink_fake_connects{$server}++; $nntplink_nospace{$server}++; } return 1; } # article .... failed(saved): 436 No space if ($left =~ /(\S+) .* failed\(saved\): 436 No space$/o) { my $server = $1; $server = lc $server unless $CASE_SENSITIVE; $nntplink_nospace{$server}++; return 1; } # article .. 400 No space left on device writing article file -- throttling if ($left =~ /(\S+) .* 400 No space left on device writing article file -- throttling$/o) { my $server = $1; $server = lc $server unless $CASE_SENSITIVE; $nntplink_nospace{$server}++; return 1; } # stats if ($left =~ /(\S+) stats (\d+) offered (\d+) accepted (\d+) rejected (\d+) failed (\d+) connects$/o) { my ($server, $offered, $accepted, $rejected, $failed, $connects) = ($1, $2, $3, $4, $5, $6); $server = lc $server unless $CASE_SENSITIVE; $nntplink_offered{$server} += $offered - $nntplink_ihfail{$server}++; $nntplink_accepted{$server} += $accepted; $nntplink_rejected{$server} += $rejected; $nntplink_failed{$server} += $failed; $nntplink_connects{$server} += $connects; $nntplink_ihfail{$server} = 0; if ($nntplink_fake_connects{$server}) { $nntplink_site{$server} += $nntplink_fake_connects{$server}; $nntplink_fake_connects{$server} = 0; } else { $nntplink_site{$server}++; } return 1; } # xmit if ($left =~ /(\S+) xmit user (.+) system (\S+) elapsed (\S+)$/o) { my ($server, $user, $system, $elapsed) = ($1, $2, $3, $4); $server = lc $server unless $CASE_SENSITIVE; $nntplink_times{$server} += $elapsed; return 1; } # xfer return 1 if $left =~ /\S+ xfer/o; # Links down .. x hours if ($left =~ /(\S+) Links* down \S+ \d+/o) { # Collected but not used # my $server = $1; # $server = lc $server unless $CASE_SENSITIVE; # $nntplink_down{$server} += $hours; return 1; } # 503 Timeout if ($left =~ /^(\S+) \S+ \S+ \S+ 503 Timeout/o) { # Collected but not used # my $server = $1; # $server = lc $server unless $CASE_SENSITIVE; # $nntplink_timeout{$server}++; return 1; } # read() error while reading reply if ($left =~ /^(\S+): read\(\) error while reading reply/o) { my $server = $1; $server = lc $server unless $CASE_SENSITIVE; $nntplink_failed{$server}++; return 1; } # Password file xxxx not found return 1 if $left =~ /^\S+ Password file \S+ not found/; # No such return 1 if $left =~ /^\S+ \S+ \S+ No such/; # already running return 1 if $left =~ /^\S+ \S+ already running/; # error reading version from datafile return 1 if $left =~ /error reading version from datafile/; } ######## ## nnrpd if ($prog =~ /^nnrpd(?:-ssl)?$/) { # Fix a small bug of nnrpd (inn 1.4*) $left =~ s/^ /\? /o; # Another bug (in INN 1.5b1) return 1 if $left =~ /^\020\002m$/o; # ^P^Bm # bad_history at num for return 1 if $left =~ /bad_history at \d+ for /o; # timeout short return 1 if $left =~ /\S+ timeout short$/o; # < or > + (blablabla) return 1 if $left =~ /^\S+ [\<\>] /o; # cant opendir ... I/O error return 1 if $left =~ /\S+ cant opendir \S+ I\/O error$/o; # perl filtering enabled return 1 if $left =~ /perl filtering enabled$/o; # Python filtering enabled return 1 if $left =~ /Python filtering enabled$/o; return 1 if $left =~ /^python interpreter initialized OK$/o; return 1 if $left =~ /^python method \S+ not found$/o; return 1 if $left =~ /^python authenticate method succeeded, return code \d+, error string /o; return 1 if $left =~ /^python access method succeeded$/o; return 1 if $left =~ /^python dynamic method \(\w+ access\) succeeded, refuse string: /o; return 1 if $left =~ /^python: .+ module successfully hooked into nnrpd$/o; return 1 if $left =~ /^python: nnrpd .+ class instance created$/o; return 1 if $left =~ /^python: n_a authenticate\(\) invoked: hostname \S+, ipaddress \S+, interface \S+, user /o; return 1 if $left =~ /^python: n_a access\(\) invoked: hostname \S+, ipaddress \S+, interface \S+, user /o; return 1 if $left =~ /^python: n_a dynamic\(\) invoked against type \S+, hostname \S+, ipaddress \S+, interface \S+, user /o; return 1 if $left =~ /^python: authentication by username succeeded$/o; return 1 if $left =~ /^python: authentication by username failed$/o; return 1 if $left =~ /^python: authentication access by IP address succeeded$/o; return 1 if $left =~ /^python: authentication access by IP address failed$/o; return 1 if $left =~ /^python: dynamic access module successfully hooked into nnrpd$/o; return 1 if $left =~ /^python: dynamic authorization access for read access granted$/o; return 1 if $left =~ /^python: dynamic authorization access type is not known: /o; # during scanlogs return 1 if $left =~ /^\S+ rejected Flushing log and syslog files$/o; return 1 if $left =~ /^\S+ rejected Snapshot log and syslog files$/o; # other logs that should not be reported as errors return 1 if $left =~ /^\S+ auth also-log: /o; return 1 if $left =~ /^\S+ res also-log: /o; return 1 if $left =~ /^\S+ rejected by rule /o; # connect if ($left =~ /(\S+) (\([0-9a-fA-F:.]*\) )?connect(?: - port \d+)?$/o) { my $cust = $1; $cust = lc $cust unless $CASE_SENSITIVE; my $dom = &host2dom($cust); $nnrpd_dom_connect{$dom}++; $nnrpd_connect{$cust}++; return 1; } # group if ($left =~ /(\S+) group (\S+) (\d+)$/o) { my ($cust, $group, $num) = ($1, $2, $3); $cust = lc $cust unless $CASE_SENSITIVE; my $dom = &host2dom($cust); if ($num) { $nnrpd_group{$group} += $num; $nnrpd_groups{$cust}++; $nnrpd_dom_groups{$dom}++; my ($hierarchy) = $group =~ /^([^\.]+).*$/o; $nnrpd_hierarchy{$hierarchy} += $num; } return 1; } # post/ihave failed if ($left =~ /(\S+) (post|ihave) failed (.*)$/o) { my ($cust, $error) = ($1, $3); $cust = lc $cust unless $CASE_SENSITIVE; my $dom = &host2dom($cust); $nnrpd_dom_post_error{$dom}++; $nnrpd_post_error{$cust}++; return 1; } # post ok return 1 if $left =~ /\S+ post ok/o; # ihave ok return 1 if $left =~ /\S+ ihave ok/o; # posts if ($left =~ /(\S+) posts received (\d+) rejected (\d+)$/o) { my ($cust, $received, $rejected) = ($1, $2, $3); $cust = lc $cust unless $CASE_SENSITIVE; my $dom = &host2dom($cust); $nnrpd_dom_post_ok{$dom} += $received; $nnrpd_dom_post_rej{$dom} += $rejected; $nnrpd_post_ok{$cust} += $received; $nnrpd_post_rej{$cust} += $rejected; return 1; } # noperm post without permission if ($left =~ /(\S+) noperm post without permission/o) { my $cust = $1; $cust = lc $cust unless $CASE_SENSITIVE; my $dom = &host2dom($cust); $nnrpd_dom_post_rej{$dom} ++; $nnrpd_post_rej{$cust} ++; return 1; } # no_permission if ($left =~ /(\S+) no_(permission|access)$/o) { my $cust = $1; $cust = lc $cust unless $CASE_SENSITIVE; my $dom = &host2dom($cust); $nnrpd_no_permission{$cust}++; $nnrpd_dom_no_permission{$dom}++; return 1; } # bad_auth if ($left =~ /(\S+) bad_auth$/o) { my $cust = $1; $cust = lc $cust unless $CASE_SENSITIVE; my $dom = &host2dom($cust); $nnrpd_dom_no_permission{$dom}++; $nnrpd_no_permission{$cust}++; return 1; } # Authentication failure # User not known to the underlying authentication module return 1 if $left =~ / ckpasswd: pam_authenticate failed: /o; return 1 if $left =~ / ckpasswd: user .+ unknown$/o; # AUTHINFO (a username is a bytes string) if (($left =~ /\S+ user (.+)$/o) && ($left !~ /\S+ times user .+ system \S+ idle \S+ elapsed \S+$/o)) { my $user = $1; $nnrpd_auth{$user}++; return 1; } # unrecognized + command if ($left =~ /(\S+) unrecognized (.*)$/o) { my ($cust, $error) = ($1, $2); $cust = lc $cust unless $CASE_SENSITIVE; my $dom = &host2dom($cust); $error = "_null command_" if ($error !~ /\S/); $error =~ s/^(xmotd) .*$/$1/i if ($error =~ /^xmotd .*$/i); $nnrpd_dom_unrecognized{$dom}++; $nnrpd_unrecognized{$cust}++; $nnrpd_unrecogn_cmd{$error}++; return 1; } # exit (also called when using STARTTLS) if ($left =~ /(\S+) (?:exit|exit for STARTTLS|exit for AUTHINFO SASL) articles (\d+) groups (\d+)$/o) { my ($cust, $articles, $groups) = ($1, $2, $3); $cust = lc $cust unless $CASE_SENSITIVE; my $dom = &host2dom($cust); if ($cust eq '?') { $nnrpd_connect{$cust}++; $nnrpd_dom_connect{$dom}++; } $nnrpd_articles{$cust} += $articles; $nnrpd_dom_articles{$dom} += $articles; return 1; } # times if ($left =~ /(\S+) times user (.+) system (\S+) idle (\S+) elapsed (\S+)$/o) { my ($cust, $user, $system, $idle, $elapsed) = ($1, $2, $3, $4, $5); $cust = lc $cust unless $CASE_SENSITIVE; my $dom = &host2dom($cust); $nnrpd_times{$cust} += $elapsed; $nnrpd_resource_user{$cust} += $user; $nnrpd_resource_system{$cust} += $system; $nnrpd_resource_idle{$cust} += $idle; $nnrpd_resource_elapsed{$cust} += $elapsed; $nnrpd_dom_times{$dom} += $elapsed; return 1; } # artstats if ($left =~ /(\S+) artstats get (\d+) time (\d+) size (\d+)$/o) { my ($cust, $articles, $time, $bytes) = ($1, $2, $3, $4); $cust = lc $cust unless $CASE_SENSITIVE; my $dom = &host2dom($cust); $nnrpd_bytes{$cust} += $bytes; $nnrpd_dom_bytes{$dom} += $bytes; return 1; } # timeout if ($left =~ /(\S+) timeout$/o) { my $cust = $1; $cust = lc $cust unless $CASE_SENSITIVE; my $dom = &host2dom($cust); $nnrpd_dom_timeout{$dom}++; $nnrpd_timeout{$cust}++; return 1; } # timeout in post if ($left =~ /(\S+) timeout in post$/o) { my $cust = $1; $cust = lc $cust unless $CASE_SENSITIVE; my $dom = &host2dom($cust); $nnrpd_dom_timeout{$dom}++; $nnrpd_timeout{$cust}++; return 1; } # can't read: Connection timed out if ($left =~ /(\S+) can\'t read: Connection timed out$/o) { my $cust = $1; $cust = lc $cust unless $CASE_SENSITIVE; my $dom = &host2dom($cust); $nnrpd_dom_timeout{$dom}++; $nnrpd_timeout{$cust}++; return 1; } # can't read: Operation timed out if ($left =~ /(\S+) can\'t read: Operation timed out$/o) { my $cust = $1; $cust = lc $cust unless $CASE_SENSITIVE; my $dom = &host2dom($cust); $nnrpd_dom_timeout{$dom}++; $nnrpd_timeout{$cust}++; return 1; } # can't read: Connection reset by peer if ($left =~ /(\S+) can\'t read: Connection reset by peer$/o) { my $cust = $1; $cust = lc $cust unless $CASE_SENSITIVE; my $dom = &host2dom($cust); $nnrpd_dom_reset_peer{$dom}++; $nnrpd_reset_peer{$cust}++; return 1; } # can't read: Network is unreachable return 1 if $left =~ /(\S+) can\'t read: Network is unreachable$/o; # gethostbyaddr: xxx.yyy.zzz != a.b.c.d if ($left =~ /^gethostbyaddr: (.*)$/o) { my $msg = $1; $nnrpd_gethostbyaddr{$msg}++; return 1; } # can't gethostbyaddr if ($left =~ /\? can\'t gethostbyaddr (\S+) .*$/o) { my $ip = $1; $nnrpd_gethostbyaddr{$ip}++; return 1; } # can't getpeername if ($left =~ /\? can\'t getpeername/o) { # $nnrpd_getpeername++; $nnrpd_gethostbyaddr{"? (can't getpeername)"}++; return 1; } # can't getsockname return 1 if $left =~ /^\S+ can\'t getsockname$/o; # reverse lookup failed return 1 if $left =~ /^\? reverse lookup for \S+ failed: .* -- using IP address for access$/o; # profile timer # ME time X nnnn X(X) [...] # The exact timers change from various versions of INN, so try to deal # with this in a general fashion. if ($left =~ m/^\S+\s+ # ME time\s(\d+)\s+ # time ((?:\S+\s\d+\(\d+\)\s*)+) # timer values $/ox) { $nnrpd_time_times += $1; my $timers = $2; while ($timers =~ /(\S+) (\d+)\((\d+)\)\s*/g) { my $name = $nnrpd_timer_names{$1} || $1; $nnrpd_time_time{$name} += $2; if ($3) { my $average = $2 / $3; $nnrpd_time_num{$name} += $3; my $min = $nnrpd_time_min{$name}; $nnrpd_time_min{$name} = $average if (!defined($min) || $min > $average); my $max = $nnrpd_time_max{$name}; $nnrpd_time_max{$name} = $average if (!defined($max) || $max < $average); } } return 1; } # ME dropping articles into ... return 1 if $left =~ /ME dropping articles into /o; # newnews (interesting but ignored till now) return 1 if $left =~ /^\S+ newnews /o; # cant fopen (ignored too) return 1 if $left =~ /^\S+ cant fopen /o; # can't read: No route to host return 1 if $left =~ /can\'t read: No route to host/o; # can't read: Broken pipe return 1 if $left =~ /can\'t read: Broken pipe/o; # eof in post return 1 if $left =~ /^\S+ EOF in post$/o; # ioctl: ... return 1 if $left =~ /^ioctl: /o; # other stats return 1 if $left =~ /^\S+ overstats count \d+ hit \d+ miss \d+ time \d+ size \d+ dbz \d+ seek \d+ get \d+ artcheck \d+$/o; # starttls return 1 if $left =~ /^starttls: \S+ with cipher \S+ \(\d+\/\d+ bits\) no authentication$/o; } ######## ## overchan if ($prog eq "overchan") { # times if ($left =~ /timings (\d+) arts (\d+) of (\d+) ms$/o) { my ($articles, $work_time, $run_time) = ($1, $2, $3); # ??? What to do with numbers return 1; } } ######## ## batcher if ($prog eq "batcher") { # times if ($left =~ /(\S+) times user (.+) system (\S+) elapsed (\S+)$/o) { my ($server, $user, $system, $elapsed) = ($1, $2, $3, $4); $server = lc $server unless $CASE_SENSITIVE; # $batcher_user{$server} += $user; # $batcher_system{$server} += $system; $batcher_elapsed{$server} += $elapsed; return 1; } # stats if ($left =~ /(\S+) stats batches (\d+) articles (\d+) bytes (\d+)$/o) { my ($server, $batches, $articles, $bytes) = ($1, $2, $3, $4); $server = lc $server unless $CASE_SENSITIVE; $batcher_offered{$server} += $batches; $batcher_articles{$server} += $articles; $batcher_bytes{$server} += $bytes; return 1; } } ######## ## rnews if ($prog eq "rnews") { # rejected connection if ($left =~ /rejected connection (.*)$/o) { $rnews_rejected{$1}++; return 1; } # cant open_remote if ($left =~ /(cant open_remote .*)$/o) { $rnews_rejected{$1}++; return 1; } # rejected 437 Unwanted newsgroup if ($left =~ /rejected (?:437|439) Unwanted newsgroup \"(.*)\"$/o) { $rnews_bogus_ng{$1}++; return 1; } # rejected 437 Unapproved for "xx" if ($left =~ /rejected (?:437|439) Unapproved for \"(.*)\"$/o) { $rnews_unapproved{$1}++; return 1; } # rejected 437 Unwanted distribution if ($left =~ /rejected (?:437|439) Unwanted distribution (.*)$/o) { $rnews_bogus_dist{$1}++; return 1; } # rejected 437 Bad "Date" if ($left =~ /rejected (?:437|439) Bad \"Date\" (.*)$/o) { $rnews_bogus_date{$1}++; return 1; } # rejected 437 Bad "Injection-Date" if ($left =~ /rejected (?:437|439) Bad \"Injection-Date\" (.*)$/o) { $rnews_bogus_date{$1}++; return 1; } # rejected 437 Article injected or posted in the future if ($left =~ /rejected (?:437|439) Article injected or posted in the future -- \"(.*)\"$/o) { $rnews_bogus_date{"(future) $1"}++; return 1; } # rejected 437 Too old -- "..." if ($left =~ /rejected (?:437|439) Too old -- (.*)$/o) { $rnews_too_old++; return 1; } # rejected 437 Linecount... if ($left =~ /rejected (?:437|439) Linecount \d+ \!= \d+/o) { $rnews_linecount++; return 1; } # rejected 437 Duplicate if ($left =~ /rejected (?:437|439) Duplicate$/o) { $rnews_duplicate++; return 1; } # rejected 437 Duplicate article if ($left =~ /rejected (?:437|439) Duplicate article/o) { $rnews_duplicate++; return 1; } # rejected 437 No colon-space ... if ($left =~ /rejected (?:437|439) No colon-space in \"(.*)\" header$/o) { $rnews_no_colon_space++; return 1; } # duplicate path.. if ($left =~ /^duplicate /o) { $rnews_duplicate++; return 1; } # offered feed if ($left =~ /^offered \S+ (\S+)/o) { my $host = $1; $host = lc $host unless $CASE_SENSITIVE; # Small hack used to join article spooled when innd is throttle. # In this situation, the hostname is a 8 hex digits string # To avoid confusions with real feeds, the first character is forced # to be a '3' or a '4' (will work between 9/7/1995 and 13/7/2012). $host = "Local postings" if $host =~ /^[34][0-9a-f]{7}$/; $rnews_host{$host}++; return 1; } # rejected 437 ECP rejected return 1 if $left =~ m/rejected (?:437|439) ECP rejected/o; # rejected 437 "Subject" header too long return 1 if $left =~ m/header too long/o; # rejected 437 Too long line in header 1163 bytes return 1 if $left =~ m/rejected (?:437|439) Too long line in header/o; # rejected 437 Too many newsgroups (meow) return 1 if $left =~ m/rejected (?:437|439) Too many newsgroups/o; # rejected 437 Space before colon in " good signature from foo@bar.com if ($left =~ /good signature from (.*)/o) { $nocem_goodsigs{$1}++; $nocem_totalgood++; $nocem_lastid = $1; return 1; } #
bad signature from foo@bar.com if ($left =~ /bad signature from (.*)/o) { $nocem_badsigs{$1}++; $nocem_goodsigs{$1} = 0 unless ($nocem_goodsigs{$1}); $nocem_totalbad++; $nocem_lastid = $1; return 1; } #
contained 123 new 456 total ids if ($left =~ /contained (\d+) new (\d+) total ids/o) { $nocem_newids += $1; $nocem_newids{$nocem_lastid} += $1; $nocem_totalids += $2; $nocem_totalids{$nocem_lastid} += $2; return 1; } return 1; } ######## ## nocem if ($prog eq "nocem") { if ($left =~ /processed notice .* by (.*) \((\d+) ids,/o) { $nocem_goodsigs{$1}++; $nocem_totalgood++; $nocem_lastid = $1; $nocem_newids += $2; $nocem_newids{$nocem_lastid} += $2; $nocem_totalids += $2; $nocem_totalids{$nocem_lastid} += $2; return 1; } if ($left =~ /Article <[^>]*>: (.*) \(ID [[:xdigit:]]*\) not in keyring/o) { $nocem_badsigs{$1}++; $nocem_goodsigs{$1} = 0 unless ($nocem_goodsigs{$1}); $nocem_totalbad++; $nocem_lastid = $1; return 1; } if ($left =~ /Article <[^>]*>: bad signature from (.*)/o) { $nocem_badsigs{$1}++; $nocem_goodsigs{$1} = 0 unless ($nocem_goodsigs{$1}); $nocem_totalbad++; $nocem_lastid = $1; return 1; } if ($left =~ /Article <[^>]*>: malformed signature/o) { $nocem_badsigs{'N/A'}++; $nocem_goodsigs{'N/A'} = 0 unless ($nocem_goodsigs{'N/A'}); $nocem_totalbad++; $nocem_lastid = 'N/A'; return 1; } return 1; } ########### ## controlchan if ($prog eq "controlchan") { # loaded /x/y/z/foo.pl return 1 if $left =~ m/^loaded /; # starting return 1 if $left =~ m/^starting/; # skipping rmgroup x@y (pgpverify failed) in if ($left =~ m/^skipping \S+ (\S+) \(pgpverify failed\) in /) { $controlchan_skippgp{$1}++; $controlchan_who{$1}++; return 1; } if ($left =~ m/^control_(sendme|ihave), [^,]+, (\S+), doit,/o) { if ($1 eq "sendme") { $controlchan_sendme_site{$2}++; } else { $controlchan_ihave_site{$2}++; } return 1; } # control_XXgroup, foo.bar [moderated] who who token, [pattern], [pattern], encoding, peer, action, 1 # # Various other random junk can end up in the moderated field, like y, # unmoderated, m, etc. depending on what the control message says. It # can even have multiple words, which we still don't handle. if ($left =~ m/^control_(\S+), # type of msg \s(?:\S+)? # newsgroup name (\s\S+)? # optional \s(\S+) # e-mail \s\S+ # e-mail \s\S+, # storage token \s(?:\S+)?, # exclusion pattern \s(?:\S+)?, # drop pattern \s\S+, # local encoding \s\S+, # server \s([^=,]+(?:=\S+)?), # action \s*(.*) # 1 if message approved and first logged in the file /x) { if ($1 eq 'newgroup') { $controlchan_new{$3}++; } elsif ($1 eq 'rmgroup') { $controlchan_rm{$3}++; } else { $controlchan_other{$3}++ if $5 >= 0; } $controlchan_who{$3}++; $controlchan_ok{$3} += $5 if $5 > 0; my $action = $4; my $email = $3; $action =~ s/=.*//; $controlchan_doit{$email}++ if $action eq 'doit'; return 1; } # checkgroups processed or not (with no change or not) return 1 if $left =~ /^checkgroups by \S+/o; } ########### ## cnfsstat if ($prog eq "cnfsstat") { # Class ALT for groups matching "alt.*" article size min/max: 0/1048576 # Buffer T3, len: 1953 Mbytes, used: 483.75 Mbytes (24.8%) 0 cycles if ($left =~ m|^Class\ (\S+)\ for\ groups\ matching\ \S+ (\ article\ size\ min/max:\ \d+/\d+)? \ Buffer\ (\S+), \ len:\ ([\d.]+)\s+Mbytes, \ used:\ ([\d.]+)\ Mbytes\ \(\s*[\d.]+%\) \s+(\d+)\ cycles\s* $|ox) { my ($class, $buffer, $size, $used, $cycles) = ($1, $3, $4, $5, $6); my ($h, $m, $s) = $hour =~ m/^(\d+):(\d+):(\d+)$/; my $time = $h * 3600 + $m * 60 + $s; $size *= 1024 * 1024; $used *= 1024 * 1024; $cnfsstat{$buffer} = $class; # If the size changed, invalidate all of our running fill rate stats. if (!exists($cnfsstat_size{$buffer}) || $size != $cnfsstat_size{$buffer}) { delete $cnfsstat_rate{$buffer}; delete $cnfsstat_samples{$buffer}; delete $cnfsstat_time{$buffer}; $cnfsstat_size{$buffer} = $size; } elsif ($cnfsstat_time{$buffer}) { # We want to gather the rate at which cycbuffs fill. Store a # running total of bytes/second and a total number of samples. # Ideally we'd want a weighted average of those samples by the # length of the sample period, but we'll ignore that and assume # cnfsstat runs at a roughly consistent interval. my ($period, $added); $period = $time - $cnfsstat_time{$buffer}; $period = 86400 - $cnfsstat_time{$buffer} + $time if $period <= 0; $added = $used - $cnfsstat_used{$buffer}; if ($cycles > $cnfsstat_cycles{$buffer}) { $added += $size * ($cycles - $cnfsstat_cycles{$buffer}); } if ($added > 0) { $cnfsstat_rate{$buffer} += $added / $period; $cnfsstat_samples{$buffer}++; } } $cnfsstat_used{$buffer} = $used; $cnfsstat_cycles{$buffer} = $cycles; $cnfsstat_time{$buffer} = $time; return 1; } } # Ignore following programs : return 1 if ($prog eq "uxfxn"); return 1 if ($prog eq "beverage"); return 1 if ($prog eq "newsx"); return 1 if ($prog eq "demmf"); return 1 if ($prog eq "nnnn"); return 1 if ($prog eq "slurp"); return 0; } ################################# # Adjust some values.. sub adjust($$) { my ($first_date, $last_date) = @_; my $nnrpd_doit = 0; my $curious; { my $serv; if (%nnrpd_connect) { my @keys = keys (%nnrpd_connect); my $c = @keys; foreach my $serv (@keys) { if ($nnrpd_no_permission{$serv}) { my $dom = &host2dom($serv); $nnrpd_dom_connect{$dom} -= $nnrpd_connect{$serv} if defined $nnrpd_dom_connect{$dom}; $nnrpd_dom_times{$dom} -= $nnrpd_times{$serv} if defined $nnrpd_dom_times{$dom}; # The message "bad_auth" can occur more than once per session. # Subtracting nnrpd_no_permission from nnrpd_connect is # broken and can yield negative values for nnrpd_connect. $nnrpd_connect{$serv} -= $nnrpd_no_permission{$serv}; # Perl considers negative values to be true. Previously the # hash entry was deleted only if the value was exactly 0. delete $nnrpd_connect{$serv} unless $nnrpd_connect{$serv} > 0; delete $nnrpd_groups{$serv} unless $nnrpd_groups{$serv}; delete $nnrpd_times{$serv} unless $nnrpd_times{$serv}; delete $nnrpd_usr_times{$serv} unless $nnrpd_usr_times{$serv}; delete $nnrpd_sys_times{$serv} unless $nnrpd_sys_times{$serv}; delete $nnrpd_dom_connect{$dom} unless $nnrpd_dom_connect{$dom} > 0; delete $nnrpd_dom_groups{$dom} unless $nnrpd_dom_groups{$dom}; delete $nnrpd_dom_times{$dom} unless $nnrpd_dom_times{$dom}; $c--; } $nnrpd_doit++ if $nnrpd_groups{$serv} || $nnrpd_post_ok{$serv}; } undef %nnrpd_connect unless $c; } foreach my $serv (keys (%nnrpd_groups)) { $curious = "ok" unless $nnrpd_groups{$serv} || $nnrpd_post_ok{$serv} || $nnrpd_articles{$serv}; } } # Fill some hashes { my ($key, $hostname, $channel); # Since the checkpoint counts include entries for all server # connections, check to see if any checkpoint server entries are not also # in %innd_connect. Add any missing servers (persistant servers with no # connected log lines) to %innd_connect so that incoming totals will be # properly computed. foreach $server (keys (%innd_accepted)) { if (! defined($innd_connect{$server})) { $innd_connect{$server} = 0; } } foreach $key (keys (%innd_connect)) { $innd_offered{$key} = ($innd_accepted{$key} || 0) + ($innd_refused{$key} || 0) + ($innd_rejected{$key} || 0); $innd_offered_size{$key} = ($innd_stored_size{$key} || 0) + ($innd_duplicated_size{$key} || 0) + ($innd_rejected_size{$key} || 0); } # Sum all incoming traffic for each full server. foreach $key (keys (%innd_connect)) { if ($key =~ /^(\S+):\d+$/) { $innd_seconds_sum{$1} += ($innd_seconds{$key} || 0); $innd_connect_sum{$1} += ($innd_connect{$key} || 0); $innd_offered_sum{$1} += ($innd_offered{$key} || 0); $innd_accepted_sum{$1} += ($innd_accepted{$key} || 0); $innd_refused_sum{$1} += ($innd_refused{$key} || 0); $innd_rejected_sum{$1} += ($innd_rejected{$key} || 0); $innd_stored_size_sum{$1} += ($innd_stored_size{$key} || 0); $innd_duplicated_size_sum{$1} += ($innd_duplicated_size{$key} || 0); $innd_offered_size_sum{$1} += ($innd_offered_size{$key} || 0); $innd_rejected_size_sum{$1} += ($innd_rejected_size{$key} || 0); } } # adjust min/max of innd timer stats. if (%innd_time_min) { foreach $key (keys (%innd_time_min)) { $innd_time_min{$key} = 0 if ($innd_time_min{$key} == $MIN); $innd_time_max{$key} = 0 if ($innd_time_max{$key} == $MAX); #$innd_time_min{$key} /= 1000; #$innd_time_max{$key} /= 1000; } } if (%innfeed_time_min) { foreach $key (keys (%innfeed_time_min)) { $innfeed_time_min{$key} = 0 if ($innfeed_time_min{$key} == $MIN); $innfeed_time_max{$key} = 0 if ($innfeed_time_max{$key} == $MAX); } } if (%nnrpd_time_min) { foreach $key (keys (%nnrpd_time_min)) { $nnrpd_time_min{$key} = 0 if ($nnrpd_time_min{$key} == $MIN); $nnrpd_time_max{$key} = 0 if ($nnrpd_time_max{$key} == $MAX); } } # remove the innd timer stats if not used. unless ($innd_time_times) { undef %innd_time_min; undef %innd_time_max; undef %innd_time_num; undef %innd_time_time; } # same thing for innfeed timer unless ($innfeed_time_times) { undef %innfeed_time_min; undef %innfeed_time_max; undef %innfeed_time_num; undef %innfeed_time_time; } # same thing for nnrpd timer unless ($nnrpd_time_times) { undef %nnrpd_time_min; undef %nnrpd_time_max; undef %nnrpd_time_num; undef %nnrpd_time_time; } } if (%inn_flow) { my ($prev_dd, $prev_d, $prev_h) = ("", -1, -1); my $day; foreach $day (sort datecmp keys (%inn_flow)) { my ($r, $h) = $day =~ /^(.*) (\d+)$/; my $d = index ("JanFebMarAprMayJunJulAugSepOctNovDec", substr ($r,0,3)) / 3 * 31 + substr ($r, 4, 2); $prev_h = $h if ($prev_h == -1); if ($prev_d == -1) { $prev_d = $d; $prev_dd = $r; } if ($r eq $prev_dd) { # Same day and same month ? if ($h != $prev_h) { if ($h == $prev_h + 1) { $prev_h++; } else { my $j; for ($j = $prev_h + 1; $j < $h; $j++) { my $t = sprintf "%02d", $j; $inn_flow{"$r $t"} = 0; } $prev_h = $h; } } } else { my $j; # then end of the first day... for ($j = ($prev_h == 23) ? 24 : $prev_h + 1; $j < 24; $j++) { my $t = sprintf "%02d", $j; $inn_flow{"$prev_dd $t"} = 0; } # all the days between (if any) # well, we can forget them as it is supposed to be a tool # launched daily. # the beginning of the last day.. for ($j = 0; $j < $h; $j++) { my $t = sprintf "%02d", $j; $inn_flow{"$r $t"} = 0; } $prev_dd = $r; $prev_d = $d; $prev_h = $h; } } my $first = 1; my (%hash, %hash_time, %hash_size, $date, $delay); foreach $day (sort datecmp keys (%inn_flow)) { my ($r, $h) = $day =~ /^(.*) (\d+)$/o; if ($first) { $first = 0; my ($t) = $first_date =~ m/:(\d\d:\d\d)$/o; $date = "$day:$t - $h:59:59"; $t =~ m/(\d\d):(\d\d)/o; $delay = 3600 - $1 * 60 - $2; } else { $date = "$day:00:00 - $h:59:59"; $delay = 3600; } $hash{$date} = $inn_flow{$day}; $hash_size{$date} = $inn_flow_size{$day}; $inn_flow_labels{$date} = $h; $hash_time{$date} = $delay; } my ($h, $t) = $last_date =~ m/ (\d+):(\d\d:\d\d)$/o; my ($h2) = $date =~ m/ (\d+):\d\d:\d\d /o; my $date2 = $date; $date2 =~ s/$h2:59:59$/$h:$t/; $hash{$date2} = $hash{$date}; delete $hash{"$date"}; $hash_size{$date2} = $hash_size{$date}; delete $hash_size{"$date"}; $t =~ m/(\d\d):(\d\d)/o; $hash_time{$date2} = $hash_time{$date} - ($h2 == $h) * 3600 + $1 * 60 + $2; delete $hash_time{"$date"}; $inn_flow_labels{$date2} = $h; %inn_flow = %hash; %inn_flow_time = %hash_time; %inn_flow_size = %hash_size; } if (%innd_bad_ihave) { my $key; my $msg = 'Bad ihave control messages received'; foreach $key (keys %innd_bad_ihave) { $innd_misc_stat{$msg}{$key} = $innd_bad_ihave{$key}; } } if (%innd_bad_msgid) { my $key; my $msg = 'Bad Message-ID\'s offered'; foreach $key (keys %innd_bad_msgid) { $innd_misc_stat{$msg}{$key} = $innd_bad_msgid{$key}; } } if (%innd_bad_sendme) { my $key; my $msg = 'Ignored sendme control messages received'; foreach $key (keys %innd_bad_sendme) { $innd_misc_stat{$msg}{$key} = $innd_bad_sendme{$key}; } } if (%innd_bad_command) { my $key; my $msg = 'Bad command received'; foreach $key (keys %innd_bad_command) { $innd_misc_stat{$msg}{$key} = $innd_bad_command{$key}; } } if (%innd_bad_newsgroup) { my $key; my $msg = 'Bad newsgroups received'; foreach $key (keys %innd_bad_newsgroup) { $innd_misc_stat{$msg}{$key} = $innd_bad_newsgroup{$key}; } } if (%innd_posted_future) { my $key; my $msg = 'Article posted in the future'; foreach $key (keys %innd_posted_future) { $innd_misc_stat{$msg}{$key} = $innd_posted_future{$key}; } } if (%innd_no_colon_space) { my $key; my $msg = 'No colon-space in header'; foreach $key (keys %innd_no_colon_space) { $innd_misc_stat{$msg}{$key} = $innd_no_colon_space{$key}; } } if (%innd_huge) { my $key; my $msg = 'Huge articles'; foreach $key (keys %innd_huge) { $innd_misc_stat{$msg}{$key} = $innd_huge{$key}; } } if (%innd_blocked) { my $key; my $msg = 'Blocked server feeds'; foreach $key (keys %innd_blocked) { $innd_misc_stat{$msg}{$key} = $innd_blocked{$key}; } } if (%innd_strange_strings) { my $key; my $msg = 'Including strange strings'; foreach $key (keys %innd_strange_strings) { $innd_misc_stat{$msg}{$key} = $innd_strange_strings{$key}; } } if (%rnews_bogus_ng) { my $key; my $msg = 'Unwanted newsgroups'; foreach $key (keys %rnews_bogus_ng) { $rnews_misc{$msg}{$key} = $rnews_bogus_ng{$key}; } } if (%rnews_bogus_dist) { my $key; my $msg = 'Unwanted distributions'; foreach $key (keys %rnews_bogus_dist) { $rnews_misc{$msg}{$key} = $rnews_bogus_dist{$key}; } } if (%rnews_unapproved) { my $key; my $msg = 'Articles unapproved'; foreach $key (keys %rnews_unapproved) { $rnews_misc{$msg}{$key} = $rnews_unapproved{$key}; } } if (%rnews_bogus_date) { my $key; my $msg = 'Bad Date'; foreach $key (keys %rnews_bogus_date) { $rnews_misc{$msg}{$key} = $rnews_bogus_date{$key}; } } $rnews_misc{'Too old'}{'--'} = $rnews_too_old if $rnews_too_old; $rnews_misc{'Bad linecount'}{'--'} = $rnews_linecount if $rnews_linecount; $rnews_misc{'Duplicate articles'}{'--'} = $rnews_duplicate if $rnews_duplicate; $rnews_misc{'No colon-space'}{'--'} = $rnews_no_colon_space if $rnews_no_colon_space; if (%nnrpd_groups) { foreach my $key (keys (%nnrpd_connect)) { unless ($nnrpd_groups{$key} || $nnrpd_post_ok{$key} || $nnrpd_post_rej{$key} || $nnrpd_post_error{$key} || $nnrpd_articles{$key}) { $nnrpd_curious{$key} = $nnrpd_connect{$key}; delete $nnrpd_connect{$key}; } } } } sub report_unwanted_ng($) { my $file = shift; open (FILE, "$file") && do { while () { my ($c, $n) = $_ =~ m/^\s*(\d+)\s+(.*)$/; next unless defined $n; $n =~ s/^newsgroup //o; # for pre 1.8 logs $inn_uw_ng{$n} += $c; } close (FILE); }; unlink ("${file}.old"); rename ($file, "${file}.old"); open (FILE, "> $file") && do { my $g; foreach $g (sort {$inn_uw_ng{$b} <=> $inn_uw_ng{$a}} (keys (%inn_uw_ng))) { printf FILE "%d %s\n", $inn_uw_ng{$g}, $g; } close (FILE); chmod(0660, "$file"); }; unlink ("${file}.old"); } ########################################################################### # Compare 2 dates (+hour), used with sort (arguments $a and $b) sub datecmp() { # ex: "May 12 06" for May 12, 6:00am # The 2 dates are near. The range is less than a few days that's why we # can cheat to determine the order. It is only important if one date # is in January and the other in December. my($date1) = substr($a, 4, 2) * 24; my($date2) = substr($b, 4, 2) * 24; $date1 += index("JanFebMarAprMayJunJulAugSepOctNovDec",substr($a,0,3)) * 288; $date2 += index("JanFebMarAprMayJunJulAugSepOctNovDec",substr($b,0,3)) * 288; if ($date1 - $date2 > 300 * 24) { $date2 += 288 * 3 * 12; } elsif ($date2 - $date1 > 300 * 24) { $date1 += 288 * 3 * 12; } $date1 += substr($a, 7, 2); $date2 += substr($b, 7, 2); $date1 - $date2; } sub host2dom($) { my $host = shift; $host =~ m/^[^\.]+(.*)/; $host =~ m/^[\d\.]+$/ ? "unresolved" : $1 ? "*$1" : "?"; } 1; inn-2.6.0/scripts/Makefile0000644000175200017520000000377712575023702015065 0ustar iuliusiulius## $Id: Makefile 7688 2007-09-20 21:53:55Z iulius $ ## ## Files that can be handled by fixscript (and need to be so handled) need ## a rule to build them from the .in version, and then all files need an ## installation rule. Do the installation rules individually so as to ## avoid needless work if the files haven't changed. We also need lists ## of files to build and files to install for the all and install rules. include ../Makefile.global top = .. ALL = inncheck innmail innreport innstat innupgrade innwatch \ news.daily rc.news scanlogs simpleftp tally.control writelog EXTRA = innshellvars innshellvars.pl innshellvars.tcl all: $(ALL) $(EXTRA) install: all for F in innmail innreport simpleftp ; do \ $(CP_XPUB) $$F $D$(PATHBIN)/$$F ; \ done for F in inncheck innstat innupgrade innwatch news.daily rc.news \ scanlogs tally.control writelog ; do \ $(CP_XPRI) $$F $D$(PATHBIN)/$$F ; \ done for F in innreport_inn.pm innshellvars innshellvars.pl \ innshellvars.tcl ; do \ $(CP_RPUB) $$F $D$(PATHLIB)/$$F ; \ done bootstrap: clean: rm -f $(ALL) clobber distclean maintclean: clean rm -f $(EXTRA) depend: profiled: all $(EXTRA) $(FIXSCRIPT): @echo Run configure before running make. See INSTALL for details. @exit 1 ## Build rules. FIX = $(FIXSCRIPT) inncheck: inncheck.in $(FIX) ; $(FIX) inncheck.in innmail: innmail.in $(FIX) ; $(FIX) innmail.in innreport: innreport.in $(FIX) ; $(FIX) innreport.in innstat: innstat.in $(FIX) ; $(FIX) innstat.in innupgrade: innupgrade.in $(FIX) ; $(FIX) -i innupgrade.in innwatch: innwatch.in $(FIX) ; $(FIX) innwatch.in news.daily: news.daily.in $(FIX) ; $(FIX) news.daily.in rc.news: rc.news.in $(FIX) ; $(FIX) rc.news.in scanlogs: scanlogs.in $(FIX) ; $(FIX) scanlogs.in simpleftp: simpleftp.in $(FIX) ; $(FIX) -i simpleftp.in tally.control: tally.control.in $(FIX) ; $(FIX) tally.control.in writelog: writelog.in $(FIX) ; $(FIX) writelog.in inn-2.6.0/scripts/innshellvars.tcl.in0000644000175200017520000001077712575023702017244 0ustar iuliusiulius# -*- tcl -*- # # Author: James Brister -- berkeley-unix -- # Start Date: Sat, 24 Aug 1996 23:45:34 +0200 # Project: INN # File: innshellvars.tcl # RCSId: $Id: innshellvars.tcl.in 9145 2010-10-30 09:44:46Z iulius $ # Description: Set up any and all variables that an INN tcl script # might need. Also sets umask. set env(LC_CTYPE) "C" set prefix "@prefix@" set exec_prefix "@exec_prefix@" eval [exec @bindir@/innconfval -t] set inn_newshome "$inn_pathnews" set inn_spooldir "$inn_pathspool" set inn_most_logs "$inn_pathlog" set env(NEWSHOME) "$inn_newshome" set env(SPOOLDIR) "$inn_spooldir" set env(MOST_LOGS) "$inn_most_logs" set inn_newsbin "${inn_pathbin}" set inn_newsetc "${inn_pathetc}" set inn_newslib "@libdir@" set inn_innddir "${inn_pathrun}" set inn_locks "${inn_pathrun}" set env(NEWSBIN) "$inn_newsbin" set env(NEWSETC) "$inn_newsetc" set env(NEWSLIB) "$inn_newslib" set env(INNDDIR) "$inn_innddir" set env(LOCKS) "$inn_locks" set inn_errlog "${inn_most_logs}/errlog" set inn_log "${inn_most_logs}/news" set inn_archivedir "${inn_patharchive}" set inn_spool "${inn_patharticles}" set inn_batch "${inn_pathoutgoing}" set inn_incoming "${inn_pathincoming}" set inn_overviewdir "${inn_pathoverview}" set inn_spoolnews "${inn_pathincoming}" set inn_badnews "${inn_pathincoming}/bad" set inn_active "${inn_pathdb}/active" set inn_activetimes "${inn_pathdb}/active.times" set inn_ctlfile "${inn_newsetc}/control.ctl" set inn_ctlwatch "${inn_newsetc}/innwatch.ctl" set inn_history "${inn_pathdb}/history" set inn_newactive "${inn_pathdb}/active.tmp" set inn_newsfeeds "${inn_newsetc}/newsfeeds" set inn_newsgroups "${inn_pathdb}/newsgroups" set inn_oldactive "${inn_pathdb}/active.old" set inn_path_motd_innd "${inn_newsetc}/motd.innd" set inn_path_motd_nnrpd "${inn_newsetc}/motd.nnrpd" set inn_path_motd "${inn_path_motd_nnrpd}" set inn_expirectl "${inn_newsetc}/expire.ctl" set inn_localgroups "${inn_newsetc}/localgroups" set inn_controlprogs "${inn_pathcontrol}" set inn_innconfval "${inn_newsbin}/innconfval" set inn_innd "${inn_newsbin}/innd" set inn_innwatch "${inn_newsbin}/innwatch" set inn_inews "${inn_newsbin}/inews" set inn_rnews "${inn_newsbin}/rnews" set inn_perl_startup_innd "${inn_pathfilter}/startup_innd.pl" set inn_perl_filter_innd "${inn_pathfilter}/filter_innd.pl" set inn_perl_filter_nnrpd "${inn_pathfilter}/filter_nnrpd.pl" set inn_python_filter_innd "${inn_pathfilter}/filter_innd.py" set inn_path_python_inn_module "${inn_pathfilter}/INN.py" set inn_path_tcl_startup "${inn_pathfilter}/startup.tcl" set inn_path_tcl_filter "${inn_pathfilter}/filter.tcl" set inn_daily "${inn_locks}/locks/LOCK.news.daily" set inn_newscontrol "${inn_innddir}/control" set inn_nntpconnect "${inn_innddir}/nntpin" set inn_serverpid "${inn_innddir}/innd.pid" set inn_innwstatus "${inn_innddir}/innwatch.status" set inn_watchpid "${inn_innddir}/innwatch.pid" set inn_awk "@AWK@" set inn_sed "@SED@" set inn_inndf "${inn_newsbin}/inndf" set inn_egrep "@EGREP@" set inn_perl "@PERL@" set inn_gpgv "@GPGV@" set inn_pgp "@PGP@" set inn_sort "@SORT@" set inn_getftp "@PATH_GETFTP@" set inn_uustat "@UUSTAT@" set inn_uux "@UUX@" set inn_bzip2 "@BZIP2@" set inn_compress "@COMPRESS@" set inn_gzip "@GZIP@" set inn_uncompress "@UNCOMPRESS@" set inn_log_compress "@LOG_COMPRESS@" set inn_z "@LOG_COMPRESSEXT@" if { $inn_ovmethod == "ovdb" } { set inn_db_home "${inn_pathoverview}" set env(DB_HOME) "${inn_db_home}" } set inn_tempsock [ eval exec basename ${inn_innddir}/ctlinndXXXXXX | $inn_sed -e {s/XXXXXX$/*/} ] set inn_tempsockdir [ exec echo ${inn_innddir}/ctlinndXXXXXX | $inn_sed -e {s@/[^/]*$@@} ] set inn_newsmaster "@NEWSMASTER@" set inn_newsuser "${inn_runasuser}" set inn_newsgroup "${inn_runasgroup}" scan "@FILEMODE@" "%o" inn_filemode scan "@INEWSMODE@" "%o" inn_inewsmode scan "@RNEWSMODE@" "%o" inn_rnewsmode set do_dbz_tagged_hash "@DO_DBZ_TAGGED_HASH@" set inn_tmpdir "$inn_pathtmp" set env(TMPDIR) "$inn_pathtmp" set inn_spooltemp "$inn_pathtmp" set inn_newslbin "${inn_newshome}/local" set env(NEWSLBIN) "$inn_newslbin" scan "@NEWSUMASK@" "%o" inn_umask catch {umask @NEWSUMASK@} set inn_syslog_facility [ exec echo [string tolower "@SYSLOG_FACILITY@"] | $inn_sed -e {s/log_//} ] set inn_path "$inn_newslbin:$inn_newsbin:$env(PATH):/bin:/usr/bin" set env(PATH) "$inn_path" set inn_home "$inn_pathnews" ## Load another script that can override or add variables. if {[file executable "${inn_pathetc}/innshellvars.tcl.local"] == 1} { source "${inn_pathetc}/innshellvars.tcl.local" } inn-2.6.0/scripts/innmail.in0000644000175200017520000000350312575023702015367 0ustar iuliusiulius#! /usr/bin/perl -w # fixscript will replace this line with code to load INN::Config # Author: James Brister -- berkeley-unix -- # Start Date: Fri, 25 Apr 1997 14:11:23 +0200 # Project: INN # File: innmail.pl # RCSId: $Id: innmail.in 9216 2011-07-05 18:30:57Z iulius $ # Description: A simple replacement for UCB Mail to avoid nasty security # problems. # use strict; use Getopt::Std; $0 =~ s!.*/!!; die "$0: No \$INN::Config::mta variable defined.\n" if ! defined ($INN::Config::mta); my $sm = $INN::Config::mta; die "$0: MTA path is not absolute\n" unless ($sm =~ m!^/!); my $usage = "usage: $0 -s subject addresses\n\n" . "Reads stdin for message body\n"; my (%opt, @addrs); getopts ("s:h", \%opt) || die $usage; die $usage if $opt{'h'}; if ( !defined($opt{'s'}) ) { warn "No subject given. Hope that's OK.\n"; $opt{'s'} = "NO SUBJECT"; } else { $opt{'s'} =~ s/\n+\Z//; } # Fix up any addresses. foreach ( @ARGV ) { s![^-a-zA-Z0-9+_.@%]!!g; push (@addrs, $_) if ($_ ne ""); } die "$0: No addresses specified\n\n$usage" unless @addrs; if ($sm =~ m!%s!) { $sm = sprintf $sm, join (' ', @addrs); } else { $sm .= " " . join(' ', @addrs); } my @smarr = split(/\s+/, $sm); (my $t = $INN::Config::mta) =~ s!\s.*!!; die "$0: MTA variable definition is changed after substitution\n" if ($t ne $smarr[0]); die "$0: MTA excutable doesn't appear to exist: $smarr[0]\n" if ! -x $smarr[0]; # Startup MTA without using the shell. my $pid = open my $MTA, '|-'; if ($pid == 0) { exec (@smarr) || die "$0: exec of $sm failed: $!\n"; } elsif ($pid < 0) { die "$0: Fork failed: $!\n"; } print $MTA "To: ", join (",\n\t", @addrs), "\n"; print $MTA "Subject: $opt{'s'}\n"; print $MTA "\n"; while () { print $MTA $_; } close $MTA; exit; inn-2.6.0/scripts/innshellvars.in0000644000175200017520000000620612575023702016453 0ustar iuliusiulius#! /bin/sh ## $Id: innshellvars.in 9567 2013-11-17 20:24:35Z iulius $ ## Set up any and all shell variables that an INN shell script ## might need. Also sets umask. ## NOTE: When adding stuff here, add the corresponding variables to ## innshellvars.tcl and the INN::Config Perl module (as well as the ## old innshellvars.pl script). LC_CTYPE=C; export LC_CTYPE prefix=@prefix@ exec_prefix=@exec_prefix@ eval `@bindir@/innconfval -s` NEWSHOME=${PATHNEWS} SPOOLDIR=${PATHSPOOL} MOST_LOGS=${PATHLOG} export NEWSHOME SPOOLDIR MOST_LOGS NEWSBIN=${PATHBIN} NEWSETC=${PATHETC} NEWSLIB=@libdir@ INNDDIR=${PATHRUN} LOCKS=${PATHRUN} export NEWSBIN NEWSETC NEWSLIB INNDDIR LOCKS ERRLOG=${MOST_LOGS}/errlog LOG=${MOST_LOGS}/news ARCHIVEDIR=${PATHARCHIVE} SPOOL=${PATHARTICLES} BATCH=${PATHOUTGOING} INCOMING=${PATHINCOMING} OVERVIEWDIR=${PATHOVERVIEW} SPOOLNEWS=${PATHINCOMING} BADNEWS=${PATHINCOMING}/bad ACTIVE=${PATHDB}/active ACTIVETIMES=${PATHDB}/active.times CTLFILE=${NEWSETC}/control.ctl CTLWATCH=${NEWSETC}/innwatch.ctl HISTORY=${PATHDB}/history NEWACTIVE=${PATHDB}/active.tmp NEWSFEEDS=${NEWSETC}/newsfeeds NEWSGROUPS=${PATHDB}/newsgroups OLDACTIVE=${PATHDB}/active.old PATH_MOTD_INND=${NEWSETC}/motd.innd PATH_MOTD_NNRPD=${NEWSETC}/motd.nnrpd PATH_MOTD=${PATH_MOTD_NNRPD} EXPIRECTL=${NEWSETC}/expire.ctl LOCALGROUPS=${NEWSETC}/localgroups CONTROLPROGS=${PATHCONTROL} INNCONFVAL=${NEWSBIN}/innconfval INND=${NEWSBIN}/innd INNWATCH=${NEWSBIN}/innwatch INEWS=${NEWSBIN}/inews RNEWS=${NEWSBIN}/rnews PERL_STARTUP_INND=${PATHFILTER}/startup_innd.pl PERL_FILTER_INND=${PATHFILTER}/filter_innd.pl PERL_FILTER_NNRPD=${PATHFILTER}/filter_nnrpd.pl PYTHON_FILTER_INND=${PATHFILTER}/filter_innd.py PATH_PYTHON_INN_MODULE=${PATHFILTER}/INN.py PATH_TCL_STARTUP=${PATHFILTER}/startup.tcl PATH_TCL_FILTER=${PATHFILTER}/filter.tcl DAILY=${LOCKS}/LOCK.news.daily NEWSCONTROL=${INNDDIR}/control NNTPCONNECT=${INNDDIR}/nntpin SERVERPID=${INNDDIR}/innd.pid INNWSTATUS=${INNDDIR}/innwatch.status WATCHPID=${INNDDIR}/innwatch.pid AWK='@AWK@' SED='@SED@' INNDF=${NEWSBIN}/inndf EGREP='@EGREP@' PERL='@PERL@' GPGV='@GPGV@' PGP='@PGP@' SORT='@SORT@' GETFTP="@PATH_GETFTP@" UUSTAT='@UUSTAT@' UUX='@UUX@' BZIP2='@BZIP2@' COMPRESS='@COMPRESS@' GZIP='@GZIP@' UNCOMPRESS='@UNCOMPRESS@' LOG_COMPRESS='@LOG_COMPRESS@' Z='@LOG_COMPRESSEXT@' if [ "$OVMETHOD" = "ovdb" ]; then DB_HOME="${PATHOVERVIEW}" export DB_HOME fi TEMPSOCK=`basename ${INNDDIR}/ctlinndXXXXXX | ${SED} -e 's/XXXXXX$/*/'` TEMPSOCKDIR=`echo ${INNDDIR}/ctlinndXXXXXX | ${SED} -e 's@/[^/]*$@@'` NEWSMASTER=@NEWSMASTER@ NEWSUSER=${RUNASUSER} NEWSGROUP=${RUNASGROUP} FILEMODE=@FILEMODE@ INEWSMODE=@INEWSMODE@ RNEWSMODE=@RNEWSMODE@ DO_DBZ_TAGGED_HASH=@DO_DBZ_TAGGED_HASH@ TMPDIR=${PATHTMP}; export TMPDIR SPOOLTEMP=${PATHTMP} NEWSLBIN=${NEWSHOME}/local export NEWSLBIN UMASK=@NEWSUMASK@ umask @NEWSUMASK@ SYSLOG_FACILITY=`echo "@SYSLOG_FACILITY@" | tr "[:upper:]" "[:lower:]" | ${SED} -e 's/log_//'` PATH=${NEWSLBIN}:${NEWSBIN}:${PATH}:/bin:/usr/bin export PATH ## Do not override HOME. ## Load another script that can override or add variables. if [ -x ${PATHETC}/innshellvars.local ]; then . ${PATHETC}/innshellvars.local fi inn-2.6.0/scripts/tally.control.in0000644000175200017520000000270012575023702016542 0ustar iuliusiulius#! /bin/sh # fixscript will replace this line with code to load innshellvars ## $Id: tally.control.in 7658 2007-08-26 08:20:53Z iulius $ ## ## Tally and update the newgroup/rmgroup control logs. ## ## Merge in a log that contains newgroup/rmgroup control messages so that ## the "control.log" file is updated to contain the new counts of how ## often each group has been newgroup'd or rmgroup'd. This is run by ## scanlogs, which prepares this from the control-message handlers if ## control.ctl specifies logging. CONTROL=${MOST_LOGS}/control.log CONTROL_NEW=${CONTROL}.new CONTROL_OLD=${CONTROL}.old PROGNAME=`basename $0` LOCK=${LOCKS}/LOCK.${PROGNAME} ## Lock. trap 'rm -f ${LOCK} ; exit 1' 1 2 3 15 shlock -f ${LOCK} -p $$ || { echo "$0: cannot lock ${LOCK}" 1>&2 exit 1 } ## Prepare the files. if [ ! -f ${CONTROL} ]; then touch ${CONTROL} chmod 0660 ${CONTROL} fi rm -f ${CONTROL_NEW} ${CONTROL_OLD} ln ${CONTROL} ${CONTROL_OLD} touch ${CONTROL_NEW} chmod 0660 ${CONTROL_NEW} ## Grab the data. ${SED} -n -e 's/[ ][ ]*/ /g' -e 's/^ \(Control:.*\)$/1 \1/p' \ | cat - ${CONTROL} \ | ${SED} -e 's/ /#/g' -e 's/\([0-9][0-9]*\)#\(.*\)/\1 \2/' \ | ${AWK} 'BEGIN { ctl[0]=0; } { ctl[$2] += $1; } END { for (line in ctl) { if (line != 0) { print ctl[line], line; } } }' \ | tr '#' ' ' \ | sort -n -r >${CONTROL_NEW} mv -f ${CONTROL_NEW} ${CONTROL} ## All done. rm -f ${LOCK} exit 0 inn-2.6.0/scripts/innshellvars.pl.in0000644000175200017520000000757612575023702017100 0ustar iuliusiulius# # This package is only kept for compatibility reasons with old softwares. # Please use the real INN::Config Perl module instead. # # Author: James Brister -- berkeley-unix -- # Start Date: Sat, 24 Aug 1996 22:08:19 +0200 # Project: INN # File: innshellvars.pl # RCSId: $Id: innshellvars.pl.in 9567 2013-11-17 20:24:35Z iulius $ # Description: Set up any and all variables that an INN perl script # might need. Also sets umask. # package inn ; $ENV{'LC_CTYPE'} = "C"; $prefix = '@prefix@'; $exec_prefix = "@exec_prefix@"; eval `@bindir@/innconfval -p`; $newshome = $pathnews; $spooldir = $pathspool; $most_logs = $pathlog; $ENV{'NEWSHOME'} = $pathnews; $ENV{'SPOOLDIR'} = $spooldir; $ENV{'MOST_LOGS'} = $most_logs; $newsbin = $pathbin; $newsetc = $pathetc; $newslib = "@libdir@"; $innddir = $pathrun; $locks = $pathrun; $ENV{'NEWSBIN'} = $newsbin; $ENV{'NEWSETC'} = $newsetc; $ENV{'NEWSLIB'} = $newslib; $ENV{'INNDDIR'} = $innddir; $ENV{'LOCKS'} = $locks; $errlog = "${most_logs}/errlog"; $log = "${most_logs}/news"; $archivedir = $patharchive; $spool = $patharticles; $batch = $pathoutgoing; $incoming = $pathincoming; $overviewdir = $pathoverview; $spoolnews = $pathincoming; $badnews = "$pathincoming/bad"; $active = "${pathdb}/active" ; $activetimes = "${pathdb}/active.times" ; $ctlfile = "${newsetc}/control.ctl" ; $ctlwatch = "${newsetc}/innwatch.ctl" ; $history = "${pathdb}/history" ; $newactive = "${pathdb}/active.tmp" ; $newsfeeds = "${newsetc}/newsfeeds" ; $newsgroups = "${pathdb}/newsgroups" ; $oldactive = "${pathdb}/active.old" ; $path_motd_innd = "${newsetc}/motd.innd" ; $path_motd_nnrpd = "${newsetc}/motd.nnrpd" ; $path_motd = "${path_motd_nnrpd}" ; $expirectl = "${newsetc}/expire.ctl" ; $localgroups = "$newsetc/localgroups" ; $controlprogs = $pathcontrol; $innconfval = "${newsbin}/innconfval" ; $innd = "${newsbin}/innd" ; $innwatch = "${newsbin}/innwatch" ; $inews = "${newsbin}/inews" ; $rnews = "${newsbin}/rnews" ; $perl_startup_innd = "$pathfilter/startup_innd.pl" ; $perl_filter_innd = "$pathfilter/filter_innd.pl" ; $perl_filter_nnrpd = "$pathfilter/filter_nnrpd.pl" ; $python_filter_innd = "$pathfilter/filter_innd.py" ; $path_python_inn_module = "$pathfilter/INN.py" ; $path_tcl_startup = "$pathfilter/startup.tcl" ; $path_tcl_filter = "$pathfilter/filter.tcl" ; $daily = "${locks}/LOCK.news.daily" ; $newscontrol = "${innddir}/control" ; $nntpconnect = "${innddir}/nntpin" ; $serverpid = "${innddir}/innd.pid" ; $innwstatus = "${innddir}/innwatch.status" ; $watchpid = "${innddir}/innwatch.pid" ; $awk = '@AWK@' ; $sed = '@SED@' ; $inndf = "${newsbin}/inndf" ; $egrep = '@EGREP@' ; $perl = '@PERL@' ; $gpgv = '@GPGV@' ; $pgp = '@PGP@' ; $sort = '@SORT@' ; $getftp = "@PATH_GETFTP@" ; $uustat = '@UUSTAT@' ; $uux = '@UUX@' ; $bzip2 = '@BZIP2@' ; $compress = '@COMPRESS@' ; $gzip = '@GZIP@' ; $uncompress = '@UNCOMPRESS@' ; $log_compress = '@LOG_COMPRESS@' ; $z = '@LOG_COMPRESSEXT@' ; if ($ovmethod && $ovmethod eq "ovdb") { $ENV{'DB_HOME'} = $pathoverview; } ($tempsock = "${innddir}/ctlinndXXXXXX") =~ s!.*/(.*)XXXXXX$!$1*! ; ($tempsockdir = "${innddir}/ctlinndXXXXXX") =~ s!/[^/]*$!! ; $newsmaster = '@NEWSMASTER@' ; $newsuser = ${runasuser} ; $newsgroup = ${runasgroup} ; $filemode = @FILEMODE@; $inewsmode = @INEWSMODE@; $rnewsmode = @RNEWSMODE@; $do_dbz_tagged_hash = '@DO_DBZ_TAGGED_HASH@'; $tmpdir = ${pathtmp}; $ENV{'TMPDIR'} = ${pathtmp}; $spooltemp = ${pathtmp}; $newslbin = "${newshome}/local"; $ENV{'NEWSLBIN'} = ${newslbin}; umask @NEWSUMASK@; $umask = @NEWSUMASK@; $syslog_facility = lc("@SYSLOG_FACILITY@"); $syslog_facility =~ s/log_//; $path = $ENV{'PATH'} || ''; $path = "${newslbin}:${newsbin}:${path}:/bin:/usr/bin"; $ENV{'PATH'} = $path; $home = ${pathnews}; ## Load another script that can override or add variables. if (-x "${pathetc}/innshellvars.pl.local") { do "${pathetc}/innshellvars.pl.local"; } 1 ; inn-2.6.0/autogen0000755000175200017520000000027312575023702013312 0ustar iuliusiulius#!/bin/sh # autogen -- Run the appropriate commands to bootstrap a Subversion checkout. # $Id: autogen 7286 2005-06-08 03:24:22Z eagle $ set -e autoconf autoheader rm -rf autom4te.cache inn-2.6.0/TODO0000644000175200017520000011447112575023702012420 0ustar iuliusiuliusThis is a rough and informal list of suggested improvements to INN, parts of INN that need work, and other tasks yet undone. Some of these may be in progress, in which case the person working on them will be noted in square brackets and should be contacted if you want to help. Otherwise, let know if you'd like to work on any item listed below. This list is currently being migrated to INN's issue tracking system . The list is divided into changes already tentatively scheduled for a particular release, higher priority changes that will hopefully be done in the near future, small or medium-scale projects for the future, and long-term, large-scale problems. Note that just because a particular feature is scheduled for a later release doesn't mean it can't be completed earlier if someone decides to take it on. The association of features with releases is intended to be a rough guide for prioritization and a set of milestones to use to judge when a new major release is justified. Also, one major thing that is *always* welcome is additions to the test suite, which is currently very minimal. Any work done on the test suite to allow more portions of INN to be automatically tested will make all changes easier and will be *greatly* appreciated. Last modified $Id: TODO 9825 2015-04-20 17:26:55Z iulius $. Scheduled for INN 2.6 * Rework and clean up the overview API. The major change is that the initialization function should return a pointer to an opaque struct which stores all of the state of the overview subsystem, rather than all of that being stored in static variables, and then all other functions should take that pointer. [The new API is available in CURRENT and conversion of portions of INN to use it is in progress. Once that conversion is done, the old API can be dropped and the overview backends converted to speak the new API at all levels.] * The TLS layer in nnrpd badly needs to be rewritten. Currently, it doesn't handle timeouts and the code could be cleaned up considerably. Support for GnuTLS would also be nice, as would client support. * Convert readers.conf and storage.conf (and related configuration files) to use the new parsing system and break out program-specific sections of inn.conf into their own groups. * The current WIP cache and history cache should be integrated into the history API, things like message ID hashing should become a selectable property of the history file, and the history API should support multiple backend storage formats and automatically select the right one for an existing history file based on stored metainformation. * The interface to embedded filters needs to be reworked. The information about which filters are enabled should be isolated in the filtering API, and there should be standard API calls for filtering message IDs, remote posts, and local posts. As part of this revision, all of the Perl callbacks should be defined before any of the user code is loaded, and the Perl loading code needs considerable cleanup. At the same time as this is done, the implementation should really be documented; we do some interesting things with embedded filters and it would be nice to have a general document describing how we do it. [Russ is planning on working on this at some point, but won't get upset if someone starts first.] * All of INN's documentation should be written in POD, with text and man pages generated from the POD source. Anyone is encouraged to work on this by just taking any existing documentation in man format and convert it to POD while checking that it's still accurate and adding any additional useful information that was missed. * Include the necessary glue so that Perl modules can be added to INN's build tree and installed with INN, allowing their capabilities to be available to the portions of INN written in Perl. * Switch nnrpd over to using the new wildmat routines rather than breaking apart strings on commas and matching each expression separately. This involves a lot of surgery, since PERMmatch is used all over the place, and may change the interpretation of ! and @ in group permission wildmats. * Rework and clean up the storage API. The major change is that the initialization function should return a pointer to an opaque struct which stores all of the state of the storage subsystem, rather than all of that being stored in static variables, and then all other functions should take that pointer. More of the structures should also be opaque, all-caps structure names should be avoided in favor of named structures, SMsetup and SMinit should be combined into one function that takes flags, SMerrno and SMerrorstr should be replaced with functions that return that information, and the wire format utilities should be moved into libinn. Scheduled for INN 2.7 * Add a generic, modular anti-spam and anti-abuse filter, off by default, but coming with INN and prominently mentioned in the INSTALL documentation. [Andrew Gierth has work in progress that may be usable for this.] * A unified configuration file combining the facilities of newsfeeds, incoming.conf, and innfeed.conf, but hopefully more readable and easier for new INN users to edit. This should have all of the capabilities of the existing configuration files, but specifying common things (such as file feeds or innfeed feeds) should be very simple and straightforward. This configuration file should use the new parsing infrastructure. * Convert all remaining INN configuration files to the new parsing infrastructure. * INN really should be capable of both sending and receiving a headers-only feed (or even an overview-only feed) similar to Diablo and using it for the same things that Diablo does, namely clustering, pull-on-demand for articles, and the like. This should be implementable as a new backend, although the API may need a few more hooks. Both a straight headers-only feed that only pulls articles down via NNTP from a remote server and a caching feed where some articles are pre-fed, some articles are pulled down at first read, and some articles are never stored locally should be possible. [A patch for a header-only feed from innfeed is included in ticket #17.] * The libinn, libstorage, and other library interfaces should be treated as stable libraries and properly versioned using libtool's recommendation for library versioning when changes are made so that they can be installed as shared libraries and work properly through releases of INN. This is currently waiting on a systematic review of the interface and removal of things that we don't want to support long-term. * The include files necessary to use libinn, libstorage, and other libraries should be installed in a suitable directory so that other programs can link against them. All such include files should be under include/inn and included with . All such include files should only depend on other inn/* header files and not on, e.g., config.h. All such include files should be careful about namespace to avoid conflicts with other include files used by applications. High Priority Projects * INN shouldn't flush all feeds (particularly all program feeds) on newgroup or rmgroup. Currently it reloads newsfeeds to reparse all of the wildmat patterns and rebuild the peer lists associated with the active file on group changes, and this forces a flush of all feeds. The best fix is probably to stash the wildmat pattern (and flags) for each peer when newsfeeds is read and then just using the stashed copy on newgroup or rmgroup, since otherwise the newsfeeds loading code would need significant modification. But in general, innd is too reload-happy; it should be better at making incremental changes without reloading everything. * Add authenticated Path support, based on USEPRO (RFC 5537). [Andrew Gierth wrote a patch for part of this a while back, which is included in ticket #19. Marco d'Itri expressed some interest in working on this.] * Various parts of INN are using write or writev; they should all use xwrite or xwritev instead. Even for writes that are unlikely to ever be partial, on some systems system calls aren't restartable and xwrite and xwritev properly handle EINTR returns. * Apparently on Solaris open can also be interrupted by a signal; we may need to have an xopen wrapper that checks for EINTR and retries. * tradspool has a few annoying problems. Deleted newsgroups never have their last articles expired, and there is no way of forcibly resynchronizing the articles stored on disk with what overview knows about unless tradindexed is used. Some sort of utility program to take care of these and to do things like analyze the tradspool.map file should be provided. * contrib/mkbuf and contrib/reset-cnfs.c should be combined into a utility for creating and clearing cycbuffs, perhaps combined with cnfsheadconf, and the whole thing moved into storage/cnfs rather than frontends (along with cnfsstat). pullart.c may also stand to be merged into the same utility (cnfs-util might not be a bad name). * The Berkeley DB integration of INN needs some improvements in robustness. Currently, BerkeleyDB functions can be called by nnrpd out of signal handlers and in other unfortunate situations, and coordination between nnrpd and innd isn't entirely robust. Berkeley DB 4.4 offers a new DB_REGISTER flag to open to allow for multi-process use of Berkeley DB databases and use of that flag should be investigated. Documentation Projects * Add man pages for all libinn interfaces. There should be a subdirectory of doc/pod for this since there will be a lot of them; installing them as libinn_
.3 seems to make the most sense (so, for example, error handling routines would be documented in libinn_error.3). * Better documentation of and support for UUCP feeds. send-uucp is now easier to use, but there's still a paucity of documentation covering the whole theory and mechanisms of UUCP feeding. * Everything installed by INN should have a man page. Currently, there are several binaries and configuration files that don't have man pages. (In some cases, the best thing to do with the configuration file may be to merge it into another one or find a way to eliminate it.) * Document the internal formats of the various overview methods, CNFS, timehash, and timecaf. A lot of this documentation already exists in various forms, but it needs to be cleaned up and collected in one place for each format, preferrably as a man page. * Add documentation for slave servers. [Russ has articles from inn-workers that can be used as a beginning.] * Write complete documentation for all of our extensions to RFC 3977 or RFC 5536 and 5537, preferrably in a format that could be suitable for future inclusion into new revisions of the RFCs. * More comprehensive documentation in texinfo would be interesting; it would allow for better organization, separation of specialized topics into cleaner chapters, and a significantly better printed manual. This would be a tremendous amount of work, though. Code Cleanup Projects * Eliminate everything in the LEGACY section of include/inn/options.h. * Go over include/inn/options.h and try to eliminate as many of the compile-time options there as possible. They should all be run-time options instead if at all possible, maybe in specific sub-sections of inn.conf. * Check to be sure we still need all of the #defines in include/inn/paths.h and look at adding anything needed by innfeed (and eliminating the separate innfeed header serving the same purpose). * Use vectors or cvectors everywhere that argify and friends are currently used and eliminate the separate implementation in nnrpd/misc.c. * Break up the remainder of inn/libinn.h into multiple inn/* include files for specific functions (such as memory management, wildmat, date handling, NNTP commands, etc.), with an inn/util.h header to collect the remaining random utilities. Consider adding some sort of prefix, like inn_, to all functions that aren't part of some other logical set with its own prefix. * Break the CNFS and tradspool code into multiple source files to make it easier to understand the logical divisions of the code and consider doing the same with the other overview and storage methods. * The source and bind addresses currently also take "any" or "all" wildcards, which can more easily be specified by just not setting them at all. Remove those special strings, modify innupgrade to fix inn.conf files using them, and simplify the code. (It's not completely clear that this is the right thing to do.) Needed Bug Fixes * Don't require an Xref slave carry all of the groups of its upstream. Fixing this will depend on the new overview API (so that overview records are stored separately in each group under innd's control) and will require ignoring failures to store overview records because the group doesn't exist, or checking first to ensure the group exists before trying to store the record. * tradspool currently uses stdio to write out tradspool.map, which can cause problems if more than 256 file descriptors are in use for other things (such as incoming connections or tradindexed overview cache). It should use write() instead. * LIST NEWSGROUPS should probably only list newsgroups that are marked in the active file as valid groups. * INN's startup script should be sure to clean out old lock files and PID files for innfeed. Be careful, though, since innfeed may still be running, spawned from a previous innd. * makedbz should be more robust in the presence of malformed history lines, discarding with them or otherwise dealing with them. * Some servers reject some IHAVE, TAKETHIS, or CHECK commands with 500 syntax errors (particularly for long message IDs), and innfeed doesn't handle this particularly well at the moment. It really should have an error handler for this case. [Sven Paulus has a preliminary patch that needs testing, included in ticket #26.] * Editing the active file by hand can currently munge it fairly badly even if the server is throttled unless you reload active before restarting the server. This could be avoidable for at least that particular case by checking the mtime of active before and after the server was throttled. * innreport silently discards news.notice entries about most of the errors innfeed generates. It should ideally generate some summary, or at least note that some error has occurred and the logs should be examined. * Handling of compressed batches needs to be thoroughly reviewed by someone who understands how they're supposed to work. It's not clear that INN_PATH_GZIP is being used correctly at the moment and that compressed batch handling will work right now on systems that don't have gzip installed (but that do have uncompress). * innfeed's statistics don't add up properly all the time. All of the article dispositions don't add up to the offered count like they should. Some article handling must not be recorded properly. * If a channel feed exits immediately, innd respawns it immediately, causing thrashing of the system and a huge spew of errors in syslog. It should mark the channel as dormant for some period of time before respawning it, perhaps only if it's already died multiple times in a short interval. * ctlinnd begin was causing innd to core dump. * Handling of innfeed's dropped batches needs looking at. There are three places where articles can fall between the cracks: an innfeed.togo file written by innd when the feed can't be spawned, a batch file named after the feed name which can be created under similar circumstances, and the dropped files written by innfeed itself. procbatch can clean these up, but has to be run by hand. * When using tradspool, groups are not immediately added to tradspool.map when created, making innfeed unable to find the articles until after some period of time. Part of the problem here is that tradspool only updates tradspool.map on a lazy basis, when it sees an article in that group, since there is no storage hook for creation of a new group. * nntpget doesn't handle long lines in messages. * WP feeds break if there are spaces in the Path header, and the inn.conf parser doesn't check for this case and will allow people to configure their server that way. (It's not clear that the latter is actually a bug, given the new USEFOR attempt to allow folding of Path headers, but the space needs to be removed for WP feeds.) * innd returns 437 for articles that were accepted but filed in the junk group. It should probably return the appropriate 2xx status code in that case instead. * SIGPIPE handling in nnrpd calls all sorts of functions that shouldn't be called from inside a signal handler. * Someone should go through the BUGS sections of all of the man pages and fix those for which the current behavior is unacceptable. * The ovdb server utilities don't handle unclean shutdowns very well. They may leave PID files sitting around, fail to start properly, and otherwise not do what's expected. This area needs some thought and a careful design. * tdx-util can't fix hash chain loops of length greater than one, and they cause both tdx-util -F and innd to hang. * CNFS is insufficiently robust when corrupt buffers are encountered. Setting cnfscheckfudgesize clears up issues that otherwises causes INN to crash. * There should be a way, with the Perl authentication hooks, to either immediately return a list of newsgroups one has access to based on the hostname or to indicate that authentication is required and make the user be prompted with a 480 code when they try to access anything. Right now, that doesn't appear to be possible. * Handling of article rejections needs a lot of cleanup in innd/art.c so that one can cleanly indicate whether a given rejection should be remembered or not, and the code that does the remembering needs to be refactored and shared. Once this is done, we need to not remember rejections for duplicated Xref headers. * rnews's error handling for non-zero exits by child batch decompressers could use some improvement (namely, there should be some). Otherwise, when gzip fails for some reason, we read zero bytes and then throw away the batch without realizing we have an error. Requested New Features * Consider implementing the HEADERS command as discussed rather extensively in news.software.nntp. HEADERS was intended as a general replacement for XHDR and XPAT. [Greg Andruk has a preliminary patch.] * There have been a few requests for the ability to programmatically set the subject of the report generated by news.daily, with escapes that are filled in by the various pieces of information that might be useful. * A bulk cancel command using the MODE CANCEL interface. Possibly through ctlinnd, although it may be a bit afield of what ctlinnd is currently for. * Sven Paulus's patch for nnrpd volume reports should be integrated. [Patch included in ticket #135.] * Lots of people encrypt Injection-Info in various ways. Should that be offered as a standard option? The first data element should probably remain unencrypted so that the O flag in newsfeeds doesn't break. Should there also be an option not to generate Injection-Info? Olaf Titz suggests for encryption: This can be done by formatting injection fields in a way that they are always a multiple of 8 bytes and applying a 64 bit block cipher in ECB mode on it (for instance "395109AA000016FF"). * ctlinnd flushlogs currently renames all of the log files. It would be nice to support the method of log rotation that most other daemons support, namely to move the logs aside and then tell innd to reopen its log files. Ideally, that behavior would be triggered with a SIGHUP. scanlogs would have to be modified to handle this. The best way to support this seems to be to leave scanlogs as is by default, but also add two additional modes. One would flush all the logs and prepare for the syslog logs to be rotated, and the other would do all the work needed after the logs have been rotated. That way, if someone wanted to plug in a separate log rotation handler, they could do so and just call scanlogs on either side of it. The reporting portions of scanlogs should be in a separate program. * Several people have Perl interfaces to pieces of INN that should ideally be part of the INN source tree in some fashion. Greg Andruk has a bunch of stuff that Russ has copies of, for example. [Patches included in tickets #133 and #134.] * There are various available patches for Cancel-Lock and an Internet draft; support should be added to INN for both generation and verification (definitely optional and not on by default at this point). * It would be nice to be able to reload inn.conf (although difficult, due to the amount of data that's generated from it and stashed in various places). * remembertrash currently rejects and remembers articles with syntax errors as well as things like unwanted newsgroups and unwanted distributions, which means that if a peer sends you a bunch of mangled articles, you'll then also reject the correct versions of the articles from other peers. This should probably be rethought. * Additional limits for readers.conf: Limit on concurrent parallel reader streams, limit on KB/second download (preliminary support for this is already in), and a limit on maximum posted articles per day (tied in with the backoff stuff?). These should be per-IP or per-user, but possibly also per-access group. (Consider pulling the -H, -T, -X, and -i code out from innd and using it here.) * timecaf should have more configurable parameters (at the least, how frequently to switch to a new CAF file should be an option). storage.conf should really be extended to allow method-specific configuration for things like this (and to allow the cycbuff.conf file to be merged into storage.conf). * Allow generation of arbitrary additional information that could go in overview by using embedded Perl or Python code. This might be a cleaner way to do the keywords code, which really wants Perl's regex engine ideally. It would also let one do something like doing MD5 hashes of each article and putting that in the overview if you care a lot about making sure that articles aren't corrupted. * Allow some way of accepting articles regardless of the Date header, even if it's far into the future. Some people are running into articles that are dated years into the future for some reason that they still want to store on the server. * There was a request to make --program-suffix and the other name transformation options to autoconf work. The standard GNU package does this with really ugly sed commands in the Makefile rules; we could probably do better, perhaps by substituting the autoconf results into support/install-sh. * INN currently uses hash tables to store the active file internally. It would be worth trying ternary search trees to see if they're faster; the data structure is simpler, performance may be comparable for hits and significantly better for misses, sizing and resizing becomes a non-issue, and the space penalty isn't too bad. A generic implementation is already available in libinn. (An even better place to use ternary search trees may be the configuration parser.) * Provide an innshellvars equivalent for Python. * inncheck should check the syntax of all the various files that are returned by LIST commands, since having those files present with the wrong syntax could result in non-compliant responses from the server. Possibly the server should also refuse to send malformatted lines to the client. * ctlinnd reload incoming.conf could return a count of the hosts that failed, or even better a list of them. This would make pruning old stuff out of incoming.conf much easier. * nnrpd could use sendfile(2), if available, to send articles directly to the socket (for those storage methods where to-wire conversion is not needed). This would need to be added to the storage API. * Somebody should look at keeping the "newsgroups" file more accurate (e.g. newgroups for existing groups should change description, better checkgroups handling, checking for duplicates). * The by-domain statistics innreport generates for nnrpd count all local connections (those with no "." in the hostname) in with the errors as just "?". The host2dom function could be updated to group these as something like "Local". * news.daily could detect if expire segfaults and unpause the server. * When using SSL, track the amount of data that's been transferred to the client and periodically renegotiate the session key. * When using SSL, use SSL_get_peer to get a verified client certificate, if available, and use it to create an additional header line when posting articles (X-Auth-Poster?). This header could use: X509_NAME_oneline(X509_get_subject_name(peer),...) for the full distinguished name, or X509_name_get_text_by_NID(X509_get_subject_name(peer), NID_commonName, ...) for the client's "common name" alone. * When using SSL, use the server's key to generate an HMAC of the body of the message (and most headers?), then include that digest in the headers. This allows a news administrator to determine if a complaint about the content of a message is fraudulent since the message was changed after transmission. * Allow permission for posting cancels to be configured in readers.conf in an access block. * Allow the applicability of auth blocks to be restricted to particular username patterns, probably by adding a users: key to the auth block that matches similar to hosts:. * It would be nice to have bindaddress (and bindaddress6) as a peer block parameter and not just a global parameter in innfeed. * Add cnfsstat to innstat. cnfsstat really needs a more succinct mode before doing this, since right now the output can be quite verbose. General Projects * All the old packages in unoff-contrib should be reviewed for integration into INN. * It may be better for INN on SysV-derived systems to use poll rather than select. The semantics are better, and on some systems (such as Solaris) select is limited to 1024 file descriptors whereas poll can handle any number. Unfortunately, the API is drastically different between the two and poll isn't portable, so supporting both cleanly would require a bit of thought. * Currently only innd and innfeed increase their file descriptor limits. Other parts of INN, notably makehistory, may benefit from doing the same thing if they can without root privileges. * Revisit support for aliased groups and what nnrpd does with them. Should posts to the alias automatically be redirected to the real group? Regardless, the error return should provide useful information about where to post instead. Also, the new overview API, for at least some of the overview methods, truncated the group status at one character and lost the name of the group to which a group is aliased; that needs to be fixed. * More details as to why a message ID is bad would be useful to return to the user, particularly for rnews, inews, etc. * Support putting the history file in different directory from the other (much smaller) db files without hand-editing a bunch of files. * frontends/pullnews and frontends/nntpget should be merged in one script. * backends/filechan is just a simple version of backends/buffchan. It looks like filechan could just be deleted and innupgrade taught to change filechan feeds to buffchan -u feeds. map.c, which is only used by those two programs, could just be included in the same source file. * actsyncd could stand a rewrite and cleaner handling of both configuration and syncing against multiple sources which are canonical for different sets of groups. In the process, FTP handling should support debugging. * send-nntp and nntpsend basically do the same thing; send-nntp could probably be removed (possibly with some extra support in nntpsend for doing simpler things). Long-Term Projects * Look at turning header parsing into a library of some sort. Lots of INN does this, but different parts of INN need subtly different things, so the best API is unclear. * INN's header handling needs to be checked against RFC 5536 and 5537. This may want wait until after we have a header parsing library. * The innd filter should be able to specify additional or replacement groups into which an article should be filed, or even spool the article to a local disk file rather than storing it. (See the stuff that the nnrpd filter can already do.) * When articles expire out of a storage method with self-expire functionality, the overview and history entries for those articles should also be expired immediately. Otherwise, things like the GROUP command don't give the correct results. This will likely require a callback that can be passed to CNFS that is called to do the overview and history cleanup for each article overwritten. * Feed control, namely allowing your peers to set policy on what articles you feed them (not just newsgroups but max article size and perhaps even filter properties like "non-binary"). Every site does this a bit differently. Some people have web interfaces, some people use GUP, some people roll their own alternate things. It would really be nice to have some good way of doing this as part of INN. It's worth considering an NNTP extension for this purpose, although the first step is to build a generic interface that an NNTP extension, a web page, etc. could all use. (An alternate way of doing this would be to extend IHAVE to pass the list of newsgroups as part of the command, although this doesn't seem as generally useful.) * Traffic classification as an extension of filtering. The filter should be able to label traffic as binary (e.g.) without rejecting it, and newsfeeds should be extended to allow feeding only non-binary articles (e.g.) to a peer. * External authenticators should also be able to do things like return a list of groups that a person is allowed to read or post to. Currently, maintaining a set of users and a set of groups, each of which some subset of the users is allowed to access, is far too difficult. For a good starting list of additional functionality that should be made available, look at everything the Perl authentication hooks can do. * Allow nnrpd to spawn long-running helper processes. Not only would this be useful for handling authentication (so that the auth hooks could work without execing a program on every connection), but it may allow for other architectures for handling requests (such as a pool of helpers that deal only with overview requests). More than that, nnrpd should *be* a long-running helper process that innd can feed open file descriptors to. [Aidan Culley has ideas along these lines.] * The tradspool storage method requires assigning a number to every newsgroup (for use in a token). Currently this is maintained in a separate tradspool.map file, but it would be much better to keep that information in the active file where it can't drop out of sync. A code assigned to each newsgroup would be useful for other things as well, such as hashing the directories for the tradindexed overview. For use for that purpose, though, the active file would have to be extended to include removed groups, since they'd need to be kept in the active file to reserve their numbers until the last articles expired. * The locking of the active file leaves something to be desired; in general, the locking in INN (for the active file, the history file, spool updates, overview updates, and the like) needs a thorough inspection and some cleanup. A good place to start would be tracing through the pause and throttle code and write up a clear description of what gets locked where and what is safely restarted and what isn't. Long term, there needs to be a library locking routine used by *everything* that needs to write to the history file, active file, etc. and that keeps track of the PID of the process locking things and is accessible via ctlinnd. * There is a fundamental problem with the current design of the control.ctl file. It combines two things: A database of hierarchies, their maintainers, and related information, and a list of which hierarchies the local server should honor. These should be separated out into the database (which could mostly be updated from a remote source like ftp.isc.org and then combined with local additions) and a configured list of hierarchies (or sub-hierarchies within hierarchies) that control messages should be honored for. This should be reasonably simple although correct handling of checkgroups could get a mite tricky. * Possible NNTP extension: Compression of the protocol, using gzip, bzip2, or some other technique. Particularly useful for long lists like the active file information or the overview information, but possibly useful in general for other things. * Install wizards. Configuring INN is currently very complex even for an experienced news admin, and there are several fairly standard configurations that shouldn't be nearly that complicated to get running out of the box. A little interactive Perl script asking some simple questions could probably get a lot of cases easily right. * One ideally wants to be able to easily convert between different overview formats or storage methods, refiling articles in place. This should be possible once we have a history API that allows changing the storage location of an article in-place. * Set up the infrastructure required so that INN can use alloca. This would significantly decrease the number of calls to malloc needed and would be a lot more convenient. alloca is now available, but most programs still need to call alloca_free in their main loops before we can use it in the various libraries. * Support building in a separate directory than the source tree. It may be best to just support this via lndir rather than try to do it in configure, but it would be ideal to add support for this to the autoconf system. Unfortunately, the standard method requires letting configure generate all of the makefiles, which would make running configure and config.status take much longer than it does currently. * Look at adding some kind of support for MODE CANCEL via network sockets and fixing up the protocol so that it could possibly be standardized (the easiest thing to do would probably be to change it into a CANCEL command). If we want to get to the point where INN can accept and even propagate such feeds from dedicated spam filters or the like, there must also be some mechanism of negotiating policy in order to decide what cancels the server wants to be fed. * The "possibly signed" char data type is one of the inherent flaws of C. Some other projects have successfully gotten completely away from this by declaring all of their strings to be unsigned char, defining a macro like U that casts strings to unsigned char for use with literal strings, and always using unsigned char everywhere. Unfortunately, this also requires wrappering all of the standard libc string functions, since they're prototyped as taking char rather than unsigned char. The benefits include cleaner and consistent handling of characters over 127, better warnings from the compiler, consistent behavior across platforms with different notions about the signedness of char, and the elimination of warnings from the macros on platforms like Solaris where those macros can't handle signed characters. We should look at doing this for INN. * It would clean up a lot of code considerably if we could just use mmap semantics regardless of whether the system has mmap. It may be possible to emulate mmap on systems that don't have it by reading the entirety of the file into memory and setting the flags that require things to call mmap_flush and mmap_invalidate on a regular basis, but it's not clear where to stash the file descriptor that corresponds to the mapped file. * Consider replacing the awkward access: parameter in readers.conf with separate commands (e.g. "allow_newnews: true") or otherwise cleaning up the interaction between access: and read:/post:. Note that at least allownewnews: can be treated as a setting for overriding inn.conf and should be very easy to add. * Add a localport: parameter (similar to localaddress:) to readers.conf auth groups. With those two parameters (and ssl_required:) we essentially eliminate the need to run multiple instances of nnrpd just to use different configurations. * Various things may break when trying to use data written while compiled with large file support using a server that wasn't so compiled (and vice versa). The main one is the history file, but tradindexed is also affected and buffindexed has been reported to have problems with this as well. Ideally, all of INN's data files should be as portable as possible. Code Reorganization * storage should be reserved just for article storage; the overview methods should be in a separate overview tree. * The split between frontends and backends is highly non-intuitive. Some better organization scheme should be arrived at. Perhaps something related to incoming and outgoing, with programs like cnfsstat moved into the storage directory with the other storage-related code? * Add a separate utils directory for things like convdate, shlock, shrinkfile, and the like. Some of the scripts may possibly want to go into that directory too. * The lib directory possibly should be split so that it contains only code always compiled and part of INN, and the various replacements for possibly missing system routines are in a separate directory (such as replace). These should possibly be separate libraries; there are things that currently link against libinn that only need the portability pieces. * The doc directory really should be broken down further by type of documentation or section or something; it's getting a bit unwieldy. * Untabify and reformat all of the code according to a consistent coding style which would then be enforced for all future check-ins. inn-2.6.0/innfeed/0000755000175200017520000000000012575023700013326 5ustar iuliusiuliusinn-2.6.0/innfeed/configfile.l0000644000175200017520000001463412575023702015622 0ustar iuliusiulius%{ /* $Id: configfile.l 9526 2013-08-07 18:37:37Z iulius $ ** ** A flex input file for the innfeed config file. ** ** Written by James Brister */ #include "innfeed.h" #include #include #include #include #include "inn/libinn.h" #include "configfile.h" #include "config_y.h" #include "misc.h" #if ! defined (FLEX_SCANNER) #error "You must use FLEX to process the lex input file." #endif #if defined (FLEX_DEBUG) #define YY_USER_INIT yy_flex_debug = (getenv ("YYDEBUG") == NULL ? 0 : 1) #endif /* We never use this function but flex always defines it, so silence the warnings about it. */ static void yyunput(int, char *) UNUSED; char *strPtr = 0 ; int strPtrLen = 0 ; int strIdx = 0 ; int sawBsl ; int lineCount = 0 ; int current ; static void strAppend (int ch) { if (strIdx == strPtrLen) { if (strPtr == 0) strPtr = xmalloc (strPtrLen = 50) ; else strPtr = xrealloc (strPtr,strPtrLen += 10) ; } strPtr [strIdx++] = ch ; } #define MAX_INCLUDE_DEPTH 11 struct includeFile { YY_BUFFER_STATE state; char *name ; } include_stack[MAX_INCLUDE_DEPTH]; int include_stack_ptr = 0; %} %x incl ID [a-zA-Z][-a-zA-Z0-9._/]+ %% \n lineCount++ ; ":" { return (COLON) ; } "{" { return (LBRACE) ; } "}" { return (RBRACE) ; } [pP][eE][eE][rR] { return (PEER) ; } ^"$INCLUDE" BEGIN(incl); [ \t]* /* eat the whitespace before include filename */ [^ \t\n]+ { if (include_stack_ptr == MAX_INCLUDE_DEPTH - 1) { int i ; fprintf( stderr, "Includes nested too deeply:\n" ); for (i = 1 ; i <= include_stack_ptr ; i++) fprintf (stderr,"\t%s\n",include_stack[i].name) ; syslog (LOG_ERR, "includes nested to deeply") ; exit( 1 ); } if ((yyin = fopen(yytext,"r")) == NULL) { syslog (LOG_CRIT,"include file fopen failed: %s %s", yytext,strerror(errno)); fprintf (stderr,"include file fopen failed: %s %s\n", yytext,strerror(errno)); exit (1) ; } else { d_printf (1,"Including (%d) from %s\n", include_stack_ptr + 1,yytext) ; include_stack[include_stack_ptr].state = YY_CURRENT_BUFFER; include_stack[++include_stack_ptr].name = xstrdup (yytext) ; yy_switch_to_buffer(yy_create_buffer(yyin, YY_BUF_SIZE)); } BEGIN(INITIAL); } <> { if ( include_stack_ptr <= 0 ) yyterminate(); else { free (include_stack[include_stack_ptr].name) ; yy_delete_buffer(YY_CURRENT_BUFFER); yy_switch_to_buffer(include_stack[--include_stack_ptr].state); } } [gG][rR][oO][uU][pP] { return (GROUP) ; } #[^\n]* { (void) 0 ; } [ \t]+ { (void) 1 ; } '\\[\\abfnrtv]' { switch (yytext[2]) { case '\\': yylval.chr = '\\' ; break ; case 'a': yylval.chr = 007 ; break ; case 'b': yylval.chr = 010 ; break ; case 'f': yylval.chr = 014 ; break ; case 'n': yylval.chr = 012 ; break ; case 'r': yylval.chr = 015 ; break ; case 't': yylval.chr = 011 ; break ; case 'v': yylval.chr = 013 ; break ; } return (CHAR) ; } '.' { yylval.chr = yytext[1] ; return (CHAR) ; } '\\[0-9][0-9][0-9]' { yylval.chr = (char)strtol(&yytext[2], (char **)NULL, 8); return (CHAR) ;} \"[^\"]* {{ size_t i ; for (i = 1, strIdx = 0, sawBsl = 0 ; ; i++) { /* Cast yyleng to size_t because it used to be an int * in flex versions anterior or equal to 2.5.35. * Do not use yyget_leng() here because old flex versions * do not define it. */ if (i < (size_t) yyleng) current = yytext [i] ; else current = input() ; if (current != EOF) { switch (current) { case '\\': if (sawBsl) { strAppend (current) ; sawBsl = 0 ; } else sawBsl = 1 ; break ; case '\n': if (!sawBsl) strAppend(current) ; sawBsl = 0 ; lineCount++ ; break ; case '\"': if (sawBsl) { strAppend (current) ; sawBsl = 0 ; } else { strAppend ('\0') ; yylval.string = strPtr ; strPtr = 0 ; strPtrLen = strIdx = 0 ; return (XSTRING) ; } break ; case 'a': case 'b': case 'f': case 'n': case 'r': case 't': case 'v': if (sawBsl) { switch (current) { case 'a': strAppend (007) ; break ; case 'b': strAppend (010) ; break ; case 'f': strAppend (014) ; break ; case 'n': strAppend (012) ; break ; case 'r': strAppend (015) ; break ; case 't': strAppend (011) ; break ; case 'v': strAppend (013) ; break ; } sawBsl = 0 ; } else strAppend (current) ; break ; default: strAppend (current) ; sawBsl = 0 ; break ; } } else { return (XSTRING) ; } } }} [-0-9][0-9]* { yylval.integer = atoi (yytext) ; return (IVAL) ; } [-0-9][0-9]*\.[0-9]* { yylval.real = atof (yytext) ; return (RVAL) ; } [^#:\'\" \t\n]+ { yylval.name = xstrdup (yytext) ; if (strcasecmp (yylval.name,"false") == 0) return (FALSEBVAL) ; else if (strcasecmp (yylval.name,"true") == 0) return (TRUEBVAL) ; else return (WORD) ; } %% inn-2.6.0/innfeed/endpoint.h0000644000175200017520000001420612575023702015324 0ustar iuliusiulius/* $Id: endpoint.h 9212 2011-06-24 17:54:10Z iulius $ ** ** The public interface to the Endpoint class. ** ** Written by James Brister ** ** The EndPoint objects are encapsulations of file descriptors that normally ** do blocking I/O (i.e. NOT fd's hooked to a disk file). The EndPoint class ** provides methods for requesting read/writes to happen when next possible ** and for the requestor to be notified when the I/O is complete (or failed ** for some reason). Facilities for timeout notifications are provided too. ** ** We should add a way to cancel prepared read/writes. */ #if ! defined ( endpoint_h__ ) #define endpoint_h__ #include "misc.h" #define clearTimer(timerId) \ do {\ if((timerId)!=0) { \ removeTimeout(timerId); \ timerId=0; \ } \ }while(0) /* These typedefs really live in misc.h. * ***************************************** * * The basic (opaque to the outside world) type: * * typedef struct endpoint_s *EndPoint ; * ***************************************** * * The return status of an I/O request: * * typedef enum { * IoDone, I/O completed successfully * IoIncomplete, I/O still got more to read/write * IoProgress, I/O still got more to read/write * IoFailed I/O failed * } IoStatus ; * * The completion callbacks are never called with the status IoIncomplete or * IoProgress. * ***************************************** * * typedef for function callback when I/O is complete (or failed). * E is the EndPoint * I is the status of the IO * B is the buffer the IO was to read to or write from * D is the client data originally given to prepare{Write,Read} * * typedef void (*EndpRWCB) (EndPoint e, IoStatus i, Buffer b, void *d) ; * ***************************************** * * typedef for function callback when a timer has gone off. D is the client * data given to prepare{Sleep,Wake}: * * typedef void (*EndpTCB) (void *d) ; * */ /* create a new EndPoint hooked up to the given file descriptor */ EndPoint newEndPoint (int fd) ; /* shutdown the file descriptor and delete the endpoint. */ void delEndPoint (EndPoint ep); /* return the file descriptor the endpoint is managing */ int endPointFd (EndPoint endp) ; /* Request a read when available. Reads MINLEN bytes into the * buffers in BUFFERS. BUFFERS is an array of Buffers, the last of which * must be NULL. Note that ownership of BUFFERS is never asserted, but * the ownership of the Buffers in it is. So if an EndPoint is * deleted while a read is pending the Buffers will be released, but * the array won't be. If MINLEN is 0 then the buffers must be * filled. The FUNC function gets called when the read is * complete. CLIENTDATA is simply passed back to the * callback. Returns non-zero if can be scheduled for processing. */ int prepareRead (EndPoint endp, Buffer *buffers, EndpRWCB func, void *clientData, int minlen) ; /* Request a write when possible. All the data in the buffers in * BUFFERS will be written out the endpoint. BUFFERS is a NULL * terminated array of Buffers. See prepareRead for a discussion on * the ownership of BUFFERS and the Buffers inside BUFFERS. The PROGRESS * callback function will be called and the CLIENTDATA value will be * passed through to it whenever any data is written except for the * final write. The DONE callback function will be called and the * CLIENTDATA value will be passed through to it after the final write. * Returns non-zero if scheduled succesfully. */ int prepareWrite (EndPoint endp, Buffer *buffers, EndpRWCB progress, EndpRWCB done, void *clientData) ; /* cancel any outstanding reads. */ void cancelRead (EndPoint endp) ; /* cancel any outstanding writes. */ void cancelWrite (EndPoint endp, char *buffer, size_t *len) ; /* return true if prepareRead has been called, but not serviced yet */ bool readIsPending (EndPoint endp) ; /* Request a wakeup at a given time. */ TimeoutId prepareWake (EndpTCB func, time_t timeToWake, void *clientData) ; /* return true if prepareWrite has been called, but not serviced yet */ bool writeIsPending (EndPoint endp) ; /* Requests a wakeup TIMETOSLEEP seconds from now. */ TimeoutId prepareSleep (EndpTCB func, int timeToSleep, void *clientData) ; /* Updates tid to wakeup TIMETOSLEEP seconds from now. */ TimeoutId updateSleep (TimeoutId tid, EndpTCB func, int timeToSleep, void *clientData) ; /* Set up a function to be called whenever the endpoint's file descriptor is NOT ready. This is called after all other fd-ready endpoints have been serviced. */ void *addWorkCallback (EndPoint endp, EndpWorkCbk cbk, void *data) ; void setSigHandler (int signum, void (*ptr)(int)); /* remove the timeout that was previously requested. Returns true if succesfully removed, false otherwise. 0 is a legal parameter value, in which case the function simply returns. */ bool removeTimeout (TimeoutId tid) ; /* start the select loop. An initial prepare(Read|Write) or a timeout better have been setup. Doesn't return unless stopRun called */ void Run (void) ; /* stops the Run loop and makes Run() return */ void stopRun (void) ; /* returns the errno the endpoint got. */ int endPointErrno (EndPoint endp) ; /* Tell the EndPoint class that the given EndPoint should always be considered first for servicing (i.e. the EndPoint connectied to innd) */ void setMainEndPoint (EndPoint endp) ; /* returns the fd of the main endpoint */ int getMainEndPointFd (void) ; void freeTimeoutQueue (void) ; int endpointConfigLoadCbk (void *data) ; /* * kre's cool idea for reducing the number of times time() is called. */ extern time_t PrivateTime; #define theTime() (PrivateTime ? PrivateTime : time(&PrivateTime)) #define timePasses() (PrivateTime = 0) #endif /* endpoint_h__ */ inn-2.6.0/innfeed/configfile.y0000644000175200017520000005421012575023702015631 0ustar iuliusiulius%{ /* $Id: configfile.y 9363 2011-08-21 23:22:01Z eagle $ ** ** A yacc input file for the innfeed config file. ** ** Written by James Brister ** ** This file contains the heart of the innfeed configuration parser, written ** in yacc. It uses an external lexer generated by flex. */ #include "innfeed.h" #include "config.h" #include "clibrary.h" #include #include #include #if defined(_HPUX_SOURCE) # include #endif #include "inn/messages.h" #include "inn/libinn.h" #include "configfile.h" #include "misc.h" #define UNKNOWN_SCOPE_TYPE "line %d: unknown scope type: %s" #define SYNTAX_ERROR "line %d: syntax error" extern int lineCount ; scope *topScope = NULL ; static scope *currScope = NULL ; char *errbuff = NULL ; static void appendName (scope *s, char *p, size_t len) ; static char *valueScopedName (value *v) ; static void freeValue (value *v) ; static char *checkName (scope *s, const char *name) ; static void addValue (scope *s, value *v) ; static char *addScope (scope *s, const char *name, scope *val) ; static void printScope (FILE *fp, scope *s, int indent) ; static void printValue (FILE *fp, value *v, int indent) ; static scope *newScope (const char *type) ; #if 0 static int strNCaseCmp (const char *a, const char *b, size_t len) ; #endif int yyerror (const char *s) ; int yywrap (void) ; int yyparse (void) ; #if 0 int isString (scope *s, const char *name, int inherit) { value *v = findValue (s,name,inherit) ; return (v != NULL && v->type == stringval) ; } #endif int getBool (scope *s, const char *name, int *rval, int inherit) { value *v = findValue (s,name,inherit) ; if (v == NULL) return 0 ; else if (v->type != boolval) return 0 ; *rval = v->v.bool_val ; return 1 ; } int getString (scope *s, const char *name, char **rval, int inherit) { value *v = findValue (s,name,inherit) ; if (v == NULL) return 0 ; else if (v->type != stringval) return 0 ; *rval = xstrdup (v->v.charp_val) ; return 1 ; } int getReal (scope *s, const char *name, double *rval, int inherit) { value *v = findValue (s,name,inherit) ; if (v == NULL) return 0 ; else if (v->type != realval) return 0 ; *rval = v->v.real_val ; return 1 ; } int getInteger (scope *s, const char *name, long *rval, int inherit) { value *v = findValue (s,name,inherit) ; if (v == NULL) return 0 ; else if (v->type != intval) return 0 ; *rval = v->v.int_val ; return 1 ; } void freeScopeTree (scope *s) { int i ; if (s == NULL) return ; if (s->parent == NULL && s->me != NULL) { /* top level scope */ free (s->me->name) ; free (s->me) ; } for (i = 0 ; i < s->value_idx ; i++) if (s->values[i] != NULL) freeValue (s->values [i]) ; free (s->values) ; free (s->scope_type) ; s->parent = NULL ; s->values = NULL ; free (s) ; } char *addInteger (scope *s, const char *name, long val) { value *v ; char *error ; if ((error = checkName (currScope,name)) != NULL) return error ; v = (value *) calloc (1,sizeof (value)) ; v->name = xstrdup (name) ; v->type = intval ; v->v.int_val = val ; addValue (s,v) ; return NULL ; } char *addChar (scope *s, const char *name, char val) { value *v ; char *error ; if ((error = checkName (currScope,name)) != NULL) return error ; v = (value *) calloc (1,sizeof (value)) ; v->name = xstrdup (name) ; v->type = charval ; v->v.char_val = val ; addValue (s,v) ; return NULL ; } char *addBoolean (scope *s, const char *name, int val) { value *v ; char *error ; if ((error = checkName (currScope,name)) != NULL) return error ; v = (value *) calloc (1,sizeof (value)) ; v->name = xstrdup (name) ; v->type = boolval ; v->v.bool_val = val ; addValue (s,v) ; return NULL ; } char *addReal (scope *s, const char *name, double val) { value *v ; char *error ; if ((error = checkName (currScope,name)) != NULL) return error ; v = (value *) calloc (1,sizeof (value)) ; v->name = xstrdup (name) ; v->type = realval ; v->v.real_val = val ; addValue (s,v) ; return NULL ; } char *addString (scope *s, const char *name, const char *val) { value *v ; char *error ; if ((error = checkName (currScope,name)) != NULL) return error ; v = (value *) calloc (1,sizeof (value)) ; v->name = xstrdup (name) ; v->type = stringval ; v->v.charp_val = xstrdup (val) ; addValue (s,v) ; return NULL ; } value *findValue (scope *s, const char *name, int inherit) { const char *p ; if (name == NULL || *name == '\0') return NULL ; if (*name == ':') return findValue (topScope,name + 1,0) ; else if (s == NULL) return findValue (topScope,name,0) ; else { int i ; if ((p = strchr (name,':')) == NULL) p = name + strlen (name) ; for (i = 0 ; i < s->value_idx ; i++) { if (strlen (s->values[i]->name) == (size_t) (p - name) && strncmp (s->values[i]->name,name,p - name) == 0) { if (*p == '\0') /* last segment of name */ return s->values[i] ; else if (s->values[i]->type != scopeval) errbuff = xstrdup ("Component not a scope") ; else return findValue (s->values[i]->v.scope_val,p + 1,0) ; } } /* not in this scope. Go up if inheriting values and only if no ':' in name */ if (inherit && *p == '\0') return findValue (s->parent,name,inherit) ; } return NULL ; } /* find the scope that name belongs to. If mustExist is true then the name must be a fully scoped name of a value. relative scopes start at s. */ scope *findScope (scope *s, const char *name, int mustExist) { scope *p = NULL ; char *q ; int i ; if ((q = strchr (name,':')) == NULL) { if (!mustExist) p = s ; else for (i = 0 ; p == NULL && i < s->value_idx ; i++) if (strcmp (s->values[i]->name,name) == 0) p = s ; return p ; } else if (*name == ':') { while (s->parent != NULL) s = s->parent ; return findScope (s,name + 1,mustExist) ; } else { for (i = 0 ; i < s->value_idx ; i++) if (strncmp (s->values[i]->name,name,q - name) == 0) if (s->values[i]->type == scopeval) return findScope (s->values[i]->v.scope_val,q + 1,mustExist) ; } return NULL ; } /****************************************************************************/ /* */ /****************************************************************************/ static void appendName (scope *s, char *p, size_t len) { if (s == NULL) return ; else { appendName (s->parent,p,len) ; strlcat (p,s->me->name,len) ; strlcat (p,":",len) ; } } static char *valueScopedName (value *v) { scope *p = v->myscope ; int len = strlen (v->name) ; char *q ; while (p != NULL) { len += strlen (p->me->name) + 1 ; p = p->parent ; } len++; q = xmalloc (len) ; q [0] = '\0' ; appendName (v->myscope,q,len) ; strlcat (q,v->name,len) ; return q ; } static void freeValue (value *v) { free (v->name) ; switch (v->type) { case scopeval: freeScopeTree (v->v.scope_val) ; break ; case stringval: free (v->v.charp_val) ; break ; default: break ; } free (v) ; } static char *checkName (scope *s, const char *name) { int i ; char *error = NULL ; if (s == NULL) return NULL ; for (i = 0 ; i < s->value_idx ; i++) { char *n = NULL ; if (strcmp (name,s->values [i]->name) == 0) { n = valueScopedName (s->values[i]) ; error = concat ("Two definitions of ", n, (char *) 0) ; free (n) ; return error ; } } return error ; } static void addValue (scope *s, value *v) { v->myscope = s ; if (s == NULL) return ; if (s->value_count == s->value_idx) { if (s->values == 0) { s->values = (value **) calloc (10,sizeof (value *)) ; s->value_count = 10 ; } else { s->value_count += 10 ; s->values = (value **) realloc (s->values, sizeof (value *) * s->value_count); } } s->values [s->value_idx++] = v ; } static char *addScope (scope *s, const char *name, scope *val) { value *v ; char *error ; if ((error = checkName (s,name)) != NULL) return error ; v = (value *) calloc (1,sizeof (value)) ; v->name = xstrdup (name) ; v->type = scopeval ; v->v.scope_val = val ; val->me = v ; val->parent = s ; addValue (s,v) ; currScope = val ; return NULL ; } static void printScope (FILE *fp, scope *s, int indent) { int i ; for (i = 0 ; i < s->value_idx ; i++) printValue (fp,s->values [i],indent + 5) ; } static void printValue (FILE *fp, value *v, int indent) { int i ; for (i = 0 ; i < indent ; i++) fputc (' ',fp) ; switch (v->type) { case intval: fprintf (fp,"%s : %ld # INTEGER\n",v->name,v->v.int_val) ; break ; case stringval: fprintf (fp,"%s : \"",v->name) ; { char *p = v->v.charp_val ; while (*p) { if (*p == '"' || *p == '\\') fputc ('\\',fp) ; fputc (*p,fp) ; p++ ; } } fprintf (fp,"\" # STRING\n") ; break ; case charval: fprintf (fp,"%s : %c",v->name,047) ; switch (v->v.char_val) { case '\\': fprintf (fp,"\\\\") ; break ; default: if (isprint((unsigned char) v->v.char_val)) fprintf (fp,"%c",v->v.char_val) ; else fprintf (fp,"\\%03o",v->v.char_val) ; } fprintf (fp,"%c # CHARACTER\n",047) ; break ; case realval: fprintf (fp,"%s : %f # REAL\n",v->name,v->v.real_val) ; break ; case boolval: fprintf (fp,"%s : %s # BOOLEAN\n", v->name,(v->v.bool_val ? "true" : "false")) ; break ; case scopeval: fprintf (fp,"%s %s { # SCOPE\n",v->v.scope_val->scope_type,v->name) ; printScope (fp,v->v.scope_val,indent + 5) ; for (i = 0 ; i < indent ; i++) fputc (' ',fp) ; fprintf (fp,"}\n") ; break ; default: fprintf (fp,"UNKNOWN value type: %d\n",v->type) ; exit (1) ; } } static scope *newScope (const char *type) { scope *t ; int i ; t = (scope *) calloc (1,sizeof (scope)) ; t->parent = NULL ; t->scope_type = xstrdup (type) ; for (i = 0 ; t->scope_type[i] != '\0' ; i++) t->scope_type[i] = tolower ((unsigned char) t->scope_type[i]) ; return t ; } #define BAD_KEY "line %d: illegal key name: %s" #define NON_ALPHA "line %d: keys must start with a letter: %s" static char *keyOk (const char *key) { const char *p = key ; char *rval ; if (key == NULL) { rval = xmalloc (strlen ("line : NULL key") + 15) ; sprintf (rval,"line %d: NULL key", lineCount) ; return rval ; } else if (*key == '\0') { rval = xmalloc (strlen ("line : EMPTY KEY") + 15) ; sprintf (rval,"line %d: EMPTY KEY", lineCount) ; return rval ; } if (!isalpha((unsigned char) *p)) { rval = xmalloc (strlen (NON_ALPHA) + strlen (key) + 15) ; sprintf (rval,NON_ALPHA,lineCount, key) ; return rval ; } p++ ; while (*p) { if (!(isalnum((unsigned char) *p) || *p == '_' || *p == '-')) { rval = xmalloc (strlen (BAD_KEY) + strlen (key) + 15) ; sprintf (rval,BAD_KEY,lineCount,key) ; return rval ; } p++ ; } return NULL ; } static PFIVP *funcs = NULL ; static void **args = NULL ; static int funcCount ; static int funcIdx ; void configAddLoadCallback (PFIVP func,void *arg) { if (func == NULL) return ; if (funcIdx == funcCount) { funcCount += 10 ; if (funcs == NULL) { funcs = xmalloc (sizeof (PFIVP) * funcCount); args = xmalloc (sizeof (void *) * funcCount) ; } else { funcs = xrealloc (funcs,sizeof (PFIVP) * funcCount); args = xrealloc (args,sizeof (void *) * funcCount) ; } } args [funcIdx] = arg ; funcs [funcIdx++] = func ; } void configRemoveLoadCallback (PFIVP func) { int i, j ; for (i = 0 ; i < funcIdx ; i++) if (funcs [i] == func) break ; for (j = i ; j < funcIdx - 1 ; j++) { funcs [j] = funcs [j + 1] ; args [j] = args [j + 1] ; } if (funcIdx > 1 && i < funcIdx) { funcs [i - 2] = funcs [i - 1] ; args [i - 2] = args [i - 1] ; } if (funcIdx > 0 && i < funcIdx) funcIdx-- ; } static int doCallbacks (void) { int i ; int rval = 1 ; for (i = 0 ; i < funcIdx ; i++) if (funcs [i] != NULL) rval = (funcs[i](args [i]) && rval) ; return rval ; } static char *key ; %} %union{ scope *scp ; value *val ; char *name ; int integer ; double real ; char *string ; char chr ; } %token PEER %token GROUP %token IVAL %token RVAL %token NAME %token XSTRING %token SCOPE %token COLON %token LBRACE %token RBRACE %token TRUEBVAL %token FALSEBVAL %token CHAR %token WORD %token IP_ADDRESS %type IVAL %type RVAL %type XSTRING %type CHAR %type TRUEBVAL FALSEBVAL WORD %% input: { lineCount = 1 ; addScope (NULL,"",newScope ("")) ; topScope = currScope ; } entries { if (!doCallbacks()) YYABORT ; } ; scope: entries ; entries: | entries entry | entries error { errbuff = xmalloc (strlen(SYNTAX_ERROR) + 12) ; sprintf (errbuff,SYNTAX_ERROR,lineCount) ; YYABORT ; } ; entry: PEER WORD LBRACE { errbuff = addScope (currScope,$2,newScope ("peer")) ; free ($2) ; if (errbuff != NULL) YYABORT ; } scope RBRACE { currScope = currScope->parent ; } | GROUP WORD LBRACE { errbuff = addScope (currScope,$2,newScope ("group")) ; free ($2) ; if (errbuff != NULL) YYABORT ; } scope RBRACE { currScope = currScope->parent ; } | WORD WORD LBRACE { errbuff = xmalloc (strlen(UNKNOWN_SCOPE_TYPE) + 15 + strlen ($1)) ; sprintf (errbuff,UNKNOWN_SCOPE_TYPE,lineCount,$1) ; free ($1) ; free ($2) ; YYABORT ; } | WORD { if ((errbuff = keyOk($1)) != NULL) { YYABORT ; } else key = $1 ; } COLON value ; value: WORD { if ((errbuff = addString (currScope, key, $1)) != NULL) YYABORT ; free (key) ; free ($1) ; } | IVAL { if ((errbuff = addInteger(currScope, key, $1)) != NULL) YYABORT; free (key) ; } | TRUEBVAL { if ((errbuff = addBoolean (currScope, key, 1)) != NULL) YYABORT ; free (key) ; free ($1) ; } | FALSEBVAL { if ((errbuff = addBoolean (currScope, key, 0)) != NULL) YYABORT ; free (key) ; free ($1) ; } | RVAL { if ((errbuff = addReal (currScope, key, $1)) != NULL) YYABORT ; free (key) ; } | XSTRING { if ((errbuff = addString (currScope, key, $1)) != NULL) YYABORT; free (key) ; } | CHAR { if ((errbuff = addChar (currScope, key, $1)) != NULL) YYABORT ; free (key) ; } ; %% int yyerror (const char *s) { #undef FMT #define FMT "line %d: %s" errbuff = xmalloc (strlen (s) + strlen (FMT) + 20) ; sprintf (errbuff,FMT,lineCount,s) ; return 0 ; } int yywrap (void) { return 1 ; } extern FILE *yyin ; int yydebug ; #define NO_INHERIT 0 #if ! defined (WANT_MAIN) struct peer_table_s { char *peerName ; value *peerValue ; } ; static struct peer_table_s *peerTable ; static int peerTableCount ; static int peerTableIdx ; void configCleanup (void) { int i ; for (i = 0 ; i < peerTableIdx ; i++) free (peerTable[i].peerName) ; free (peerTable) ; freeScopeTree (topScope); free (funcs) ; free (args) ; } int buildPeerTable (FILE *fp, scope *s) { int rval = 1 ; int i, j ; for (i = 0 ; i < s->value_idx ; i++) { if (ISSCOPE (s->values[i]) && ISPEER (s->values[i])) { for (j = 0 ; j < peerTableIdx ; j++) { if (strcmp (peerTable[j].peerName,s->values[i]->name) == 0) { logOrPrint (LOG_ERR,fp, "ME config: two peers with the same name: %s", peerTable[j].peerName) ; rval = 0 ; break ; } } if (j == peerTableIdx) { if (peerTableCount == peerTableIdx) { peerTableCount += 10 ; if (peerTable == NULL) peerTable = xmalloc (sizeof(struct peer_table_s) * peerTableCount) ; else peerTable = xrealloc (peerTable, sizeof(struct peer_table_s) * peerTableCount) ; } peerTable[peerTableIdx].peerName = xstrdup (s->values[i]->name); peerTable[peerTableIdx].peerValue = s->values[i] ; peerTableIdx++ ; } } else if (ISSCOPE (s->values[i])) rval = (buildPeerTable (fp,s->values[i]->v.scope_val) && rval) ; } return rval ; } /* read the config file. Any errors go to errorDest if it is non-NULL, otherwise they are syslogged. If justCheck is true then return after parsing */ static int inited = 0 ; int readConfig (const char *file, FILE *errorDest, int justCheck, int dump) { scope *oldTop = topScope ; FILE *fp ; int rval ; if (!inited) { inited = 1 ; yydebug = (getenv ("YYDEBUG") == NULL ? 0 : 1) ; if (yydebug) atexit (configCleanup) ; } if (file == NULL || strlen (file) == 0 || !fileExistsP (file)) { logOrPrint (LOG_ERR,errorDest, "ME config aborting, no such config file: %s", file ? file : "(null)") ; d_printf (1,"No such config file: %s\n", file ? file : "(null)") ; exit (1) ; } if ((fp = fopen (file,"r")) == NULL) { logOrPrint (LOG_ERR,errorDest, "ME config aborting fopen %s: %s", file, strerror (errno)) ; exit (1) ; } logOrPrint (LOG_NOTICE,errorDest,"loading %s", file) ; yyin = fp ; topScope = NULL ; rval = yyparse () ; fclose (fp) ; if (rval != 0) /* failure */ { freeScopeTree (topScope) ; if (justCheck) freeScopeTree (oldTop) ; else topScope = oldTop ; topScope = NULL ; if (errbuff != NULL) { if (errorDest != NULL) fprintf (errorDest,"config file error: %s\n",errbuff) ; else warn ("ME config file error: %s", errbuff) ; free (errbuff) ; } return 0 ; } if (dump) { fprintf (errorDest ? errorDest : stderr,"Parsed config file:\n") ; printScope (errorDest ? errorDest : stderr,topScope,-5) ; fprintf (errorDest ? errorDest : stderr,"\n") ; } if (justCheck) { freeScopeTree (topScope) ; freeScopeTree (oldTop) ; topScope = NULL ; } else { for (peerTableIdx-- ; peerTableIdx >= 0 ; peerTableIdx--) { free (peerTable [peerTableIdx].peerName) ; peerTable [peerTableIdx].peerName = NULL ; peerTable [peerTableIdx].peerValue = NULL ; } peerTableIdx = 0 ; if (!buildPeerTable (errorDest,topScope)) logAndExit (1,"Failed to build list of peers") ; } return 1 ; } value *getNextPeer (int *cookie) { value *rval ; if (*cookie < 0 || *cookie >= peerTableIdx) return NULL ; rval = peerTable[*cookie].peerValue ; (*cookie)++ ; return rval ; } value *findPeer (const char *name) { value *v = NULL ; int i ; for (i = 0 ; i < peerTableIdx ; i++) if (strcmp (peerTable[i].peerName,name) == 0) { v = peerTable[i].peerValue ; break ; } return v ; } #endif #if defined (WANT_MAIN) int main (int argc, char **argv) { if ( yyparse() ) printf ("parsing failed: %s\n",errbuff ? errbuff : "NONE") ; else { printScope (stdout,topScope,-5) ; if (argc == 3) { #if 0 printf ("Looking for %s of type %s: ",argv[2],argv[1]) ; if (strncmp (argv[1],"int",3) == 0) { int i = 0 ; if (!getInteger (topScope,argv[2],&i)) printf ("wasn't found.\n") ; else printf (" %d\n",i) ; } else if (strncmp (argv[1],"real",4) == 0) { double d = 0.0 ; if (!getReal (topScope,argv[2],&d)) printf ("wasn't found.\n") ; else printf (" %0.5f\n",d) ; } #else value *v = findValue (topScope,argv[1],1) ; if (v == NULL) printf ("Can't find %s\n",argv[1]) ; else { long ival = 987654 ; if (getInteger (v->v.scope_val,argv[2],&ival,1)) printf ("Getting %s : %ld",argv[2],ival) ; else printf ("Name is not legal: %s\n",argv[2]) ; } #endif } else if (argc == 2) { #if 1 value *v = findValue (topScope,argv[1],1) ; if (v == NULL) printf ("Can't find %s\n",argv[1]) ; else { printf ("Getting %s : ",argv[1]) ; printValue (stdout,v,0) ; } #else if (findScope (topScope,argv[1],1) == NULL) printf ("Can't find the scope of %s\n",argv[1]) ; #endif } } freeScopeTree (topScope) ; return 0 ; } #endif /* defined (WANT_MAIN) */ inn-2.6.0/innfeed/buffer.c0000644000175200017520000002640512575023702014754 0ustar iuliusiulius/* $Id: buffer.c 7585 2006-11-21 09:37:51Z eagle $ ** ** The Buffer class for innfeed. ** ** Written by James Brister ** ** The implementation of the Buffer class. Buffers are reference counted ** objects that abstract memory regions in a way similar to struct iovec. */ #include "innfeed.h" #include "config.h" #include "clibrary.h" #include #include "inn/messages.h" #include "inn/libinn.h" #include "buffer.h" static Buffer gBufferList = NULL ; static Buffer bufferPool = NULL ; static unsigned int bufferCount = 0 ; static unsigned int bufferByteCount = 0 ; struct buffer_s { int refCount ; char *mem ; size_t memSize ; /* the length of mem */ size_t dataSize ; /* amount that has actual data in it. */ bool deletable ; void (*bufferDeletedCbk)(void *); void *bufferDeletedCbkData; struct buffer_s *next ; struct buffer_s *prev ; }; #define BUFFER_POOL_SIZE ((4096 - 2 * (sizeof (void *))) / (sizeof (struct buffer_s))) static void fillBufferPool (void) { unsigned int i ; bufferPool = xmalloc (sizeof(struct buffer_s) * BUFFER_POOL_SIZE) ; for (i = 0; i < BUFFER_POOL_SIZE - 1; i++) bufferPool[i] . next = &(bufferPool [i + 1]) ; bufferPool [BUFFER_POOL_SIZE-1] . next = NULL ; } Buffer newBuffer (size_t size) { Buffer nb ; if (bufferPool == NULL) fillBufferPool() ; nb = bufferPool; ASSERT (nb != NULL) ; bufferPool = bufferPool->next ; nb->refCount = 1 ; nb->mem = xmalloc (size + 1) ; nb->mem [size] = '\0' ; nb->memSize = size ; nb->dataSize = 0 ; nb->deletable = true ; nb->bufferDeletedCbk = NULL; nb->bufferDeletedCbkData = NULL; bufferByteCount += size + 1 ; bufferCount++ ; nb->next = gBufferList ; nb->prev = NULL; if (gBufferList != NULL) gBufferList->prev = nb ; gBufferList = nb ; #if 0 d_printf (1,"Creating a DELETABLE buffer %p\n",nb) ; #endif return nb ; } Buffer newBufferByCharP (const char *ptr, size_t size, size_t dataSize) { Buffer nb ; if (bufferPool == NULL) fillBufferPool() ; nb = bufferPool; ASSERT (nb != NULL) ; bufferPool = bufferPool->next ; nb->refCount = 1 ; nb->mem = (char *) ptr ; /* cast away const */ nb->memSize = size ; nb->dataSize = dataSize ; nb->deletable = false ; nb->bufferDeletedCbk = NULL; nb->bufferDeletedCbkData = NULL; nb->next = gBufferList ; nb->prev = NULL; if (gBufferList != NULL) gBufferList->prev = nb ; gBufferList = nb ; bufferCount++ ; #if 0 d_printf (1,"Creating a NON-DELETABLE buffer %p\n",nb) ; #endif return nb ; } void delBuffer (Buffer buff) { if (buff != NULL && --(buff->refCount) == 0) { #if 0 d_printf (1,"Freeing a %s buffer (%p)\n", (buff->deletable ? "DELETABLE" : "NON-DELETABLE"), buff) ; #endif bufferCount-- ; if (buff->deletable) { bufferByteCount -= (buff->memSize + 1) ; free (buff->mem) ; buff->mem = NULL ; } if (buff->bufferDeletedCbk) { (buff->bufferDeletedCbk)(buff->bufferDeletedCbkData); buff->bufferDeletedCbk = NULL; buff->bufferDeletedCbkData = NULL; } if (buff->next != NULL) buff->next->prev = buff->prev ; if (buff->prev != NULL) buff->prev->next = buff->next ; else { ASSERT(gBufferList == buff) ; gBufferList = buff->next ; } buff->next = bufferPool ; bufferPool = buff ; } } Buffer bufferTakeRef (Buffer buff) { ASSERT (buff != NULL) ; if (buff != NULL) buff->refCount++ ; return buff ; } void gPrintBufferInfo (FILE *fp, unsigned int indentAmt) { Buffer b ; char indent [INDENT_BUFFER_SIZE] ; unsigned int i ; for (i = 0 ; i < MIN(INDENT_BUFFER_SIZE - 1,indentAmt) ; i++) indent [i] = ' ' ; indent [i] = '\0' ; fprintf (fp,"%sGlobal Buffer List : (count %d) {\n",indent,bufferCount) ; for (b = gBufferList ; b != NULL ; b = b->next) printBufferInfo (b,fp,indentAmt + INDENT_INCR) ; fprintf (fp,"%s}\n",indent) ; } void printBufferInfo (Buffer buffer, FILE *fp, unsigned int indentAmt) { char indent [INDENT_BUFFER_SIZE] ; char bufferStart [256] ; unsigned int i ; for (i = 0 ; i < MIN(INDENT_BUFFER_SIZE - 1,indentAmt) ; i++) indent [i] = ' ' ; indent [i] = '\0' ; fprintf (fp,"%sBuffer : %p {\n",indent,(void *) buffer) ; if (buffer == NULL) { fprintf (fp,"%s}\n",indent) ; return ; } i = MIN(sizeof (bufferStart) - 1,buffer->dataSize) ; memcpy (bufferStart,buffer->mem,i) ; bufferStart[i] = '\0'; fprintf (fp,"%s refcount : %d\n",indent,buffer->refCount) ; fprintf (fp,"%s data-size : %ld\n",indent,(long) buffer->dataSize) ; fprintf (fp,"%s mem-size : %ld\n",indent,(long) buffer->memSize) ; fprintf (fp,"%s base : %p\n", indent,(void *) buffer->mem) ; fprintf (fp,"%s deletable : %s\n",indent,boolToString(buffer->deletable)); fprintf (fp,"%s buffer [0:%ld] : \"%s\"\n", indent, (long) i, bufferStart) ; fprintf (fp,"%s}\n",indent) ; } void *bufferBase (Buffer buff) { return buff->mem ; } size_t bufferSize (Buffer buff) { return buff->memSize ; } size_t bufferDataSize (Buffer buff) { return buff->dataSize ; } void bufferIncrDataSize (Buffer buff, size_t size) { if (buff->dataSize + size > buff->memSize) die ("Trying to make a buffer data size bigger than its memory alloc"); else buff->dataSize += size ; } void bufferDecrDataSize (Buffer buff, size_t size) { ASSERT (size > buff->dataSize) ; buff->dataSize -= size ; } void bufferSetDataSize (Buffer buff, size_t size) { buff->dataSize = size ; ASSERT (buff->dataSize <= buff->memSize) ; } void bufferSetDeletedCbk (Buffer buff, void (*cbk)(void *), void *data) { ASSERT(buff->bufferDeletedCbk == NULL && buff->bufferDeletedCbk != cbk); ASSERT(buff->bufferDeletedCbkData == NULL && buff->bufferDeletedCbkData != data); buff->bufferDeletedCbk = cbk; buff->bufferDeletedCbkData = data; } void freeBufferArray (Buffer *buffs) { Buffer *b = buffs ; while (b && *b) { delBuffer (*b) ; b++ ; } if (buffs) free (buffs) ; } /* Allocate an array and put all the arguments (the last of which must be NULL) into it. The terminating NULL is put in the returned array. */ Buffer *makeBufferArray (Buffer buff, ...) { va_list ap ; size_t cLen = 10, idx = 0 ; Buffer *ptr, p ; ptr = xcalloc (cLen, sizeof(Buffer)) ; ptr [idx++] = buff ; va_start (ap, buff) ; do { p = va_arg (ap, Buffer) ; if (idx == cLen) { cLen += 10 ; ptr = xrealloc (ptr, sizeof(Buffer) * cLen) ; } ptr [idx++] = p ; } while (p != NULL) ; va_end (ap) ; return ptr ; } bool isDeletable (Buffer buff) { return buff->deletable ; } /* Dups the array including taking out references on the Buffers inside */ Buffer *dupBufferArray (Buffer *array) { Buffer *newArr ; int count = 0 ; while (array && array [count] != NULL) count++ ; newArr = xmalloc (sizeof(Buffer) * (count + 1)) ; for (count = 0 ; array [count] != NULL ; count++) newArr [count] = bufferTakeRef (array [count]) ; newArr [count] = NULL ; return newArr ; } unsigned int bufferArrayLen (Buffer *array) { unsigned int count = 0 ; if (array != NULL) while (*array != NULL) { count++ ; array++ ; } return count ; } bool copyBuffer (Buffer dest, Buffer src) { char *baseDest = bufferBase (dest) ; char *baseSrc = bufferBase (src) ; unsigned int amt = bufferDataSize (src) ; if (amt > bufferSize (dest)) return false ; memcpy (baseDest, baseSrc, amt) ; bufferSetDataSize (dest,amt) ; return true ; } unsigned int bufferRefCount (Buffer buf) { return buf->refCount ; } void bufferAddNullByte (Buffer buff) { char *p = bufferBase (buff) ; p [buff->dataSize] = '\0' ; } /* append the src buffer to the dest buffer growing the dest as needed. Can only be done to deletable buffers. */ bool concatBuffer (Buffer dest, Buffer src) { ASSERT (dest->deletable) ; if ( !dest->deletable ) return false ; /* yeah, i know this is taken care of above */ if ((dest->dataSize + src->dataSize) > dest->memSize) { char *newMem = xcalloc (dest->dataSize + src->dataSize + 1, 1) ; bufferByteCount += ((dest->dataSize + src->dataSize) - dest->memSize) ; memcpy (newMem, dest->mem, dest->dataSize) ; ASSERT (dest->mem != NULL) ; free (dest->mem) ; dest->mem = newMem ; dest->memSize = dest->dataSize + src->dataSize ; /* yep. 1 less */ } memcpy (&dest->mem[dest->dataSize], src->mem, dest->dataSize) ; dest->dataSize += src->dataSize ; return true ; } /* realloc the buffer's memory to increase the size by AMT */ bool expandBuffer (Buffer buff, size_t amt) { d_printf (2,"Expanding buffer....\n") ; if (!buff->deletable) return false ; bufferByteCount += amt ; buff->memSize += amt ; buff->mem = xrealloc (buff->mem, buff->memSize + 1) ; return true ; } /* Take a buffer and shift the contents around to add the necessary CR before every line feed and a '.' before every '.' at the start of a line. */ bool nntpPrepareBuffer (Buffer buffer) { int msize, newDsize, dsize, extra ; char *base, p, *src, *dst ; bool needfinal = false ; ASSERT (buffer != NULL) ; dsize = buffer->dataSize ; msize = buffer->memSize - 1 ; base = buffer->mem ; extra = 3 ; p = '\0' ; for (src = base + dsize - 1 ; src > base ; ) { if (*src == '\n') { extra++ ; if (p == '.') extra++ ; } p = *src-- ; } if (*src == '\n') { extra++ ; if (p == '.') extra++ ; } if (dsize > 0 && base [dsize - 1] != '\n') { needfinal = true ; extra += 2 ; } newDsize = dsize + extra ; if (msize - dsize < extra) { d_printf (2,"Expanding buffer in nntpPrepareBuffer (from %d to %d)\n", msize, msize + (extra - (msize - dsize))) ; if ( !expandBuffer (buffer, extra - (msize - dsize)) ) { d_printf (1,"Expand failed...\n") ; return false ; } ASSERT (dsize == (int) buffer->dataSize) ; base = buffer->mem ; } base [newDsize] = '\0' ; base [newDsize - 1] = '\n' ; base [newDsize - 2] = '\r' ; base [newDsize - 3] = '.' ; newDsize -= 3 ; extra -= 3 ; if (needfinal) { base [newDsize - 1] = '\n' ; base [newDsize - 2] = '\r' ; newDsize -= 2 ; extra -= 2 ; } if (extra) { p = '\0'; src = base + dsize - 1 ; dst = base + newDsize - 1 ; while (1) { if (*src == '\n') { if (p == '.') { *dst-- = '.' ; extra-- ; } *dst-- = '\n' ; *dst = '\r' ; if (--extra <= 0) break ; p = '\0' ; dst-- ; src-- ; } else p = *dst-- = *src-- ; } ASSERT(dst >= base && src >= base) ; } newDsize += 3; if (needfinal) newDsize += 2 ; bufferSetDataSize (buffer,newDsize) ; return true ; } inn-2.6.0/innfeed/main.c0000644000175200017520000006275512575023702014437 0ustar iuliusiulius/* $Id: main.c 9926 2015-08-08 17:08:02Z iulius $ ** ** Main routines for the innfeed program. ** ** Written by James Brister */ #include "innfeed.h" #include "config.h" #include "clibrary.h" #include "portable/socket.h" #include #include #include #include #include #include #include #include #include #ifdef HAVE_SYS_TIME_H # include #endif #include #if defined(HAVE_UNIX_DOMAIN_SOCKETS) # include "portable/socket-unix.h" #endif #include "inn/innconf.h" #include "inn/messages.h" #include "inn/version.h" #include "inn/libinn.h" #include "inn/storage.h" #include "article.h" #include "buffer.h" #include "configfile.h" #include "connection.h" #include "endpoint.h" #include "host.h" #include "innlistener.h" #include "misc.h" #include "tape.h" #define INHERIT 1 #define NO_INHERIT 0 /* exports */ bool talkToSelf ; extern int debugWrites ; bool sigFlag = false ; const char *InputFile ; char *configFile = NULL ; bool RollInputFile = false ; char *pidFile = NULL ; bool useMMap = true ; /* Even if !HAVE_MMAP. Only used when innfeed is given * file names to send instead of storage API tokens, * which is a fairly rare use case. */ void (*gPrintInfo) (void) ; char *dflTapeDir; /* these are used by imapfeed */ char *deliver_username = NULL; char *deliver_authname = NULL; char *deliver_password = NULL; char *deliver_realm = NULL; const char *deliver_rcpt_to = "+%s"; char *deliver_to_header = NULL; /* imports */ #if defined (sun) extern char *optarg ; /* needed for Solaris */ extern int optind; #endif extern bool genHtml ; extern bool debugShrinking; extern bool fastExit; extern unsigned int stdioFdMax; extern void openInputFile (void); /* privates */ static char *logFile ; static char *newsspool ; static void sigemt (int sig) ; static void sigalrm (int sig) ; static void sigchld (int sig) ; static void sigint (int sig) ; static void sigquit (int sig) ; static void sighup (int sig) ; static void sigterm (int sig) ; static void sigusr (int sig) ; static void usage (int) ; static void gprintinfo (void) ; static void openLogFile (void) ; static void writePidFile (void) ; static int mainOptionsProcess (void *data) ; static int mainConfigLoadCbk (void *data) ; static void mainCleanup (void) ; static char *bopt = NULL ; static char *aopt = NULL ; static char *popt = NULL ; static bool Mopt = false ; static bool Zopt = false ; static bool Dopt = false ; static int debugLevel = 0 ; static unsigned int initialSleep = 2 ; static char *sopt = NULL ; static char *lopt = NULL ; static bool eopt = false ; static int elimit = 0 ; int main (int argc, char **argv) { EndPoint ep ; InnListener listener ; int optVal, fd, rval ; const char *subProgram = NULL ; bool seenV = false ; bool dynamicPeers = false ; time_t now = theTime() ; char dateString [30] ; char *copt = NULL ; char *debugFile; bool checkConfig = false ; bool val; timeToString (now, dateString, sizeof (dateString)) ; message_program_name = strrchr (argv [0],'/'); if (message_program_name == NULL) message_program_name = argv [0] ; else message_program_name++; gPrintInfo = gprintinfo ; openlog (message_program_name,(int)(L_OPENLOG_FLAGS|LOG_PID),LOG_INN_PROG) ; if (!innconf_read(NULL)) { syslog(LOG_ERR, "cant read inn.conf\n"); exit(1); } message_handlers_die (2, error_log_stderr_date, message_log_syslog_err) ; message_handlers_warn (1, message_log_syslog_warning); message_handlers_notice (1, message_log_syslog_notice) ; #define OPT_STRING "a:b:c:Cd:e:hl:mMo:p:S:s:vxyz" while ((optVal = getopt (argc,argv,OPT_STRING)) != EOF) { switch (optVal) { case 'a': aopt = optarg ; break ; case 'b': if ( !isDirectory (optarg) ) logAndExit (1,"Not a directory: %s\n",optarg) ; bopt = optarg ; break ; case 'C': checkConfig = true ; break ; case 'c': copt = optarg ; break ; case 'd': loggingLevel = atoi (optarg) ; debugLevel = loggingLevel ; Dopt = true ; break ; case 'e': eopt = true ; elimit = atoi (optarg) ; if (elimit <= 0) { fprintf (stderr,"Illegal value for -e option\n") ; usage (1) ; } break ; case 'h': usage (0) ; case 'l': lopt = optarg ; break ; case 'M': Mopt = true ; useMMap = false ; break ; case 'm': artLogMissingArticles (true) ; break ; case 'o': artSetMaxBytesInUse (atoi (optarg)) ; break ; case 'p': popt = optarg ; break ; case 's': subProgram = optarg ; break ; case 'S': sopt = optarg ; break ; case 'v': seenV = true ; break ; case 'x': talkToSelf = true ; break ; case 'y': dynamicPeers = true ; break ; case 'z': Zopt = true ; break ; default: usage (1) ; } } argc -= optind; argv += optind; if (argc > 1) usage (1) ; else if (argc == 1) InputFile = *argv; if (seenV) { printf ("%s version: %s\n", message_program_name, INN_VERSION_STRING); exit (0) ; } /* make sure we have valid fds 0, 1 & 2 so it is not taken by something else, probably openlog(). fd 0 will be freopen()ed on the inputFile, the subProgram, or ourself. fd 1 and fd 2 will be freopen()ed on the log file (or will stay pointed at /dev/null). without doing this, if the descriptors were closed then the freopen calls on some systems (like BSDI 2.1) will really close whatever has aquired the stdio descriptors, such as the socket to syslogd. XXX possible problems: what if fd 0 is closed but no inputFile, XXX subProgram or talkToSelf is true? it will not be freopen()ed, so XXX innfeed won't have any fresh data (besides, fd 0 is only writable XXX here). perhaps a warning should be issued. */ do { fd = open("/dev/null", O_WRONLY); switch (fd) { case -1: logAndExit (1,"open(\"/dev/null\", O_WRONLY): %s", strerror (errno)); break; case 0: case 1: case 2: /* good, we saved an fd from being trounced */ break; default: close(fd); } } while (fd < 2); if ( !checkConfig ) { notice("ME starting at %s (%s)", dateString, INN_VERSION_STRING); } val = true; if (!SMsetup(SM_PREOPEN, (void *)&val)) { syslog(LOG_ERR, "cant setup the storage subsystem\n"); exit(1); } if (!SMinit()) { d_printf(0, "Storage manager initialization failed -- it is OK after a change in storage methods\n"); syslog(LOG_ERR, "Storage manager initialization failed -- it is OK after a change in storage methods\n"); exit(1); } if (subProgram == NULL && talkToSelf == false) { struct stat buf ; if (fstat (0,&buf) < 0) logAndExit (1,"ME oserr fstat stdin: %s", strerror (errno)) ; else if (S_ISREG (buf.st_mode)) InputFile = ""; } /* * set up the config file name and then read the file in. Order is important. */ configAddLoadCallback (mainOptionsProcess,(checkConfig ? stderr : NULL)) ; configAddLoadCallback (tapeConfigLoadCbk,(checkConfig ? stderr : NULL)) ; configAddLoadCallback (endpointConfigLoadCbk,(checkConfig ? stderr : NULL)); configAddLoadCallback (hostConfigLoadCbk,(checkConfig ? stderr : NULL)) ; configAddLoadCallback (cxnConfigLoadCbk,(checkConfig ? stderr : NULL)) ; configAddLoadCallback (mainConfigLoadCbk,(checkConfig ? stderr : NULL)) ; configAddLoadCallback (listenerConfigLoadCbk,(checkConfig ? stderr : NULL)); if (copt != NULL && *copt == '\0') { logOrPrint (LOG_CRIT,(checkConfig ? stderr : NULL), "Empty pathname for ``-c'' option") ; exit (1) ; } configFile = concatpath(innconf->pathetc, copt ? copt : CONFIG_FILE); dflTapeDir = concatpath(innconf->pathspool, TAPE_DIRECTORY); rval = readConfig (configFile,(checkConfig ? stderr : NULL), checkConfig,loggingLevel > 0); if (subProgram != NULL && (talkToSelf == true || InputFile)) { d_printf (0,"Cannot specify '-s' with '-x' or an input file\n") ; syslog (LOG_ERR,"Incorrect arguments: '-s' with '-x' or an input file\n"); usage (1) ; } if (checkConfig) { if (!rval) { fprintf (stderr,"config loading failed.\n") ; exit (1) ; } else { fprintf (stderr,"config loading succeeded.\n") ; exit (0) ; } } else if (!rval) exit (1) ; debugFile = concatpath(innconf->pathlog, DEBUG_FILE); if (loggingLevel == 0 && fileExistsP (debugFile)) loggingLevel = 1 ; free(debugFile); if (logFile == NULL && ! isatty (fileno (stderr))) logFile = concatpath(innconf->pathlog, LOG_FILE); if (logFile) openLogFile () ; openfds = 4 ; /* stdin, stdout, stderr and syslog */ writePidFile (); if (subProgram != NULL) { int fds [2] ; int pid ; if (pipe (fds) < 0) sysdie ("ME fatal pipe") ; if ((pid = fork ()) < 0) { sysdie ("ME fatal fork") ; } else if (pid == 0) { /* child */ close (fds[0]) ; close (0) ; close (1) ; close (2) ; dup2 (fds[1],1) ; dup2 (fds[1],2) ; execlp ("sh", "sh", "-c", subProgram, (char *) 0) ; perror ("execlp") ; exit (1) ; } else { /* parent */ close (0) ; dup2 (fds[0],0) ; close (fds[1]) ; xsignal(SIGCHLD,sigchld) ; openfds++ ; } } else if (talkToSelf) { /* We're not really getting information from innd or a subprogram, but are just processing backlog files. We set up a pipe to ourself that we never write to, to simulate an idle innd. */ int pipefds [2] ; if (pipe (pipefds) != 0) sysdie ("ME fatal pipe") ; close (0) ; dup2 (pipefds [0], 0) ; openfds++ ; openfds++ ; } if (chdir (newsspool) != 0) sysdie ("ME fatal chdir %s", newsspool) ; /* hook up the endpoint to the source of new article information (usually innd). */ ep = newEndPoint (0) ; /* fd 0, i.e. stdin */ /* now arrange for this endpoint to always be the first one checked for possible activity. */ setMainEndPoint (ep) ; listener = newListener (ep, talkToSelf,dynamicPeers) ; mainListener = listener ; sleep (initialSleep) ; if (innconf->rlimitnofile >= 0) if (setfdlimit (innconf->rlimitnofile) < 0) syswarn ("ME oserr setrlimit(RLIM_NOFILE,%ld)", innconf->rlimitnofile) ; if (innconf->timer != 0) TMRinit (TMR_MAX) ; configHosts (talkToSelf) ; if (InputFile && *InputFile) { openInputFile () ; } /* handle signal to shutdown */ setSigHandler (SIGTERM,sigterm) ; setSigHandler (SIGQUIT,sigquit) ; /* handle signal to reload config */ setSigHandler (SIGHUP,sighup) ; /* handle signal to print snapshot. */ setSigHandler (SIGINT,sigint) ; /* handle signal to roll input file */ setSigHandler (SIGALRM,sigalrm) ; /* handle signal to flush all the backlog files */ setSigHandler (SIGCHLD,sigemt) ; /* we can increment and decrement logging levels by sending SIGUSR{1,2} */ setSigHandler (SIGUSR1,sigusr) ; setSigHandler (SIGUSR2,sigusr) ; atexit (mainCleanup) ; Run () ; exit (0) ; } static void usage (int val) { fprintf (stderr,"usage: %s [ options ] [ file ]\n\n", message_program_name) ; fprintf (stderr,"Version: %s\n\n",INN_VERSION_STRING) ; fprintf (stderr,"Config file: %s\n",CONFIG_FILE) ; fprintf (stderr,"Backlog directory: %s/%s\n", innconf->pathspool, TAPE_DIRECTORY) ; fprintf (stderr,"\nLegal options are:\n") ; fprintf (stderr,"\t-a dir Use the given directory as the top of the article spool\n") ; fprintf (stderr,"\t-b dir Use the given directory as the the storage\n"); fprintf (stderr,"\t place for backlog files and lock files\n"); fprintf (stderr,"\t-c file Use the given file as the config file instead of the\n"); fprintf (stderr,"\t default of %s\n",CONFIG_FILE); fprintf (stderr,"\t-C Check the config file and then exit\n") ; fprintf (stderr,"\t-d num Set the logging level to num (an integer).\n"); fprintf (stderr,"\t Larger value means more logging. 0 means no\n"); fprintf (stderr,"\t logging. The default is 0\n"); fprintf (stderr,"\t-e bytes Keep the output backlog files to no bigger\n"); fprintf (stderr,"\t than %.2f times this number\n",LIMIT_FUDGE); fprintf (stderr,"\t-h Print this message\n"); fprintf (stderr,"\t-l file Redirect stderr and stdout to the given file.\n"); fprintf (stderr,"\t When run under INN, they normally are redirected to\n"); fprintf (stderr,"\t /dev/null. This is needed if using '-d'\n"); fprintf (stderr,"\t-m Log information on all missing articles\n"); fprintf (stderr,"\t-M Turn *off* use of mmap\n") ; #if ! defined (HAVE_MMAP) fprintf (stderr,"\t (a no-op as this excutable has been built without mmap support)\n") ; #endif fprintf (stderr,"\t-o bytes Set a limit for the maximum number of bytes of article\n"); fprintf (stderr,"\t data innfeed is supposed to keep in memory\n"); fprintf (stderr,"\t-p file Write the process id to the given file\n") ; fprintf (stderr,"\t instead of the default of %s;\n",PID_FILE); fprintf (stderr,"\t a relative path is relative to %s\n", innconf->pathrun) ; fprintf (stderr,"\t-s command Run the given command in a subprocess and use\n"); fprintf (stderr,"\t its output as article information instead of\n"); fprintf (stderr,"\t running under innd\n"); fprintf (stderr,"\t-S file Use the given filename instead of innfeed.status;\n") ; fprintf (stderr,"\t relative path names start from %s\n", innconf->pathlog) ; fprintf (stderr,"\t-v Print version information\n"); fprintf (stderr,"\t-x Do not read any article information off stdin,\n"); fprintf (stderr,"\t but simply process backlog files and then exit\n"); fprintf (stderr,"\t when done\n"); fprintf (stderr,"\t-y Add peers dynamically. If an unrecognized peer name\n"); fprintf (stderr,"\t is received from innd, then it is presumed to also\n"); fprintf (stderr,"\t be the IP name and a new peer binding is set up\n"); fprintf (stderr,"\t-z Have each of the connections issue their own stats\n"); fprintf (stderr,"\t whenever they close, or whenever their controller\n"); fprintf (stderr,"\t issues its own stats\n"); exit (val) ; } static void sigterm (int sig UNUSED) { notice ("ME received shutdown signal") ; shutDown (mainListener) ; } static void sigquit (int sig UNUSED) { sigterm (0) ; } static void sigint (int sig UNUSED) { gprintinfo () ; } static void sighup (int sig UNUSED) { notice ("ME reloading config file %s", configFile) ; if (!readConfig (configFile,NULL,false,loggingLevel > 0)) { die ("ME config aborting, error parsing config file") ; } configHosts (talkToSelf) ; notice ("ME reloading log file %s", logFile) ; openLogFile() ; } static void sigemt (int sig UNUSED) { gFlushTapes () ; } static void sigalrm (int sig UNUSED) { if (InputFile == NULL) warn ("ME signal SIGALRM in non-funnel-file mode ignored") ; else { RollInputFile = true; syslog(LOG_NOTICE, "ME preparing to roll %s", InputFile); } } static void sigchld (int sig UNUSED) { #if 0 wait (&status) ; /* we don't care */ #endif xsignal (sig,sigchld) ; } /* SIGUSR1 increments logging level. SIGUSR2 decrements. */ static void sigusr (int sig) { if (sig == SIGUSR1) { loggingLevel++ ; notice ("ME increasing logging level to %d", loggingLevel) ; } else if (sig == SIGUSR2 && loggingLevel > 0) { loggingLevel-- ; notice ("ME decreasing logging level to %d", loggingLevel) ; } } static void openLogFile (void) { FILE *fpr ; if (logFile) { fpr = freopen (logFile,"a",stdout) ; if (fpr != stdout) logAndExit (1,"freopen (%s, \"a\", stdout): %s", logFile, strerror (errno)) ; fpr = freopen (logFile,"a",stderr) ; if (fpr != stderr) logAndExit (1,"freopen (%s, \"a\", stderr): %s", logFile, strerror (errno)) ; #if defined (HAVE_SETBUFFER) setbuffer (stdout, NULL, 0) ; setbuffer (stderr, NULL, 0) ; #else setbuf (stdout, NULL) ; setbuf (stderr, NULL) ; #endif } } static void writePidFile (void) { FILE *F; int pid; if (pidFile == NULL) logAndExit (1,"NULL pidFile\n") ; /* Record our PID. */ pid = getpid(); if ((F = fopen(pidFile, "w")) == NULL) { syslog(LOG_ERR, "ME cant fopen %s %m", pidFile); } else { if (fprintf(F, "%ld\n", (long)pid) == EOF || ferror(F)) { syslog(LOG_ERR, "ME cant fprintf %s %m", pidFile); } if (fclose(F) == EOF) { syslog(LOG_ERR, "ME cant fclose %s %m", pidFile); } if (chmod(pidFile, 0664) < 0) { syslog(LOG_ERR, "ME cant chmod %s %m", pidFile); } } } static void gprintinfo (void) { char *snapshotFile; FILE *fp; char nowString[30]; time_t now = theTime() ; snapshotFile = concatpath(innconf->pathlog, SNAPSHOT_FILE); fp = fopen (snapshotFile,"a") ; if (fp == NULL) { syswarn ("ME fopen %s", snapshotFile) ; free(snapshotFile); return ; } free(snapshotFile); #if defined (HAVE_SETBUFFER) setbuffer (fp, NULL, 0) ; #else setbuf (fp, NULL) ; #endif timeToString (now, nowString, sizeof (nowString)) ; fprintf (fp,"----------------------------System snaphot taken at: %s\n\n", nowString) ; gPrintListenerInfo (fp,0) ; fprintf (fp,"\n\n\n\n") ; gPrintHostInfo (fp,0) ; fprintf (fp,"\n\n\n\n") ; gPrintCxnInfo (fp,0) ; fprintf (fp,"\n\n\n\n") ; gPrintArticleInfo (fp,0) ; fprintf (fp,"\n\n\n\n") ; gPrintBufferInfo (fp,0) ; fprintf (fp,"\n\n\n\n") ; fclose (fp) ; } /* called after the config file is loaded and after the config data has been updated with command line options. */ static int mainConfigLoadCbk (void *data) { FILE *fp = (FILE *) data ; char *p ; long ival ; int bval ; if (getString (topScope,"news-spool", &p,NO_INHERIT)) { if ( !isDirectory (p) && isDirectory (innconf->patharticles) ) { logOrPrint (LOG_WARNING,fp, "ME config: definition of news-spool (%s) is a" " non-existant directory. Using %s",p, innconf->patharticles) ; p = xstrdup (innconf->patharticles) ; } else if (!isDirectory (p)) logAndExit (1,"Bad spool directories: %s, %s\n",p,innconf->patharticles) ; } else if (!isDirectory (innconf->patharticles)) logAndExit (1,"ME config: no definition of news-spool, and %s is no good", innconf->patharticles); else p = xstrdup (innconf->patharticles) ; newsspool = p ; /***************************************************/ if (getString (topScope,"input-file",&p,NO_INHERIT)) { if (*p != '\0') InputFile = concatpath(getTapeDirectory(), p); else InputFile = "" ; free (p) ; } if (getString (topScope,"pid-file",&p,NO_INHERIT)) { pidFile = concatpath(innconf->pathrun, p); free (p) ; } else pidFile = concatpath(innconf->pathrun, PID_FILE); if (getInteger (topScope,"debug-level",&ival,NO_INHERIT)) loggingLevel = (unsigned int) ival ; if (getInteger (topScope,"initial-sleep",&ival,NO_INHERIT)) initialSleep = (unsigned int) ival ; if (getBool (topScope,"use-mmap",&bval,NO_INHERIT)) useMMap = (bval ? true : false) ; if (getString (topScope,"log-file",&p,NO_INHERIT)) { logFile = concatpath(innconf->pathlog, p); free (p) ; } if (getString (topScope,"log-time-format",&p,NO_INHERIT)) { free(timeToStringFormat); timeToStringFormat = p; } /* For imap/lmtp delivering */ if (getString (topScope,"deliver-username",&p, NO_INHERIT)) { deliver_username = p; /* don't need to free */ } if (getString (topScope,"deliver-authname",&p, NO_INHERIT)) { deliver_authname = p; /* don't need to free */ } if (getString (topScope,"deliver-password",&p, NO_INHERIT)) { deliver_password = p; /* don't need to free */ } if (getString (topScope,"deliver-realm",&p, NO_INHERIT)) { deliver_realm = p; /* don't need to free */ } if (getString (topScope,"deliver-rcpt-to",&p, NO_INHERIT)) { deliver_rcpt_to = p; /* don't need to free */ } if (getString (topScope,"deliver-to-header",&p, NO_INHERIT)) { deliver_to_header = p; /* don't need to free */ } return 1 ; } /* * called after config file is loaded but before other callbacks, so we * can adjust config file values from options. They will be validated in the * second callback. */ static int mainOptionsProcess (void *data UNUSED) { value *v ; if (bopt != NULL) { if ((v = findValue (topScope,"backlog-directory",NO_INHERIT)) != NULL) { free (v->v.charp_val) ; v->v.charp_val = xstrdup (bopt) ; } else addString (topScope,"backlog-directory",xstrdup (bopt)) ; } if (aopt != NULL) { if ((v = findValue (topScope,"news-spool",NO_INHERIT)) != NULL) { free (v->v.charp_val) ; v->v.charp_val = xstrdup (aopt) ; } else addString (topScope,"news-spool",xstrdup (aopt)) ; } if (sopt != NULL) { if ((v = findValue (topScope,"status-file",NO_INHERIT)) != NULL) { free (v->v.charp_val) ; v->v.charp_val = xstrdup (sopt) ; } else addString (topScope,"status-file",xstrdup (sopt)) ; } if (Dopt) { if ((v = findValue (topScope,"debug-level",NO_INHERIT)) != NULL) v->v.int_val = debugLevel ; else addInteger (topScope,"debug-level",debugLevel) ; } if (eopt || talkToSelf) { if (talkToSelf) elimit = 0 ; if ((v = findValue (topScope,"backlog-limit",NO_INHERIT)) != NULL) v->v.int_val = elimit ; else addInteger (topScope,"backlog-limit",elimit) ; } if (Mopt) { if ((v = findValue (topScope,"use-mmap",NO_INHERIT)) != NULL) v->v.bool_val = 0 ; else addBoolean (topScope,"use-mmap",0) ; } if (popt != NULL) { if ((v = findValue (topScope,"pid-file",NO_INHERIT)) != NULL) { free (v->v.charp_val) ; v->v.charp_val = xstrdup (popt) ; } else addString (topScope,"pid-file",xstrdup (popt)) ; } if (Zopt) { if ((v = findValue (topScope,"connection-stats",NO_INHERIT)) != NULL) v->v.bool_val = 1 ; else addBoolean (topScope,"connection-stats",1) ; } if (lopt != NULL) { if ((v = findValue (topScope,"log-file",NO_INHERIT)) != NULL) { free (v->v.charp_val) ; v->v.charp_val = xstrdup (lopt) ; } else addString (topScope,"log-file",xstrdup (lopt)) ; } if (InputFile != NULL) { if ((v = findValue (topScope,"input-file",NO_INHERIT)) != NULL) { free (v->v.charp_val) ; v->v.charp_val = xstrdup (InputFile) ; } else addString (topScope,"input-file",xstrdup (InputFile)) ; } return 1 ; } static void mainCleanup (void) { free ((void *)configFile) ; free ((void *)pidFile) ; free (logFile) ; free (newsspool) ; configFile = NULL ; pidFile = NULL ; logFile = NULL ; newsspool = NULL ; } void mainLogStatus (FILE *fp) { fprintf (fp,"%sGlobal configuration parameters:%s\n", genHtml ? "" : "", genHtml ? "" : "") ; fprintf (fp," Mode: ") ; if (InputFile != NULL) fprintf (fp,"Funnel file") ; else if (talkToSelf) fprintf (fp,"Batch") ; else fprintf (fp,"Channel") ; if (InputFile != NULL) fprintf (fp," (%s)",(*InputFile == '\0' ? "stdin" : InputFile)) ; fprintf (fp,"\n") ; fprintf (fp," News spool: %s\n",newsspool) ; fprintf (fp," Pid file: %s\n",pidFile) ; fprintf (fp," Log file: %s\n",(logFile == NULL ? "(none)" : logFile)); fprintf (fp," Debug level: %-5u Debug shrinking: %s\n", loggingLevel, boolToString(debugShrinking)); fprintf (fp," Fast exit: %-5s stdio-fdmax: %d\n", boolToString(fastExit), stdioFdMax); fprintf (fp," Mmap: %s\n", boolToString(useMMap)); fprintf (fp,"\n") ; } inn-2.6.0/innfeed/connection.h0000644000175200017520000001067012575023702015644 0ustar iuliusiulius/* $Id: connection.h 8276 2009-01-08 19:15:52Z iulius $ ** ** The public interface to the Connection class. ** ** Written by James Brister ** ** The Connection class encapulates an NNTP protocol endpoint (either regular ** or extended with the streaming protocol). Each Connection is owned by a ** single Host object. ** ** It manages the network connection (via an EndPoint) the the pumping of ** articles to the remote host. It gets these articles from its Host object. ** If the remote doesn't handle the streaming extension, then the Connection ** will only manage one article at a time. If the remote handles the ** extension, then the connection will queue up articles while sending the ** CHECK and TAKETHIS commands. ** ** If the network connection drops while the Connection object has articles ** queued up, then it will hand them back to its Host object. */ #if ! defined ( connection_h__ ) #define connection_h__ #include #include #include "misc.h" /* * Create a new Connection. * * HOST is the host object we're owned by. * IDENT is an identifier to be added to syslog entries so we can tell * what's happening on different connections to the same peer. * IPNAME is the name (or ip address) of the remote) * MAXTOUT is the maximum amount of time to wait for a response before * considering the remote host dead. * PORTNUM is the portnum to contact on the remote end. * RESPTIMEOUT is the amount of time to wait for a response from a remote * before considering the connection dead. * CLOSEPERIOD is the number of seconds after connecting that the * connections should be closed down and reinitialized (due to problems * with old NNTP servers that hold history files open. Value of 0 means * no close down. */ Connection newConnection (Host host, unsigned int ident, const char *ipname, unsigned int artTout, unsigned int portNum, unsigned int respTimeout, unsigned int closePeriod, double lowPassLow, double lowPassHigh, double lowPassFilter) ; /* Causes the Connection to build the network connection. */ bool cxnConnect (Connection cxn) ; /* puts the connection into the wait state (i.e. waits for an article before initiating a connect). Can only be called right after newConnection returns, or while the Connection is in the (internal) Sleeping state. */ void cxnWait (Connection cxn) ; /* The Connection will disconnect as if cxnDisconnect were called and then it automatically reconnects to the remote. */ void cxnFlush (Connection cxn) ; /* The Connection sends remaining articles, then issues a QUIT and then deletes itself */ void cxnClose (Connection cxn) ; /* The Connection drops all queueed articles, then issues a QUIT and then deletes itself */ void cxnTerminate (Connection cxn) ; /* Blow away the connection gracelessly and immedately clean up */ void cxnNuke (Connection cxn) ; /* Tells the Connection to take the article and handle its transmission. If it can't (due to queue size or whatever), then the function returns false. The connection assumes ownership of the article if it accepts it (returns true). */ bool cxnTakeArticle (Connection cxn, Article art) ; /* Tell the Connection to take the article (if it can) for later processing. Assumes ownership of it if it takes it. */ bool cxnQueueArticle (Connection cxn, Article art) ; /* generate a syslog message for the connections activity. Called by Host. */ void cxnLogStats (Connection cxn, bool final) ; /* return the number of articles the connection can be given. This lets the host shovel in as many as possible. May be zero. */ size_t cxnQueueSpace (Connection cxn) ; /* adjust the mode no-CHECK filter values */ void cxnSetCheckThresholds (Connection cxn, double lowFilter, double highFilter, double lowPassFilter) ; /* print some debugging info. */ void gPrintCxnInfo (FILE *fp, unsigned int indentAmt) ; void printCxnInfo (Connection cxn, FILE *fp, unsigned int indentAmt) ; /* config file load callback */ int cxnConfigLoadCbk (void *data) ; /* Check connection state is in cxnFeedingS, cxnIdleS or cxnConnectingS. */ bool cxnCheckstate (Connection cxn) ; #endif /* connection_h__ */ inn-2.6.0/innfeed/innfeed.h0000644000175200017520000001536612575023702015124 0ustar iuliusiulius/* $Id: innfeed.h 9923 2015-07-14 16:48:11Z iulius $ ** ** innfeed's configuration values. ** ** Written by James Brister ** ** The application configuration values. This file is #include'd before any ** system header files, so it can't rely on any CPP symbols other that what ** the compiler defines. */ #if ! defined ( innfeed_h__ ) #define innfeed_h__ #include "inn/timer.h" /**********************************************************************/ /* Application specific defines */ /**********************************************************************/ /* the path to the run-time config file. If relative, then relative to @ETCDIR@. Overridden by ``-c'' option. */ #define CONFIG_FILE "innfeed.conf" /* * This next section contains things than can be overridden in the config * file. The strings inside each comment is the key used in the * innfeed.conf file to override the value here. See innfeed.conf for a * description of each./ */ /* in tape.c */ #define TAPE_DIRECTORY "innfeed" /* [pathspool]/backlog-directory */ #define TAPE_HIGHWATER 5 /* backlog-highwater */ #define TAPE_ROTATE_PERIOD 60 /* backlog-rotate-period */ #define TAPE_CHECKPOINT_PERIOD 30 /* backlog-ckpt-period */ #define TAPE_NEWFILE_PERIOD 600 /* backlog-newfile-period */ #define TAPE_DISABLE false /* no-backlog */ /* in main.c */ #define PID_FILE "innfeed.pid" /* [pathrun]/pid-file */ #define LOG_FILE "innfeed.log" /* [pathlog]/log-file */ /* in host.c */ #define DNS_RETRY_PERIOD 900 /* dns-retry */ #define DNS_EXPIRE_PERIOD 86400 /* dns-expire */ #define CLOSE_PERIOD (60 * 60 * 24) /* close-period */ #define GEN_HTML false /* gen-html */ #define INNFEED_STATUS "innfeed.status" /* status-file */ #define LOG_CONNECTION_STATS 0 /* connection-stats */ #define HOST_HIGHWATER 10 /* host-queue-highwater */ #define STATS_PERIOD (60 * 10) /* stats-period */ #define STATS_RESET_PERIOD (60 * 60 * 12) /* stats-reset-period */ #define ARTTOUT 600 /* article-timeout */ #define RESPTOUT 300 /* response-timeout */ #define INIT_CXNS 1 /* initial-connections */ #define MAX_CXNS 2 /* max-connections */ #define MAX_Q_SIZE 20 /* max-queue-size */ #define STREAM true /* streaming */ #define NOCHECKHIGH 95.0 /* no-check-high */ #define NOCHECKLOW 90.0 /* no-check-low */ #define PORTNUM 119 /* port-number */ #define FORCE_IPv4 false /* force using IPv4 */ #define BLOGLIMIT 0 /* backlog-limit */ #define LIMIT_FUDGE 1.10 /* backlog-factor */ #define BLOGLIMIT_HIGH 0 /* backlog-limit-high */ #define INIT_RECON_PER 30 /* initial-reconnect-time */ #define MAX_RECON_PER (60 * 60 * 1)/* max-reconnect-time */ /****************************************************************************/ /* * The rest below are not run-time configurable. */ /* If this file exists at startup then it's the same as having done '-d 1' on the command line. This is a cheap way of avoiding continual reloading of the newsfeeds file when debugging. */ #define DEBUG_FILE "innfeed.debug" /* Relative to pathlog */ /* if defined to a non-zero number, then a snapshot will be printed whenever die() is called (e.g. on assert failure). This can use up a lot of disk space. */ #define SNAPSHOT_ON_DIE 0 /* the full pathname of the file to get a printed dump of the system when a SIGINT is delivered (or SNAPSHOT_ON_DIE is non-zero--see below). */ #define SNAPSHOT_FILE "innfeed.snapshot" /* Relative to pathlog */ /* strings that get added to the end of a peer name for generating backlog file names. A peername cannot end in any of these string (e.g. having a peer called 'mypeer.input' will not work) */ #define OUTPUT_TAIL ".output" #define INPUT_TAIL ".input" #define LOCK_TAIL ".lock" /* rough estimate of average article line length (including headers). Smaller number means more efficient article preparation (for transfer), but, if much smaller than reality, then more memory wastage. */ #define CHARS_PER_LINE 60 /* How many seconds between logging statistics on article allocation. For no logging set to 0 */ #define ARTICLE_STATS_PERIOD (10 * 60) /* 10 minutes */ /* max number of parallel connections to a single remote. This is just a sanity check for the runtime config file. */ #define MAX_CONNECTION_COUNT 50 /* default size in bytes for buffers */ #define BUFFER_SIZE 256 /* amount we expand buffers on partial reads */ #define BUFFER_EXPAND_AMOUNT 128 /* minimum number of seconds between log messages for starting spooling. i.e. if the connection bounces up and down this will prevent frequent logging of the spooling message. 0 turns off this logging. */ #define SPOOL_LOG_PERIOD 600 /* some big numbers just for sanity checking */ #define MAX_MAXCHECKS 10000 /* no more than 10000 articles at a time */ #define MAX_MAXART_TOUT 86400 /* one day max between articles from inn */ #define MAX_RESP_TOUT 3600 /* one hour max to wait for response */ /* the check / no-check filter value, i.e. roughly how many past articles we take into account whilst doing the average for check / no-check mode. Ensure it's a float. */ #define FILTERVALUE 50.0 /* the maximum number of peers we'll handle (not connections) */ #define MAX_HOSTS 100 /* We try to keep article memory allocation below this limit. Doesn't work very well, though. */ #define SOFT_ARTICLE_BYTE_LIMIT (1024 * 1024 * 10) /* 10MB */ /* define SELECT_RATIO to the number of times through the main loop before checking on the fd from inn again.... */ #define SELECT_RATIO 3 #if defined (DBTIMES) /* some small values for testing things. */ #undef STATS_PERIOD #define STATS_PERIOD 30 /* 30 seconds */ #undef STATS_RESET_PERIOD #define STATS_RESET_PERIOD (6 * 60) /* 6 minutes */ #undef ARTICLE_STATS_PERIOD #define ARTICLE_STATS_PERIOD (6 * 60) /* 7 minutes */ #undef CLOSE_PERIOD #define CLOSE_PERIOD (3 * 60) /* 5 minutes */ #endif /* DBTIMES */ /* Additional OS-specific defines. These should really be moved into configure at some point. */ /* Some broken system (all SunOS versions) have a lower limit for the maximum number of stdio files that can be open, than the limit of open file the OS will let you have. If this value is > 0 (and ``stdio-fdmax'' is *not* used in the config file), then all non-stdio file descriptors will be kept above this value (by dup'ing them). */ #if defined (sun) # if defined (__SVR4) # define MAX_STDIO_FD 256 # else # define MAX_STDIO_FD 128 # endif #else # define MAX_STDIO_FD 0 #endif /* some timer constants */ typedef enum { TMR_IDLE = TMR_APPLICATION, TMR_BACKLOGSTATS, TMR_STATUSFILE, TMR_NEWARTICLE, TMR_READART, TMR_PREPART, TMR_READ, TMR_WRITE, TMR_CALLBACK, TMR_MAX } TMRTYPE; #endif /* innfeed_h__ */ inn-2.6.0/innfeed/config_y.c0000644000175200017520000021107412575023702015276 0ustar iuliusiulius/* A Bison parser, made by GNU Bison 2.5. */ /* Bison implementation for Yacc-like parsers in C Copyright (C) 1984, 1989-1990, 2000-2011 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* As a special exception, you may create a larger work that contains part or all of the Bison parser skeleton and distribute that work under terms of your choice, so long as that work isn't itself a parser generator using the skeleton or a modified version thereof as a parser skeleton. Alternatively, if you modify or redistribute the parser skeleton itself, you may (at your option) remove this special exception, which will cause the skeleton and the resulting Bison output files to be licensed under the GNU General Public License without this special exception. This special exception was added by the Free Software Foundation in version 2.2 of Bison. */ /* C LALR(1) parser skeleton written by Richard Stallman, by simplifying the original so-called "semantic" parser. */ /* All symbols defined below should begin with yy or YY, to avoid infringing on user name space. This should be done even for local variables, as they might otherwise be expanded by user macros. There are some unavoidable exceptions within include files to define necessary library symbols; they are noted "INFRINGES ON USER NAME SPACE" below. */ /* Identify Bison output. */ #define YYBISON 1 /* Bison version. */ #define YYBISON_VERSION "2.5" /* Skeleton name. */ #define YYSKELETON_NAME "yacc.c" /* Pure parsers. */ #define YYPURE 0 /* Push parsers. */ #define YYPUSH 0 /* Pull parsers. */ #define YYPULL 1 /* Using locations. */ #define YYLSP_NEEDED 0 /* Copy the first part of user declarations. */ /* Line 268 of yacc.c */ #line 1 "configfile.y" /* $Id: configfile.y 9363 2011-08-21 23:22:01Z eagle $ ** ** A yacc input file for the innfeed config file. ** ** Written by James Brister ** ** This file contains the heart of the innfeed configuration parser, written ** in yacc. It uses an external lexer generated by flex. */ #include "innfeed.h" #include "config.h" #include "clibrary.h" #include #include #include #if defined(_HPUX_SOURCE) # include #endif #include "inn/messages.h" #include "inn/libinn.h" #include "configfile.h" #include "misc.h" #define UNKNOWN_SCOPE_TYPE "line %d: unknown scope type: %s" #define SYNTAX_ERROR "line %d: syntax error" extern int lineCount ; scope *topScope = NULL ; static scope *currScope = NULL ; char *errbuff = NULL ; static void appendName (scope *s, char *p, size_t len) ; static char *valueScopedName (value *v) ; static void freeValue (value *v) ; static char *checkName (scope *s, const char *name) ; static void addValue (scope *s, value *v) ; static char *addScope (scope *s, const char *name, scope *val) ; static void printScope (FILE *fp, scope *s, int indent) ; static void printValue (FILE *fp, value *v, int indent) ; static scope *newScope (const char *type) ; #if 0 static int strNCaseCmp (const char *a, const char *b, size_t len) ; #endif int yyerror (const char *s) ; int yywrap (void) ; int yyparse (void) ; #if 0 int isString (scope *s, const char *name, int inherit) { value *v = findValue (s,name,inherit) ; return (v != NULL && v->type == stringval) ; } #endif int getBool (scope *s, const char *name, int *rval, int inherit) { value *v = findValue (s,name,inherit) ; if (v == NULL) return 0 ; else if (v->type != boolval) return 0 ; *rval = v->v.bool_val ; return 1 ; } int getString (scope *s, const char *name, char **rval, int inherit) { value *v = findValue (s,name,inherit) ; if (v == NULL) return 0 ; else if (v->type != stringval) return 0 ; *rval = xstrdup (v->v.charp_val) ; return 1 ; } int getReal (scope *s, const char *name, double *rval, int inherit) { value *v = findValue (s,name,inherit) ; if (v == NULL) return 0 ; else if (v->type != realval) return 0 ; *rval = v->v.real_val ; return 1 ; } int getInteger (scope *s, const char *name, long *rval, int inherit) { value *v = findValue (s,name,inherit) ; if (v == NULL) return 0 ; else if (v->type != intval) return 0 ; *rval = v->v.int_val ; return 1 ; } void freeScopeTree (scope *s) { int i ; if (s == NULL) return ; if (s->parent == NULL && s->me != NULL) { /* top level scope */ free (s->me->name) ; free (s->me) ; } for (i = 0 ; i < s->value_idx ; i++) if (s->values[i] != NULL) freeValue (s->values [i]) ; free (s->values) ; free (s->scope_type) ; s->parent = NULL ; s->values = NULL ; free (s) ; } char *addInteger (scope *s, const char *name, long val) { value *v ; char *error ; if ((error = checkName (currScope,name)) != NULL) return error ; v = (value *) calloc (1,sizeof (value)) ; v->name = xstrdup (name) ; v->type = intval ; v->v.int_val = val ; addValue (s,v) ; return NULL ; } char *addChar (scope *s, const char *name, char val) { value *v ; char *error ; if ((error = checkName (currScope,name)) != NULL) return error ; v = (value *) calloc (1,sizeof (value)) ; v->name = xstrdup (name) ; v->type = charval ; v->v.char_val = val ; addValue (s,v) ; return NULL ; } char *addBoolean (scope *s, const char *name, int val) { value *v ; char *error ; if ((error = checkName (currScope,name)) != NULL) return error ; v = (value *) calloc (1,sizeof (value)) ; v->name = xstrdup (name) ; v->type = boolval ; v->v.bool_val = val ; addValue (s,v) ; return NULL ; } char *addReal (scope *s, const char *name, double val) { value *v ; char *error ; if ((error = checkName (currScope,name)) != NULL) return error ; v = (value *) calloc (1,sizeof (value)) ; v->name = xstrdup (name) ; v->type = realval ; v->v.real_val = val ; addValue (s,v) ; return NULL ; } char *addString (scope *s, const char *name, const char *val) { value *v ; char *error ; if ((error = checkName (currScope,name)) != NULL) return error ; v = (value *) calloc (1,sizeof (value)) ; v->name = xstrdup (name) ; v->type = stringval ; v->v.charp_val = xstrdup (val) ; addValue (s,v) ; return NULL ; } value *findValue (scope *s, const char *name, int inherit) { const char *p ; if (name == NULL || *name == '\0') return NULL ; if (*name == ':') return findValue (topScope,name + 1,0) ; else if (s == NULL) return findValue (topScope,name,0) ; else { int i ; if ((p = strchr (name,':')) == NULL) p = name + strlen (name) ; for (i = 0 ; i < s->value_idx ; i++) { if (strlen (s->values[i]->name) == (size_t) (p - name) && strncmp (s->values[i]->name,name,p - name) == 0) { if (*p == '\0') /* last segment of name */ return s->values[i] ; else if (s->values[i]->type != scopeval) errbuff = xstrdup ("Component not a scope") ; else return findValue (s->values[i]->v.scope_val,p + 1,0) ; } } /* not in this scope. Go up if inheriting values and only if no ':' in name */ if (inherit && *p == '\0') return findValue (s->parent,name,inherit) ; } return NULL ; } /* find the scope that name belongs to. If mustExist is true then the name must be a fully scoped name of a value. relative scopes start at s. */ scope *findScope (scope *s, const char *name, int mustExist) { scope *p = NULL ; char *q ; int i ; if ((q = strchr (name,':')) == NULL) { if (!mustExist) p = s ; else for (i = 0 ; p == NULL && i < s->value_idx ; i++) if (strcmp (s->values[i]->name,name) == 0) p = s ; return p ; } else if (*name == ':') { while (s->parent != NULL) s = s->parent ; return findScope (s,name + 1,mustExist) ; } else { for (i = 0 ; i < s->value_idx ; i++) if (strncmp (s->values[i]->name,name,q - name) == 0) if (s->values[i]->type == scopeval) return findScope (s->values[i]->v.scope_val,q + 1,mustExist) ; } return NULL ; } /****************************************************************************/ /* */ /****************************************************************************/ static void appendName (scope *s, char *p, size_t len) { if (s == NULL) return ; else { appendName (s->parent,p,len) ; strlcat (p,s->me->name,len) ; strlcat (p,":",len) ; } } static char *valueScopedName (value *v) { scope *p = v->myscope ; int len = strlen (v->name) ; char *q ; while (p != NULL) { len += strlen (p->me->name) + 1 ; p = p->parent ; } len++; q = xmalloc (len) ; q [0] = '\0' ; appendName (v->myscope,q,len) ; strlcat (q,v->name,len) ; return q ; } static void freeValue (value *v) { free (v->name) ; switch (v->type) { case scopeval: freeScopeTree (v->v.scope_val) ; break ; case stringval: free (v->v.charp_val) ; break ; default: break ; } free (v) ; } static char *checkName (scope *s, const char *name) { int i ; char *error = NULL ; if (s == NULL) return NULL ; for (i = 0 ; i < s->value_idx ; i++) { char *n = NULL ; if (strcmp (name,s->values [i]->name) == 0) { n = valueScopedName (s->values[i]) ; error = concat ("Two definitions of ", n, (char *) 0) ; free (n) ; return error ; } } return error ; } static void addValue (scope *s, value *v) { v->myscope = s ; if (s == NULL) return ; if (s->value_count == s->value_idx) { if (s->values == 0) { s->values = (value **) calloc (10,sizeof (value *)) ; s->value_count = 10 ; } else { s->value_count += 10 ; s->values = (value **) realloc (s->values, sizeof (value *) * s->value_count); } } s->values [s->value_idx++] = v ; } static char *addScope (scope *s, const char *name, scope *val) { value *v ; char *error ; if ((error = checkName (s,name)) != NULL) return error ; v = (value *) calloc (1,sizeof (value)) ; v->name = xstrdup (name) ; v->type = scopeval ; v->v.scope_val = val ; val->me = v ; val->parent = s ; addValue (s,v) ; currScope = val ; return NULL ; } static void printScope (FILE *fp, scope *s, int indent) { int i ; for (i = 0 ; i < s->value_idx ; i++) printValue (fp,s->values [i],indent + 5) ; } static void printValue (FILE *fp, value *v, int indent) { int i ; for (i = 0 ; i < indent ; i++) fputc (' ',fp) ; switch (v->type) { case intval: fprintf (fp,"%s : %ld # INTEGER\n",v->name,v->v.int_val) ; break ; case stringval: fprintf (fp,"%s : \"",v->name) ; { char *p = v->v.charp_val ; while (*p) { if (*p == '"' || *p == '\\') fputc ('\\',fp) ; fputc (*p,fp) ; p++ ; } } fprintf (fp,"\" # STRING\n") ; break ; case charval: fprintf (fp,"%s : %c",v->name,047) ; switch (v->v.char_val) { case '\\': fprintf (fp,"\\\\") ; break ; default: if (isprint((unsigned char) v->v.char_val)) fprintf (fp,"%c",v->v.char_val) ; else fprintf (fp,"\\%03o",v->v.char_val) ; } fprintf (fp,"%c # CHARACTER\n",047) ; break ; case realval: fprintf (fp,"%s : %f # REAL\n",v->name,v->v.real_val) ; break ; case boolval: fprintf (fp,"%s : %s # BOOLEAN\n", v->name,(v->v.bool_val ? "true" : "false")) ; break ; case scopeval: fprintf (fp,"%s %s { # SCOPE\n",v->v.scope_val->scope_type,v->name) ; printScope (fp,v->v.scope_val,indent + 5) ; for (i = 0 ; i < indent ; i++) fputc (' ',fp) ; fprintf (fp,"}\n") ; break ; default: fprintf (fp,"UNKNOWN value type: %d\n",v->type) ; exit (1) ; } } static scope *newScope (const char *type) { scope *t ; int i ; t = (scope *) calloc (1,sizeof (scope)) ; t->parent = NULL ; t->scope_type = xstrdup (type) ; for (i = 0 ; t->scope_type[i] != '\0' ; i++) t->scope_type[i] = tolower ((unsigned char) t->scope_type[i]) ; return t ; } #define BAD_KEY "line %d: illegal key name: %s" #define NON_ALPHA "line %d: keys must start with a letter: %s" static char *keyOk (const char *key) { const char *p = key ; char *rval ; if (key == NULL) { rval = xmalloc (strlen ("line : NULL key") + 15) ; sprintf (rval,"line %d: NULL key", lineCount) ; return rval ; } else if (*key == '\0') { rval = xmalloc (strlen ("line : EMPTY KEY") + 15) ; sprintf (rval,"line %d: EMPTY KEY", lineCount) ; return rval ; } if (!isalpha((unsigned char) *p)) { rval = xmalloc (strlen (NON_ALPHA) + strlen (key) + 15) ; sprintf (rval,NON_ALPHA,lineCount, key) ; return rval ; } p++ ; while (*p) { if (!(isalnum((unsigned char) *p) || *p == '_' || *p == '-')) { rval = xmalloc (strlen (BAD_KEY) + strlen (key) + 15) ; sprintf (rval,BAD_KEY,lineCount,key) ; return rval ; } p++ ; } return NULL ; } static PFIVP *funcs = NULL ; static void **args = NULL ; static int funcCount ; static int funcIdx ; void configAddLoadCallback (PFIVP func,void *arg) { if (func == NULL) return ; if (funcIdx == funcCount) { funcCount += 10 ; if (funcs == NULL) { funcs = xmalloc (sizeof (PFIVP) * funcCount); args = xmalloc (sizeof (void *) * funcCount) ; } else { funcs = xrealloc (funcs,sizeof (PFIVP) * funcCount); args = xrealloc (args,sizeof (void *) * funcCount) ; } } args [funcIdx] = arg ; funcs [funcIdx++] = func ; } void configRemoveLoadCallback (PFIVP func) { int i, j ; for (i = 0 ; i < funcIdx ; i++) if (funcs [i] == func) break ; for (j = i ; j < funcIdx - 1 ; j++) { funcs [j] = funcs [j + 1] ; args [j] = args [j + 1] ; } if (funcIdx > 1 && i < funcIdx) { funcs [i - 2] = funcs [i - 1] ; args [i - 2] = args [i - 1] ; } if (funcIdx > 0 && i < funcIdx) funcIdx-- ; } static int doCallbacks (void) { int i ; int rval = 1 ; for (i = 0 ; i < funcIdx ; i++) if (funcs [i] != NULL) rval = (funcs[i](args [i]) && rval) ; return rval ; } static char *key ; /* Line 268 of yacc.c */ #line 728 "y.tab.c" /* Enabling traces. */ #ifndef YYDEBUG # define YYDEBUG 0 #endif /* Enabling verbose error messages. */ #ifdef YYERROR_VERBOSE # undef YYERROR_VERBOSE # define YYERROR_VERBOSE 1 #else # define YYERROR_VERBOSE 0 #endif /* Enabling the token table. */ #ifndef YYTOKEN_TABLE # define YYTOKEN_TABLE 0 #endif /* Tokens. */ #ifndef YYTOKENTYPE # define YYTOKENTYPE /* Put the tokens into the symbol table, so that GDB and other debuggers know about them. */ enum yytokentype { PEER = 258, GROUP = 259, IVAL = 260, RVAL = 261, NAME = 262, XSTRING = 263, SCOPE = 264, COLON = 265, LBRACE = 266, RBRACE = 267, TRUEBVAL = 268, FALSEBVAL = 269, CHAR = 270, WORD = 271, IP_ADDRESS = 272 }; #endif /* Tokens. */ #define PEER 258 #define GROUP 259 #define IVAL 260 #define RVAL 261 #define NAME 262 #define XSTRING 263 #define SCOPE 264 #define COLON 265 #define LBRACE 266 #define RBRACE 267 #define TRUEBVAL 268 #define FALSEBVAL 269 #define CHAR 270 #define WORD 271 #define IP_ADDRESS 272 #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED typedef union YYSTYPE { /* Line 293 of yacc.c */ #line 657 "configfile.y" scope *scp ; value *val ; char *name ; int integer ; double real ; char *string ; char chr ; /* Line 293 of yacc.c */ #line 810 "y.tab.c" } YYSTYPE; # define YYSTYPE_IS_TRIVIAL 1 # define yystype YYSTYPE /* obsolescent; will be withdrawn */ # define YYSTYPE_IS_DECLARED 1 #endif /* Copy the second part of user declarations. */ /* Line 343 of yacc.c */ #line 822 "y.tab.c" #ifdef short # undef short #endif #ifdef YYTYPE_UINT8 typedef YYTYPE_UINT8 yytype_uint8; #else typedef unsigned char yytype_uint8; #endif #ifdef YYTYPE_INT8 typedef YYTYPE_INT8 yytype_int8; #elif (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) typedef signed char yytype_int8; #else typedef short int yytype_int8; #endif #ifdef YYTYPE_UINT16 typedef YYTYPE_UINT16 yytype_uint16; #else typedef unsigned short int yytype_uint16; #endif #ifdef YYTYPE_INT16 typedef YYTYPE_INT16 yytype_int16; #else typedef short int yytype_int16; #endif #ifndef YYSIZE_T # ifdef __SIZE_TYPE__ # define YYSIZE_T __SIZE_TYPE__ # elif defined size_t # define YYSIZE_T size_t # elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) # include /* INFRINGES ON USER NAME SPACE */ # define YYSIZE_T size_t # else # define YYSIZE_T unsigned int # endif #endif #define YYSIZE_MAXIMUM ((YYSIZE_T) -1) #ifndef YY_ # if defined YYENABLE_NLS && YYENABLE_NLS # if ENABLE_NLS # include /* INFRINGES ON USER NAME SPACE */ # define YY_(msgid) dgettext ("bison-runtime", msgid) # endif # endif # ifndef YY_ # define YY_(msgid) msgid # endif #endif /* Suppress unused-variable warnings by "using" E. */ #if ! defined lint || defined __GNUC__ # define YYUSE(e) ((void) (e)) #else # define YYUSE(e) /* empty */ #endif /* Identity function, used to suppress warnings about constant conditions. */ #ifndef lint # define YYID(n) (n) #else #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static int YYID (int yyi) #else static int YYID (yyi) int yyi; #endif { return yyi; } #endif #if ! defined yyoverflow || YYERROR_VERBOSE /* The parser invokes alloca or malloc; define the necessary symbols. */ # ifdef YYSTACK_USE_ALLOCA # if YYSTACK_USE_ALLOCA # ifdef __GNUC__ # define YYSTACK_ALLOC __builtin_alloca # elif defined __BUILTIN_VA_ARG_INCR # include /* INFRINGES ON USER NAME SPACE */ # elif defined _AIX # define YYSTACK_ALLOC __alloca # elif defined _MSC_VER # include /* INFRINGES ON USER NAME SPACE */ # define alloca _alloca # else # define YYSTACK_ALLOC alloca # if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) # include /* INFRINGES ON USER NAME SPACE */ # ifndef EXIT_SUCCESS # define EXIT_SUCCESS 0 # endif # endif # endif # endif # endif # ifdef YYSTACK_ALLOC /* Pacify GCC's `empty if-body' warning. */ # define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0)) # ifndef YYSTACK_ALLOC_MAXIMUM /* The OS might guarantee only one guard page at the bottom of the stack, and a page size can be as small as 4096 bytes. So we cannot safely invoke alloca (N) if N exceeds 4096. Use a slightly smaller number to allow for a few compiler-allocated temporary stack slots. */ # define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ # endif # else # define YYSTACK_ALLOC YYMALLOC # define YYSTACK_FREE YYFREE # ifndef YYSTACK_ALLOC_MAXIMUM # define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM # endif # if (defined __cplusplus && ! defined EXIT_SUCCESS \ && ! ((defined YYMALLOC || defined malloc) \ && (defined YYFREE || defined free))) # include /* INFRINGES ON USER NAME SPACE */ # ifndef EXIT_SUCCESS # define EXIT_SUCCESS 0 # endif # endif # ifndef YYMALLOC # define YYMALLOC malloc # if ! defined malloc && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ # endif # endif # ifndef YYFREE # define YYFREE free # if ! defined free && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) void free (void *); /* INFRINGES ON USER NAME SPACE */ # endif # endif # endif #endif /* ! defined yyoverflow || YYERROR_VERBOSE */ #if (! defined yyoverflow \ && (! defined __cplusplus \ || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) /* A type that is properly aligned for any stack member. */ union yyalloc { yytype_int16 yyss_alloc; YYSTYPE yyvs_alloc; }; /* The size of the maximum gap between one aligned stack and the next. */ # define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) /* The size of an array large to enough to hold all stacks, each with N elements. */ # define YYSTACK_BYTES(N) \ ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \ + YYSTACK_GAP_MAXIMUM) # define YYCOPY_NEEDED 1 /* Relocate STACK from its old location to the new one. The local variables YYSIZE and YYSTACKSIZE give the old and new number of elements in the stack, and YYPTR gives the new location of the stack. Advance YYPTR to a properly aligned location for the next stack. */ # define YYSTACK_RELOCATE(Stack_alloc, Stack) \ do \ { \ YYSIZE_T yynewbytes; \ YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ Stack = &yyptr->Stack_alloc; \ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ yyptr += yynewbytes / sizeof (*yyptr); \ } \ while (YYID (0)) #endif #if defined YYCOPY_NEEDED && YYCOPY_NEEDED /* Copy COUNT objects from FROM to TO. The source and destination do not overlap. */ # ifndef YYCOPY # if defined __GNUC__ && 1 < __GNUC__ # define YYCOPY(To, From, Count) \ __builtin_memcpy (To, From, (Count) * sizeof (*(From))) # else # define YYCOPY(To, From, Count) \ do \ { \ YYSIZE_T yyi; \ for (yyi = 0; yyi < (Count); yyi++) \ (To)[yyi] = (From)[yyi]; \ } \ while (YYID (0)) # endif # endif #endif /* !YYCOPY_NEEDED */ /* YYFINAL -- State number of the termination state. */ #define YYFINAL 3 /* YYLAST -- Last index in YYTABLE. */ #define YYLAST 30 /* YYNTOKENS -- Number of terminals. */ #define YYNTOKENS 18 /* YYNNTS -- Number of nonterminals. */ #define YYNNTS 10 /* YYNRULES -- Number of rules. */ #define YYNRULES 21 /* YYNRULES -- Number of states. */ #define YYNSTATES 33 /* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ #define YYUNDEFTOK 2 #define YYMAXUTOK 272 #define YYTRANSLATE(YYX) \ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) /* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ static const yytype_uint8 yytranslate[] = { 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 }; #if YYDEBUG /* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in YYRHS. */ static const yytype_uint8 yyprhs[] = { 0, 0, 3, 4, 7, 9, 10, 13, 16, 17, 24, 25, 32, 36, 37, 42, 44, 46, 48, 50, 52, 54 }; /* YYRHS -- A `-1'-separated list of the rules' RHS. */ static const yytype_int8 yyrhs[] = { 19, 0, -1, -1, 20, 22, -1, 22, -1, -1, 22, 23, -1, 22, 1, -1, -1, 3, 16, 11, 24, 21, 12, -1, -1, 4, 16, 11, 25, 21, 12, -1, 16, 16, 11, -1, -1, 16, 26, 10, 27, -1, 16, -1, 5, -1, 13, -1, 14, -1, 6, -1, 8, -1, 15, -1 }; /* YYRLINE[YYN] -- source line where rule number YYN was defined. */ static const yytype_uint16 yyrline[] = { 0, 690, 690, 690, 696, 698, 699, 700, 707, 707, 714, 714, 721, 729, 729, 736, 742, 747, 753, 759, 764, 769 }; #endif #if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE /* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. First, the terminals, then, starting at YYNTOKENS, nonterminals. */ static const char *const yytname[] = { "$end", "error", "$undefined", "PEER", "GROUP", "IVAL", "RVAL", "NAME", "XSTRING", "SCOPE", "COLON", "LBRACE", "RBRACE", "TRUEBVAL", "FALSEBVAL", "CHAR", "WORD", "IP_ADDRESS", "$accept", "input", "$@1", "scope", "entries", "entry", "$@2", "$@3", "$@4", "value", 0 }; #endif # ifdef YYPRINT /* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to token YYLEX-NUM. */ static const yytype_uint16 yytoknum[] = { 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272 }; # endif /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ static const yytype_uint8 yyr1[] = { 0, 18, 20, 19, 21, 22, 22, 22, 24, 23, 25, 23, 23, 26, 23, 27, 27, 27, 27, 27, 27, 27 }; /* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ static const yytype_uint8 yyr2[] = { 0, 2, 0, 2, 1, 0, 2, 2, 0, 6, 0, 6, 3, 0, 4, 1, 1, 1, 1, 1, 1, 1 }; /* YYDEFACT[STATE-NAME] -- Default reduction number in state STATE-NUM. Performed when YYTABLE doesn't specify something else to do. Zero means the default is an error. */ static const yytype_uint8 yydefact[] = { 2, 0, 5, 1, 0, 7, 0, 0, 13, 6, 0, 0, 0, 0, 8, 10, 12, 0, 5, 5, 16, 19, 20, 17, 18, 21, 15, 14, 0, 0, 0, 9, 11 }; /* YYDEFGOTO[NTERM-NUM]. */ static const yytype_int8 yydefgoto[] = { -1, 1, 2, 28, 29, 9, 18, 19, 13, 27 }; /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing STATE-NUM. */ #define YYPACT_NINF -12 static const yytype_int8 yypact[] = { -12, 2, -12, -12, 0, -12, -11, -9, -6, -12, 1, 3, 4, 8, -12, -12, -12, 14, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -1, 5, 11, -12, -12 }; /* YYPGOTO[NTERM-NUM]. */ static const yytype_int8 yypgoto[] = { -12, -12, -12, 6, 22, -12, -12, -12, -12, -12 }; /* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If positive, shift that token. If negative, reduce the rule which number is the opposite. If YYTABLE_NINF, syntax error. */ #define YYTABLE_NINF -5 static const yytype_int8 yytable[] = { -3, 5, 3, 6, 7, 10, 5, 11, 6, 7, 12, 31, 14, 0, 15, 16, 8, -4, 17, 20, 21, 8, 22, 32, 4, 30, 0, 23, 24, 25, 26 }; #define yypact_value_is_default(yystate) \ ((yystate) == (-12)) #define yytable_value_is_error(yytable_value) \ YYID (0) static const yytype_int8 yycheck[] = { 0, 1, 0, 3, 4, 16, 1, 16, 3, 4, 16, 12, 11, -1, 11, 11, 16, 12, 10, 5, 6, 16, 8, 12, 2, 19, -1, 13, 14, 15, 16 }; /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing symbol of state STATE-NUM. */ static const yytype_uint8 yystos[] = { 0, 19, 20, 0, 22, 1, 3, 4, 16, 23, 16, 16, 16, 26, 11, 11, 11, 10, 24, 25, 5, 6, 8, 13, 14, 15, 16, 27, 21, 22, 21, 12, 12 }; #define yyerrok (yyerrstatus = 0) #define yyclearin (yychar = YYEMPTY) #define YYEMPTY (-2) #define YYEOF 0 #define YYACCEPT goto yyacceptlab #define YYABORT goto yyabortlab #define YYERROR goto yyerrorlab /* Like YYERROR except do call yyerror. This remains here temporarily to ease the transition to the new meaning of YYERROR, for GCC. Once GCC version 2 has supplanted version 1, this can go. However, YYFAIL appears to be in use. Nevertheless, it is formally deprecated in Bison 2.4.2's NEWS entry, where a plan to phase it out is discussed. */ #define YYFAIL goto yyerrlab #if defined YYFAIL /* This is here to suppress warnings from the GCC cpp's -Wunused-macros. Normally we don't worry about that warning, but some users do, and we want to make it easy for users to remove YYFAIL uses, which will produce warnings from Bison 2.5. */ #endif #define YYRECOVERING() (!!yyerrstatus) #define YYBACKUP(Token, Value) \ do \ if (yychar == YYEMPTY && yylen == 1) \ { \ yychar = (Token); \ yylval = (Value); \ YYPOPSTACK (1); \ goto yybackup; \ } \ else \ { \ yyerror (YY_("syntax error: cannot back up")); \ YYERROR; \ } \ while (YYID (0)) #define YYTERROR 1 #define YYERRCODE 256 /* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. If N is 0, then set CURRENT to the empty location which ends the previous symbol: RHS[0] (always defined). */ #define YYRHSLOC(Rhs, K) ((Rhs)[K]) #ifndef YYLLOC_DEFAULT # define YYLLOC_DEFAULT(Current, Rhs, N) \ do \ if (YYID (N)) \ { \ (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ } \ else \ { \ (Current).first_line = (Current).last_line = \ YYRHSLOC (Rhs, 0).last_line; \ (Current).first_column = (Current).last_column = \ YYRHSLOC (Rhs, 0).last_column; \ } \ while (YYID (0)) #endif /* This macro is provided for backward compatibility. */ #ifndef YY_LOCATION_PRINT # define YY_LOCATION_PRINT(File, Loc) ((void) 0) #endif /* YYLEX -- calling `yylex' with the right arguments. */ #ifdef YYLEX_PARAM # define YYLEX yylex (YYLEX_PARAM) #else # define YYLEX yylex () #endif /* Enable debugging if requested. */ #if YYDEBUG # ifndef YYFPRINTF # include /* INFRINGES ON USER NAME SPACE */ # define YYFPRINTF fprintf # endif # define YYDPRINTF(Args) \ do { \ if (yydebug) \ YYFPRINTF Args; \ } while (YYID (0)) # define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ do { \ if (yydebug) \ { \ YYFPRINTF (stderr, "%s ", Title); \ yy_symbol_print (stderr, \ Type, Value); \ YYFPRINTF (stderr, "\n"); \ } \ } while (YYID (0)) /*--------------------------------. | Print this symbol on YYOUTPUT. | `--------------------------------*/ /*ARGSUSED*/ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) #else static void yy_symbol_value_print (yyoutput, yytype, yyvaluep) FILE *yyoutput; int yytype; YYSTYPE const * const yyvaluep; #endif { if (!yyvaluep) return; # ifdef YYPRINT if (yytype < YYNTOKENS) YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); # else YYUSE (yyoutput); # endif switch (yytype) { default: break; } } /*--------------------------------. | Print this symbol on YYOUTPUT. | `--------------------------------*/ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) #else static void yy_symbol_print (yyoutput, yytype, yyvaluep) FILE *yyoutput; int yytype; YYSTYPE const * const yyvaluep; #endif { if (yytype < YYNTOKENS) YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); else YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); yy_symbol_value_print (yyoutput, yytype, yyvaluep); YYFPRINTF (yyoutput, ")"); } /*------------------------------------------------------------------. | yy_stack_print -- Print the state stack from its BOTTOM up to its | | TOP (included). | `------------------------------------------------------------------*/ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop) #else static void yy_stack_print (yybottom, yytop) yytype_int16 *yybottom; yytype_int16 *yytop; #endif { YYFPRINTF (stderr, "Stack now"); for (; yybottom <= yytop; yybottom++) { int yybot = *yybottom; YYFPRINTF (stderr, " %d", yybot); } YYFPRINTF (stderr, "\n"); } # define YY_STACK_PRINT(Bottom, Top) \ do { \ if (yydebug) \ yy_stack_print ((Bottom), (Top)); \ } while (YYID (0)) /*------------------------------------------------. | Report that the YYRULE is going to be reduced. | `------------------------------------------------*/ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void yy_reduce_print (YYSTYPE *yyvsp, int yyrule) #else static void yy_reduce_print (yyvsp, yyrule) YYSTYPE *yyvsp; int yyrule; #endif { int yynrhs = yyr2[yyrule]; int yyi; unsigned long int yylno = yyrline[yyrule]; YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", yyrule - 1, yylno); /* The symbols being reduced. */ for (yyi = 0; yyi < yynrhs; yyi++) { YYFPRINTF (stderr, " $%d = ", yyi + 1); yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi], &(yyvsp[(yyi + 1) - (yynrhs)]) ); YYFPRINTF (stderr, "\n"); } } # define YY_REDUCE_PRINT(Rule) \ do { \ if (yydebug) \ yy_reduce_print (yyvsp, Rule); \ } while (YYID (0)) /* Nonzero means print parse trace. It is left uninitialized so that multiple parsers can coexist. */ int yydebug; #else /* !YYDEBUG */ # define YYDPRINTF(Args) # define YY_SYMBOL_PRINT(Title, Type, Value, Location) # define YY_STACK_PRINT(Bottom, Top) # define YY_REDUCE_PRINT(Rule) #endif /* !YYDEBUG */ /* YYINITDEPTH -- initial size of the parser's stacks. */ #ifndef YYINITDEPTH # define YYINITDEPTH 200 #endif /* YYMAXDEPTH -- maximum size the stacks can grow to (effective only if the built-in stack extension method is used). Do not make this value too large; the results are undefined if YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) evaluated with infinite-precision integer arithmetic. */ #ifndef YYMAXDEPTH # define YYMAXDEPTH 10000 #endif #if YYERROR_VERBOSE # ifndef yystrlen # if defined __GLIBC__ && defined _STRING_H # define yystrlen strlen # else /* Return the length of YYSTR. */ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static YYSIZE_T yystrlen (const char *yystr) #else static YYSIZE_T yystrlen (yystr) const char *yystr; #endif { YYSIZE_T yylen; for (yylen = 0; yystr[yylen]; yylen++) continue; return yylen; } # endif # endif # ifndef yystpcpy # if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE # define yystpcpy stpcpy # else /* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in YYDEST. */ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static char * yystpcpy (char *yydest, const char *yysrc) #else static char * yystpcpy (yydest, yysrc) char *yydest; const char *yysrc; #endif { char *yyd = yydest; const char *yys = yysrc; while ((*yyd++ = *yys++) != '\0') continue; return yyd - 1; } # endif # endif # ifndef yytnamerr /* Copy to YYRES the contents of YYSTR after stripping away unnecessary quotes and backslashes, so that it's suitable for yyerror. The heuristic is that double-quoting is unnecessary unless the string contains an apostrophe, a comma, or backslash (other than backslash-backslash). YYSTR is taken from yytname. If YYRES is null, do not copy; instead, return the length of what the result would have been. */ static YYSIZE_T yytnamerr (char *yyres, const char *yystr) { if (*yystr == '"') { YYSIZE_T yyn = 0; char const *yyp = yystr; for (;;) switch (*++yyp) { case '\'': case ',': goto do_not_strip_quotes; case '\\': if (*++yyp != '\\') goto do_not_strip_quotes; /* Fall through. */ default: if (yyres) yyres[yyn] = *yyp; yyn++; break; case '"': if (yyres) yyres[yyn] = '\0'; return yyn; } do_not_strip_quotes: ; } if (! yyres) return yystrlen (yystr); return yystpcpy (yyres, yystr) - yyres; } # endif /* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message about the unexpected token YYTOKEN for the state stack whose top is YYSSP. Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is not large enough to hold the message. In that case, also set *YYMSG_ALLOC to the required number of bytes. Return 2 if the required number of bytes is too large to store. */ static int yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg, yytype_int16 *yyssp, int yytoken) { YYSIZE_T yysize0 = yytnamerr (0, yytname[yytoken]); YYSIZE_T yysize = yysize0; YYSIZE_T yysize1; enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; /* Internationalized format string. */ const char *yyformat = 0; /* Arguments of yyformat. */ char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; /* Number of reported tokens (one for the "unexpected", one per "expected"). */ int yycount = 0; /* There are many possibilities here to consider: - Assume YYFAIL is not used. It's too flawed to consider. See for details. YYERROR is fine as it does not invoke this function. - If this state is a consistent state with a default action, then the only way this function was invoked is if the default action is an error action. In that case, don't check for expected tokens because there are none. - The only way there can be no lookahead present (in yychar) is if this state is a consistent state with a default action. Thus, detecting the absence of a lookahead is sufficient to determine that there is no unexpected or expected token to report. In that case, just report a simple "syntax error". - Don't assume there isn't a lookahead just because this state is a consistent state with a default action. There might have been a previous inconsistent state, consistent state with a non-default action, or user semantic action that manipulated yychar. - Of course, the expected token list depends on states to have correct lookahead information, and it depends on the parser not to perform extra reductions after fetching a lookahead from the scanner and before detecting a syntax error. Thus, state merging (from LALR or IELR) and default reductions corrupt the expected token list. However, the list is correct for canonical LR with one exception: it will still contain any token that will not be accepted due to an error action in a later state. */ if (yytoken != YYEMPTY) { int yyn = yypact[*yyssp]; yyarg[yycount++] = yytname[yytoken]; if (!yypact_value_is_default (yyn)) { /* Start YYX at -YYN if negative to avoid negative indexes in YYCHECK. In other words, skip the first -YYN actions for this state because they are default actions. */ int yyxbegin = yyn < 0 ? -yyn : 0; /* Stay within bounds of both yycheck and yytname. */ int yychecklim = YYLAST - yyn + 1; int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; int yyx; for (yyx = yyxbegin; yyx < yyxend; ++yyx) if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR && !yytable_value_is_error (yytable[yyx + yyn])) { if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) { yycount = 1; yysize = yysize0; break; } yyarg[yycount++] = yytname[yyx]; yysize1 = yysize + yytnamerr (0, yytname[yyx]); if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) return 2; yysize = yysize1; } } } switch (yycount) { # define YYCASE_(N, S) \ case N: \ yyformat = S; \ break YYCASE_(0, YY_("syntax error")); YYCASE_(1, YY_("syntax error, unexpected %s")); YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); # undef YYCASE_ } yysize1 = yysize + yystrlen (yyformat); if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) return 2; yysize = yysize1; if (*yymsg_alloc < yysize) { *yymsg_alloc = 2 * yysize; if (! (yysize <= *yymsg_alloc && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; return 1; } /* Avoid sprintf, as that infringes on the user's name space. Don't have undefined behavior even if the translation produced a string with the wrong number of "%s"s. */ { char *yyp = *yymsg; int yyi = 0; while ((*yyp = *yyformat) != '\0') if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) { yyp += yytnamerr (yyp, yyarg[yyi++]); yyformat += 2; } else { yyp++; yyformat++; } } return 0; } #endif /* YYERROR_VERBOSE */ /*-----------------------------------------------. | Release the memory associated to this symbol. | `-----------------------------------------------*/ /*ARGSUSED*/ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep) #else static void yydestruct (yymsg, yytype, yyvaluep) const char *yymsg; int yytype; YYSTYPE *yyvaluep; #endif { YYUSE (yyvaluep); if (!yymsg) yymsg = "Deleting"; YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); switch (yytype) { default: break; } } /* Prevent warnings from -Wmissing-prototypes. */ #ifdef YYPARSE_PARAM #if defined __STDC__ || defined __cplusplus int yyparse (void *YYPARSE_PARAM); #else int yyparse (); #endif #else /* ! YYPARSE_PARAM */ #if defined __STDC__ || defined __cplusplus int yyparse (void); #else int yyparse (); #endif #endif /* ! YYPARSE_PARAM */ /* The lookahead symbol. */ int yychar; /* The semantic value of the lookahead symbol. */ YYSTYPE yylval; /* Number of syntax errors so far. */ int yynerrs; /*----------. | yyparse. | `----------*/ #ifdef YYPARSE_PARAM #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) int yyparse (void *YYPARSE_PARAM) #else int yyparse (YYPARSE_PARAM) void *YYPARSE_PARAM; #endif #else /* ! YYPARSE_PARAM */ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) int yyparse (void) #else int yyparse () #endif #endif { int yystate; /* Number of tokens to shift before error messages enabled. */ int yyerrstatus; /* The stacks and their tools: `yyss': related to states. `yyvs': related to semantic values. Refer to the stacks thru separate pointers, to allow yyoverflow to reallocate them elsewhere. */ /* The state stack. */ yytype_int16 yyssa[YYINITDEPTH]; yytype_int16 *yyss; yytype_int16 *yyssp; /* The semantic value stack. */ YYSTYPE yyvsa[YYINITDEPTH]; YYSTYPE *yyvs; YYSTYPE *yyvsp; YYSIZE_T yystacksize; int yyn; int yyresult; /* Lookahead token as an internal (translated) token number. */ int yytoken; /* The variables used to return semantic value and location from the action routines. */ YYSTYPE yyval; #if YYERROR_VERBOSE /* Buffer for error messages, and its allocated size. */ char yymsgbuf[128]; char *yymsg = yymsgbuf; YYSIZE_T yymsg_alloc = sizeof yymsgbuf; #endif #define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) /* The number of symbols on the RHS of the reduced rule. Keep to zero when no symbol should be popped. */ int yylen = 0; yytoken = 0; yyss = yyssa; yyvs = yyvsa; yystacksize = YYINITDEPTH; YYDPRINTF ((stderr, "Starting parse\n")); yystate = 0; yyerrstatus = 0; yynerrs = 0; yychar = YYEMPTY; /* Cause a token to be read. */ /* Initialize stack pointers. Waste one element of value and location stack so that they stay on the same level as the state stack. The wasted elements are never initialized. */ yyssp = yyss; yyvsp = yyvs; goto yysetstate; /*------------------------------------------------------------. | yynewstate -- Push a new state, which is found in yystate. | `------------------------------------------------------------*/ yynewstate: /* In all cases, when you get here, the value and location stacks have just been pushed. So pushing a state here evens the stacks. */ yyssp++; yysetstate: *yyssp = yystate; if (yyss + yystacksize - 1 <= yyssp) { /* Get the current used size of the three stacks, in elements. */ YYSIZE_T yysize = yyssp - yyss + 1; #ifdef yyoverflow { /* Give user a chance to reallocate the stack. Use copies of these so that the &'s don't force the real ones into memory. */ YYSTYPE *yyvs1 = yyvs; yytype_int16 *yyss1 = yyss; /* Each stack pointer address is followed by the size of the data in use in that stack, in bytes. This used to be a conditional around just the two extra args, but that might be undefined if yyoverflow is a macro. */ yyoverflow (YY_("memory exhausted"), &yyss1, yysize * sizeof (*yyssp), &yyvs1, yysize * sizeof (*yyvsp), &yystacksize); yyss = yyss1; yyvs = yyvs1; } #else /* no yyoverflow */ # ifndef YYSTACK_RELOCATE goto yyexhaustedlab; # else /* Extend the stack our own way. */ if (YYMAXDEPTH <= yystacksize) goto yyexhaustedlab; yystacksize *= 2; if (YYMAXDEPTH < yystacksize) yystacksize = YYMAXDEPTH; { yytype_int16 *yyss1 = yyss; union yyalloc *yyptr = (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); if (! yyptr) goto yyexhaustedlab; YYSTACK_RELOCATE (yyss_alloc, yyss); YYSTACK_RELOCATE (yyvs_alloc, yyvs); # undef YYSTACK_RELOCATE if (yyss1 != yyssa) YYSTACK_FREE (yyss1); } # endif #endif /* no yyoverflow */ yyssp = yyss + yysize - 1; yyvsp = yyvs + yysize - 1; YYDPRINTF ((stderr, "Stack size increased to %lu\n", (unsigned long int) yystacksize)); if (yyss + yystacksize - 1 <= yyssp) YYABORT; } YYDPRINTF ((stderr, "Entering state %d\n", yystate)); if (yystate == YYFINAL) YYACCEPT; goto yybackup; /*-----------. | yybackup. | `-----------*/ yybackup: /* Do appropriate processing given the current state. Read a lookahead token if we need one and don't already have one. */ /* First try to decide what to do without reference to lookahead token. */ yyn = yypact[yystate]; if (yypact_value_is_default (yyn)) goto yydefault; /* Not known => get a lookahead token if don't already have one. */ /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ if (yychar == YYEMPTY) { YYDPRINTF ((stderr, "Reading a token: ")); yychar = YYLEX; } if (yychar <= YYEOF) { yychar = yytoken = YYEOF; YYDPRINTF ((stderr, "Now at end of input.\n")); } else { yytoken = YYTRANSLATE (yychar); YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); } /* If the proper action on seeing token YYTOKEN is to reduce or to detect an error, take that action. */ yyn += yytoken; if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) goto yydefault; yyn = yytable[yyn]; if (yyn <= 0) { if (yytable_value_is_error (yyn)) goto yyerrlab; yyn = -yyn; goto yyreduce; } /* Count tokens shifted since error; after three, turn off error status. */ if (yyerrstatus) yyerrstatus--; /* Shift the lookahead token. */ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); /* Discard the shifted token. */ yychar = YYEMPTY; yystate = yyn; *++yyvsp = yylval; goto yynewstate; /*-----------------------------------------------------------. | yydefault -- do the default action for the current state. | `-----------------------------------------------------------*/ yydefault: yyn = yydefact[yystate]; if (yyn == 0) goto yyerrlab; goto yyreduce; /*-----------------------------. | yyreduce -- Do a reduction. | `-----------------------------*/ yyreduce: /* yyn is the number of a rule to reduce with. */ yylen = yyr2[yyn]; /* If YYLEN is nonzero, implement the default value of the action: `$$ = $1'. Otherwise, the following line sets YYVAL to garbage. This behavior is undocumented and Bison users should not rely upon it. Assigning to YYVAL unconditionally makes the parser a bit smaller, and it avoids a GCC warning that YYVAL may be used uninitialized. */ yyval = yyvsp[1-yylen]; YY_REDUCE_PRINT (yyn); switch (yyn) { case 2: /* Line 1806 of yacc.c */ #line 690 "configfile.y" { lineCount = 1 ; addScope (NULL,"",newScope ("")) ; topScope = currScope ; } break; case 3: /* Line 1806 of yacc.c */ #line 694 "configfile.y" { if (!doCallbacks()) YYABORT ; } break; case 7: /* Line 1806 of yacc.c */ #line 700 "configfile.y" { errbuff = xmalloc (strlen(SYNTAX_ERROR) + 12) ; sprintf (errbuff,SYNTAX_ERROR,lineCount) ; YYABORT ; } break; case 8: /* Line 1806 of yacc.c */ #line 707 "configfile.y" { errbuff = addScope (currScope,(yyvsp[(2) - (3)].name),newScope ("peer")) ; free ((yyvsp[(2) - (3)].name)) ; if (errbuff != NULL) YYABORT ; } break; case 9: /* Line 1806 of yacc.c */ #line 711 "configfile.y" { currScope = currScope->parent ; } break; case 10: /* Line 1806 of yacc.c */ #line 714 "configfile.y" { errbuff = addScope (currScope,(yyvsp[(2) - (3)].name),newScope ("group")) ; free ((yyvsp[(2) - (3)].name)) ; if (errbuff != NULL) YYABORT ; } break; case 11: /* Line 1806 of yacc.c */ #line 718 "configfile.y" { currScope = currScope->parent ; } break; case 12: /* Line 1806 of yacc.c */ #line 721 "configfile.y" { errbuff = xmalloc (strlen(UNKNOWN_SCOPE_TYPE) + 15 + strlen ((yyvsp[(1) - (3)].name))) ; sprintf (errbuff,UNKNOWN_SCOPE_TYPE,lineCount,(yyvsp[(1) - (3)].name)) ; free ((yyvsp[(1) - (3)].name)) ; free ((yyvsp[(2) - (3)].name)) ; YYABORT ; } break; case 13: /* Line 1806 of yacc.c */ #line 729 "configfile.y" { if ((errbuff = keyOk((yyvsp[(1) - (1)].name))) != NULL) { YYABORT ; } else key = (yyvsp[(1) - (1)].name) ; } break; case 15: /* Line 1806 of yacc.c */ #line 736 "configfile.y" { if ((errbuff = addString (currScope, key, (yyvsp[(1) - (1)].name))) != NULL) YYABORT ; free (key) ; free ((yyvsp[(1) - (1)].name)) ; } break; case 16: /* Line 1806 of yacc.c */ #line 742 "configfile.y" { if ((errbuff = addInteger(currScope, key, (yyvsp[(1) - (1)].integer))) != NULL) YYABORT; free (key) ; } break; case 17: /* Line 1806 of yacc.c */ #line 747 "configfile.y" { if ((errbuff = addBoolean (currScope, key, 1)) != NULL) YYABORT ; free (key) ; free ((yyvsp[(1) - (1)].name)) ; } break; case 18: /* Line 1806 of yacc.c */ #line 753 "configfile.y" { if ((errbuff = addBoolean (currScope, key, 0)) != NULL) YYABORT ; free (key) ; free ((yyvsp[(1) - (1)].name)) ; } break; case 19: /* Line 1806 of yacc.c */ #line 759 "configfile.y" { if ((errbuff = addReal (currScope, key, (yyvsp[(1) - (1)].real))) != NULL) YYABORT ; free (key) ; } break; case 20: /* Line 1806 of yacc.c */ #line 764 "configfile.y" { if ((errbuff = addString (currScope, key, (yyvsp[(1) - (1)].string))) != NULL) YYABORT; free (key) ; } break; case 21: /* Line 1806 of yacc.c */ #line 769 "configfile.y" { if ((errbuff = addChar (currScope, key, (yyvsp[(1) - (1)].chr))) != NULL) YYABORT ; free (key) ; } break; /* Line 1806 of yacc.c */ #line 2239 "y.tab.c" default: break; } /* User semantic actions sometimes alter yychar, and that requires that yytoken be updated with the new translation. We take the approach of translating immediately before every use of yytoken. One alternative is translating here after every semantic action, but that translation would be missed if the semantic action invokes YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an incorrect destructor might then be invoked immediately. In the case of YYERROR or YYBACKUP, subsequent parser actions might lead to an incorrect destructor call or verbose syntax error message before the lookahead is translated. */ YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); YYPOPSTACK (yylen); yylen = 0; YY_STACK_PRINT (yyss, yyssp); *++yyvsp = yyval; /* Now `shift' the result of the reduction. Determine what state that goes to, based on the state we popped back to and the rule number reduced by. */ yyn = yyr1[yyn]; yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) yystate = yytable[yystate]; else yystate = yydefgoto[yyn - YYNTOKENS]; goto yynewstate; /*------------------------------------. | yyerrlab -- here on detecting error | `------------------------------------*/ yyerrlab: /* Make sure we have latest lookahead translation. See comments at user semantic actions for why this is necessary. */ yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar); /* If not already recovering from an error, report this error. */ if (!yyerrstatus) { ++yynerrs; #if ! YYERROR_VERBOSE yyerror (YY_("syntax error")); #else # define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \ yyssp, yytoken) { char const *yymsgp = YY_("syntax error"); int yysyntax_error_status; yysyntax_error_status = YYSYNTAX_ERROR; if (yysyntax_error_status == 0) yymsgp = yymsg; else if (yysyntax_error_status == 1) { if (yymsg != yymsgbuf) YYSTACK_FREE (yymsg); yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc); if (!yymsg) { yymsg = yymsgbuf; yymsg_alloc = sizeof yymsgbuf; yysyntax_error_status = 2; } else { yysyntax_error_status = YYSYNTAX_ERROR; yymsgp = yymsg; } } yyerror (yymsgp); if (yysyntax_error_status == 2) goto yyexhaustedlab; } # undef YYSYNTAX_ERROR #endif } if (yyerrstatus == 3) { /* If just tried and failed to reuse lookahead token after an error, discard it. */ if (yychar <= YYEOF) { /* Return failure if at end of input. */ if (yychar == YYEOF) YYABORT; } else { yydestruct ("Error: discarding", yytoken, &yylval); yychar = YYEMPTY; } } /* Else will try to reuse lookahead token after shifting the error token. */ goto yyerrlab1; /*---------------------------------------------------. | yyerrorlab -- error raised explicitly by YYERROR. | `---------------------------------------------------*/ yyerrorlab: /* Pacify compilers like GCC when the user code never invokes YYERROR and the label yyerrorlab therefore never appears in user code. */ if (/*CONSTCOND*/ 0) goto yyerrorlab; /* Do not reclaim the symbols of the rule which action triggered this YYERROR. */ YYPOPSTACK (yylen); yylen = 0; YY_STACK_PRINT (yyss, yyssp); yystate = *yyssp; goto yyerrlab1; /*-------------------------------------------------------------. | yyerrlab1 -- common code for both syntax error and YYERROR. | `-------------------------------------------------------------*/ yyerrlab1: yyerrstatus = 3; /* Each real token shifted decrements this. */ for (;;) { yyn = yypact[yystate]; if (!yypact_value_is_default (yyn)) { yyn += YYTERROR; if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) { yyn = yytable[yyn]; if (0 < yyn) break; } } /* Pop the current state because it cannot handle the error token. */ if (yyssp == yyss) YYABORT; yydestruct ("Error: popping", yystos[yystate], yyvsp); YYPOPSTACK (1); yystate = *yyssp; YY_STACK_PRINT (yyss, yyssp); } *++yyvsp = yylval; /* Shift the error token. */ YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); yystate = yyn; goto yynewstate; /*-------------------------------------. | yyacceptlab -- YYACCEPT comes here. | `-------------------------------------*/ yyacceptlab: yyresult = 0; goto yyreturn; /*-----------------------------------. | yyabortlab -- YYABORT comes here. | `-----------------------------------*/ yyabortlab: yyresult = 1; goto yyreturn; #if !defined(yyoverflow) || YYERROR_VERBOSE /*-------------------------------------------------. | yyexhaustedlab -- memory exhaustion comes here. | `-------------------------------------------------*/ yyexhaustedlab: yyerror (YY_("memory exhausted")); yyresult = 2; /* Fall through. */ #endif yyreturn: if (yychar != YYEMPTY) { /* Make sure we have latest lookahead translation. See comments at user semantic actions for why this is necessary. */ yytoken = YYTRANSLATE (yychar); yydestruct ("Cleanup: discarding lookahead", yytoken, &yylval); } /* Do not reclaim the symbols of the rule which action triggered this YYABORT or YYACCEPT. */ YYPOPSTACK (yylen); YY_STACK_PRINT (yyss, yyssp); while (yyssp != yyss) { yydestruct ("Cleanup: popping", yystos[*yyssp], yyvsp); YYPOPSTACK (1); } #ifndef yyoverflow if (yyss != yyssa) YYSTACK_FREE (yyss); #endif #if YYERROR_VERBOSE if (yymsg != yymsgbuf) YYSTACK_FREE (yymsg); #endif /* Make sure YYID is used. */ return YYID (yyresult); } /* Line 2067 of yacc.c */ #line 776 "configfile.y" int yyerror (const char *s) { #undef FMT #define FMT "line %d: %s" errbuff = xmalloc (strlen (s) + strlen (FMT) + 20) ; sprintf (errbuff,FMT,lineCount,s) ; return 0 ; } int yywrap (void) { return 1 ; } extern FILE *yyin ; int yydebug ; #define NO_INHERIT 0 #if ! defined (WANT_MAIN) struct peer_table_s { char *peerName ; value *peerValue ; } ; static struct peer_table_s *peerTable ; static int peerTableCount ; static int peerTableIdx ; void configCleanup (void) { int i ; for (i = 0 ; i < peerTableIdx ; i++) free (peerTable[i].peerName) ; free (peerTable) ; freeScopeTree (topScope); free (funcs) ; free (args) ; } int buildPeerTable (FILE *fp, scope *s) { int rval = 1 ; int i, j ; for (i = 0 ; i < s->value_idx ; i++) { if (ISSCOPE (s->values[i]) && ISPEER (s->values[i])) { for (j = 0 ; j < peerTableIdx ; j++) { if (strcmp (peerTable[j].peerName,s->values[i]->name) == 0) { logOrPrint (LOG_ERR,fp, "ME config: two peers with the same name: %s", peerTable[j].peerName) ; rval = 0 ; break ; } } if (j == peerTableIdx) { if (peerTableCount == peerTableIdx) { peerTableCount += 10 ; if (peerTable == NULL) peerTable = xmalloc (sizeof(struct peer_table_s) * peerTableCount) ; else peerTable = xrealloc (peerTable, sizeof(struct peer_table_s) * peerTableCount) ; } peerTable[peerTableIdx].peerName = xstrdup (s->values[i]->name); peerTable[peerTableIdx].peerValue = s->values[i] ; peerTableIdx++ ; } } else if (ISSCOPE (s->values[i])) rval = (buildPeerTable (fp,s->values[i]->v.scope_val) && rval) ; } return rval ; } /* read the config file. Any errors go to errorDest if it is non-NULL, otherwise they are syslogged. If justCheck is true then return after parsing */ static int inited = 0 ; int readConfig (const char *file, FILE *errorDest, int justCheck, int dump) { scope *oldTop = topScope ; FILE *fp ; int rval ; if (!inited) { inited = 1 ; yydebug = (getenv ("YYDEBUG") == NULL ? 0 : 1) ; if (yydebug) atexit (configCleanup) ; } if (file == NULL || strlen (file) == 0 || !fileExistsP (file)) { logOrPrint (LOG_ERR,errorDest, "ME config aborting, no such config file: %s", file ? file : "(null)") ; d_printf (1,"No such config file: %s\n", file ? file : "(null)") ; exit (1) ; } if ((fp = fopen (file,"r")) == NULL) { logOrPrint (LOG_ERR,errorDest, "ME config aborting fopen %s: %s", file, strerror (errno)) ; exit (1) ; } logOrPrint (LOG_NOTICE,errorDest,"loading %s", file) ; yyin = fp ; topScope = NULL ; rval = yyparse () ; fclose (fp) ; if (rval != 0) /* failure */ { freeScopeTree (topScope) ; if (justCheck) freeScopeTree (oldTop) ; else topScope = oldTop ; topScope = NULL ; if (errbuff != NULL) { if (errorDest != NULL) fprintf (errorDest,"config file error: %s\n",errbuff) ; else warn ("ME config file error: %s", errbuff) ; free (errbuff) ; } return 0 ; } if (dump) { fprintf (errorDest ? errorDest : stderr,"Parsed config file:\n") ; printScope (errorDest ? errorDest : stderr,topScope,-5) ; fprintf (errorDest ? errorDest : stderr,"\n") ; } if (justCheck) { freeScopeTree (topScope) ; freeScopeTree (oldTop) ; topScope = NULL ; } else { for (peerTableIdx-- ; peerTableIdx >= 0 ; peerTableIdx--) { free (peerTable [peerTableIdx].peerName) ; peerTable [peerTableIdx].peerName = NULL ; peerTable [peerTableIdx].peerValue = NULL ; } peerTableIdx = 0 ; if (!buildPeerTable (errorDest,topScope)) logAndExit (1,"Failed to build list of peers") ; } return 1 ; } value *getNextPeer (int *cookie) { value *rval ; if (*cookie < 0 || *cookie >= peerTableIdx) return NULL ; rval = peerTable[*cookie].peerValue ; (*cookie)++ ; return rval ; } value *findPeer (const char *name) { value *v = NULL ; int i ; for (i = 0 ; i < peerTableIdx ; i++) if (strcmp (peerTable[i].peerName,name) == 0) { v = peerTable[i].peerValue ; break ; } return v ; } #endif #if defined (WANT_MAIN) int main (int argc, char **argv) { if ( yyparse() ) printf ("parsing failed: %s\n",errbuff ? errbuff : "NONE") ; else { printScope (stdout,topScope,-5) ; if (argc == 3) { #if 0 printf ("Looking for %s of type %s: ",argv[2],argv[1]) ; if (strncmp (argv[1],"int",3) == 0) { int i = 0 ; if (!getInteger (topScope,argv[2],&i)) printf ("wasn't found.\n") ; else printf (" %d\n",i) ; } else if (strncmp (argv[1],"real",4) == 0) { double d = 0.0 ; if (!getReal (topScope,argv[2],&d)) printf ("wasn't found.\n") ; else printf (" %0.5f\n",d) ; } #else value *v = findValue (topScope,argv[1],1) ; if (v == NULL) printf ("Can't find %s\n",argv[1]) ; else { long ival = 987654 ; if (getInteger (v->v.scope_val,argv[2],&ival,1)) printf ("Getting %s : %ld",argv[2],ival) ; else printf ("Name is not legal: %s\n",argv[2]) ; } #endif } else if (argc == 2) { #if 1 value *v = findValue (topScope,argv[1],1) ; if (v == NULL) printf ("Can't find %s\n",argv[1]) ; else { printf ("Getting %s : ",argv[1]) ; printValue (stdout,v,0) ; } #else if (findScope (topScope,argv[1],1) == NULL) printf ("Can't find the scope of %s\n",argv[1]) ; #endif } } freeScopeTree (topScope) ; return 0 ; } #endif /* defined (WANT_MAIN) */ inn-2.6.0/innfeed/host.h0000644000175200017520000001547612575023702014473 0ustar iuliusiulius/* $Id: host.h 7786 2008-04-25 11:55:39Z iulius $ ** ** The public interface to the Host class. ** ** Written by James Brister ** ** The Host class represents the remote news system that we're feeding. A ** Host object has possibly multiple connections to the remote system which ** it sends articles down. It is given the articles by other objects ** (typically the InnListener), and once taken it assumes all responsibility ** for transmission or temporary storage on network failures etc. */ #if ! defined ( host_h__ ) #define host_h__ #include #include "misc.h" /* * Functions from elsewhere used by host.c */ extern void mainLogStatus (FILE *fp) ; /* * Functions used by the InnListener */ /* * Create a new Host object. * * NAME is the name that INN uses. * IPNAME is the name the networking code uses (or the ascii dotted quad * IP address). * ARTTIMEOUT is the max amount of time we'll wait for a new article * from INN before considering the connection unused and we'll close * down. * RESPTIMEOUT is the max amount of time we'll wait for any reponse * from a remote. Past this we'll close down the network connection. * INITIALCXNS is the number of Connections to create at Host creation time. * MAXCXNS is the maximum number of parallel connections to the * remote host we can run at any one time. * MAXCHECK is the maximum number of nntp CHECK commands to be outstanding * on a connection before opening up a new connection (or refusing * new articles if we get to MAXCXNS). * PORTNUM is the port number on the remote host we should talk to. * CLOSEPERIOD is the number of seconds after connecting that the * connections should be closed down and reinitialized (due to problems * with old NNTP servers that hold history files open. Value of 0 means * dont close down. * STREAMING is a boolean flag to tell if the Host wants its Connections to * do streaming or not. * LOWPASSHIGH is the high value for the low-pass filter. * LOWPASSLOW is the low value for the low-pass filter. */ void configHosts (bool talkSelf) ; /* print some debugging info. */ void gPrintHostInfo (FILE *fp, unsigned int indentAmt) ; void printHostInfo (Host host, FILE *fp, unsigned int indentAmt) ; /* Delete the host object. Drops all the active connections immediately (i.e. no QUIT) . */ void delHost (Host host) ; /* Get a new default host object */ Host newDefaultHost (InnListener listener, const char *name); /* gently close down all the host's connections (issue QUITs). */ void hostClose (Host host) ; /* gently close down all active connections (issue QUITs) and recreate them immediately */ void hostFlush (Host host) ; /* have the HOST transmit the ARTICLE, or, failing that, store article information for later attempts. */ void hostSendArticle (Host host, Article article) ; /* return an IP address for the host */ struct sockaddr *hostIpAddr (Host host) ; /* Delete all IPv4 addresses from the address list. */ void hostDeleteIpv4Addr (Host host); /* mark the current IP address as failed and rotate to the next one */ void hostIpFailed (Host host) ; /* * Functions used by the Connection to indicate Connection state. */ /* called by the Host's connection when the remote is refusing postings. Code 400 in the banner */ void hostCxnBlocked (Host host, Connection cxn, char *reason) ; /* called by the Connection when it has determined if the remote supports the streaming extension or not. */ void hostRemoteStreams (Host host, Connection cxn, bool doesStream) ; /* Called by the connection when it is no longer connected to the remote. Perhaps due to getting a code 400 to an IHAVE. */ void hostCxnDead (Host host, Connection cxn) ; /* Called when the Connection deletes itself */ bool hostCxnGone (Host host, Connection cxn) ; /* Called when the Connection goes to sleep. */ void hostCxnSleeping (Host host, Connection cxn) ; /* Called when the Connection starts waiting for articles. */ void hostCxnWaiting (Host host, Connection cxn) ; /* Called when the connection has sent an IHAVE or a CHECK, or a TAKETHIS when in no-check mode.*/ void hostArticleOffered (Host host, Connection cxn) ; /* called by the Connection when the article was transferred. */ void hostArticleAccepted (Host host, Connection cxn, Article article) ; /* Called by the connection when the remote answered 435 or 438 */ void hostArticleNotWanted (Host host, Connection cxn, Article article) ; /* Called by the connection when the remote answered 437 or 439 */ void hostArticleRejected (Host host, Connection cxn, Article article) ; /* Called when the connection when the remote answered 400 or 431 or 436 */ void hostArticleDeferred (Host host, Connection cxn, Article article) ; /* Called by the connection if it discovers the file is gone. */ void hostArticleIsMissing (Host host, Connection cxn, Article article) ; /* Called by the connection when it wants to defer articles, but it doesn't want the Host to queue any news on it. */ void hostTakeBackArticle (Host host, Connection cxn, Article article) ; /* called by the Connection when it is idle and wants to get things moving. Returns true if there was something to do and the Host called cxnQueueArticle() . */ bool hostGimmeArticle (Host host, Connection cxn) ; /* get the name that INN uses for this host */ const char *hostPeerName (Host host) ; /* Get the bindaddress. */ const char *hostBindAddr (Host host) ; const char *hostBindAddr6 (Host host) ; /* get the username and password for authentication */ const char *hostUsername (Host host) ; const char *hostPassword (Host host) ; /* if VAL is true then each time the host logs its stats all its connections will too. */ void hostLogConnectionStats (bool val) ; bool hostLogConnectionStatsP (void) ; #if 0 /* Set the frequency (in seconds) with which we log statistics */ void hostSetStatsPeriod (unsigned int period) ; #endif /* return whether or not the Connections should attempt to stream. */ bool hostWantsStreaming (Host host) ; /* return maxChecks */ unsigned int hostmaxChecks (Host host); /* return if we should drop deferred articles */ bool hostDropDeferred (Host host); /* return the maximum number of CHECKs that can be outstanding */ unsigned int hostMaxChecks (Host host) ; /* Called by the Host's connections when they go into (true) or out of (false) no-CHECK mode. */ void hostLogNoCheckMode (Host host, bool on, double low, double cur, double high) ; /* calculate host backlog statistics */ void gCalcHostBlStat (void) ; /* calculate host global statistics */ void gHostStats (void) ; /* set the pathname of the file to use instead of innfeed.status */ void hostSetStatusFile (const char *filename) ; /* function called when config file is loaded. */ int hostConfigLoadCbk (void *data) ; void hostChkCxns(TimeoutId tid, void *data); #endif /* host_h__ */ inn-2.6.0/innfeed/imap_connection.c0000644000175200017520000034250312575023702016650 0ustar iuliusiulius/* $Id: imap_connection.c 9911 2015-07-04 21:32:56Z iulius $ ** ** Feed articles to an IMAP server via LMTP and IMAP. ** ** Written by Tim Martin. ** ** Instead of feeding articles via nntp to another host this feeds the ** messages via lmtp to a host and the control messages (cancel's etc..) it ** performs via IMAP. This means it has 2 active connections at any given ** time and 2 queues. ** ** When an article comes in it is immediatly placed in the lmtp queue. When ** an article is picked off the lmtp queue for processing first check if it's ** a control message. If so, place it in the IMAP queue. If not, attempt to ** deliver via LMTP. ** ** This attempts to follow the exact same api as connection.c. ** ** TODO: ** ** feed to smtp ** security layers? <--punt on for now ** authname/password per connection object ** untagged IMAP messages */ #include "config.h" #include "clibrary.h" #include "portable/socket.h" #include #include #include #include #include #include #include "inn/messages.h" #include "inn/libinn.h" #include "buffer.h" #include "connection.h" #include "endpoint.h" #include "host.h" #include "innfeed.h" #include "article.h" #include "configfile.h" #ifndef HAVE_SASL_SASL_H # undef HAVE_SASL #endif #ifdef HAVE_SASL # include # include # include /* For Cyrus SASL versions < 2.1.25 (in hexadecimal notation below). */ # if !defined (SASL_VERSION_FULL) || SASL_VERSION_FULL < 0x020119 typedef int (*sasl_callback_ft)(void); # endif #endif #ifndef MAXHOSTNAMELEN #define MAXHOSTNAMELEN 1024 #endif #define IMAP_PORT 143 #ifdef SMTPMODE # define LMTP_PORT 25 #else # define LMTP_PORT 2003 #endif #define IMAP_TAGLENGTH 6 #define QUEUE_MAX_SIZE 250 #define DOSOMETHING_TIMEOUT 60 /* external */ extern char *deliver_username; extern char *deliver_authname; extern char *deliver_password; extern char *deliver_realm; extern char *deliver_rcpt_to; extern char *deliver_to_header; char hostname[MAXHOSTNAMELEN]; char *mailfrom_name = NULL; /* default to no return path */ #ifdef HAVE_SASL static int initialized_sasl = 0; /* weather sasl_client_init() has been called */ #endif /* states the imap connection may be in */ typedef enum { IMAP_DISCONNECTED = 1, IMAP_WAITING, IMAP_CONNECTED_NOTAUTH, IMAP_READING_INTRO, IMAP_WRITING_CAPABILITY, IMAP_READING_CAPABILITY, IMAP_WRITING_STARTAUTH, IMAP_READING_STEPAUTH, IMAP_WRITING_STEPAUTH, IMAP_IDLE_AUTHED, IMAP_WRITING_NOOP, IMAP_READING_NOOP, IMAP_WRITING_CREATE, IMAP_READING_CREATE, IMAP_WRITING_DELETE, IMAP_READING_DELETE, IMAP_WRITING_SELECT, IMAP_READING_SELECT, IMAP_WRITING_SEARCH, IMAP_READING_SEARCH, IMAP_WRITING_STORE, IMAP_READING_STORE, IMAP_WRITING_CLOSE, IMAP_READING_CLOSE, IMAP_WRITING_QUIT, IMAP_READING_QUIT } imap_state_t; typedef enum { LMTP_DISCONNECTED = 1, LMTP_WAITING, LMTP_CONNECTED_NOTAUTH, LMTP_READING_INTRO, LMTP_WRITING_LHLO, LMTP_READING_LHLO, LMTP_WRITING_STARTAUTH, LMTP_READING_STEPAUTH, LMTP_WRITING_STEPAUTH, LMTP_AUTHED_IDLE, LMTP_WRITING_NOOP, LMTP_READING_NOOP, LMTP_READING_RSET, LMTP_READING_MAILFROM, LMTP_READING_RCPTTO, LMTP_READING_DATA, LMTP_READING_CONTENTS, LMTP_WRITING_UPTODATA, LMTP_WRITING_CONTENTS, LMTP_WRITING_QUIT, LMTP_READING_QUIT } lmtp_state_t; typedef struct imap_capabilities_s { int imap4; /* does server support imap4bis? */ int logindisabled; /* does the server allow the login command? */ char *saslmechs; /* supported SASL mechanisms */ } imap_capabilities_t; typedef struct lmtp_capabilities_s { int Eightbitmime; int EnhancedStatusCodes; int pipelining; char *saslmechs; } lmtp_capabilities_t; typedef enum { STAT_CONT = 0, STAT_NO = 1, STAT_OK = 2, STAT_FAIL = 3 } imt_stat; /* Message types */ typedef enum { DELIVER, CREATE_FOLDER, CANCEL_MSG, DELETE_FOLDER } control_type_t; typedef struct control_item_s { Article article; char *folder; char *msgid; /* only for cancel's */ unsigned long uid; /* only for cancel's */ } control_item_t; typedef struct article_queue_s { control_type_t type; time_t arrived; time_t nextsend; /* time we should next try to send article */ int trys; int counts_toward_size; union { Article article; control_item_t *control; void *generic; } data; struct article_queue_s *next; } article_queue_t; typedef struct Q_s { article_queue_t *head; article_queue_t *tail; int size; } Q_t; typedef struct connection_s { /* common stuff */ char *ServerName; char *lmtp_respBuffer; /* buffer all responses are read into */ Buffer lmtp_rBuffer; /* buffer all responses are read into */ Host myHost ; /* the host who owns the connection */ time_t timeCon ; /* the time the connect happened (last auth succeeded) */ int issue_quit; /* Three states: * 0 - don't do anything * 1 - after issue quit enter wait state * 2 - after issue quit reconnect * 3 - after issue quit delete connection * 4 - nuke cxn */ /* Statistics */ int lmtp_succeeded; int lmtp_failed; int cancel_succeeded; int cancel_failed; int create_succeeded; int create_failed; int remove_succeeded; int remove_failed; /* LMTP stuff */ int lmtp_port; lmtp_state_t lmtp_state; #ifdef HAVE_SASL sasl_conn_t *saslconn_lmtp; #endif /* HAVE_SASL */ int sockfd_lmtp; time_t lmtp_timeCon ; EndPoint lmtp_endpoint; unsigned int ident ; /* an identifier for syslogging. */ lmtp_capabilities_t *lmtp_capabilities; int lmtp_disconnects; char *lmtp_tofree_str; article_queue_t *current_article; Buffer *current_bufs; int current_rcpts_issued; int current_rcpts_okayed; /* Timer for the max amount of time to wait for a response from the remote */ unsigned int lmtp_readTimeout ; TimeoutId lmtp_readBlockedTimerId ; /* Timer for the max amount of time to wait for a any amount of data to be written to the remote */ unsigned int lmtp_writeTimeout ; TimeoutId lmtp_writeBlockedTimerId ; /* Timer for the number of seconds to sleep before attempting a reconnect. */ unsigned int lmtp_sleepTimeout ; TimeoutId lmtp_sleepTimerId ; /* Timer for max amount between queueing some articles and trying to send them */ unsigned int dosomethingTimeout ; TimeoutId dosomethingTimerId ; Q_t lmtp_todeliver_q; /* IMAP stuff */ int imap_port; #ifdef HAVE_SASL sasl_conn_t *imap_saslconn; #endif /* HAVE_SASL */ char *imap_respBuffer; Buffer imap_rBuffer; EndPoint imap_endpoint; imap_capabilities_t *imap_capabilities; int imap_sockfd; time_t imap_timeCon ; imap_state_t imap_state; int imap_disconnects; char *imap_tofree_str; char imap_currentTag[IMAP_TAGLENGTH+1]; int imap_tag_num; /* Timer for the max amount of time to wait for a response from the remote */ unsigned int imap_readTimeout ; TimeoutId imap_readBlockedTimerId ; /* Timer for the max amount of time to wait for a any amount of data to be written to the remote */ unsigned int imap_writeTimeout ; TimeoutId imap_writeBlockedTimerId ; /* Timer for the number of seconds to sleep before attempting a reconnect. */ unsigned int imap_sleepTimeout ; TimeoutId imap_sleepTimerId ; Q_t imap_controlMsg_q; article_queue_t *current_control; struct connection_s *next; } connection_t; static Connection gCxnList = NULL ; static unsigned int gCxnCount= 0 ; unsigned int max_reconnect_period = MAX_RECON_PER ; unsigned int init_reconnect_period = INIT_RECON_PER; typedef enum { RET_OK = 0, RET_FAIL = 1, RET_QUEUE_EMPTY, RET_EXCEEDS_SIZE, RET_NO_FULLLINE, RET_NO, RET_ARTICLE_BAD } conn_ret; /********** Private Function Declarations *************/ static void lmtp_readCB (EndPoint e, IoStatus i, Buffer *b, void *d); static void imap_readCB (EndPoint e, IoStatus i, Buffer *b, void *d); static void imap_writeCB (EndPoint e, IoStatus i, Buffer *b, void *d); static void lmtp_writeCB (EndPoint e, IoStatus i, Buffer *b, void *d); static conn_ret lmtp_Connect(connection_t *cxn); static conn_ret imap_Connect(connection_t *cxn); static void prepareReopenCbk (Connection cxn, int type); static void lmtp_readTimeoutCbk (TimeoutId id, void *data); static void imap_readTimeoutCbk (TimeoutId id, void *data); static void dosomethingTimeoutCbk (TimeoutId id, void *data); static conn_ret WriteToWire_imapstr(connection_t *cxn, char *str, int slen); static conn_ret WriteToWire_lmtpstr(connection_t *cxn, char *str, int slen); static conn_ret WriteToWire(connection_t *cxn, EndpRWCB callback, EndPoint endp, Buffer *array); static void lmtp_sendmessage(connection_t *cxn, Article justadded); static void imap_ProcessQueue(connection_t *cxn); static conn_ret FindHeader(Buffer *bufs, const char *header, char **start, char **end); static conn_ret PopFromQueue(Q_t *q, article_queue_t **item); enum failure_type { MSG_SUCCESS = 0, MSG_FAIL_DELIVER = 1, MSG_GIVE_BACK = 2, MSG_MISSING = 3 }; static void QueueForgetAbout(connection_t *cxn, article_queue_t *item, enum failure_type failed); static void delConnection (Connection cxn); static void DeleteIfDisconnected(Connection cxn); static void DeferAllArticles(connection_t *cxn, Q_t *q); static void lmtp_Disconnect(connection_t *cxn); static void imap_Disconnect(connection_t *cxn); static conn_ret imap_listenintro(connection_t *cxn); static void imap_writeTimeoutCbk (TimeoutId id, void *data); static void lmtp_writeTimeoutCbk (TimeoutId id, void *data); /******************** PRIVATE FUNCTIONS ***************************/ static const char *imap_stateToString(int state) { switch (state) { case IMAP_DISCONNECTED: return "disconnected"; case IMAP_WAITING: return "waiting"; case IMAP_CONNECTED_NOTAUTH: return "connected (unauthenticated)"; case IMAP_READING_INTRO: return "reading intro"; case IMAP_WRITING_CAPABILITY: return "writing CAPABILITY"; case IMAP_READING_CAPABILITY: return "reading CAPABILITY"; case IMAP_WRITING_STARTAUTH: return "writing AUTHENTICATE"; case IMAP_READING_STEPAUTH: return "reading stepauth"; case IMAP_WRITING_STEPAUTH: return "writing stepauth"; case IMAP_IDLE_AUTHED: return "idle (authenticated)"; case IMAP_WRITING_NOOP: return "writing NOOP"; case IMAP_READING_NOOP: return "reading NOOP response"; case IMAP_WRITING_CREATE: return "writing CREATE"; case IMAP_READING_CREATE: return "reading CREATE response"; case IMAP_WRITING_DELETE: return "writing DELETE command"; case IMAP_READING_DELETE: return "reading DELETE response"; case IMAP_WRITING_SELECT: return "writing SELECT"; case IMAP_READING_SELECT: return "reading SELECT response"; case IMAP_WRITING_SEARCH: return "writing SEARCH"; case IMAP_READING_SEARCH: return "reading SEARCH response"; case IMAP_WRITING_STORE: return "writing STORE"; case IMAP_READING_STORE: return "reading STORE response"; case IMAP_WRITING_CLOSE: return "writing CLOSE"; case IMAP_READING_CLOSE: return "reading CLOSE response"; case IMAP_WRITING_QUIT: return "writing LOGOUT"; case IMAP_READING_QUIT: return "reading LOGOUT response"; default: return "Unknown state"; } } static const char *lmtp_stateToString(int state) { switch(state) { case LMTP_DISCONNECTED: return "disconnected"; case LMTP_WAITING: return "waiting"; case LMTP_CONNECTED_NOTAUTH: return "connected (unauthenticated)"; case LMTP_READING_INTRO: return "reading intro"; case LMTP_WRITING_LHLO: return "writing LHLO"; case LMTP_READING_LHLO: return "reading LHLO response"; case LMTP_WRITING_STARTAUTH: return "writing AUTH"; case LMTP_READING_STEPAUTH: return "reading stepauth"; case LMTP_WRITING_STEPAUTH: return "writing stepauth"; case LMTP_AUTHED_IDLE: return "idle (authenticated)"; case LMTP_WRITING_NOOP: return "writing NOOP"; case LMTP_READING_NOOP: return "reading NOOP response"; case LMTP_READING_RSET: return "reading RSET response"; case LMTP_READING_MAILFROM: return "reading MAIL FROM response"; case LMTP_READING_RCPTTO: return "reading RCPT TO response"; case LMTP_READING_DATA: return "reading DATA response"; case LMTP_READING_CONTENTS: return "reading contents response"; case LMTP_WRITING_UPTODATA: return "writing RSET, MAIL FROM, RCPT TO, DATA commands"; case LMTP_WRITING_CONTENTS: return "writing contents of message"; case LMTP_WRITING_QUIT: return "writing QUIT"; case LMTP_READING_QUIT: return "reading QUIT"; default: return "unknown state"; } } /******************************* Queue functions ***********************************/ /* * Add a message to a generic queue * * q - the queue adding to * item - the data to add to the queue * type - the type of item it is (i.e. cancel,lmtp,etc..) * addsmsg - weather this should be counted toward the queue size * this is for control msg's that create multiple queue items. * For example a cancel message canceling a message in multiple * newsgroups will create >1 queue item but we only want it to count * once towards the queue * must - wheather we must take it even though it may put us over our max size */ static conn_ret AddToQueue(Q_t *q, void *item, control_type_t type, int addsmsg, bool must) { article_queue_t *newentry; if (must == false) { if (q->size >= QUEUE_MAX_SIZE) { return RET_EXCEEDS_SIZE; } } else { if (q->size >= QUEUE_MAX_SIZE * 10) { d_printf(0, "Queue has grown way too much. Dropping article\n"); return RET_FAIL; } } /* add to the end of our queue */ newentry = xmalloc(sizeof(article_queue_t)); newentry->type = type; /* send as soon as possible */ newentry->nextsend = newentry->arrived = time(NULL); newentry->trys = 0; newentry->data.generic = item; newentry->next = NULL; newentry->counts_toward_size = addsmsg; /* add to end of queue */ if (q->tail == NULL) { q->head = newentry; q->tail = newentry; } else { q->tail->next = newentry; q->tail = newentry; } q->size+=addsmsg; return RET_OK; } /* * Pop an item from the queue * * q - the queue to pop from * item - where the item shall be placed upon sucess * */ static conn_ret PopFromQueue(Q_t *q, article_queue_t **item) { /* if queue empty return error */ if ( q->head == NULL) { return RET_QUEUE_EMPTY; } /* set what we return */ *item = q->head; q->head = q->head->next; if (q->head == NULL) q->tail = NULL; q->size-=(*item)->counts_toward_size; return RET_OK; } /* * ReQueue an item. Will either put it back in the queue for another try * or forget about it * * cxn - our connection object (needed so forget about things) * q - the queue to requeue to * entry - the item to put back */ static void ReQueue(connection_t *cxn, Q_t *q, article_queue_t *entry) { /* look at the time it's been here */ entry->nextsend = time(NULL) + (entry->trys *30); /* xxx better formula? */ entry->trys++; /* give up after 5 tries xxx configurable??? */ if (entry->trys >= 5) { QueueForgetAbout(cxn, entry, MSG_FAIL_DELIVER); return; } /* ok let's add back to the end of the queue */ entry->next = NULL; /* add to end of queue */ if (q->tail == NULL) { q->head = entry; q->tail = entry; } else { q->tail->next = entry; q->tail = entry; } q->size+=entry->counts_toward_size; } /* * Forget about an item. Tells host object if we succeeded/failed/etc with the message * * cxn - connection object * item - item * failed - type of failure (see below) * * failed: * 0 - succeeded delivering message * 1 - failed delivering message * 2 - Try to give back to host * 3 - Article missing (i.e. can't find on disk) */ static void QueueForgetAbout(connection_t *cxn, article_queue_t *item, enum failure_type failed) { Article art = NULL; switch (item->type) { case DELIVER: if (failed>0) cxn->lmtp_failed++; art = item->data.article; break; case CANCEL_MSG: if (failed>0) cxn->cancel_failed++; free(item->data.control->msgid); free(item->data.control->folder); if (item->counts_toward_size == 1) art = item->data.control->article; free(item->data.control ); break; case CREATE_FOLDER: if (failed>0) cxn->create_failed++; free(item->data.control->folder); art = item->data.control->article; free(item->data.control ); break; case DELETE_FOLDER: if (failed>0) cxn->remove_failed++; free(item->data.control->folder); art = item->data.control->article; free(item->data.control ); break; default: d_printf(0, "%s:%d QueueForgetAbout(): " "Unknown type to forget about\n", hostPeerName (cxn->myHost), cxn->ident); break; } if (art!=NULL) { switch (failed) { case MSG_SUCCESS: hostArticleAccepted (cxn->myHost, cxn, art); break; case MSG_FAIL_DELIVER: hostArticleRejected (cxn->myHost, cxn, art); break; case MSG_GIVE_BACK: hostTakeBackArticle (cxn->myHost, cxn, art); break; case MSG_MISSING: hostArticleIsMissing(cxn->myHost, cxn, art); break; default: d_printf(0,"%s:%d QueueForgetAbout(): failure type unknown\n", hostPeerName (cxn->myHost), cxn->ident); break; } } free(item); } /* * How much space is available in the queue */ static int QueueSpace(Q_t *q) { int ret = QUEUE_MAX_SIZE - q->size; if (ret < 0) ret = 0; return ret; } /* * How many items are in the queue */ static int QueueItems(Q_t *q) { return q->size; } /***************************** END Queue functions ***********************************/ /***************************** Generic Parse Functions *******************************/ /* returns the end of the header */ static char *GetUntil(char *str) { while (((*str) != '\0') && ( (*str) != '\r') && ( (*str) != '\n')) { str++; } return str; } static char *GotoNextLine(char *str) { while (((*str) != '\0') && ( (*str) != '\r') && ( (*str) != '\n')) { str++; } if (*str == '\r') str++; if (*str == '\n') str++; return str; } /* * Finds the given header in the message * Returns NULL if not found */ static conn_ret FindHeader(Buffer *bufs, const char *header, char **start, char **end) { Buffer b; int size; char *str_base; char *str; int headerlen = strlen(header); if (bufs==NULL) { if (start) *start=NULL; return RET_ARTICLE_BAD; } b = bufs[0]; size = bufferSize(b); str_base = bufferBase(b); str = str_base; while ((str - str_base) < size - headerlen) { if (*str == header[0]) { if ((strncasecmp(header, str, headerlen)==0) && ( *(str + headerlen)==':')) { if (start) { *start = str+headerlen+1; /* get rid of leading whitespace */ while (isspace((unsigned char) **start)) (*start)++; } if (end) *end = GetUntil(str+headerlen+1); return RET_OK; } } else if (*str == '\n') { /* end of headers */ return RET_NO; } str = GotoNextLine(str); } return RET_NO; } static conn_ret GetLine(char *buf, char *ret, int retmaxsize) { char *str_base; char *str; int size = strlen(buf); str_base = buf; str = str_base; while ( (*str) != '\0') { if ((*str) == '\n') { if (str-str_base > retmaxsize) { d_printf(0, "Max size exceeded! %s\n",str_base); return RET_FAIL; } /* fill in the return string */ memcpy(ret, str_base, str-str_base); ret[ str - str_base -1] = '\0'; memcpy( str_base, str_base + (str-str_base)+1, size - (str-str_base)); str_base[size - (str-str_base)]='\0'; return RET_OK; } str++; } /* couldn't find a full line */ return RET_NO_FULLLINE; } /************************** END Generic Parse Functions *******************************/ /************************ Writing to Network functions *****************/ static conn_ret WriteToWire(connection_t *cxn, EndpRWCB callback, EndPoint endp, Buffer *array) { if (array == NULL) return RET_FAIL; prepareWrite (endp, array, NULL, callback, cxn); return RET_OK; } static conn_ret WriteToWire_str(connection_t *cxn, EndpRWCB callback, EndPoint endp, char *str, int slen) { conn_ret result; Buffer buff; Buffer *writeArr; if (slen==-1) slen = strlen(str); buff = newBufferByCharP(str, slen+1, slen); ASSERT (buff != NULL); writeArr = makeBufferArray (buff, NULL) ; result = WriteToWire(cxn, callback, endp, writeArr); return result; } static conn_ret WriteToWire_imapstr(connection_t *cxn, char *str, int slen) { /* prepare the timeouts */ clearTimer (cxn->imap_readBlockedTimerId) ; /* set up the write timer. */ clearTimer (cxn->imap_writeBlockedTimerId) ; if (cxn->imap_writeTimeout > 0) cxn->imap_writeBlockedTimerId = prepareSleep (imap_writeTimeoutCbk, cxn->imap_writeTimeout, cxn); cxn->imap_tofree_str = str; return WriteToWire_str(cxn, imap_writeCB, cxn->imap_endpoint, str, slen); } static conn_ret WriteToWire_lmtpstr(connection_t *cxn, char *str, int slen) { /* prepare the timeouts */ clearTimer (cxn->lmtp_readBlockedTimerId) ; /* set up the write timer. */ clearTimer (cxn->lmtp_writeBlockedTimerId) ; if (cxn->lmtp_writeTimeout > 0) cxn->lmtp_writeBlockedTimerId = prepareSleep (lmtp_writeTimeoutCbk, cxn->lmtp_writeTimeout, cxn) ; cxn->lmtp_tofree_str = str; return WriteToWire_str(cxn, lmtp_writeCB, cxn->lmtp_endpoint, str, slen); } static conn_ret WriteArticle(connection_t *cxn, Buffer *array) { conn_ret result; /* Just call WriteToWire since it's easy. */ result = WriteToWire(cxn, lmtp_writeCB, cxn->lmtp_endpoint, array); if (result != RET_OK) { return result; } cxn->lmtp_state = LMTP_WRITING_CONTENTS; return RET_OK; } /************************ END Writing to Network functions *****************/ /* * Adds a cancel item to the control queue * Cancel item to delete message with in * * cxn - connection object * folder - pointer to start of folder string (this is a pointer into the actual message buffer) * folderlen - length of folder string * msgid - pointer to start of msgid string (this is a pointer into the actual message buffer) * msgidlen - length of msgid string * art - the article for this control message (NULL if this cancel object lacks one) * must - if must be accepted into queue */ static conn_ret addCancelItem(connection_t *cxn, char *folder, int folderlen, char *msgid, int msgidlen, Article art, int must) { control_item_t *item; conn_ret result; int i; ASSERT(folder); ASSERT(msgid); ASSERT(cxn); /* sanity check folder, msgid */ for (i = 0; i < folderlen; i++) ASSERT(!isspace((unsigned char) folder[i])); for (i = 0; i < msgidlen; i++) ASSERT(!isspace((unsigned char) msgid[i])); /* create the object */ item = xcalloc (1, sizeof(control_item_t)); item->folder = xcalloc(folderlen+1, 1); memcpy(item->folder, folder, folderlen); item->folder[folderlen] = '\0'; item->msgid = xcalloc (msgidlen+1, 1); memcpy(item->msgid, msgid, msgidlen); item->msgid[msgidlen] = '\0'; item->article = art; /* try to add to the queue (counts if art isn't null) */ result = AddToQueue(&(cxn->imap_controlMsg_q), item, CANCEL_MSG, (art != NULL), must); if (result != RET_OK) { d_printf(1,"%s:%d addCancelItem(): " "I thought we had in space in [imap] queue " "but apparently not\n", hostPeerName (cxn->myHost), cxn->ident); /* cleanup */ free(item->folder); free(item->msgid); free(item); return result; } return RET_OK; } static conn_ret AddControlMsg(connection_t *cxn, Article art, Buffer *bufs, char *control_header, char *control_header_end, bool must) { char *rcpt_list = NULL, *rcpt_list_end; control_item_t *item; conn_ret res = RET_OK; int t; /* make sure contents ok; this also should load it into memory */ if (!artContentsOk (art)) { d_printf(0, "%s:%d AddControlMsg(): " "artContentsOk() said article was bad\n", hostPeerName (cxn->myHost), cxn->ident); hostArticleIsMissing (cxn->myHost, cxn, art); return RET_FAIL; } /* now let's look at the control to see what it is */ if (!strncasecmp(control_header,"newgroup",8)) { control_header += 8; t = CREATE_FOLDER; } else if (!strncasecmp(control_header,"rmgroup",7)) { /* jump past "rmgroup" */ control_header += 7; t = DELETE_FOLDER; } else if (!strncasecmp(control_header,"cancel",6)) { t = CANCEL_MSG; control_header += 6; } else { /* unrecognized type */ char tmp[100]; char *endstr; size_t clen; endstr = strchr(control_header,'\n'); clen = endstr - control_header; if (clen > sizeof(tmp)-1) clen = sizeof(tmp)-1; memcpy(tmp,control_header, clen); tmp[clen]='\0'; d_printf(0,"%s:%d Don't understand control header [%s]\n", hostPeerName (cxn->myHost), cxn->ident,tmp); return RET_FAIL; } switch (t) { case CREATE_FOLDER: case DELETE_FOLDER: { int folderlen; /* go past all white space */ while ((*control_header == ' ') && (control_header != control_header_end)) { control_header++; } /* trim trailing whitespace */ while (control_header_end[-1] == ' ') { control_header_end--; } if (control_header >= control_header_end) { d_printf(0,"%s:%d addControlMsg(): " "newgroup/rmgroup header has no group specified\n", hostPeerName (cxn->myHost), cxn->ident); return RET_FAIL; } folderlen = control_header_end - control_header; item = xcalloc(1, sizeof(control_item_t)); item->folder = xcalloc(folderlen + 1, 1); memcpy(item->folder, control_header, folderlen); item->folder[folderlen] = '\0'; item->article = art; if (AddToQueue(&(cxn->imap_controlMsg_q), item, t, 1, must) != RET_OK) { d_printf(1,"%s:%d addCancelItem(): " "I thought we had in space in [imap] queue" " but apparently not\n", hostPeerName (cxn->myHost), cxn->ident); free(item->folder); free(item); return RET_FAIL; } break; } case CANCEL_MSG: { char *str, *laststart; while (((*control_header) == ' ') && (control_header != control_header_end)) { control_header++; } if (control_header == control_header_end) { d_printf(0, "%s:%d Control header contains cancel " "with no msgid specified\n", hostPeerName (cxn->myHost), cxn->ident); return RET_FAIL; } if (FindHeader(bufs, "Newsgroups", &rcpt_list, &rcpt_list_end) != RET_OK) { d_printf(0,"%s:%d Cancel msg contains no newsgroups header\n", hostPeerName (cxn->myHost), cxn->ident); return RET_FAIL; } str = rcpt_list; laststart = rcpt_list; while (str != rcpt_list_end) { if (*str == ',') { /* eliminate leading whitespace */ while (((*laststart) ==' ') || ((*laststart)=='\t')) { laststart++; } res = addCancelItem(cxn, laststart, str - laststart, control_header, control_header_end - control_header, NULL, must); if (res != RET_OK) return res; laststart = str+1; } str++; } if (laststartmyHost), cxn->ident); } return RET_FAIL; } /* * Show msg handling statistics */ static void show_stats(connection_t *cxn) { d_printf(0, "%s:%d\n",hostPeerName (cxn->myHost), cxn->ident); d_printf(0, " imap queue = %d lmtp queue = %d\n", QueueItems(&(cxn->imap_controlMsg_q)), QueueItems(&(cxn->lmtp_todeliver_q))); d_printf(0," imap state = %s\n", imap_stateToString(cxn->imap_state)); d_printf(0," lmtp state = %s\n", lmtp_stateToString(cxn->lmtp_state)); d_printf(0," delivered: yes: %d no: %d\n", cxn->lmtp_succeeded, cxn->lmtp_failed); d_printf(0," cancel: yes: %d no: %d\n", cxn->cancel_succeeded, cxn->cancel_failed); d_printf(0," create: yes: %d no: %d\n", cxn->create_succeeded, cxn->create_failed); d_printf(0," remove: yes: %d no: %d\n", cxn->remove_succeeded, cxn->remove_failed); } /**************************** SASL helper functions ******************************/ #ifdef HAVE_SASL /* callback to get userid or authid */ static int getsimple(void *context UNUSED, int id, const char **result, unsigned *len) { char *authid; if (! result) return SASL_BADPARAM; switch (id) { case SASL_CB_GETREALM: *result = deliver_realm; if (len) *len = deliver_realm ? strlen(deliver_realm) : 0; break; case SASL_CB_USER: *result = deliver_username; if (len) *len = deliver_username ? strlen(deliver_username) : 0; break; case SASL_CB_AUTHNAME: authid=deliver_authname; *result = authid; if (len) *len = authid ? strlen(authid) : 0; break; case SASL_CB_LANGUAGE: *result = NULL; if (len) *len = 0; break; default: return SASL_BADPARAM; } return SASL_OK; } /* callback to get password */ static int getsecret(sasl_conn_t *conn, void *context UNUSED, int id, sasl_secret_t **psecret) { size_t passlen; if (! conn || ! psecret || id != SASL_CB_PASS) return SASL_BADPARAM; if (deliver_password==NULL) { d_printf(0,"SASL requested a password but I don't have one\n"); return SASL_FAIL; } passlen = strlen(deliver_password); *psecret = xmalloc(sizeof(sasl_secret_t) + passlen + 1); if (! *psecret) return SASL_FAIL; strlcpy((char *)(*psecret)->data, deliver_password, passlen + 1); (*psecret)->len = passlen; return SASL_OK; } /* callbacks we support */ static sasl_callback_t saslcallbacks[] = { { SASL_CB_GETREALM, (sasl_callback_ft) &getsimple, NULL }, { SASL_CB_USER, (sasl_callback_ft) &getsimple, NULL }, { SASL_CB_AUTHNAME, (sasl_callback_ft) &getsimple, NULL }, { SASL_CB_PASS, (sasl_callback_ft) &getsecret, NULL }, { SASL_CB_LIST_END, NULL, NULL } }; static sasl_security_properties_t *make_secprops(int min,int max) { sasl_security_properties_t *ret= xmalloc(sizeof(sasl_security_properties_t)); ret->maxbufsize=1024; ret->min_ssf=min; ret->max_ssf=max; ret->security_flags=0; ret->property_names=NULL; ret->property_values=NULL; return ret; } #ifndef NI_WITHSCOPEID #define NI_WITHSCOPEID 0 #endif #ifndef NI_MAXHOST #define NI_MAXHOST 1025 #endif #ifndef NI_MAXSERV #define NI_MAXSERV 32 #endif static int iptostring(const struct sockaddr *addr, socklen_t addrlen, char *out, unsigned outlen) { char hbuf[NI_MAXHOST], pbuf[NI_MAXSERV]; if (!addr || !out) return SASL_BADPARAM; getnameinfo(addr, addrlen, hbuf, sizeof(hbuf), pbuf, sizeof(pbuf), NI_NUMERICHOST | NI_WITHSCOPEID | NI_NUMERICSERV); if(outlen < strlen(hbuf) + strlen(pbuf) + 2) return SASL_BUFOVER; snprintf(out, outlen, "%s;%s", hbuf, pbuf); return SASL_OK; } static conn_ret SetSASLProperties(sasl_conn_t *conn, int sock, int minssf, int maxssf) { sasl_security_properties_t *secprops=NULL; socklen_t addrsize = sizeof(struct sockaddr_in); char localip[60], remoteip[60]; struct sockaddr_in saddr_l; struct sockaddr_in saddr_r; /* create a security structure and give it to sasl */ secprops = make_secprops(minssf, maxssf); if (secprops != NULL) { sasl_setprop(conn, SASL_SEC_PROPS, secprops); free(secprops); } if (getpeername(sock,(struct sockaddr *)&saddr_r,&addrsize)!=0) return RET_FAIL; if (iptostring((struct sockaddr *)&saddr_r, sizeof(struct sockaddr_in), remoteip, sizeof(remoteip))) return RET_FAIL; if (sasl_setprop(conn, SASL_IPREMOTEPORT, remoteip)!=SASL_OK) return RET_FAIL; addrsize=sizeof(struct sockaddr_in); if (getsockname(sock,(struct sockaddr *) &saddr_l,&addrsize)!=0) return RET_FAIL; if (iptostring((struct sockaddr *)&saddr_l, sizeof(struct sockaddr_in), localip, sizeof(localip))) return RET_FAIL; if (sasl_setprop(conn, SASL_IPLOCALPORT, localip)!=SASL_OK) return RET_FAIL; return RET_OK; } #endif /* HAVE_SASL */ /************************** END SASL helper functions ******************************/ /************************* Startup functions **********************************/ static conn_ret Initialize(connection_t *cxn, int respTimeout) { #ifdef HAVE_SASL conn_ret saslresult; #endif /* HAVE_SASL */ d_printf(1,"%s:%d initializing....\n", hostPeerName (cxn->myHost), cxn->ident); #ifdef HAVE_SASL /* only call sasl_client_init() once */ if (initialized_sasl == 0) { /* Initialize SASL */ saslresult=sasl_client_init(saslcallbacks); if (saslresult!=SASL_OK) { d_printf(0, "%s:%d Error initializing SASL (sasl_client_init) (%s)\n", hostPeerName (cxn->myHost), cxn->ident, sasl_errstring(saslresult, NULL, NULL)); return RET_FAIL; } else { initialized_sasl = 1; } } #endif /* HAVE_SASL */ cxn->lmtp_rBuffer = newBuffer(4096); if (cxn->lmtp_rBuffer == NULL) { d_printf(0, "%s:%d Failure allocating buffer for lmtp_rBuffer\n", hostPeerName (cxn->myHost), cxn->ident); return RET_FAIL; } bufferAddNullByte(cxn->lmtp_rBuffer); cxn->imap_rBuffer = newBuffer(4096); if (cxn->imap_rBuffer == NULL) { d_printf(0, "%s:%d Failure allocating buffer for imap_rBuffer \n", hostPeerName (cxn->myHost), cxn->ident); return RET_FAIL; } bufferAddNullByte(cxn->imap_rBuffer); /* Initialize timeouts */ cxn->lmtp_writeTimeout = respTimeout; cxn->lmtp_readTimeout = respTimeout; cxn->imap_writeTimeout = respTimeout; cxn->imap_readTimeout = respTimeout; cxn->lmtp_sleepTimerId = 0 ; cxn->lmtp_sleepTimeout = init_reconnect_period ; cxn->imap_sleepTimerId = 0 ; cxn->imap_sleepTimeout = init_reconnect_period ; cxn->dosomethingTimeout = DOSOMETHING_TIMEOUT; /* set up the write timer. */ clearTimer (cxn->dosomethingTimerId) ; if (cxn->dosomethingTimeout > 0) cxn->dosomethingTimerId = prepareSleep (dosomethingTimeoutCbk, cxn->dosomethingTimeout, cxn); return RET_OK; } /* initialize the network */ static conn_ret init_net(char *serverFQDN, int port, int *sock) { struct sockaddr_in addr; struct hostent *hp; if ((hp = gethostbyname(serverFQDN)) == NULL) { d_printf(0, "gethostbyname(): %s\n", strerror(errno)); return RET_FAIL; } if (( (*sock) = socket(AF_INET, SOCK_STREAM, 0)) < 0) { d_printf(0, "socket(): %s\n", strerror(errno)); return RET_FAIL; } memset(&addr, 0, sizeof addr); addr.sin_family = AF_INET; memcpy(&addr.sin_addr, hp->h_addr, hp->h_length); addr.sin_port = htons(port); if (connect( (*sock), (struct sockaddr *) &addr, sizeof (addr)) < 0) { d_printf(0,"connect(): %s\n", strerror(errno)); return RET_FAIL; } return RET_OK; } static conn_ret SetupLMTPConnection(connection_t *cxn, char *serverName, int port) { #ifdef HAVE_SASL int saslresult; #endif /* HAVE_SASL */ conn_ret result; cxn->lmtp_port = port; if (serverName==NULL) { d_printf(0, "%s:%d serverName is null\n", hostPeerName (cxn->myHost), cxn->ident); return RET_FAIL; } #ifdef HAVE_SASL /* Free the SASL connection if we already had one */ if (cxn->saslconn_lmtp!=NULL) { sasl_dispose(&cxn->saslconn_lmtp); } /* Start SASL */ saslresult=sasl_client_new("lmtp", serverName, NULL, NULL, NULL, 0, &cxn->saslconn_lmtp); if (saslresult != SASL_OK) { d_printf(0, "%s:%d:LMTP Error creating a new SASL connection (%s)\n", hostPeerName (cxn->myHost), cxn->ident, sasl_errstring(saslresult,NULL,NULL)); return RET_FAIL; } #endif /* HAVE_SASL */ /* Connect the Socket */ result = init_net(serverName, LMTP_PORT, /*port,*/ &(cxn->sockfd_lmtp)); if (result != RET_OK) { d_printf(0, "%s:%d unable to connect to lmtp host\n", hostPeerName (cxn->myHost), cxn->ident); return RET_FAIL; } if (cxn->lmtp_respBuffer) free(cxn->lmtp_respBuffer); cxn->lmtp_respBuffer = xmalloc (4096); cxn->lmtp_respBuffer[0]='\0'; /* Free if we had an existing one */ if (cxn->lmtp_endpoint != NULL) { delEndPoint(cxn->lmtp_endpoint); cxn->lmtp_endpoint = NULL; } cxn->lmtp_endpoint = newEndPoint(cxn->sockfd_lmtp); if (cxn->lmtp_endpoint == NULL) { d_printf(0, "%s:%d:LMTP failure creating endpoint\n", hostPeerName (cxn->myHost), cxn->ident); return RET_FAIL; } #ifdef HAVE_SASL /* Set the SASL properties */ result = SetSASLProperties(cxn->saslconn_lmtp, cxn->sockfd_lmtp, 0, 0); if (result != RET_OK) { d_printf(0,"%s:%d:LMTP error setting SASL properties\n", hostPeerName (cxn->myHost), cxn->ident); return RET_FAIL; } #endif /* HAVE_SASL */ return RET_OK; } static conn_ret SetupIMAPConnection(connection_t *cxn, char *serverName, int port) { #ifdef HAVE_SASL int saslresult; #endif /* HAVE_SASL */ conn_ret result; cxn->imap_port = port; if (serverName==NULL) { d_printf(0,"%s:%d:IMAP Servername is null", hostPeerName (cxn->myHost), cxn->ident); return RET_FAIL; } #ifdef HAVE_SASL /* Free the SASL connection if we already had one */ if (cxn->imap_saslconn!=NULL) { sasl_dispose(&cxn->imap_saslconn); } /* Start SASL */ saslresult=sasl_client_new("imap", serverName, NULL, NULL, NULL, 0, &cxn->imap_saslconn); if (saslresult != SASL_OK) { d_printf(0,"%s:%d:IMAP Error creating a new SASL connection (%s)", hostPeerName (cxn->myHost), cxn->ident, sasl_errstring(saslresult,NULL,NULL)); return RET_FAIL; } #endif /* HAVE_SASL */ /* Connect the Socket */ result = init_net(serverName, port, &(cxn->imap_sockfd)); if (result != RET_OK) { d_printf(0,"%s:%d:IMAP Unable to start network connection for IMAP", hostPeerName (cxn->myHost), cxn->ident); return RET_FAIL; } if (cxn->imap_respBuffer) free(cxn->imap_respBuffer); cxn->imap_respBuffer = xmalloc (4096); cxn->imap_respBuffer[0]='\0'; /* Free if we had an existing one */ if (cxn->imap_endpoint != NULL) { delEndPoint(cxn->imap_endpoint); cxn->imap_endpoint = NULL; } cxn->imap_endpoint = newEndPoint(cxn->imap_sockfd); if (cxn->imap_endpoint == NULL) { d_printf(0,"%s:%d:IMAP Failure creating imap endpoint\n", hostPeerName (cxn->myHost), cxn->ident); return RET_FAIL; } #ifdef HAVE_SASL /* Set the SASL properties */ result = SetSASLProperties(cxn->imap_saslconn, cxn->imap_sockfd, 0, 0); if (result != RET_OK) { d_printf(0,"%s:%d:IMAP Error setting sasl properties", hostPeerName (cxn->myHost), cxn->ident); return result; } #endif /* HAVE_SASL */ return RET_OK; } /************************* END Startup functions **********************************/ /* Return the response code for this line -1 if it doesn't seem to have one */ static int ask_code(char *str) { int ret = 0; if (str == NULL) return -1; if (strlen(str) < 3) return -1; /* check to make sure 0-2 are digits */ if ((!isdigit((unsigned char) str[0])) || (!isdigit((unsigned char) str[1])) || (!isdigit((unsigned char) str[2]))) { d_printf(0, "Parse error: response does not begin with a code [%s]\n", str); return -1; } ret = ((str[0]-'0')*100)+ ((str[1]-'0')*10)+ (str[2]-'0'); return ret; } /* is this a continuation or not? 220-fdfsd is (1) 220 fdsfs is not (0) */ static int ask_keepgoing(char *str) { if (str == NULL) return 0; if (strlen(str) < 4) return 0; if (str[3] == '-') return 1; return 0; } static conn_ret lmtp_listenintro(connection_t *cxn) { Buffer *readBuffers; /* set up to receive */ readBuffers = makeBufferArray (bufferTakeRef (cxn->lmtp_rBuffer), NULL) ; prepareRead(cxn->lmtp_endpoint, readBuffers, lmtp_readCB, cxn, 5); cxn->lmtp_state = LMTP_READING_INTRO; return RET_OK; } /************************** IMAP functions ***********************/ static conn_ret imap_Connect(connection_t *cxn) { conn_ret result; ASSERT(cxn->imap_sleepTimerId == 0); /* make the IMAP connection */ SetupIMAPConnection(cxn, cxn->ServerName, IMAP_PORT); /* Listen to the intro and start the authenticating process */ result = imap_listenintro(cxn); return result; } /* * This is called when the data write timeout for the remote * goes off. We tear down the connection and notify our host. */ static void imap_writeTimeoutCbk (TimeoutId id UNUSED, void *data) { connection_t *cxn = (Connection) data ; const char *peerName ; peerName = hostPeerName (cxn->myHost) ; syslog (LOG_WARNING, "timeout for %s", peerName); d_printf(0, "%s: shutting down non-responsive IMAP connection (%s)\n", hostPeerName (cxn->myHost), imap_stateToString(cxn->imap_state)); cxnLogStats (cxn,true) ; imap_Disconnect(cxn); } /* * This is called when the timeout for the reponse from the remote * goes off. We tear down the connection and notify our host. */ static void imap_readTimeoutCbk (TimeoutId id, void *data) { Connection cxn = (Connection) data ; const char *peerName ; ASSERT (id == cxn->imap_readBlockedTimerId) ; peerName = hostPeerName (cxn->myHost) ; warn ("%s:%d cxnsleep non-responsive connection", peerName, cxn->ident) ; d_printf(0, "%s:%d shutting down non-responsive IMAP connection (%s)\n", hostPeerName (cxn->myHost), cxn->ident, imap_stateToString(cxn->imap_state)); cxnLogStats (cxn,true); if (cxn->imap_state == IMAP_DISCONNECTED) { imap_Disconnect(cxn); lmtp_Disconnect(cxn); delConnection (cxn) ; } else { imap_Disconnect(cxn); } } /* * Called by the EndPoint class when the timer goes off */ static void imap_reopenTimeoutCbk (TimeoutId id, void *data) { Connection cxn = (Connection) data ; ASSERT (id == cxn->imap_sleepTimerId) ; cxn->imap_sleepTimerId = 0 ; d_printf(1,"%s:%d:IMAP Reopen timer rang. Try to make new connection now\n", hostPeerName (cxn->myHost), cxn->ident) ; if (cxn->imap_state != IMAP_DISCONNECTED) { warn ("%s:%d cxnsleep connection in bad state: %s", hostPeerName (cxn->myHost), cxn->ident, imap_stateToString (cxn->imap_state)) ; } else { if (imap_Connect(cxn) != RET_OK) prepareReopenCbk(cxn, 0); } } static void imap_Disconnect(connection_t *cxn) { clearTimer (cxn->imap_sleepTimerId) ; cxn->imap_sleepTimerId = 0; clearTimer (cxn->imap_readBlockedTimerId) ; clearTimer (cxn->imap_writeBlockedTimerId) ; DeferAllArticles(cxn, &(cxn->imap_controlMsg_q)) ; /* give any articles back to Host */ cxn->imap_state = IMAP_DISCONNECTED; cxn->imap_disconnects++; cxn->imap_respBuffer[0]='\0'; if (cxn->issue_quit == 0) prepareReopenCbk(cxn,0); DeleteIfDisconnected(cxn); } /************************** END IMAP functions ***********************/ /************************ LMTP functions **************************/ /* * Create a network lmtp connection * and start listening for the intro string * */ static conn_ret lmtp_Connect(connection_t *cxn) { conn_ret result; ASSERT(cxn->lmtp_sleepTimerId == 0); /* make the LMTP connection */ result = SetupLMTPConnection(cxn, cxn->ServerName, LMTP_PORT); if (result != RET_OK) return result; /* Listen to the intro */ result = lmtp_listenintro(cxn); return result; } static void lmtp_Disconnect(connection_t *cxn) { clearTimer (cxn->lmtp_sleepTimerId) ; cxn->lmtp_sleepTimerId = 0; clearTimer (cxn->lmtp_readBlockedTimerId) ; clearTimer (cxn->lmtp_writeBlockedTimerId) ; /* give any articles back to Host */ DeferAllArticles(cxn, &(cxn->lmtp_todeliver_q)) ; cxn->lmtp_state = LMTP_DISCONNECTED; cxn->lmtp_disconnects++; cxn->lmtp_respBuffer[0]='\0'; if (cxn->issue_quit == 0) prepareReopenCbk(cxn,1); DeleteIfDisconnected(cxn); } /* * Called by the EndPoint class when the timer goes off */ static void lmtp_reopenTimeoutCbk (TimeoutId id, void *data) { Connection cxn = (Connection) data ; ASSERT (id == cxn->lmtp_sleepTimerId) ; cxn->lmtp_sleepTimerId = 0 ; d_printf(1,"%s:%d:LMTP Reopen timer rang. Try to make new connection now\n", hostPeerName (cxn->myHost), cxn->ident) ; if (cxn->lmtp_state != LMTP_DISCONNECTED) { warn ("%s:%d cxnsleep connection in bad state: %s", hostPeerName (cxn->myHost), cxn->ident, lmtp_stateToString (cxn->lmtp_state)) ; } else { if (lmtp_Connect(cxn) != RET_OK) prepareReopenCbk(cxn, 1); } } /* * Set up the callback used when the Connection is sleeping (i.e. will try * to reopen the connection). * * type (0 = imap, 1 = lmtp) */ static void prepareReopenCbk (Connection cxn, int type) { /* xxx check state */ if (type == 0) { cxn->imap_sleepTimerId = prepareSleep (imap_reopenTimeoutCbk, cxn->imap_sleepTimeout, cxn) ; d_printf (1,"%s:%d IMAP connection error\n" " will try to reconnect in %d seconds\n", hostPeerName (cxn->myHost), cxn->ident, cxn->imap_sleepTimeout) ; } else { cxn->lmtp_sleepTimerId = prepareSleep (lmtp_reopenTimeoutCbk, cxn->lmtp_sleepTimeout, cxn) ; d_printf (1,"%s:%d:LMTP connection error\n" "will try to reconnect in %d seconds\n", hostPeerName (cxn->myHost), cxn->ident, cxn->lmtp_sleepTimeout) ; } /* bump the sleep timer amount each time to wait longer and longer. Gets reset in resetConnection() */ if (type == 0) { cxn->imap_sleepTimeout *= 2 ; if (cxn->imap_sleepTimeout > max_reconnect_period) cxn->imap_sleepTimeout = max_reconnect_period ; } else { cxn->lmtp_sleepTimeout *= 2 ; if (cxn->lmtp_sleepTimeout > max_reconnect_period) cxn->lmtp_sleepTimeout = max_reconnect_period ; } } /* * This is called when the timeout for the reponse from the remote * goes off. We tear down the connection and notify our host. */ static void lmtp_readTimeoutCbk (TimeoutId id, void *data) { Connection cxn = (Connection) data ; const char *peerName ; ASSERT (id == cxn->lmtp_readBlockedTimerId) ; peerName = hostPeerName (cxn->myHost) ; warn ("%s:%d cxnsleep non-responsive connection", peerName, cxn->ident) ; d_printf(0,"%s:%d shutting down non-responsive LMTP connection (%s)\n", hostPeerName (cxn->myHost), cxn->ident, lmtp_stateToString(cxn->lmtp_state)); cxnLogStats (cxn,true) ; if (cxn->lmtp_state == LMTP_DISCONNECTED) { imap_Disconnect(cxn); lmtp_Disconnect(cxn); delConnection (cxn) ; } else { lmtp_Disconnect(cxn); } } /* * This is called when the data write timeout for the remote * goes off. We tear down the connection and notify our host. */ static void lmtp_writeTimeoutCbk (TimeoutId id UNUSED, void *data) { connection_t *cxn = (Connection) data ; const char *peerName ; peerName = hostPeerName (cxn->myHost) ; syslog (LOG_WARNING, "timeout for %s", peerName); d_printf(0, "%s:%d shutting down non-responsive LMTP connection (%s)\n", hostPeerName (cxn->myHost), cxn->ident, lmtp_stateToString(cxn->lmtp_state)) ; cxnLogStats (cxn,true); lmtp_Disconnect(cxn); } /************************ END LMTP functions **************************/ /************************** LMTP write functions ********************/ static conn_ret lmtp_noop(connection_t *cxn) { conn_ret result; char *p; p = xstrdup("NOOP\r\n"); result = WriteToWire_lmtpstr(cxn, p, strlen(p)); if (result != RET_OK) return result; cxn->lmtp_state = LMTP_WRITING_NOOP; return RET_OK; } static conn_ret lmtp_IssueQuit(connection_t *cxn) { conn_ret result; char *p; p = xstrdup("QUIT\r\n"); result = WriteToWire_lmtpstr(cxn, p, strlen(p)); if (result != RET_OK) return result; cxn->lmtp_state = LMTP_WRITING_QUIT; return RET_OK; } static conn_ret lmtp_getcapabilities(connection_t *cxn) { conn_ret result; char *p; if (cxn->lmtp_capabilities != NULL) { if (cxn->lmtp_capabilities->saslmechs) { free( cxn->lmtp_capabilities->saslmechs); } free( cxn->lmtp_capabilities ); cxn->lmtp_capabilities = NULL; } cxn->lmtp_capabilities = xcalloc (1, sizeof(lmtp_capabilities_t)); cxn->lmtp_capabilities->saslmechs = NULL; #ifdef SMTPMODE p = concat("EHLO ", hostname, "\r\n", (char *) 0); #else p = concat("LHLO ", hostname, "\r\n", (char *) 0); #endif /* SMTPMODE */ result = WriteToWire_lmtpstr(cxn, p, strlen(p)); if (result != RET_OK) return result; cxn->lmtp_state = LMTP_WRITING_LHLO; return RET_OK; } #ifdef HAVE_SASL static conn_ret lmtp_authenticate(connection_t *cxn) { int saslresult; const char *mechusing; const char *out; unsigned int outlen; char *inbase64; int inbase64len; conn_ret result; char *p; sasl_interact_t *client_interact=NULL; saslresult=sasl_client_start(cxn->saslconn_lmtp, cxn->lmtp_capabilities->saslmechs, &client_interact, &out, &outlen, &mechusing); if ((saslresult != SASL_OK) && (saslresult != SASL_CONTINUE)) { d_printf(0,"%s:%d:LMTP Error calling sasl_client_start (%s)\n", hostPeerName (cxn->myHost), cxn->ident, sasl_errstring(saslresult, NULL, NULL)); return RET_FAIL; } d_printf(1,"%s:%d:LMTP Decided to try to authenticate with SASL mechanism=%s\n", hostPeerName (cxn->myHost), cxn->ident,mechusing); if (!out) { /* no initial client response */ p = concat("AUTH ", mechusing, "\r\n", (char *) 0); } else if (!outlen) { /* empty initial client response */ p = concat("AUTH ", mechusing, " =\r\n", (char *) 0); } else { /* Initial client response - convert to base64. * 2n+7 bytes are enough to contain the result of the base64 * encoding of a string whose length is n bytes. * In sasl_encode64() calls, the fourth argument is the length * of the third including the null terminator (thus 2n+8 bytes). */ inbase64 = xmalloc(outlen*2 + 8); saslresult = sasl_encode64(out, outlen, inbase64, outlen*2 + 8, (unsigned *) &inbase64len); if (saslresult != SASL_OK) return RET_FAIL; p = concat("AUTH ", mechusing, " ", inbase64, "\r\n", (char *) 0); free(inbase64); } result = WriteToWire_lmtpstr(cxn, p, strlen(p)); if (result != RET_OK) { d_printf(0, "%s:%d:LMTP WriteToWire() failure during AUTH\n", hostPeerName (cxn->myHost), cxn->ident); /* Disconnection is handled in the calling function. */ return result; } cxn->lmtp_state = LMTP_WRITING_STARTAUTH; return RET_OK; } static imt_stat lmtp_getauthline(char *str, char **line, int *linelen) { int saslresult; int response_code = -1; response_code = ask_code(str); if (response_code == 334) { /* continue */ } else if (response_code == 235) { /* woohoo! authentication complete */ return STAT_OK; } else { /* failure of some sort */ d_printf(0,"?:?:LMTP Authentication failure (%d) [%s]\n", response_code,str); return STAT_NO; } str += 4; /* jump past the "334 " */ *line = xmalloc(strlen(str)+30); if ((*line)==NULL) { return STAT_NO; } /* decode this line */ saslresult = sasl_decode64(str, strlen(str), *line, strlen(str)+1, (unsigned *) linelen); if (saslresult != SASL_OK) { d_printf(0,"?:?:LMTP base64 decoding error\n"); return STAT_NO; } return STAT_CONT; } #endif /* HAVE_SASL */ static void lmtp_writeCB (EndPoint e UNUSED, IoStatus i UNUSED, Buffer *b, void *d) { connection_t *cxn = (connection_t *) d; Buffer *readBuffers; clearTimer (cxn->lmtp_writeBlockedTimerId) ; /* Free the string that was written */ freeBufferArray (b); if (cxn->lmtp_tofree_str!=NULL) { free(cxn->lmtp_tofree_str); cxn->lmtp_tofree_str=NULL; } /* set up to receive */ readBuffers = makeBufferArray (bufferTakeRef (cxn->lmtp_rBuffer), NULL) ; prepareRead(cxn->lmtp_endpoint, readBuffers, lmtp_readCB, cxn, 5); /* set up the response timer. */ clearTimer (cxn->lmtp_readBlockedTimerId) ; if (cxn->lmtp_readTimeout > 0) cxn->lmtp_readBlockedTimerId = prepareSleep (lmtp_readTimeoutCbk, cxn->lmtp_readTimeout, cxn) ; switch (cxn->lmtp_state) { case LMTP_WRITING_LHLO: cxn->lmtp_state = LMTP_READING_LHLO; break; case LMTP_WRITING_STARTAUTH: case LMTP_WRITING_STEPAUTH: cxn->lmtp_state = LMTP_READING_STEPAUTH; break; case LMTP_WRITING_UPTODATA: /* expect result to rset */ cxn->lmtp_state = LMTP_READING_RSET; break; case LMTP_WRITING_CONTENTS: /* so we sent the whole DATA command let's see what the server responded */ cxn->lmtp_state = LMTP_READING_CONTENTS; break; case LMTP_WRITING_NOOP: cxn->lmtp_state = LMTP_READING_NOOP; break; case LMTP_WRITING_QUIT: cxn->lmtp_state = LMTP_READING_QUIT; break; default: d_printf(0,"%s:%d:LMTP Unknown state. Internal error\n", hostPeerName (cxn->myHost), cxn->ident) ; break; } } /************************** END LMTP write functions ********************/ /************************** IMAP sending functions ************************/ static void imap_writeCB (EndPoint e UNUSED, IoStatus i UNUSED, Buffer *b, void *d) { connection_t *cxn = (connection_t *) d; Buffer *readBuffers; clearTimer (cxn->imap_writeBlockedTimerId) ; /* free the string we just wrote out */ freeBufferArray (b); if (cxn->imap_tofree_str!=NULL) { free(cxn->imap_tofree_str); cxn->imap_tofree_str=NULL; } /* set up to receive */ readBuffers = makeBufferArray (bufferTakeRef (cxn->imap_rBuffer), NULL) ; prepareRead(cxn->imap_endpoint, readBuffers, imap_readCB, cxn, 5); /* set up the response timer. */ clearTimer (cxn->imap_readBlockedTimerId) ; if (cxn->imap_readTimeout > 0) cxn->imap_readBlockedTimerId = prepareSleep(imap_readTimeoutCbk, cxn->imap_readTimeout, cxn); switch (cxn->imap_state) { case IMAP_WRITING_CAPABILITY: cxn->imap_state = IMAP_READING_CAPABILITY; break; case IMAP_WRITING_STEPAUTH: case IMAP_WRITING_STARTAUTH: cxn->imap_state = IMAP_READING_STEPAUTH; break; case IMAP_WRITING_CREATE: cxn->imap_state = IMAP_READING_CREATE; break; case IMAP_WRITING_DELETE: cxn->imap_state = IMAP_READING_DELETE; break; case IMAP_WRITING_SELECT: cxn->imap_state = IMAP_READING_SELECT; break; case IMAP_WRITING_SEARCH: cxn->imap_state = IMAP_READING_SEARCH; break; case IMAP_WRITING_STORE: cxn->imap_state = IMAP_READING_STORE; break; case IMAP_WRITING_CLOSE: cxn->imap_state = IMAP_READING_CLOSE; break; case IMAP_WRITING_NOOP: cxn->imap_state = IMAP_READING_NOOP; break; case IMAP_WRITING_QUIT: cxn->imap_state = IMAP_READING_QUIT; break; default: d_printf(0,"%s:%d:IMAP invalid connection state\n", hostPeerName (cxn->myHost), cxn->ident) ; imap_Disconnect(cxn); break; } } /* * Tag is already allocated */ static void imap_GetTag(connection_t *cxn) { snprintf(cxn->imap_currentTag, IMAP_TAGLENGTH+1, "%06d", cxn->imap_tag_num); cxn->imap_tag_num++; if (cxn->imap_tag_num >= 999999) { cxn->imap_tag_num = 0; } } #ifdef HAVE_SASL static conn_ret imap_sendAuthStep(connection_t *cxn, char *str) { conn_ret result; int saslresult; char in[4096]; unsigned int inlen; const char *out; unsigned int outlen; char *inbase64; unsigned int inbase64len; /* base64 decode it */ saslresult = sasl_decode64(str, strlen(str), in, strlen(str)+1, &inlen); if (saslresult != SASL_OK) { d_printf(0,"%s:%d:IMAP base64 decoding error\n", hostPeerName (cxn->myHost), cxn->ident) ; return RET_FAIL; } saslresult=sasl_client_step(cxn->imap_saslconn, in, inlen, NULL, &out, &outlen); /* check if sasl succeeded */ if (saslresult != SASL_OK && saslresult != SASL_CONTINUE) { d_printf(0,"%s:%d:IMAP sasl_client_step failed with %s\n", hostPeerName (cxn->myHost), cxn->ident, sasl_errstring(saslresult,NULL,NULL)); cxn->imap_state = IMAP_CONNECTED_NOTAUTH; return RET_FAIL; } /* Convert to base64. * 2n+7 bytes are enough to contain the result of the base64 * encoding of a string whose length is n bytes. * In sasl_encode64() calls, the fourth argument is the length * of the third including the null terminator (thus 2n+8 bytes). * And CRLF takes the last two bytes (thus 2n+10 bytes). */ inbase64 = xmalloc(outlen*2 + 10); saslresult = sasl_encode64(out, outlen, inbase64, outlen*2 + 8, (unsigned *) &inbase64len); if (saslresult != SASL_OK) return RET_FAIL; /* Append endline. */ strlcpy(inbase64 + inbase64len, "\r\n", outlen*2 + 10 - inbase64len); inbase64len += 2; /* Send to server. */ result = WriteToWire_imapstr(cxn, inbase64, inbase64len); if (result != RET_OK) return result; cxn->imap_state = IMAP_WRITING_STEPAUTH; return RET_OK; } #endif /* HAVE_SASL */ static conn_ret imap_sendAuthenticate(connection_t *cxn) { conn_ret result; char *p; #ifdef HAVE_SASL const char *mechusing; int saslresult=SASL_NOMECH; sasl_interact_t *client_interact=NULL; if (cxn->imap_capabilities->saslmechs) { saslresult=sasl_client_start(cxn->imap_saslconn, cxn->imap_capabilities->saslmechs, &client_interact, NULL, NULL, &mechusing); } /* If no mechs try "login" */ if (saslresult == SASL_NOMECH) { #else /* HAVE_SASL */ { /* always do login */ #endif /* HAVE_SASL */ d_printf(1,"%s:%d:IMAP No mechanism found. Trying login method\n", hostPeerName (cxn->myHost), cxn->ident) ; if (cxn->imap_capabilities->logindisabled==1) { d_printf(0,"%s:%d:IMAP Login command w/o security layer not allowed on this server\n", hostPeerName (cxn->myHost), cxn->ident); return RET_FAIL; } if (deliver_authname==NULL) { d_printf(0,"%s:%d:IMAP Unable to log in because can't find a authname\n", hostPeerName (cxn->myHost), cxn->ident) ; return RET_FAIL; } if (deliver_password==NULL) { d_printf(0,"%s:%d:IMAP Unable to log in because can't find a password\n", hostPeerName (cxn->myHost), cxn->ident) ; return RET_FAIL; } imap_GetTag(cxn); p = concat(cxn->imap_currentTag, " LOGIN ", deliver_authname, " \"", deliver_password, "\"\r\n", (char *) 0); result = WriteToWire_imapstr(cxn, p, strlen(p)); if (result != RET_OK) return result; cxn->imap_state = IMAP_WRITING_STARTAUTH; return RET_OK; } #ifdef HAVE_SASL if ((saslresult != SASL_OK) && (saslresult != SASL_CONTINUE)) { d_printf(0,"%s:%d:IMAP Error calling sasl_client_start (%s) mechusing = %s\n", hostPeerName (cxn->myHost), cxn->ident, sasl_errstring(saslresult, NULL, NULL), mechusing); return RET_FAIL; } d_printf(1,"%s:%d:IMAP Trying to authenticate to imap with %s mechanism\n", hostPeerName (cxn->myHost), cxn->ident, mechusing); imap_GetTag(cxn); p = concat(cxn->imap_currentTag, " AUTHENTICATE ", mechusing, "\r\n", (char *) 0); result = WriteToWire_imapstr(cxn, p, strlen(p)); if (result != RET_OK) return result; cxn->imap_state = IMAP_WRITING_STARTAUTH; return RET_OK; #endif /* HAVE_SASL */ } static conn_ret imap_CreateGroup(connection_t *cxn, char *bboard) { conn_ret result; char *tosend; d_printf(1,"%s:%d:IMAP Ok creating group [%s]\n", hostPeerName (cxn->myHost), cxn->ident, bboard); imap_GetTag(cxn); tosend = concat(cxn->imap_currentTag, " CREATE ", bboard, "\r\n", (char *) 0); result = WriteToWire_imapstr(cxn, tosend, -1); if (result != RET_OK) return result; cxn->imap_state = IMAP_WRITING_CREATE; return RET_OK; } static conn_ret imap_DeleteGroup(connection_t *cxn, char *bboard) { conn_ret result; char *tosend; d_printf(1,"%s:%d:IMAP Ok removing bboard [%s]\n", hostPeerName (cxn->myHost), cxn->ident, bboard); imap_GetTag(cxn); tosend = concat(cxn->imap_currentTag, " DELETE ", bboard, "\r\n", (char *) 0); result = WriteToWire_imapstr(cxn, tosend, -1); if (result != RET_OK) return result; cxn->imap_state = IMAP_WRITING_DELETE; return RET_OK; } static conn_ret imap_CancelMsg(connection_t *cxn, char *newsgroup) { conn_ret result; char *tosend; ASSERT(newsgroup); imap_GetTag(cxn); /* select mbox */ tosend = concat(cxn->imap_currentTag, " SELECT ", newsgroup, "\r\n", (char *) 0); result = WriteToWire_imapstr(cxn, tosend, -1); if (result != RET_OK) return result; cxn->imap_state = IMAP_WRITING_SELECT; hostArticleOffered (cxn->myHost, cxn); return RET_OK; } static conn_ret imap_sendSearch(connection_t *cxn, char *msgid) { conn_ret result; char *tosend; ASSERT(msgid); imap_GetTag(cxn); /* preform search */ tosend = concat(cxn->imap_currentTag, " UID SEARCH header \"Message-ID\" \"", msgid, "\"\r\n", (char *) 0); result = WriteToWire_imapstr(cxn, tosend, -1); if (result != RET_OK) return result; cxn->imap_state = IMAP_WRITING_SEARCH; return RET_OK; } static conn_ret imap_sendKill(connection_t *cxn, unsigned uid) { conn_ret result; char *tosend = NULL; imap_GetTag(cxn); xasprintf(&tosend, "%s UID STORE %d +FLAGS.SILENT (\\Deleted)\r\n", cxn->imap_currentTag, uid); result = WriteToWire_imapstr(cxn, tosend, -1); if (result != RET_OK) return result; cxn->imap_state = IMAP_WRITING_STORE; return RET_OK; } static conn_ret imap_sendSimple(connection_t *cxn, const char *atom, int st) { char *tosend; conn_ret result; imap_GetTag(cxn); tosend = concat(cxn->imap_currentTag, " ", atom, "\r\n", (char *) 0); result = WriteToWire_imapstr(cxn, tosend, -1); if (result != RET_OK) return result; cxn->imap_state = st; return RET_OK; } static conn_ret imap_sendClose(connection_t *cxn) { return imap_sendSimple(cxn, "CLOSE", IMAP_WRITING_CLOSE); } static conn_ret imap_sendQuit(connection_t *cxn) { return imap_sendSimple(cxn, "LOGOUT", IMAP_WRITING_QUIT); } static conn_ret imap_noop(connection_t *cxn) { return imap_sendSimple(cxn, "NOOP", IMAP_WRITING_NOOP); } static conn_ret imap_sendCapability(connection_t *cxn) { return imap_sendSimple(cxn, "CAPABILITY", IMAP_WRITING_CAPABILITY); } /************************** END IMAP sending functions ************************/ /************************** IMAP reading functions ***************************/ static conn_ret imap_listenintro(connection_t *cxn) { Buffer *readBuffers; /* set up to receive */ readBuffers = makeBufferArray (bufferTakeRef (cxn->imap_rBuffer), NULL) ; prepareRead(cxn->imap_endpoint, readBuffers, imap_readCB, cxn, 5); cxn->imap_state = IMAP_READING_INTRO; return RET_OK; } static conn_ret imap_ParseCapability(char *string, imap_capabilities_t **caps) { char *str = string; char *start = str; size_t mechlen; /* allocate the caps structure if it doesn't already exist */ if ( (*caps) == NULL) (*caps) = xcalloc(1, sizeof(imap_capabilities_t)); while ( (*str) != '\0') { while (((*str) != '\0') && ((*str)!=' ')) { str++; } if ( (*str) != '\0') { *str = '\0'; str++; } if ( strcasecmp(start,"IMAP4")==0) { (*caps)->imap4 = 1; } else if (strcasecmp(start,"LOGINDISABLED")==0) { (*caps)->logindisabled = 1; } else if ( strncasecmp(start, "AUTH=", 5)==0) { if ( (*caps)->saslmechs == NULL) { (*caps)->saslmechs = xstrdup (start + 5); } else { mechlen = strlen((*caps)->saslmechs) + 1; mechlen += strlen(start + 5) + 1; (*caps)->saslmechs = xrealloc((*caps)->saslmechs, mechlen); strlcat((*caps)->saslmechs, " ", mechlen); strlcat((*caps)->saslmechs, start + 5, mechlen); } } start = str; } if ((*caps)->saslmechs) { d_printf(1,"?:?:IMAP parsed capabilities: saslmechs = %s\n", (*caps)->saslmechs); } return RET_OK; } static void imap_readCB (EndPoint e, IoStatus i, Buffer *b, void *d) { connection_t *cxn = (connection_t *) d; Buffer *readBuffers ; int okno; char *str; char strbuf[4096]; conn_ret ret; char *p; p = bufferBase(b[0]); /* Add what we got to our internal read buffer */ bufferAddNullByte (b[0]) ; if (i != IoDone) { errno = endPointErrno(e); syslog(LOG_ERR, "%s:%d IMAP i/o failed: %m", hostPeerName (cxn->myHost), cxn->ident); freeBufferArray (b); imap_Disconnect(cxn); return; } if (strchr (p, '\n') == NULL) { /* partial read. expand buffer and retry */ if (expandBuffer (b[0], BUFFER_EXPAND_AMOUNT)==false) { d_printf(0,"%s:%d:IMAP expanding buffer returned false\n", hostPeerName (cxn->myHost), cxn->ident); imap_Disconnect(cxn); return; } readBuffers = makeBufferArray (bufferTakeRef (b[0]), NULL) ; if (!prepareRead (e, readBuffers, imap_readCB, cxn, 1)) { imap_Disconnect(cxn); } freeBufferArray (b); return; } clearTimer (cxn->imap_readBlockedTimerId) ; /* we got something. add to our buffer and free b */ strcat(cxn->imap_respBuffer, p); bufferSetDataSize( b[0], 0); freeBufferArray (b); /* goto here to take another step */ reset: /* see if we have a full line */ ret = GetLine( cxn->imap_respBuffer , strbuf, sizeof(strbuf)); str = strbuf; /* if we don't have a full line */ if ( ret == RET_NO_FULLLINE) { readBuffers = makeBufferArray (bufferTakeRef (cxn->imap_rBuffer), NULL) ; if ( !prepareRead (e, readBuffers, imap_readCB, cxn, 1) ) { imap_Disconnect(cxn); } return; } else if (ret != RET_OK) { return; } /* if untagged */ if ((str[0]=='*') && (str[1]==' ')) { str+=2; /* now figure out what kind of untagged it is */ if (strncasecmp(str,"CAPABILITY ",11)==0) { str+=11; imap_ParseCapability(str,&(cxn->imap_capabilities)); } else if (strncasecmp(str,"SEARCH",6)==0) { str+=6; if ( (*str) == ' ') { str++; cxn->current_control->data.control->uid = atoi(str); d_printf(1,"%s:%d:IMAP i think the UID = %ld\n", hostPeerName (cxn->myHost), cxn->ident, cxn->current_control->data.control->uid); } else { /* it's probably a blank uid (i.e. message doesn't exist) */ cxn->current_control->data.control->uid = (unsigned long)-1; } } else if (strncasecmp(str,"OK ",3)==0) { if (cxn->imap_state==IMAP_READING_INTRO) { imap_sendCapability(cxn); /* xxx errors */ return; } else { } } else { /* untagged command not understood */ } /* always might be more to look at */ goto reset; } else if ((str[0]=='+') && (str[1]==' ')) { str+=2; if (cxn->imap_state == IMAP_READING_STEPAUTH) { #ifdef HAVE_SASL if (imap_sendAuthStep(cxn, str) != RET_OK) { imap_Disconnect(cxn); } #else d_printf(0,"%s:%d:IMAP got a '+ ...' without SASL. Something's wrong\n", hostPeerName (cxn->myHost), cxn->ident); imap_Disconnect(cxn); #endif /* HAVE_SASL */ return; } else { d_printf(0,"%s:%d:IMAP got a '+ ...' in state where not expected\n", hostPeerName (cxn->myHost), cxn->ident); imap_Disconnect(cxn); return; } } else if (strncasecmp(str, cxn->imap_currentTag, IMAP_TAGLENGTH)==0) { /* matches our tag */ str += IMAP_TAGLENGTH; if (str[0]!=' ') { d_printf(0,"%s:%d:IMAP Parse error: tag with no space afterward\n", hostPeerName (cxn->myHost), cxn->ident); imap_Disconnect(cxn); return; } str++; /* should be OK/NO */ if (strncasecmp(str,"OK",2)==0) { okno = 1; } else { okno = 0; } switch(cxn->imap_state) { case IMAP_READING_CAPABILITY: if (okno==1) { if (imap_sendAuthenticate(cxn) != RET_OK) { d_printf(0,"%s:%d:IMAP sendauthenticate failed\n", hostPeerName (cxn->myHost), cxn->ident); imap_Disconnect(cxn); } return; } else { d_printf(0,"%s:%d:IMAP CAPABILITY gave a NO response\n", hostPeerName (cxn->myHost), cxn->ident); imap_Disconnect(cxn); } return; break; case IMAP_READING_STEPAUTH: if (okno == 1) { cxn->imap_sleepTimeout = init_reconnect_period ; cxn->imap_timeCon = theTime () ; cxn->timeCon = theTime () ; d_printf(0,"%s:%d IMAP authentication succeeded\n", hostPeerName (cxn->myHost), cxn->ident); cxn->imap_disconnects=0; cxn->imap_state = IMAP_IDLE_AUTHED; /* try to send a message if we have one */ imap_ProcessQueue(cxn); } else { d_printf(0,"%s:%d:IMAP Authentication failed with [%s]\n", hostPeerName (cxn->myHost), cxn->ident,str); imap_Disconnect(cxn); } return; break; case IMAP_READING_CREATE: if (okno==1) { d_printf(1,"%s:%d:IMAP Create of bboard successful\n", hostPeerName (cxn->myHost), cxn->ident); cxn->create_succeeded++; /* we can delete article now */ QueueForgetAbout(cxn, cxn->current_control, MSG_SUCCESS); } else { d_printf(1,"%s:%d:IMAP Create failed with [%s] for %s\n", hostPeerName (cxn->myHost), cxn->ident,str, cxn->current_control->data.control->folder); ReQueue(cxn, &(cxn->imap_controlMsg_q), cxn->current_control); } imap_ProcessQueue(cxn); break; case IMAP_READING_DELETE: if (okno==1) { d_printf(1,"%s:%d:IMAP Delete successful\n", hostPeerName (cxn->myHost), cxn->ident); cxn->remove_succeeded++; /* we can delete article now */ QueueForgetAbout(cxn, cxn->current_control, MSG_SUCCESS); } else { d_printf(1,"%s:%d:IMAP Delete mailbox failed with [%s] for %s\n", hostPeerName (cxn->myHost), cxn->ident,str, cxn->current_control->data.control->folder); ReQueue(cxn, &(cxn->imap_controlMsg_q), cxn->current_control); } imap_ProcessQueue(cxn); return; break; case IMAP_READING_SELECT: if (okno==1) { imap_sendSearch(cxn, cxn->current_control->data.control->msgid); return; } else { d_printf(1,"%s:%d:IMAP Select failed with [%s] for %s\n", hostPeerName (cxn->myHost), cxn->ident,str, cxn->current_control->data.control->folder); ReQueue(cxn, &(cxn->imap_controlMsg_q), cxn->current_control); cxn->imap_state = IMAP_IDLE_AUTHED; imap_ProcessQueue(cxn); return; } break; case IMAP_READING_SEARCH: /* if no message let's forget about it */ if (cxn->current_control->data.control->uid == (unsigned long)-1) { d_printf(2, "%s:%d:IMAP Search didn't find the message\n", hostPeerName (cxn->myHost), cxn->ident); QueueForgetAbout(cxn, cxn->current_control, MSG_FAIL_DELIVER); if (imap_sendClose(cxn) != RET_OK) imap_Disconnect(cxn); return; } if (okno==1) { /* we got a uid. let's delete it */ if (imap_sendKill(cxn, cxn->current_control->data.control->uid) != RET_OK) imap_Disconnect(cxn); return; } else { d_printf(0, "%s:%d IMAP Received NO response to SEARCH\n", hostPeerName (cxn->myHost), cxn->ident); ReQueue(cxn, &(cxn->imap_controlMsg_q), cxn->current_control); if (imap_sendClose(cxn) != RET_OK) imap_Disconnect(cxn); return; } break; case IMAP_READING_STORE: if (okno==1) { d_printf(1,"%s:%d:IMAP Processed a Cancel fully\n", hostPeerName (cxn->myHost), cxn->ident); /* we can delete article now */ QueueForgetAbout(cxn, cxn->current_control, MSG_SUCCESS); cxn->cancel_succeeded++; if (imap_sendClose(cxn) != RET_OK) imap_Disconnect(cxn); return; } else { d_printf(1,"%s:%d:IMAP Store failed\n", hostPeerName (cxn->myHost), cxn->ident); ReQueue(cxn, &(cxn->imap_controlMsg_q), cxn->current_control); if (imap_sendClose(cxn) != RET_OK) imap_Disconnect(cxn); return; } break; case IMAP_READING_NOOP: cxn->imap_state = IMAP_IDLE_AUTHED; return; break; case IMAP_READING_CLOSE: if (!okno) { /* we can't do anything about it */ d_printf(1,"%s:%d:IMAP Close failed\n", hostPeerName (cxn->myHost), cxn->ident); } cxn->imap_state = IMAP_IDLE_AUTHED; imap_ProcessQueue(cxn); return; break; case IMAP_READING_QUIT: /* we don't care if the server said OK or NO just that it said something */ d_printf(1,"%s:%d:IMAP Read quit response\n", hostPeerName (cxn->myHost), cxn->ident); cxn->imap_state = IMAP_DISCONNECTED; DeleteIfDisconnected(cxn); break; default: d_printf(0,"%s:%d:IMAP I don't understand state %d [%s]\n", hostPeerName (cxn->myHost), cxn->ident, cxn->imap_state,str); imap_Disconnect(cxn); break; } } else { d_printf(0,"%s:%d:IMAP tag (%s) doesn't match what we gave (%s). What's up with that??\n", hostPeerName (cxn->myHost), cxn->ident, str, cxn->imap_currentTag); imap_Disconnect(cxn); } } /************************** END IMAP reading functions ***************************/ /*************************** LMTP reading functions ****************************/ static void lmtp_readCB (EndPoint e, IoStatus i, Buffer *b, void *d) { connection_t *cxn = (connection_t *) d; char str[4096]; Buffer *readBuffers; conn_ret result; int response_code; conn_ret ret; #ifdef HAVE_SASL int inlen; char *in; size_t outlen; const char *out; char *inbase64; int inbase64len; imt_stat status; int saslresult; sasl_interact_t *client_interact=NULL; #endif /* HAVE_SASL */ char *p = bufferBase(b[0]); bufferAddNullByte (b[0]) ; if (i != IoDone) { errno = endPointErrno(e); syslog(LOG_ERR, "%s:%d LMTP i/o failed: %m", hostPeerName (cxn->myHost), cxn->ident); freeBufferArray (b); lmtp_Disconnect(cxn); return; } if (strchr (p, '\n') == NULL) { /* partial read. expand buffer and retry */ d_printf(0,"%s:%d:LMTP Partial. retry\n", hostPeerName (cxn->myHost),cxn->ident); expandBuffer (b[0], BUFFER_EXPAND_AMOUNT) ; readBuffers = makeBufferArray (bufferTakeRef (b[0]), NULL) ; if ( !prepareRead (e, readBuffers, lmtp_readCB, cxn, 1) ) { lmtp_Disconnect(cxn); } freeBufferArray (b); return; } clearTimer (cxn->lmtp_readBlockedTimerId) ; /* Add what we got to our internal read buffer */ strcat(cxn->lmtp_respBuffer, p); bufferSetDataSize( b[0], 0); freeBufferArray (b); reset: /* see if we have a full line */ ret = GetLine( cxn->lmtp_respBuffer, str, sizeof(str)); /* get a line */ if (ret != RET_OK) { if (ret != RET_NO_FULLLINE) { /* was a more serious error */ d_printf(0,"%s:%d:LMTP Internal error getting line from server\n", hostPeerName (cxn->myHost),cxn->ident); lmtp_Disconnect(cxn); return; } /* set up to receive some more */ readBuffers = makeBufferArray (bufferTakeRef (cxn->lmtp_rBuffer), NULL) ; prepareRead(cxn->lmtp_endpoint, readBuffers, lmtp_readCB, cxn, 5); return; } switch (cxn->lmtp_state) { case LMTP_READING_INTRO: if (ask_code(str)!=220) { d_printf(0,"%s:%d:LMTP Initial server msg does not start with 220 (began with %d)\n", hostPeerName (cxn->myHost),cxn->ident, ask_code(str)); lmtp_Disconnect(cxn); return; } /* the initial intro could have many lines via continuations. see if we need to read more */ if (ask_keepgoing(str)==1) { goto reset; } result = lmtp_getcapabilities(cxn); if (result != RET_OK) { d_printf(0,"%s:%d:LMTP lmtp_getcapabilities() failure\n", hostPeerName (cxn->myHost),cxn->ident); lmtp_Disconnect(cxn); return; } break; case LMTP_READING_LHLO: /* recieve the response(s) */ response_code = ask_code(str); if (response_code != 250) /* was none */ { d_printf(0,"%s:%d:LMTP Response code unexpected (%d)\n", hostPeerName (cxn->myHost),cxn->ident, response_code); lmtp_Disconnect(cxn); return; } /* look for one we know about; ignore all others */ if (strncasecmp(str+4,"8BITMIME",strlen("8BITMIME"))==0) { cxn->lmtp_capabilities->Eightbitmime = 1; } else if (strncasecmp(str+4, "ENHANCEDSTATUSCODES", strlen("ENHANCEDSTATUSCODES"))==0) { cxn->lmtp_capabilities->EnhancedStatusCodes = 1; } else if (strncasecmp(str+4, "AUTH",4)==0) { cxn->lmtp_capabilities->saslmechs = xstrdup(str + 4 + 5); } else if (strncasecmp(str+4,"PIPELINING",strlen("PIPELINING"))==0) { cxn->lmtp_capabilities->pipelining = 1; } else { /* don't care; ignore */ } /* see if this is the last line of the capability */ if (ask_keepgoing(str)==1) { goto reset; } else { /* we require a few capabilities */ if (!cxn->lmtp_capabilities->pipelining) { d_printf(0,"%s:%d:LMTP We require PIPELINING\n", hostPeerName (cxn->myHost),cxn->ident); lmtp_Disconnect(cxn); return; } #ifdef HAVE_SASL if (cxn->lmtp_capabilities->saslmechs) { /* start the authentication */ result = lmtp_authenticate(cxn); if (result != RET_OK) { d_printf(0,"%s:%d:LMTP lmtp_authenticate() error\n", hostPeerName (cxn->myHost),cxn->ident); lmtp_Disconnect(cxn); return; } #else if (0) { /* noop */ #endif } else { /* either we can't authenticate or the remote server doesn't support it */ cxn->lmtp_state = LMTP_AUTHED_IDLE; d_printf(1,"%s:%d:LMTP Even though we can't authenticate" " we're going to try to feed anyway\n", hostPeerName (cxn->myHost),cxn->ident); /* We just assume we don't need to authenticate (great assumption huh?) */ hostRemoteStreams (cxn->myHost, cxn, true) ; cxn->lmtp_timeCon = theTime () ; cxn->timeCon = theTime () ; /* try to send a message if we have one */ lmtp_sendmessage(cxn,NULL); return; } } break; #ifdef HAVE_SASL case LMTP_READING_STEPAUTH: inlen = 0; status = lmtp_getauthline(str, &in, &inlen); switch (status) { case STAT_CONT: saslresult=sasl_client_step(cxn->saslconn_lmtp, in, inlen, &client_interact, &out, (unsigned *) &outlen); free(in); /* check if sasl succeeded */ if (saslresult != SASL_OK && saslresult != SASL_CONTINUE) { d_printf(0,"%s:%d:LMTP sasl_client_step(): %s\n", hostPeerName (cxn->myHost),cxn->ident, sasl_errstring(saslresult,NULL,NULL)); lmtp_Disconnect(cxn); return; } /* Convert to base64. * 2n+7 bytes are enough to contain the result of the base64 * encoding of a string whose length is n bytes. * In sasl_encode64() calls, the fourth argument is the length * of the third including the null terminator (thus 2n+8 bytes). * And CRLF takes the last two bytes (thus 2n+10 bytes). */ inbase64 = xmalloc(outlen*2 + 10); saslresult = sasl_encode64(out, outlen, inbase64, outlen*2 + 8, (unsigned *) &inbase64len); if (saslresult != SASL_OK) { d_printf(0,"%s:%d:LMTP sasl_encode64(): %s\n", hostPeerName(cxn->myHost), cxn->ident, sasl_errstring(saslresult, NULL, NULL)); lmtp_Disconnect(cxn); return; } /* Add an endline. */ strlcpy(inbase64 + inbase64len, "\r\n", outlen*2 + 10 - inbase64len); inbase64len += 2; /* Send to server. */ result = WriteToWire_lmtpstr(cxn, inbase64, inbase64len); if (result != RET_OK) { d_printf(0,"%s:%d:LMTP WriteToWire() failure\n", hostPeerName (cxn->myHost),cxn->ident); lmtp_Disconnect(cxn); return; } cxn->lmtp_state = LMTP_WRITING_STEPAUTH; break; case STAT_OK: cxn->lmtp_sleepTimeout = init_reconnect_period ; d_printf(0,"%s:%d LMTP authentication succeeded\n", hostPeerName (cxn->myHost), cxn->ident); cxn->lmtp_disconnects=0; hostRemoteStreams (cxn->myHost, cxn, true) ; cxn->lmtp_timeCon = theTime () ; cxn->timeCon = theTime () ; cxn->lmtp_state = LMTP_AUTHED_IDLE; /* try to send a message if we have one */ lmtp_sendmessage(cxn,NULL); return; break; default: d_printf(0,"%s:%d:LMTP failed authentication\n", hostPeerName (cxn->myHost),cxn->ident); lmtp_Disconnect(cxn); return; } break; #endif /* HAVE_SASL */ case LMTP_READING_RSET: if (ask_keepgoing(str)) { goto reset; } if (ask_code(str) != 250) { d_printf(0,"%s:%d:LMTP RSET failed with (%d)\n", hostPeerName (cxn->myHost),cxn->ident, ask_code(str)); lmtp_Disconnect(cxn); return; } /* we pipelined so next we recieve the mail from response */ cxn->lmtp_state = LMTP_READING_MAILFROM; goto reset; case LMTP_READING_MAILFROM: if (ask_keepgoing(str)) { goto reset; } if (ask_code(str) != 250) { d_printf(0,"%s:%d:LMTP MAILFROM failed with (%d)\n", hostPeerName (cxn->myHost),cxn->ident, ask_code(str)); lmtp_Disconnect(cxn); return; } /* we pipelined so next we recieve the rcpt's */ cxn->lmtp_state = LMTP_READING_RCPTTO; goto reset; break; case LMTP_READING_RCPTTO: if (ask_keepgoing(str)) { goto reset; } if (ask_code(str)!=250) { d_printf(1,"%s:%d:LMTP RCPT TO failed with (%d) %s\n", hostPeerName (cxn->myHost),cxn->ident, ask_code(str), str); /* if got a 5xx don't try to send anymore */ cxn->current_article->trys=100; cxn->current_rcpts_issued--; } else { cxn->current_rcpts_okayed++; } /* if issued equals number okayed then we're done */ if ( cxn->current_rcpts_okayed == cxn->current_rcpts_issued) { cxn->lmtp_state = LMTP_READING_DATA; } else { /* stay in same state */ } goto reset; break; case LMTP_READING_DATA: if (ask_keepgoing(str)) { goto reset; } if (cxn->current_rcpts_issued == 0) { if (cxn->current_article->trys < 100) { d_printf(1, "%s:%d:LMTP None of the rcpts " "were accepted for this message. Re-queueing\n", hostPeerName (cxn->myHost),cxn->ident); } ReQueue(cxn, &(cxn->lmtp_todeliver_q), cxn->current_article); cxn->lmtp_state = LMTP_AUTHED_IDLE; lmtp_sendmessage(cxn,NULL); } else { if (WriteArticle(cxn, cxn->current_bufs) != RET_OK) { d_printf(0, "%s:%d:LMTP Error writing article\n", hostPeerName (cxn->myHost),cxn->ident); lmtp_Disconnect(cxn); return; } cxn->lmtp_state = LMTP_WRITING_CONTENTS; } break; case LMTP_READING_CONTENTS: if (ask_keepgoing(str)) { goto reset; } /* need 1 response from server for every rcpt */ cxn->current_rcpts_issued--; if (ask_code(str) != 250) { d_printf(1, "%s:%d:LMTP DATA failed with %d (%s)\n", hostPeerName (cxn->myHost),cxn->ident, ask_code(str), str); cxn->current_rcpts_okayed--; } if (cxn->current_rcpts_issued>0) { goto reset; } /* * current_rcpts_okayed is number that succeeded * */ if (cxn->current_rcpts_okayed == 0) { cxn->lmtp_state = LMTP_AUTHED_IDLE; } else { cxn->lmtp_state = LMTP_AUTHED_IDLE; cxn->lmtp_succeeded++; d_printf(1, "%s:%d:LMTP Woohoo! message accepted\n", hostPeerName (cxn->myHost),cxn->ident); } /* we can delete article now */ QueueForgetAbout(cxn, cxn->current_article, MSG_SUCCESS); /* try to send another if we have one and we're still idle * forgetting the msg might have made us unidle */ if (cxn->lmtp_state == LMTP_AUTHED_IDLE) { lmtp_sendmessage(cxn,NULL); } break; case LMTP_READING_NOOP: if (ask_keepgoing(str)) { goto reset; } cxn->lmtp_state = LMTP_AUTHED_IDLE; break; case LMTP_READING_QUIT: d_printf(1,"%s:%d:LMTP read quit\n", hostPeerName (cxn->myHost),cxn->ident); cxn->lmtp_state = LMTP_DISCONNECTED; DeleteIfDisconnected(cxn); break; default: d_printf(0,"%s:%d:LMTP Bad state in lmtp_readCB %d\n", hostPeerName (cxn->myHost),cxn->ident, cxn->lmtp_state); lmtp_Disconnect(cxn); return; } } /* * Add a rcpt to: to the string * */ static void addrcpt(char *newrcpt, int newrcptlen, char **out, int *outalloc) { int size = strlen(*out); int fsize = size; int newsize = size + 9+strlen(deliver_rcpt_to)+newrcptlen+3; char c; /* see if we need to grow the string */ if (newsize > *outalloc) { (*outalloc) = newsize; (*out) = xrealloc(*out, *outalloc); } strlcpy((*out) + size,"RCPT TO:<", newsize - size); size += 9; c = newrcpt[newrcptlen]; newrcpt[newrcptlen] = '\0'; #pragma GCC diagnostic ignored "-Wformat-nonliteral" size += snprintf((*out) + size, newsize - size, deliver_rcpt_to, newrcpt); #pragma GCC diagnostic warning "-Wformat-nonliteral" newrcpt[newrcptlen] = c; strlcpy((*out) + size, ">\r\n", newsize - size); /* has embedded '\n' */ d_printf(2, "Attempting to send to: %s", (*out) + fsize); } /* * Takes the newsgroups header value and makes it into a list of RCPT TO:'s we can send over the wire * * in - newsgroups header start * in_end - end of newsgroups header * num - number of rcpt's we created */ static char *ConvertRcptList(char *in, char *in_end, int *num) { int retalloc = 400; char *ret = xmalloc(retalloc); char *str = in; char *laststart = in; (*num) = 0; /* start it off empty */ strlcpy(ret, "", retalloc); while ( str != in_end) { if ((*str) == ',') { /* eliminate leading whitespace */ while (((*laststart) ==' ') || ((*laststart)=='\t')) { laststart++; } #ifndef SMTPMODE addrcpt(laststart, str - laststart, &ret, &retalloc); (*num)++; #endif /* SMTPMODE */ laststart = str+1; } str++; } if (laststart *outalloc) { (*outalloc) = newsize; (*out) = xrealloc(*out, *outalloc); } size += snprintf((*out) + size, newsize - size, "%s<", sep); c = newrcpt[newrcptlen]; newrcpt[newrcptlen] = '\0'; #pragma GCC diagnostic ignored "-Wformat-nonliteral" size += snprintf((*out) + size, newsize - size, deliver_to_header,newrcpt); #pragma GCC diagnostic warning "-Wformat-nonliteral" newrcpt[newrcptlen] = c; strlcpy((*out) + size, ">", newsize - size); } /* * Takes the newsgroups header value and makes it into a To: header * * in - newsgroups header start * in_end - end of newsgroups header */ static char *BuildToHeader(char *in, char *in_end) { int retalloc = 400; char *ret = xmalloc(retalloc); char *str = in; char *laststart = in; const char *sep = ""; /* start it off with the header name */ strlcpy(ret,"To: ", retalloc); while ( str != in_end) { if ((*str) == ',') { /* eliminate leading whitespace */ while (((*laststart) ==' ') || ((*laststart)=='\t')) { laststart++; } addto(laststart, str - laststart, sep, &ret, &retalloc); laststart = str+1; /* separate multiple addresses with a comma */ sep = ", "; } str++; } if (laststartimap_controlMsg_q), &item); if (result == RET_QUEUE_EMPTY) { if (cxn->issue_quit) { imap_sendQuit(cxn); return; } cxn->imap_state = IMAP_IDLE_AUTHED; /* now we wait for articles from our Host, or we have some articles already. On infrequently used connections, the network link is torn down and rebuilt as needed. So we may be rebuilding the connection here in which case we have an article to send. */ /* make sure imap has _lots_ of space too */ if ((QueueItems(&(cxn->lmtp_todeliver_q)) == 0) && (QueueItems(&(cxn->imap_controlMsg_q)) == 0)) { if (hostGimmeArticle (cxn->myHost,cxn)==true) goto retry; } return; } cxn->current_control = item; switch (item->type) { case CREATE_FOLDER: imap_CreateGroup(cxn, item->data.control->folder); break; case CANCEL_MSG: imap_CancelMsg(cxn, item->data.control->folder); break; case DELETE_FOLDER: imap_DeleteGroup(cxn, item->data.control->folder); break; default: break; } return; } /* * * Pulls a message off the queue and trys to start sending it. If the * message is a control message put it in the control queue and grab * another message. If the message doesn't exist on disk or something * is wrong with it tell the host and try again. If we run out of * messages to get tell the host we want more * * cxn - connection object * justadded - the article that was just added to the queue */ static void lmtp_sendmessage(connection_t *cxn, Article justadded) { bool res; conn_ret result; char *p; Buffer *bufs; char *control_header = NULL; char *control_header_end = NULL; article_queue_t *item; char *rcpt_list, *rcpt_list_end; /* retry point */ retry: /* pull an article off the queue */ result = PopFromQueue(&(cxn->lmtp_todeliver_q), &item); if (result == RET_QUEUE_EMPTY) { if (cxn->issue_quit) { lmtp_IssueQuit(cxn); return; } /* now we wait for articles from our Host, or we have some articles already. On infrequently used connections, the network link is torn down and rebuilt as needed. So we may be rebuilding the connection here in which case we have an article to send. */ /* make sure imap has space too */ d_printf(1,"%s:%d stalled waiting for articles\n", hostPeerName (cxn->myHost),cxn->ident); if ((QueueItems(&(cxn->lmtp_todeliver_q)) == 0) && (QueueItems(&(cxn->imap_controlMsg_q)) == 0)) { if (hostGimmeArticle (cxn->myHost,cxn)==true) goto retry; } return; } /* make sure contents ok; this also should load it into memory */ res = artContentsOk (item->data.article); if (res==false) { if (justadded == item->data.article) { ReQueue(cxn, &(cxn->lmtp_todeliver_q), item); return; } else { /* tell to reject taking this message */ QueueForgetAbout(cxn,item, MSG_MISSING); } goto retry; } /* Check if it's a control message */ bufs = artGetNntpBuffers (item->data.article); if (bufs == NULL) { /* tell to reject taking this message */ QueueForgetAbout(cxn,item, MSG_MISSING); goto retry; } result = FindHeader(bufs, "Control", &control_header, &control_header_end); if (result == RET_OK) { result = AddControlMsg(cxn, item->data.article, bufs, control_header,control_header_end, 1); if (result != RET_OK) { d_printf(1,"%s:%d Error adding to [imap] control queue\n", hostPeerName (cxn->myHost),cxn->ident) ; ReQueue(cxn, &(cxn->lmtp_todeliver_q), item); return; } switch(cxn->imap_state) { case IMAP_IDLE_AUTHED: /* we're idle. let's process the queue */ imap_ProcessQueue(cxn); break; case IMAP_DISCONNECTED: case IMAP_WAITING: /* Let's connect. Once we're connected we can worry about the message */ if (cxn->imap_sleepTimerId == 0) { if (imap_Connect(cxn) != RET_OK) prepareReopenCbk(cxn,0); } break; default: /* we're doing something right now */ break; } /* all we did was add a control message. we still want to get an lmtp message */ goto retry; } if (cxn->current_bufs != NULL) { /* freeBufferArray(cxn->current_bufs); */ cxn->current_bufs = NULL; } cxn->current_bufs = bufs; cxn->current_article = item; /* we make use of pipelining here send: rset mail from rcpt to data */ /* find out who it's going to */ result = FindHeader(cxn->current_bufs, "Newsgroups", &rcpt_list, &rcpt_list_end); if ((result != RET_OK) || (rcpt_list == NULL)) { d_printf(1,"%s:%d Didn't find Newsgroups header\n", hostPeerName (cxn->myHost),cxn->ident) ; QueueForgetAbout(cxn, cxn->current_article, MSG_FAIL_DELIVER); goto retry; } /* free's original rcpt_list */ rcpt_list = ConvertRcptList(rcpt_list, rcpt_list_end, &cxn->current_rcpts_issued); cxn->current_rcpts_okayed = 0; if(mailfrom_name == NULL) mailfrom_name = xstrdup(""); p = concat("RSET\r\n" "MAIL FROM:<", mailfrom_name, ">\r\n", rcpt_list, "DATA\r\n", (char *) 0); cxn->lmtp_state = LMTP_WRITING_UPTODATA; result = WriteToWire_lmtpstr(cxn, p, strlen(p)); if (result != RET_OK) { d_printf(0,"%s:%d failed trying to write\n", hostPeerName (cxn->myHost),cxn->ident) ; lmtp_Disconnect(cxn); return; } /* prepend To: header to article */ if (deliver_to_header) { char *to_list, *to_list_end; int i, len; result = FindHeader(cxn->current_bufs, "Followup-To", &to_list, &to_list_end); if ((result != RET_OK) || (to_list == NULL)){ FindHeader(cxn->current_bufs, "Newsgroups", &to_list, &to_list_end); } /* free's original to_list */ to_list = BuildToHeader(to_list, to_list_end); len = bufferArrayLen(cxn->current_bufs); cxn->current_bufs = xrealloc(cxn->current_bufs, sizeof(Buffer) * (len+2)); cxn->current_bufs[len+1] = NULL; for (i = len; i > 0; i--) { cxn->current_bufs[i] = cxn->current_bufs[i-1]; } cxn->current_bufs[0] = newBufferByCharP(to_list, strlen(to_list+1), strlen(to_list)); } hostArticleOffered (cxn->myHost, cxn); } /* * Called by the EndPoint class when the timer goes off */ static void dosomethingTimeoutCbk (TimeoutId id, void *data) { Connection cxn = (Connection) data ; ASSERT (id == cxn->dosomethingTimerId) ; show_stats(cxn); /* we're disconnected but there are things to send */ if ((cxn->lmtp_state == LMTP_DISCONNECTED) && (cxn->lmtp_sleepTimerId == 0) && QueueItems(&(cxn->lmtp_todeliver_q)) > 0) { if (lmtp_Connect(cxn) != RET_OK) prepareReopenCbk(cxn, 1); } if ((cxn->imap_state == IMAP_DISCONNECTED) && (cxn->imap_sleepTimerId == 0) && (QueueItems(&(cxn->imap_controlMsg_q)) > 0)) { if (imap_Connect(cxn) != RET_OK) prepareReopenCbk(cxn, 0); } /* if we're idle and there are items to send let's send them */ if ((cxn->lmtp_state == LMTP_AUTHED_IDLE) && QueueItems(&(cxn->lmtp_todeliver_q)) > 0) { lmtp_sendmessage(cxn,NULL); } else if (cxn->lmtp_state == LMTP_AUTHED_IDLE) { lmtp_noop(cxn); } if ((cxn->imap_state == IMAP_IDLE_AUTHED) && (QueueItems(&(cxn->imap_controlMsg_q)) > 0)) { imap_ProcessQueue(cxn); } else if (cxn->imap_state == IMAP_IDLE_AUTHED) { imap_noop(cxn); } /* set up the timer. */ clearTimer (cxn->dosomethingTimerId) ; cxn->dosomethingTimerId = prepareSleep (dosomethingTimeoutCbk, cxn->dosomethingTimeout, cxn); } /* Give all articles in the queue back to the host. We're probably * going to exit soon. * */ static void DeferAllArticles(connection_t *cxn, Q_t *q) { article_queue_t *cur; conn_ret ret; while (1) { ret = PopFromQueue(q, &cur); if (ret == RET_QUEUE_EMPTY) return; if (ret == RET_OK) { QueueForgetAbout(cxn, cur, MSG_GIVE_BACK); } else { d_printf(0,"%s:%d Error emptying queue (deffering all articles)\n", hostPeerName (cxn->myHost),cxn->ident); return; } } } /* * Does the actual deletion of a connection and all its private data. */ static void delConnection (Connection cxn) { bool shutDown; Connection c, q; if (cxn == NULL) return ; d_printf (1,"Deleting connection: %s:%d\n", hostPeerName (cxn->myHost),cxn->ident) ; for (c = gCxnList, q = NULL ; c != NULL ; q = c, c = c->next) if (c == cxn) { if (gCxnList == c) gCxnList = gCxnList->next ; else q->next = c->next ; break ; } ASSERT (c != NULL) ; if (cxn->lmtp_endpoint != NULL) delEndPoint (cxn->lmtp_endpoint) ; if (cxn->imap_endpoint != NULL) delEndPoint (cxn->imap_endpoint) ; delBuffer (cxn->imap_rBuffer) ; delBuffer (cxn->lmtp_rBuffer) ; /* tell the Host we're outta here. */ shutDown = hostCxnGone (cxn->myHost, cxn) ; cxn->ident = 0 ; cxn->timeCon = 0 ; free (cxn->ServerName) ; clearTimer (cxn->imap_readBlockedTimerId) ; clearTimer (cxn->imap_writeBlockedTimerId) ; clearTimer (cxn->lmtp_readBlockedTimerId) ; clearTimer (cxn->lmtp_writeBlockedTimerId) ; clearTimer (cxn->imap_sleepTimerId); cxn->imap_sleepTimerId = 0; clearTimer (cxn->lmtp_sleepTimerId); cxn->lmtp_sleepTimerId = 0; clearTimer (cxn->dosomethingTimerId); free (cxn->imap_respBuffer); free (cxn->lmtp_respBuffer); free (cxn) ; if (shutDown) { /* exit program if that was the last connexion for the last host */ /* XXX what about if there are ever multiple listeners? XXX this will be executed if all hosts on only one of the XXX listeners have gone */ time_t now = theTime () ; char dateString [30] ; timeToString (now, dateString, sizeof (dateString)) ; notice ("ME finishing at %s", dateString) ; exit (0) ; } } /******************** PUBLIC FUNCTIONS ****************************/ /* * Create a new Connection. * * HOST is the host object we're owned by. * IDENT is an identifier to be added to syslog entries so we can tell * what's happening on different connections to the same peer. * IPNAME is the name (or ip address) of the remote) * MAXTOUT is the maximum amount of time to wait for a response before * considering the remote host dead. * PORTNUM is the portnum to contact on the remote end. * RESPTIMEOUT is the amount of time to wait for a response from a remote * before considering the connection dead. * CLOSEPERIOD is the number of seconds after connecting that the * connections should be closed down and reinitialized (due to problems * with old NNTP servers that hold history files open. Value of 0 means * no close down. */ Connection newConnection (Host host, unsigned int ident, const char *ipname, unsigned int artTout UNUSED, unsigned int portNum UNUSED, unsigned int respTimeout, unsigned int closePeriod UNUSED, double lowPassLow UNUSED, double lowPassHigh UNUSED, double lowPassFilter UNUSED) { Connection cxn; /* check arguments */ /* allocate connection structure */ cxn = xcalloc (1, sizeof(connection_t)) ; cxn->ident = ident ; cxn->ServerName = xstrdup (ipname) ; cxn->myHost = host ; /* setup mailfrom user */ if (gethostname(hostname, MAXHOSTNAMELEN)!=0) { d_printf(0,"%s gethostname failed\n",ipname); return NULL; } mailfrom_name = concat("news@", hostname, (char *) 0); cxn->next = gCxnList ; gCxnList = cxn ; gCxnCount++ ; /* init stuff */ Initialize(cxn, respTimeout); return cxn; } /* Causes the Connection to build the network connection. */ bool cxnConnect (Connection cxn) { /* make the lmtp connection */ if (lmtp_Connect(cxn) != RET_OK) return false; if (imap_Connect(cxn) != RET_OK) return false; return true; } static void QuitIfIdle(Connection cxn) { if ((cxn->lmtp_state == LMTP_AUTHED_IDLE) && (QueueItems(&(cxn->lmtp_todeliver_q))<=0)) { lmtp_IssueQuit(cxn); } if ((cxn->imap_state == IMAP_IDLE_AUTHED) && (QueueItems(&(cxn->imap_controlMsg_q))<=0)) { imap_sendQuit(cxn); } } static void DeleteIfDisconnected(Connection cxn) { /* we want to shut everything down. if both connections disconnected now we can */ if ((cxn->issue_quit >= 1) && (cxn->lmtp_state == LMTP_DISCONNECTED) && (cxn->imap_state == IMAP_DISCONNECTED)) { switch (cxn->issue_quit) { case 1: if (cxn->lmtp_state == LMTP_DISCONNECTED) { cxn->lmtp_state = LMTP_WAITING; cxn->imap_state = IMAP_WAITING; cxn->issue_quit = 0; hostCxnWaiting (cxn->myHost,cxn) ; /* tell our Host we're waiting */ } break; case 2: if (cxn->lmtp_state == LMTP_DISCONNECTED) { cxn->issue_quit = 0; if (imap_Connect(cxn) != RET_OK) prepareReopenCbk(cxn,0); if (lmtp_Connect(cxn) != RET_OK) prepareReopenCbk(cxn,1); } break; case 3: if (cxn->lmtp_state == LMTP_DISCONNECTED) { hostCxnDead (cxn->myHost,cxn) ; delConnection(cxn); } break; } } } /* puts the connection into the wait state (i.e. waits for an article before initiating a connect). Can only be called right after newConnection returns, or while the Connection is in the (internal) Sleeping state. */ void cxnWait (Connection cxn) { cxn->issue_quit = 1; QuitIfIdle(cxn); } /* The Connection will disconnect as if cxnDisconnect were called and then it automatically reconnects to the remote. */ void cxnFlush (Connection cxn) { cxn->issue_quit = 2; QuitIfIdle(cxn); } /* The Connection sends remaining articles, then issues a QUIT and then deletes itself */ void cxnClose (Connection cxn) { d_printf(0,"%s:%d Closing cxn\n",hostPeerName (cxn->myHost), cxn->ident); cxn->issue_quit = 3; QuitIfIdle(cxn); DeleteIfDisconnected(cxn); } /* The Connection drops all queueed articles, then issues a QUIT and then deletes itself */ void cxnTerminate (Connection cxn) { d_printf(0,"%s:%d Terminate\n",hostPeerName (cxn->myHost), cxn->ident); cxn->issue_quit = 3; /* give any articles back to host in both queues */ DeferAllArticles(cxn, &(cxn->lmtp_todeliver_q)); DeferAllArticles(cxn, &(cxn->imap_controlMsg_q)); QuitIfIdle(cxn); } /* Blow away the connection gracelessly and immedately clean up */ void cxnNuke (Connection cxn) { d_printf(0,"%s:%d Nuking connection\n",cxn->ServerName, cxn->ident); cxn->issue_quit = 4; /* give any articles back to host in both queues */ DeferAllArticles(cxn, &(cxn->lmtp_todeliver_q)); DeferAllArticles(cxn, &(cxn->imap_controlMsg_q)); imap_Disconnect(cxn); lmtp_Disconnect(cxn); hostCxnDead (cxn->myHost,cxn); delConnection(cxn); } /* * must * true - must queue article. Don't try sending * false - queue of article may fail. Try sending * * Always adds to lmtp queue even if control message * */ static bool ProcessArticle(Connection cxn, Article art, bool must) { conn_ret result; /* Don't accept any articles when we're closing down the connection */ if (cxn->issue_quit > 1) { return false; } /* if it's a regular message let's add it to the queue */ result = AddToQueue(&(cxn->lmtp_todeliver_q), art, DELIVER,1,must); if (result == RET_EXCEEDS_SIZE) { return false; } if (result != RET_OK) { d_printf(0,"%s:%d Error adding to delivery queue\n", hostPeerName (cxn->myHost), cxn->ident); return must; } if (must) return true; switch (cxn->lmtp_state) { case LMTP_WAITING: case LMTP_DISCONNECTED: if (cxn->lmtp_sleepTimerId == 0) if (lmtp_Connect(cxn) != RET_OK) prepareReopenCbk(cxn,1); break; case LMTP_AUTHED_IDLE: lmtp_sendmessage(cxn,art); break; default: /* currently doing something */ break; } return true; } /* Tells the Connection to take the article and handle its transmission. If it can't (due to queue size or whatever), then the function returns false. The connection assumes ownership of the article if it accepts it (returns true). */ bool cxnTakeArticle (Connection cxn, Article art) { /* if we're closing down always refuse */ if (cxn->issue_quit == 1) return false; return ProcessArticle (cxn,art,false); } /* Tell the Connection to take the article (if it can) for later processing. Assumes ownership of it if it takes it. */ bool cxnQueueArticle (Connection cxn, Article art) { return ProcessArticle (cxn,art,true); } /* generate a syslog message for the connections activity. Called by Host. */ void cxnLogStats (Connection cxn, bool final) { const char *peerName ; time_t now = theTime() ; int total, bad; ASSERT (cxn != NULL) ; peerName = hostPeerName (cxn->myHost) ; total = cxn->lmtp_succeeded + cxn->lmtp_failed; total += cxn->cancel_succeeded + cxn->cancel_failed; total += cxn->create_succeeded + cxn->create_failed; total += cxn->remove_succeeded + cxn->remove_failed; bad = cxn->lmtp_failed; bad += cxn->cancel_failed; bad += cxn->create_failed; bad += cxn->remove_failed; notice ("%s:%d %s seconds %ld accepted %d refused %d rejected %d", peerName, cxn->ident, (final ? "final" : "checkpoint"), (long) (now - cxn->timeCon), total, 0, bad); show_stats(cxn); if (final) { cxn->lmtp_succeeded = 0; cxn->lmtp_failed = 0; cxn->cancel_succeeded = 0; cxn->cancel_failed = 0; cxn->create_succeeded = 0; cxn->create_failed = 0; cxn->remove_succeeded = 0; cxn->remove_failed = 0; if (cxn->timeCon > 0) cxn->timeCon = theTime() ; } } /* return the number of articles the connection can be given. This lets the host shovel in as many as possible. May be zero. */ size_t cxnQueueSpace (Connection cxn) { int lmtpsize; int imapsize; lmtpsize = QueueSpace(&(cxn->lmtp_todeliver_q)); imapsize = QueueSpace(&(cxn->imap_controlMsg_q)); if (lmtpsize >=1) lmtpsize--; if (imapsize >=1) imapsize--; d_printf(1,"%s:%d Q Space lmtp size = %d state = %d\n", hostPeerName (cxn->myHost), cxn->ident, lmtpsize,cxn->lmtp_state); d_printf(1,"%s:%d Q Space imap size = %d state = %d\n", hostPeerName (cxn->myHost), cxn->ident, imapsize,cxn->imap_state); /* return the smaller of our 2 queues */ if (lmtpsize < imapsize) return lmtpsize; else return imapsize; } /* adjust the mode no-CHECK filter values */ void cxnSetCheckThresholds (Connection cxn, double lowFilter UNUSED, double highFilter UNUSED, double lowPassFilter UNUSED) { d_printf(1,"%s:%d Threshold change. This means nothing to me\n", hostPeerName (cxn->myHost), cxn->ident); } /* print some debugging info. */ void gPrintCxnInfo (FILE *fp, unsigned int indentAmt) { char indent [INDENT_BUFFER_SIZE] ; unsigned int i ; Connection cxn ; for (i = 0 ; i < MIN(INDENT_BUFFER_SIZE - 1,indentAmt) ; i++) indent [i] = ' ' ; indent [i] = '\0' ; fprintf (fp,"%sGlobal Connection list : (count %d) {\n", indent,gCxnCount) ; for (cxn = gCxnList ; cxn != NULL ; cxn = cxn->next) printCxnInfo (cxn,fp,indentAmt + INDENT_INCR) ; fprintf (fp,"%s}\n",indent) ; } void printCxnInfo (Connection cxn, FILE *fp, unsigned int indentAmt) { char indent [INDENT_BUFFER_SIZE] ; unsigned int i ; article_queue_t *artH ; for (i = 0 ; i < MIN(INDENT_BUFFER_SIZE - 1,indentAmt) ; i++) indent [i] = ' ' ; indent [i] = '\0' ; fprintf (fp,"%sConnection : %p {\n",indent, (void *) cxn) ; fprintf (fp,"%s host : %p\n",indent, (void *) cxn->myHost) ; fprintf (fp,"%s endpoint (imap): %p\n",indent, (void *) cxn->imap_endpoint) ; fprintf (fp,"%s endpoint (lmtp): %p\n",indent, (void *) cxn->lmtp_endpoint) ; fprintf (fp,"%s state (imap) : %s\n",indent, imap_stateToString (cxn->imap_state)) ; fprintf (fp,"%s state (lmtp) : %s\n",indent, lmtp_stateToString (cxn->lmtp_state)) ; fprintf (fp,"%s ident : %d\n",indent,cxn->ident) ; fprintf (fp,"%s ip-name (imap): %s\n", indent, cxn->ServerName) ; fprintf (fp,"%s ip-name (lmtp): %s\n", indent, cxn->ServerName) ; fprintf (fp,"%s port-number (imap) : %d\n",indent,cxn->imap_port) ; fprintf (fp,"%s port-number (lmtp) : %d\n",indent,cxn->lmtp_port) ; fprintf (fp,"%s Issuing Quit : %d\n",indent, cxn->issue_quit) ; fprintf (fp,"%s time-connected (imap) : %ld\n",indent,(long) cxn->imap_timeCon) ; fprintf (fp,"%s time-connected (lmtp) : %ld\n",indent,(long) cxn->lmtp_timeCon) ; fprintf (fp,"%s articles from INN : %d\n",indent, cxn->lmtp_succeeded+ cxn->lmtp_failed+ cxn->cancel_succeeded+ cxn->cancel_failed+ cxn->create_succeeded+ cxn->create_failed+ cxn->remove_succeeded+ cxn->remove_failed+ QueueSpace(&(cxn->lmtp_todeliver_q))+ QueueSpace(&(cxn->imap_controlMsg_q)) ); fprintf(fp,"%s LMTP STATS: yes: %d no: %d\n",indent, cxn->lmtp_succeeded, cxn->lmtp_failed); fprintf(fp,"%s control: yes: %d no: %d\n",indent, cxn->cancel_succeeded, cxn->cancel_failed); fprintf(fp,"%s create: yes: %d no: %d\n",indent, cxn->create_succeeded, cxn->create_failed); fprintf(fp,"%s remove: yes: %d no: %d\n",indent, cxn->remove_succeeded, cxn->remove_failed); fprintf (fp,"%s response-timeout : %d\n",indent,cxn->imap_readTimeout) ; fprintf (fp,"%s response-callback : %d\n",indent,cxn->imap_readBlockedTimerId) ; fprintf (fp,"%s write-timeout : %d\n",indent,cxn->imap_writeTimeout) ; fprintf (fp,"%s write-callback : %d\n",indent,cxn->imap_writeBlockedTimerId) ; fprintf (fp,"%s reopen wait : %d\n",indent,cxn->imap_sleepTimeout) ; fprintf (fp,"%s reopen id : %d\n",indent,cxn->imap_sleepTimerId) ; fprintf (fp,"%s IMAP queue {\n",indent) ; for (artH = cxn->imap_controlMsg_q.head; artH != NULL ; artH = artH->next) printArticleInfo (artH->data.article,fp,indentAmt + INDENT_INCR) ; fprintf (fp,"%s }\n",indent) ; fprintf (fp,"%s LMTP queue {\n",indent) ; for (artH = cxn->lmtp_todeliver_q.head ; artH != NULL ; artH = artH->next) printArticleInfo (artH->data.control->article,fp,indentAmt + INDENT_INCR) ; fprintf (fp,"%s }\n",indent) ; fprintf (fp,"%s}\n",indent) ; } /* config file load callback */ int cxnConfigLoadCbk (void *data UNUSED) { long iv ; int rval = 1 ; FILE *fp = (FILE *) data ; if (getInteger (topScope,"max-reconnect-time",&iv,NO_INHERIT)) { if (iv < 1) { rval = 0 ; logOrPrint (LOG_ERR,fp, "ME config: value of %s (%ld) in %s cannot be less" " than 1. Using %ld", "max-reconnect-time", iv,"global scope",(long) MAX_RECON_PER); iv = MAX_RECON_PER ; } } else iv = MAX_RECON_PER ; max_reconnect_period = (unsigned int) iv ; if (getInteger (topScope,"initial-reconnect-time",&iv,NO_INHERIT)) { if (iv < 1) { rval = 0 ; logOrPrint (LOG_ERR,fp, "ME config: value of %s (%ld) in %s cannot be less" " than 1. Using %ld", "initial-reconnect-time", iv,"global scope",(long)INIT_RECON_PER); iv = INIT_RECON_PER ; } } else iv = INIT_RECON_PER ; init_reconnect_period = (unsigned int) iv ; return rval ; } /* check connection state is in cxnWaitingS, cxnConnectingS or cxnIdleS */ bool cxnCheckstate (Connection cxn) { d_printf(5, "%s:%d Being asked to check state\n", hostPeerName (cxn->myHost), cxn->ident); /* return false if either connection is doing something */ if (cxn->imap_state > IMAP_IDLE_AUTHED) return false; if (cxn->lmtp_state > LMTP_AUTHED_IDLE) return false; return true; } inn-2.6.0/innfeed/config_y.h0000644000175200017520000000532512575023702015303 0ustar iuliusiulius/* A Bison parser, made by GNU Bison 2.5. */ /* Bison interface for Yacc-like parsers in C Copyright (C) 1984, 1989-1990, 2000-2011 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* As a special exception, you may create a larger work that contains part or all of the Bison parser skeleton and distribute that work under terms of your choice, so long as that work isn't itself a parser generator using the skeleton or a modified version thereof as a parser skeleton. Alternatively, if you modify or redistribute the parser skeleton itself, you may (at your option) remove this special exception, which will cause the skeleton and the resulting Bison output files to be licensed under the GNU General Public License without this special exception. This special exception was added by the Free Software Foundation in version 2.2 of Bison. */ /* Tokens. */ #ifndef YYTOKENTYPE # define YYTOKENTYPE /* Put the tokens into the symbol table, so that GDB and other debuggers know about them. */ enum yytokentype { PEER = 258, GROUP = 259, IVAL = 260, RVAL = 261, NAME = 262, XSTRING = 263, SCOPE = 264, COLON = 265, LBRACE = 266, RBRACE = 267, TRUEBVAL = 268, FALSEBVAL = 269, CHAR = 270, WORD = 271, IP_ADDRESS = 272 }; #endif /* Tokens. */ #define PEER 258 #define GROUP 259 #define IVAL 260 #define RVAL 261 #define NAME 262 #define XSTRING 263 #define SCOPE 264 #define COLON 265 #define LBRACE 266 #define RBRACE 267 #define TRUEBVAL 268 #define FALSEBVAL 269 #define CHAR 270 #define WORD 271 #define IP_ADDRESS 272 #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED typedef union YYSTYPE { /* Line 2068 of yacc.c */ #line 657 "configfile.y" scope *scp ; value *val ; char *name ; int integer ; double real ; char *string ; char chr ; /* Line 2068 of yacc.c */ #line 96 "y.tab.h" } YYSTYPE; # define YYSTYPE_IS_TRIVIAL 1 # define yystype YYSTYPE /* obsolescent; will be withdrawn */ # define YYSTYPE_IS_DECLARED 1 #endif extern YYSTYPE yylval; inn-2.6.0/innfeed/innlistener.c0000644000175200017520000005012312575023702016027 0ustar iuliusiulius/* $Id: innlistener.c 9233 2011-07-09 19:17:21Z iulius $ ** ** The implementation of the innfeed InnListener class. ** ** Written by James Brister */ #include "innfeed.h" #include "config.h" #include "clibrary.h" #include #include #include #include #include #include #include "inn/libinn.h" #include "inn/messages.h" #include "inn/nntp.h" #include "article.h" #include "buffer.h" #include "configfile.h" #include "endpoint.h" #include "host.h" #include "innlistener.h" #include "tape.h" #define LISTENER_INPUT_BUFFER (1024 * 8) /* byte size of the input buffer */ #define EOF_SLEEP_TIME 1 /* seconds to sleep when EOF on InputFile */ struct innlistener_s { EndPoint myep ; Host *myHosts ; size_t hostLen ; Buffer inputBuffer ; bool dummyListener ; bool dynamicPeers ; TimeoutId inputEOFSleepId ; InnListener next ; }; static unsigned int listenerCount = 0 ; static InnListener listenerList = NULL ; InnListener mainListener ; static FILE *droppedFp = NULL ; static long droppedCount = 0 ; static int droppedFileCount = 0 ; static char *dropArtFile = NULL ; bool fastExit = false ; extern const char *pidFile ; extern const char *InputFile ; extern bool RollInputFile ; extern bool genHtml ; static void giveArticleToPeer (InnListener lis, Article article, const char *peerName) ; static void newArticleCommand (EndPoint ep, IoStatus i, Buffer *buffs, void *data) ; static void wakeUp (TimeoutId id, void *data) ; static void writeCheckPoint (int offsetAdjust) ; static void dropArticle (const char *peer, Article article) ; static void listenerCleanup (void) ; static bool inited = false ; void listenerLogStatus (FILE *fp) { fprintf (fp,"%sListener Status:%s\n", genHtml ? "" : "", genHtml ? "" : "") ; fprintf (fp," Dropped article file: %s\n",dropArtFile) ; fprintf (fp," Dropped article count: %ld\n",(long) droppedCount) ; fprintf (fp,"\n") ; } InnListener newListener (EndPoint endp, bool isDummy, bool dynamicPeers) { InnListener l = xcalloc (1, sizeof(struct innlistener_s)) ; Buffer *readArray ; if (!inited) { inited = true ; atexit (listenerCleanup) ; } l->myep = endp ; l->hostLen = MAX_HOSTS ; l->myHosts = xcalloc (l->hostLen, sizeof(Host)) ; l->inputBuffer = newBuffer (LISTENER_INPUT_BUFFER) ; l->dummyListener = isDummy ; l->dynamicPeers = dynamicPeers ; addPointerFreedOnExit ((char *)bufferBase(l->inputBuffer)) ; addPointerFreedOnExit ((char *)l->myHosts) ; addPointerFreedOnExit ((char *)l) ; readArray = makeBufferArray (bufferTakeRef (l->inputBuffer), NULL) ; prepareRead (endp,readArray,newArticleCommand,l,1) ; l->next = listenerList ; listenerList = l ; listenerCount++ ; return l ; } void gPrintListenerInfo (FILE *fp, unsigned int indentAmt) { InnListener p ; char indent [INDENT_BUFFER_SIZE] ; unsigned int i ; for (i = 0 ; i < MIN(INDENT_BUFFER_SIZE - 1,indentAmt) ; i++) indent [i] = ' ' ; indent [i] = '\0' ; fprintf (fp,"%sGlobal InnListener list : %p (count %d) {\n", indent,(void *) listenerList,listenerCount) ; for (p = listenerList ; p != NULL ; p = p->next) printListenerInfo (p,fp,indentAmt + INDENT_INCR) ; fprintf (fp,"%s}\n",indent) ; } void printListenerInfo (InnListener listener, FILE *fp, unsigned int indentAmt) { char indent [INDENT_BUFFER_SIZE] ; unsigned int i ; for (i = 0 ; i < MIN(INDENT_BUFFER_SIZE - 1,indentAmt) ; i++) indent [i] = ' ' ; indent [i] = '\0' ; fprintf (fp,"%sInnListener : %p {\n",indent,(void *) listener) ; fprintf (fp,"%s endpoint : %p\n", indent,(void *) listener->myep) ; fprintf (fp,"%s dummy-listener : %s\n",indent, boolToString (listener->dummyListener)) ; fprintf (fp,"%s dynamicPeers : %s\n",indent, boolToString (listener->dynamicPeers)) ; fprintf (fp,"%s input-buffer {\n",indent) ; printBufferInfo (listener->inputBuffer,fp,indentAmt + INDENT_INCR) ; fprintf (fp,"%s }\n",indent) ; fprintf (fp,"%s hosts {\n",indent) ; for (i = 0 ; i < listener->hostLen ; i++) { #if 0 if (listener->myHosts [i] != NULL) printHostInfo (listener->myHosts [i],fp,indentAmt + INDENT_INCR) ; #else fprintf (fp,"%s %p\n",indent,(void *) listener->myHosts[i]) ; #endif } fprintf (fp,"%s }\n",indent) ; fprintf (fp,"%s}\n",indent) ; } /* Unlink the pidFile if and only if it is our pidFile. There is still a racecondition here but very small. */ static void unlinkPidFile (void) { FILE *fp; char buf[32]; if ((fp = fopen(pidFile, "r")) == NULL) return; if (fgets(buf, 32, fp) != NULL && atoi(buf) == getpid()) unlink(pidFile); fclose(fp); } /* Close down all hosts on this listener. When they're all gone the Listener will be deleted. */ void shutDown (InnListener l) { unsigned int i ; unsigned int count ; d_printf (1,"Shutting down the listener\n") ; /* When shutting down the mainListener, stop writing to the StatusFile and remove our pidFile. */ if (l == mainListener) { /*hostCleanup (); should do this but .. */ hostSetStatusFile ("/dev/null"); unlinkPidFile(); } closeDroppedArticleFile () ; if (l->myep != NULL) { if (l->inputEOFSleepId != 0) removeTimeout (l->inputEOFSleepId) ; l->inputEOFSleepId = 0 ; delEndPoint (l->myep) ; } l->myep = NULL ; for (i = 0, count = 0 ; i < l->hostLen ; i++) if (l->myHosts [i] != NULL) { hostClose (l->myHosts[i]) ; count++ ; } if (count == 0 || fastExit) { time_t now = theTime () ; char dateString [30] ; gHostStats(); timeToString (now, dateString, sizeof (dateString)) ; if (fastExit) notice ("ME finishing (quickly) at %s", dateString) ; else notice ("ME finishing at %s", dateString) ; unlinkPidFile(); exit (0) ; } } bool listenerAddPeer (InnListener listener, Host hostObj) { unsigned int i ; d_printf (1,"Adding peer: %s\n", hostPeerName (hostObj)) ; for (i = 0 ; i < listener->hostLen ; i++) { if (listener->myHosts [i] == NULL) { listener->myHosts [i] = hostObj ; return true ; } } return false ; } /* return true if this listener doesn't ever generate articles. */ bool listenerIsDummy (InnListener listener) { return listener->dummyListener ; } /* Called by the Host when it (the Host) is about to delete itself. */ unsigned int listenerHostGone (InnListener listener, Host host) { unsigned int i ; unsigned int someThere = 0 ; d_printf (1,"Host is gone: %s\n", hostPeerName (host)) ; for (i = 0 ; i < listener->hostLen ; i++) if (listener->myHosts [i] == host) listener->myHosts [i] = NULL ; else if (listener->myHosts [i] != NULL) someThere++ ; return someThere ; } /* called by the Host when it has nothing to do. */ void listenerHostIsIdle (InnListener listener, Host host) { ASSERT (listener != NULL) ; ASSERT (host != NULL) ; d_printf (1,"Host is idle: %s\n", hostPeerName (host)) ; if (!listener->dummyListener) return ; /* if this listener is a dummy (i.e. not generating articles cause we're just dealing with backlog files) then forget about the host and when last one is gone we exit. */ hostClose (host) ; } void openInputFile (void) { int fd, i, mainFd ; off_t offset ; char buf [32], *p ; ASSERT (InputFile && *InputFile) ; fd = open(InputFile, O_RDWR) ; if (fd < 0) die ("open %s: %s\n", InputFile, strerror(errno)) ; mainFd = getMainEndPointFd() ; if (fd != mainFd) { if (dup2(fd, mainFd) < 0) die ("dup2 %d %d: %s\n", fd, mainFd, strerror(errno)) ; close (fd); } i = read(mainFd, buf, sizeof (buf)) ; if (i < 0) die ("read %s: %s\n", InputFile, strerror(errno)) ; else if (i > 0) { p = buf; buf [ sizeof(buf) - 1 ] = '\0'; offset = (off_t) strtol (p, &p, 10) ; if (offset > 0 && *p == '\n') lseek (mainFd, offset, SEEK_SET) ; else lseek (mainFd, 0, SEEK_SET) ; } syslog(LOG_NOTICE, "ME opened %s", InputFile); } int listenerConfigLoadCbk (void *data UNUSED) { int bval ; if (getBool (topScope,"fast-exit",&bval,NO_INHERIT)) fastExit = (bval ? true : false) ; return 1 ; } /**********************************************************************/ /** STATIC PRIVATE FUNCTIONS **/ /**********************************************************************/ /* EndPoint callback function for when the InnListener's fd is ready for reading. */ static void newArticleCommand (EndPoint ep, IoStatus i, Buffer *buffs, void *data) { InnListener lis = (InnListener) data ; char *msgid, *msgidEnd ; char *fileName, *fileNameEnd ; char *peer, *peerEnd ; char *cmd, *endc ; char *bbase = bufferBase (buffs [0]) ; size_t blen = bufferDataSize (buffs [0]) ; Buffer *readArray ; static int checkPointCounter ; char *s; ASSERT (ep == lis->myep) ; bufferAddNullByte (buffs [0]) ; if (i == IoEOF) { if ( lis == mainListener && InputFile != NULL ) { if ( RollInputFile ) { syslog(LOG_NOTICE, "ME reached EOF in %s", InputFile); openInputFile () ; RollInputFile = false ; readArray = makeBufferArray (bufferTakeRef (buffs [0]),NULL) ; prepareRead (ep, readArray, newArticleCommand, data, 1) ; } else { lis->inputEOFSleepId = prepareSleep (wakeUp, EOF_SLEEP_TIME, data) ; } } else { d_printf (1,"Got EOF on listener\n") ; notice ("ME source lost . Exiting"); shutDown (lis) ; } } else if (i == IoFailed) { errno = endPointErrno (ep) ; #if HAVE_SOCKETPAIR if (errno != ECONNABORTED) #endif syswarn ("ME source read error, exiting") ; d_printf (1,"Got IO Error on listener\n") ; shutDown (lis) ; } else if (strchr (bbase, '\n') == NULL) /* partial read */ { /* check for input corrupted by NULs - if they precede the newline, we never get out of here */ if (strlen(bbase) < blen) { warn ("ME source format bad, exiting: %s", bbase) ; shutDown (lis) ; return ; } if (blen == bufferSize(buffs [0])) { if (!expandBuffer (buffs [0], BUFFER_EXPAND_AMOUNT)) { warn ("ME error expanding input buffer") ; shutDown (lis) ; return ; } } readArray = makeBufferArray (bufferTakeRef (buffs [0]),NULL) ; if (!prepareRead (ep, readArray, newArticleCommand, data, 1)) { warn ("ME error prepare read failed") ; freeBufferArray (readArray) ; shutDown (lis) ; return ; } } else { /* now iterate over each full command we got on the input. */ cmd = bbase ; while ((cmd < (bbase + blen)) && ((endc = strchr (cmd,'\n')) != NULL)) { Article article ; char *next = endc + 1; if (*next == '\r') next++ ; endc-- ; if (*endc != '\r') endc++ ; *endc = '\0' ; /* check for input corrupted by NULs - if they are preceded by newline, we may skip a large chunk without noticing */ if (*next == '\0' && next < bbase + blen) { warn ("ME source format bad, exiting: %s", cmd) ; shutDown (lis) ; return ; } d_printf (2,"INN Command: %s\n", cmd) ; /* pick out the leading string (the filename) */ if ((fileName = findNonBlankString (cmd,&fileNameEnd)) == NULL) { warn ("ME source format bad, exiting: %s", cmd) ; shutDown (lis) ; return ; } *fileNameEnd = '\0' ; /* for the benefit of newArticle() */ /* now pick out the next string (the message id) */ if ((msgid = findNonBlankString (fileNameEnd + 1,&msgidEnd)) == NULL) { *fileNameEnd = ' ' ; /* to make syslog work properly */ warn ("ME source format bad, exiting: %s", cmd) ; shutDown (lis) ; return ; } *msgidEnd = '\0' ; /* for the benefit of newArticle() */ /* now create an article object and give it all the peers on the rest of the command line. Will return null if file is missing. */ article = newArticle (fileName, msgid) ; *fileNameEnd = ' ' ; /* Check the message ID length */ if (strlen(msgid) > NNTP_MAXLEN_MSGID) { warn ("ME message id exceeds limit of %d octets: %s", NNTP_MAXLEN_MSGID, msgid) ; *(msgidEnd+1) = '\0' ; } *msgidEnd = ' ' ; /* Check if message ID starts with < and ends with > */ if (*msgid != '<' || *(msgidEnd-1) != '>') { warn ("ME source format bad, exiting: %s", cmd) ; *(msgidEnd+1) = '\0'; } /* now get all the peernames off the rest of the command lines */ peerEnd = msgidEnd ; do { *peerEnd = ' ' ; /* pick out the next peer name */ if ((peer = findNonBlankString (peerEnd + 1,&peerEnd))==NULL) break ; /* even no peer names is OK. */ /* XXX REALLY? */ *peerEnd = '\0' ; /* See if this is a valid peername */ for(s = peer; *s; s++) if (!isalnum((unsigned char) *s) && *s != '.' && *s != '-' && *s != '_') break; if (*s != 0) { warn ("ME invalid peername %s", peer) ; continue; } if (article != NULL) giveArticleToPeer (lis,article,peer) ; } while (peerEnd < endc) ; delArticle (article) ; cmd = next ; /* write a checkpoint marker if we've done another large chunk */ if (InputFile && *InputFile && ++checkPointCounter == 1000) { /* adjust the seek pointer value by the current location within the input buffer */ writeCheckPoint (blen - (cmd - bbase)) ; checkPointCounter = 0 ; } } if (*cmd != '\0') /* partial command left in buffer */ { Buffer *bArr ; unsigned int leftAmt = blen - (cmd - bbase) ; ASSERT (cmd != bbase) ; /* first we shift whats left in the buffer down to the bottom */ memmove (bbase,cmd,leftAmt) ; bufferSetDataSize (buffs [0],leftAmt) ; bArr = makeBufferArray (bufferTakeRef (buffs [0]),NULL) ; if ( !prepareRead (lis->myep, bArr, newArticleCommand, lis, 1) ) { warn ("ME error prepare read failed") ; freeBufferArray (bArr) ; shutDown (lis) ; return ; } } else if ( !readIsPending (lis->myep) ) { /* XXX read should never be pending here */ Buffer *bArr = makeBufferArray (bufferTakeRef (buffs [0]),NULL) ; bufferSetDataSize (buffs [0],0) ; if ( !prepareRead (lis->myep, bArr, newArticleCommand, lis, 1) ) { warn ("ME error prepare read failed") ; shutDown (lis) ; return ; } } } freeBufferArray (buffs) ; } /* EndPoint callback function for when the sleep due to having reached EOF on InputFile is done. */ static void wakeUp (TimeoutId id, void *data) { InnListener lis = (InnListener) data ; Buffer *readArray ; ASSERT (id == lis->inputEOFSleepId) ; lis->inputEOFSleepId = 0 ; readArray = makeBufferArray (bufferTakeRef (lis->inputBuffer), NULL) ; prepareRead (lis->myep,readArray,newArticleCommand,lis,1) ; } /* Find the Host object for the peer and hand off a reference to the article for it to transmit. */ static void giveArticleToPeer (InnListener lis, Article article, const char *peerName) { unsigned int i ; for (i = 0 ; i < lis->hostLen ; i++) if (lis->myHosts[i] != NULL) if (strcmp (peerName,hostPeerName (lis->myHosts [i])) == 0) { d_printf (1,"Giving article to peer: %s\n", peerName) ; hostSendArticle (lis->myHosts [i],artTakeRef (article)) ; break ; } if (i == lis->hostLen) { d_printf (1,"Failed to give article to peer: -%s-\n", peerName) ; if (lis->dynamicPeers) { Host newHostObj; d_printf (1, "Adding peer dynamically\n") ; newHostObj = newDefaultHost (lis, peerName); if (newHostObj == NULL) { /* Most likely we couldn't get the lock, i.e. the peer is blocked. */ dropArticle (peerName,article) ; } else if ( !listenerAddPeer (lis, newHostObj) ) { /* XXX need to remember we've gone over the limit and not try to add any more. */ warn ("ME internal too many hosts. (max is %lu)", (unsigned long) lis->hostLen) ; dropArticle (peerName,article) ; } else { d_printf (1,"Giving article to peer: %s\n", peerName) ; hostSendArticle (newHostObj,artTakeRef (article)) ; } } else { dropArticle (peerName,article) ; } } } static void writeCheckPoint (int offsetAdjust) { char offsetString[16], *writePointer ; off_t offset ; int writeBytes, writeReturn, mainFd ; mainFd = getMainEndPointFd() ; offset = lseek (mainFd, 0, SEEK_CUR) ; if (offset < 0) syslog (LOG_ERR, "ME tell(mainFd): %m") ; else { snprintf (offsetString, sizeof(offsetString), "%ld\n", (long)(offset - offsetAdjust) ) ; if ( lseek (mainFd, 0, SEEK_SET) != 0 ) syslog (LOG_ERR, "ME seek(mainFd, 0, 0): %m") ; else { writeBytes = strlen (offsetString) ; writePointer = offsetString ; do { writeReturn = write (mainFd, writePointer, writeBytes) ; if (writeReturn < 0) { syslog (LOG_ERR,"ME write input checkpoint: %m") ; break ; } writePointer += writeReturn ; writeBytes -= writeReturn ; } while (writeBytes) ; if ( lseek (mainFd, offset, SEEK_SET) != offset ) die ("ME seek(mainFd, %ld, SEEK_SET): %s\n", (long)offset, strerror(errno) ) ; } } } void openDroppedArticleFile (void) { pid_t myPid = getpid () ; const char *tapeDir = getTapeDirectory() ; if (dropArtFile != NULL) free (dropArtFile) ; xasprintf (&dropArtFile, "%s/innfeed-dropped.%c%06d", tapeDir, droppedFileCount + 'A', (int) myPid) ; if ((droppedFp = fopen (dropArtFile,"w")) == NULL) { syswarn ("ME cant open %s: loosing articles", dropArtFile) ; free (dropArtFile) ; dropArtFile = NULL ; if ((droppedFp = fopen ("/dev/null","w")) == NULL) { die ("ME error opening /dev/null") ; } } } void closeDroppedArticleFile (void) { off_t pos ; if (droppedFp == NULL) return ; fflush (droppedFp) ; pos = ftello (droppedFp) ; fclose (droppedFp) ; droppedFp = NULL ; if (pos == 0 && dropArtFile != NULL) unlink (dropArtFile) ; else if (pos != 0 && dropArtFile == NULL) warn ("ME lost %ld articles", droppedCount) ; else if (pos != 0) notice ("ME dropped %ld articles", droppedCount) ; droppedFileCount = (droppedFileCount + 1) % 26 ; droppedCount = 0 ; } static void dropArticle (const char *peerName, Article article) { static bool logged = false ; if (!logged) { warn ("ME dropping articles into %s", dropArtFile) ; logged = true ; } droppedCount++ ; fprintf (droppedFp,"%s %s %s\n",artFileName (article), artMsgId (article), peerName) ; } static void listenerCleanup (void) { free (dropArtFile) ; dropArtFile = NULL ; } inn-2.6.0/innfeed/innlistener.h0000644000175200017520000000425612575023702016042 0ustar iuliusiulius/* $Id: innlistener.h 6647 2004-01-25 20:06:42Z rra $ ** ** The public interface to the InnListener class. ** ** Written by James Brister ** ** The public interface of the things that listens to commands from INN. It ** receives lines of the form: ** ** filename msgid peer1 peer2 peer3 ** ** and turns them into Article objects and hands those Articles off to the ** Host objects. */ #if ! defined ( innlistener_h__ ) #define innlistener_h__ #include #include "misc.h" extern InnListener mainListener ; /* Initialization of the InnListener object. If it fails then returns NULL. ENDPOINT is the endpoint where the article info will come from. A dummy listener exists when processing backlog files and is there just to help drive the process. */ InnListener newListener (EndPoint endp, bool isDummy, bool dynamicPeers) ; /* print some useful debugging information about the Listener and all its * Hosts and all their Connecitons/Article/Buffers etc. to the given FILE. */ void gPrintListenerInfo (FILE *fp, unsigned int indentAmt) ; void printListenerInfo (InnListener listener, FILE *fp, unsigned int indentAmt) ; /* Called by the Host when it is about to delete itself */ unsigned int listenerHostGone (InnListener listener, Host host) ; /* Called to hook up the given Host to the Listener. */ bool listenerAddPeer (InnListener listener, Host hostObj) ; /* true if the listener is a dummy. */ bool listenerIsDummy (InnListener listener) ; /* * This gets called to stop accepting new articles from innd. Typically * called by the signal handler, or when the listener gets EOF on its input * (in channel mode) */ void shutDown (InnListener cxn) ; /* Callback fired after config file is loaded */ int listenerConfigLoadCbk (void *data) ; /* stop a specific host. */ void shutDownHost (InnListener cxn, const char *peerName) ; /* Called by the Host when it has nothing to do (so it can be shut down if necessary). */ void listenerHostIsIdle (InnListener listener, Host host) ; void openInputFile (void) ; void openDroppedArticleFile (void) ; void closeDroppedArticleFile (void) ; void listenerLogStatus (FILE *fp) ; #endif /* innlistener_h__ */ inn-2.6.0/innfeed/Makefile0000644000175200017520000002374412575023702015002 0ustar iuliusiulius## $Id: Makefile 9794 2015-03-17 20:49:15Z iulius $ include ../Makefile.global top = .. CFLAGS = $(GCFLAGS) $(SASLINC) ALL = innfeed procbatch imapfeed SOURCES = article.c buffer.c config_l.c config_y.c connection.c endpoint.c \ host.c imap_connection.c innlistener.c main.c misc.c \ tape.c INCLUDES = article.h buffer.h configfile.h config_y.h connection.h \ endpoint.h host.h innfeed.h innlistener.h misc.h tape.h # The objects linked into innfeed. All SOURCES except connection.o or # imap_connection.o. OBJECTS = article.o buffer.o config_l.o config_y.o endpoint.o host.o \ innlistener.o main.o misc.o tape.o all: $(ALL) warnings: $(MAKE) COPT='$(WARNINGS)' all install: all $(LI_XPRI) innfeed $D$(PATHBIN)/innfeed $(LI_XPRI) imapfeed $D$(PATHBIN)/imapfeed $(CP_XPRI) procbatch $D$(PATHBIN)/procbatch bootstrap: config_y.c config_y.h config_l.c clean: rm -f *.o $(ALL) rm -f innfeedp rm -rf .libs clobber distclean: clean rm -f y.tab.c y.tab.h lex.yy.c maintclean: distclean rm -f config_l.c config_y.c config_y.h $(FIXSCRIPT): @echo Run configure before running make. See INSTALL for details. @exit 1 ## Compilation rules. INNFEEDLIBS = $(LIBSTORAGE) $(LIBHIST) $(LIBINN) $(STORAGE_LIBS) $(LIBS) config_y.c config_y.h: configfile.y $(YACC) -d configfile.y mv y.tab.h config_y.h mv y.tab.c config_y.c touch config_y.h config_y.h: config_y.c config_l.c: configfile.l $(LEX) $? mv lex.yy.c config_l.c imap_connection.o: imap_connection.c $(CC) $(CFLAGS) $(SASL_CPPFLAGS) -c $< innfeed: $(OBJECTS) connection.o $(LIBSTORAGE) $(LIBINN) $(LIBLD) $(LDFLAGS) -o $@ $(OBJECTS) connection.o $(INNFEEDLIBS) imapfeed: $(OBJECTS) imap_connection.o $(LIBSTORAGE) $(LIBINN) $(LIBLD) $(LDFLAGS) -o $@ $(OBJECTS) imap_connection.o \ $(SASL_LDFLAGS) $(SASL_LIBS) $(INNFEEDLIBS) procbatch: procbatch.in $(FIXSCRIPT) $(FIXSCRIPT) procbatch.in tst: config_y.c config_l.c gcc -DWANT_MAIN -o tst -g -Wall config_y.c config_l.c -ly -ll ## Profiling. These rules have not been checked for a while and may need ## some work. profiled: innfeedp innfeedp: $(SOURCES) rm -f $(OBJECTS) $(MAKEPROFILING) innfeed mv innfeed innfeedp rm -f $(OBJECTS) ## Dependencies. Default list, below, is probably good enough. depend: Makefile $(SOURCES) $(MAKEDEPEND) '$(CFLAGS)' $(SOURCES) # DO NOT DELETE THIS LINE -- make depend depends on it. article.o: article.c innfeed.h ../include/inn/timer.h \ ../include/inn/defines.h ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/config.h ../include/inn/defines.h ../include/inn/options.h \ ../include/clibrary.h ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/portable/mmap.h \ ../include/inn/messages.h ../include/inn/libinn.h \ ../include/inn/xmalloc.h ../include/inn/xwrite.h \ ../include/inn/storage.h ../include/inn/options.h article.h misc.h \ ../include/portable/macros.h buffer.h endpoint.h buffer.o: buffer.c innfeed.h ../include/inn/timer.h \ ../include/inn/defines.h ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/config.h ../include/inn/defines.h ../include/inn/options.h \ ../include/clibrary.h ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/messages.h \ ../include/inn/libinn.h ../include/inn/xmalloc.h ../include/inn/xwrite.h \ buffer.h misc.h ../include/portable/macros.h config_l.o: config_l.c innfeed.h ../include/inn/timer.h \ ../include/inn/defines.h ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/libinn.h ../include/inn/xmalloc.h ../include/inn/xwrite.h \ configfile.h config_y.h misc.h ../include/config.h \ ../include/inn/defines.h ../include/inn/options.h \ ../include/portable/macros.h config_y.o: config_y.c innfeed.h ../include/inn/timer.h \ ../include/inn/defines.h ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/config.h ../include/inn/defines.h ../include/inn/options.h \ ../include/clibrary.h ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/messages.h \ ../include/inn/libinn.h ../include/inn/xmalloc.h ../include/inn/xwrite.h \ configfile.h misc.h ../include/portable/macros.h connection.o: connection.c innfeed.h ../include/inn/timer.h \ ../include/inn/defines.h ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/config.h ../include/inn/defines.h ../include/inn/options.h \ ../include/clibrary.h ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/portable/socket.h \ ../include/portable/macros.h ../include/portable/getaddrinfo.h \ ../include/portable/getnameinfo.h ../include/inn/fdflag.h \ ../include/inn/portable-socket.h ../include/inn/innconf.h \ ../include/inn/messages.h ../include/inn/network.h \ ../include/inn/libinn.h ../include/inn/xmalloc.h ../include/inn/xwrite.h \ article.h misc.h buffer.h configfile.h connection.h endpoint.h host.h endpoint.o: endpoint.c innfeed.h ../include/inn/timer.h \ ../include/inn/defines.h ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/config.h ../include/inn/defines.h ../include/inn/options.h \ ../include/clibrary.h ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/portable/socket.h \ ../include/portable/macros.h ../include/portable/getaddrinfo.h \ ../include/portable/getnameinfo.h ../include/inn/innconf.h \ ../include/inn/messages.h ../include/inn/libinn.h \ ../include/inn/xmalloc.h ../include/inn/xwrite.h buffer.h misc.h \ configfile.h endpoint.h host.h host.o: host.c innfeed.h ../include/inn/timer.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/config.h ../include/inn/defines.h ../include/inn/options.h \ ../include/clibrary.h ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/portable/socket.h \ ../include/portable/macros.h ../include/portable/getaddrinfo.h \ ../include/portable/getnameinfo.h ../include/inn/innconf.h \ ../include/inn/messages.h ../include/inn/network.h \ ../include/inn/portable-socket.h ../include/inn/version.h \ ../include/inn/libinn.h ../include/inn/xmalloc.h ../include/inn/xwrite.h \ article.h misc.h buffer.h configfile.h connection.h endpoint.h host.h \ innlistener.h tape.h imap_connection.o: imap_connection.c ../include/config.h \ ../include/inn/defines.h ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/portable/socket.h \ ../include/portable/macros.h ../include/portable/getaddrinfo.h \ ../include/portable/getnameinfo.h ../include/inn/messages.h \ ../include/inn/libinn.h ../include/inn/xmalloc.h ../include/inn/xwrite.h \ buffer.h misc.h connection.h endpoint.h host.h innfeed.h \ ../include/inn/timer.h article.h configfile.h innlistener.o: innlistener.c innfeed.h ../include/inn/timer.h \ ../include/inn/defines.h ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/config.h ../include/inn/defines.h ../include/inn/options.h \ ../include/clibrary.h ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/libinn.h \ ../include/inn/xmalloc.h ../include/inn/xwrite.h \ ../include/inn/messages.h ../include/inn/nntp.h article.h misc.h \ ../include/portable/macros.h buffer.h configfile.h endpoint.h host.h \ innlistener.h tape.h main.o: main.c innfeed.h ../include/inn/timer.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/config.h ../include/inn/defines.h ../include/inn/options.h \ ../include/clibrary.h ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/portable/socket.h \ ../include/portable/macros.h ../include/portable/getaddrinfo.h \ ../include/portable/getnameinfo.h ../include/portable/socket-unix.h \ ../include/inn/innconf.h ../include/inn/messages.h \ ../include/inn/version.h ../include/inn/libinn.h \ ../include/inn/xmalloc.h ../include/inn/xwrite.h \ ../include/inn/storage.h ../include/inn/options.h article.h misc.h \ buffer.h configfile.h connection.h endpoint.h host.h innlistener.h \ tape.h misc.o: misc.c innfeed.h ../include/inn/timer.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/config.h ../include/inn/defines.h ../include/inn/options.h \ ../include/clibrary.h ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/messages.h \ ../include/inn/libinn.h ../include/inn/xmalloc.h ../include/inn/xwrite.h \ endpoint.h misc.h ../include/portable/macros.h tape.h tape.o: tape.c innfeed.h ../include/inn/timer.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/config.h ../include/inn/defines.h ../include/inn/options.h \ ../include/clibrary.h ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/innconf.h \ ../include/inn/messages.h ../include/inn/libinn.h \ ../include/inn/xmalloc.h ../include/inn/xwrite.h article.h misc.h \ ../include/portable/macros.h configfile.h endpoint.h tape.h inn-2.6.0/innfeed/testListener.pl0000644000175200017520000001017112575023702016352 0ustar iuliusiulius#!/usr/bin/perl # # Author: James Brister -- berkeley-unix -- # Start Date: Wed Jan 3 00:09:01 1996 # Project: INN -- innfeed # File: testListener.pl # RCSId: $Id: testListener.pl 8290 2009-01-17 08:15:31Z iulius $ # Description: Generate news files for testing the innfeed feeder. # # Run like this: # # testListener.pl -t 30 -d tmp | innfeed # # or like this: # # innfeed -s 'perl testListener.pl -t 30 -d tmp' # $0 =~ s!.*/!! ; use Getopt::Std; $usage = "$0 [ -a -b name -d directory -c count -t sleep-amt -r -u ] peers\n" . " -a is for duplicate article id's periodically\n" . " -u is for random unlinking of article\n" . " -b add bogus peername periodically\n" . " -d is the directory where articles show be written.\n" . " -c is how many articles to create (0 the default meamns no limit)\n" . " -t is the number of seconds to sleep between each article.\n" . " -r is to have articles be created in NNTP ready format\n" ; getopts ("a:b:c:d:t:rl:h:") || die $usage ; die $usage if $opt_h ; $total = $opt_c ; $sleepAmt = 1 ; $sleepAmt = $opt_t if ($opt_t =~ /^[\d\.]+/) ; $lineCount = 50 ; $lineCount = $opt_l if ($opt_l =~ /^\d+$/) ; $directory = "." ; $directory = $opt_d if $opt_d ; $bogus = $opt_b ; if ( $bogus && $bogus !~ /^[a-zA-Z]+$/ ) { print "The bogus peername must contain only letters\n" ; } $cr = ($opt_r ? "\r" : "") ; $SIG{'INT'} = 'IGNORE' ; $SIG{'TERM'} = 'sigHup' ; $SIG{'QUIT'} = 'sigHup' ; $SIG{'HUP'} = 'sigHup' ; sub sigHup { exit (1) ; } $monstr = "JanFebMarAprMayJunJulAugSepOctNovDec" ; $letstr = "abcdefghijklmnopqrstuvwxyz" ; sub createArticle { local ($counter) = @_ ; local ($filename,$msgid,$i) ; local ($time) = $^T ; local ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime($time); local ($index) = $counter ; if ($opt_a && ((int (rand (4)) % 2) == 0)) { $index = int ($index / 2) ; } $msgid = "<$index.$$.$time\@home.octet.com.au>" ; $filename = sprintf ("%s/SampleArticle.%06d",$directory,$index) ; open (ARTICLE,">$filename") || die "open ($filename): $!\n" ; print ARTICLE "Path: home.octet.com.au!not-for-mail$cr\n" ; print ARTICLE "From: brister\@home.octet.com.au$cr\n" ; print ARTICLE "Newsgroups: junk,local.test$cr\n" ; print ARTICLE "Subject: Test$cr\n" ; print ARTICLE "Date: " ; printf ARTICLE "%d %s %d %02d:%02d:%02d UTC$cr\n", $mday, substr($monstr,$mon * 3, 3), $year + 1900, $hour, $min, $sec ; print ARTICLE "Organization: None that I can think of$cr\n" ; print ARTICLE "Lines: 5$cr\n" ; print ARTICLE "Distribution: world$cr\n" ; print ARTICLE "Message-ID: $msgid$cr\n" ; print ARTICLE "NNTP-Posting-Host: localhost$cr\n" ; print ARTICLE "$cr\n" ; for ($i = 0 ; $i < $lineCount ; $i++) { print ARTICLE "x" x ($lineCount + 1), "$cr\n"; } print ARTICLE ".This line has a leading dot.$cr\n" ; print ARTICLE "And the next line only has a dot.$cr\n" ; if ($opt_r) { print ARTICLE "..$cr\n" ; } else { print ARTICLE ".$cr\n" ; } print ARTICLE "And the next line has just two dots...$cr\n" ; print ARTICLE "...$cr\n" ; print ARTICLE "foo$cr\n" ; print ARTICLE "And the next line is the last line of the article$cr\n" ; print ARTICLE "and it only has a single dot on it.$cr\n" ; if ($opt_r) { print ARTICLE "..$cr\n" ; } else { print ARTICLE ".$cr\n" ; } close (ARTICLE) ; return ($msgid, $filename) ; } srand ; $| = 1 ; if ( ! -t STDERR ) { open (STDERR,">>/tmp/TESTLISTENER.LOG") || die ; } srand ; $sleepAmt = 1 if ($sleepAmt < 0) ; foreach $peer ( @ARGV ) { $PEERS{$peer} = 1 ; } die "Must give peernames on command line:\n$usage" if ( ! @ARGV ) ; for ( $i = 0 ; $total == 0 || $i < $total ; $i++ ) { ($msgid,$filename) = &createArticle ($i) ; if ($opt_a && ((rand (3) % 3) == 0)) { print TTY "Removing file $filename\n" ; unlink ($filename) if $opt_u ; } print "$filename $msgid @ARGV" ; print " $bogus" if ($bogus && (rand (5) % 5) == 0) ; print "\n" ; select (undef,undef,undef,(rand ($sleepAmt-1) + 1)) if $sleepAmt ; } sleep 11500 unless -f STDOUT ; inn-2.6.0/innfeed/article.c0000644000175200017520000007022712575023702015127 0ustar iuliusiulius/* $Id: article.c 9894 2015-06-14 10:06:27Z iulius $ ** ** The Article class for innfeed. ** ** Written by James Brister ** ** The implementation of the Article class. Articles are the abstraction for ** the actual news articles. They are a reference counted object because they ** need to be shared by multiple Host and Connection objects. When an Article ** is created it verifies that the actual file exists. When a Connection ** wants the article's contents for transmission the Article reads the data ** off disk and returns a set of Buffer objects. The Article holds onto these ** Buffers so that the next Connection that wants to transmit it won't have ** to wait for a disk read to be done again. */ #include "innfeed.h" #include "config.h" #include "clibrary.h" #include "portable/mmap.h" #include #include #include #if HAVE_LIMITS_H # include #endif #include #include #include "inn/messages.h" #include "inn/libinn.h" #include "inn/storage.h" #include "article.h" #include "buffer.h" #include "endpoint.h" #if defined (NDEBUG) #define VALIDATE_HASH_TABLE() (void (0)) #else #define VALIDATE_HASH_TABLE() hashValidateTable () #endif extern bool useMMap ; struct map_info_s { ARTHANDLE *arthandle; const void *mMapping; size_t size; int refCount; struct map_info_s *next; }; typedef struct map_info_s *MapInfo; struct article_s { int refCount ; /* the reference count on this article */ char *fname ; /* the file name of the article */ char *msgid ; /* the msgid of the article (INN tells us) */ Buffer contents ; /* the buffer of the actual on disk stuff */ Buffer *nntpBuffers ; /* list of buffers for transmisson */ MapInfo mapInfo; /* arthandle and mMapping */ bool loggedMissing ; /* true if article is missing and we logged */ bool articleOk ; /* true until we know otherwise. */ bool inWireFormat ; /* true if ->contents is \r\n/dot-escaped */ } ; struct hash_entry_s { struct hash_entry_s *next ; struct hash_entry_s *prev ; struct hash_entry_s *nextTime ; struct hash_entry_s *prevTime ; unsigned int hash ; Article article ; }; typedef struct hash_entry_s *HashEntry ; /* * Private functions */ static Buffer artGetContents (Article article) ; /* Return the buffer that fillContents() filled up. */ /* Log statistics on article memory usage. */ static void logArticleStats (TimeoutId id, void *data) ; static bool fillContents (Article article) ; /* Read the article's bits off the disk. */ /* Append buffer B to the buffer array BUFFS. */ static void appendBuffer (Buffer b, Buffer **buffs, int *newSpot, int *curLen); static bool prepareArticleForNNTP (Article article) ; /* Do the necessary CR-LF stuff */ static bool artFreeContents (Article art) ; /* Tell the Article to release its contents buffer if possible. */ static void artUnmap (Article art) ; /* munmap an mmap()ed article */ /* * Hash table routine declarations. */ /* Returns a has value for the given string */ static unsigned int hashString (const char *string) ; /* Locates the article with the given message ID, in the has table. */ static Article hashFindArticle (const char *msgid) ; /* Puts the given article in the hash table. */ static void hashAddArticle (Article article) ; /* Removes the given article from the has table */ static bool hashRemoveArticle (Article article) ; /* Does some simple-minded hash table validation */ static void hashValidateTable (void) ; /* * Private data */ static unsigned int missingArticleCount ; /* Number of articles that were missing */ static bool logMissingArticles ; /* if true then we log the details on a missing article. */ static unsigned int preparedBytes ; /* The number of areticle bytes read in so far of disk (will wrap around) */ static unsigned int preparedNewlines ; /* The number of newlines found in all the preparedBytes */ static unsigned int avgCharsPerLine ; /* The average number of characters per line over all articles. */ static bool rolledOver ; /* true if preparedBytes wrapped around */ static unsigned int bytesInUse ; /* count of article contents bytes in use--just the amount read from the article files, not all memory involved in. */ static unsigned int maxBytesInUse ; /* the limit we want to stay under for total bytes resident in memory dedicated to article contents. */ static unsigned int articlesInUse ; /* number of articles currently allocated. */ static unsigned int byteTotal ; /* number of bytes for article contents allocated totally since last log. */ static unsigned int articleTotal ; /* number of articles alloced since last log. */ static TimeoutId articleStatsId ; /* The timer callback id. */ static MapInfo mapInfo; /* * Hash Table data */ static HashEntry *hashTable ; /* the has table itself */ static HashEntry chronList ; /* chronologically ordered. Points at newest */ #define TABLE_SIZE 2048 /* MUST be a power of 2 */ #define HASH_MASK (TABLE_SIZE - 1) #define TABLE_ENTRY(hash) ((hash) & HASH_MASK) /*******************************************************************/ /** PUBLIC FUNCTIONS **/ /*******************************************************************/ /* Create a new article object. First looks to see if one with the given message id already exists in the hash table and if so returns that (after incrementing the reference count). */ Article newArticle (const char *filename, const char *msgid) { Article newArt = NULL ; TMRstart(TMR_NEWARTICLE); if (hashTable == NULL) { /* first-time through initialization. */ int i ; ASSERT ((TABLE_SIZE & HASH_MASK) == 0) ; hashTable = xmalloc (sizeof(HashEntry) * TABLE_SIZE) ; addPointerFreedOnExit ((char *)hashTable) ; for (i = 0 ; i < TABLE_SIZE ; i++) hashTable [i] = NULL ; if (ARTICLE_STATS_PERIOD > 0) articleStatsId = prepareSleep (logArticleStats,ARTICLE_STATS_PERIOD,0); } /* now look for it in the hash table. We presume the disk file is still ok */ if ((newArt = hashFindArticle (msgid)) == NULL) { newArt = xcalloc (1, sizeof(struct article_s)) ; newArt->fname = xstrdup (filename) ; newArt->msgid = xstrdup (msgid) ; newArt->contents = NULL ; newArt->mapInfo = NULL ; newArt->refCount = 1 ; newArt->loggedMissing = false ; newArt->articleOk = true ; newArt->inWireFormat = false ; d_printf (3,"Adding a new article(%p): %s\n", (void *)newArt, msgid) ; articlesInUse++ ; articleTotal++ ; hashAddArticle (newArt) ; } else { if (strcmp (filename,newArt->fname) != 0) warn ("ME two filenames for same article: %s, %s", filename, newArt->fname) ; newArt->refCount++ ; d_printf (2,"Reusing existing article for %s\n",msgid) ; } TMRstop(TMR_NEWARTICLE); return newArt ; } /* Decrement the reference count on the article and free up its memory if the ref count gets to 0. */ void delArticle (Article article) { if (article == NULL) return ; ASSERT (article->refCount > 0) ; if (--(article->refCount) == 0) { bool removed = hashRemoveArticle (article) ; ASSERT (removed == true) ; d_printf (2,"Cleaning up article (%p): %s\n", (void *)article, article->msgid) ; if (article->contents != NULL) { if (article->mapInfo) artUnmap(article); else bytesInUse -= bufferDataSize (article->contents) ; if (article->nntpBuffers != NULL) freeBufferArray (article->nntpBuffers) ; delBuffer (article->contents) ; } articlesInUse-- ; free (article->fname) ; free (article->msgid) ; free (article) ; } VALIDATE_HASH_TABLE () ; } void gPrintArticleInfo (FILE *fp, unsigned int indentAmt) { char indent [INDENT_BUFFER_SIZE] ; unsigned int i ; for (i = 0 ; i < MIN(INDENT_BUFFER_SIZE - 1,indentAmt) ; i++) indent [i] = ' ' ; indent [i] = '\0' ; fprintf (fp,"%sGlobal Article information : (count %d) {\n", indent, articlesInUse) ; fprintf (fp,"%s missingArticleCount : %d\n",indent,missingArticleCount) ; fprintf (fp,"%s logMissingArticles : %d\n",indent,logMissingArticles) ; fprintf (fp,"%s preparedBytes : %d\n",indent,preparedBytes) ; fprintf (fp,"%s preparedNewlines : %d\n",indent,preparedNewlines) ; fprintf (fp,"%s avgCharsPerLine : %d\n",indent,avgCharsPerLine) ; fprintf (fp,"%s rolledOver : %s\n",indent,boolToString (rolledOver)) ; fprintf (fp,"%s bytesInUse : %d\n",indent,bytesInUse) ; fprintf (fp,"%s maxBytesInUse : %d\n",indent,maxBytesInUse) ; fprintf (fp,"%s articlesInUse : %d\n",indent,articlesInUse) ; fprintf (fp,"%s byteTotal : %d\n",indent,byteTotal) ; fprintf (fp,"%s articleTotal : %d\n",indent,articleTotal) ; fprintf (fp,"%s articleStatsId : %d\n",indent,articleStatsId) ; { HashEntry he ; for (he = chronList ; he != NULL ; he = he->nextTime) printArticleInfo (he->article,fp,indentAmt + INDENT_INCR) ; } fprintf (fp,"%s}\n",indent) ; } void printArticleInfo (Article art, FILE *fp, unsigned int indentAmt) { Buffer *b ; char indent [INDENT_BUFFER_SIZE] ; unsigned int i ; for (i = 0 ; i < MIN(INDENT_BUFFER_SIZE - 1,indentAmt) ; i++) indent [i] = ' ' ; indent [i] = '\0' ; fprintf (fp,"%sArticle : %p {\n",indent,(void *) art) ; fprintf (fp,"%s article ok : %s\n",indent,boolToString (art->articleOk)) ; fprintf (fp,"%s refcount : %d\n",indent,art->refCount) ; fprintf (fp,"%s filename : %s\n",indent,art->fname) ; fprintf (fp,"%s msgid : %s\n",indent,art->msgid) ; fprintf (fp,"%s contents buffer : {\n",indent) ; #if 0 printBufferInfo (art->contents,fp,indentAmt + INDENT_INCR) ; #else fprintf (fp,"%s %p\n",indent,(void *) art->contents) ; #endif fprintf (fp,"%s }\n",indent) ; fprintf (fp,"%s nntp buffers : {\n",indent) ; for (b = art->nntpBuffers ; b != NULL && *b != NULL ; b++) #if 0 printBufferInfo (*b,fp,indentAmt + INDENT_INCR) ; #else fprintf (fp,"%s %p\n",indent,(void *) *b) ; #endif fprintf (fp,"%s }\n", indent) ; fprintf (fp,"%s logged missing : %s\n", indent,boolToString (art->loggedMissing)); fprintf (fp,"%s}\n", indent) ; } /* return true if we have or are able to get the contents off the disk */ bool artContentsOk (Article article) { bool rval = false ; if ( prepareArticleForNNTP (article) ) rval = true ; return rval ; } /* bump reference count on the article. */ Article artTakeRef (Article article) { if (article != NULL) article->refCount++ ; return article ; } /* return the filename of the article */ const char *artFileName (Article article) { if (article == NULL) return NULL ; else return article->fname ; } /* Get a NULL terminated array of Buffers that is ready for sending via NNTP */ Buffer *artGetNntpBuffers (Article article) { if ( !prepareArticleForNNTP (article) ) return NULL ; return dupBufferArray (article->nntpBuffers) ; } /* return the message id of the article */ const char *artMsgId (Article article) { return article->msgid ; } /* return size of the article */ int artSize (Article article) { if (article == NULL || article->contents == NULL) return (int)0 ; return (int)bufferDataSize(article->contents); } /* return how many NNTP-ready buffers the article contains */ unsigned int artNntpBufferCount (Article article) { if ( !prepareArticleForNNTP (article) ) return 0 ; return bufferArrayLen (article->nntpBuffers) ; } /* if VAL is true then all missing articles will be logged. */ void artLogMissingArticles (bool val) { logMissingArticles = val ; } /* set the limit we want to stay under. */ void artSetMaxBytesInUse (unsigned int val) { ASSERT (maxBytesInUse > 0) ; /* can only set one time. */ ASSERT (val > 0) ; maxBytesInUse = val ; } /**********************************************************************/ /** STATIC FUNCTIONS **/ /**********************************************************************/ /* return a single buffer that contains the disk image of the article (i.e. not fixed up for NNTP). */ static Buffer artGetContents (Article article) { Buffer rval = NULL ; if (article->articleOk) { if (article->contents == NULL) fillContents (article) ; if (article->contents != NULL) rval = bufferTakeRef (article->contents) ; } return rval ; } /* arthandle/mMapping needs to be refcounted since a buffer may exist referencing it after delArticle if the remote host sends the return status too soon (diablo, 439). */ static MapInfo getMapInfo(ARTHANDLE *arthandle, const void *mMapping, size_t size) { MapInfo m; for (m = mapInfo; m; m = m->next) { if (m->arthandle == arthandle && m->mMapping == mMapping) { m->refCount++; return m; } } m = xmalloc(sizeof(struct map_info_s)); m->refCount = 1; m->arthandle = arthandle; m->mMapping = mMapping; m->size = size; m->next = mapInfo; mapInfo = m; return m; } static void delMapInfo(void *vm) { MapInfo i, prev; MapInfo m = (MapInfo)vm; if (m == NULL) return; if (--(m->refCount) > 0) return; if (m->arthandle) SMfreearticle(m->arthandle); else if (munmap(m->mMapping, m->size) < 0) syslog (LOG_NOTICE, "munmap article: %m"); prev = NULL; for (i = mapInfo; i != m; i = i->next) prev = i; if (prev) prev->next = m->next; else mapInfo = m->next; free(m); } static void artUnmap (Article article) { delMapInfo(article->mapInfo); article->mapInfo = NULL; } static void logArticleStats (TimeoutId id, void *data UNUSED) { ASSERT (id == articleStatsId) ; notice ("ME articles active %d bytes %d", articlesInUse, bytesInUse) ; notice ("ME articles total %d bytes %d", articleTotal, byteTotal) ; byteTotal = 0 ; articleTotal = 0 ; articleStatsId = prepareSleep (logArticleStats,ARTICLE_STATS_PERIOD,0) ; } /* do the actual read of the article off disk into a Buffer that is stored in the Article object. The Article will end up with its contents field having a buffer with the article data in it. This buffer may be holding a mmapped pointer, or it may be simply a regular buffer with the data read off disk into it. In the regular buffer case the contents may be copied around after reading to insert a carriage return before each newline. */ static bool fillContents (Article article) { int fd = -1; char *p; static bool maxLimitNotified ; bool opened; size_t articlesize = 0; char *buffer = NULL ; int amt = 0 ; size_t idx = 0, amtToRead ; size_t newBufferSize ; HashEntry h ; ARTHANDLE *arthandle = NULL; const void *mMapping = NULL; ASSERT (article->contents == NULL) ; TMRstart(TMR_READART); if (maxBytesInUse == 0) maxBytesInUse = SOFT_ARTICLE_BYTE_LIMIT ; if (avgCharsPerLine == 0) avgCharsPerLine = 75 ; /* roughly number of characters per line */ if (IsToken(article->fname)) { opened = ((arthandle = SMretrieve(TextToToken(article->fname), RETR_ALL)) != NULL) ? true : false; if (opened) articlesize = arthandle->len; else { if (SMerrno != SMERR_NOENT && SMerrno != SMERR_UNINIT) { syslog(LOG_ERR, "Could not retrieve %s: %s", article->fname, SMerrorstr); article->articleOk = false; TMRstop(TMR_READART); return false; } } } else { struct stat sb ; opened = ((fd = open (article->fname,O_RDONLY,0)) >= 0) ? true : false; arthandle = NULL; if (opened) { if (fstat (fd, &sb) < 0) { article->articleOk = false ; syswarn ("ME oserr fstat %s", article->fname) ; TMRstop(TMR_READART); return false; } if (!S_ISREG (sb.st_mode)) { article->articleOk = false ; warn ("ME article file-type: %s", article->fname) ; TMRstop(TMR_READART); return false; } if (sb.st_size == 0) { article->articleOk = false ; warn ("ME article 0 bytes: %s", article->fname) ; TMRstop(TMR_READART); return false; } articlesize = sb.st_size; } } if (!opened) { article->articleOk = false ; missingArticleCount++ ; if (logMissingArticles && !article->loggedMissing) { notice ("ME article missing: %s, %s", article->msgid, article->fname) ; article->loggedMissing = true ; } TMRstop(TMR_READART); return false; } amtToRead = articlesize ; newBufferSize = articlesize ; if (arthandle || useMMap) { if (arthandle) mMapping = arthandle->data; else mMapping = mmap(NULL, articlesize, PROT_READ, MAP_SHARED, fd, 0); if (mMapping == MAP_FAILED) { /* dunno, but revert to plain reading */ mMapping = NULL ; syswarn ("ME mmap failure %s (%s)", article->fname, strerror(errno)) ; } else { article->contents = newBufferByCharP(mMapping, articlesize, articlesize); article->mapInfo = getMapInfo(arthandle, mMapping, articlesize); article->mapInfo->refCount++; /* one more for the buffer */ bufferSetDeletedCbk(article->contents, delMapInfo, article->mapInfo); buffer = bufferBase (article->contents) ; if ((p = strchr(buffer, '\n')) == NULL) { article->articleOk = false; delBuffer (article->contents) ; article->contents = NULL ; warn ("ME munged article %s", article->fname) ; } else { if (p > buffer && p[-1] == '\r') { article->inWireFormat = true ; } else { /* we need to copy the contents into a buffer below */ delBuffer (article->contents) ; article->contents = NULL ; } } } } if (article->contents == NULL && article->articleOk) { /* an estimate to give some room for nntpPrepareBuffer to use. */ newBufferSize *= (1.0 + (1.0 / avgCharsPerLine)) ; newBufferSize ++ ; /* if we're going over the limit try to free up some older article's contents. */ if (amtToRead + bytesInUse > maxBytesInUse) { for (h = chronList ; h != NULL ; h = h->nextTime) { if (artFreeContents (h->article)) if (amtToRead + bytesInUse <= maxBytesInUse) break ; } } /* we we couldn't get below, then log it (one time only) */ if ((amtToRead + bytesInUse) > maxBytesInUse && maxLimitNotified == false) { maxLimitNotified = true ; notice ("ME exceeding maximum article byte limit: %d (max)," " %lu (cur)", maxBytesInUse, (unsigned long) (amtToRead + bytesInUse)) ; } if ((article->contents = newBuffer (newBufferSize)) == NULL) amtToRead = 0 ; else { buffer = bufferBase (article->contents) ; bytesInUse += articlesize ; byteTotal += articlesize ; } if (mMapping && buffer != NULL) { memcpy(buffer, mMapping, articlesize); artUnmap(article) ; amtToRead = 0; } while (amtToRead > 0) { if ((amt = read (fd, buffer + idx,amtToRead)) <= 0) { syswarn ("ME article read error: %s", article->fname) ; bytesInUse -= articlesize ; byteTotal -= articlesize ; amtToRead = 0 ; delBuffer (article->contents) ; article->contents = NULL ; } else { idx += amt ; amtToRead -= amt ; } } if (article->contents != NULL) { bufferSetDataSize (article->contents, articlesize) ; if ((p = strchr(buffer, '\n')) == NULL) { article->articleOk = false; warn ("ME munged article %s", article->fname) ; } else if (p > buffer && p[-1] == '\r') { article->inWireFormat = true ; } else { if ( nntpPrepareBuffer (article->contents) ) { size_t diff = (bufferDataSize (article->contents) - articlesize) ; if (((unsigned int) UINT_MAX) - diff <= preparedBytes) { d_printf (2,"Newline ratio so far: %02.2f\n", ((double) preparedBytes / preparedNewlines)) ; notice ("ME newline to file size ratio: %0.2f (%d/%d)", ((double) preparedBytes)/preparedNewlines, preparedBytes,preparedNewlines) ; preparedBytes = 0 ; preparedNewlines = 0 ; rolledOver = true ; } preparedBytes += articlesize ; preparedNewlines += diff ; bytesInUse += diff ; byteTotal += diff ; if (preparedBytes > (1024 * 1024)) { avgCharsPerLine = ((double) preparedBytes) / preparedNewlines ; avgCharsPerLine++ ; } article->inWireFormat = true ; } else { warn ("ME internal failed to prepare buffer for NNTP") ; bytesInUse -= articlesize ; byteTotal -= articlesize ; delBuffer (article->contents) ; article->contents = NULL ; } } } } /* If we're not useing storage api, we should close a valid file descriptor */ if (!arthandle && (fd >= 0)) close (fd) ; TMRstop(TMR_READART); return (article->contents != NULL ? true : false) ; } /* stick the buffer B into the Buffer array pointed at by BUFFS *BUFFS is reallocated if necessary. NEWSPOT points at the index where B should be put (presumably the end). CURLEN points at the length of the BUFFS and it will get updated if BUFFS is reallocated. */ static void appendBuffer (Buffer b, Buffer **buffs, int *newSpot, int *curLen) { if (*newSpot == *curLen) { *curLen += 10 ; *buffs = xrealloc (*buffs, sizeof(Buffer) * *curLen) ; } (*buffs) [(*newSpot)++] = b ; } /* Takes the articles contents buffer and overlays a set of new buffers on top of it. These buffers insert the required carriage return and dot characters as needed */ static bool prepareArticleForNNTP (Article article) { static Buffer dotBuffer ; Buffer *nntpBuffs = NULL ; int buffLen = 0 ; int buffIdx = 0 ; char *start, *end ; Buffer contents ; contents = artGetContents (article) ; /* returns a reference */ TMRstart(TMR_PREPART); if (contents == NULL) { TMRstop(TMR_PREPART); return false ; } else if (article->nntpBuffers != NULL) { delBuffer (contents) ; TMRstop(TMR_PREPART); return true ; /* already done */ } if (dotBuffer == NULL) { dotBuffer = newBufferByCharP (".\r\n",3,3) ; } /* overlay a set of buffers on top of the articles contents buffer. This is a real speed loss at the moment, so by default it's disabled (by calling artBitFiddleContents(true) in main(). */ if (article->inWireFormat == false) { end = bufferBase (contents) ; do { start = end ; while (*end && *end != '\n') end++ ; appendBuffer (newBufferByCharP (start, (size_t) (end - start), (size_t) (end - start)), &nntpBuffs,&buffIdx,&buffLen) ; if (*end != '\0') end++ ; } while (*end != '\0') ; appendBuffer (bufferTakeRef (dotBuffer), &nntpBuffs,&buffIdx,&buffLen) ; appendBuffer (NULL,&nntpBuffs,&buffIdx,&buffLen) ; } else { /* we already fixed the contents up when we read in the article */ nntpBuffs = xmalloc (sizeof(Buffer) * 3) ; nntpBuffs [0] = newBufferByCharP (bufferBase (contents), bufferDataSize (contents), bufferDataSize (contents)) ; if (article->mapInfo) { article->mapInfo->refCount++; bufferSetDeletedCbk(nntpBuffs[0], delMapInfo, article->mapInfo); } nntpBuffs [1] = NULL ; } delBuffer (contents) ; /* the article is still holding a reference */ article->nntpBuffers = nntpBuffs ; TMRstop(TMR_PREPART); return true ; } /* free the contents of the buffers if article is the only thing holding a reference. Returns true if it could, false if it couldn't */ static bool artFreeContents (Article art) { if (art->contents == NULL) return false ; if (art->nntpBuffers != NULL) { if (bufferRefCount (art->nntpBuffers[0]) > 1) return false ; else { freeBufferArray (art->nntpBuffers) ; art->nntpBuffers = NULL ; } } ASSERT (bufferRefCount (art->contents) == 1) ; if (art->mapInfo) artUnmap(art); else bytesInUse -= bufferDataSize (art->contents) ; delBuffer (art->contents) ; art->contents = NULL ; return true ; } /**********************************************************************/ /* Private hash table and routines for storing articles */ /**********************************************************************/ /* Hash function lifted from perl 5 */ static unsigned int hashString (const char *string) { unsigned int i ; for (i = 0 ; string && *string ; string++) i = 33 * i + (u_char) *string ; return i ; } /* find the article in the has table and return it. */ static Article hashFindArticle (const char *msgid) { unsigned int hash = hashString (msgid) ; HashEntry h ; for (h = hashTable [TABLE_ENTRY(hash)] ; h != NULL ; h = h->next) if (hash == h->hash && strcmp (msgid,h->article->msgid) == 0) break ; return (h == NULL ? NULL : h->article) ; } /* add the article to the hash table. */ static void hashAddArticle (Article article) { unsigned int hash = hashString (article->msgid) ; HashEntry h ; HashEntry ne ; h = hashTable [TABLE_ENTRY(hash)] ; ne = xmalloc (sizeof(struct hash_entry_s)); ne->article = article ; ne->hash = hash ; ne->next = hashTable [TABLE_ENTRY(hash)] ; ne->prev = NULL ; if (h != NULL) h->prev = ne ; hashTable [TABLE_ENTRY(hash)] = ne ; ne->nextTime = chronList ; ne->prevTime = NULL ; if (chronList != NULL) chronList->prevTime = ne ; chronList = ne ; } /* remove the article from the hash table and chronological list. Does not delete the article itself. */ static bool hashRemoveArticle (Article article) { unsigned int hash = hashString (article->msgid) ; HashEntry h ; for (h = hashTable [TABLE_ENTRY(hash)] ; h != NULL ; h = h->next) if (hash == h->hash && strcmp (article->msgid,h->article->msgid) == 0) break ; if (h == NULL) return false ; if (h == hashTable [TABLE_ENTRY(hash)]) { hashTable [TABLE_ENTRY(hash)] = h->next ; if (h->next != NULL) h->next->prev = NULL ; } else { h->prev->next = h->next ; if (h->next != NULL) h->next->prev = h->prev ; } if (chronList == h) { chronList = h->nextTime ; if (chronList != NULL) chronList->prevTime = NULL ; } else { h->prevTime->nextTime = h->nextTime ; if (h->nextTime != NULL) h->nextTime->prevTime = h->prevTime ; } free (h) ; return true ; } #define HASH_VALIDATE_BUCKET_COUNT 1 /* hash buckets to check per call */ static void hashValidateTable (void) { static int hbn = 0 ; int i ; HashEntry he ; #if ! defined (NDEBUG) for (i = 0 ; i < HASH_VALIDATE_BUCKET_COUNT ; i++) { for (he = hashTable [hbn] ; he != NULL ; he = he->next) ASSERT (he->article->refCount > 0) ; if (++hbn >= TABLE_SIZE) hbn = 0 ; } #endif } inn-2.6.0/innfeed/connection.c0000644000175200017520000040452712575023702015647 0ustar iuliusiulius/* $Id: connection.c 9926 2015-08-08 17:08:02Z iulius $ ** ** The implementation of the innfeed Connection class. ** ** Written by James Brister ** ** The Connection object is what manages the NNTP protocol. If the remote ** doesn't do streaming, then the standard IHAVE lock-step protcol is ** performed. In the streaming situation we have two cases. One where we must ** send CHECK commands, and the other where we can directly send TAKETHIS ** commands without a prior CHECK. ** ** The Connection object maintains four article queues. The first one is ** where new articles are put if they need to have an IHAVE or CHECK command ** sent for them. The second queue is where the articles move from the first ** after their IHAVE/CHECK command is sent, but the reply has not yet been ** seen. The third queue is where articles go after the IHAVE/CHECK reply has ** been seen (and the reply says to send the article). It is articles in the ** third queue that have the TAKETHIS command sent, or the body of an IHAVE. ** The third queue is also where new articles go if the connection is running ** in no-CHECK mode. The fourth queue is where the articles move to from the ** third queue after their IHAVE-body or TAKETHIS command has been sent. When ** the response to the IHAVE-body or TAKETHIS is received the articles are ** removed from the fourth queue and the Host object controlling this ** Connection is notified of the success or failure of the transfer. ** ** The whole system is event-driven by the EndPoint class and the Host via ** calls to prepareRead() and prepareWrite() and prepareSleep(). ** ** ** We should probably store the results of gethostbyname in the connection so ** we can rotate through the address when one fails for connecting. Perhaps ** the gethostbyname should be done in the Host and the connection should ** just be given the address to use. ** ** Should we worry about articles being stuck on a queue for ever if the ** remote forgets to send a response to a CHECK? ** ** Perhaps instead of killing the connection on some of the more simple ** errors, we should perhaps try to flush the input and keep going. ** ** Worry about counter overflow. ** ** Worry about stats gathering when switch to no-check mode. ** ** XXX if issueQUIT() has a problem and the state goes to cxnDeadS this is ** not handled properly everywhere yet. */ #include "innfeed.h" #include "config.h" #include "clibrary.h" #include "portable/socket.h" #include #include #include #include #include #include #ifdef HAVE_SYS_TIME_H # include #endif #include #if defined (__FreeBSD__) # include #endif #include "inn/fdflag.h" #include "inn/innconf.h" #include "inn/messages.h" #include "inn/network.h" #include "inn/libinn.h" #include "article.h" #include "buffer.h" #include "configfile.h" #include "connection.h" #include "endpoint.h" #include "host.h" #if defined (NDEBUG) #define VALIDATE_CONNECTION(x) ((void) 0) #else #define VALIDATE_CONNECTION(x) validateConnection (x) #endif extern char **PointersFreedOnExit ; extern const char *pidFile ; /* * Private types. */ /* We keep a linked list of articles the connection is trying to transmit */ typedef struct art_holder_s { Article article ; struct art_holder_s *next ; } *ArtHolder ; typedef enum { cxnStartingS, /* the connection's start state. */ cxnWaitingS, /* not connected. Waiting for an article. */ cxnConnectingS, /* in the middle of connecting */ cxnIdleS, /* open and ready to feed, has empty queues */ cxnIdleTimeoutS, /* timed out in the idle state */ cxnFeedingS, /* in the processes of feeding articles */ cxnSleepingS, /* blocked on reestablishment timer */ cxnFlushingS, /* am waiting for queues to drain to bounce connection. */ cxnClosingS, /* have been told to close down permanently when queues drained */ cxnDeadS /* connection is dead. */ } CxnState ; /* The Connection class */ struct connection_s { Host myHost ; /* the host who owns the connection */ EndPoint myEp ; /* the endpoint the connection talks through */ unsigned int ident ; /* an identifier for syslogging. */ CxnState state ; /* the state the connection is in */ /* * The Connection maintains 4 queue of articles. */ ArtHolder checkHead ; /* head of article list to do CHECK/IHAVE */ ArtHolder checkRespHead ; /* head of list waiting on CHECK/IHAVE response */ ArtHolder takeHead ; /* head of list of articles to send TAKETHIS/IHAVE-body */ ArtHolder takeRespHead ; /* list of articles waiting on TAKETHIS/IHAVE-body response */ unsigned int articleQTotal ; /* number of articles in all four queues */ ArtHolder missing ; /* head of missing list */ Buffer respBuffer ; /* buffer all responses are read into */ char *ipName ; /* the ip name (possibly quad) of the remote */ unsigned int maxCheck ; /* the max number of CHECKs to send */ unsigned short port ; /* the port number to use */ /* * Timeout values and their callback IDs */ /* Timer for max amount of time between receiving articles from the Host */ unsigned int articleReceiptTimeout ; TimeoutId artReceiptTimerId ; /* Timer for the max amount of time to wait for a response from the remote */ unsigned int readTimeout ; TimeoutId readBlockedTimerId ; /* Timer for the max amount of time to wait for a any amount of data to be written to the remote */ unsigned int writeTimeout ; TimeoutId writeBlockedTimerId ; /* Timer for the max number of seconds to keep the network connection up (long lasting connections give older nntp servers problems). */ unsigned int flushTimeout ; TimeoutId flushTimerId ; /* Timer for the number of seconds to sleep before attempting a reconnect. */ unsigned int sleepTimeout ; TimeoutId sleepTimerId ; bool loggedNoCr ; /* true if we logged the NOCR_MSG */ bool immedRecon ; /* true if we recon immediately after flushing. */ bool doesStreaming ; /* true if remote will handle streaming */ bool authenticated ; /* true if remote authenticated */ bool quitWasIssued ; /* true if QUIT command was sent. */ bool needsChecks ; /* true if we issue CHECK commands in streaming mode (rather than just sending TAKETHIS commands) */ time_t timeCon; /* the time the connect happened (including the MODE STREAM command) */ time_t timeCon_checkpoint; /* * STATISTICS */ unsigned int artsTaken; /* the number of articles INN gave this cxn */ unsigned int checksIssued; /* the number of CHECKs/IHAVEs we sent. Note that if we're running in no-CHECK mode, then we add in the TAKETHIS commands too */ unsigned int checksIssued_checkpoint; unsigned int checksRefused; /* the number of response 435/438 */ unsigned int checksRefused_checkpoint; unsigned int takesRejected; /* the number of response 437/439 received */ unsigned int takesRejected_checkpoint; unsigned int takesOkayed; /* the number of response 235/239 received */ unsigned int takesOkayed_checkpoint; double takesSizeRejected; double takesSizeRejected_checkpoint; double takesSizeOkayed; double takesSizeOkayed_checkpoint; double onThreshold ; /* for no-CHECK mode */ double offThreshold ; /* for no-CHECK mode */ double filterValue ; /* current value of IIR filter */ double lowPassFilter ; /* time constant for IIR filter */ Connection next ; /* for global list */ }; static Connection gCxnList = NULL ; static unsigned int gCxnCount = 0 ; unsigned int max_reconnect_period = MAX_RECON_PER ; unsigned int init_reconnect_period = INIT_RECON_PER ; #if 0 static bool inited = false ; #endif static Buffer dotFirstBuffer ; static Buffer dotBuffer ; static Buffer crlfBuffer ; /*************************************************** * * Private function declarations. * ***************************************************/ /* I/O Callbacks */ static void connectionDone (EndPoint e, IoStatus i, Buffer *b, void *d) ; static void getBanner (EndPoint e, IoStatus i, Buffer *b, void *d) ; static void getAuthUserResponse (EndPoint e, IoStatus i, Buffer *b, void *d) ; static void getAuthPassResponse (EndPoint e, IoStatus i, Buffer *b, void *d) ; static void getModeResponse (EndPoint e, IoStatus i, Buffer *b, void *d) ; static void responseIsRead (EndPoint e, IoStatus i, Buffer *b, void *d) ; static void quitWritten (EndPoint e, IoStatus i, Buffer *b, void *d) ; static void ihaveBodyDone (EndPoint e, IoStatus i, Buffer *b, void *d) ; static void commandWriteDone (EndPoint e, IoStatus i, Buffer *b, void *d) ; static void modeCmdIssued (EndPoint e, IoStatus i, Buffer *b, void *d) ; static void authUserIssued (EndPoint e, IoStatus i, Buffer *b, void *d) ; static void authPassIssued (EndPoint e, IoStatus i, Buffer *b, void *d) ; static void writeProgress (EndPoint e, IoStatus i, Buffer *b, void *d) ; /* Timer callbacks */ static void responseTimeoutCbk (TimeoutId id, void *data) ; static void writeTimeoutCbk (TimeoutId id, void *data) ; static void reopenTimeoutCbk (TimeoutId id, void *data) ; static void flushCxnCbk (TimeoutId, void *data) ; static void articleTimeoutCbk (TimeoutId id, void *data) ; /* Work callbacks */ static void cxnWorkProc (EndPoint ep, void *data) ; static void cxnSleepOrDie (Connection cxn) ; /* Response processing. */ static void processResponse205 (Connection cxn, char *response) ; static void processResponse238 (Connection cxn, char *response) ; static void processResponse431 (Connection cxn, char *response) ; static void processResponse438 (Connection cxn, char *response) ; static void processResponse239 (Connection cxn, char *response) ; static void processResponse439 (Connection cxn, char *response) ; static void processResponse235 (Connection cxn, char *response) ; static void processResponse335 (Connection cxn, char *response) ; static void processResponse400 (Connection cxn, char *response) ; static void processResponse435 (Connection cxn, char *response) ; static void processResponse436 (Connection cxn, char *response) ; static void processResponse437 (Connection cxn, char *response) ; static void processResponse480 (Connection cxn, char *response) ; static void processResponse503 (Connection cxn, char *response) ; /* Misc functions */ static void cxnSleep (Connection cxn) ; static void cxnDead (Connection cxn) ; static void cxnIdle (Connection cxn) ; static void noSuchMessageId (Connection cxn, unsigned int responseCode, const char *msgid, const char *response) ; static void abortConnection (Connection cxn) ; static void resetConnection (Connection cxn) ; static void deferAllArticles (Connection cxn) ; static void deferQueuedArticles (Connection cxn) ; static void doSomeWrites (Connection cxn) ; static bool issueIHAVE (Connection cxn) ; static void issueIHAVEBody (Connection cxn) ; static bool issueStreamingCommands (Connection cxn) ; static Buffer buildCheckBuffer (Connection cxn) ; static Buffer *buildTakethisBuffers (Connection cxn, Buffer checkBuffer) ; static void issueQUIT (Connection cxn) ; static void initReadBlockedTimeout (Connection cxn) ; static int prepareWriteWithTimeout (EndPoint endp, Buffer *buffers, EndpRWCB done, Connection cxn) ; static void delConnection (Connection cxn) ; static void incrFilter (Connection cxn) ; static void decrFilter (Connection cxn) ; static bool writesNeeded (Connection cxn) ; static void validateConnection (Connection cxn) ; static const char *stateToString (CxnState state) ; static void issueModeStream (EndPoint e, Connection cxn) ; static void issueAuthUser (EndPoint e, Connection cxn) ; static void issueAuthPass (EndPoint e, Connection cxn) ; static void prepareReopenCbk (Connection cxn) ; /* Article queue management routines. */ static ArtHolder newArtHolder (Article art) ; static void delArtHolder (ArtHolder artH) ; static bool remArtHolder (ArtHolder art, ArtHolder *head, unsigned int *count) ; static void appendArtHolder (ArtHolder artH, ArtHolder *head, unsigned int *count) ; static ArtHolder artHolderByMsgId (const char *msgid, ArtHolder head) ; static int fudgeFactor (int initVal) ; /*************************************************** * * Public functions implementation. * ***************************************************/ int cxnConfigLoadCbk (void *data UNUSED) { long iv ; int rval = 1 ; FILE *fp = (FILE *) data ; if (getInteger (topScope,"max-reconnect-time",&iv,NO_INHERIT)) { if (iv < 1) { rval = 0 ; logOrPrint (LOG_ERR,fp, "ME config: value of %s (%ld) in %s cannot be less" " than 1. Using %ld", "max-reconnect-time", iv,"global scope",(long) MAX_RECON_PER); iv = MAX_RECON_PER ; } } else iv = MAX_RECON_PER ; max_reconnect_period = (unsigned int) iv ; if (getInteger (topScope,"initial-reconnect-time",&iv,NO_INHERIT)) { if (iv < 1) { rval = 0 ; logOrPrint (LOG_ERR,fp, "ME config: value of %s (%ld) in %s cannot be less" " than 1. Using %ld", "initial-reconnect-time", iv,"global scope",(long)INIT_RECON_PER); iv = INIT_RECON_PER ; } } else iv = INIT_RECON_PER ; init_reconnect_period = (unsigned int) iv ; return rval ; } /* * Create a new Connection object and return it. All fields are * initialized to reasonable values. */ Connection newConnection (Host host, unsigned int id, const char *ipname, unsigned int articleReceiptTimeout, unsigned int portNum, unsigned int respTimeout, unsigned int flushTimeout, double lowPassLow, double lowPassHigh, double lowPassFilter) { Connection cxn ; bool croak = false ; if (ipname == NULL) { d_printf (1,"NULL ipname in newConnection\n") ; croak = true ; } if (ipname && strlen (ipname) == 0) { d_printf (1,"Empty ipname in newConnection\n") ; croak = true ; } if (croak) return NULL ; cxn = xcalloc (1, sizeof(struct connection_s)); cxn->myHost = host ; cxn->myEp = NULL ; cxn->ident = id ; cxn->checkHead = NULL ; cxn->checkRespHead = NULL ; cxn->takeHead = NULL ; cxn->takeRespHead = NULL ; cxn->articleQTotal = 0 ; cxn->missing = NULL ; cxn->respBuffer = newBuffer (BUFFER_SIZE) ; ASSERT (cxn->respBuffer != NULL) ; cxn->ipName = xstrdup (ipname) ; cxn->port = portNum ; /* Time out the higher numbered connections faster */ cxn->articleReceiptTimeout = articleReceiptTimeout * 10.0 / (10.0 + id) ; cxn->artReceiptTimerId = 0 ; cxn->readTimeout = respTimeout ; cxn->readBlockedTimerId = 0 ; cxn->writeTimeout = respTimeout ; /* XXX should be a separate value */ cxn->writeBlockedTimerId = 0 ; cxn->flushTimeout = fudgeFactor (flushTimeout) ; cxn->flushTimerId = 0 ; cxn->onThreshold = lowPassHigh * lowPassFilter / 100.0 ; cxn->offThreshold = lowPassLow * lowPassFilter / 100.0 ; cxn->lowPassFilter = lowPassFilter; cxn->sleepTimerId = 0 ; cxn->sleepTimeout = init_reconnect_period ; resetConnection (cxn) ; cxn->next = gCxnList ; gCxnList = cxn ; gCxnCount++ ; cxn->state = cxnStartingS ; return cxn ; } /* Create a new endpoint hooked to a non-blocking socket that is trying to * connect to the host info stored in the Connection. On fast machines * connecting locally the connect() may have already succeeded when this * returns, but typically the connect will still be running and when it * completes. The Connection will be notified via a write callback setup by * prepareWrite below. If nothing goes wrong then this will return true * (even if the connect() has not yet completed). If something fails * (hostname lookup etc.) then it returns false (and the Connection is left * in the sleeping state).. * * Pre-state Reason cxnConnect called * --------- ------------------------ * cxnStartingS Connection owner issued call. * cxnWaitingS side effect of cxnTakeArticle() call * cxnConnecting side effect of cxnFlush() call * cxnSleepingS side effect of reopenTimeoutCbk() call. */ bool cxnConnect (Connection cxn) { struct sockaddr *cxnAddr; int fd, rval; const char *src; const char *peerName = hostPeerName (cxn->myHost) ; ASSERT (cxn->myEp == NULL) ; if (!(cxn->state == cxnStartingS || cxn->state == cxnWaitingS || cxn->state == cxnFlushingS || cxn->state == cxnSleepingS)) { warn ("%s:%d cxnsleep connection in bad state: %s", hostPeerName (cxn->myHost), cxn->ident, stateToString (cxn->state)) ; cxnSleepOrDie (cxn) ; return false; } if (cxn->state == cxnWaitingS) ASSERT (cxn->articleQTotal == 1) ; else ASSERT (cxn->articleQTotal == 0) ; cxn->state = cxnConnectingS ; cxnAddr = hostIpAddr (cxn->myHost) ; if (cxnAddr == NULL) { cxnSleepOrDie (cxn) ; return false ; } if (cxnAddr->sa_family == AF_INET) src = hostBindAddr(cxn->myHost); else src = hostBindAddr6(cxn->myHost); if (src && strcmp(src, "none") == 0) src = NULL; fd = network_client_create (cxnAddr->sa_family, SOCK_STREAM, src); if (fd < 0) { syswarn ("%s:%d cxnsleep can't create socket", peerName, cxn->ident) ; d_printf (1,"Can't get a socket: %s\n", strerror (errno)) ; hostIpFailed (cxn->myHost) ; cxnSleepOrDie (cxn) ; return false ; } if (!fdflag_nonblocking (fd, true)) { syswarn ("%s:%d cxnsleep can't set socket non-blocking", peerName, cxn->ident) ; close (fd) ; cxnSleepOrDie (cxn) ; return false ; } rval = connect (fd, cxnAddr, SA_LEN(cxnAddr)) ; if (rval < 0 && errno != EINPROGRESS) { syswarn ("%s:%d connect", peerName, cxn->ident) ; hostIpFailed (cxn->myHost) ; close (fd) ; cxnSleepOrDie (cxn) ; return false ; } if ((cxn->myEp = newEndPoint (fd)) == NULL) { /* If this happens, then fd was bigger than what select could handle, so endpoint.c refused to create the new object. */ close (fd) ; cxnSleepOrDie (cxn) ; return false ; } if (rval < 0) /* when the write callback gets done the connection went through */ prepareWrite (cxn->myEp, NULL, NULL, connectionDone, cxn) ; else connectionDone (cxn->myEp, IoDone, NULL, cxn) ; /* connectionDone() could set state to sleeping */ return (cxn->state == cxnConnectingS ? true : false) ; } /* Put the Connection into the wait state. * * Pre-state Reason cxnWait called * --------- ------------------------ * cxnStartingS - Connection owner called cxnWait() * cxnSleepingS - side effect of cxnFlush() call. * cxnConnectingS - side effect of cxnFlush() call. * cxnFlushingS - side effect of receiving response 205 * and Connection had no articles when * cxnFlush() was issued. * - prepareRead failed. * - I/O failed. * */ void cxnWait (Connection cxn) { ASSERT (cxn->state == cxnStartingS || cxn->state == cxnSleepingS || cxn->state == cxnConnectingS || cxn->state == cxnFeedingS || cxn->state == cxnFlushingS) ; VALIDATE_CONNECTION (cxn) ; abortConnection (cxn) ; cxn->state = cxnWaitingS ; hostCxnWaiting (cxn->myHost,cxn) ; /* tell our Host we're waiting */ } /* Tells the Connection to flush itself (i.e. push out all articles, * issue a QUIT and drop the network connection. If necessary a * reconnect will be done immediately after. Called by the Host, or * by the timer callback. * * Pre-state Reason cxnFlush called * --------- ------------------------ * ALL (except cxnDeadS - Connection owner called cxnFlush() * and cxnStartingS) * cxnFeedingS - side effect of flushCxnCbk() call. */ void cxnFlush (Connection cxn) { ASSERT (cxn != NULL) ; ASSERT (cxn->state != cxnStartingS) ; ASSERT (cxn->state != cxnDeadS) ; VALIDATE_CONNECTION (cxn) ; switch (cxn->state) { case cxnSleepingS: cxnWait (cxn) ; break ; case cxnConnectingS: cxnWait (cxn) ; cxnConnect (cxn) ; break ; case cxnIdleTimeoutS: case cxnIdleS: ASSERT (cxn->articleQTotal == 0) ; if (cxn->state != cxnIdleTimeoutS) clearTimer (cxn->artReceiptTimerId) ; clearTimer (cxn->flushTimerId) ; cxn->state = cxnFlushingS ; issueQUIT (cxn) ; break ; case cxnClosingS: case cxnFlushingS: case cxnWaitingS: if (cxn->articleQTotal == 0 && !writeIsPending (cxn->myEp)) issueQUIT (cxn) ; break ; case cxnFeedingS: /* we only reconnect immediately if we're not idle when cxnFlush() is called. */ if (!cxn->immedRecon) { cxn->immedRecon = (cxn->articleQTotal > 0 ? true : false) ; d_printf (1,"%s:%d immediate reconnect for a cxnFlush()\n", hostPeerName (cxn->myHost), cxn->ident) ; } clearTimer (cxn->flushTimerId) ; cxn->state = cxnFlushingS ; if (cxn->articleQTotal == 0 && !writeIsPending (cxn->myEp)) issueQUIT (cxn) ; break ; default: die ("Bad connection state: %s\n",stateToString (cxn->state)) ; } } /* * Tells the Connection to dump all articles that are queued and to issue a * QUIT as quickly as possible. Much like cxnClose, except queued articles * are not sent, but are given back to the Host. */ void cxnTerminate (Connection cxn) { ASSERT (cxn != NULL) ; ASSERT (cxn->state != cxnDeadS) ; ASSERT (cxn->state != cxnStartingS) ; VALIDATE_CONNECTION (cxn) ; switch (cxn->state) { case cxnFeedingS: d_printf (1,"%s:%d Issuing terminate\n", hostPeerName (cxn->myHost), cxn->ident) ; clearTimer (cxn->flushTimerId) ; cxn->state = cxnClosingS ; deferQueuedArticles (cxn) ; if (cxn->articleQTotal == 0) issueQUIT (cxn) ; /* send out the QUIT if we can */ break ; case cxnIdleTimeoutS: case cxnIdleS: ASSERT (cxn->articleQTotal == 0) ; if (cxn->state != cxnIdleTimeoutS) clearTimer (cxn->artReceiptTimerId) ; clearTimer (cxn->flushTimerId) ; cxn->state = cxnClosingS ; issueQUIT (cxn) ; break ; case cxnFlushingS: /* we are in the middle of a periodic close. */ d_printf (1,"%s:%d Connection already being flushed\n", hostPeerName (cxn->myHost),cxn->ident); cxn->state = cxnClosingS ; if (cxn->articleQTotal == 0) issueQUIT (cxn) ; /* send out the QUIT if we can */ break ; case cxnClosingS: d_printf (1,"%s:%d Connection already closing\n", hostPeerName (cxn->myHost),cxn->ident) ; break ; case cxnWaitingS: case cxnConnectingS: case cxnSleepingS: cxnDead (cxn) ; break ; default: die ("Bad connection state: %s\n",stateToString (cxn->state)) ; } VALIDATE_CONNECTION (cxn) ; if (cxn->state == cxnDeadS) { d_printf (1,"%s:%d Deleting connection\n",hostPeerName (cxn->myHost), cxn->ident) ; delConnection (cxn) ; } } /* Tells the Connection to do a disconnect and then when it is * disconnected to delete itself. * * Pre-state Reason cxnClose called * --------- ------------------------ * ALL (except cxnDeadS - Connecton owner called directly. * and cxnStartingS). */ void cxnClose (Connection cxn) { ASSERT (cxn != NULL) ; ASSERT (cxn->state != cxnDeadS) ; ASSERT (cxn->state != cxnStartingS) ; VALIDATE_CONNECTION (cxn) ; switch (cxn->state) { case cxnFeedingS: d_printf (1,"%s:%d Issuing disconnect\n", hostPeerName (cxn->myHost), cxn->ident) ; clearTimer (cxn->flushTimerId) ; cxn->state = cxnClosingS ; if (cxn->articleQTotal == 0) issueQUIT (cxn) ; /* send out the QUIT if we can */ break ; case cxnIdleS: case cxnIdleTimeoutS: ASSERT (cxn->articleQTotal == 0) ; if (cxn->state != cxnIdleTimeoutS) clearTimer (cxn->artReceiptTimerId) ; clearTimer (cxn->flushTimerId) ; cxn->state = cxnClosingS ; issueQUIT (cxn) ; break ; case cxnFlushingS: /* we are in the middle of a periodic close. */ d_printf (1,"%s:%d Connection already being flushed\n", hostPeerName (cxn->myHost),cxn->ident); cxn->state = cxnClosingS ; if (cxn->articleQTotal == 0) issueQUIT (cxn) ; /* send out the QUIT if we can */ break ; case cxnClosingS: d_printf (1,"%s:%d Connection already closing\n", hostPeerName (cxn->myHost),cxn->ident) ; break ; case cxnWaitingS: case cxnConnectingS: case cxnSleepingS: cxnDead (cxn) ; break ; default: die ("Bad connection state: %s\n",stateToString (cxn->state)) ; } VALIDATE_CONNECTION (cxn) ; if (cxn->state == cxnDeadS) { d_printf (1,"%s:%d Deleting connection\n",hostPeerName (cxn->myHost), cxn->ident) ; delConnection (cxn) ; } } /* This is what the Host calls to get us to tranfer an article. If * we're running the IHAVE sequence, then we can't take it if we've * got an article already. If we're running the CHECK/TAKETHIS * sequence, then we'll take as many as we can (up to our MAXCHECK * limit). */ bool cxnTakeArticle (Connection cxn, Article art) { bool rval = true ; ASSERT (cxn != NULL) ; VALIDATE_CONNECTION (cxn) ; if ( !cxnQueueArticle (cxn,art) ) /* might change cxnIdleS to cxnFeedingS */ return false ; if (!(cxn->state == cxnConnectingS || cxn->state == cxnFeedingS || cxn->state == cxnWaitingS)) { warn ("%s:%d cxnsleep connection in bad state: %s", hostPeerName (cxn->myHost), cxn->ident, stateToString (cxn->state)) ; cxnSleepOrDie (cxn) ; return false ; } if (cxn->state != cxnWaitingS) /* because articleQTotal == 1 */ VALIDATE_CONNECTION (cxn) ; else ASSERT (cxn->articleQTotal == 1) ; switch (cxn->state) { case cxnWaitingS: cxnConnect (cxn) ; break ; case cxnFeedingS: doSomeWrites (cxn) ; break ; case cxnConnectingS: break ; default: die ("Bad connection state: %s\n",stateToString (cxn->state)) ; } return rval ; } /* if there's room in the Connection then stick the article on the * queue, otherwise return false. */ bool cxnQueueArticle (Connection cxn, Article art) { ArtHolder newArt ; bool rval = false ; ASSERT (cxn != NULL) ; ASSERT (cxn->state != cxnStartingS) ; ASSERT (cxn->state != cxnDeadS) ; VALIDATE_CONNECTION (cxn) ; switch (cxn->state) { case cxnClosingS: d_printf (5,"%s:%d Refusing article due to closing\n", hostPeerName (cxn->myHost),cxn->ident) ; break ; case cxnFlushingS: d_printf (5,"%s:%d Refusing article due to flushing\n", hostPeerName (cxn->myHost),cxn->ident) ; break ; case cxnSleepingS: d_printf (5,"%s:%d Refusing article due to sleeping\n", hostPeerName (cxn->myHost),cxn->ident) ; break ; case cxnWaitingS: rval = true ; newArt = newArtHolder (art) ; appendArtHolder (newArt, &cxn->checkHead, &cxn->articleQTotal) ; break ; case cxnConnectingS: if (cxn->articleQTotal != 0) break ; rval = true ; newArt = newArtHolder (art) ; appendArtHolder (newArt, &cxn->checkHead, &cxn->articleQTotal) ; break ; case cxnIdleS: case cxnFeedingS: if (cxn->articleQTotal >= cxn->maxCheck) d_printf (5, "%s:%d Refusing article due to articleQTotal >= maxCheck (%d > %d)\n", hostPeerName (cxn->myHost), cxn->ident, cxn->articleQTotal, cxn->maxCheck) ; else { rval = true ; newArt = newArtHolder (art) ; if (cxn->needsChecks) appendArtHolder (newArt, &cxn->checkHead, &cxn->articleQTotal) ; else appendArtHolder (newArt, &cxn->takeHead, &cxn->articleQTotal) ; if (cxn->state == cxnIdleS) { cxn->state = cxnFeedingS ; clearTimer (cxn->artReceiptTimerId) ; } } break ; default: die ("Invalid state: %s\n", stateToString (cxn->state)) ; } if (rval) { d_printf (5,"%s:%d accepting article %s\n",hostPeerName (cxn->myHost), cxn->ident,artMsgId (art)) ; cxn->artsTaken++ ; } return rval ; } /* * Generate a log message for activity. Usually called by the Connection's * owner. */ void cxnLogStats (Connection cxn, bool final) { const char *peerName ; time_t now = theTime() ; ASSERT (cxn != NULL) ; /* Only log stats when in one of these three states. */ switch (cxn->state) { case cxnFeedingS: case cxnFlushingS: case cxnClosingS: break ; default: return ; } peerName = hostPeerName (cxn->myHost) ; /* Log a checkpoint in any case. */ notice("%s:%d checkpoint seconds %ld offered %d accepted %d refused %d" " rejected %d accsize %.0f rejsize %.0f", peerName, cxn->ident, (long) (now - cxn->timeCon_checkpoint), cxn->checksIssued - cxn->checksIssued_checkpoint, cxn->takesOkayed - cxn->takesOkayed_checkpoint, cxn->checksRefused - cxn->checksRefused_checkpoint, cxn->takesRejected - cxn->takesRejected_checkpoint, cxn->takesSizeOkayed - cxn->takesSizeOkayed_checkpoint, cxn->takesSizeRejected - cxn->takesSizeRejected_checkpoint); if (final) { notice("%s:%d final seconds %ld offered %d accepted %d refused %d" " rejected %d accsize %.0f rejsize %.0f", peerName, cxn->ident, (long) (now - cxn->timeCon), cxn->checksIssued, cxn->takesOkayed, cxn->checksRefused, cxn->takesRejected, cxn->takesSizeOkayed, cxn->takesSizeRejected); cxn->artsTaken = 0; cxn->checksIssued = 0; cxn->checksRefused = 0; cxn->takesRejected = 0; cxn->takesOkayed = 0; cxn->takesSizeRejected = 0; cxn->takesSizeOkayed = 0; if (cxn->timeCon > 0) { cxn->timeCon = theTime(); } } cxn->timeCon_checkpoint = now; cxn->checksIssued_checkpoint = cxn->checksIssued; cxn->takesOkayed_checkpoint = cxn->takesOkayed; cxn->checksRefused_checkpoint = cxn->checksRefused; cxn->takesRejected_checkpoint = cxn->takesRejected; cxn->takesSizeOkayed_checkpoint = cxn->takesSizeOkayed; cxn->takesSizeRejected_checkpoint = cxn->takesSizeRejected; } /* * return the number of articles the connection will accept. */ size_t cxnQueueSpace (Connection cxn) { int rval = 0 ; ASSERT (cxn != NULL) ; if (cxn->state == cxnFeedingS || cxn->state == cxnIdleS || cxn->state == cxnConnectingS || cxn->state == cxnWaitingS) rval = cxn->maxCheck - cxn->articleQTotal ; return rval ; } /* * Print info on all the connections that currently exist. */ void gPrintCxnInfo (FILE *fp, unsigned int indentAmt) { char indent [INDENT_BUFFER_SIZE] ; unsigned int i ; Connection cxn ; for (i = 0 ; i < MIN(INDENT_BUFFER_SIZE - 1,indentAmt) ; i++) indent [i] = ' ' ; indent [i] = '\0' ; fprintf (fp,"%sGlobal Connection list : (count %d) {\n", indent,gCxnCount) ; for (cxn = gCxnList ; cxn != NULL ; cxn = cxn->next) printCxnInfo (cxn,fp,indentAmt + INDENT_INCR) ; fprintf (fp,"%s}\n",indent) ; } /* * Print the info about the given connection. */ void printCxnInfo (Connection cxn, FILE *fp, unsigned int indentAmt) { char indent [INDENT_BUFFER_SIZE] ; unsigned int i ; ArtHolder artH ; for (i = 0 ; i < MIN(INDENT_BUFFER_SIZE - 1,indentAmt) ; i++) indent [i] = ' ' ; indent [i] = '\0' ; fprintf (fp,"%sConnection : %p {\n",indent, (void *) cxn) ; fprintf (fp,"%s host : %p\n",indent, (void *) cxn->myHost) ; fprintf (fp,"%s endpoint : %p\n",indent, (void *) cxn->myEp) ; fprintf (fp,"%s state : %s\n",indent, stateToString (cxn->state)) ; fprintf (fp,"%s ident : %d\n",indent,cxn->ident) ; fprintf (fp,"%s ip-name : %s\n", indent, cxn->ipName) ; fprintf (fp,"%s port-number : %d\n",indent,cxn->port) ; fprintf (fp,"%s max-checks : %d\n",indent,cxn->maxCheck) ; fprintf (fp,"%s does-streaming : %s\n",indent, boolToString (cxn->doesStreaming)) ; fprintf (fp,"%s authenticated : %s\n",indent, boolToString (cxn->authenticated)) ; fprintf (fp,"%s quitWasIssued : %s\n",indent, boolToString (cxn->quitWasIssued)) ; fprintf (fp,"%s needs-checks : %s\n",indent, boolToString (cxn->needsChecks)) ; fprintf (fp,"%s time-connected : %ld\n",indent,(long) cxn->timeCon) ; fprintf (fp,"%s articles from INN : %d\n",indent,cxn->artsTaken) ; fprintf (fp,"%s articles offered : %d\n",indent, cxn->checksIssued) ; fprintf (fp,"%s articles refused : %d\n",indent, cxn->checksRefused) ; fprintf (fp,"%s articles rejected : %d\n",indent, cxn->takesRejected) ; fprintf (fp,"%s articles accepted : %d\n",indent, cxn->takesOkayed) ; fprintf (fp,"%s low-pass upper limit : %0.6f\n", indent, cxn->onThreshold) ; fprintf (fp,"%s low-pass lower limit : %0.6f\n", indent, cxn->offThreshold) ; fprintf (fp,"%s low-pass filter tc : %0.6f\n", indent, cxn->lowPassFilter) ; fprintf (fp,"%s low-pass filter : %0.6f\n", indent, cxn->filterValue) ; fprintf (fp,"%s article-timeout : %d\n",indent,cxn->articleReceiptTimeout) ; fprintf (fp,"%s article-callback : %d\n",indent,cxn->artReceiptTimerId) ; fprintf (fp,"%s response-timeout : %d\n",indent,cxn->readTimeout) ; fprintf (fp,"%s response-callback : %d\n",indent,cxn->readBlockedTimerId) ; fprintf (fp,"%s write-timeout : %d\n",indent,cxn->writeTimeout) ; fprintf (fp,"%s write-callback : %d\n",indent,cxn->writeBlockedTimerId) ; fprintf (fp,"%s flushTimeout : %d\n",indent,cxn->flushTimeout) ; fprintf (fp,"%s flushTimerId : %d\n",indent,cxn->flushTimerId) ; fprintf (fp,"%s reopen wait : %d\n",indent,cxn->sleepTimeout) ; fprintf (fp,"%s reopen id : %d\n",indent,cxn->sleepTimerId) ; fprintf (fp,"%s CHECK queue {\n",indent) ; for (artH = cxn->checkHead ; artH != NULL ; artH = artH->next) printArticleInfo (artH->article,fp,indentAmt + INDENT_INCR) ; fprintf (fp,"%s }\n",indent) ; fprintf (fp,"%s CHECK Response queue {\n",indent) ; for (artH = cxn->checkRespHead ; artH != NULL ; artH = artH->next) printArticleInfo (artH->article,fp,indentAmt + INDENT_INCR) ; fprintf (fp,"%s }\n",indent) ; fprintf (fp,"%s TAKE queue {\n",indent) ; for (artH = cxn->takeHead ; artH != NULL ; artH = artH->next) printArticleInfo (artH->article,fp,indentAmt + INDENT_INCR) ; fprintf (fp,"%s }\n",indent) ; fprintf (fp,"%s TAKE response queue {\n",indent) ; for (artH = cxn->takeRespHead ; artH != NULL ; artH = artH->next) printArticleInfo (artH->article,fp,indentAmt + INDENT_INCR) ; fprintf (fp,"%s }\n",indent) ; fprintf (fp,"%s response buffer {\n",indent) ; printBufferInfo (cxn->respBuffer,fp,indentAmt + INDENT_INCR) ; fprintf (fp,"%s }\n",indent) ; fprintf (fp,"%s}\n",indent) ; } /* * Return whether the connection will accept articles. */ bool cxnCheckstate (Connection cxn) { bool rval = false ; ASSERT (cxn != NULL) ; if (cxn->state == cxnFeedingS || cxn->state == cxnIdleS || cxn->state == cxnConnectingS) rval = true ; return rval ; } /**********************************************************************/ /** STATIC PRIVATE FUNCTIONS **/ /**********************************************************************/ /* * ENDPOINT CALLBACK AREA. * * All the functions in this next section are callbacks fired by the * EndPoint objects/class (either timers or i/o completion callbacks).. */ /* * this is the first stage of the NNTP FSM. This function is called * when the tcp/ip network connection is setup and we should get * ready to read the banner message. When this function returns the * state of the Connection will still be cxnConnectingS unless * something broken, in which case it probably went into the * cxnSleepingS state. */ static void connectionDone (EndPoint e, IoStatus i, Buffer *b, void *d) { Buffer *readBuffers ; Connection cxn = (Connection) d ; const char *peerName ; int optval; socklen_t size ; ASSERT (b == NULL) ; ASSERT (cxn->state == cxnConnectingS) ; ASSERT (!writeIsPending (cxn->myEp)) ; size = sizeof (optval) ; peerName = hostPeerName (cxn->myHost) ; if (i != IoDone) { errno = endPointErrno (e) ; syswarn ("%s:%d cxnsleep i/o failed", peerName, cxn->ident) ; cxnSleepOrDie (cxn) ; } else if (getsockopt (endPointFd (e), SOL_SOCKET, SO_ERROR, (char *) &optval, &size) != 0) { /* This is bad. Can't even get the SO_ERROR value out of the socket */ syswarn ("%s:%d cxnsleep internal getsockopt", peerName, cxn->ident) ; cxnSleepOrDie (cxn) ; } else if (optval != 0) { /* if the connect failed then the only way to know is by getting the SO_ERROR value out of the socket. */ errno = optval ; syswarn ("%s:%d cxnsleep connect", peerName, cxn->ident) ; hostIpFailed (cxn->myHost) ; cxnSleepOrDie (cxn) ; } else { readBuffers = makeBufferArray (bufferTakeRef (cxn->respBuffer), NULL) ; if ( !prepareRead (e, readBuffers, getBanner, cxn, 1) ) { warn ("%s:%d cxnsleep prepare read failed", peerName, cxn->ident) ; cxnSleepOrDie (cxn) ; } else { initReadBlockedTimeout (cxn) ; /* set up the callback for closing down the connection at regular intervals (due to problems with long running nntpd). */ if (cxn->flushTimeout > 0) cxn->flushTimerId = prepareSleep (flushCxnCbk, cxn->flushTimeout,cxn) ; /* The state doesn't change yet until we've read the banner and tried the MODE STREAM command. */ } } VALIDATE_CONNECTION (cxn) ; } /* * This is called when we are so far in the connection setup that * we're confident it'll work. If the connection is IPv6, remove * the IPv4 addresses from the address list. */ static void connectionIfIpv6DeleteIpv4Addr (Connection cxn) { union { struct sockaddr sa; struct sockaddr_storage ss; } u; socklen_t len = sizeof(u); if (getpeername (endPointFd (cxn->myEp), &u.sa, &len) < 0) return; if (u.sa.sa_family == AF_INET) return; hostDeleteIpv4Addr (cxn->myHost); } /* * Called when the banner message has been read off the wire and is * in the buffer(s). When this function returns the state of the * Connection will still be cxnConnectingS unless something broken, * in which case it probably went into the cxnSleepiongS state. */ static void getBanner (EndPoint e, IoStatus i, Buffer *b, void *d) { Buffer *readBuffers ; Connection cxn = (Connection) d ; char *p = bufferBase (b[0]) ; int code ; bool isOk = false ; const char *peerName ; char *rest ; ASSERT (e == cxn->myEp) ; ASSERT (b[0] == cxn->respBuffer) ; ASSERT (b[1] == NULL) ; ASSERT (cxn->state == cxnConnectingS) ; ASSERT (!writeIsPending (cxn->myEp)); peerName = hostPeerName (cxn->myHost) ; bufferAddNullByte (b[0]) ; if (i != IoDone) { errno = endPointErrno (cxn->myEp) ; syswarn ("%s:%d cxnsleep can't read banner", peerName, cxn->ident) ; hostIpFailed (cxn->myHost) ; cxnSleepOrDie (cxn) ; } else if (strchr (p, '\n') == NULL) { /* partial read. expand buffer and retry */ expandBuffer (b[0], BUFFER_EXPAND_AMOUNT) ; readBuffers = makeBufferArray (bufferTakeRef (b[0]), NULL) ; if ( !prepareRead (e, readBuffers, getBanner, cxn, 1) ) { warn ("%s:%d cxnsleep prepare read failed", peerName, cxn->ident) ; cxnSleepOrDie (cxn) ; } } else if ( !getNntpResponse (p, &code, &rest) ) { trim_ws (p) ; warn ("%s:%d cxnsleep response format: %s", peerName, cxn->ident, p) ; cxnSleepOrDie (cxn) ; } else { trim_ws (p) ; switch (code) { case 200: /* normal */ case 201: /* can transfer but not post -- old nntpd */ isOk = true ; break ; case 400: cxnSleepOrDie (cxn) ; hostIpFailed (cxn->myHost) ; hostCxnBlocked (cxn->myHost, cxn, rest) ; break ; case 502: warn ("%s:%d cxnsleep no permission to talk: %s", peerName, cxn->ident, p) ; cxnSleepOrDie (cxn) ; hostIpFailed (cxn->myHost) ; hostCxnBlocked (cxn->myHost, cxn, rest) ; break ; default: warn ("%s:%d cxnsleep response unknown banner: %d %s", peerName, cxn->ident, code, p) ; d_printf (1,"%s:%d Unknown response code: %d: %s\n", hostPeerName (cxn->myHost),cxn->ident, code, p) ; cxnSleepOrDie (cxn) ; hostIpFailed (cxn->myHost) ; hostCxnBlocked (cxn->myHost, cxn, rest) ; break ; } if ( isOk ) { /* If we got this far and the connection is IPv6, remove the IPv4 addresses from the address list. */ connectionIfIpv6DeleteIpv4Addr (cxn); if (hostUsername (cxn->myHost) != NULL && hostPassword (cxn->myHost) != NULL) issueAuthUser (e,cxn); else issueModeStream (e,cxn); } } freeBufferArray (b) ; } static void issueAuthUser (EndPoint e, Connection cxn) { Buffer authUserBuffer; Buffer *authUserCmdBuffers,*readBuffers; size_t lenBuff = 0 ; char *t ; /* 17 == strlen("AUTHINFO USER \r\n\0") */ lenBuff = (17 + strlen (hostUsername (cxn->myHost))) ; authUserBuffer = newBuffer (lenBuff) ; t = bufferBase (authUserBuffer) ; sprintf (t, "AUTHINFO USER %s\r\n", hostUsername (cxn->myHost)) ; bufferSetDataSize (authUserBuffer, strlen (t)) ; authUserCmdBuffers = makeBufferArray (authUserBuffer, NULL) ; if ( !prepareWriteWithTimeout (e, authUserCmdBuffers, authUserIssued, cxn) ) { die ("%s:%d fatal prepare write for authinfo user failed", hostPeerName (cxn->myHost), cxn->ident) ; } bufferSetDataSize (cxn->respBuffer, 0) ; readBuffers = makeBufferArray (bufferTakeRef(cxn->respBuffer),NULL); if ( !prepareRead (e, readBuffers, getAuthUserResponse, cxn, 1) ) { warn ("%s:%d cxnsleep prepare read failed", hostPeerName (cxn->myHost), cxn->ident) ; freeBufferArray (readBuffers) ; cxnSleepOrDie (cxn) ; } } static void issueAuthPass (EndPoint e, Connection cxn) { Buffer authPassBuffer; Buffer *authPassCmdBuffers,*readBuffers; size_t lenBuff = 0 ; char *t ; /* 17 == strlen("AUTHINFO PASS \r\n\0") */ lenBuff = (17 + strlen (hostPassword (cxn->myHost))) ; authPassBuffer = newBuffer (lenBuff) ; t = bufferBase (authPassBuffer) ; sprintf (t, "AUTHINFO PASS %s\r\n", hostPassword (cxn->myHost)) ; bufferSetDataSize (authPassBuffer, strlen (t)) ; authPassCmdBuffers = makeBufferArray (authPassBuffer, NULL) ; if ( !prepareWriteWithTimeout (e, authPassCmdBuffers, authPassIssued, cxn) ) { die ("%s:%d fatal prepare write for authinfo pass failed", hostPeerName (cxn->myHost), cxn->ident) ; } bufferSetDataSize (cxn->respBuffer, 0) ; readBuffers = makeBufferArray (bufferTakeRef(cxn->respBuffer),NULL); if ( !prepareRead (e, readBuffers, getAuthPassResponse, cxn, 1) ) { warn ("%s:%d cxnsleep prepare read failed", hostPeerName (cxn->myHost), cxn->ident) ; freeBufferArray (readBuffers) ; cxnSleepOrDie (cxn) ; } } static void issueModeStream (EndPoint e, Connection cxn) { Buffer *modeCmdBuffers,*readBuffers ; Buffer modeBuffer ; char *p; #define MODE_CMD "MODE STREAM\r\n" modeBuffer = newBuffer (strlen (MODE_CMD) + 1) ; p = bufferBase (modeBuffer) ; /* now issue the MODE STREAM command */ d_printf (1, "%s:%d Issuing the streaming command\n", hostPeerName (cxn->myHost), cxn->ident) ; strlcpy (p, MODE_CMD, bufferSize (modeBuffer)) ; bufferSetDataSize (modeBuffer, strlen (p)) ; modeCmdBuffers = makeBufferArray (modeBuffer, NULL) ; if ( !prepareWriteWithTimeout (e, modeCmdBuffers, modeCmdIssued, cxn) ) { die ("%s:%d fatal prepare write for mode stream failed", hostPeerName (cxn->myHost), cxn->ident) ; } bufferSetDataSize (cxn->respBuffer, 0) ; readBuffers = makeBufferArray (bufferTakeRef(cxn->respBuffer),NULL); if ( !prepareRead (e, readBuffers, getModeResponse, cxn, 1) ) { warn ("%s:%d cxnsleep prepare read failed", hostPeerName (cxn->myHost), cxn->ident) ; freeBufferArray (readBuffers) ; cxnSleepOrDie (cxn) ; } } /* * */ static void getAuthUserResponse (EndPoint e, IoStatus i, Buffer *b, void *d) { Connection cxn = (Connection) d ; int code ; char *p = bufferBase (b[0]) ; Buffer *buffers ; const char *peerName ; ASSERT (e == cxn->myEp) ; ASSERT (b [0] == cxn->respBuffer) ; ASSERT (b [1] == NULL) ; /* only ever one buffer on this read */ ASSERT (cxn->state == cxnConnectingS) ; VALIDATE_CONNECTION (cxn) ; peerName = hostPeerName (cxn->myHost) ; bufferAddNullByte (b[0]) ; d_printf (1,"%s:%d Processing authinfo user response: %s", /* no NL */ hostPeerName (cxn->myHost), cxn->ident, p) ; if (i == IoDone && writeIsPending (cxn->myEp)) { /* badness. should never happen */ warn ("%s:%d cxnsleep authinfo command still pending", peerName, cxn->ident) ; cxnSleepOrDie (cxn) ; } else if (i != IoDone) { if (i != IoEOF) { errno = endPointErrno (e) ; syswarn ("%s:%d cxnsleep can't read response", peerName, cxn->ident); } cxnSleepOrDie (cxn) ; } else if (strchr (p, '\n') == NULL) { /* partial read */ expandBuffer (b [0], BUFFER_EXPAND_AMOUNT) ; buffers = makeBufferArray (bufferTakeRef (b [0]), NULL) ; if ( !prepareRead (e, buffers, getAuthUserResponse, cxn, 1) ) { warn ("%s:%d cxnsleep prepare read failed", peerName, cxn->ident) ; freeBufferArray (buffers) ; cxnSleepOrDie (cxn) ; } } else { clearTimer (cxn->readBlockedTimerId) ; if ( !getNntpResponse (p, &code, NULL) ) { warn ("%s:%d cxnsleep response to AUTHINFO USER: %s", peerName, cxn->ident, p) ; cxnSleepOrDie (cxn) ; } else { notice ("%s:%d connected", peerName, cxn->ident) ; switch (code) { case 381: issueAuthPass (e,cxn); break ; default: warn ("%s:%d cxnsleep response to AUTHINFO USER: %s", peerName, cxn->ident, p) ; cxn->authenticated = true; issueModeStream (e,cxn); break ; } } } } /* * */ static void getAuthPassResponse (EndPoint e, IoStatus i, Buffer *b, void *d) { Connection cxn = (Connection) d ; int code ; char *p = bufferBase (b[0]) ; Buffer *buffers ; const char *peerName ; ASSERT (e == cxn->myEp) ; ASSERT (b [0] == cxn->respBuffer) ; ASSERT (b [1] == NULL) ; /* only ever one buffer on this read */ ASSERT (cxn->state == cxnConnectingS) ; VALIDATE_CONNECTION (cxn) ; peerName = hostPeerName (cxn->myHost) ; bufferAddNullByte (b[0]) ; d_printf (1,"%s:%d Processing authinfo pass response: %s", /* no NL */ hostPeerName (cxn->myHost), cxn->ident, p) ; if (i == IoDone && writeIsPending (cxn->myEp)) { /* badness. should never happen */ warn ("%s:%d cxnsleep authinfo command still pending", peerName, cxn->ident) ; cxnSleepOrDie (cxn) ; } else if (i != IoDone) { if (i != IoEOF) { errno = endPointErrno (e) ; syswarn ("%s:%d cxnsleep can't read response", peerName, cxn->ident); } cxnSleepOrDie (cxn) ; } else if (strchr (p, '\n') == NULL) { /* partial read */ expandBuffer (b [0], BUFFER_EXPAND_AMOUNT) ; buffers = makeBufferArray (bufferTakeRef (b [0]), NULL) ; if ( !prepareRead (e, buffers, getAuthPassResponse, cxn, 1) ) { warn ("%s:%d cxnsleep prepare read failed", peerName, cxn->ident) ; freeBufferArray (buffers) ; cxnSleepOrDie (cxn) ; } } else { clearTimer (cxn->readBlockedTimerId) ; if ( !getNntpResponse (p, &code, NULL) ) { warn ("%s:%d cxnsleep response to AUTHINFO PASS: %s", peerName, cxn->ident, p) ; cxnSleepOrDie (cxn) ; } else { switch (code) { case 281: notice ("%s:%d authenticated", peerName, cxn->ident) ; cxn->authenticated = true ; issueModeStream (e,cxn); break ; default: warn ("%s:%d cxnsleep response to AUTHINFO PASS: %s", peerName, cxn->ident, p) ; cxnSleepOrDie (cxn) ; break ; } } } } /* * Process the remote's response to our MODE STREAM command. This is where * the Connection moves into the cxnFeedingS state. If the remote has given * us a good welcome banner, but then immediately dropped the connection, * we'll arrive here with the MODE STREAM command still queued up. */ static void getModeResponse (EndPoint e, IoStatus i, Buffer *b, void *d) { Connection cxn = (Connection) d ; int code ; char *p = bufferBase (b[0]) ; Buffer *buffers ; const char *peerName ; ASSERT (e == cxn->myEp) ; ASSERT (b [0] == cxn->respBuffer) ; ASSERT (b [1] == NULL) ; /* only ever one buffer on this read */ ASSERT (cxn->state == cxnConnectingS) ; VALIDATE_CONNECTION (cxn) ; peerName = hostPeerName (cxn->myHost) ; bufferAddNullByte (b[0]) ; d_printf (1,"%s:%d Processing mode response: %s", /* no NL */ hostPeerName (cxn->myHost), cxn->ident, p) ; if (i == IoDone && writeIsPending (cxn->myEp)) { /* badness. should never happen */ warn ("%s:%d cxnsleep mode stream command still pending", peerName, cxn->ident) ; cxnSleepOrDie (cxn) ; } else if (i != IoDone) { if (i != IoEOF) { errno = endPointErrno (e) ; syswarn ("%s:%d cxnsleep can't read response", peerName, cxn->ident); } cxnSleepOrDie (cxn) ; } else if (strchr (p, '\n') == NULL) { /* partial read */ expandBuffer (b [0], BUFFER_EXPAND_AMOUNT) ; buffers = makeBufferArray (bufferTakeRef (b [0]), NULL) ; if ( !prepareRead (e, buffers, getModeResponse, cxn, 1) ) { warn ("%s:%d cxnsleep prepare read failed", peerName, cxn->ident) ; freeBufferArray (buffers) ; cxnSleepOrDie (cxn) ; } } else { clearTimer (cxn->readBlockedTimerId) ; if ( !getNntpResponse (p, &code, NULL) ) { warn ("%s:%d cxnsleep response to MODE STREAM: %s", peerName, cxn->ident, p) ; cxnSleepOrDie (cxn) ; } else { if (!cxn->authenticated) notice ("%s:%d connected", peerName, cxn->ident) ; switch (code) { case 203: /* will do streaming */ hostRemoteStreams (cxn->myHost, cxn, true) ; if (hostWantsStreaming (cxn->myHost)) { cxn->doesStreaming = true ; cxn->maxCheck = hostMaxChecks (cxn->myHost) ; } else cxn->maxCheck = 1 ; break ; default: /* won't do it */ hostRemoteStreams (cxn->myHost, cxn, false) ; cxn->maxCheck = 1 ; break ; } /* now we consider ourselves completly connected. */ cxn->timeCon = theTime(); cxn->timeCon_checkpoint = theTime(); if (cxn->articleQTotal == 0) cxnIdle (cxn) ; else cxn->state = cxnFeedingS ; /* one for the connection and one for the buffer array */ ASSERT (cxn->authenticated || bufferRefCount (cxn->respBuffer) == 2) ; /* there was only one line in there, right? */ bufferSetDataSize (cxn->respBuffer, 0) ; buffers = makeBufferArray (bufferTakeRef (cxn->respBuffer), NULL) ; /* sleepTimeout get changed at each failed attempt, so reset. */ cxn->sleepTimeout = init_reconnect_period ; if ( !prepareRead (cxn->myEp, buffers, responseIsRead, cxn, 1) ) { freeBufferArray (buffers) ; cxnSleepOrDie (cxn) ; } else { /* now we wait for articles from our Host, or we have some articles already. On infrequently used connections, the network link is torn down and rebuilt as needed. So we may be rebuilding the connection here in which case we have an article to send. */ if (writesNeeded (cxn) || hostGimmeArticle (cxn->myHost,cxn)) doSomeWrites (cxn) ; } } } freeBufferArray (b) ; } /* * called when a response has been read from the socket. This is * where the bulk of the processing starts. */ static void responseIsRead (EndPoint e, IoStatus i, Buffer *b, void *d) { Connection cxn = (Connection) d ; char *response ; char *endr ; char *bufBase ; unsigned int respSize ; int code ; char *rest = NULL ; Buffer buf ; Buffer *bArr ; const char *peerName ; ASSERT (e == cxn->myEp) ; ASSERT (b != NULL) ; ASSERT (b [1] == NULL) ; ASSERT (b [0] == cxn->respBuffer) ; ASSERT (cxn->state == cxnFeedingS || cxn->state == cxnIdleS || cxn->state == cxnClosingS || cxn->state == cxnFlushingS) ; VALIDATE_CONNECTION (cxn) ; bufferAddNullByte (b [0]) ; peerName = hostPeerName (cxn->myHost) ; if (i != IoDone) { /* uh oh. */ if (i != IoEOF) { errno = endPointErrno (e) ; syswarn ("%s:%d cxnsleep can't read response", peerName, cxn->ident); } freeBufferArray (b) ; cxnLogStats (cxn,true) ; if (cxn->state == cxnClosingS) { cxnDead (cxn) ; delConnection (cxn) ; } else cxnSleep (cxn) ; return ; } buf = b [0] ; bufBase = bufferBase (buf) ; /* check that we have (at least) a full line response. If not expand the buffer and resubmit the read. */ if (strchr (bufBase, '\n') == 0) { if (!expandBuffer (buf, BUFFER_EXPAND_AMOUNT)) { warn ("%s:%d cxnsleep can't expand input buffer", peerName, cxn->ident) ; freeBufferArray (b) ; cxnSleepOrDie (cxn) ; } else if ( !prepareRead (cxn->myEp, b, responseIsRead, cxn, 1)) { warn ("%s:%d cxnsleep prepare read failed", peerName, cxn->ident) ; freeBufferArray (b) ; cxnSleepOrDie (cxn) ; } return ; } freeBufferArray (b) ; /* connection still has reference to buffer */ /* * Now process all the full responses that we have. */ response = bufBase ; respSize = bufferDataSize (cxn->respBuffer) ; while ((endr = strchr (response, '\n')) != NULL) { char *next = endr + 1 ; if (*next == '\r') next++ ; endr-- ; if (*endr != '\r') endr++ ; if (next - endr != 2 && !cxn->loggedNoCr) { /* only a newline there. we'll live with it */ warn ("%s:%d remote not giving out CR characters", peerName, cxn->ident) ; cxn->loggedNoCr = true ; } *endr = '\0' ; if ( !getNntpResponse (response, &code, &rest) ) { warn ("%s:%d cxnsleep response format: %s", peerName, cxn->ident, response) ; cxnSleepOrDie (cxn) ; return ; } d_printf (5,"%s:%d Response %d: %s\n", peerName, cxn->ident, code, response) ; /* now handle the response code. I'm not using symbolic names on purpose--the numbers are all you see in the RFC's. */ switch (code) { case 205: /* OK response to QUIT. */ processResponse205 (cxn, response) ; break ; /* These three are from the CHECK command */ case 238: /* no such article found */ /* Do not incrFilter (cxn) now, wait till after subsequent TAKETHIS */ processResponse238 (cxn, response) ; break ; case 431: /* try again later (also for TAKETHIS) */ decrFilter (cxn) ; if (hostDropDeferred (cxn->myHost)) processResponse438 (cxn, response) ; else processResponse431 (cxn, response) ; break ; case 438: /* already have it */ decrFilter (cxn) ; processResponse438 (cxn, response) ; break ; /* These are from the TAKETHIS command */ case 239: /* article transferred OK */ incrFilter (cxn) ; processResponse239 (cxn, response) ; break ; case 439: /* article rejected */ decrFilter (cxn) ; processResponse439 (cxn, response) ; break ; /* These are from the IHAVE command */ case 335: /* send article */ processResponse335 (cxn, response) ; break ; case 435: /* article not wanted */ processResponse435 (cxn, response) ; break ; case 436: /* transfer failed try again later */ if (cxn->takeRespHead == NULL && hostDropDeferred (cxn->myHost)) processResponse435 (cxn, response) ; else processResponse436 (cxn, response) ; break ; case 437: /* article rejected */ processResponse437 (cxn, response) ; break ; case 400: /* has stopped accepting articles */ processResponse400 (cxn, response) ; break ; case 235: /* article transfered OK (IHAVE-body) */ processResponse235 (cxn, response) ; break ; case 480: /* Transfer permission denied. */ processResponse480 (cxn,response) ; break ; case 503: /* remote timeout. */ processResponse503 (cxn,response) ; break ; default: warn ("%s:%d cxnsleep response unknown: %d %s", peerName, cxn->ident, code, response) ; cxnSleepOrDie (cxn) ; break ; } VALIDATE_CONNECTION (cxn) ; if (cxn->state != cxnFeedingS && cxn->state != cxnClosingS && cxn->state != cxnFlushingS && cxn->state != cxnIdleS /* XXX */) break ; /* connection is terminated */ response = next ; } d_printf (5,"%s:%d done with responses\n",hostPeerName (cxn->myHost), cxn->ident) ; switch (cxn->state) { case cxnIdleS: case cxnFeedingS: case cxnClosingS: case cxnFlushingS: /* see if we need to drop in to or out of no-CHECK mode */ if (cxn->state == cxnFeedingS && cxn->doesStreaming) { if ((cxn->filterValue > cxn->onThreshold) && cxn->needsChecks) { cxn->needsChecks = false; hostLogNoCheckMode (cxn->myHost, true, cxn->offThreshold/cxn->lowPassFilter, cxn->filterValue/cxn->lowPassFilter, cxn->onThreshold/cxn->lowPassFilter) ; /* on and log */ } else if ((cxn->filterValue < cxn->offThreshold) && !cxn->needsChecks) { cxn->needsChecks = true; hostLogNoCheckMode (cxn->myHost, false, cxn->offThreshold/cxn->lowPassFilter, cxn->filterValue/cxn->lowPassFilter, cxn->onThreshold/cxn->lowPassFilter) ; /* off and log */ } } /* Now handle possible remaining partial reponse and set up for next read. */ if (*response != '\0') { /* partial response */ unsigned int leftAmt = respSize - (response - bufBase) ; d_printf (2,"%s:%d handling a partial response\n", hostPeerName (cxn->myHost),cxn->ident) ; /* first we shift what's left in the buffer down to the bottom, if needed, or just expand the buffer */ if (response != bufBase) { /* so next read appends */ memmove (bufBase, response, leftAmt) ; bufferSetDataSize (cxn->respBuffer, leftAmt) ; } else if (!expandBuffer (cxn->respBuffer, BUFFER_EXPAND_AMOUNT)) die ("%s:%d cxnsleep can't expand input buffer", peerName, cxn->ident) ; } else bufferSetDataSize (cxn->respBuffer, 0) ; bArr = makeBufferArray (bufferTakeRef (cxn->respBuffer), NULL) ; if ( !prepareRead (e, bArr, responseIsRead, cxn, 1) ) { warn ("%s:%d cxnsleep prepare read failed", peerName, cxn->ident) ; freeBufferArray (bArr) ; cxnWait (cxn) ; return ; } else { /* only setup the timer if we're still waiting for a response to something. There's not necessarily a 1-to-1 mapping between reads and writes in streaming mode. May have been set already above (that would be unlikely I think). */ VALIDATE_CONNECTION (cxn) ; d_printf (5,"%s:%d about to do some writes\n", hostPeerName (cxn->myHost),cxn->ident) ; doSomeWrites (cxn) ; /* If the read timer is (still) running, update it to give those terminally slow hosts that take forever to drain the network buffers and just dribble out responses the benefit of the doubt. XXX - maybe should just increase timeout for these! */ if (cxn->readBlockedTimerId) cxn->readBlockedTimerId = updateSleep (cxn->readBlockedTimerId, responseTimeoutCbk, cxn->readTimeout, cxn) ; } VALIDATE_CONNECTION (cxn) ; break ; case cxnWaitingS: /* presumably after a code 205 or 400 */ case cxnConnectingS: /* presumably after a code 205 or 400 */ case cxnSleepingS: /* probably after a 480 */ break ; case cxnDeadS: delConnection (cxn) ; break ; case cxnStartingS: default: die ("Bad connection state: %s\n",stateToString (cxn->state)) ; } } /* * called when the write of the QUIT command has completed. */ static void quitWritten (EndPoint e, IoStatus i, Buffer *b, void *d) { Connection cxn = (Connection) d ; const char *peerName ; peerName = hostPeerName (cxn->myHost) ; clearTimer (cxn->writeBlockedTimerId) ; ASSERT (cxn->myEp == e) ; VALIDATE_CONNECTION (cxn) ; if (i != IoDone) { errno = endPointErrno (e) ; syswarn ("%s:%d cxnsleep can't write QUIT", peerName, cxn->ident) ; if (cxn->state == cxnClosingS) { cxnDead (cxn) ; delConnection (cxn) ; } else cxnWait (cxn) ; } else /* The QUIT command has been sent, so start the response timer. */ initReadBlockedTimeout (cxn) ; freeBufferArray (b) ; } /* * called when the write of the IHAVE-body data is finished */ static void ihaveBodyDone (EndPoint e, IoStatus i, Buffer *b, void *d) { Connection cxn = (Connection) d ; ASSERT (e == cxn->myEp) ; clearTimer (cxn->writeBlockedTimerId) ; if (i != IoDone) { errno = endPointErrno (e) ; syswarn ("%s:%d cxnsleep can't write IHAVE body", hostPeerName (cxn->myHost), cxn->ident) ; cxnLogStats (cxn,true) ; if (cxn->state == cxnClosingS) { cxnDead (cxn) ; delConnection (cxn) ; } else cxnSleep (cxn) ; } else { /* Some hosts return a response even before we're done sending, so don't go idle until here. */ if (cxn->state == cxnFeedingS && cxn->articleQTotal == 0) cxnIdle (cxn) ; else /* The command set has been sent, so start the response timer. */ initReadBlockedTimeout (cxn) ; } freeBufferArray (b) ; return ; } /* * Called when a command set (IHAVE, CHECK, TAKETHIS) has been * written to the remote. */ static void commandWriteDone (EndPoint e, IoStatus i, Buffer *b, void *d) { Connection cxn = (Connection) d ; const char *peerName ; ASSERT (e == cxn->myEp) ; peerName = hostPeerName (cxn->myHost) ; freeBufferArray (b) ; clearTimer (cxn->writeBlockedTimerId) ; if (i != IoDone) { errno = endPointErrno (e) ; syswarn ("%s:%d cxnsleep can't write command", peerName, cxn->ident) ; cxnLogStats (cxn,true) ; if (cxn->state == cxnClosingS) { cxnDead (cxn) ; delConnection (cxn) ; } else { /* XXX - so cxnSleep() doesn't die in VALIDATE_CONNECTION () */ deferAllArticles (cxn) ; cxnIdle (cxn) ; cxnSleep (cxn) ; } } else { /* Some hosts return a response even before we're done sending, so don't go idle until here */ if (cxn->state == cxnFeedingS && cxn->articleQTotal == 0) cxnIdle (cxn) ; else /* The command set has been sent, so start the response timer. XXX - we'd like finer grained control */ initReadBlockedTimeout (cxn) ; if ( cxn->doesStreaming ) doSomeWrites (cxn) ; /* pump data as fast as possible */ /* XXX - will clear the read timeout */ } } /* * Called when the MODE STREAM command has been written down the pipe. */ static void modeCmdIssued (EndPoint e, IoStatus i, Buffer *b, void *d) { Connection cxn = (Connection) d ; ASSERT (e == cxn->myEp) ; clearTimer (cxn->writeBlockedTimerId) ; /* The mode command has been sent, so start the response timer */ initReadBlockedTimeout (cxn) ; if (i != IoDone) { d_printf (1,"%s:%d MODE STREAM command failed to write\n", hostPeerName (cxn->myHost), cxn->ident) ; syswarn ("%s:%d cxnsleep can't write MODE STREAM", hostPeerName (cxn->myHost), cxn->ident) ; cxnSleepOrDie (cxn) ; } freeBufferArray (b) ; } /* * Called when the AUTHINFO USER command has been written down the pipe. */ static void authUserIssued (EndPoint e, IoStatus i, Buffer *b, void *d) { Connection cxn = (Connection) d ; ASSERT (e == cxn->myEp) ; clearTimer (cxn->writeBlockedTimerId) ; /* The authinfo user command has been sent, so start the response timer */ initReadBlockedTimeout (cxn) ; if (i != IoDone) { d_printf (1,"%s:%d AUTHINFO USER command failed to write\n", hostPeerName (cxn->myHost), cxn->ident) ; syswarn ("%s:%d cxnsleep can't write AUTHINFO USER", hostPeerName (cxn->myHost), cxn->ident) ; cxnSleepOrDie (cxn) ; } freeBufferArray (b) ; } /* * Called when the AUTHINFO USER command has been written down the pipe. */ static void authPassIssued (EndPoint e, IoStatus i, Buffer *b, void *d) { Connection cxn = (Connection) d ; ASSERT (e == cxn->myEp) ; clearTimer (cxn->writeBlockedTimerId) ; /* The authinfo pass command has been sent, so start the response timer */ initReadBlockedTimeout (cxn) ; if (i != IoDone) { d_printf (1,"%s:%d AUTHINFO PASS command failed to write\n", hostPeerName (cxn->myHost), cxn->ident) ; syswarn ("%s:%d cxnsleep can't write AUTHINFO PASS", hostPeerName (cxn->myHost), cxn->ident) ; cxnSleepOrDie (cxn) ; } freeBufferArray (b) ; } /* * Called whenever some amount of data has been written to the pipe but * more data remains to be written */ static void writeProgress (EndPoint e UNUSED, IoStatus i, Buffer *b UNUSED, void *d) { Connection cxn = (Connection) d ; ASSERT (i == IoProgress) ; if (cxn->writeTimeout > 0) cxn->writeBlockedTimerId = updateSleep (cxn->writeBlockedTimerId, writeTimeoutCbk, cxn->writeTimeout, cxn) ; } /* * Timers. */ /* * This is called when the timeout for the reponse from the remote * goes off. We tear down the connection and notify our host. */ static void responseTimeoutCbk (TimeoutId id, void *data) { Connection cxn = (Connection) data ; const char *peerName ; ASSERT (id == cxn->readBlockedTimerId) ; ASSERT (cxn->state == cxnConnectingS || cxn->state == cxnFeedingS || cxn->state == cxnFlushingS || cxn->state == cxnClosingS) ; VALIDATE_CONNECTION (cxn) ; /* XXX - let abortConnection clear readBlockedTimerId, otherwise VALIDATE_CONNECTION() will croak */ peerName = hostPeerName (cxn->myHost) ; warn ("%s:%d cxnsleep non-responsive connection", peerName, cxn->ident) ; d_printf (1,"%s:%d shutting down non-responsive connection\n", hostPeerName (cxn->myHost), cxn->ident) ; cxnLogStats (cxn,true) ; if (cxn->state == cxnClosingS) { abortConnection (cxn) ; delConnection (cxn) ; } else cxnSleep (cxn) ; /* will notify the Host */ } /* * This is called when the data write timeout for the remote * goes off. We tear down the connection and notify our host. */ static void writeTimeoutCbk (TimeoutId id, void *data) { Connection cxn = (Connection) data ; const char *peerName ; ASSERT (id == cxn->writeBlockedTimerId) ; ASSERT (cxn->state == cxnConnectingS || cxn->state == cxnFeedingS || cxn->state == cxnFlushingS || cxn->state == cxnClosingS) ; VALIDATE_CONNECTION (cxn) ; /* XXX - let abortConnection clear writeBlockedTimerId, otherwise VALIDATE_CONNECTION() will croak */ peerName = hostPeerName (cxn->myHost) ; warn ("%s:%d cxnsleep write timeout", peerName, cxn->ident) ; d_printf (1,"%s:%d shutting down non-responsive connection\n", hostPeerName (cxn->myHost), cxn->ident) ; cxnLogStats (cxn,true) ; if (cxn->state == cxnClosingS) { abortConnection (cxn) ; delConnection (cxn) ; } else cxnSleep (cxn) ; /* will notify the Host */ } /* * Called by the EndPoint class when the timer goes off */ static void reopenTimeoutCbk (TimeoutId id, void *data) { Connection cxn = (Connection) data ; ASSERT (id == cxn->sleepTimerId) ; cxn->sleepTimerId = 0 ; if (cxn->state != cxnSleepingS) { warn ("%s:%d cxnsleep connection in bad state: %s", hostPeerName (cxn->myHost), cxn->ident, stateToString (cxn->state)) ; cxnSleepOrDie (cxn) ; } else cxnConnect (cxn) ; } /* * timeout callback to close down long running connection. */ static void flushCxnCbk (TimeoutId id, void *data) { Connection cxn = (Connection) data ; ASSERT (id == cxn->flushTimerId) ; VALIDATE_CONNECTION (cxn) ; cxn->flushTimerId = 0 ; if (!(cxn->state == cxnFeedingS || cxn->state == cxnConnectingS || cxn->state == cxnIdleS)) { warn ("%s:%d cxnsleep connection in bad state: %s", hostPeerName (cxn->myHost), cxn->ident, stateToString (cxn->state)) ; cxnSleepOrDie (cxn) ; } else { d_printf (1,"%s:%d Handling periodic connection close.\n", hostPeerName (cxn->myHost), cxn->ident) ; notice ("%s:%d periodic close", hostPeerName (cxn->myHost), cxn->ident) ; cxnFlush (cxn) ; } } /* * Timer callback for when the connection has not received an * article from INN. When that happens we tear down the network * connection to help recycle fds */ static void articleTimeoutCbk (TimeoutId id, void *data) { Connection cxn = (Connection) data ; const char *peerName = hostPeerName (cxn->myHost) ; ASSERT (cxn->artReceiptTimerId == id) ; VALIDATE_CONNECTION (cxn) ; cxn->artReceiptTimerId = 0 ; if (cxn->state != cxnIdleS) { warn ("%s:%d cxnsleep connection in bad state: %s", hostPeerName (cxn->myHost), cxn->ident, stateToString (cxn->state)) ; cxnSleepOrDie (cxn) ; return ; } /* it's doubtful (right?) that this timer could go off and there'd still be articles in the queue. */ if (cxn->articleQTotal > 0) { warn ("%s:%d idle connection still has articles", peerName, cxn->ident) ; } else { notice ("%s:%d idle tearing down connection", peerName, cxn->ident) ; cxn->state = cxnIdleTimeoutS ; cxnFlush (cxn) ; } } /* * function to be called when the fd is not ready for reading, but there is * an article on tape or in the queue to be done. Things are done this way * so that a Connection doesn't hog time trying to find the next good * article for writing. With a large backlog of expired articles that would * take a long time. Instead the Connection just tries its next article on * tape or queue, and if that's no good then it registers this callback so * that other Connections have a chance of being serviced. * * This function is also put on the callback queue if we called doSomeWrites * but there was already a write pending, mostly so that we don't deadlock the * connection when we got a response to the body of an IHAVE command before we * finished sending the body. */ static void cxnWorkProc (EndPoint ep UNUSED, void *data) { Connection cxn = (Connection) data ; d_printf (2,"%s:%d calling work proc\n", hostPeerName (cxn->myHost),cxn->ident) ; if (writesNeeded (cxn)) doSomeWrites (cxn) ; /* may re-register the work proc... */ else if (cxn->state == cxnFlushingS || cxn->state == cxnClosingS) { if (cxn->articleQTotal == 0) issueQUIT (cxn) ; } else d_printf (2,"%s:%d no writes were needed...\n", hostPeerName (cxn->myHost), cxn->ident) ; } /**************************************************************************** * * END EndPoint callback area. * ****************************************************************************/ /**************************************************************************** * * REPONSE CODE PROCESSING. * ***************************************************************************/ /* * A connection needs to sleep, but if it's closing it needs to die instead. */ static void cxnSleepOrDie (Connection cxn) { if (cxn->state == cxnClosingS) cxnDead (cxn) ; else cxnSleep (cxn) ; } /* * Handle the response 205 to our QUIT command, which means the * remote is going away and we can happily cleanup */ static void processResponse205 (Connection cxn, char *response UNUSED) { bool immedRecon ; VALIDATE_CONNECTION (cxn) ; if (!(cxn->state == cxnFeedingS || cxn->state == cxnIdleS || cxn->state == cxnFlushingS || cxn->state == cxnClosingS)) { warn ("%s:%d cxnsleep connection in bad state: %s", hostPeerName (cxn->myHost), cxn->ident, stateToString (cxn->state)) ; cxnSleepOrDie (cxn) ; return ; } switch (cxn->state) { case cxnFlushingS: case cxnClosingS: ASSERT (cxn->articleQTotal == 0) ; cxnLogStats (cxn,true) ; immedRecon = cxn->immedRecon ; hostCxnDead (cxn->myHost,cxn) ; if (cxn->state == cxnFlushingS && immedRecon) { abortConnection (cxn) ; if (!cxnConnect (cxn)) notice ("%s:%d flush re-connect failed", hostPeerName (cxn->myHost), cxn->ident) ; } else if (cxn->state == cxnFlushingS) cxnWait (cxn) ; else cxnDead (cxn) ; break ; case cxnIdleS: case cxnFeedingS: /* this shouldn't ever happen... */ warn ("%s:%d cxnsleep response unexpected: %d", hostPeerName (cxn->myHost), cxn->ident, 205) ; cxnSleepOrDie (cxn) ; break ; default: die ("Bad connection state: %s\n",stateToString (cxn->state)) ; } } /* * Handle a response code of 238 which is the "no such article" * reply to the CHECK command (i.e. remote wants it). */ static void processResponse238 (Connection cxn, char *response) { char *msgid ; ArtHolder artHolder ; if (!cxn->doesStreaming) { warn ("%s:%d cxnsleep unexpected streaming response for non-streaming" " connection: %s", hostPeerName (cxn->myHost), cxn->ident, response) ; cxnSleepOrDie (cxn) ; return ; } if (!(cxn->state == cxnFlushingS || cxn->state == cxnFeedingS || cxn->state == cxnClosingS)) { warn ("%s:%d cxnsleep connection in bad state: %s", hostPeerName (cxn->myHost), cxn->ident, stateToString (cxn->state)) ; cxnSleepOrDie (cxn) ; return ; } VALIDATE_CONNECTION (cxn) ; msgid = getMsgId (response) ; if (cxn->checkRespHead == NULL) /* peer is confused */ { warn ("%s:%d cxnsleep response unexpected: %d", hostPeerName (cxn->myHost),cxn->ident,238) ; cxnSleepOrDie (cxn) ; } else if (msgid == NULL || strlen (msgid) == 0 || (artHolder = artHolderByMsgId (msgid, cxn->checkRespHead)) == NULL) noSuchMessageId (cxn,238,msgid,response) ; else { /* now remove the article from the check queue and move it onto the transmit queue. Another function wil take care of transmitting */ remArtHolder (artHolder, &cxn->checkRespHead, &cxn->articleQTotal) ; if (cxn->state != cxnClosingS) appendArtHolder (artHolder, &cxn->takeHead, &cxn->articleQTotal) ; else { hostTakeBackArticle (cxn->myHost, cxn, artHolder->article) ; delArtHolder (artHolder) ; } } if (msgid != NULL) free (msgid) ; } /* * process the response "try again later" to the CHECK command If this * returns true then the connection is still usable. */ static void processResponse431 (Connection cxn, char *response) { char *msgid ; ArtHolder artHolder ; if (!cxn->doesStreaming) { warn ("%s:%d cxnsleep unexpected streaming response for non-streaming" " connection: %s", hostPeerName (cxn->myHost), cxn->ident, response) ; cxnSleepOrDie (cxn) ; return ; } if (!(cxn->state == cxnFlushingS || cxn->state == cxnFeedingS || cxn->state == cxnClosingS)) { warn ("%s:%d cxnsleep connection in bad state: %s", hostPeerName (cxn->myHost), cxn->ident, stateToString (cxn->state)) ; cxnSleepOrDie (cxn) ; return ; } VALIDATE_CONNECTION (cxn) ; msgid = getMsgId (response) ; if (cxn->checkRespHead == NULL) /* peer is confused */ { warn ("%s:%d cxnsleep response unexpected: %d", hostPeerName (cxn->myHost),cxn->ident,431) ; cxnSleepOrDie (cxn) ; } else if (msgid == NULL || strlen (msgid) == 0 || (artHolder = artHolderByMsgId (msgid, cxn->checkRespHead)) == NULL) noSuchMessageId (cxn,431,msgid,response) ; else { remArtHolder (artHolder, &cxn->checkRespHead, &cxn->articleQTotal) ; if (cxn->articleQTotal == 0 && !writeIsPending (cxn->myEp)) cxnIdle (cxn) ; hostArticleDeferred (cxn->myHost, cxn, artHolder->article) ; delArtHolder (artHolder) ; } if (msgid != NULL) free (msgid) ; } /* * process the "already have it" response to the CHECK command. If this * returns true then the connection is still usable. */ static void processResponse438 (Connection cxn, char *response) { char *msgid ; ArtHolder artHolder ; if (!cxn->doesStreaming) { warn ("%s:%d cxnsleep unexpected streaming response for non-streaming" " connection: %s", hostPeerName (cxn->myHost), cxn->ident, response) ; cxnSleepOrDie (cxn) ; return ; } if (!(cxn->state == cxnFlushingS || cxn->state == cxnFeedingS || cxn->state == cxnClosingS)) { warn ("%s:%d cxnsleep connection in bad state: %s", hostPeerName (cxn->myHost), cxn->ident, stateToString (cxn->state)) ; cxnSleepOrDie (cxn) ; return ; } VALIDATE_CONNECTION (cxn) ; msgid = getMsgId (response) ; if (cxn->checkRespHead == NULL) /* peer is confused */ { warn ("%s:%d cxnsleep response unexpected: %d", hostPeerName (cxn->myHost),cxn->ident,438) ; cxnSleepOrDie (cxn) ; } else if (msgid == NULL || strlen (msgid) == 0 || (artHolder = artHolderByMsgId (msgid, cxn->checkRespHead)) == NULL) noSuchMessageId (cxn,438,msgid,response) ; else { cxn->checksRefused++ ; remArtHolder (artHolder, &cxn->checkRespHead, &cxn->articleQTotal) ; if (cxn->articleQTotal == 0 && !writeIsPending (cxn->myEp)) cxnIdle (cxn) ; hostArticleNotWanted (cxn->myHost, cxn, artHolder->article); delArtHolder (artHolder) ; } if (msgid != NULL) free (msgid) ; } /* * process the "article transferred ok" response to the TAKETHIS. */ static void processResponse239 (Connection cxn, char *response) { char *msgid ; ArtHolder artHolder ; if (!cxn->doesStreaming) { warn ("%s:%d cxnsleep unexpected streaming response for non-streaming" " connection: %s", hostPeerName (cxn->myHost), cxn->ident, response) ; cxnSleepOrDie (cxn) ; return ; } if (!(cxn->state == cxnFlushingS || cxn->state == cxnFeedingS || cxn->state == cxnClosingS)) { warn ("%s:%d cxnsleep connection in bad state: %s", hostPeerName (cxn->myHost), cxn->ident, stateToString (cxn->state)) ; cxnSleepOrDie (cxn) ; return ; } VALIDATE_CONNECTION (cxn) ; msgid = getMsgId (response) ; if (cxn->takeRespHead == NULL) /* peer is confused */ { warn ("%s:%d cxnsleep response unexpected: %d", hostPeerName (cxn->myHost),cxn->ident,239) ; cxnSleepOrDie (cxn) ; } else if (msgid == NULL || strlen (msgid) == 0 || (artHolder = artHolderByMsgId (msgid, cxn->takeRespHead)) == NULL) noSuchMessageId (cxn,239,msgid,response) ; else { cxn->takesOkayed++ ; cxn->takesSizeOkayed += artSize(artHolder->article); remArtHolder (artHolder, &cxn->takeRespHead, &cxn->articleQTotal) ; if (cxn->articleQTotal == 0 && !writeIsPending (cxn->myEp)) cxnIdle (cxn) ; hostArticleAccepted (cxn->myHost, cxn, artHolder->article) ; delArtHolder (artHolder) ; } if (msgid != NULL) free (msgid) ; } /* * Set the thresholds for no-CHECK mode; negative means leave existing value */ void cxnSetCheckThresholds (Connection cxn, double lowFilter, double highFilter, double lowPassFilter) { /* Adjust current value for new scaling */ if (cxn->lowPassFilter > 0.0) cxn->filterValue = cxn->filterValue / cxn->lowPassFilter * lowPassFilter; /* Stick in new values */ if (highFilter >= 0) cxn->onThreshold = highFilter * lowPassFilter / 100.0; if (lowFilter >= 0) cxn->offThreshold = lowFilter * lowPassFilter / 100.0; cxn->lowPassFilter = lowPassFilter; } /* * Blow away the connection gracelessly and immedately clean up */ void cxnNuke (Connection cxn) { abortConnection (cxn) ; hostCxnDead (cxn->myHost,cxn) ; delConnection(cxn) ; } /* * process a "article rejected do not try again" response to the * TAKETHIS. */ static void processResponse439 (Connection cxn, char *response) { char *msgid ; ArtHolder artHolder ; if (!cxn->doesStreaming) { warn ("%s:%d cxnsleep unexpected streaming response for non-streaming" " connection: %s", hostPeerName (cxn->myHost), cxn->ident, response) ; cxnSleepOrDie (cxn) ; return ; } if (!(cxn->state == cxnFlushingS || cxn->state == cxnFeedingS || cxn->state == cxnClosingS)) { warn ("%s:%d cxnsleep connection in bad state: %s", hostPeerName (cxn->myHost), cxn->ident, stateToString (cxn->state)) ; cxnSleepOrDie (cxn) ; return ; } VALIDATE_CONNECTION (cxn) ; msgid = getMsgId (response) ; if (cxn->takeRespHead == NULL) /* peer is confused */ { /* NNTPRelay return 439 for check if messid is bad */ if (cxn->checkRespHead == NULL) /* peer is confused */ { warn ("%s:%d cxnsleep response unexpected: %d", hostPeerName (cxn->myHost),cxn->ident,439) ; cxnSleepOrDie (cxn) ; } else { if ((artHolder = artHolderByMsgId (msgid, cxn->checkRespHead)) == NULL) noSuchMessageId (cxn,439,msgid,response) ; else { cxn->checksRefused++ ; remArtHolder (artHolder, &cxn->checkRespHead, &cxn->articleQTotal) ; if (cxn->articleQTotal == 0 && !writeIsPending (cxn->myEp)) cxnIdle (cxn) ; hostArticleNotWanted (cxn->myHost, cxn, artHolder->article); delArtHolder (artHolder) ; } } } else if (msgid == NULL || strlen (msgid) == 0 || (artHolder = artHolderByMsgId (msgid, cxn->takeRespHead)) == NULL) noSuchMessageId (cxn,439,msgid,response) ; else { cxn->takesRejected++ ; cxn->takesSizeRejected += artSize(artHolder->article); remArtHolder (artHolder, &cxn->takeRespHead, &cxn->articleQTotal) ; /* Some hosts return the 439 response even before we're done sending */ if (cxn->articleQTotal == 0 && !writeIsPending(cxn->myEp)) cxnIdle (cxn) ; hostArticleRejected (cxn->myHost, cxn, artHolder->article) ; delArtHolder (artHolder) ; } if (msgid != NULL) free (msgid) ; } /* * process the "article transferred ok" response to the IHAVE-body. */ static void processResponse235 (Connection cxn, char *response UNUSED) { ArtHolder artHolder ; if (cxn->doesStreaming) { warn ("%s:%d cxnsleep unexpected non-streaming response for" " streaming connection: %s", hostPeerName (cxn->myHost), cxn->ident,response) ; cxnSleepOrDie (cxn) ; return ; } if (!(cxn->state == cxnFlushingS || cxn->state == cxnFeedingS || cxn->state == cxnClosingS)) { warn ("%s:%d cxnsleep connection in bad state: %s", hostPeerName (cxn->myHost), cxn->ident, stateToString (cxn->state)) ; cxnSleepOrDie (cxn) ; return ; } ASSERT (cxn->articleQTotal == 1) ; ASSERT (cxn->takeRespHead != NULL) ; VALIDATE_CONNECTION (cxn) ; if (cxn->takeRespHead == NULL) /* peer is confused */ { warn ("%s:%d cxnsleep response unexpected: %d", hostPeerName (cxn->myHost),cxn->ident,235) ; cxnSleepOrDie (cxn) ; } else { /* now remove the article from the queue and tell the Host to forget about it. */ artHolder = cxn->takeRespHead ; cxn->takeRespHead = NULL ; cxn->articleQTotal = 0 ; cxn->takesOkayed++ ; cxn->takesSizeOkayed += artSize(artHolder->article); if (cxn->articleQTotal == 0 && !writeIsPending (cxn->myEp)) cxnIdle (cxn) ; hostArticleAccepted (cxn->myHost, cxn, artHolder->article) ; delArtHolder (artHolder) ; } } /* * process the "send article to be transfered" reponse to the IHAVE. */ static void processResponse335 (Connection cxn, char *response UNUSED) { if (cxn->doesStreaming) { warn ("%s:%d cxnsleep unexpected non-streaming response for" " streaming connection: %s", hostPeerName (cxn->myHost), cxn->ident,response) ; cxnSleepOrDie (cxn) ; return ; } if (!(cxn->state == cxnFlushingS || cxn->state == cxnFeedingS || cxn->state == cxnClosingS)) { warn ("%s:%d cxnsleep connection in bad state: %s", hostPeerName (cxn->myHost), cxn->ident, stateToString (cxn->state)) ; cxnSleepOrDie (cxn) ; return ; } if (cxn->checkRespHead == NULL) { warn ("%s:%d cxnsleep response unexpected: %d", hostPeerName (cxn->myHost),cxn->ident,335) ; cxnSleepOrDie (cxn) ; } else { VALIDATE_CONNECTION (cxn) ; /* now move the article into the third queue */ cxn->takeHead = cxn->checkRespHead ; cxn->checkRespHead = NULL ; issueIHAVEBody (cxn) ; } } /* * process the "not accepting articles" response. This could be to any of * the IHAVE/CHECK/TAKETHIS command, but not the banner--that's handled * elsewhere. */ static void processResponse400 (Connection cxn, char *response) { if (!(cxn->state == cxnFlushingS || cxn->state == cxnFeedingS || cxn->state == cxnIdleS || cxn->state == cxnClosingS)) { warn ("%s:%d cxnsleep connection in bad state: %s", hostPeerName (cxn->myHost), cxn->ident, stateToString (cxn->state)) ; cxnSleepOrDie (cxn) ; return ; } VALIDATE_CONNECTION (cxn) ; /* We may get a response 400 multiple times when in streaming mode. */ notice ("%s:%d remote cannot accept articles: %s", hostPeerName(cxn->myHost), cxn->ident, response) ; /* right here there may still be data queued to write and so we'll fail trying to issue the quit ('cause a write will be pending). Furthermore, the data pending may be half way through an command, and so just tossing the buffer is nt sufficient. But figuring out where we are and doing a tidy job is hard */ if (writeIsPending (cxn->myEp)) cxnSleepOrDie (cxn) ; else { if (cxn->articleQTotal > 0) { /* Defer the articles here so that cxnFlush() doesn't set up an immediate reconnect. */ deferAllArticles (cxn) ; clearTimer (cxn->readBlockedTimerId) ; /* XXX - so cxnSleep() doesn't die when it validates the connection */ cxnIdle (cxn) ; } /* XXX - it would be nice if we QUIT first, but we'd have to go into a state where we just search for the 205 response, and only go into the sleep state at that point */ cxnSleepOrDie (cxn) ; } } /* * process the "not wanted" reponse to the IHAVE. */ static void processResponse435 (Connection cxn, char *response UNUSED) { ArtHolder artHolder ; if (cxn->doesStreaming) { warn ("%s:%d cxnsleep unexpected non-streaming response for" " streaming connection: %s", hostPeerName (cxn->myHost), cxn->ident,response) ; cxnSleepOrDie (cxn) ; return ; } if (!(cxn->state == cxnFlushingS || cxn->state == cxnFeedingS || cxn->state == cxnClosingS)) { warn ("%s:%d cxnsleep connection in bad state: %s", hostPeerName (cxn->myHost), cxn->ident, stateToString (cxn->state)) ; cxnSleepOrDie (cxn) ; return ; } /* Some servers, such as early versions of Diablo, had a bug where they'd respond with a 435 code (which should only be used for refusing an article before it was offered) after an article has been sent. */ if (cxn->checkRespHead == NULL) { warn ("%s:%d cxnsleep response unexpected: %d", hostPeerName (cxn->myHost), cxn->ident, 435) ; cxnSleepOrDie (cxn) ; return ; } ASSERT (cxn->articleQTotal == 1) ; VALIDATE_CONNECTION (cxn) ; cxn->articleQTotal-- ; cxn->checksRefused++ ; artHolder = cxn->checkRespHead ; cxn->checkRespHead = NULL ; if (cxn->articleQTotal == 0 && !writeIsPending(cxn->myEp)) cxnIdle (cxn) ; hostArticleNotWanted (cxn->myHost, cxn, artHolder->article) ; delArtHolder (artHolder) ; #if 0 d_printf (1,"%s:%d On exiting 435 article queue total is %d (%d %d %d %d)\n", hostPeerName (cxn->myHost), cxn->ident, cxn->articleQTotal, (int) (cxn->checkHead != NULL), (int) (cxn->checkRespHead != NULL), (int) (cxn->takeHead != NULL), (int) (cxn->takeRespHead != NULL)); #endif } /* * process the "transfer failed" response to the IHAVE-body, (seems this * can come from the IHAVE too). */ static void processResponse436 (Connection cxn, char *response UNUSED) { ArtHolder artHolder ; if (cxn->doesStreaming) { warn ("%s:%d cxnsleep unexpected non-streaming response for" " streaming connection: %s", hostPeerName (cxn->myHost), cxn->ident,response) ; cxnSleepOrDie (cxn) ; return ; } if (!(cxn->state == cxnFlushingS || cxn->state == cxnFeedingS || cxn->state == cxnClosingS)) { warn ("%s:%d cxnsleep connection in bad state: %s", hostPeerName (cxn->myHost), cxn->ident, stateToString (cxn->state)) ; cxnSleepOrDie (cxn) ; return ; } ASSERT (cxn->articleQTotal == 1) ; ASSERT (cxn->takeRespHead != NULL || cxn->checkRespHead != NULL) ; VALIDATE_CONNECTION (cxn) ; cxn->articleQTotal-- ; if (cxn->takeRespHead != NULL) /* IHAVE-body command barfed */ { artHolder = cxn->takeRespHead ; cxn->takeRespHead = NULL ; } else /* IHAVE command barfed */ { artHolder = cxn->checkRespHead ; cxn->checkRespHead = NULL ; } if (cxn->articleQTotal == 0 && !writeIsPending(cxn->myEp)) cxnIdle (cxn) ; hostArticleDeferred (cxn->myHost, cxn, artHolder->article) ; delArtHolder (artHolder) ; } /* * Process the "article rejected do not try again" response to the * IHAVE-body. */ static void processResponse437 (Connection cxn, char *response UNUSED) { ArtHolder artHolder ; if (cxn->doesStreaming) { warn ("%s:%d cxnsleep unexpected non-streaming response for" " streaming connection: %s", hostPeerName (cxn->myHost), cxn->ident,response) ; cxnSleepOrDie (cxn) ; return ; } if (!(cxn->state == cxnFlushingS || cxn->state == cxnFeedingS || cxn->state == cxnClosingS)) { warn ("%s:%d cxnsleep connection in bad state: %s", hostPeerName (cxn->myHost), cxn->ident, stateToString (cxn->state)) ; cxnSleepOrDie (cxn) ; return ; } ASSERT (cxn->articleQTotal == 1) ; ASSERT (cxn->takeRespHead != NULL) ; VALIDATE_CONNECTION (cxn) ; cxn->articleQTotal-- ; cxn->takesRejected++ ; artHolder = cxn->takeRespHead ; cxn->takeRespHead = NULL ; cxn->takesSizeRejected += artSize(artHolder->article); /* Some servers return the 437 response before we're done sending. */ if (cxn->articleQTotal == 0 && !writeIsPending (cxn->myEp)) cxnIdle (cxn) ; hostArticleRejected (cxn->myHost, cxn, artHolder->article) ; delArtHolder (artHolder) ; } /* Process the response 480 Transfer permission defined. We're probably talking to a remote nnrpd on a system that forgot to put us in the hosts.nntp */ static void processResponse480 (Connection cxn, char *response UNUSED) { if (cxn->doesStreaming) { warn ("%s:%d cxnsleep unexpected non-streaming response for" " streaming connection: %s", hostPeerName (cxn->myHost), cxn->ident,response) ; cxnSleepOrDie (cxn) ; return ; } if (!(cxn->state == cxnFlushingS || cxn->state == cxnFeedingS || cxn->state == cxnClosingS)) { warn ("%s:%d cxnsleep connection in bad state: %s", hostPeerName (cxn->myHost), cxn->ident, stateToString (cxn->state)) ; cxnSleepOrDie (cxn) ; return ; } VALIDATE_CONNECTION (cxn) ; warn ("%s:%d cxnsleep transfer permission denied", hostPeerName (cxn->myHost), cxn->ident) ; if (cxn->state == cxnClosingS) cxnDead (cxn) ; else cxnSleep (cxn) ; } /* * Handle the response 503, which means the timeout of nnrpd. */ static void processResponse503 (Connection cxn, char *response UNUSED) { bool immedRecon ; VALIDATE_CONNECTION (cxn) ; if (!(cxn->state == cxnFeedingS || cxn->state == cxnIdleS || cxn->state == cxnFlushingS || cxn->state == cxnClosingS)) { warn ("%s:%d cxnsleep connection in bad state: %s", hostPeerName (cxn->myHost), cxn->ident, stateToString (cxn->state)) ; cxnSleepOrDie (cxn) ; return ; } if (cxn->articleQTotal != 0) notice ("%s:%d flush re-connect failed", hostPeerName (cxn->myHost), cxn->ident) ; cxnLogStats (cxn,true) ; immedRecon = cxn->immedRecon ; hostCxnDead (cxn->myHost,cxn) ; if (cxn->state == cxnFlushingS && immedRecon) { abortConnection (cxn) ; if (!cxnConnect (cxn)) notice ("%s:%d flush re-connect failed", hostPeerName (cxn->myHost), cxn->ident) ; } else if (cxn->state == cxnFlushingS) cxnWait (cxn) ; else cxnDead (cxn) ; } /**************************************************************************** * * END REPONSE CODE PROCESSING. * ***************************************************************************/ /* * puts the Connection into the sleep state. */ static void cxnSleep (Connection cxn) { ASSERT (cxn != NULL) ; ASSERT (cxn->state == cxnFlushingS || cxn->state == cxnIdleS || cxn->state == cxnFeedingS || cxn->state == cxnConnectingS) ; VALIDATE_CONNECTION (cxn) ; abortConnection (cxn) ; prepareReopenCbk (cxn) ; /* XXX - we don't want to reopen if idle */ cxn->state = cxnSleepingS ; /* tell our Host we're asleep so it doesn't try to give us articles */ hostCxnSleeping (cxn->myHost,cxn) ; } static void cxnDead (Connection cxn) { ASSERT (cxn != NULL) ; VALIDATE_CONNECTION (cxn) ; abortConnection (cxn) ; cxn->state = cxnDeadS ; } /* * Sets the idle timer. If no articles arrive before the timer expires, the * connection will be closed. */ static void cxnIdle (Connection cxn) { ASSERT (cxn != NULL) ; ASSERT (cxn->state == cxnFeedingS || cxn->state == cxnConnectingS || cxn->state == cxnFlushingS || cxn->state == cxnClosingS) ; ASSERT (cxn->articleQTotal == 0) ; ASSERT (cxn->writeBlockedTimerId == 0) ; ASSERT (!writeIsPending (cxn->myEp)) ; ASSERT (cxn->sleepTimerId == 0) ; if (cxn->state == cxnFeedingS || cxn->state == cxnConnectingS) { if (cxn->articleReceiptTimeout > 0) { clearTimer (cxn->artReceiptTimerId) ; cxn->artReceiptTimerId = prepareSleep (articleTimeoutCbk, cxn->articleReceiptTimeout, cxn) ; } if (cxn->readTimeout > 0 && cxn->state == cxnFeedingS) clearTimer (cxn->readBlockedTimerId) ; cxn->state = cxnIdleS ; ASSERT (cxn->readBlockedTimerId == 0) ; } } /* * Called when a response from the remote refers to a non-existant * message-id. The network connection is aborted and the Connection * object goes into sleep mode. */ static void noSuchMessageId (Connection cxn, unsigned int responseCode, const char *msgid, const char *response) { const char *peerName = hostPeerName (cxn->myHost) ; if (msgid == NULL || strlen (msgid) == 0) warn ("%s:%d cxnsleep message-id missing in reponse code %d: %s", peerName, cxn->ident, responseCode, response) ; else warn ("%s:%d cxnsleep message-id invalid message-id in reponse code" " %d: %s", peerName, cxn->ident, responseCode, msgid) ; cxnLogStats (cxn,true) ; if (cxn->state != cxnClosingS) cxnSleep (cxn) ; else cxnDead (cxn) ; } /* * a processing error has occured (for example in parsing a response), or * we're at the end of the FSM and we're cleaning up. */ static void abortConnection (Connection cxn) { ASSERT (cxn != NULL) ; VALIDATE_CONNECTION (cxn) ; /* cxn->myEp could be NULL if we get here during cxnConnect (via cxnWait()) */ if (cxn->myEp != NULL) { delEndPoint (cxn->myEp) ; cxn->myEp = NULL ; } clearTimer (cxn->sleepTimerId) ; clearTimer (cxn->artReceiptTimerId) ; clearTimer (cxn->readBlockedTimerId) ; clearTimer (cxn->writeBlockedTimerId) ; clearTimer (cxn->flushTimerId) ; deferAllArticles (cxn) ; /* give any articles back to Host */ bufferSetDataSize (cxn->respBuffer,0) ; resetConnection (cxn) ; if (cxn->state == cxnFeedingS || cxn->state == cxnIdleS || cxn->state == cxnFlushingS || cxn->state == cxnClosingS) hostCxnDead (cxn->myHost,cxn) ; } /* * Set up the callback used when the Connection is sleeping (i.e. will try * to reopen the connection). */ static void prepareReopenCbk (Connection cxn) { ASSERT (cxn->sleepTimerId == 0) ; if (!(cxn->state == cxnConnectingS || cxn->state == cxnIdleS || cxn->state == cxnFeedingS || cxn->state == cxnFlushingS || cxn->state == cxnStartingS)) { warn ("%s:%d cxnsleep connection in bad state: %s", hostPeerName (cxn->myHost), cxn->ident, stateToString (cxn->state)) ; cxnSleepOrDie (cxn) ; return ; } d_printf (1,"%s:%d Setting up a reopen callback\n", hostPeerName (cxn->myHost), cxn->ident) ; cxn->sleepTimerId = prepareSleep (reopenTimeoutCbk, cxn->sleepTimeout, cxn) ; /* bump the sleep timer amount each time to wait longer and longer. Gets reset in resetConnection() */ cxn->sleepTimeout *= 2 ; if (cxn->sleepTimeout > max_reconnect_period) cxn->sleepTimeout = max_reconnect_period ; } /* * (re)set all state variables to inital condition. */ static void resetConnection (Connection cxn) { ASSERT (cxn != NULL) ; bufferSetDataSize (cxn->respBuffer,0) ; cxn->loggedNoCr = false ; cxn->maxCheck = 1 ; cxn->immedRecon = false ; cxn->doesStreaming = false ; /* who knows, next time around maybe... */ cxn->authenticated = false ; cxn->quitWasIssued = false ; cxn->needsChecks = true ; cxn->timeCon = 0 ; cxn->artsTaken = 0 ; cxn->checksIssued = 0 ; cxn->checksRefused = 0 ; cxn->takesRejected = 0 ; cxn->takesOkayed = 0 ; cxn->takesSizeRejected = 0 ; cxn->takesSizeOkayed = 0 ; cxn->timeCon_checkpoint = 0; cxn->checksIssued_checkpoint = 0; cxn->checksRefused_checkpoint = 0; cxn->takesRejected_checkpoint = 0; cxn->takesOkayed_checkpoint = 0; cxn->takesSizeRejected_checkpoint = 0; cxn->takesSizeOkayed_checkpoint = 0; cxn->filterValue = 0.0 ; } /* * Give back all articles that are queued, but not actually in progress. * XXX merge come of this with deferAllArticles */ static void deferQueuedArticles (Connection cxn) { ArtHolder p, q ; for (q = NULL, p = cxn->checkHead ; p != NULL ; p = q) { q = p->next ; hostTakeBackArticle (cxn->myHost, cxn, p->article) ; delArtHolder (p) ; cxn->articleQTotal-- ; } cxn->checkHead = NULL ; for (q = NULL, p = cxn->takeHead ; cxn->doesStreaming && p != NULL ; p = q) { q = p->next ; hostTakeBackArticle (cxn->myHost, cxn, p->article) ; delArtHolder (p) ; cxn->articleQTotal-- ; } cxn->takeHead = NULL ; } /* * Give back any articles we have to the Host for later retrys. */ static void deferAllArticles (Connection cxn) { ArtHolder p, q ; for (q = NULL, p = cxn->checkHead ; p != NULL ; p = q) { q = p->next ; hostTakeBackArticle (cxn->myHost, cxn, p->article) ; delArtHolder (p) ; cxn->articleQTotal-- ; } cxn->checkHead = NULL ; for (q = NULL, p = cxn->checkRespHead ; p != NULL ; p = q) { q = p->next ; hostTakeBackArticle (cxn->myHost, cxn, p->article) ; delArtHolder (p) ; cxn->articleQTotal-- ; } cxn->checkRespHead = NULL ; for (q = NULL, p = cxn->takeHead ; p != NULL ; p = q) { q = p->next ; hostTakeBackArticle (cxn->myHost, cxn, p->article) ; delArtHolder (p) ; cxn->articleQTotal-- ; } cxn->takeHead = NULL ; for (q = NULL, p = cxn->takeRespHead ; p != NULL ; p = q) { q = p->next ; hostTakeBackArticle (cxn->myHost, cxn, p->article) ; delArtHolder (p) ; cxn->articleQTotal-- ; } cxn->takeRespHead = NULL ; ASSERT (cxn->articleQTotal == 0) ; } /* * Called when there's an article to be pushed out to the remote. Even if * the Connection has an article it's possible that nothing will be written * (e.g. if the article on the queue doesn't exist any more) */ static void doSomeWrites (Connection cxn) { bool doneSome = false ; /* If there's a write pending we can't do anything now. * No addWorkCallback here (otherwise innfeed consumes too much CPU). */ if ( writeIsPending (cxn->myEp) ) { return ; } else if ( writesNeeded (cxn) ) /* something on a queue. */ { if (cxn->doesStreaming) doneSome = issueStreamingCommands (cxn) ; else doneSome = issueIHAVE (cxn) ; /* doneSome will be false if article(s) were gone, but if the Host has something available, then it would have been put on the queue for next time around. */ if (!doneSome) { if (writesNeeded (cxn)) /* Host gave us something */ addWorkCallback (cxn->myEp,cxnWorkProc,cxn) ; /* for next time. */ else if (cxn->articleQTotal == 0) { /* if we were in cxnFeedingS, then issueStreamingCommands already called cxnIdle(). */ if (cxn->state == cxnClosingS || cxn->state == cxnFlushingS) issueQUIT (cxn) ; /* and nothing to wait for... */ } } } else if (cxn->state == cxnClosingS || cxn->state == cxnFlushingS) { /* nothing to do... */ if (cxn->articleQTotal == 0) issueQUIT (cxn) ; /* and nothing to wait for before closing */ } } /* Queue up a buffer with the IHAVE command in it for the article at * the head of the transmisson queue. * * If the article is missing, then the Host will be notified and * another article may be put on the Connections queue. This new * article is ignored for now, but a work callback is registered so * that it can be looked at later. */ static bool issueIHAVE (Connection cxn) { Buffer ihaveBuff, *writeArr ; ArtHolder artH ; Article article ; const char *msgid ; char *p ; unsigned int tmp ; size_t bufLen = 256 ; bool rval = false ; ASSERT (!cxn->doesStreaming) ; ASSERT (cxn->state == cxnFlushingS || cxn->state == cxnFeedingS || cxn->state == cxnClosingS) ; ASSERT (cxn->articleQTotal == 1) ; ASSERT (cxn->checkHead != NULL) ; ASSERT (writeIsPending (cxn->myEp) == false) ; VALIDATE_CONNECTION (cxn) ; artH = cxn->checkHead ; article = cxn->checkHead->article ; msgid = artMsgId (artH->article) ; ASSERT (msgid != NULL) ; ASSERT (article != NULL) ; if ((tmp = (strlen (msgid) + 10)) > bufLen) bufLen = tmp ; ihaveBuff = newBuffer (bufLen) ; ASSERT (ihaveBuff != NULL) ; p = bufferBase (ihaveBuff) ; sprintf (p, "IHAVE %s\r\n", msgid) ; bufferSetDataSize (ihaveBuff, strlen (p)) ; d_printf (5,"%s:%d Command IHAVE %s\n", hostPeerName (cxn->myHost),cxn->ident,msgid) ; writeArr = makeBufferArray (ihaveBuff, NULL) ; if ( !prepareWriteWithTimeout (cxn->myEp, writeArr, commandWriteDone, cxn) ) { die ("%s:%d fatal prepare write for IHAVE failed", hostPeerName (cxn->myHost), cxn->ident) ; } /* now move the article to the second queue */ cxn->checkRespHead = cxn->checkHead ; cxn->checkHead = NULL ; cxn->checksIssued++ ; hostArticleOffered (cxn->myHost, cxn) ; rval = true ; return rval ; } /* * Do a prepare write with the article body as the body portion of the * IHAVE command */ static void issueIHAVEBody (Connection cxn) { Buffer *writeArray ; Article article ; ASSERT (cxn != NULL) ; ASSERT (!cxn->doesStreaming) ; ASSERT (cxn->state == cxnFlushingS || cxn->state == cxnFeedingS || cxn->state == cxnClosingS) ; ASSERT (cxn->articleQTotal == 1) ; ASSERT (cxn->takeHead != NULL) ; VALIDATE_CONNECTION (cxn) ; article = cxn->takeHead->article ; ASSERT (article != NULL) ; if (cxn->state != cxnClosingS) writeArray = artGetNntpBuffers (article) ; else writeArray = NULL ; if (writeArray == NULL) { /* missing article (expired for example) will get us here. */ if (dotBuffer == NULL) { dotBuffer = newBufferByCharP (".\r\n",3,3) ; dotFirstBuffer = newBufferByCharP ("\r\n.",3,3) ; crlfBuffer = newBufferByCharP ("\r\n",2,2) ; } /* we'll just write the empty buffer and the remote will complain with 437 */ writeArray = makeBufferArray (bufferTakeRef (dotBuffer),NULL) ; } if ( !prepareWriteWithTimeout (cxn->myEp, writeArray, ihaveBodyDone, cxn) ) { die ("%s:%d fatal prepare write failed in issueIHAVEBody", hostPeerName (cxn->myHost), cxn->ident) ; } else { d_printf (5,"%s:%d prepared write for IHAVE body.\n", hostPeerName (cxn->myHost),cxn->ident) ; } /* now move the article to the last queue */ cxn->takeRespHead = cxn->takeHead ; cxn->takeHead = NULL ; return ; } /* Process the two command queues. Slaps all the CHECKs together and * then does the TAKETHIS commands. * * If no articles on the queue(s) are valid, then the Host is * notified. It may queue up new articles on the Connection, but * these are ignored for now. A work proc is registered so the * articles can be processed later. */ static bool issueStreamingCommands (Connection cxn) { Buffer checkBuffer = NULL ; /* the buffer with the CHECK commands in it. */ Buffer *writeArray = NULL ; ArtHolder p, q ; bool rval = false ; ASSERT (cxn != NULL) ; ASSERT (cxn->myEp != NULL) ; ASSERT (cxn->doesStreaming) ; VALIDATE_CONNECTION (cxn) ; checkBuffer = buildCheckBuffer (cxn) ; /* may be null if none to issue */ if (checkBuffer != NULL) { /* Now shift the articles to their new queue. */ for (p = cxn->checkRespHead ; p != NULL && p->next != NULL ; p = p->next) /* nada--finding end of queue*/ ; if (p == NULL) cxn->checkRespHead = cxn->checkHead ; else p->next = cxn->checkHead ; cxn->checkHead = NULL ; } writeArray = buildTakethisBuffers (cxn,checkBuffer) ; /* may be null */ /* If not null, then writeArray will have checkBuffer (if it wasn't NULL) in the first spot and the takethis buffers after that. */ if (writeArray) { if ( !prepareWriteWithTimeout (cxn->myEp, writeArray, commandWriteDone, cxn) ) { die ("%s:%d fatal prepare write for STREAMING commands failed", hostPeerName (cxn->myHost), cxn->ident) ; } rval = true ; /* now shift articles over to their new queue. */ for (p = cxn->takeRespHead ; p != NULL && p->next != NULL ; p = p->next) /* nada--finding end of queue */ ; if (p == NULL) cxn->takeRespHead = cxn->takeHead ; else p->next = cxn->takeHead ; cxn->takeHead = NULL ; } /* we defer the missing article notification to here because if there was a big backlog of missing articles *and* we're running in no-CHECK mode, then the Host would be putting bad articles on the queue we're taking them off of. */ if (cxn->missing && cxn->articleQTotal == 0 && !writeIsPending (cxn->myEp)) cxnIdle (cxn) ; for (p = cxn->missing ; p != NULL ; p = q) { hostArticleIsMissing (cxn->myHost, cxn, p->article) ; q = p->next ; delArtHolder (p) ; } cxn->missing = NULL ; return rval ; } /* * build up the buffer of all the CHECK commands. */ static Buffer buildCheckBuffer (Connection cxn) { ArtHolder p ; size_t lenBuff = 0 ; Buffer checkBuffer = NULL ; const char *peerName = hostPeerName (cxn->myHost) ; p = cxn->checkHead ; while (p != NULL) { Article article = p->article ; const char *msgid ; msgid = artMsgId (article) ; lenBuff += (8 + strlen (msgid)) ; /* 8 == strlen("CHECK \r\n") */ p = p->next ; } if (lenBuff > 0) lenBuff++ ; /* for the null byte */ /* now build up the single buffer that contains all the CHECK commands */ if (lenBuff > 0) { char *t ; size_t tlen = 0 ; checkBuffer = newBuffer (lenBuff) ; t = bufferBase (checkBuffer) ; p = cxn->checkHead ; while (p != NULL) { const char *msgid = artMsgId (p->article) ; sprintf (t,"CHECK %s\r\n", msgid) ; d_printf (5,"%s:%d Command %s\n", peerName, cxn->ident, t) ; tlen += strlen (t) ; while ( *t ) t++ ; cxn->checksIssued++ ; hostArticleOffered (cxn->myHost,cxn) ; p = p->next ; } ASSERT (tlen + 1 == lenBuff) ; bufferSetDataSize (checkBuffer, tlen) ; } return checkBuffer ; } /* * Construct and array of TAKETHIS commands and the command bodies. Any * articles on the queue that are missing will be removed and the Host will * be informed. */ static Buffer *buildTakethisBuffers (Connection cxn, Buffer checkBuffer) { size_t lenArray = 0 ; ArtHolder p, q ; Buffer *rval = NULL ; const char *peerName = hostPeerName (cxn->myHost) ; if (checkBuffer != NULL) lenArray++ ; if (cxn->takeHead != NULL) /* some TAKETHIS commands to be done. */ { Buffer takeBuffer ; unsigned int takeBuffLen ; unsigned int writeIdx = 0 ; /* count up all the buffers we'll be writing. One extra each time for the TAKETHIS command buffer*/ for (p = cxn->takeHead ; p != NULL ; p = p->next) if (artContentsOk (p->article)) lenArray += (1 + artNntpBufferCount (p->article)) ; /* now allocate the array for the buffers and put them all in it */ /* 1 for the terminator */ rval = xmalloc (sizeof(Buffer) * (lenArray + 1)) ; if (checkBuffer != NULL) rval [writeIdx++] = checkBuffer ; q = NULL ; p = cxn->takeHead ; while (p != NULL) { char *t ; const char *msgid ; Article article ; Buffer *articleBuffers ; int i, nntpLen ; article = p->article ; nntpLen = artNntpBufferCount (article) ; msgid = artMsgId (article) ; if (nntpLen == 0) { /* file no longer valid so drop from queue */ ArtHolder ta = p ; if (q == NULL) /* it's the first in the queue */ cxn->takeHead = p->next ; else q->next = p->next ; p = p->next ; ASSERT (cxn->articleQTotal > 0) ; cxn->articleQTotal-- ; ta->next = cxn->missing ; cxn->missing = ta ; } else { articleBuffers = artGetNntpBuffers (article) ; /* set up the buffer with the TAKETHIS command in it. 12 == strlen ("TAKETHIS \n\r") */ takeBuffLen = 12 + strlen (msgid) ; takeBuffer = newBuffer (takeBuffLen) ; t = bufferBase (takeBuffer) ; sprintf (t, "TAKETHIS %s\r\n", msgid) ; bufferSetDataSize (takeBuffer, strlen (t)) ; d_printf (5,"%s:%d Command %s\n", peerName, cxn->ident, t) ; ASSERT (writeIdx <= lenArray) ; rval [writeIdx++] = takeBuffer ; /* now add all the buffers that make up the body of the TAKETHIS command */ for (i = 0 ; i < nntpLen ; i++) { ASSERT (writeIdx <= lenArray) ; rval [writeIdx++] = bufferTakeRef (articleBuffers [i]) ; } freeBufferArray (articleBuffers) ; if ( !cxn->needsChecks ) { /* this isn't quite right. An article may be counted twice if we switch to no-CHECK mode after its CHECK was issued, but before its TAKETHIS was done just now. I'm not going to worry unless someone complains. */ cxn->checksIssued++ ; hostArticleOffered (cxn->myHost,cxn) ; } q = p ; p = p->next ; } } if (writeIdx > 0) rval [writeIdx] = NULL ; else { /* all articles were missing and no CHECKS */ free (rval) ; rval = NULL ; } } else if (checkBuffer != NULL) /* no TAKETHIS to do, but some CHECKS */ rval = makeBufferArray (checkBuffer, NULL) ; return rval ; } /* * for one reason or another we need to disconnect gracefully. We send a * QUIT command. */ static void issueQUIT (Connection cxn) { Buffer quitBuffer, *writeArray ; const char *peerName = hostPeerName (cxn->myHost) ; ASSERT (cxn->takeHead == NULL) ; ASSERT (cxn->checkHead == NULL) ; VALIDATE_CONNECTION (cxn) ; if (cxn->quitWasIssued) return ; if (writeIsPending (cxn->myEp)) { warn ("%s:%d internal QUIT while write pending", peerName, cxn->ident) ; if (cxn->state == cxnClosingS) cxnDead (cxn) ; else cxnWait (cxn) ; } else { quitBuffer = newBuffer (7) ; strlcpy (bufferBase (quitBuffer), "QUIT\r\n", 7) ; bufferSetDataSize (quitBuffer, 6) ; writeArray = makeBufferArray (quitBuffer, NULL) ; d_printf (1,"%s:%d Sending a quit command\n", hostPeerName (cxn->myHost),cxn->ident) ; cxn->quitWasIssued = true ; /* not exactly true, but good enough */ if ( !prepareWriteWithTimeout (cxn->myEp, writeArray, quitWritten, cxn) ) { die ("%s:%d fatal prepare write for QUIT command failed", peerName, cxn->ident) ; } } } /* * Set up the timer for the blocked reads */ static void initReadBlockedTimeout (Connection cxn) { ASSERT (cxn != NULL) ; ASSERT (cxn->state != cxnIdleS ) ; /* set up the response timer. */ clearTimer (cxn->readBlockedTimerId) ; if (cxn->readTimeout > 0) cxn->readBlockedTimerId = prepareSleep (responseTimeoutCbk, cxn->readTimeout, cxn) ; } /* * Set up the timer for the blocked reads */ static int prepareWriteWithTimeout (EndPoint endp, Buffer *buffers, EndpRWCB done, Connection cxn) { /* Clear the read timer, since we can't expect a response until everything is sent. XXX - would be nice to have a timeout for reponses if we're sending a string of commands. */ clearTimer (cxn->readBlockedTimerId) ; /* set up the write timer. */ clearTimer (cxn->writeBlockedTimerId) ; if (cxn->writeTimeout > 0) cxn->writeBlockedTimerId = prepareSleep (writeTimeoutCbk, cxn->writeTimeout, cxn) ; /* set up the write. */ return prepareWrite (endp, buffers, writeProgress, done, cxn) ; } /* * Does the actual deletion of a connection and all its private data. */ static void delConnection (Connection cxn) { bool shutDown; Connection c, q; if (cxn == NULL) return ; d_printf (1,"Deleting connection: %s:%d\n", hostPeerName (cxn->myHost),cxn->ident) ; for (c = gCxnList, q = NULL ; c != NULL ; q = c, c = c->next) if (c == cxn) { if (gCxnList == c) gCxnList = gCxnList->next ; else q->next = c->next ; break ; } ASSERT (c != NULL) ; if (cxn->myEp != NULL) delEndPoint (cxn->myEp) ; ASSERT (cxn->checkHead == NULL) ; ASSERT (cxn->checkRespHead == NULL) ; ASSERT (cxn->takeHead == NULL) ; ASSERT (cxn->takeRespHead == NULL) ; delBuffer (cxn->respBuffer) ; /* tell the Host we're outta here. */ shutDown = hostCxnGone (cxn->myHost, cxn) ; cxn->ident = 0 ; cxn->timeCon = 0 ; free (cxn->ipName) ; clearTimer (cxn->artReceiptTimerId) ; clearTimer (cxn->readBlockedTimerId) ; clearTimer (cxn->writeBlockedTimerId) ; clearTimer (cxn->flushTimerId) ; free (cxn) ; if (shutDown) { /* exit program if that was the last connexion for the last host */ /* XXX what about if there are ever multiple listeners? XXX this will be executed if all hosts on only one of the XXX listeners have gone */ time_t now = theTime () ; char dateString [30] ; char **p = PointersFreedOnExit ; /* finish out all outstanding memory */ while (*p) free (*p++) ; free (PointersFreedOnExit) ; freeTimeoutQueue () ; timeToString (now, dateString, sizeof(dateString)) ; notice ("ME finishing at %s", dateString) ; exit (0) ; } } /* * Bump up the value of the low pass filter on the connection. */ static void incrFilter (Connection cxn) { cxn->filterValue *= (1.0 - (1.0 / cxn->lowPassFilter)) ; cxn->filterValue += 1.0 ; } /* * decrement the value of the low pass filter on the connection. */ static void decrFilter (Connection cxn) { cxn->filterValue *= (1.0 - (1.0 / cxn->lowPassFilter)) ; } /* * return true if we have articles we need to issue commands for. */ static bool writesNeeded (Connection cxn) { return (cxn->checkHead != NULL || cxn->takeHead != NULL ? true : false) ; } /* * do some simple tests to make sure it's OK. */ static void validateConnection (Connection cxn) { unsigned int i ; unsigned int old ; ArtHolder p ; i = 0 ; /* count up the articles the Connection has and make sure that matches. */ for (p = cxn->takeHead ; p != NULL ; p = p->next) i++ ; d_printf (4,"TAKE queue: %d\n",i) ; old = i ; for (p = cxn->takeRespHead ; p != NULL ; p = p->next) i++ ; d_printf (4,"TAKE response queue: %d\n",i - old) ; old = i ; for (p = cxn->checkHead ; p != NULL ; p = p->next) i++ ; d_printf (4,"CHECK queue: %d\n",i - old) ; old = i ; for (p = cxn->checkRespHead ; p != NULL ; p = p->next) i++ ; d_printf (4,"CHECK response queue: %d\n",i - old) ; ASSERT (i == cxn->articleQTotal) ; switch (cxn->state) { case cxnConnectingS: ASSERT (cxn->doesStreaming == false) ; ASSERT (cxn->articleQTotal <= 1) ; ASSERT (cxn->artReceiptTimerId == 0) ; ASSERT (cxn->sleepTimerId == 0) ; /* ASSERT (cxn->timeCon == 0) ; */ break ; case cxnWaitingS: ASSERT (cxn->articleQTotal == 0) ; ASSERT (cxn->myEp == NULL) ; ASSERT (cxn->artReceiptTimerId == 0) ; ASSERT (cxn->readBlockedTimerId == 0) ; ASSERT (cxn->writeBlockedTimerId == 0) ; ASSERT (cxn->flushTimerId == 0) ; ASSERT (cxn->sleepTimerId == 0) ; ASSERT (cxn->timeCon == 0) ; break ; case cxnFlushingS: case cxnClosingS: if (!cxn->doesStreaming) ASSERT (cxn->articleQTotal <= 1) ; ASSERT (cxn->artReceiptTimerId == 0) ; ASSERT (cxn->flushTimerId == 0) ; ASSERT (cxn->sleepTimerId == 0) ; ASSERT (cxn->timeCon != 0) ; ASSERT (cxn->doesStreaming || cxn->maxCheck == 1) ; break ; case cxnFeedingS: if (cxn->doesStreaming) /* Some(?) hosts return the 439 response even before we're done sending, so don't go idle until here */ ASSERT (cxn->articleQTotal > 0 || writeIsPending (cxn->myEp)) ; else ASSERT (cxn->articleQTotal == 1) ; if (cxn->readTimeout > 0 && !writeIsPending (cxn->myEp) && cxn->checkRespHead != NULL && cxn->takeRespHead != NULL) ASSERT (cxn->readBlockedTimerId != 0) ; if (cxn->writeTimeout > 0 && writeIsPending (cxn->myEp)) ASSERT (cxn->writeBlockedTimerId != 0) ; ASSERT (cxn->sleepTimerId == 0) ; ASSERT (cxn->timeCon != 0) ; ASSERT (cxn->doesStreaming || cxn->maxCheck == 1) ; break; case cxnIdleS: ASSERT (cxn->articleQTotal == 0) ; if (cxn->articleReceiptTimeout > 0) ASSERT (cxn->artReceiptTimerId != 0) ; ASSERT (cxn->readBlockedTimerId == 0) ; ASSERT (cxn->writeBlockedTimerId == 0) ; ASSERT (cxn->sleepTimerId == 0) ; ASSERT (cxn->timeCon != 0) ; ASSERT (!writeIsPending (cxn->myEp)) ; break ; case cxnIdleTimeoutS: ASSERT (cxn->articleQTotal == 0) ; ASSERT (cxn->artReceiptTimerId == 0) ; ASSERT (cxn->writeBlockedTimerId == 0) ; ASSERT (cxn->sleepTimerId == 0) ; ASSERT (cxn->timeCon != 0) ; ASSERT (!writeIsPending (cxn->myEp)) ; break ; case cxnSleepingS: ASSERT (cxn->articleQTotal == 0) ; ASSERT (cxn->myEp == NULL) ; ASSERT (cxn->artReceiptTimerId == 0) ; ASSERT (cxn->readBlockedTimerId == 0) ; ASSERT (cxn->writeBlockedTimerId == 0) ; ASSERT (cxn->flushTimerId == 0) ; ASSERT (cxn->timeCon == 0) ; break ; case cxnStartingS: ASSERT (cxn->articleQTotal == 0) ; ASSERT (cxn->myEp == NULL) ; ASSERT (cxn->artReceiptTimerId == 0) ; ASSERT (cxn->readBlockedTimerId == 0) ; ASSERT (cxn->writeBlockedTimerId == 0) ; ASSERT (cxn->flushTimerId == 0) ; ASSERT (cxn->sleepTimerId == 0) ; ASSERT (cxn->timeCon == 0) ; break ; case cxnDeadS: break ; } } /* * Generate a printable string of the parameter. */ static const char *stateToString (CxnState state) { static char rval [64] ; switch (state) { case cxnStartingS: strlcpy (rval,"cxnStartingS", sizeof(rval)) ; break ; case cxnWaitingS: strlcpy (rval,"cxnWaitingS", sizeof(rval)) ; break ; case cxnConnectingS: strlcpy (rval,"cxnConnectingS", sizeof(rval)) ; break ; case cxnIdleS: strlcpy (rval,"cxnIdleS", sizeof(rval)) ; break ; case cxnIdleTimeoutS: strlcpy (rval,"cxnIdleTimeoutS", sizeof(rval)) ; break ; case cxnFeedingS: strlcpy (rval,"cxnFeedingS", sizeof(rval)) ; break ; case cxnSleepingS: strlcpy (rval,"cxnSleepingS", sizeof(rval)) ; break ; case cxnFlushingS: strlcpy (rval,"cxnFlushingS", sizeof(rval)) ; break ; case cxnClosingS: strlcpy (rval,"cxnClosingS", sizeof(rval)) ; break ; case cxnDeadS: strlcpy (rval,"cxnDeadS", sizeof(rval)) ; break ; default: snprintf (rval,sizeof(rval),"UNKNOWN STATE: %d",state) ; break ; } return rval ; } /**************************************************************************** * * Functions for managing the internal queue of Articles on each Connection. * ****************************************************************************/ static ArtHolder newArtHolder (Article article) { ArtHolder a = xmalloc (sizeof(struct art_holder_s)) ; a->article = article ; a->next = NULL ; return a ; } /* * Deletes the article holder */ static void delArtHolder (ArtHolder artH) { if (artH != NULL) free (artH) ; } /* * remove the article holder from the queue. Adjust the count and if nxtPtr * points at the element then adjust that too. */ static bool remArtHolder (ArtHolder artH, ArtHolder *head, unsigned int *count) { ArtHolder h, i ; ASSERT (head != NULL) ; ASSERT (count != NULL) ; h = *head ; i = NULL ; while (h != NULL && h != artH) { i = h ; h = h->next ; } if (h == NULL) return false ; if (i == NULL) *head = (*head)->next ; else i->next = artH->next ; (*count)-- ; return true ; } /* * append the ArticleHolder to the queue */ static void appendArtHolder (ArtHolder artH, ArtHolder *head, unsigned int *count) { ArtHolder p ; ASSERT (head != NULL) ; ASSERT (count != NULL) ; for (p = *head ; p != NULL && p->next != NULL ; p = p->next) /* nada */ ; if (p == NULL) *head = artH ; else p->next = artH ; artH->next = NULL ; (*count)++ ; } /* * find the article holder on the queue by comparing the message-id. */ static ArtHolder artHolderByMsgId (const char *msgid, ArtHolder head) { while (head != NULL) { if (strcmp (msgid, artMsgId (head->article)) == 0) return head ; head = head->next ; } return NULL ; } /* * Randomize a numeber by the given percentage */ static int fudgeFactor (int initVal) { int newValue ; static bool seeded ; if ( !seeded ) { time_t t = theTime () ; /* this may have been done already in endpoint.c. Is that a problem??? */ srand (t) ; seeded = true ; } newValue = initVal + (initVal / 10 - (rand() % (initVal / 5))); return newValue ; } inn-2.6.0/innfeed/tape.h0000644000175200017520000000417412575023702014440 0ustar iuliusiulius/* $Id: tape.h 6647 2004-01-25 20:06:42Z rra $ ** ** The public interface to the Tape class. ** ** Written by James Brister ** ** The Tape class simulates a mag tape. It only reads or writes Articles. A ** tape is either in an Input or Output state. When an Article is given to a ** Tape it will store the Article in memory until it reaches a highwater mark ** at which point it dumps all it's articles to disk. ** ** Input tapes generate article objects on request if the underlying tape ** file has info in it. The Tapes take care of cleaning up used-up files as ** needed. */ #if ! defined ( tape_h__ ) #define tape_h__ #include #include "misc.h" /* If dontRotate is true, then any articles that get written to the tape will never be read back in again. This is for the batch-mode-only case where articles written to tape were done so 'cause the remote temporarily rejected them. */ Tape newTape (const char *peerName, bool dontRotate) ; void gPrintTapeInfo (FILE *fp, unsigned int inedntAmt) ; void printTapeInfo (Tape tape, FILE *fp, unsigned int indentAmt) ; /* deletes the tape objects. If it has any articles cached then it dumps them to the disk. */ void delTape (Tape tape) ; /* give an article to the Tape for storage */ void tapeTakeArticle (Tape tape, Article article) ; /* get a new article from an Input tape. */ Article getArticle (Tape tape) ; /* close the input and output files and reopen them. */ void gFlushTapes (void) ; void tapeFlush (Tape tape) ; /**************************************************/ /* CLASS LEVEL FUNCTIONS */ /**************************************************/ /* get all the active input tapes to checkpoint their current positions */ void checkPointTapes (void) ; /* get the name of the directory tapes are being stored in. */ const char *getTapeDirectory (void) ; /* set the size limit of the output tapes. Default is zero which is no limit. */ void setOutputSizeLimit (long limit) ; int tapeConfigLoadCbk (void *data) ; void tapeLogGlobalStatus (FILE *fp) ; void tapeLogStatus (Tape tape, FILE *fp) ; #endif /* tape_h__ */ inn-2.6.0/innfeed/buffer.h0000644000175200017520000001044312575023702014754 0ustar iuliusiulius/* $Id: buffer.h 7284 2005-06-07 06:38:08Z eagle $ ** ** The public interface to the Buffer class. ** ** Written by James Brister ** ** The Buffer class encapsulates a region of memory. It provides reference ** counting so that redundant memory allocation and copying is minimized. A ** good example of this is the need to write the same data (e.g. an article ** body) out more than one socket. The data is stored in a Buffer and the ** Buffer is given to each EndPoint that is to write it. With the Refcount ** appropriately set the EndPoints can release their references at will and ** the Buffer will be cleaned up when necessary. */ #if ! defined ( buffer_h__ ) #define buffer_h__ #include #include #include "misc.h" /* * Create a new Buffer object and initialize it. */ Buffer newBuffer (size_t size) ; /* Create a new Buffer object around the preallocted PTR, which is SIZE bytes long. The data size of the Buffer is set to DATASIZE. When then buffer is released it will not delete the ptr (this is useful to have Buffers around contant strings, or Buffers around other Buffers) */ Buffer newBufferByCharP (const char *ptr, size_t size, size_t dataSize) ; /* * give up interest in the Buffer. Decrement refcount and delete if no * more referants */ void delBuffer (Buffer buff) ; /* print some debugging information about the buffer. */ void printBufferInfo (Buffer buffer, FILE *fp, unsigned int indentAmt) ; /* print debugging information about all outstanding buffers. */ void gPrintBufferInfo (FILE *fp, unsigned int indentAmt) ; /* increment reference counts so that the buffer object can be */ /* handed off to another function without it being deleted when that */ /* function calls bufferDelete(). Returns buff, so the call can be */ /* used in function arg list. */ Buffer bufferTakeRef (Buffer buff) ; /* returns the address of the base of the memory owned by the Buffer */ void *bufferBase (Buffer buff) ; /* returns the size of the memory buffer has available. This always returns 1 less than the real size so that there's space left for a null byte when needed. The extra byte is accounted for when the newBuffer function is called. */ size_t bufferSize (Buffer) ; /* return the amount of data actually in the buffer */ size_t bufferDataSize (Buffer buff) ; /* increment the size of the buffer's data region */ void bufferIncrDataSize (Buffer buff, size_t size) ; /* decrement the size of the buffer's data region */ void bufferDecrDataSize (Buffer buff, size_t size) ; /* set the size of the data actually in the buffer */ void bufferSetDataSize (Buffer buff, size_t size) ; /* callback to be called when buffer gets deleted */ void bufferSetDeletedCbk (Buffer buff, void (*cbk)(void *), void *data); /* walk down the BUFFS releasing each buffer and then freeing BUFFS itself */ void freeBufferArray (Buffer *buffs) ; /* All arguments are non-NULL Buffers, except for the last which must be NULL. Creates a free'able array and puts all the buffers into it (does not call bufferTakeRef on them). */ Buffer *makeBufferArray (Buffer buff, ...) ; /* returns true if the buffer was created via newBuffer (vs. newBufferByCharP) */ bool isDeletable (Buffer buff) ; /* Dups the array including taking out references on the Buffers inside. Return value must be freed (or given to freeBufferArray) */ Buffer *dupBufferArray (Buffer *array) ; /* return the number of non-NULL elements in the array. */ unsigned int bufferArrayLen (Buffer *array) ; /* copies the contents of the SRC buffer into the DEST buffer and sets the data size appropriately. Returns false if src is bigger than dest. */ bool copyBuffer (Buffer dest, Buffer src) ; /* return the ref count on the buffer */ unsigned int bufferRefCount (Buffer buff) ; /* insert a null byte at the end of the data region */ void bufferAddNullByte (Buffer buff) ; /* append the data of src to the data of dest, if dest is big enough */ bool concatBuffer (Buffer dest, Buffer src) ; /* expand the buffer's memory by the given AMT */ bool expandBuffer (Buffer buff, size_t amt) ; /* Expand the buffer (if necessary) and insert carriage returns before very line feed. Adjusts the data size. The base address may change afterwards. */ bool nntpPrepareBuffer (Buffer buffer) ; #endif /* buffer_h__ */ inn-2.6.0/innfeed/procbatch.in0000644000175200017520000001111312575023702015622 0ustar iuliusiulius#! /usr/bin/perl -w # fixscript will replace this line with code to load INN::Config # Author: James Brister -- berkeley-unix -- # Start Date: Thu May 16 10:32:02 1996 +0200 # RCSId: $Id: procbatch.in 9371 2011-09-04 09:21:43Z iulius $ # # Description: Take a file of the form generated by innd in the outgoing # directory ("Wnm*") and separate it into tape files for innfeed. # # Thanks to Clayton O'Neill for serious speed # improvements. # # # Hmm, perhaps we should try to read "backlog-directory" # from innfeed.conf. Oh well. # use strict; my $tapeDir = $INN::Config::pathspool . "/innfeed"; my $destDir = $INN::Config::pathtmp; my $spoolArts = $INN::Config::patharticles; my $outGoing = $INN::Config::pathoutgoing; ## ## Everything below here should probably be left alone. ## $0 =~ s!.*/!!; use Getopt::Std; my $usage = "$0 [-hquv] [-c [-s dir]] [-d dir] [-e host] [-m [-t dir]] inn-batchfile\n -c to check pathnames of articles before storing them -d dir to put the output file(s) in that directory ($destDir) -e host to process on entries for only that host -h display a short help screen -m to have $0 move the new files to the backlog directory -q quiet mode: only display error messages; good for cron jobs -s dir to specify where the news articles are ($spoolArts) -t dir to specify the backlog directory ($tapeDir) -u to unlink the input files when finished -v for verbosity $0 will take an INN funnel file (normally a file in $outGoing), or an innfeed \"dropped\" file, which is presumed to be of the format: pathname message-id peer1 peer2 peer3 ... and will break it up into files peer1.tmp, peer2.tmp, peer3.tmp... Each of these files will be of the format: pathname message-id that is the same as innfeed's backlog file format. Simply rename these files to peer1, peer2, peer3... in a running innfeed's backlog directory and they will be picked up automatically and processed by innfeed. Use the '-m' flag and they'll be moved automatically. "; my (%opt, %hosts); my $missing = 0; getopts ("cd:e:hmqs:t:uv", \%opt) || die $usage; die $usage if $opt{'h'}; die "Cannot specify both -q and -v\n\n" . $usage if ($opt{'q'} && $opt{'v'}); $spoolArts = $opt{'s'} if $opt{'s'}; $destDir = $opt{'d'} if $opt{'d'}; $tapeDir = $opt{'t'} if $opt{'t'}; my $inputFile = shift; die $usage if !$inputFile; unless (-f $inputFile) { exit if $opt{'q'}; die "No such file: $inputFile\n\n" . $usage; } die "No such directory: $spoolArts\n\n" . $usage if ( ! -d $spoolArts && $opt{'c'} ); die "No such directory: $destDir\n\n" . $usage if ( ! -d $destDir ); die "No such directory: $tapeDir\n\n" . $usage if ( ! -d $tapeDir && $opt{'m'} ); warn "Specifying -s without -c has no effect!" if $opt{'s'} and not $opt{'c'}; warn "Specifying -t without -m has no effect!" if $opt{'t'} and not $opt{'m'}; print "Using $inputFile\n" if $opt{'v'}; open (my $INPUT, '<', $inputFile) || die "$0: open ($inputFile): $!\n"; while (<$INPUT>) { chop; my @F = split; # Check the format of the line vigorously next unless (m!^\S+/\d+ <.+@.+> \S+! || m!^@[0-9A-F]+@ <.+@.+> \S+!); if ( $opt{'c'} ) { if ( ! -f "$spoolArts/$F[0]" ) { $missing++; print "Dropping file: $spoolArts/$F[0]\n" if $opt{'v'}; next; } } for (my $i = 2 ; $i <= $#F ; $i++) { my $host = $F[$i]; next if ($opt{'e'} && $opt{'e'} ne $host); # Keep out host names with any funny characters (from # corrupted files) if ($host !~ /^[-\._0-9A-Za-z]+$/) { warn "$0: bad site name ignored: \"$host\"\n"; next; } if ($hosts{$host}) { print {$hosts{$host}} "$F[0] $F[1]\n"; } else { my $outputFile = "$destDir/$host.tmp"; print "Starting $host\n" if ($opt{'v'}); open $hosts{$host}, '>>', $outputFile or die "open >>$outputFile: $!\n"; print {$hosts{$host}} "$F[0] $F[1]\n"; } } } close $INPUT; foreach my $host (keys %hosts) { close $hosts{$host}; my $outputFile = "$destDir/$host.tmp"; my $tmpTape = "$tapeDir/$host.tmp"; my $tapeFile = "$tapeDir/$host"; if ( $opt{'m'} ) { if ($outputFile ne $tmpTape) { my $cmd = "mv $outputFile $tmpTape"; system $cmd; die "$0: $cmd: failed\n" unless ($? == 0); } my $cmd = "cat $tmpTape | $INN::Config::sort -u >> $tapeFile && rm -f $tmpTape"; system $cmd; die "$0: $cmd: failed\n" unless ($? == 0); } } unlink $inputFile if $opt{'u'}; print "$missing articles dropped\n" if ( $opt{'v'} && $missing > 0 ); inn-2.6.0/innfeed/misc.c0000644000175200017520000004024712575023702014436 0ustar iuliusiulius/* $Id: misc.c 9911 2015-07-04 21:32:56Z iulius $ ** ** Helper routines for the innfeed program. ** ** Written by James Brister */ #include "innfeed.h" #include "config.h" #include "clibrary.h" #include #include #include #include #include #include #include #include #include #include #include /* FIXME: Default to a max length of 256 characters for path names if the host headers doesn't give better information. Should be replaced by the code from Stevens. */ #ifndef PATH_MAX # define PATH_MAX 256 #endif #include "inn/messages.h" #include "inn/libinn.h" #include "endpoint.h" #include "misc.h" #include "tape.h" unsigned int openfds ; int debuggingOutput ; unsigned int loggingLevel ; char **PointersFreedOnExit ; char *timeToStringFormat = 0; bool debuggingDump = true ; extern void (*gPrintInfo) (void) ; void (*gCleanUp) (void) = 0 ; /* Log a message to stderr, called from warn or die. Mostly the same as the standard message_log_stderr, but prepends the date to each line. */ void error_log_stderr_date(int len UNUSED, const char *fmt, va_list args, int err) { char timebuff[30]; time_t now; struct tm *tm; now = time(NULL); tm = localtime(&now); strftime(timebuff, sizeof(timebuff), "%Y-%m-%d %H:%M:%S", tm); fprintf(stderr, "%s %s: ", timebuff, (message_program_name ? message_program_name : "UNKNOWN")); vfprintf(stderr, fmt, args); if (err) fprintf(stderr, ": %s", strerror(err)); fprintf(stderr, "\n"); } /* If desired, print out the state of innfeed, call a cleanup function, and then dump core. Used as an exit handler for die. */ void dump_core(void) { #if SNAPSHOT_ON_DIE if (debuggingDump && gPrintInfo != NULL) (*gPrintInfo)(); #endif if (gCleanUp != NULL) (*gCleanUp)(); chdir(getTapeDirectory()); sleep(5); abort(); } /* An alternate version of die, used when we don't want to dump core. This should somehow eventually be phased out to simplify things; it's basically a copy of die() from lib/error.c that ignores the cleanup handler and has innfeed's handlers hard-coded (ugh). */ void logAndExit(int status, const char *format, ...) { va_list args; int length; va_start(args, format); length = vsnprintf(NULL, 0, format, args); va_end(args); va_start(args, format); error_log_stderr_date(length, format, args, 0); va_end(args); va_start(args, format); message_log_syslog_err(length, format, args, 0); va_end(args); exit(status); } void d_printf (unsigned int level, const char *fmt, ...) { static pid_t myPid ; char timeString [30] ; time_t now ; struct tm *tm ; va_list ap ; if (myPid == 0) myPid = getpid () ; if (loggingLevel < level) return ; now = theTime() ; tm = localtime (&now); strftime (timeString, sizeof(timeString), "%b %d %H:%M:%S", tm); va_start (ap, fmt) ; fprintf (stderr, "%s %s[%ld]: ",timeString, (message_program_name ? message_program_name : "UNKNOWN"), (long) myPid) ; vfprintf (stderr, fmt, ap) ; va_end (ap) ; } void logOrPrint (int level, FILE *fp, const char *fmt, ...) { va_list ap ; va_start (ap,fmt) ; if (fp != NULL) { vfprintf (fp,fmt,ap) ; fputc ('\n',fp) ; } else { char buffer [512] ; /* gag me */ vsnprintf (buffer,sizeof (buffer),fmt,ap) ; syslog (level,"%s",buffer) ; } va_end (ap) ; } /* return true if the file exists and is a regular file. */ bool fileExistsP (const char *filename) { struct stat buf ; if (stat (filename,&buf) < 0) return false ; return (S_ISREG (buf.st_mode) ? true : false) ; } bool isDirectory (const char *filename) { struct stat buf ; if (stat (filename,&buf) < 0) return false ; return (S_ISDIR (buf.st_mode) ? true : false) ; } bool getNntpResponse (char *p, int *code, char **rest) { bool rval = true ; int cd = 0 ; int digits = 0 ; if (rest) *rest = 0 ; *code = 0 ; if (p == NULL) return false ; while (*p && isspace((unsigned char) *p)) p++ ; while (*p && isdigit((unsigned char) *p)) { digits++ ; cd = (cd * 10) + (*p - '0') ; p++ ; } if (digits != 3) return false ; if (*p == '-') p++ ; while (*p && isspace((unsigned char) *p)) p++ ; if (rest) *rest = p ; *code = cd ; return rval ; } /* Pull out a message id from a response on to a streaming command */ char *getMsgId (const char *p) { const char *q ; char *rval ; while (*p && isspace((unsigned char) *p)) p++ ; while (*p && !isspace((unsigned char) *p)) p++ ; /* skip response code */ while (*p && isspace((unsigned char) *p)) p++ ; if ( *p == '\0' ) return NULL ; q = p ; while ( *q && !isspace((unsigned char) *q) ) q++ ; rval = xstrndup (p, q - p) ; return rval ; } char *findNonBlankString (char *ptr, char **tail) { char *p, *q ; for (p = ptr ; *p && isspace((unsigned char) *p) ; p++) /* nada */ ; if ( ! *p ) return NULL ; for (q = p ; *q && !isspace((unsigned char) *q) ; q++) /* nada */ ; *tail = q ; return p ; } /* strtok can't handle zero length tokens. */ char *mystrtok (char *line, const char *sep) { static char *newPoint ; char *oldline ; if (line == NULL && newPoint == NULL) return NULL ; if (line != NULL) { oldline = line ; while (*line != '\0' && strchr (sep,*line) == NULL) line++ ; if (*line == '\0') newPoint = NULL ; else { newPoint = line + 1 ; *line = '\0' ; } } else { if (newPoint == NULL) return NULL ; oldline = newPoint ; line = oldline ; while (*line != '\0' && strchr (sep,*line) == NULL) line++ ; if (*line == '\0') newPoint = NULL ; else { newPoint = line + 1 ; *line = '\0' ; } } return oldline ; } void trim_ws (char *string) { char *p ; unsigned int len ; assert (string != NULL) ; len = strlen (string) ; if (len == 0) return ; for (p = string + len - 1 ; p >= string && isspace((unsigned char) *p) ; p--) /* nada */ ; *++p = '\0' ; } #if 0 /* Scribble on top of memory we're about to free. */ void deadBeef (void *base, size_t byteCount) { unsigned char *b = (unsigned char *) base ; int i ; #if 0 memset (base, 0, byteCount) ; #else assert (b != NULL) ; for (i = 0 ; i < ((int) byteCount) - 4 ; i += 4) { #if 0 *((int *) (b + i)) = 0xdeadbeef ; #else b [i + 0] = (unsigned char) 0xde ; b [i + 1] = (unsigned char) 0xad ; b [i + 2] = (unsigned char) 0xbe ; b [i + 3] = (unsigned char) 0xef ; #endif } switch (byteCount % 4) { case 0: *(b + i + 3) = (unsigned char) 0xef ; case 3: *(b + i + 2) = (unsigned char) 0xbe ; case 2: *(b + i + 1) = (unsigned char) 0xad ; case 1: *b = (unsigned char) 0xde ; } #endif } #endif /* Not using plain flock or lockf 'cause I don't want to waste file descriptors. This routine is based on the file shlock.c from INN. */ bool lockFile (const char *fileName) { char buff [20] ; char tmpName [PATH_MAX], realName [PATH_MAX] ; char *p ; int fd, i ; pid_t pid = getpid () ; strlcpy (realName,fileName,sizeof (realName)) ; if ((p = strrchr (realName, '/')) != NULL) { *p = '\0' ; snprintf (tmpName, sizeof(tmpName), "%s/lockf%ld", realName, (long) pid) ; *p = '/' ; } else snprintf (tmpName, sizeof(tmpName), "lockf%ld", (long) pid) ; /* Create the temporary name for the lock file. */ while ((fd = open (tmpName, O_RDWR | O_CREAT | O_EXCL, 0644)) < 0) { switch (errno) { default: unlink (tmpName) ; syswarn ("ME lock file open: %s", tmpName) ; return false ; case EEXIST: if (unlink (tmpName) < 0) { syswarn ("ME lock file unlink: %s", tmpName) ; return false ; } break; } } /* stick our pid in the temp file. */ snprintf (buff,sizeof(buff),"%ld\n",(long) pid) ; if (write (fd,buff,(size_t) strlen (buff)) != (int) strlen (buff)) { syswarn ("ME lock file pid-write") ; close (fd) ; unlink (tmpName) ; return false ; } close (fd) ; /* now link the real name to the temp file. */ while (link (tmpName,realName) < 0) { switch (errno) { default: /* opps. bailing out. */ syswarn ("ME lock file link: %s", realName) ; unlink (tmpName) ; return false ; case EEXIST: /* the real lock file exists. So pull out the pid in there and see if that process is still alive. */ if ((fd = open (realName,O_RDONLY)) < 0) { syswarn ("ME lock file open: %s", realName) ; unlink (tmpName) ; return false ; } if ((i = read (fd,buff,sizeof (buff) - 1)) <= 0) { close (fd) ; unlink (tmpName) ; return false ; } close (fd) ; buff [i] = '\0' ; pid = (pid_t) atol (buff) ; if (pid <= 0) { warn ("ME lock bad-pid info in %s: %s", realName, buff) ; unlink (tmpName) ; return false ; } /* now send a null signal to the process named inside to see if it's still alive. */ if (kill (pid,0) == 0) { warn ("ME lock in-use already: %s by pid %ld", realName, (unsigned long) pid); unlink (tmpName) ; return false ; /* process is still alive */ } /* process that took out the lock is gone */ if (unlink (realName) < 0) { syswarn ("ME lock file unlink: %s", realName) ; unlink (tmpName) ; return false ; } } } unlink (tmpName) ; return true ; } void unlockFile (const char *lockfile) { unlink (lockfile) ; } bool endsIn (const char *string, const char *tail) { size_t len = strlen (tail) ; size_t slen = strlen (string) ; if (slen < len) return false ; else if (strcmp (string + slen - len, tail) == 0) return true ; else return false ; } /* append the contents of src to dest. src is removed if append if successful */ bool appendFile (const char *dest, const char *src) { FILE *inTmp, *outTmp ; char buff [BUFSIZ] ; size_t rval ; /* append the outputFilename file to the inputFilename file */ if ((outTmp = fopen (dest, "a")) == NULL) die ("fopen (%s): %s",dest, strerror (errno)) ; if ((inTmp = fopen (src, "r")) == NULL) die ("fopen (%s): %s",src, strerror (errno)) ; while ((rval = fread (buff,sizeof (char),BUFSIZ,inTmp)) > 0) { if (fwrite (buff,sizeof (char), rval, outTmp) != rval) die ("fwrite: %s", strerror (errno)) ; } if (ferror (inTmp)) die ("Error on inTmp in newTape") ; if (ferror (outTmp)) die ("Error on outTmp in newTape") ; if (fclose (inTmp) != 0) die ("fclose (inTmp): appendFile (%s,%s): %s",dest,src,strerror (errno)) ; if (fclose (outTmp) != 0) die ("fclose (outTmp): appendFile (%s,%s): %s",dest,src,strerror (errno)) ; if (unlink (src) != 0) die ("unlink (%s): %s", src, strerror (errno)) ; return true ; } /* return true if file1 is older than file2 */ bool isOlder (const char *file1, const char *file2) { struct stat buf1 ; struct stat buf2 ; if (stat (file1,&buf1) < 0) return false ; if (stat (file2,&buf2) < 0) return false ; return ((buf1.st_mtime < buf2.st_mtime) ? true : false) ; } void freeCharP (char *charp) { free (charp) ; } /* return the length of the file reference by the given file descriptor */ long fileLength (int fd) { struct stat buf ; if (fstat (fd,&buf) < 0) return false ; return ((long) buf.st_size) ; } const char *boolToString (bool val) { return val ? "true" : "false" ; } char* timeToString(time_t t, char* buffer, size_t size) { static const char defaultFormat[] = "%a %b %d %H:%M:%S %Y" ; const struct tm *const tm = localtime(&t); #pragma GCC diagnostic ignored "-Wformat-nonliteral" strftime (buffer, size, timeToStringFormat == 0 ? defaultFormat : timeToStringFormat, tm); #pragma GCC diagnostic warning "-Wformat-nonliteral" return buffer; } void addPointerFreedOnExit (char *pointerToFree) { static int totalPointers = 0 ; static int nextPointer = 0 ; if (nextPointer == 0 || nextPointer == totalPointers - 1) { int i; totalPointers += 16 ; if (PointersFreedOnExit == NULL) PointersFreedOnExit = xmalloc (sizeof(char *) * totalPointers) ; else PointersFreedOnExit = xrealloc (PointersFreedOnExit, sizeof(char *) * totalPointers) ; for (i = nextPointer; i < totalPointers; i++) PointersFreedOnExit [i] = NULL; } PointersFreedOnExit [nextPointer++] = pointerToFree ; } /* borrows heavily from the shrinkfile program by chongo. */ bool shrinkfile (FILE *fp, long size, char *name, const char *mode) { long currlen = ftello (fp) ; char *tmpname ; char buffer [BUFSIZ] ; FILE *tmpFp ; int c ; int i ; int fd ; if (currlen <= size) { d_printf (1,"No need to shrink file (%s %ld vs %ld\n", name,size,currlen) ; return true ; } /* create a temp file. */ tmpname = concat (name,".XXXXXX",(char *)0) ; fd = mkstemp (tmpname) ; if (fd < 0) { syswarn ("ME error creating temp shrink file for %s", name) ; free (tmpname) ; return false ; } if ((tmpFp = fdopen (fd,"w")) == NULL) { syswarn ("ME error opening temp shrink file %s", tmpname) ; free (tmpname) ; return false ; } if (fseeko (fp,currlen - size,SEEK_SET) != 0) { fclose (tmpFp) ; warn ("ME error seeking to point %ld in %s", currlen - size, name) ; free (tmpname) ; return false ; } /* find the end of the next line in the shrinking file. */ while ((c = fgetc (fp)) != '\n') if (c == EOF) { warn ("ME no newline in shrinking file %s", name) ; fclose (tmpFp) ; fseeko (fp,currlen,SEEK_SET) ; free (tmpname) ; return false ; } /* copy the tail of the shrinking file to the temp file. */ while ((i = fread (buffer,1,sizeof (buffer),fp)) > 0) { if (fwrite (buffer,1,i,tmpFp) != (size_t) i) { fclose (tmpFp) ; syswarn ("ME fwrite failed to temp shrink file %s", tmpname) ; fseeko (fp,currlen, SEEK_SET) ; free (tmpname) ; return false ; } } if (i < 0) logAndExit (1,"ME fread failed on file %s: %s",name, strerror (errno)) ; fclose (tmpFp) ; if (unlink (name) != 0) logAndExit (1,"ME oserr unlink %s: %s",name, strerror (errno)) ; /* we're in the same directory so this is ok. */ if (rename (tmpname,name) != 0) logAndExit (1,"ME oserr rename %s, %s: %s", tmpname, name, strerror (errno)) ; if (freopen (name,mode,fp) != fp) logAndExit (1,"ME freopen on shrink file failed %s: %s", name, strerror (errno)) ; fseeko (fp,0,SEEK_END) ; size = ftello (fp) ; notice ("ME file %s shrunk from %ld to %ld", name, currlen, size) ; free (tmpname) ; return true ; } long pathMax (const char *pathname UNUSED) { static long rval = 0 ; if (rval > 0) return rval ; #if defined (PATH_MAX) rval = PATH_MAX ; #elif defined (_POSIX_PATH_MAX) rval = _POSIX_PATH_MAX ; #elif defined (DO_HAVE_PATHCONF) && defined (_PC_PATH_MAX) if (pathname == NULL) pathname = "/tmp" ; rval = pathconf (pathname,_PC_PATH_MAX) ; #else rval = 255 ; if (!logged) { syslog (LOG_ERR,NO_PATH_MAX,rval) ; logged = true ; } #endif return rval ; } inn-2.6.0/innfeed/tape.c0000644000175200017520000010376112575023702014435 0ustar iuliusiulius/* $Id: tape.c 9695 2014-09-20 05:53:22Z iulius $ ** ** The implementation of the innfeed Tape class. ** ** Written by James Brister ** ** The implementation of the Tape class. Tapes are read-only or write-only ** files that are accessed sequentially. Their basic unit of i/o is an ** Article. Tapes work out of a single directory and manage all file names ** themselves. ** ** Tapes will checkpoint themselves periodically so that when innfeed exits ** or crashes things can restart close to where they were last. The period ** checkpointing is handled entirely by the Tape class, but the checkpoint ** period needs to be set by some external user before the first tape is ** created. */ #include "innfeed.h" #include "config.h" #include "clibrary.h" #include #include #include #include #include #include #include typedef struct dirent DIRENTRY ; #ifdef HAVE_SYS_TIME_H # include #endif #include #include "inn/innconf.h" #include "inn/messages.h" #include "inn/libinn.h" #include "article.h" #include "configfile.h" #include "endpoint.h" #include "tape.h" extern char *dflTapeDir; extern bool genHtml ; extern unsigned int hostHighwater; #if 0 /* a structure for temporary storage of articles. */ typedef struct q_e_s { Article article ; struct q_e_s *next ; } *QueueElem ; #endif /* The Tape class type. */ struct tape_s { /* the pathname of the file the administrator can drop in by hand. */ char *handFilename ; /* the pathname of the file the Tape will read from */ char *inputFilename ; /* the pathname of the file the Tape will write to. */ char *outputFilename ; /* the pathname of the file used in locking */ char *lockFilename ; /* the peer we're doing this for. */ char *peerName ; FILE *inFp ; /* input FILE */ FILE *outFp ; /* output FILE */ time_t lastRotated ; /* time files last got switched */ bool checkNew ; /* set bool when we need to check for hand-crafted file. */ #if 0 /* the tape holds a small output queue in memory to avoid thrashing. */ QueueElem head ; QueueElem tail ; unsigned int qLength ; /* amount on the queue */ #endif long outputSize ; /* the current size of the output file. */ long lossage ; /* the number of bytes we try to keep the output under. We actually wait for the outputSize to get 10% greater than this amount before shrinking the file down again. A value of zero means no limit. */ long outputLowLimit ; long outputHighLimit ; double backlogFactor ; bool scribbled ; /* have we scribbled a checkpoint value in the file. */ long tellpos ; /* for input file checkpointing. */ bool changed ; /* true if tape was read since last checkpoint or start. */ /* true if articles that are output are NOT later input. */ bool noRotate ; /* true if no articles should ever be spooled */ bool noBacklog ; }; static void checkpointTape (Tape tape) ; static void removeTapeGlobally (Tape tape) ; static void addTapeGlobally (Tape tape) ; static void prepareFiles (Tape tape) ; static void tapeCkNewFileCbk (TimeoutId id, void *d) ; static void tapeCheckpointCallback (TimeoutId id, void *d) ; #if 0 static void flushTape (Tape tape) ; #endif static void tapesSetCheckNew (void) ; static void initTape (Tape nt) ; static void tapeCleanup (void) ; /* pathname of directory we store tape files in. */ static char *tapeDirectory ; /* the callback ID of the checkpoint timer callback. */ static TimeoutId checkPtId ; static TimeoutId ckNewFileId ; /* number of seconds between tape checkpoints. */ static unsigned int tapeCkPtPeriod ; static unsigned int tapeCkNewFilePeriod ; static time_t rotatePeriod = TAPE_ROTATE_PERIOD ; /* global list of tapes so we can checkpoint them periodically */ static Tape *activeTapes ; /* Size of the activeTapes array */ static size_t activeTapeSize ; /* index of last element in activeTapes that's being used. */ static size_t activeTapeIdx ; #if 0 /* default limit of the size of output tapes. */ static long defaultSizeLimit ; #endif unsigned int tapeHighwater ; bool debugShrinking = false ; extern bool talkToSelf ; /* main.c */ /* callback when config file is loaded */ int tapeConfigLoadCbk (void *data) { int rval = 1 ; long iv ; int bv ; FILE *fp = (FILE *) data ; char *dir, *p ; if (getString (topScope,"backlog-directory",&p,NO_INHERIT)) { dir = concatpath(innconf->pathspool, p); free(p); if (tapeDirectory != NULL && strcmp (tapeDirectory,dir) != 0) { warn ("ME config: cannot change backlog-directory of a running" " process") ; free (dir) ; dir = xstrdup (tapeDirectory) ; } if (!isDirectory (dir) && isDirectory (dflTapeDir)) { logOrPrint (LOG_ERR,fp, "ME config: definition of backlog-directory (%s) is a" " non-existant directory. Using %s", dir,dflTapeDir) ; free (dir) ; dir = xstrdup (dflTapeDir) ; } else if (!isDirectory (dir)) logAndExit (1,"ME config: no usable value for backlog-directory") ; } else if (!isDirectory (dflTapeDir)) { logAndExit (1,"ME config: no usable value for backlog-directory") ; return -1; /* not reached */ } else dir = xstrdup (dflTapeDir) ; if (tapeDirectory != NULL) free (tapeDirectory) ; tapeDirectory = dir ; if (getInteger (topScope,"backlog-highwater",&iv,NO_INHERIT)) { if (iv < 0) { rval = 0 ; logOrPrint (LOG_ERR,fp, "ME config: value of %s (%ld) in %s cannot be less" " than 0. Using %ld","backlog-highwater", iv,"global scope",(long)TAPE_HIGHWATER); iv = TAPE_HIGHWATER ; } } else iv = TAPE_HIGHWATER ; tapeHighwater = (unsigned int) iv ; if (getInteger (topScope,"backlog-rotate-period",&iv,NO_INHERIT)) { if (iv < 0) { rval = 0 ; logOrPrint (LOG_ERR,fp, "ME config: value of %s (%ld) in %s cannot be less" " than 0. Using %ld", "backlog-rotate-period", iv,"global scope",(long)TAPE_ROTATE_PERIOD); iv = TAPE_ROTATE_PERIOD ; } } else iv = TAPE_ROTATE_PERIOD ; rotatePeriod = (unsigned int) iv ; if (getInteger (topScope,"backlog-ckpt-period",&iv,NO_INHERIT)) { if (iv < 0) { rval = 0 ; logOrPrint (LOG_ERR,fp, "ME config: value of %s (%ld) in %s cannot be less" " than 0. Using %ld", "backlog-ckpt-period",iv, "global scope",(long)TAPE_CHECKPOINT_PERIOD); iv = TAPE_CHECKPOINT_PERIOD ; } } else iv = TAPE_CHECKPOINT_PERIOD ; tapeCkPtPeriod = (unsigned int) iv ; if (getInteger (topScope,"backlog-newfile-period",&iv,NO_INHERIT)) { if (iv < 0) { rval = 0 ; logOrPrint (LOG_ERR,fp, "ME config: value of %s (%ld) in %s cannot be less" " than 0. Using %ld", "backlog-newfile-period", iv,"global scope",(long)TAPE_NEWFILE_PERIOD); iv = TAPE_NEWFILE_PERIOD ; } } else iv = TAPE_NEWFILE_PERIOD ; tapeCkNewFilePeriod = (unsigned int) iv ; if (getBool (topScope,"debug-shrinking",&bv,NO_INHERIT)) debugShrinking = (bv ? true : false) ; return rval ; } /* Create a new Tape object. There are three potential files involved in I/O. 'peerName' is what the admin may have dropped in by hand. 'peerName.input' is the file that was being used as input the last time things were run. 'peerName.output' is the file that was being used as output. The file 'peerName' is appended to 'peerName.input' (or renamed if 'peerName.input' doesn't exist). Then 'peerName.output' is appeneded (or renamed) to 'peerName.input' */ static bool inited = false ; Tape newTape (const char *peerName, bool dontRotate) { Tape nt = xmalloc (sizeof(struct tape_s)) ; size_t pLen = strlen (peerName) ; size_t dLen = strlen (tapeDirectory) ; if (!inited) { inited = true ; atexit (tapeCleanup) ; } ASSERT (nt != NULL) ; if (endsIn (peerName,INPUT_TAIL)) die ("Sorry, can't have a peer name ending in \"%s\"",INPUT_TAIL) ; if (endsIn (peerName,OUTPUT_TAIL)) die ("Sorry, can't have a peer name ending in \"%s\"",OUTPUT_TAIL) ; if (endsIn (peerName,LOCK_TAIL)) die ("Sorry, can't have a peer name ending in \"%s\"",LOCK_TAIL) ; nt->peerName = xstrdup (peerName) ; nt->handFilename = xmalloc (pLen + dLen + 2) ; sprintf (nt->handFilename,"%s/%s",tapeDirectory,peerName) ; nt->lockFilename = xmalloc (pLen + dLen + strlen(LOCK_TAIL) + 2) ; sprintf (nt->lockFilename,"%s/%s%s",tapeDirectory,peerName,LOCK_TAIL) ; nt->inputFilename = xmalloc (pLen + dLen + strlen(INPUT_TAIL) + 2) ; sprintf (nt->inputFilename,"%s/%s%s",tapeDirectory,peerName,INPUT_TAIL) ; nt->outputFilename = xmalloc (pLen + dLen + strlen(OUTPUT_TAIL) + 2) ; sprintf (nt->outputFilename,"%s/%s%s",tapeDirectory,peerName,OUTPUT_TAIL) ; if ( !lockFile (nt->lockFilename) ) { warn ("ME lock failed for host: %s", nt->lockFilename) ; free (nt->handFilename) ; free (nt->lockFilename) ; free (nt->inputFilename) ; free (nt->outputFilename) ; free (nt) ; return NULL ; } nt->noRotate = false ; /* for first time prepare */ initTape (nt) ; nt->noRotate = dontRotate ; addTapeGlobally (nt) ; if (checkPtId == 0 && tapeCkPtPeriod > 0) /* only done once. */ checkPtId = prepareSleep (tapeCheckpointCallback,tapeCkPtPeriod,NULL); if (ckNewFileId == 0 && tapeCkNewFilePeriod > 0) ckNewFileId = prepareSleep (tapeCkNewFileCbk,tapeCkNewFilePeriod,NULL) ; return nt ; } static void initTape (Tape nt) { value *peerVal = findPeer (nt->peerName) ; scope *s = (peerVal == NULL ? NULL : peerVal->v.scope_val) ; nt->inFp = NULL ; nt->outFp = NULL ; nt->lastRotated = 0 ; nt->checkNew = false ; #if 0 nt->head = NULL ; nt->tail = NULL ; nt->qLength = 0 ; #endif nt->scribbled = false ; nt->tellpos = 0 ; nt->changed = false ; nt->outputSize = 0 ; nt->lossage = 0 ; nt->noBacklog = false ; nt->outputLowLimit = BLOGLIMIT; nt->outputHighLimit = BLOGLIMIT_HIGH; nt->backlogFactor = LIMIT_FUDGE; if (!talkToSelf) { int bval ; if (getBool (s, "no-backlog", &bval, INHERIT)) nt->noBacklog = (bval ? true : false); else nt->noBacklog = TAPE_DISABLE; if (getInteger (s,"backlog-limit",&nt->outputLowLimit,INHERIT)) { if (!getReal (s,"backlog-factor",&nt->backlogFactor,INHERIT)) { if (!getInteger (s,"backlog-limit-highwater", &nt->outputHighLimit,INHERIT)) { warn ("%s no backlog-factor or backlog-limit-highwater", nt->peerName) ; nt->outputLowLimit = BLOGLIMIT; nt->outputHighLimit = BLOGLIMIT_HIGH; nt->backlogFactor = LIMIT_FUDGE; } } else nt->outputHighLimit = (long)(nt->outputLowLimit * nt->backlogFactor); } else warn ("ME config: no definition for required key backlog-limit") ; } d_printf (1, "%s spooling: %s\n", nt->peerName, nt->noBacklog ? "disabled" : "enabled"); d_printf (1,"%s tape backlog limit: [%ld %ld]\n",nt->peerName, nt->outputLowLimit, nt->outputHighLimit) ; prepareFiles (nt) ; } void gFlushTapes (void) { unsigned int i ; notice ("ME flushing tapes") ; for (i = 0 ; i < activeTapeIdx ; i++) tapeFlush (activeTapes [i]) ; } /* close the input and output tapes and reinitialize everything in the tape. */ void tapeFlush (Tape tape) { if (tape->inFp != NULL) { checkpointTape (tape) ; fclose (tape->inFp) ; } if (tape->outFp != NULL) fclose (tape->outFp) ; initTape (tape) ; } void gPrintTapeInfo (FILE *fp, unsigned int indentAmt) { char indent [INDENT_BUFFER_SIZE] ; unsigned int i ; for (i = 0 ; i < MIN(INDENT_BUFFER_SIZE - 1,indentAmt) ; i++) indent [i] = ' ' ; indent [i] = '\0' ; fprintf (fp,"%sGlobal Tape List : (count %lu) {\n", indent,(unsigned long) activeTapeIdx) ; for (i = 0 ; i < activeTapeIdx ; i++) printTapeInfo (activeTapes [i],fp,indentAmt + INDENT_INCR) ; fprintf (fp,"%s}\n",indent) ; } void tapeLogGlobalStatus (FILE *fp) { fprintf (fp,"%sBacklog file global values:%s\n", genHtml ? "" : "", genHtml ? "" : "") ; fprintf (fp," directory: %s\n",tapeDirectory) ; fprintf (fp," rotate period: %-3ld seconds\n",(long) rotatePeriod) ; fprintf (fp,"checkpoint period: %-3ld seconds\n",(long) tapeCkPtPeriod) ; fprintf (fp," newfile period: %-3ld seconds\n",(long) tapeCkNewFilePeriod); fprintf (fp,"backlog highwater: %u\n", tapeHighwater); fprintf (fp," highwater queue: %u\n", hostHighwater); fprintf (fp,"\n") ; } void tapeLogStatus (Tape tape, FILE *fp) { if (tape == NULL) fprintf (fp,"(no tape)\n") ; else if (tape->noBacklog) fprintf (fp," spooling: DISABLED\n"); else if (tape->outputLowLimit == 0) fprintf (fp," spooling: UNLIMITED\n"); else { fprintf (fp," backlog low limit: %ld\n", tape->outputLowLimit) ; fprintf (fp," backlog upper limit: %ld", tape->outputHighLimit) ; if (tape->backlogFactor > 0.0) fprintf (fp," (factor %1.2f)",tape->backlogFactor) ; fputc ('\n',fp) ; fprintf (fp," backlog shrinkage: ") ; fprintf (fp,"%ld bytes (from current file)\n",tape->lossage) ; } } void printTapeInfo (Tape tape, FILE *fp, unsigned int indentAmt) { char indent [INDENT_BUFFER_SIZE] ; unsigned int i ; #if 0 QueueElem qe ; #endif for (i = 0 ; i < MIN(INDENT_BUFFER_SIZE - 1,indentAmt) ; i++) indent [i] = ' ' ; indent [i] = '\0' ; fprintf (fp,"%sTape : %p {\n",indent,(void *) tape) ; if (tape == NULL) { fprintf (fp,"%s}\n",indent) ; return ; } fprintf (fp,"%s master-file : %s\n", indent, tape->handFilename) ; fprintf (fp,"%s input-file : %s\n", indent, tape->inputFilename) ; fprintf (fp,"%s output-file : %s\n",indent, tape->outputFilename) ; fprintf (fp,"%s lock-file : %s\n",indent, tape->lockFilename) ; fprintf (fp,"%s peerName : %s\n",indent,tape->peerName) ; fprintf (fp,"%s input-FILE : %p\n",indent, (void *) tape->inFp) ; fprintf (fp,"%s output-FILE : %p\n",indent, (void *) tape->outFp) ; fprintf (fp,"%s output-limit : %ld\n",indent, tape->outputLowLimit) ; #if 0 fprintf (fp,"%s in-memory article queue (length %d) {\n",indent, tape->qLength) ; for (qe = tape->head ; qe != NULL ; qe = qe->next) { #if 0 printArticleInfo (qe->article,fp,indentAmt + INDENT_INCR) ; #else fprintf (fp,"%s %p\n",indent,qe->article) ; #endif } fprintf (fp,"%s }\n",indent) ; #endif fprintf (fp,"%s tell-position : %ld\n",indent,(long) tape->tellpos) ; fprintf (fp,"%s input-FILE-changed : %s\n",indent, boolToString (tape->changed)) ; fprintf (fp,"%s no-rotate : %s\n",indent, boolToString (tape->noRotate)); fprintf (fp,"%s}\n",indent) ; } /* delete the tape. Spools the in-memory articles to disk. */ void delTape (Tape tape) { struct stat st ; if (tape == NULL) return ; #if 1 if (tape->outFp != NULL && fclose (tape->outFp) != 0) syswarn ("ME ioerr fclose %s", tape->outputFilename) ; if (stat(tape->outputFilename, &st) == 0 && st.st_size == 0) { d_printf (1,"removing empty output tape: %s\n",tape->outputFilename) ; unlink (tape->outputFilename) ; } tape->outFp = NULL ; tape->outputSize = 0 ; #else tapeClose (tape) ; #endif if (tape->inFp != NULL) { checkpointTape (tape) ; fclose (tape->inFp) ; } unlockFile (tape->lockFilename) ; freeCharP (tape->handFilename) ; freeCharP (tape->inputFilename) ; freeCharP (tape->outputFilename) ; freeCharP (tape->lockFilename) ; freeCharP (tape->peerName) ; removeTapeGlobally (tape) ; free (tape) ; } void tapeTakeArticle (Tape tape, Article article) { #if 0 QueueElem elem ; #endif const char *fname, *msgid ; ASSERT (tape != NULL) ; ASSERT (article != NULL) ; /* return immediately if spooling disabled - jgarzik */ if (tape->noBacklog) { delArticle (article) ; return; } fname = artFileName (article) ; msgid = artMsgId (article) ; fprintf (tape->outFp,"%s %s\n", fname, msgid); /* I'd rather know where I am each time, and I don't trust all * fprintf's to give me character counts. Therefore, do not use: * tape->outputSize += (return value of the previous fprintf call); * nor: * tape->outputSize = ftello (tape->outFp); */ tape->outputSize += strlen(fname) + strlen(msgid) + 2 ; /* " " + "\n" */ delArticle (article) ; if (debugShrinking) { struct stat sb ; fflush (tape->outFp) ; if (fstat (fileno (tape->outFp),&sb) != 0) syswarn ("ME oserr fstat %s",tape->outputFilename) ; else if (sb.st_size != tape->outputSize) syslog (LOG_ERR,"fstat and ftello do not agree: %ld %ld for %s\n", (long)sb.st_size,tape->outputSize,tape->outputFilename) ; } if (tape->outputHighLimit > 0 && tape->outputSize >= tape->outputHighLimit) { long oldSize = tape->outputSize ; shrinkfile (tape->outFp,tape->outputLowLimit,tape->outputFilename,"a+"); tape->outputSize = ftello (tape->outFp) ; tape->lossage += oldSize - tape->outputSize ; } } /* Pick an article off a tape and return it. NULL is returned if there are no more articles. */ Article getArticle (Tape tape) { char line [2048] ; /* ick. 1024 for filename + 1024 for msgid */ char *p, *q ; char *msgid, *filename ; Article art = NULL ; time_t now = theTime() ; ASSERT (tape != NULL) ; if (tape->inFp == NULL && (now - tape->lastRotated) > rotatePeriod) prepareFiles (tape) ; /* will flush queue too. */ while (tape->inFp != NULL && art == NULL) { tape->changed = true ; if (fgets (line,sizeof (line), tape->inFp) == NULL) { if (ferror (tape->inFp)) syswarn ("ME ioerr on tape file %s", tape->inputFilename) ; else if ( !feof (tape->inFp) ) syswarn ("ME oserr fgets %s", tape->inputFilename) ; if (fclose (tape->inFp) != 0) syswarn ("ME ioerr fclose %s", tape->inputFilename) ; d_printf (1,"No more articles on tape %s\n",tape->inputFilename) ; tape->inFp = NULL ; tape->scribbled = false ; unlink (tape->inputFilename) ; if ((now - tape->lastRotated) > rotatePeriod) prepareFiles (tape) ; /* rotate files to try next. */ } else { msgid = filename = NULL ; for (p = line ; *p && isspace((unsigned char) *p) ; p++) /* eat whitespace */ /* nada */ ; if (*p != '\0') { q = strchr (p,' ') ; if (q != NULL) { filename = p ; *q = '\0' ; for (q++ ; *q && isspace((unsigned char) *q) ; q++) /* nada */ ; if (*q != '\0') { if (((p = strchr (q, ' ')) != NULL) || ((p = strchr (q, '\n')) != NULL)) *p = '\0' ; if (p != NULL) msgid = q ; else filename = NULL ; /* no trailing newline or blank */ } else filename = NULL ; /* line had one field and some blanks */ } else filename = NULL ; /* line only had one field */ } /* See if message ID looks valid. */ if (msgid) { for (p = msgid; *p; p++) ; if (p > msgid) p--; if (*msgid != '<' || *p != '>') { warn ("ME tape invalid messageID in %s: %s", tape->inputFilename,msgid); msgid = NULL; } } if (filename != NULL && msgid != NULL) art = newArticle (filename, msgid) ; /* art may be NULL here if the file is no longer valid. */ } } #if 0 /* now we either have an article or there is no more on disk */ if (art == NULL) { if (tape->inFp != NULL && ((c = fgetc (tape->inFp)) != EOF)) ungetc (c,tape->inFp) ; /* shouldn't happen */ else if (tape->inFp != NULL) { /* last article read was the end of the tape. */ if (fclose (tape->inFp) != 0) syswarn ("ME ioerr fclose %s", tape->inputFilename) ; tape->inFp = NULL ; tape->scribbled = false ; /* toss out the old input file and prepare the new one */ unlink (tape->inputFilename) ; if (now - tape->lastRotated > rotatePeriod) prepareFiles (tape) ; } } #endif if (art == NULL) d_printf (2,"%s All out of articles in the backlog\n", tape->peerName) ; else d_printf (2,"%s Peeled article %s from backlog\n",tape->peerName, artMsgId (art)) ; return art ; } /****************************************************/ /** CLASS FUNCTIONS **/ /****************************************************/ /* Cause all the Tapes to checkpoint themselves. */ void checkPointTapes (void) { unsigned int i ; for (i = 0 ; i < activeTapeIdx ; i++) checkpointTape (activeTapes [i]) ; } /* make all the tapes set their checkNew flag. */ static void tapesSetCheckNew (void) { unsigned int i ; for (i = 0 ; i < activeTapeIdx ; i++) activeTapes[i]->checkNew = true ; } #if 0 /* Set the pathname of the directory for storing tapes in. */ void setTapeDirectory (const char *newDir) { /* the activeTape variable gets set when the first Tape object is created */ if (activeTapes != NULL) { syslog (LOG_CRIT,"Resetting backlog directory") ; abort() ; } if (tapeDirectory != NULL) freeCharP (tapeDirectory) ; tapeDirectory = xstrdup (newDir) ; addPointerFreedOnExit (tapeDirectory) ; } #endif /* Get the pathname of the directory tapes are stored in. */ const char *getTapeDirectory (void) { ASSERT (tapeDirectory != NULL) ; #if 0 if (tapeDirectory == NULL) { tapeDirectory = xstrdup (dflTapeDir) ; addPointerFreedOnExit (tapeDirectory) ; } #endif return tapeDirectory ; } #if 0 void setOutputSizeLimit (long val) { defaultSizeLimit = val ; } #endif /**********************************************************************/ /* PRIVATE FUNCTIONS */ /**********************************************************************/ /* Add a new tape to the class-level list of active tapes. */ static void addTapeGlobally (Tape tape) { ASSERT (tape != NULL) ; if (activeTapeSize == activeTapeIdx) { unsigned int i ; activeTapeSize += 10 ; if (activeTapes != NULL) activeTapes = xrealloc (activeTapes, sizeof(Tape) * activeTapeSize) ; else activeTapes = xmalloc (sizeof(Tape) * activeTapeSize) ; for (i = activeTapeIdx ; i < activeTapeSize ; i++) activeTapes [i] = NULL ; } activeTapes [activeTapeIdx++] = tape ; } /* Remove a tape for the class-level list of active tapes. */ static void removeTapeGlobally (Tape tape) { unsigned int i ; if (tape == NULL) return ; ASSERT (activeTapeIdx > 0) ; for (i = 0 ; i < activeTapeIdx ; i++) if (activeTapes [i] == tape) break ; ASSERT (i < activeTapeIdx) ; for ( ; i < (activeTapeIdx - 1) ; i++) activeTapes [i] = activeTapes [i + 1] ; activeTapes [--activeTapeIdx] = NULL ; if (activeTapeIdx == 0) { free (activeTapes) ; activeTapes = NULL ; } } /* Have a tape checkpoint itself so that next process can pick up where this one left off. */ static void checkpointTape (Tape tape) { if (tape->inFp == NULL) /* no input file being read. */ return ; if (!tape->changed) /* haven't read since last checkpoint */ { d_printf (1,"Not checkpointing unchanged tape: %s\n", tape->peerName) ; return ; } if ((tape->tellpos = ftello (tape->inFp)) < 0) { syswarn ("ME oserr ftello %s", tape->inputFilename) ; return ; } /* strlen of "18446744073709551616\n" (2^64) */ #define BITS64 21 /* make sure we're not right at the beginning of the file so we can write. */ if (tape->tellpos > BITS64) { rewind (tape->inFp) ; /* scribble blanks over the first lines characters */ if (!tape->scribbled) { int currloc = 0 ; int c ; while ((c = fgetc (tape->inFp)) != '\n' || currloc <= BITS64) if (c == EOF) return ; else currloc++ ; rewind (tape->inFp) ; while (currloc-- > 0) fputc (' ',tape->inFp) ; rewind (tape->inFp) ; fflush (tape->inFp) ; tape->scribbled = true ; } fprintf (tape->inFp,"%ld",tape->tellpos) ; if (fseeko (tape->inFp,tape->tellpos,SEEK_SET) != 0) syswarn ("ME oserr fseeko(%s,%ld,SEEK_SET)",tape->inputFilename, tape->tellpos) ; } tape->changed = false ; } /* Prepare the tape file(s) for input and output */ /* For a given Tape there are * three possible files: PEER.input PEER and * PEER.output. PEER.input and PEER.output are private to * innfeed. PEER is where a sysadmin can drop a file that (s)he * wants to inject into the process. If the first line of the input file * contains only an integer (possibly surrounded by spaces), then this is * taken to be the position to seek to before reading. * * prepareFiles will process them in a manner much like the following shell * commands: * * if [ ! -f PEER.input ]; then * if [ -f PEER ]; then mv PEER PEER.input * elif [ -f PEER.output ]; then mv PEER.output PEER; fi * fi * * At this point PEER.input is opened for reading if it exists. * * The checkpoint file is left as-is unless the PEER.input file * happens to be newer that the checkpoint file. */ static void prepareFiles (Tape tape) { bool inpExists ; bool outExists ; bool newExists ; #if 0 /* flush any in memory articles to disk */ if (tape->head != NULL && tape->outFp != NULL) flushTape(tape) ; #endif tape->tellpos = 0 ; /* First time through, or something external has set checkNew */ if (tape->lastRotated == 0 || tape->checkNew) { newExists = fileExistsP (tape->handFilename) ; if (newExists) notice ("%s new hand-prepared backlog file", tape->peerName) ; } else newExists = false ; if (tape->lastRotated == 0) /* first time here */ { inpExists = fileExistsP (tape->inputFilename) ; outExists = fileExistsP (tape->outputFilename) ; } else { inpExists = (tape->inFp != NULL) ? true : false ; /* can this ever be true?? */ outExists = (tape->outFp != NULL && tape->outputSize > 0) ? true : false ; } /* move the hand-dropped file to the input file if needed. */ if (newExists && !inpExists) { if (rename (tape->handFilename,tape->inputFilename) != 0) syswarn ("ME oserr rename %s, %s", tape->handFilename, tape->inputFilename); else { notice ("%s grabbing external tape file", tape->peerName) ; inpExists = true ; } } /* now move the output file to the input file, if needed and only if in not in NOROTATE mode. */ if (outExists && !inpExists && !tape->noRotate) { if (tape->outFp != NULL) { fclose (tape->outFp) ; tape->outFp = NULL ; } if (rename (tape->outputFilename,tape->inputFilename) != 0) syswarn ("ME oserr rename %s, %s", tape->outputFilename, tape->inputFilename) ; else inpExists = true ; } /* now open up the input file and seek to the proper position. */ if (inpExists) { int c ; long flength ; if ((tape->inFp = fopen (tape->inputFilename,"r+")) == NULL) syswarn ("ME fopen %s", tape->inputFilename) ; else { char buffer [64] ; if (fgets (buffer,sizeof (buffer) - 1, tape->inFp) == NULL) { if (feof (tape->inFp)) { d_printf (1,"Empty input file: %s\n",tape->inputFilename) ; unlink (tape->inputFilename) ; } else syswarn ("ME oserr fgets %s", tape->inputFilename) ; fclose (tape->inFp) ; tape->inFp = NULL ; tape->scribbled = false ; } else { unsigned int len = strlen (buffer) ; long newPos = 0 ; if (len > 0 && buffer [len - 1] == '\n') buffer [--len] = '\0' ; if (len > 0 && strspn (buffer,"0123456789 \n") == len) { if (sscanf (buffer,"%ld",&newPos) == 1) { tape->scribbled = true ; tape->tellpos = newPos ; } } if ((flength = fileLength (fileno (tape->inFp))) < tape->tellpos) { warn ("ME tape short: %s %ld %ld", tape->inputFilename, flength, tape->tellpos) ; tape->tellpos = 0 ; } else if (tape->tellpos == 0) rewind (tape->inFp) ; else if (fseeko (tape->inFp,tape->tellpos - 1,SEEK_SET) != 0) syswarn ("ME oserr fseeko(%s,%ld,SEEK_SET)", tape->inputFilename,tape->tellpos) ; else if ((c = fgetc (tape->inFp)) != '\n') { while (c != EOF && c != '\n') c = fgetc (tape->inFp) ; if (c == EOF) { fclose (tape->inFp) ; unlink (tape->inputFilename) ; tape->inFp = NULL ; tape->scribbled = false ; prepareFiles (tape) ; } else { long oldPos = tape->tellpos ; tape->changed = true ; checkpointTape (tape) ; warn ("ME internal checkpoint line boundary missed:" " %s %ld vs. %ld",tape->inputFilename, tape->tellpos, oldPos) ; } } } } } tape->lastRotated = theTime() ; tape->checkNew = false ; /* now open up the output file. */ if (tape->outFp == NULL) { if ((tape->outFp = fopen (tape->outputFilename,"a+")) == NULL) { syswarn ("ME tape open failed (a+) %s", tape->outputFilename) ; return ; } fseeko (tape->outFp,0,SEEK_END) ; tape->outputSize = ftello (tape->outFp) ; tape->lossage = 0 ; } } static void tapeCkNewFileCbk (TimeoutId id, void *d UNUSED) { ASSERT (id == ckNewFileId) ; ASSERT (tapeCkNewFilePeriod > 0) ; tapesSetCheckNew () ; ckNewFileId = prepareSleep (tapeCkNewFileCbk,tapeCkNewFilePeriod,NULL) ; } /* The timer callback function that will checkpoint all the active tapes. */ static void tapeCheckpointCallback (TimeoutId id, void *d UNUSED) { ASSERT (id == checkPtId) ; ASSERT (tapeCkPtPeriod > 0) ; d_printf (1,"Checkpointing tapes\n") ; checkPointTapes () ; checkPtId = prepareSleep (tapeCheckpointCallback,tapeCkPtPeriod,NULL) ; } #if 0 static void flushTape (Tape tape) { QueueElem elem ; /* flush out queue to disk. */ elem = tape->head ; while (elem != NULL) { tape->head = tape->head->next ; fprintf (tape->outFp,"%s %s\n", artFileName (elem->article), artMsgId (elem->article)) ; delArticle (elem->article) ; free (elem) ; elem = tape->head ; } tape->tail = NULL ; tape->qLength = 0; /* I'd rather know where I am each time, and I don't trust all fprintf's to give me character counts. */ tape->outputSize = ftello (tape->outFp) ; if (debugShrinking) { struct stat buf ; static bool logged = false ; fflush (tape->outFp) ; if (fstat (fileno (tape->outFp),&buf) != 0 && !logged) { syslog (LOG_ERR,FSTAT_FAILURE,tape->outputFilename) ; logged = true ; } else if (buf.st_size != tape->outputSize) { warn ("ME fstat and ftello do not agree for %s", tape->outputFilename) ; logged = true ; } } if (tape->outputHighLimit > 0 && tape->outputSize > tape->outputHighLimit) { shrinkfile (tape->outFp,tape->outputLowLimit,tape->outputFilename,"a+"); tape->outputSize = ftello (tape->outFp) ; } } #endif static void tapeCleanup (void) { free (tapeDirectory) ; tapeDirectory = NULL ; } inn-2.6.0/innfeed/configfile.h0000644000175200017520000000562412575023702015615 0ustar iuliusiulius/* $Id: configfile.h 7084 2004-12-22 20:33:24Z rra $ ** ** Interface to innfeed's configuration file parser. ** ** Written by James Brister */ #if ! defined ( configfile_h__ ) #define configfile_h__ /* pointer to function taking void-star param and returning int. */ typedef int (*PFIVP)(void *) ; typedef enum { intval, charval, boolval, realval, stringval, scopeval } tag ; typedef struct _scope { struct _value *me ; char *scope_type ; int value_count ; int value_idx ; struct _value **values ; struct _scope *parent ; } scope ; typedef struct _value { char *name ; struct _scope *myscope ; tag type ; union { char *charp_val ; char char_val ; double real_val ; int bool_val ; long int_val ; struct _scope *scope_val ; } v ; } value ; extern scope *topScope ; extern char *errbuff ; int isWord (scope *s, const char *name, int inherit) ; int isName (scope *s, const char *name, int inherit) ; int getReal (scope *s, const char *name, double *rval, int inherit) ; int getInteger (scope *s, const char *name, long *rval, int inherit) ; int getBool (scope *s, const char *name, int *rval, int inherit) ; int getString (scope *s, const char *name, char **rval, int inherit) ; int getWord (scope *s, const char *name, char **rval, int inherit) ; void freeScopeTree (scope *s) ; char *addInteger (scope *s, const char *name, long val) ; char *addChar (scope *s, const char *name, char val) ; char *addBoolean (scope *s, const char *name, int val) ; char *addName (scope *s, const char *name, char *val) ; char *addWord (scope *s, const char *name, char *val) ; char *addReal (scope *s, const char *name, double val) ; char *addString (scope *s, const char *name, const char *val) ; scope *findScope (scope *s, const char *name, int mustExist) ; value *findValue (scope *s, const char *name, int inherit) ; value *findPeer (const char *name) ; value *getNextPeer (int *cookie) ; void configAddLoadCallback (PFIVP func,void *arg) ; void configRemoveLoadCallback (PFIVP func) ; int readConfig (const char *file, FILE *errorDest, int justCheck, int dump) ; int buildPeerTable (FILE *fp, scope *currScope); void configCleanup (void) ; #define ARTICLE_TIMEOUT "article-timeout" #define BACKLOG_LIMIT "backlog-limit" #define INITIAL_CONNECTIONS "initial-connections" #define IP_NAME "ip-name" #define MAX_CONNECTIONS "max-connections" #define MAX_QUEUE_SIZE "max-queue-size" #define NO_CHECK_HIGH "no-check-high" #define NO_CHECK_LOW "no-check-low" #define PORT_NUMBER "port-number" #define RESP_TIMEOUT "response-timeout" #define STREAMING "streaming" #define DROP_DEFERRED "drop-deferred" #define ISPEER(V) (ISSCOPE(V) && strcmp ((V)->v.scope_val->scope_type,"peer") == 0) #define ISSCOPE(V) (V->type == scopeval) #define INHERIT 1 #define NO_INHERIT 0 /* Interface between lexer and parser. */ int yylex (void) ; #endif /* configfile_h__ */ inn-2.6.0/innfeed/config_l.c0000644000175200017520000015225712575023702015270 0ustar iuliusiulius #line 3 "lex.yy.c" #define YY_INT_ALIGNED short int /* A lexical scanner generated by flex */ #define FLEX_SCANNER #define YY_FLEX_MAJOR_VERSION 2 #define YY_FLEX_MINOR_VERSION 5 #define YY_FLEX_SUBMINOR_VERSION 35 #if YY_FLEX_SUBMINOR_VERSION > 0 #define FLEX_BETA #endif /* First, we deal with platform-specific or compiler-specific issues. */ /* begin standard C headers. */ #include #include #include #include /* end standard C headers. */ /* flex integer type definitions */ #ifndef FLEXINT_H #define FLEXINT_H /* C99 systems have . Non-C99 systems may or may not. */ #if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, * if you want the limit (max/min) macros for int types. */ #ifndef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS 1 #endif #include typedef int8_t flex_int8_t; typedef uint8_t flex_uint8_t; typedef int16_t flex_int16_t; typedef uint16_t flex_uint16_t; typedef int32_t flex_int32_t; typedef uint32_t flex_uint32_t; #else typedef signed char flex_int8_t; typedef short int flex_int16_t; typedef int flex_int32_t; typedef unsigned char flex_uint8_t; typedef unsigned short int flex_uint16_t; typedef unsigned int flex_uint32_t; /* Limits of integral types. */ #ifndef INT8_MIN #define INT8_MIN (-128) #endif #ifndef INT16_MIN #define INT16_MIN (-32767-1) #endif #ifndef INT32_MIN #define INT32_MIN (-2147483647-1) #endif #ifndef INT8_MAX #define INT8_MAX (127) #endif #ifndef INT16_MAX #define INT16_MAX (32767) #endif #ifndef INT32_MAX #define INT32_MAX (2147483647) #endif #ifndef UINT8_MAX #define UINT8_MAX (255U) #endif #ifndef UINT16_MAX #define UINT16_MAX (65535U) #endif #ifndef UINT32_MAX #define UINT32_MAX (4294967295U) #endif #endif /* ! C99 */ #endif /* ! FLEXINT_H */ #ifdef __cplusplus /* The "const" storage-class-modifier is valid. */ #define YY_USE_CONST #else /* ! __cplusplus */ /* C99 requires __STDC__ to be defined as 1. */ #if defined (__STDC__) #define YY_USE_CONST #endif /* defined (__STDC__) */ #endif /* ! __cplusplus */ #ifdef YY_USE_CONST #define yyconst const #else #define yyconst #endif /* Returned upon end-of-file. */ #define YY_NULL 0 /* Promotes a possibly negative, possibly signed char to an unsigned * integer for use as an array index. If the signed char is negative, * we want to instead treat it as an 8-bit unsigned char, hence the * double cast. */ #define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) /* Enter a start condition. This macro really ought to take a parameter, * but we do it the disgusting crufty way forced on us by the ()-less * definition of BEGIN. */ #define BEGIN (yy_start) = 1 + 2 * /* Translate the current start state into a value that can be later handed * to BEGIN to return to the state. The YYSTATE alias is for lex * compatibility. */ #define YY_START (((yy_start) - 1) / 2) #define YYSTATE YY_START /* Action number for EOF rule of a given start state. */ #define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) /* Special action meaning "start processing a new file". */ #define YY_NEW_FILE yyrestart(yyin ) #define YY_END_OF_BUFFER_CHAR 0 /* Size of default input buffer. */ #ifndef YY_BUF_SIZE #ifdef __ia64__ /* On IA-64, the buffer size is 16k, not 8k. * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. * Ditto for the __ia64__ case accordingly. */ #define YY_BUF_SIZE 32768 #else #define YY_BUF_SIZE 16384 #endif /* __ia64__ */ #endif /* The state buf must be large enough to hold one state per character in the main buffer. */ #define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) #ifndef YY_TYPEDEF_YY_BUFFER_STATE #define YY_TYPEDEF_YY_BUFFER_STATE typedef struct yy_buffer_state *YY_BUFFER_STATE; #endif extern int yyleng; extern FILE *yyin, *yyout; #define EOB_ACT_CONTINUE_SCAN 0 #define EOB_ACT_END_OF_FILE 1 #define EOB_ACT_LAST_MATCH 2 #define YY_LESS_LINENO(n) /* Return all but the first "n" matched characters back to the input stream. */ #define yyless(n) \ do \ { \ /* Undo effects of setting up yytext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ *yy_cp = (yy_hold_char); \ YY_RESTORE_YY_MORE_OFFSET \ (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ YY_DO_BEFORE_ACTION; /* set up yytext again */ \ } \ while ( 0 ) #define unput(c) yyunput( c, (yytext_ptr) ) #ifndef YY_TYPEDEF_YY_SIZE_T #define YY_TYPEDEF_YY_SIZE_T typedef size_t yy_size_t; #endif #ifndef YY_STRUCT_YY_BUFFER_STATE #define YY_STRUCT_YY_BUFFER_STATE struct yy_buffer_state { FILE *yy_input_file; char *yy_ch_buf; /* input buffer */ char *yy_buf_pos; /* current position in input buffer */ /* Size of input buffer in bytes, not including room for EOB * characters. */ yy_size_t yy_buf_size; /* Number of characters read into yy_ch_buf, not including EOB * characters. */ int yy_n_chars; /* Whether we "own" the buffer - i.e., we know we created it, * and can realloc() it to grow it, and should free() it to * delete it. */ int yy_is_our_buffer; /* Whether this is an "interactive" input source; if so, and * if we're using stdio for input, then we want to use getc() * instead of fread(), to make sure we stop fetching input after * each newline. */ int yy_is_interactive; /* Whether we're considered to be at the beginning of a line. * If so, '^' rules will be active on the next match, otherwise * not. */ int yy_at_bol; int yy_bs_lineno; /**< The line count. */ int yy_bs_column; /**< The column count. */ /* Whether to try to fill the input buffer when we reach the * end of it. */ int yy_fill_buffer; int yy_buffer_status; #define YY_BUFFER_NEW 0 #define YY_BUFFER_NORMAL 1 /* When an EOF's been seen but there's still some text to process * then we mark the buffer as YY_EOF_PENDING, to indicate that we * shouldn't try reading from the input source any more. We might * still have a bunch of tokens to match, though, because of * possible backing-up. * * When we actually see the EOF, we change the status to "new" * (via yyrestart()), so that the user can continue scanning by * just pointing yyin at a new input file. */ #define YY_BUFFER_EOF_PENDING 2 }; #endif /* !YY_STRUCT_YY_BUFFER_STATE */ /* Stack of input buffers. */ static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */ static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */ static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */ /* We provide macros for accessing buffer states in case in the * future we want to put the buffer states in a more general * "scanner state". * * Returns the top of the stack, or NULL. */ #define YY_CURRENT_BUFFER ( (yy_buffer_stack) \ ? (yy_buffer_stack)[(yy_buffer_stack_top)] \ : NULL) /* Same as previous macro, but useful when we know that the buffer stack is not * NULL or when we need an lvalue. For internal use only. */ #define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)] /* yy_hold_char holds the character lost when yytext is formed. */ static char yy_hold_char; static int yy_n_chars; /* number of characters read into yy_ch_buf */ int yyleng; /* Points to current character in buffer. */ static char *yy_c_buf_p = (char *) 0; static int yy_init = 0; /* whether we need to initialize */ static int yy_start = 0; /* start state number */ /* Flag which is used to allow yywrap()'s to do buffer switches * instead of setting up a fresh yyin. A bit of a hack ... */ static int yy_did_buffer_switch_on_eof; void yyrestart (FILE *input_file ); void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ); YY_BUFFER_STATE yy_create_buffer (FILE *file,int size ); void yy_delete_buffer (YY_BUFFER_STATE b ); void yy_flush_buffer (YY_BUFFER_STATE b ); void yypush_buffer_state (YY_BUFFER_STATE new_buffer ); void yypop_buffer_state (void ); static void yyensure_buffer_stack (void ); static void yy_load_buffer_state (void ); static void yy_init_buffer (YY_BUFFER_STATE b,FILE *file ); #define YY_FLUSH_BUFFER yy_flush_buffer(YY_CURRENT_BUFFER ) YY_BUFFER_STATE yy_scan_buffer (char *base,yy_size_t size ); YY_BUFFER_STATE yy_scan_string (yyconst char *yy_str ); YY_BUFFER_STATE yy_scan_bytes (yyconst char *bytes,int len ); void *yyalloc (yy_size_t ); void *yyrealloc (void *,yy_size_t ); void yyfree (void * ); #define yy_new_buffer yy_create_buffer #define yy_set_interactive(is_interactive) \ { \ if ( ! YY_CURRENT_BUFFER ){ \ yyensure_buffer_stack (); \ YY_CURRENT_BUFFER_LVALUE = \ yy_create_buffer(yyin,YY_BUF_SIZE ); \ } \ YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ } #define yy_set_bol(at_bol) \ { \ if ( ! YY_CURRENT_BUFFER ){\ yyensure_buffer_stack (); \ YY_CURRENT_BUFFER_LVALUE = \ yy_create_buffer(yyin,YY_BUF_SIZE ); \ } \ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ } #define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) /* Begin user sect3 */ typedef unsigned char YY_CHAR; FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0; typedef int yy_state_type; extern int yylineno; int yylineno = 1; extern char *yytext; #define yytext_ptr yytext static yy_state_type yy_get_previous_state (void ); static yy_state_type yy_try_NUL_trans (yy_state_type current_state ); static int yy_get_next_buffer (void ); static void yy_fatal_error (yyconst char msg[] ); /* Done after the current pattern has been matched and before the * corresponding action - sets up yytext. */ #define YY_DO_BEFORE_ACTION \ (yytext_ptr) = yy_bp; \ yyleng = (size_t) (yy_cp - yy_bp); \ (yy_hold_char) = *yy_cp; \ *yy_cp = '\0'; \ (yy_c_buf_p) = yy_cp; #define YY_NUM_RULES 19 #define YY_END_OF_BUFFER 20 /* This struct is not used in this scanner, but its presence is necessary. */ struct yy_trans_info { flex_int32_t yy_verify; flex_int32_t yy_nxt; }; static yyconst flex_int16_t yy_accept[55] = { 0, 0, 0, 7, 7, 20, 18, 11, 1, 15, 10, 19, 16, 2, 18, 18, 3, 4, 18, 8, 7, 19, 18, 11, 15, 10, 0, 0, 17, 16, 18, 18, 18, 8, 7, 13, 0, 0, 17, 18, 18, 18, 0, 12, 18, 5, 18, 0, 9, 18, 14, 18, 18, 6, 0 } ; static yyconst flex_int32_t yy_ec[256] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 4, 5, 6, 1, 1, 7, 1, 1, 1, 1, 1, 8, 9, 1, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 1, 1, 1, 1, 1, 1, 1, 1, 12, 13, 14, 1, 15, 1, 16, 1, 1, 17, 1, 18, 19, 20, 1, 21, 1, 1, 22, 1, 1, 1, 1, 1, 1, 23, 1, 1, 1, 1, 24, 24, 1, 1, 25, 24, 15, 1, 1, 1, 1, 1, 1, 24, 19, 20, 1, 26, 1, 24, 27, 24, 1, 1, 1, 1, 28, 1, 29, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 } ; static yyconst flex_int32_t yy_meta[30] = { 0, 1, 2, 3, 4, 5, 1, 6, 1, 1, 7, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 7, 7, 1, 7, 1, 1, 1 } ; static yyconst flex_int16_t yy_base[62] = { 0, 0, 100, 28, 30, 105, 0, 102, 107, 0, 0, 80, 25, 107, 15, 23, 0, 0, 86, 0, 99, 107, 0, 98, 0, 0, 92, 32, 88, 34, 78, 24, 78, 0, 87, 107, 78, 75, 65, 18, 25, 57, 54, 107, 43, 0, 45, 54, 0, 38, 107, 37, 33, 0, 107, 51, 58, 65, 72, 79, 86, 88 } ; static yyconst flex_int16_t yy_def[62] = { 0, 54, 1, 55, 55, 54, 56, 54, 54, 57, 58, 59, 56, 54, 56, 56, 56, 56, 56, 60, 54, 54, 56, 54, 57, 58, 54, 61, 56, 56, 56, 56, 56, 60, 54, 54, 54, 54, 56, 56, 56, 56, 54, 54, 56, 56, 56, 54, 56, 56, 54, 56, 56, 56, 0, 54, 54, 54, 54, 54, 54, 54 } ; static yyconst flex_int16_t yy_nxt[137] = { 0, 6, 7, 8, 9, 10, 6, 11, 12, 6, 12, 13, 6, 6, 6, 14, 6, 6, 6, 6, 15, 6, 6, 6, 6, 6, 6, 6, 16, 17, 20, 21, 20, 21, 28, 29, 30, 31, 40, 35, 44, 30, 36, 28, 29, 44, 45, 53, 31, 40, 52, 45, 19, 19, 19, 19, 19, 19, 19, 22, 51, 50, 49, 48, 47, 22, 24, 24, 24, 46, 24, 24, 24, 25, 25, 38, 25, 25, 25, 25, 26, 26, 43, 26, 26, 26, 26, 33, 42, 34, 33, 33, 33, 33, 37, 37, 41, 39, 38, 35, 23, 34, 32, 27, 23, 54, 18, 5, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54 } ; static yyconst flex_int16_t yy_chk[137] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 4, 4, 12, 12, 14, 15, 31, 27, 39, 14, 27, 29, 29, 39, 40, 52, 15, 31, 51, 40, 55, 55, 55, 55, 55, 55, 55, 56, 49, 47, 46, 44, 42, 56, 57, 57, 57, 41, 57, 57, 57, 58, 58, 38, 58, 58, 58, 58, 59, 59, 37, 59, 59, 59, 59, 60, 36, 34, 60, 60, 60, 60, 61, 61, 32, 30, 28, 26, 23, 20, 18, 11, 7, 5, 2, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54 } ; static yy_state_type yy_last_accepting_state; static char *yy_last_accepting_cpos; extern int yy_flex_debug; int yy_flex_debug = 0; /* The intent behind this definition is that it'll catch * any uses of REJECT which flex missed. */ #define REJECT reject_used_but_not_detected #define yymore() yymore_used_but_not_detected #define YY_MORE_ADJ 0 #define YY_RESTORE_YY_MORE_OFFSET char *yytext; #line 1 "configfile.l" #line 2 "configfile.l" /* $Id: configfile.l 9526 2013-08-07 18:37:37Z iulius $ ** ** A flex input file for the innfeed config file. ** ** Written by James Brister */ #include "innfeed.h" #include #include #include #include #include "inn/libinn.h" #include "configfile.h" #include "config_y.h" #include "misc.h" #if ! defined (FLEX_SCANNER) #error "You must use FLEX to process the lex input file." #endif #if defined (FLEX_DEBUG) #define YY_USER_INIT yy_flex_debug = (getenv ("YYDEBUG") == NULL ? 0 : 1) #endif /* We never use this function but flex always defines it, so silence the warnings about it. */ static void yyunput(int, char *) UNUSED; char *strPtr = 0 ; int strPtrLen = 0 ; int strIdx = 0 ; int sawBsl ; int lineCount = 0 ; int current ; static void strAppend (int ch) { if (strIdx == strPtrLen) { if (strPtr == 0) strPtr = xmalloc (strPtrLen = 50) ; else strPtr = xrealloc (strPtr,strPtrLen += 10) ; } strPtr [strIdx++] = ch ; } #define MAX_INCLUDE_DEPTH 11 struct includeFile { YY_BUFFER_STATE state; char *name ; } include_stack[MAX_INCLUDE_DEPTH]; int include_stack_ptr = 0; #line 565 "lex.yy.c" #define INITIAL 0 #define incl 1 #ifndef YY_NO_UNISTD_H /* Special case for "unistd.h", since it is non-ANSI. We include it way * down here because we want the user's section 1 to have been scanned first. * The user has a chance to override it with an option. */ #include #endif #ifndef YY_EXTRA_TYPE #define YY_EXTRA_TYPE void * #endif static int yy_init_globals (void ); /* Accessor methods to globals. These are made visible to non-reentrant scanners for convenience. */ int yylex_destroy (void ); int yyget_debug (void ); void yyset_debug (int debug_flag ); YY_EXTRA_TYPE yyget_extra (void ); void yyset_extra (YY_EXTRA_TYPE user_defined ); FILE *yyget_in (void ); void yyset_in (FILE * in_str ); FILE *yyget_out (void ); void yyset_out (FILE * out_str ); int yyget_leng (void ); char *yyget_text (void ); int yyget_lineno (void ); void yyset_lineno (int line_number ); /* Macros after this point can all be overridden by user definitions in * section 1. */ #ifndef YY_SKIP_YYWRAP #ifdef __cplusplus extern "C" int yywrap (void ); #else extern int yywrap (void ); #endif #endif static void yyunput (int c,char *buf_ptr ); #ifndef yytext_ptr static void yy_flex_strncpy (char *,yyconst char *,int ); #endif #ifdef YY_NEED_STRLEN static int yy_flex_strlen (yyconst char * ); #endif #ifndef YY_NO_INPUT #ifdef __cplusplus static int yyinput (void ); #else static int input (void ); #endif #endif /* Amount of stuff to slurp up with each read. */ #ifndef YY_READ_BUF_SIZE #ifdef __ia64__ /* On IA-64, the buffer size is 16k, not 8k */ #define YY_READ_BUF_SIZE 16384 #else #define YY_READ_BUF_SIZE 8192 #endif /* __ia64__ */ #endif /* Copy whatever the last rule matched to the standard output. */ #ifndef ECHO /* This used to be an fputs(), but since the string might contain NUL's, * we now use fwrite(). */ #define ECHO do { if (fwrite( yytext, yyleng, 1, yyout )) {} } while (0) #endif /* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, * is returned in "result". */ #ifndef YY_INPUT #define YY_INPUT(buf,result,max_size) \ if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ { \ int c = '*'; \ size_t n; \ for ( n = 0; n < max_size && \ (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ buf[n] = (char) c; \ if ( c == '\n' ) \ buf[n++] = (char) c; \ if ( c == EOF && ferror( yyin ) ) \ YY_FATAL_ERROR( "input in flex scanner failed" ); \ result = n; \ } \ else \ { \ errno=0; \ while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \ { \ if( errno != EINTR) \ { \ YY_FATAL_ERROR( "input in flex scanner failed" ); \ break; \ } \ errno=0; \ clearerr(yyin); \ } \ }\ \ #endif /* No semi-colon after return; correct usage is to write "yyterminate();" - * we don't want an extra ';' after the "return" because that will cause * some compilers to complain about unreachable statements. */ #ifndef yyterminate #define yyterminate() return YY_NULL #endif /* Number of entries by which start-condition stack grows. */ #ifndef YY_START_STACK_INCR #define YY_START_STACK_INCR 25 #endif /* Report a fatal error. */ #ifndef YY_FATAL_ERROR #define YY_FATAL_ERROR(msg) yy_fatal_error( msg ) #endif /* end tables serialization structures and prototypes */ /* Default declaration of generated scanner - a define so the user can * easily add parameters. */ #ifndef YY_DECL #define YY_DECL_IS_OURS 1 extern int yylex (void); #define YY_DECL int yylex (void) #endif /* !YY_DECL */ /* Code executed at the beginning of each rule, after yytext and yyleng * have been set up. */ #ifndef YY_USER_ACTION #define YY_USER_ACTION #endif /* Code executed at the end of each rule. */ #ifndef YY_BREAK #define YY_BREAK break; #endif #define YY_RULE_SETUP \ if ( yyleng > 0 ) \ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = \ (yytext[yyleng - 1] == '\n'); \ YY_USER_ACTION /** The main scanner function which does all the work. */ YY_DECL { register yy_state_type yy_current_state; register char *yy_cp, *yy_bp; register int yy_act; #line 66 "configfile.l" #line 759 "lex.yy.c" if ( !(yy_init) ) { (yy_init) = 1; #ifdef YY_USER_INIT YY_USER_INIT; #endif if ( ! (yy_start) ) (yy_start) = 1; /* first start state */ if ( ! yyin ) yyin = stdin; if ( ! yyout ) yyout = stdout; if ( ! YY_CURRENT_BUFFER ) { yyensure_buffer_stack (); YY_CURRENT_BUFFER_LVALUE = yy_create_buffer(yyin,YY_BUF_SIZE ); } yy_load_buffer_state( ); } while ( 1 ) /* loops until end-of-file is reached */ { yy_cp = (yy_c_buf_p); /* Support of yytext. */ *yy_cp = (yy_hold_char); /* yy_bp points to the position in yy_ch_buf of the start of * the current run. */ yy_bp = yy_cp; yy_current_state = (yy_start); yy_current_state += YY_AT_BOL(); yy_match: do { register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)]; if ( yy_accept[yy_current_state] ) { (yy_last_accepting_state) = yy_current_state; (yy_last_accepting_cpos) = yy_cp; } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 55 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; ++yy_cp; } while ( yy_base[yy_current_state] != 107 ); yy_find_action: yy_act = yy_accept[yy_current_state]; if ( yy_act == 0 ) { /* have to back up */ yy_cp = (yy_last_accepting_cpos); yy_current_state = (yy_last_accepting_state); yy_act = yy_accept[yy_current_state]; } YY_DO_BEFORE_ACTION; do_action: /* This label is used only to access EOF actions. */ switch ( yy_act ) { /* beginning of action switch */ case 0: /* must back up */ /* undo the effects of YY_DO_BEFORE_ACTION */ *yy_cp = (yy_hold_char); yy_cp = (yy_last_accepting_cpos); yy_current_state = (yy_last_accepting_state); goto yy_find_action; case 1: /* rule 1 can match eol */ YY_RULE_SETUP #line 68 "configfile.l" lineCount++ ; YY_BREAK case 2: YY_RULE_SETUP #line 70 "configfile.l" { return (COLON) ; } YY_BREAK case 3: YY_RULE_SETUP #line 72 "configfile.l" { return (LBRACE) ; } YY_BREAK case 4: YY_RULE_SETUP #line 74 "configfile.l" { return (RBRACE) ; } YY_BREAK case 5: YY_RULE_SETUP #line 76 "configfile.l" { return (PEER) ; } YY_BREAK case 6: YY_RULE_SETUP #line 78 "configfile.l" BEGIN(incl); YY_BREAK case 7: YY_RULE_SETUP #line 80 "configfile.l" /* eat the whitespace before include filename */ YY_BREAK case 8: YY_RULE_SETUP #line 82 "configfile.l" { if (include_stack_ptr == MAX_INCLUDE_DEPTH - 1) { int i ; fprintf( stderr, "Includes nested too deeply:\n" ); for (i = 1 ; i <= include_stack_ptr ; i++) fprintf (stderr,"\t%s\n",include_stack[i].name) ; syslog (LOG_ERR, "includes nested to deeply") ; exit( 1 ); } if ((yyin = fopen(yytext,"r")) == NULL) { syslog (LOG_CRIT,"include file fopen failed: %s %s", yytext,strerror(errno)); fprintf (stderr,"include file fopen failed: %s %s\n", yytext,strerror(errno)); exit (1) ; } else { d_printf (1,"Including (%d) from %s\n", include_stack_ptr + 1,yytext) ; include_stack[include_stack_ptr].state = YY_CURRENT_BUFFER; include_stack[++include_stack_ptr].name = xstrdup (yytext) ; yy_switch_to_buffer(yy_create_buffer(yyin,YY_BUF_SIZE)); } BEGIN(INITIAL); } YY_BREAK case YY_STATE_EOF(INITIAL): case YY_STATE_EOF(incl): #line 114 "configfile.l" { if ( include_stack_ptr <= 0 ) yyterminate(); else { free (include_stack[include_stack_ptr].name) ; yy_delete_buffer(YY_CURRENT_BUFFER); yy_switch_to_buffer(include_stack[--include_stack_ptr].state); } } YY_BREAK case 9: YY_RULE_SETUP #line 125 "configfile.l" { return (GROUP) ; } YY_BREAK case 10: YY_RULE_SETUP #line 127 "configfile.l" { (void) 0 ; } YY_BREAK case 11: YY_RULE_SETUP #line 129 "configfile.l" { (void) 1 ; } YY_BREAK case 12: YY_RULE_SETUP #line 131 "configfile.l" { switch (yytext[2]) { case '\\': yylval.chr = '\\' ; break ; case 'a': yylval.chr = 007 ; break ; case 'b': yylval.chr = 010 ; break ; case 'f': yylval.chr = 014 ; break ; case 'n': yylval.chr = 012 ; break ; case 'r': yylval.chr = 015 ; break ; case 't': yylval.chr = 011 ; break ; case 'v': yylval.chr = 013 ; break ; } return (CHAR) ; } YY_BREAK case 13: YY_RULE_SETUP #line 144 "configfile.l" { yylval.chr = yytext[1] ; return (CHAR) ; } YY_BREAK case 14: YY_RULE_SETUP #line 146 "configfile.l" { yylval.chr = (char)strtol(&yytext[2], (char **)NULL, 8); return (CHAR) ;} YY_BREAK case 15: /* rule 15 can match eol */ YY_RULE_SETUP #line 149 "configfile.l" {{ size_t i ; for (i = 1, strIdx = 0, sawBsl = 0 ; ; i++) { /* Cast yyleng to size_t because it used to be an int * in flex versions anterior or equal to 2.5.35. * Do not use yyget_leng() here because old flex versions * do not define it. */ if (i < (size_t) yyleng) current = yytext [i] ; else current = input() ; if (current != EOF) { switch (current) { case '\\': if (sawBsl) { strAppend (current) ; sawBsl = 0 ; } else sawBsl = 1 ; break ; case '\n': if (!sawBsl) strAppend(current) ; sawBsl = 0 ; lineCount++ ; break ; case '\"': if (sawBsl) { strAppend (current) ; sawBsl = 0 ; } else { strAppend ('\0') ; yylval.string = strPtr ; strPtr = 0 ; strPtrLen = strIdx = 0 ; return (XSTRING) ; } break ; case 'a': case 'b': case 'f': case 'n': case 'r': case 't': case 'v': if (sawBsl) { switch (current) { case 'a': strAppend (007) ; break ; case 'b': strAppend (010) ; break ; case 'f': strAppend (014) ; break ; case 'n': strAppend (012) ; break ; case 'r': strAppend (015) ; break ; case 't': strAppend (011) ; break ; case 'v': strAppend (013) ; break ; } sawBsl = 0 ; } else strAppend (current) ; break ; default: strAppend (current) ; sawBsl = 0 ; break ; } } else { return (XSTRING) ; } } }} YY_BREAK case 16: YY_RULE_SETUP #line 238 "configfile.l" { yylval.integer = atoi (yytext) ; return (IVAL) ; } YY_BREAK case 17: YY_RULE_SETUP #line 240 "configfile.l" { yylval.real = atof (yytext) ; return (RVAL) ; } YY_BREAK case 18: YY_RULE_SETUP #line 242 "configfile.l" { yylval.name = xstrdup (yytext) ; if (strcasecmp (yylval.name,"false") == 0) return (FALSEBVAL) ; else if (strcasecmp (yylval.name,"true") == 0) return (TRUEBVAL) ; else return (WORD) ; } YY_BREAK case 19: YY_RULE_SETUP #line 252 "configfile.l" ECHO; YY_BREAK #line 1091 "lex.yy.c" case YY_END_OF_BUFFER: { /* Amount of text matched not including the EOB char. */ int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1; /* Undo the effects of YY_DO_BEFORE_ACTION. */ *yy_cp = (yy_hold_char); YY_RESTORE_YY_MORE_OFFSET if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) { /* We're scanning a new file or input source. It's * possible that this happened because the user * just pointed yyin at a new source and called * yylex(). If so, then we have to assure * consistency between YY_CURRENT_BUFFER and our * globals. Here is the right place to do so, because * this is the first action (other than possibly a * back-up) that will match for the new input source. */ (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin; YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; } /* Note that here we test for yy_c_buf_p "<=" to the position * of the first EOB in the buffer, since yy_c_buf_p will * already have been incremented past the NUL character * (since all states make transitions on EOB to the * end-of-buffer state). Contrast this with the test * in input(). */ if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) { /* This was really a NUL. */ yy_state_type yy_next_state; (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state( ); /* Okay, we're now positioned to make the NUL * transition. We couldn't have * yy_get_previous_state() go ahead and do it * for us because it doesn't know how to deal * with the possibility of jamming (and we don't * want to build jamming into it because then it * will run more slowly). */ yy_next_state = yy_try_NUL_trans( yy_current_state ); yy_bp = (yytext_ptr) + YY_MORE_ADJ; if ( yy_next_state ) { /* Consume the NUL. */ yy_cp = ++(yy_c_buf_p); yy_current_state = yy_next_state; goto yy_match; } else { yy_cp = (yy_c_buf_p); goto yy_find_action; } } else switch ( yy_get_next_buffer( ) ) { case EOB_ACT_END_OF_FILE: { (yy_did_buffer_switch_on_eof) = 0; if ( yywrap( ) ) { /* Note: because we've taken care in * yy_get_next_buffer() to have set up * yytext, we can now set up * yy_c_buf_p so that if some total * hoser (like flex itself) wants to * call the scanner after we return the * YY_NULL, it'll still work - another * YY_NULL will get returned. */ (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ; yy_act = YY_STATE_EOF(YY_START); goto do_action; } else { if ( ! (yy_did_buffer_switch_on_eof) ) YY_NEW_FILE; } break; } case EOB_ACT_CONTINUE_SCAN: (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state( ); yy_cp = (yy_c_buf_p); yy_bp = (yytext_ptr) + YY_MORE_ADJ; goto yy_match; case EOB_ACT_LAST_MATCH: (yy_c_buf_p) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)]; yy_current_state = yy_get_previous_state( ); yy_cp = (yy_c_buf_p); yy_bp = (yytext_ptr) + YY_MORE_ADJ; goto yy_find_action; } break; } default: YY_FATAL_ERROR( "fatal flex scanner internal error--no action found" ); } /* end of action switch */ } /* end of scanning one token */ } /* end of yylex */ /* yy_get_next_buffer - try to read in a new buffer * * Returns a code representing an action: * EOB_ACT_LAST_MATCH - * EOB_ACT_CONTINUE_SCAN - continue scanning from current position * EOB_ACT_END_OF_FILE - end of file */ static int yy_get_next_buffer (void) { register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; register char *source = (yytext_ptr); register int number_to_move, i; int ret_val; if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] ) YY_FATAL_ERROR( "fatal flex scanner internal error--end of buffer missed" ); if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) { /* Don't try to fill the buffer, so this is an EOF. */ if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 ) { /* We matched a single character, the EOB, so * treat this as a final EOF. */ return EOB_ACT_END_OF_FILE; } else { /* We matched some text prior to the EOB, first * process it. */ return EOB_ACT_LAST_MATCH; } } /* Try to read more data. */ /* First move last chars to start of buffer. */ number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr)) - 1; for ( i = 0; i < number_to_move; ++i ) *(dest++) = *(source++); if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) /* don't do the read, it's not guaranteed to return an EOF, * just force an EOF */ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0; else { int num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; while ( num_to_read <= 0 ) { /* Not enough room in the buffer - grow it. */ /* just a shorter name for the current buffer */ YY_BUFFER_STATE b = YY_CURRENT_BUFFER; int yy_c_buf_p_offset = (int) ((yy_c_buf_p) - b->yy_ch_buf); if ( b->yy_is_our_buffer ) { int new_size = b->yy_buf_size * 2; if ( new_size <= 0 ) b->yy_buf_size += b->yy_buf_size / 8; else b->yy_buf_size *= 2; b->yy_ch_buf = (char *) /* Include room in for 2 EOB chars. */ yyrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 ); } else /* Can't grow it, we don't own it. */ b->yy_ch_buf = 0; if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "fatal error - scanner input buffer overflow" ); (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset]; num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; } if ( num_to_read > YY_READ_BUF_SIZE ) num_to_read = YY_READ_BUF_SIZE; /* Read in more data. */ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), (yy_n_chars), (size_t) num_to_read ); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); } if ( (yy_n_chars) == 0 ) { if ( number_to_move == YY_MORE_ADJ ) { ret_val = EOB_ACT_END_OF_FILE; yyrestart(yyin ); } else { ret_val = EOB_ACT_LAST_MATCH; YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_EOF_PENDING; } } else ret_val = EOB_ACT_CONTINUE_SCAN; if ((yy_size_t) ((yy_n_chars) + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { /* Extend the array by 50%, plus the number we really need. */ yy_size_t new_size = (yy_n_chars) + number_to_move + ((yy_n_chars) >> 1); YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size ); if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); } (yy_n_chars) += number_to_move; YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR; YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR; (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; return ret_val; } /* yy_get_previous_state - get the state just before the EOB char was reached */ static yy_state_type yy_get_previous_state (void) { register yy_state_type yy_current_state; register char *yy_cp; yy_current_state = (yy_start); yy_current_state += YY_AT_BOL(); for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp ) { register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); if ( yy_accept[yy_current_state] ) { (yy_last_accepting_state) = yy_current_state; (yy_last_accepting_cpos) = yy_cp; } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 55 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; } return yy_current_state; } /* yy_try_NUL_trans - try to make a transition on the NUL character * * synopsis * next_state = yy_try_NUL_trans( current_state ); */ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state ) { register int yy_is_jam; register char *yy_cp = (yy_c_buf_p); register YY_CHAR yy_c = 1; if ( yy_accept[yy_current_state] ) { (yy_last_accepting_state) = yy_current_state; (yy_last_accepting_cpos) = yy_cp; } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 55 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; yy_is_jam = (yy_current_state == 54); return yy_is_jam ? 0 : yy_current_state; } static void yyunput (int c, register char * yy_bp ) { register char *yy_cp; yy_cp = (yy_c_buf_p); /* undo effects of setting up yytext */ *yy_cp = (yy_hold_char); if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) { /* need to shift things up to make room */ /* +2 for EOB chars. */ register int number_to_move = (yy_n_chars) + 2; register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[ YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2]; register char *source = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]; while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) *--dest = *--source; yy_cp += (int) (dest - source); yy_bp += (int) (dest - source); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_buf_size; if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) YY_FATAL_ERROR( "flex scanner push-back overflow" ); } *--yy_cp = (char) c; (yytext_ptr) = yy_bp; (yy_hold_char) = *yy_cp; (yy_c_buf_p) = yy_cp; } #ifndef YY_NO_INPUT #ifdef __cplusplus static int yyinput (void) #else static int input (void) #endif { int c; *(yy_c_buf_p) = (yy_hold_char); if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR ) { /* yy_c_buf_p now points to the character we want to return. * If this occurs *before* the EOB characters, then it's a * valid NUL; if not, then we've hit the end of the buffer. */ if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) /* This was really a NUL. */ *(yy_c_buf_p) = '\0'; else { /* need more input */ int offset = (yy_c_buf_p) - (yytext_ptr); ++(yy_c_buf_p); switch ( yy_get_next_buffer( ) ) { case EOB_ACT_LAST_MATCH: /* This happens because yy_g_n_b() * sees that we've accumulated a * token and flags that we need to * try matching the token before * proceeding. But for input(), * there's no matching to consider. * So convert the EOB_ACT_LAST_MATCH * to EOB_ACT_END_OF_FILE. */ /* Reset buffer status. */ yyrestart(yyin ); /*FALLTHROUGH*/ case EOB_ACT_END_OF_FILE: { if ( yywrap( ) ) return EOF; if ( ! (yy_did_buffer_switch_on_eof) ) YY_NEW_FILE; #ifdef __cplusplus return yyinput(); #else return input(); #endif } case EOB_ACT_CONTINUE_SCAN: (yy_c_buf_p) = (yytext_ptr) + offset; break; } } } c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */ *(yy_c_buf_p) = '\0'; /* preserve yytext */ (yy_hold_char) = *++(yy_c_buf_p); YY_CURRENT_BUFFER_LVALUE->yy_at_bol = (c == '\n'); return c; } #endif /* ifndef YY_NO_INPUT */ /** Immediately switch to a different input stream. * @param input_file A readable stream. * * @note This function does not reset the start condition to @c INITIAL . */ void yyrestart (FILE * input_file ) { if ( ! YY_CURRENT_BUFFER ){ yyensure_buffer_stack (); YY_CURRENT_BUFFER_LVALUE = yy_create_buffer(yyin,YY_BUF_SIZE ); } yy_init_buffer(YY_CURRENT_BUFFER,input_file ); yy_load_buffer_state( ); } /** Switch to a different input buffer. * @param new_buffer The new input buffer. * */ void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ) { /* TODO. We should be able to replace this entire function body * with * yypop_buffer_state(); * yypush_buffer_state(new_buffer); */ yyensure_buffer_stack (); if ( YY_CURRENT_BUFFER == new_buffer ) return; if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ *(yy_c_buf_p) = (yy_hold_char); YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); } YY_CURRENT_BUFFER_LVALUE = new_buffer; yy_load_buffer_state( ); /* We don't actually know whether we did this switch during * EOF (yywrap()) processing, but the only time this flag * is looked at is after yywrap() is called, so it's safe * to go ahead and always set it. */ (yy_did_buffer_switch_on_eof) = 1; } static void yy_load_buffer_state (void) { (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; (yy_hold_char) = *(yy_c_buf_p); } /** Allocate and initialize an input buffer state. * @param file A readable stream. * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. * * @return the allocated buffer state. */ YY_BUFFER_STATE yy_create_buffer (FILE * file, int size ) { YY_BUFFER_STATE b; b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state ) ); if ( ! b ) YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); b->yy_buf_size = size; /* yy_ch_buf has to be 2 characters longer than the size given because * we need to put in 2 end-of-buffer characters. */ b->yy_ch_buf = (char *) yyalloc(b->yy_buf_size + 2 ); if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); b->yy_is_our_buffer = 1; yy_init_buffer(b,file ); return b; } /** Destroy the buffer. * @param b a buffer created with yy_create_buffer() * */ void yy_delete_buffer (YY_BUFFER_STATE b ) { if ( ! b ) return; if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; if ( b->yy_is_our_buffer ) yyfree((void *) b->yy_ch_buf ); yyfree((void *) b ); } #ifndef __cplusplus extern int isatty (int ); #endif /* __cplusplus */ /* Initializes or reinitializes a buffer. * This function is sometimes called more than once on the same buffer, * such as during a yyrestart() or at EOF. */ static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file ) { int oerrno = errno; yy_flush_buffer(b ); b->yy_input_file = file; b->yy_fill_buffer = 1; /* If b is the current buffer, then yy_init_buffer was _probably_ * called from yyrestart() or through yy_get_next_buffer. * In that case, we don't want to reset the lineno or column. */ if (b != YY_CURRENT_BUFFER){ b->yy_bs_lineno = 1; b->yy_bs_column = 0; } b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; errno = oerrno; } /** Discard all buffered characters. On the next scan, YY_INPUT will be called. * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. * */ void yy_flush_buffer (YY_BUFFER_STATE b ) { if ( ! b ) return; b->yy_n_chars = 0; /* We always need two end-of-buffer characters. The first causes * a transition to the end-of-buffer state. The second causes * a jam in that state. */ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; b->yy_buf_pos = &b->yy_ch_buf[0]; b->yy_at_bol = 1; b->yy_buffer_status = YY_BUFFER_NEW; if ( b == YY_CURRENT_BUFFER ) yy_load_buffer_state( ); } /** Pushes the new state onto the stack. The new state becomes * the current state. This function will allocate the stack * if necessary. * @param new_buffer The new state. * */ void yypush_buffer_state (YY_BUFFER_STATE new_buffer ) { if (new_buffer == NULL) return; yyensure_buffer_stack(); /* This block is copied from yy_switch_to_buffer. */ if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ *(yy_c_buf_p) = (yy_hold_char); YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); } /* Only push if top exists. Otherwise, replace top. */ if (YY_CURRENT_BUFFER) (yy_buffer_stack_top)++; YY_CURRENT_BUFFER_LVALUE = new_buffer; /* copied from yy_switch_to_buffer. */ yy_load_buffer_state( ); (yy_did_buffer_switch_on_eof) = 1; } /** Removes and deletes the top of the stack, if present. * The next element becomes the new top. * */ void yypop_buffer_state (void) { if (!YY_CURRENT_BUFFER) return; yy_delete_buffer(YY_CURRENT_BUFFER ); YY_CURRENT_BUFFER_LVALUE = NULL; if ((yy_buffer_stack_top) > 0) --(yy_buffer_stack_top); if (YY_CURRENT_BUFFER) { yy_load_buffer_state( ); (yy_did_buffer_switch_on_eof) = 1; } } /* Allocates the stack if it does not exist. * Guarantees space for at least one push. */ static void yyensure_buffer_stack (void) { int num_to_alloc; if (!(yy_buffer_stack)) { /* First allocation is just for 2 elements, since we don't know if this * scanner will even need a stack. We use 2 instead of 1 to avoid an * immediate realloc on the next call. */ num_to_alloc = 1; (yy_buffer_stack) = (struct yy_buffer_state**)yyalloc (num_to_alloc * sizeof(struct yy_buffer_state*) ); if ( ! (yy_buffer_stack) ) YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*)); (yy_buffer_stack_max) = num_to_alloc; (yy_buffer_stack_top) = 0; return; } if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){ /* Increase the buffer to prepare for a possible push. */ int grow_size = 8 /* arbitrary grow size */; num_to_alloc = (yy_buffer_stack_max) + grow_size; (yy_buffer_stack) = (struct yy_buffer_state**)yyrealloc ((yy_buffer_stack), num_to_alloc * sizeof(struct yy_buffer_state*) ); if ( ! (yy_buffer_stack) ) YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); /* zero only the new slots.*/ memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*)); (yy_buffer_stack_max) = num_to_alloc; } } /** Setup the input buffer state to scan directly from a user-specified character buffer. * @param base the character buffer * @param size the size in bytes of the character buffer * * @return the newly allocated buffer state object. */ YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size ) { YY_BUFFER_STATE b; if ( size < 2 || base[size-2] != YY_END_OF_BUFFER_CHAR || base[size-1] != YY_END_OF_BUFFER_CHAR ) /* They forgot to leave room for the EOB's. */ return 0; b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state ) ); if ( ! b ) YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ b->yy_buf_pos = b->yy_ch_buf = base; b->yy_is_our_buffer = 0; b->yy_input_file = 0; b->yy_n_chars = b->yy_buf_size; b->yy_is_interactive = 0; b->yy_at_bol = 1; b->yy_fill_buffer = 0; b->yy_buffer_status = YY_BUFFER_NEW; yy_switch_to_buffer(b ); return b; } /** Setup the input buffer state to scan a string. The next call to yylex() will * scan from a @e copy of @a str. * @param yystr a NUL-terminated string to scan * * @return the newly allocated buffer state object. * @note If you want to scan bytes that may contain NUL values, then use * yy_scan_bytes() instead. */ YY_BUFFER_STATE yy_scan_string (yyconst char * yystr ) { return yy_scan_bytes(yystr,strlen(yystr) ); } /** Setup the input buffer state to scan the given bytes. The next call to yylex() will * scan from a @e copy of @a bytes. * @param yybytes the byte buffer to scan * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes. * * @return the newly allocated buffer state object. */ YY_BUFFER_STATE yy_scan_bytes (yyconst char * yybytes, int _yybytes_len ) { YY_BUFFER_STATE b; char *buf; yy_size_t n; int i; /* Get memory for full buffer, including space for trailing EOB's. */ n = _yybytes_len + 2; buf = (char *) yyalloc(n ); if ( ! buf ) YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" ); for ( i = 0; i < _yybytes_len; ++i ) buf[i] = yybytes[i]; buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; b = yy_scan_buffer(buf,n ); if ( ! b ) YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" ); /* It's okay to grow etc. this buffer, and we should throw it * away when we're done. */ b->yy_is_our_buffer = 1; return b; } #ifndef YY_EXIT_FAILURE #define YY_EXIT_FAILURE 2 #endif static void yy_fatal_error (yyconst char* msg ) { (void) fprintf( stderr, "%s\n", msg ); exit( YY_EXIT_FAILURE ); } /* Redefine yyless() so it works in section 3 code. */ #undef yyless #define yyless(n) \ do \ { \ /* Undo effects of setting up yytext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ yytext[yyleng] = (yy_hold_char); \ (yy_c_buf_p) = yytext + yyless_macro_arg; \ (yy_hold_char) = *(yy_c_buf_p); \ *(yy_c_buf_p) = '\0'; \ yyleng = yyless_macro_arg; \ } \ while ( 0 ) /* Accessor methods (get/set functions) to struct members. */ /** Get the current line number. * */ int yyget_lineno (void) { return yylineno; } /** Get the input stream. * */ FILE *yyget_in (void) { return yyin; } /** Get the output stream. * */ FILE *yyget_out (void) { return yyout; } /** Get the length of the current token. * */ int yyget_leng (void) { return yyleng; } /** Get the current token. * */ char *yyget_text (void) { return yytext; } /** Set the current line number. * @param line_number * */ void yyset_lineno (int line_number ) { yylineno = line_number; } /** Set the input stream. This does not discard the current * input buffer. * @param in_str A readable stream. * * @see yy_switch_to_buffer */ void yyset_in (FILE * in_str ) { yyin = in_str ; } void yyset_out (FILE * out_str ) { yyout = out_str ; } int yyget_debug (void) { return yy_flex_debug; } void yyset_debug (int bdebug ) { yy_flex_debug = bdebug ; } static int yy_init_globals (void) { /* Initialization is the same as for the non-reentrant scanner. * This function is called from yylex_destroy(), so don't allocate here. */ (yy_buffer_stack) = 0; (yy_buffer_stack_top) = 0; (yy_buffer_stack_max) = 0; (yy_c_buf_p) = (char *) 0; (yy_init) = 0; (yy_start) = 0; /* Defined in main.c */ #ifdef YY_STDINIT yyin = stdin; yyout = stdout; #else yyin = (FILE *) 0; yyout = (FILE *) 0; #endif /* For future reference: Set errno on error, since we are called by * yylex_init() */ return 0; } /* yylex_destroy is for both reentrant and non-reentrant scanners. */ int yylex_destroy (void) { /* Pop the buffer stack, destroying each element. */ while(YY_CURRENT_BUFFER){ yy_delete_buffer(YY_CURRENT_BUFFER ); YY_CURRENT_BUFFER_LVALUE = NULL; yypop_buffer_state(); } /* Destroy the stack itself. */ yyfree((yy_buffer_stack) ); (yy_buffer_stack) = NULL; /* Reset the globals. This is important in a non-reentrant scanner so the next time * yylex() is called, initialization will occur. */ yy_init_globals( ); return 0; } /* * Internal utility routines. */ #ifndef yytext_ptr static void yy_flex_strncpy (char* s1, yyconst char * s2, int n ) { register int i; for ( i = 0; i < n; ++i ) s1[i] = s2[i]; } #endif #ifdef YY_NEED_STRLEN static int yy_flex_strlen (yyconst char * s ) { register int n; for ( n = 0; s[n]; ++n ) ; return n; } #endif void *yyalloc (yy_size_t size ) { return (void *) malloc( size ); } void *yyrealloc (void * ptr, yy_size_t size ) { /* The cast to (char *) in the following accommodates both * implementations that use char* generic pointers, and those * that use void* generic pointers. It works with the latter * because both ANSI C and C++ allow castless assignment from * any pointer type to void*, and deal with argument conversions * as though doing an assignment. */ return (void *) realloc( (char *) ptr, size ); } void yyfree (void * ptr ) { free( (char *) ptr ); /* see yyrealloc() for (char *) cast */ } #define YYTABLES_NAME "yytables" #line 252 "configfile.l" inn-2.6.0/innfeed/article.h0000644000175200017520000000565312575023702015135 0ustar iuliusiulius/* $Id: article.h 6647 2004-01-25 20:06:42Z rra $ ** ** The public interface to articles. ** ** Written by James Brister ** ** The public interface to articles. The articles are implemented via ** reference counting. This interface provides the methods for getting the ** contents of the article. ** ** When an Article is created there's a chance that another copy of it ** already exists. For example if the Article is pulled out of a Tape for a ** particular host it may already be in existance in some other host. This ** class will manage this situation to prevent multiple copies of the article ** being in core. */ #if ! defined ( article_h__ ) #define article_h__ #include #include "misc.h" /* Create a new Article object. FILENAME is the path of the file the */ /* article is in. MSGID is the news message id of the article */ Article newArticle (const char *filename, const char *msgid) ; /* delete the given article. Just decrements refcount and then FREEs if the refcount is 0. */ void delArticle (Article article) ; void gPrintArticleInfo (FILE *fp, unsigned int indentAmt) ; /* print some debugging info about the article. */ void printArticleInfo (Article art, FILE *fp, unsigned int indentAmt) ; /* return true if this article's file still exists. */ bool artFileIsValid (Article article) ; /* return true if we have the article's contents (calling this may trigger the reading off the disk). */ bool artContentsOk (Article article) ; /* increments reference count and returns a copy of article that can be kept (or passed off to someone else) */ Article artTakeRef (Article article) ; /* return the pathname of the file the article is in. */ const char *artFileName (Article article) ; /* return a list of buffers suitable for giving to an endpoint. The return value can (must) be given to freeBufferArray */ Buffer *artGetNntpBuffers (Article article) ; /* return the message id stoed in the article object */ const char *artMsgId (Article article) ; /* return size of the article */ int artSize (Article article) ; /* return the number of buffers that artGetNntpBuffers() would return. */ unsigned int artNntpBufferCount (Article article) ; /* tell the Article class to log (or not) missing articles as they occur. */ void artLogMissingArticles (bool val) ; /* if VAL is true, then when an article is read off disk the necesary carriage returns are inserted instead of setting up iovec-style buffers for writev. Useful for systems like solaris that have very small max number of iovecs that writev can take. Must be called only once before the first article is created. */ void artBitFiddleContents (bool val) ; /* set the limit on the number of bytes in all articles (this is not a hard limit). Can only be called one time before any articles are created. */ void artSetMaxBytesInUse (unsigned int val) ; #endif /* article_h__ */ inn-2.6.0/innfeed/endpoint.c0000644000175200017520000013015212575023702015316 0ustar iuliusiulius/* $Id: endpoint.c 9895 2015-06-14 10:08:21Z iulius $ ** ** The implementation of the innfeed EndPoint object class. ** ** Written by James Brister ** ** The EndPoint class is what gives the illusion (sort of, kind of) of ** threading. Basically it controls a select loop and a set of EndPoint ** objects. Each EndPoint has a file descriptor it is interested in. The ** users of the EndPoint tell the EndPoints to notify them when a read or ** write has been completed (or simple if the file descriptor is read or ** write ready). */ #include "innfeed.h" #include "config.h" #include "clibrary.h" #include "portable/socket.h" #include #include #include #include #include #include #include #ifdef HAVE_LIMITS_H # include #endif #ifdef HAVE_SYS_SELECT_H # include #endif #ifdef HAVE_SYS_TIME_H # include #endif #include #include "inn/innconf.h" #include "inn/messages.h" #include "inn/libinn.h" #include "buffer.h" #include "configfile.h" #include "endpoint.h" #include "host.h" static const char *const timer_name[] = { "idle", "blstats", "stsfile", "newart", "readart", "prepart", "read", "write", "cb" }; #if ! defined (NSIG) #define NSIG 32 #endif /* This is the structure that is the EndPoint */ struct endpoint_s { /* fields for managing multiple reads into the inBuffer. */ Buffer *inBuffer ; /* list of buffers to read into */ unsigned int inBufferIdx ; /* where is list we're at. */ size_t inIndex ; /* where in current read we're at */ size_t inMinLen ; /* minimum amount to read */ size_t inAmtRead ; /* amount read so far */ EndpRWCB inCbk ; /* callback for when read complete */ void *inClientData ; /* callback data */ /* fields for managing multiple writes from the outBuffer */ Buffer *outBuffer ; /* list of buffers to write */ unsigned int outBufferIdx ; /* index into buffer list to start write */ size_t outIndex ; /* where in current buffer we write from */ size_t outSize ; /* total of all the buffers */ size_t outAmtWritten ; /* amount written so far */ EndpRWCB outProgressCbk ; /* callback when done */ EndpRWCB outDoneCbk ; /* callback when done */ void *outClientData ; /* callback data */ EndpWorkCbk workCbk ; /* callback to run if no I/O to do */ void *workData ; /* data for callback */ int myFd ; /* the file descriptor we're handling */ int myErrno ; /* the errno when I/O fails */ double selectHits ; /* indicates how often it's ready */ }; /* A private structure. These hold the information on the timer callbacks. */ typedef struct timerqelem_s { TimeoutId id ; /* the id we gave out */ time_t when ; /* The time the timer should go off */ EndpTCB func ; /* the function to call */ void *data ; /* the client callback data */ struct timerqelem_s *next ; /* next in the queue */ } *TimerElem, TimerElemStruct ; /* set to 1 elsewhere if you want stderr to get what's also being written in doWrite. */ int debugWrites ; extern const char *InputFile ; static EndPoint mainEndPoint ; static bool mainEpIsReg = false ; unsigned int stdioFdMax = MAX_STDIO_FD ; time_t PrivateTime; typedef void (*sigfn) (int) ; static sigfn *sigHandlers ; static volatile sig_atomic_t *sigFlags ; /* private functions */ static IoStatus doRead (EndPoint endp) ; static IoStatus doWrite (EndPoint endp) ; static IoStatus doExcept (EndPoint endp) ; static void pipeHandler (int s) ; static void signalHandler (int s) ; static int hitCompare (const void *v1, const void *v2) ; static void reorderPriorityList (void) ; static TimerElem newTimerElem (TimeoutId i, time_t w, EndpTCB f, void *d) ; static TimeoutId timerElemAdd (time_t when, EndpTCB func, void *data) ; static struct timeval *getTimeout (struct timeval *tout) ; static void doTimeout (void) ; static void handleSignals (void) ; #if 0 static int ff_set (fd_set *set, unsigned int start) ; static int ff_free (fd_set *set, unsigned int start) ; #endif static void endpointCleanup (void) ; /* Private data */ static size_t maxEndPoints ; static EndPoint *endPoints ; /* endpoints indexed on fd */ static EndPoint *priorityList ; /* endpoints indexed on priority */ static int absHighestFd = 0 ; /* never goes down */ static int highestFd = -1 ; static unsigned int endPointCount = 0 ; static unsigned int priorityCount = 0 ; static fd_set rdSet ; static fd_set wrSet ; static fd_set exSet ; static int keepSelecting ; static TimerElem timeoutQueue ; static TimerElem timeoutPool ; static TimeoutId nextId ; static int timeoutQueueLength ; /* Create a new EndPoint and hook it to the give file descriptor. All fields are initialized to appropriate values. On the first time this function is called the global data structs that manages lists of endpoints are intialized. */ static bool inited = false ; EndPoint newEndPoint (int fd) { EndPoint ep ; if (!inited) { inited = true ; atexit (endpointCleanup) ; } if (fd < 0) return NULL ; /* try to dup the fd to a larger number to leave lower values free for broken stdio implementations. */ if (stdioFdMax > 0 && ((unsigned int) fd) <= stdioFdMax) { int newfd = fcntl(fd, F_DUPFD, stdioFdMax + 1); if (newfd >= 0) { d_printf (1,"Dupped fd %d to %d\n",fd,newfd) ; if (close (fd) != 0) syswarn ("ME oserr close (%d)", fd) ; } else { d_printf (1,"Couldn't dup fd %d to above %d\n",fd,stdioFdMax) ; newfd = fd ; } fd = newfd ; } if ((unsigned int) fd >= maxEndPoints) { unsigned int i = maxEndPoints ; maxEndPoints = (((fd + 256) / 256) * 256); /* round up to nearest 256 */ if (endPoints == NULL) { endPoints = xmalloc (sizeof(EndPoint) * maxEndPoints) ; priorityList = xmalloc (sizeof(EndPoint) * maxEndPoints) ; } else { endPoints = xrealloc (endPoints,sizeof(EndPoint) * maxEndPoints) ; priorityList = xrealloc (priorityList, sizeof(EndPoint) * maxEndPoints) ; } for ( ; i < maxEndPoints ; i++) endPoints [i] = priorityList [i] = NULL ; } ASSERT (endPoints [fd] == NULL) ; if (fd > absHighestFd) { #if defined (FD_SETSIZE) if ((unsigned int) fd >= FD_SETSIZE) { warn ("ME fd (%d) looks too big (%d -- FD_SETSIZE)", fd, FD_SETSIZE) ; return NULL ; } #else if (fd > (sizeof (fd_set) * CHAR_BIT)) { warn ("ME fd (%d) looks too big (%d -- sizeof (fd_set) * CHAR_BIT)", fd, (sizeof (fd_set) * CHAR_BIT)) ; return NULL ; } #endif absHighestFd = fd ; } ep = xcalloc (1, sizeof(struct endpoint_s)) ; ep->inBuffer = NULL ; ep->inBufferIdx = 0 ; ep->inIndex = 0 ; ep->inMinLen = 0 ; ep->inAmtRead = 0 ; ep->inCbk = NULL ; ep->inClientData = NULL ; ep->outBuffer = 0 ; ep->outBufferIdx = 0 ; ep->outIndex = 0 ; ep->outSize = 0 ; ep->outProgressCbk = NULL ; ep->outDoneCbk = NULL ; ep->outClientData = NULL ; ep->outAmtWritten = 0 ; ep->workCbk = NULL ; ep->workData = NULL ; ep->myFd = fd ; ep->myErrno = 0 ; ep->selectHits = 0.0 ; endPoints [fd] = ep ; priorityList [priorityCount++] = ep ; endPointCount++ ; highestFd = (fd > highestFd ? fd : highestFd) ; return ep ; } /* Delete the given endpoint. The files descriptor is closed and the two Buffer arrays are released. */ void delEndPoint (EndPoint ep) { unsigned int idx ; if (ep == NULL) return ; ASSERT (endPoints [ep->myFd] == ep) ; if (mainEndPoint == ep) mainEndPoint = NULL ; if (ep->inBuffer != NULL) freeBufferArray (ep->inBuffer) ; if (ep->outBuffer != NULL) freeBufferArray (ep->outBuffer) ; close (ep->myFd) ; /* remove from selectable bits */ FD_CLR (ep->myFd,&rdSet) ; FD_CLR (ep->myFd,&wrSet) ; FD_CLR (ep->myFd,&exSet) ; /* Adjust the global arrays to account for deleted endpoint. */ endPoints [ep->myFd] = NULL ; if (ep->myFd == highestFd) while (highestFd >= 0 && endPoints [highestFd] == NULL) highestFd-- ; for (idx = 0 ; idx < priorityCount ; idx++) if (priorityList [idx] == ep) break ; ASSERT (idx < priorityCount) ; /* i.e. was found */ ASSERT (priorityList [idx] == ep) ; /* redundant */ /* this hole will removed in the reorder routine */ priorityList [idx] = NULL ; endPointCount-- ; free (ep) ; } int endPointFd (EndPoint endp) { ASSERT (endp != NULL) ; return endp->myFd ; } /* Request a read to be done next time there's data. The endpoint ENDP * is what will do the read. BUFFERS is the array of Buffers the data * should go into. FUNC is the callback function to call when the read * is complete. CLIENTDATA is the client data to pass back into the * callback function. MINLEN is the minimum amount of data to * read. If MINLEN is 0 then then BUFFERS must be filled, otherwise at * least MINLEN bytes must be read. * * BUFFERS can be NULL, in which case no read is actually done, but the * callback function will be called still. This is useful for * listening sockets. * * Returns 0 if the read couldn't be prepared (for example if a read * is already outstanding). */ int prepareRead (EndPoint endp, Buffer *buffers, EndpRWCB func, void *clientData, int minlen) { int bufferSizeTotal = 0 ; int idx ; ASSERT (endp != NULL) ; if (endp->inBuffer != NULL || FD_ISSET (endp->myFd,&rdSet)) return 0 ; /* something already there */ for (idx = 0 ; buffers != NULL && buffers [idx] != NULL ; idx++) { size_t bs = bufferSize (buffers [idx]) ; size_t bds = bufferDataSize (buffers [idx]) ; bufferSizeTotal += (bs - bds) ; } endp->inBuffer = buffers ; endp->inBufferIdx = 0 ; endp->inIndex = 0 ; endp->inMinLen = (minlen > 0 ? minlen : bufferSizeTotal) ; endp->inCbk = func ; endp->inAmtRead = 0 ; endp->inClientData = clientData ; FD_SET (endp->myFd, &rdSet) ; if ( InputFile == NULL ) FD_SET (endp->myFd, &exSet) ; return 1 ; } /* Request a write to be done at a later point. ENDP is the EndPoint to * do the write. BUFFERS is the array of Buffers to write from. FUNC is * the function to call when the write is complete. CLIENTDATA is some * data to hand back to the callback function. * * If BUFFERS is NULL, then no write will actually by done, but the * callback function will still be called. This is useful for * connecting sockets. * * Returns 0 if the write couldn't be prepared (like if a write is * still in process. */ int prepareWrite (EndPoint endp, Buffer *buffers, EndpRWCB progress, EndpRWCB done, void *clientData) { int bufferSizeTotal = 0 ; int idx ; ASSERT (endp != NULL) ; if (endp->outBuffer != NULL || FD_ISSET (endp->myFd,&wrSet)) return 0 ; /* something already there */ for (idx = 0 ; buffers != NULL && buffers [idx] != NULL ; idx++) bufferSizeTotal += bufferDataSize (buffers [idx]) ; endp->outBuffer = buffers ; endp->outBufferIdx = 0 ; endp->outIndex = 0 ; endp->outProgressCbk = progress ; endp->outDoneCbk = done ; endp->outClientData = clientData ; endp->outSize = bufferSizeTotal ; endp->outAmtWritten = 0 ; FD_SET (endp->myFd, &wrSet) ; FD_SET (endp->myFd, &exSet) ; return 1 ; } /* Cancel the pending read. */ void cancelRead (EndPoint endp) { FD_CLR (endp->myFd,&rdSet) ; if (!FD_ISSET (endp->myFd, &wrSet)) FD_CLR (endp->myFd,&exSet) ; freeBufferArray (endp->inBuffer) ; endp->inBuffer = NULL ; endp->inBufferIdx = 0 ; endp->inIndex = 0 ; endp->inMinLen = 0 ; endp->inAmtRead = 0 ; endp->inCbk = NULL ; endp->inClientData = NULL ; } /* cancel all pending writes. The first len bytes of the queued write are copied to buffer. The number of bytes copied (if it is less than *len) is copied to len. If no write was outstanding then len will have 0 stored in it. */ void cancelWrite (EndPoint endp, char *buffer UNUSED, size_t *len UNUSED) { FD_CLR (endp->myFd, &wrSet) ; if (!FD_ISSET (endp->myFd, &rdSet)) FD_CLR (endp->myFd, &exSet) ; #if 0 #error XXX need to copy data to buffer and *len #endif freeBufferArray (endp->outBuffer) ; endp->outBuffer = NULL ; endp->outBufferIdx = 0 ; endp->outIndex = 0 ; endp->outProgressCbk = NULL ; endp->outDoneCbk = NULL ; endp->outClientData = NULL ; endp->outSize = 0 ; endp->outAmtWritten = 0 ; } /* queue up a new timeout request. to go off at a specific time. */ TimeoutId prepareWake (EndpTCB func, time_t timeToWake, void *clientData) { TimeoutId id ; time_t now = theTime() ; if (now > timeToWake) return 0 ; id = timerElemAdd (timeToWake,func,clientData) ; #if 0 d_printf (1, "Preparing wake %d at date %ld for %ld seconds\n", (int) id, (long) now, (long) (timeToWake - now)) ; #endif return id ; } /* queue up a new timeout request to off TIMETOSLEEP seconds from now */ TimeoutId prepareSleep (EndpTCB func, int timeToSleep, void *clientData) { time_t now = theTime() ; TimeoutId id ; id = timerElemAdd (now + timeToSleep,func,clientData) ; #if 0 d_printf (1, "Preparing sleep %d at date %ld for %ld seconds\n", (int) id, (long) now, (long) timeToSleep) ; #endif return id ; } /* Updates an existing timeout request to go off TIMETOSLEEP seconds from now, or queues a new request. Always returns a new ID. */ TimeoutId updateSleep (TimeoutId tid, EndpTCB func, int timeToSleep, void *clientData) { if (tid == 0) return prepareSleep (func, timeToSleep, clientData) ; else { /* XXX - quick and dirty but CPU wasteful implementation */ removeTimeout (tid) ; return prepareSleep (func, timeToSleep, clientData) ; } } /* Remove a timeout that was previously prepared. 0 is a legal value that is just ignored. */ bool removeTimeout (TimeoutId tid) { TimerElem n = timeoutQueue ; TimerElem p = NULL ; if (tid == 0) return true ; while (n != NULL && n->id != tid) { p = n ; n = n->next ; } if (n == NULL) return false ; if (p == NULL) /* at the head. */ timeoutQueue = n->next ; else p->next = n->next ; n->next = timeoutPool ; timeoutPool = n ; timeoutQueueLength-- ; return true ; } /* The main routine. This is a near-infinite loop that drives the whole program. */ void Run (void) { fd_set rSet ; fd_set wSet ; fd_set eSet ; keepSelecting = 1 ; xsignal (SIGPIPE, pipeHandler) ; while (keepSelecting) { struct timeval timeout ; struct timeval *twait ; int sval ; unsigned int idx ; bool modifiedTime = false ; twait = getTimeout (&timeout) ; memcpy (&rSet,&rdSet,sizeof (rdSet)) ; memcpy (&wSet,&wrSet,sizeof (wrSet)) ; memcpy (&eSet,&exSet,sizeof (exSet)) ; if (highestFd < 0 && twait == NULL) /* no fds and no timeout */ break ; else if (twait != NULL && (twait->tv_sec != 0 || twait->tv_usec != 0)) { /* if we have any workprocs registered we poll rather than block on the fds */ for (idx = 0 ; idx < priorityCount ; idx++) if (priorityList [idx] != NULL && priorityList [idx]->workCbk != NULL) { modifiedTime = true ; twait->tv_sec = 0 ; twait->tv_usec = 0 ; break ; } } /* calculate host backlog statistics */ TMRstart(TMR_BACKLOGSTATS); gCalcHostBlStat (); TMRstop(TMR_BACKLOGSTATS); TMRstart(TMR_IDLE); sval = select (highestFd + 1, &rSet, &wSet, &eSet, twait) ; TMRstop(TMR_IDLE); timePasses () ; if (innconf->timer != 0 && TMRnow() > innconf->timer * 1000) { TMRsummary ("ME", timer_name); } if (sval == 0 && twait == NULL) die ("No fd's ready and no timeouts") ; else if (sval < 0 && errno == EINTR) { handleSignals () ; } else if (sval < 0) { syswarn ("ME exception: select failed: %d", sval) ; stopRun () ; } else if (sval > 0) { IoStatus rval ; int readyCount = sval ; int endpointsServiced = 1 ; handleSignals() ; for (idx = 0 ; idx < priorityCount ; idx++) { EndPoint ep = priorityList [idx] ; bool specialCheck = false ; if (readyCount > 0 && ep != NULL) { int fd = ep->myFd ; int selectHit = 0, readMiss = 0, writeMiss = 0 ; /* Every SELECT_RATIO times we service an endpoint in this loop we check to see if the mainEndPoint fd is ready to read or write. If so we process it and do the current endpoint next time around. */ if (((endpointsServiced % (SELECT_RATIO + 1)) == 0) && ep != mainEndPoint && mainEndPoint != NULL && !mainEpIsReg) { fd_set trSet, twSet ; struct timeval tw ; int checkRead = FD_ISSET (mainEndPoint->myFd,&rdSet) ; int checkWrite = FD_ISSET (mainEndPoint->myFd,&wrSet) ; endpointsServiced++; if (checkRead || checkWrite) { fd = mainEndPoint->myFd ; tw.tv_sec = tw.tv_usec = 0 ; memset (&trSet,0,sizeof (trSet)) ; memset (&twSet,0,sizeof (twSet)) ; if (checkRead) FD_SET (fd,&trSet) ; if (checkWrite) FD_SET (fd,&twSet) ; sval = select (fd + 1,&trSet,&twSet,0,&tw) ; if (sval > 0) { idx-- ; ep = mainEndPoint ; specialCheck = true ; if (checkRead && FD_ISSET (fd,&trSet)) { FD_SET (fd,&rSet) ; readyCount++ ; } if (checkWrite && FD_ISSET (fd,&twSet)) { FD_SET (fd,&wSet) ; readyCount++ ; } } else if (sval < 0) { syswarn ("ME exception: select failed: %d", sval) ; stopRun () ; return ; } else fd = ep->myFd ; /* back to original fd. */ } } FD_CLR (fd, &exSet) ; if (FD_ISSET (fd,&rSet)) { readyCount-- ; endpointsServiced++ ; selectHit = 1 ; if ((rval = doRead (ep)) != IoIncomplete) { Buffer *buff = ep->inBuffer ; FD_CLR (fd, &rdSet) ; /* incase callback wants to issue read */ ep->inBuffer = NULL ; if (ep->inCbk != NULL) (*ep->inCbk) (ep,rval,buff,ep->inClientData) ; else freeBufferArray (buff) ; } else { if ( InputFile == NULL ) FD_SET (ep->myFd, &exSet) ; } } else if (FD_ISSET(fd,&rdSet)) readMiss = 1; /* get it again as the read callback may have deleted the */ /* endpoint */ if (specialCheck) ep = mainEndPoint ; else ep = priorityList [idx] ; if (readyCount > 0 && ep != NULL && FD_ISSET (fd,&wSet)) { readyCount-- ; endpointsServiced++ ; selectHit = 1 ; if ((rval = doWrite (ep)) != IoIncomplete && rval != IoProgress) { Buffer *buff = ep->outBuffer ; FD_CLR (fd, &wrSet) ; /* incase callback wants to issue a write */ ep->outBuffer = NULL ; if (ep->outDoneCbk != NULL) (*ep->outDoneCbk) (ep,rval,buff,ep->outClientData) ; else freeBufferArray (buff) ; } else if (rval == IoProgress) { Buffer *buff = ep->outBuffer ; if (ep->outProgressCbk != NULL) (*ep->outProgressCbk) (ep,rval,buff,ep->outClientData) ; } else { FD_SET (ep->myFd, &exSet) ; } } else if (FD_ISSET(fd,&wrSet)) writeMiss = 1; /* get it again as the write callback may have deleted the */ /* endpoint */ if (specialCheck) ep = mainEndPoint ; else ep = priorityList [idx] ; if (ep != NULL) { ep->selectHits *= 0.9 ; if (selectHit) ep->selectHits += 1.0 ; else if (readMiss && writeMiss) ep->selectHits -= 1.0 ; } if (readyCount > 0 && ep != NULL && FD_ISSET (fd,&eSet)) doExcept (ep) ; } } reorderPriorityList () ; } else if (sval == 0 && !modifiedTime) doTimeout () ; /* now we're done processing all read fds and/or the timeout(s). Next we do the work callbacks for all the endpoints whose fds weren't ready. */ for (idx = 0 ; idx < priorityCount ; idx++) { EndPoint ep = priorityList [idx] ; if (ep != NULL) { int fd = ep->myFd ; if ( !FD_ISSET (fd,&rSet) && !FD_ISSET (fd,&wSet) ) if (ep->workCbk != NULL) { EndpWorkCbk func = ep->workCbk ; void *data = ep->workData ; ep->workCbk = NULL ; ep->workData = NULL ; TMRstart(TMR_CALLBACK); func (ep,data) ; TMRstop(TMR_CALLBACK); } } } } } void *addWorkCallback (EndPoint endp, EndpWorkCbk cbk, void *data) { void *oldBk = endp->workData ; endp->workCbk = cbk ; endp->workData = data ; return oldBk ; } /* Tell the Run routine to stop next time around. */ void stopRun (void) { keepSelecting = 0 ; } int endPointErrno (EndPoint endp) { return endp->myErrno ; } bool readIsPending (EndPoint endp) { return (endp->inBuffer != NULL ? true : false) ; } bool writeIsPending (EndPoint endp) { return (endp->outBuffer != NULL ? true : false) ; } void setMainEndPoint (EndPoint endp) { struct stat buf ; mainEndPoint = endp ; if (endp->myFd >= 0 && fstat (endp->myFd,&buf) < 0) syslog (LOG_ERR,"Can't fstat mainEndPoint fd (%d): %m", endp->myFd) ; else if (endp->myFd < 0) syslog (LOG_ERR,"Negative fd for mainEndPoint???") ; else mainEpIsReg = (S_ISREG(buf.st_mode) ? true : false) ; } int getMainEndPointFd (void) { return(mainEndPoint->myFd) ; } void freeTimeoutQueue (void) { TimerElem p, n ; p = timeoutQueue ; while (p) { n = p->next ; p->next = timeoutPool ; timeoutPool = p; p = n ; timeoutQueueLength-- ; } } /***********************************************************************/ /* STATIC FUNCTIONS BELOW HERE */ /***********************************************************************/ /* * called when the file descriptor on this endpoint is read ready. */ static IoStatus doRead (EndPoint endp) { int i = 0 ; unsigned int idx ; unsigned int bCount = 0 ; struct iovec *vp = NULL ; Buffer *buffers = endp->inBuffer ; unsigned int currIdx = endp->inBufferIdx ; size_t amt = 0 ; IoStatus rval = IoIncomplete ; TMRstart(TMR_READ); for (i = currIdx ; buffers && buffers [i] != NULL ; i++) bCount++ ; bCount = (bCount > IOV_MAX ? IOV_MAX : bCount) ; /* now set up the iovecs for the readv */ if (bCount > 0) { char *bbase ; size_t bds, bs ; vp = xcalloc (bCount, sizeof(struct iovec)) ; bbase = bufferBase (buffers[currIdx]) ; bds = bufferDataSize (buffers[currIdx]) ; bs = bufferSize (buffers [currIdx]) ; /* inIndex is an index in the virtual array of the read, not directly into the buffer. */ vp[0].iov_base = bbase + bds + endp->inIndex ; vp[0].iov_len = bs - bds - endp->inIndex ; amt = vp[0].iov_len ; for (idx = currIdx + 1 ; idx < bCount ; idx++) { bbase = bufferBase (buffers[idx]) ; bds = bufferDataSize (buffers[idx]) ; bs = bufferSize (buffers [idx]) ; vp [idx].iov_base = bbase ; vp [idx].iov_len = bs - bds ; amt += (bs - bds) ; } i = readv (endp->myFd,vp,(int) bCount) ; if (i > 0) { size_t readAmt = (size_t) i ; endp->inAmtRead += readAmt ; /* check if we filled the first buffer */ if (readAmt >= (size_t) vp[0].iov_len) { /* we did */ bufferIncrDataSize (buffers[currIdx], vp[0].iov_len) ; readAmt -= vp [0].iov_len ; endp->inBufferIdx++ ; } else { endp->inIndex += readAmt ; bufferIncrDataSize (buffers[currIdx], readAmt) ; readAmt = 0 ; } /* now check the rest of the buffers */ for (idx = 1 ; readAmt > 0 ; idx++) { ASSERT (idx < bCount) ; bs = bufferSize (buffers [currIdx + idx]) ; bbase = bufferBase (buffers [currIdx + idx]) ; bds = bufferDataSize (buffers [currIdx + idx]) ; if (readAmt >= (bs - bds)) { bufferSetDataSize (buffers [currIdx + idx],bs) ; readAmt -= bs ; endp->inBufferIdx++ ; } else { endp->inIndex = readAmt ; bufferIncrDataSize (buffers [currIdx + idx], readAmt) ; memset (bbase + bds + readAmt, 0, bs - bds - readAmt) ; readAmt = 0 ; } } if (endp->inAmtRead >= endp->inMinLen) { endp->inIndex = 0 ; rval = IoDone ; } } else if (i < 0 && errno != EINTR && errno != EAGAIN) { endp->myErrno = errno ; rval = IoFailed ; } else if (i < 0 && errno == EINTR) { handleSignals () ; } else if (i == 0) rval = IoEOF ; else /* i < 0 && errno == EAGAIN */ rval = IoIncomplete ; free (vp) ; } else rval = IoDone ; TMRstop(TMR_READ); return rval ; } /* called when the file descriptor on the endpoint is write ready. */ static IoStatus doWrite (EndPoint endp) { unsigned int idx ; int i = 0 ; size_t amt = 0 ; unsigned int bCount = 0 ; struct iovec *vp = NULL ; Buffer *buffers = endp->outBuffer ; unsigned int currIdx = endp->outBufferIdx ; IoStatus rval = IoIncomplete ; TMRstart(TMR_WRITE); for (i = currIdx ; buffers && buffers [i] != NULL ; i++) bCount++ ; bCount = (bCount > IOV_MAX ? IOV_MAX : bCount) ; if (bCount > 0) { vp = xcalloc (bCount, sizeof(struct iovec)) ; vp[0].iov_base = bufferBase (buffers [currIdx]) ; vp[0].iov_base = (char *) vp[0].iov_base + endp->outIndex ; vp[0].iov_len = bufferDataSize (buffers [currIdx]) - endp->outIndex ; amt = vp[0].iov_len ; for (idx = 1 ; idx < bCount ; idx++) { vp [idx].iov_base = bufferBase (buffers [idx + currIdx]) ; vp [idx].iov_len = bufferDataSize (buffers [idx + currIdx]) ; amt += vp[idx].iov_len ; } #if 1 if (debugWrites) { /* nasty mixing, but stderr is unbuffered usually. It's debugging only */ d_printf (5,"About to write this:================================\n") ; writev (2,vp,bCount) ; d_printf (5,"end=================================================\n") ; } #endif ASSERT (endp->myFd >= 0) ; ASSERT (vp != 0) ; ASSERT (bCount > 0) ; i = writev (endp->myFd,vp,(int) bCount) ; if (i > 0) { size_t writeAmt = (size_t) i ; endp->outAmtWritten += writeAmt ; /* now figure out which buffers got completely written */ for (idx = 0 ; writeAmt > 0 ; idx++) { if (writeAmt >= (size_t) vp[idx].iov_len) { endp->outBufferIdx++ ; endp->outIndex = 0 ; writeAmt -= vp [idx].iov_len ; } else { /* this buffer was not completly written */ endp->outIndex += writeAmt ; writeAmt = 0 ; } } if (endp->outAmtWritten == endp->outSize) rval = IoDone ; else rval = IoProgress ; } else if (i < 0 && errno == EINTR) { handleSignals () ; } else if (i < 0 && errno == EAGAIN) { rval = IoIncomplete ; } else if (i < 0) { endp->myErrno = errno ; rval = IoFailed ; } else d_printf (1,"Wrote 0 bytes in doWrite()?\n") ; free (vp) ; } else rval = IoDone ; TMRstop(TMR_WRITE); return rval ; } static IoStatus doExcept (EndPoint endp) { int optval; socklen_t size ; int fd = endPointFd (endp) ; if (getsockopt (fd, SOL_SOCKET, SO_ERROR, (char *) &optval, &size) != 0) syswarn ("ME exception: getsockopt (%d)", fd) ; else if (optval != 0) { errno = optval ; syswarn ("ME exception: fd %d", fd) ; } else syswarn ("ME exception: fd %d: Unknown error", fd) ; #if 0 sleep (5) ; abort () ; #endif /* Not reached */ return IoFailed ; } #if 0 static void endPointPrint (EndPoint ep, FILE *fp) { fprintf (fp,"EndPoint [%p]: fd [%d]\n",(void *) ep, ep->myFd) ; } #endif static void signalHandler (int s) { sigFlags[s] = 1 ; #ifndef HAVE_SIGACTION xsignal (s, signalHandler) ; #endif } static void pipeHandler (int s) { xsignal (s, pipeHandler) ; } /* compare the hit ratio of two endpoint for qsort. We're sorting the endpoints on their relative activity */ static int hitCompare (const void *v1, const void *v2) { const struct endpoint_s *e1 = *((const struct endpoint_s * const *) v1) ; const struct endpoint_s *e2 = *((const struct endpoint_s * const *) v2) ; double e1Hit = e1->selectHits ; double e2Hit = e2->selectHits ; if (e1 == mainEndPoint) return -1 ; else if (e2 == mainEndPoint) return 1 ; else if (e1Hit < e2Hit) return 1 ; else if (e1Hit > e2Hit) return -1 ; return 0 ; } /* We maintain the endpoints in order of the percent times they're ready for read/write when they've been selected. This helps us favour the more active endpoints. */ static void reorderPriorityList (void) { unsigned int i, j ; static int thisTime = 4; /* only sort every 4th time since it's so expensive */ if (--thisTime > 0) return ; thisTime = 4; for (i = j = 0; i < priorityCount; i++) if (priorityList [i] != NULL) { if (i != j) priorityList [j] = priorityList [i] ; j++ ; } for (i = j; i < priorityCount; i++) priorityList [ i ] = NULL; priorityCount = j; qsort (priorityList, (size_t)priorityCount, sizeof (EndPoint), &hitCompare); } #define TIMEOUT_POOL_SIZE ((4096 - 2 * (sizeof (void *))) / (sizeof (TimerElemStruct))) /* create a new timeout data structure properly initialized. */ static TimerElem newTimerElem (TimeoutId i, time_t w, EndpTCB f, void *d) { TimerElem p ; if (timeoutPool == NULL) { unsigned int j ; timeoutPool = xmalloc (sizeof(TimerElemStruct) * TIMEOUT_POOL_SIZE) ; for (j = 0; j < TIMEOUT_POOL_SIZE - 1; j++) timeoutPool[j] . next = &(timeoutPool [j + 1]) ; timeoutPool [TIMEOUT_POOL_SIZE-1] . next = NULL ; } p = timeoutPool ; timeoutPool = timeoutPool->next ; ASSERT (p != NULL) ; p->id = i ; p->when = w ; p->func = f ; p->data = d ; p->next = NULL ; return p ; } /* add a new timeout structure to the global list. */ static TimeoutId timerElemAdd (time_t when, EndpTCB func, void *data) { TimerElem p = newTimerElem (++nextId ? nextId : ++nextId,when,func,data) ; TimerElem n = timeoutQueue ; TimerElem q = NULL ; while (n != NULL && n->when <= when) { q = n ; n = n->next ; } if (n == NULL && q == NULL) /* empty list so put at head */ timeoutQueue = p ; else if (q == NULL) /* put at head of list */ { p->next = timeoutQueue ; timeoutQueue = p ; } else if (n == NULL) /* put at end of list */ q->next = p ; else /* in middle of list */ { p->next = q->next ; q->next = p ; } timeoutQueueLength++ ; return p->id ; } /* Fills in TOUT with the timeout to use on the next call to * select. Returns TOUT. If there is no timeout, then returns NULL. If the * timeout has already passed, then it calls the timeout handling routine * first. */ static struct timeval *getTimeout (struct timeval *tout) { struct timeval *rval = NULL ; if (timeoutQueue != NULL) { time_t now = theTime() ; while (timeoutQueue && now > timeoutQueue->when) doTimeout () ; if (timeoutQueue != NULL && now == timeoutQueue->when) { tout->tv_sec = 0 ; tout->tv_usec = 0 ; rval = tout ; } else if (timeoutQueue != NULL) { tout->tv_sec = timeoutQueue->when - now ; tout->tv_usec = 0 ; rval = tout ; } } return rval ; } static void doTimeout (void) { EndpTCB cbk = timeoutQueue->func ; void *data = timeoutQueue->data ; TimerElem p = timeoutQueue ; TimeoutId tid = timeoutQueue->id ; timeoutQueue = timeoutQueue->next ; p->next = timeoutPool ; timeoutPool = p ; timeoutQueueLength-- ; if (cbk) (*cbk) (tid, data) ; /* call the callback function */ } #if defined (WANT_MAIN) #define BUFF_SIZE 100 static void lineIsWritten (EndPoint ep, IoStatus status, Buffer *buffers, void *d UNUSED) { int i ; if (status == IoDone) d_printf (1,"LINE was written\n") ; else { int oldErrno = errno ; errno = endPointErrno (ep) ; perror ("write failed") ; errno = oldErrno ; } for (i = 0 ; buffers && buffers[i] ; i++) { delBuffer (buffers[i]); } } static void lineIsRead (EndPoint myEp, IoStatus status, Buffer *buffers, void *d UNUSED) { Buffer *writeBuffers, *readBuffers ; Buffer newBuff1, newBuff2 ; Buffer newInputBuffer ; char *data, *p ; size_t len ; if (status == IoFailed) { int oldErrno = errno ; errno = endPointErrno (myEp) ; perror ("read failed") ; errno = oldErrno ; return ; } else if (status == IoEOF) { d_printf (1,"EOF on endpoint.\n") ; delEndPoint (myEp) ; return ; } data = bufferBase (buffers[0]); len = bufferDataSize (buffers[0]); if (data [len - 1] == '\r' || data [len - 1] == '\n') bufferDecrDataSize (buffers[0], 1); if (data [len - 1] == '\r' || data [len - 1] == '\n') bufferDecrDataSize (buffers[0], 1); data [len] = '\0' ; d_printf (1,"Got a line: %s\n", data) ; newBuff1 = newBuffer (len + 50) ; newBuff2 = newBuffer (len + 50) ; newInputBuffer = newBuffer (BUFF_SIZE) ; p = bufferBase (newBuff1) ; strlcpy (p, "Thanks for that \"", bufferSize (newBuff1)) ; bufferSetDataSize (newBuff1,strlen (p)) ; p = bufferBase (newBuff2) ; strlcpy (p,"\" very tasty\n", bufferSize (newBuff2)) ; bufferSetDataSize (newBuff2,strlen (p)) ; writeBuffers = makeBufferArray (newBuff1, buffers[0], newBuff2, NULL); readBuffers = makeBufferArray (newInputBuffer,NULL) ; prepareWrite(myEp, writeBuffers, NULL, lineIsWritten, NULL); prepareRead(myEp, readBuffers, lineIsRead, NULL, 1); } static void printDate (TimeoutId tid, void *data) { char dateString[30]; const time_t t = theTime() ; timeToString (t, dateString, sizeof (dateString)) ; d_printf (1,"Timeout (%d) time now is %ld %s\n", (int) tid, (long) t, dateString) ; if (timeoutQueue == NULL) { int ti = (rand () % 10) + 1 ; prepareSleep (printDate,ti,data) ; } } TimeoutId rm ; static void Timeout (TimeoutId tid, void *data) { static int seeded ; static int howMany ; static int i ; char dateString[30]; const time_t t = theTime() ; if ( !seeded ) { srand (t) ; seeded = 1 ; } timeToString (t, dateString, sizeof (dateString)) ; d_printf (1,"Timeout (%d) time now is %ld %s\n", (int) tid, (long) t, dateString) ; if (timeoutQueue != NULL && timeoutQueue->next != NULL) d_printf (1,"%s timeout id %d\n", (removeTimeout (rm) ? "REMOVED" : "FAILED TO REMOVE"), rm) ; rm = 0 ; howMany = (rand() % 10) + (timeoutQueue == NULL ? 1 : 0) ; for (i = 0 ; i < howMany ; i++ ) { TimeoutId id ; int count = (rand () % 30) + 1 ; id = (i % 2 == 0 ? prepareSleep (Timeout,count,data) : prepareWake (Timeout,t + count,data)) ; if (rm == 0) rm = id ; } } static void newConn (EndPoint ep, IoStatus status UNUSED, Buffer *buffers UNUSED, void *d UNUSED) { EndPoint newEp ; struct sockaddr_in in ; Buffer *readBuffers ; Buffer newBuff = newBuffer (BUFF_SIZE) ; socklen_t len = sizeof (in); int fd ; memset (&in, 0, sizeof (in)) ; fd = accept (ep->myFd, (struct sockaddr *) &in, &len) ; if (fd < 0) { perror ("::accept") ; return ; } newEp = newEndPoint (fd) ; prepareRead(ep, NULL, newConn, NULL, 0); readBuffers = makeBufferArray (newBuff,NULL) ; prepareRead(newEp, readBuffers, lineIsRead, NULL, 1); d_printf (1,"Set up a new connection\n"); } int main (int argc, char **argv) { EndPoint accConn ; struct sockaddr_in accNet ; int accFd = socket (AF_INET,SOCK_STREAM,0) ; unsigned short port = atoi (argc > 1 ? argv[1] : "10000") ; time_t t = theTime() ; char * program = strrchr (argv[0],'/') ; if (!program) program = argv [0] ; else program++ ; ASSERT (accFd >= 0) ; memset (&accNet,0,sizeof (accNet)) ; accNet.sin_family = AF_INET ; accNet.sin_addr.s_addr = htonl (INADDR_ANY) ; accNet.sin_port = htons (port) ; #ifdef LOG_PERROR openlog (program, LOG_PERROR | LOG_PID, LOG_NEWS) ; #else openlog (program, LOG_PID, LOG_NEWS) ; #endif if (bind (accFd, (struct sockaddr *) &accNet, sizeof (accNet)) < 0) { perror ("bind: %m") ; exit (1) ; } listen (accFd,5) ; accConn = newEndPoint (accFd) ; prepareRead(accConn, NULL, newConn, NULL, 0); prepareSleep (Timeout,5,(void *) 0x10) ; t = theTime() ; d_printf (1,"Time now is %s",ctime(&t)) ; prepareWake (printDate,t + 16,NULL) ; Run () ; return 0; } #endif /* WANT_MAIN */ /* Probably doesn't do the right thing for SIGCHLD */ void setSigHandler (int signum, void (*ptr)(int)) { unsigned int i ; if (sigHandlers == NULL) { sigHandlers = xmalloc (sizeof(sigfn) * NSIG) ; sigFlags = xmalloc (sizeof(sig_atomic_t) * NSIG) ; for (i = 0 ; i < NSIG ; i++) { sigHandlers [i] = NULL ; sigFlags [i] = 0 ; } } if (signum >= NSIG) { syslog (LOG_ERR,"ME signal number bigger than NSIG: %d vs %d", signum,NSIG) ; return ; } if (xsignal (signum, signalHandler) == SIG_ERR) die ("signal failed: %s", strerror(errno)) ; sigHandlers[signum] = ptr ; } static void handleSignals (void) { int i ; for (i = 1; i < NSIG; i++) { if (sigFlags[i]) { #if defined(HAVE_SIGACTION) sigset_t set, oset ; if (sigemptyset (&set) != 0 || sigaddset (&set, i) != 0) die ("sigemptyset or sigaddset failed") ; if (sigprocmask (SIG_BLOCK, &set, &oset) != 0) die ("sigprocmask failed: %s", strerror(errno)) ; #else /* hope for the best */ #endif sigFlags[i] = 0; if (sigHandlers[i] != NULL && sigHandlers[i] != SIG_IGN && sigHandlers[i] != SIG_DFL) (sigHandlers[i])(i) ; #if defined(HAVE_SIGACTION) if (sigprocmask (SIG_SETMASK, &oset, (sigset_t *)NULL) != 0) die ("sigprocmask failed: %s", strerror(errno)) ; #else /* hope for the best */ #endif } } } int endpointConfigLoadCbk (void *data) { FILE *fp = (FILE *) data ; long ival ; int rval = 1 ; if (getInteger (topScope,"stdio-fdmax",&ival,NO_INHERIT)) { stdioFdMax = ival ; #if ! defined (FD_SETSIZE) if (stdioFdMax > 0) { logOrPrint (LOG_ERR,fp,NO_STDIO_FDMAX) ; stdioFdMax = 0 ; rval = 0 ; } #else if (stdioFdMax > FD_SETSIZE) { logOrPrint (LOG_ERR,fp, "ME config: value of %s (%ld) in %s is higher" " than maximum of %ld. Using %ld","stdio-fdmax", ival,"global scope", (long) FD_SETSIZE, (long) FD_SETSIZE) ; stdioFdMax = FD_SETSIZE ; rval = 0 ; } #endif } else stdioFdMax = 0 ; return rval ; } #if 0 /* definitely not the fastest, but the most portable way to find the first set bit in a mask */ static int ff_set (fd_set *set,unsigned int start) { unsigned int i ; for (i = start ; i < FD_SETSIZE ; i++) if (FD_ISSET (i,set)) return (int) i ; return -1 ; } static int ff_free (fd_set *set, unsigned int start) { unsigned int i ; for (i = start ; i < FD_SETSIZE ; i++) if (!FD_ISSET (i,set)) return i ; return -1 ; } #endif static void endpointCleanup (void) { free (endPoints) ; free (priorityList) ; free (sigHandlers) ; endPoints = NULL ; priorityList = NULL ; sigHandlers = NULL ; } inn-2.6.0/innfeed/misc.h0000644000175200017520000001217712575023702014444 0ustar iuliusiulius/* $Id: misc.h 9913 2015-07-07 16:31:52Z iulius $ ** ** Miscellaneous utility functions for innfeed. ** ** Written by James Brister */ #if ! defined ( misc_h__ ) #define misc_h__ #include "config.h" #include "portable/macros.h" #include #include /* These typedefs are all here because C is too stupid to let me multiply define typedefs to the same things (as C++ will). Hence I can't redeclare the typedefs to get around recursive header file includes (like host.h and connection.h would need if they contained their own typedefs). */ typedef struct article_s *Article ; /* see article.h */ typedef struct buffer_s *Buffer ; /* see buffer.h */ typedef struct commander_s *Commander ; /* see commander.h */ typedef struct config_s *Config ; /* see config.h */ typedef struct connection_s *Connection ; /* see connection.h */ typedef struct endpoint_s *EndPoint ; /* see endpoint.h */ typedef struct host_s *Host ; /* see host.h */ typedef struct innlistener_s *InnListener ; /* see innlistener.h */ typedef struct tape_s *Tape ; /* see tape.h */ typedef int TimeoutId ; /* see endpoint.h */ typedef enum { /* see endpoint.h */ IoDone, IoIncomplete, IoFailed, IoEOF, IoProgress } IoStatus ; typedef void (*EndpRWCB) (EndPoint e, /* see endpoint.h */ IoStatus i, Buffer *b, void *d) ; typedef void (*EndpTCB) (TimeoutId tid, void *d) ; /* see endpoint.h */ typedef void (*EndpWorkCbk) (EndPoint ep, void *data) ; /* debugging information */ extern unsigned int loggingLevel ; /* if 0 then d_printf is a no-op */ /* used by timeToString with strftime(3) */ extern char *timeToStringFormat ; /* the current count of file desccriptors */ extern unsigned int openfds ; /* if level <= loggingLevel then print */ void d_printf (unsigned int level, const char *fmt, ...) __attribute__((__format__ (printf, 2, 3))); /* for the gethostbyname() error code */ const char *host_err_str (void) ; /* parse a reponse line into it's code and body. *rest will end up pointing into the middle of p */ bool getNntpResponse (char *p, int *code, char **rest) ; /* parse out the first field of a nntp response code as a message-id. Caller must free the return value when done. */ char *getMsgId (const char *p) ; /* pick out the next non-blank string inside PTR. TAIL is set to point at the first blank (or NULL) after the string. Returns a pointer into PTR */ char *findNonBlankString (char *ptr, char **tail) ; /* if fp is not NULL then print to it, otherwise syslog at the level. */ void logOrPrint (int level, FILE *fp, const char *fmt, ...) __attribute__((__format__(printf, 3, 4))); /* Error handling functions for use with warn and die. */ void error_log_stderr_date(int len, const char *fmt, va_list args, int err) __attribute__((__format__(printf, 2, 0))); /* Do cleanup and then abort, for use with die. */ void dump_core(void) __attribute__ ((noreturn)); /* Alternate die that doesn't invoke an error handler. */ void logAndExit (int exitVal, const char *fmt, ...) __attribute__((noreturn, __format__(printf, 2, 3))); /* return true of the file exists and is a regular file */ bool fileExistsP (const char *filename) ; /* return true if file exists and is a directory */ bool isDirectory (const char *filename) ; char *mystrtok (char *string, const char *sep) ; /* remove trailing whitespace */ void trim_ws (char *string) ; /* locks the peer and returns true or returns false */ bool lockPeer (const char *peerName) ; /* return true if the end of string matches tail. */ bool endsIn (const char *string, const char *tail) ; /* scribble over then free up the null-terminated string */ void freeCharP (char *charp) ; /* append the contents of src to dest. src is removed if append if successful */ bool appendFile (const char *dest, const char *src) ; /* return the length of the file reference by the given file descriptor */ long fileLength (int fd) ; bool lockFile (const char *fileName) ; void unlockFile (const char *lockfile) ; /* return true if file1 is older than file2 */ bool isOlder (const char *file1, const char *file2) ; /* converts val into a printable string */ const char *boolToString (bool val) ; /* strftime with "%a %b %d %H:%M:%S %Y" (like ctime without linefeed) */ char* timeToString (time_t time, char* buffer, size_t size) ; /* memory leak checker helper. */ void addPointerFreedOnExit (char *pointerToFree) ; /* string the file opened by FP to the give SIZE. The NEWNAME is the name of the file to have after truncation. FP will be reopened for writing on the new file and will be positioned at the end */ bool shrinkfile (FILE *fp, long size, char *name, const char *mode) ; /* max length of pathname */ long pathMax (const char *pathname) ; #define ASSERT(x) do{if (!(x))die("assertion -- %s -- failed in file %s line %d",#x,__FILE__,__LINE__);}while(0) #define INDENT_INCR 5 #define INDENT_BUFFER_SIZE 80 #if ! defined (MIN) #define MIN(A,B) ((A) < (B) ? (A) : (B)) #endif #endif /* misc_h__ */ inn-2.6.0/innfeed/host.c0000644000175200017520000035303012575023702014455 0ustar iuliusiulius/* $Id: host.c 9897 2015-06-14 10:10:37Z iulius $ ** ** The implementation of the innfeed Host class. ** ** Written by James Brister */ #include "innfeed.h" #include "config.h" #include "clibrary.h" #include "portable/socket.h" #include #include #include #include #include #include #include #include #ifdef HAVE_LIMITS_H # include #endif #include "inn/innconf.h" #include "inn/messages.h" #include "inn/network.h" #include "inn/version.h" #include "inn/libinn.h" #include "article.h" #include "buffer.h" #include "configfile.h" #include "connection.h" #include "endpoint.h" #include "host.h" #include "innlistener.h" #include "tape.h" #define REQ 1 #define NOTREQ 0 #define NOTREQNOADD 2 #define VALUE_OK 0 #define VALUE_TOO_HIGH 1 #define VALUE_TOO_LOW 2 #define VALUE_MISSING 3 #define VALUE_WRONG_TYPE 4 #define METHOD_STATIC 0 #define METHOD_APS 1 #define METHOD_QUEUE 2 #define METHOD_COMBINED 3 /* the limit of number of connections open when a host is set to 0 to mean "infinite" */ #define MAXCON 500 #define MAXCONLIMIT(xx) ((xx==0)?MAXCON:xx) #define BACKLOGFILTER 0.7 #define BACKLOGLWM 20.0 #define BACKLOGHWM 50.0 /* time between retrying blocked hosts in seconds */ #define TRYBLOCKEDHOSTPERIOD 120 extern char *configFile ; extern unsigned int init_reconnect_period; extern unsigned int max_reconnect_period; /* the host keeps a couple lists of these */ typedef struct proc_q_elem { Article article ; struct proc_q_elem *next ; struct proc_q_elem *prev ; time_t whenToRequeue ; } *ProcQElem ; typedef struct host_param_s { char *peerName; char *ipName; char *bindAddr; char *bindAddr6; unsigned int articleTimeout; unsigned int responseTimeout; unsigned int initialConnections; unsigned int absMaxConnections; unsigned int maxChecks; unsigned short portNum; bool forceIPv4; unsigned int closePeriod; unsigned int dynamicMethod; bool wantStreaming; bool dropDeferred; bool minQueueCxn; double lowPassLow; /* as percentages */ double lowPassHigh; double lowPassFilter; unsigned int backlogLimit ; unsigned int backlogLimitHigh ; double backlogFactor ; double dynBacklogFilter ; double dynBacklogLowWaterMark ; double dynBacklogHighWaterMark ; bool backlogFeedFirst ; char *username; char *password; } *HostParams ; struct host_s { InnListener listener ; /* who created me. */ struct sockaddr **ipAddrs ; /* the ip addresses of the remote */ int nextIpAddr ; /* the next ip address to hand out */ Connection *connections ; /* NULL-terminated list of all connections */ bool *cxnActive ; /* true if the corresponding cxn is active */ bool *cxnSleeping ; /* true if the connection is sleeping */ unsigned int maxConnections; /* maximum no of cxns controlled by method */ unsigned int activeCxns ; /* number of connections currently active */ unsigned int sleepingCxns ; /* number of connections currently sleeping */ Connection blockedCxn ; /* the first connection to get the 400 banner*/ Connection notThisCxn ; /* don't offer articles to this connection */ HostParams params; /* Parameters from config file */ bool remoteStreams ; /* true if remote supports streaming */ ProcQElem queued ; /* articles done nothing with yet. */ ProcQElem queuedTail ; ProcQElem processed ; /* articles given to a Connection */ ProcQElem processedTail ; ProcQElem deferred ; /* articles which have been deferred by */ ProcQElem deferredTail ; /* a connection */ TimeoutId statsId ; /* timeout id for stats logging. */ TimeoutId ChkCxnsId ; /* timeout id for dynamic connections */ TimeoutId deferredId ; /* timeout id for deferred articles */ Tape myTape ; bool backedUp ; /* set to true when all cxns are full */ unsigned int backlog ; /* number of arts in `queued' queue */ unsigned int deferLen ; /* number of arts in `deferred' queue */ bool loggedModeOn ; /* true if we logged going into no-CHECK mode */ bool loggedModeOff ; /* true if we logged going out of no-CHECK mode */ bool loggedBacklog ; /* true if we already logged the fact */ bool notifiedChangedRemBlckd ; /* true if we logged a new response 400 */ bool removeOnReload ; /* true if host should be removed at end of * config reload */ bool isDynamic; /* true if host created dynamically */ /* these numbers get reset periodically (after a 'final' logging). */ unsigned int artsOffered ; /* # of articles we offered to remote. */ unsigned int artsOffered_checkpoint ; unsigned int artsAccepted ; /* # of articles succesfully transferred */ unsigned int artsAccepted_checkpoint ; unsigned int artsNotWanted ; /* # of articles remote already had */ unsigned int artsNotWanted_checkpoint ; unsigned int artsRejected ; /* # of articles remote rejected */ unsigned int artsRejected_checkpoint ; unsigned int artsDeferred ; /* # of articles remote asked us to retry */ unsigned int artsDeferred_checkpoint ; unsigned int artsMissing ; /* # of articles whose file was missing. */ unsigned int artsMissing_checkpoint ; unsigned int artsToTape ; /* # of articles given to tape */ unsigned int artsToTape_checkpoint ; unsigned int artsQueueOverflow ; /* # of articles that overflowed `queued' */ unsigned int artsCxnDrop ; /* # of articles caught in dead cxn */ unsigned int artsCxnDrop_checkpoint ; unsigned int artsHostSleep ; /* # of articles spooled by sleeping host */ unsigned int artsHostSleep_checkpoint ; unsigned int artsHostClose ; /* # of articles caught by closing host */ unsigned int artsHostClose_checkpoint ; unsigned int artsFromTape ; /* # of articles we pulled off tape */ unsigned int artsFromTape_checkpoint ; double artsSizeAccepted ; /* size of articles succesfully transferred */ double artsSizeAccepted_checkpoint ; double artsSizeRejected ; /* size of articles remote rejected */ double artsSizeRejected_checkpoint ; /* Dynamic Peerage - MGF */ unsigned int artsProcLastPeriod ; /* # of articles processed in last period */ unsigned int secsInLastPeriod ; /* Number of seconds in last period */ unsigned int lastCheckPoint ; /* total articles at end of last period */ unsigned int lastSentCheckPoint ; /* total articles sent end of last period */ unsigned int lastTotalCheckPoint ; /* total articles total end of last period */ bool maxCxnChk ; /* check for maxConnections */ time_t lastMaxCxnTime ; /* last time a maxConnections increased */ time_t lastChkTime; /* last time a check was made for maxConnect */ unsigned int nextCxnTimeChk ; /* next check for maxConnect */ double backlogFilter; /* IIR filter for size of backlog */ /* These numbers are as above, but for the life of the process. */ unsigned int gArtsOffered ; unsigned int gArtsAccepted ; unsigned int gArtsNotWanted ; unsigned int gArtsRejected ; unsigned int gArtsDeferred ; unsigned int gArtsMissing ; unsigned int gArtsToTape ; unsigned int gArtsQueueOverflow ; unsigned int gArtsCxnDrop ; unsigned int gArtsHostSleep ; unsigned int gArtsHostClose ; unsigned int gArtsFromTape ; double gArtsSizeAccepted ; double gArtsSizeRejected ; unsigned int gCxnQueue ; unsigned int gNoQueue ; time_t firstConnectTime ; /* time of first connect. */ time_t connectTime ; /* the time the first connection was fully set up (MODE STREAM and everything else). */ time_t connectTime_checkpoint ; time_t spoolTime ; /* the time the Host had to revert to spooling articles to tape. */ time_t spoolTime_checkpoint ; time_t lastSpoolTime ; /* the time the last time the Host had to revert to spooling articles to tape. */ time_t nextIpLookup ; /* time of last IP name resolution */ char *blockedReason ; /* what the 400 from the remote says. */ Host next ; /* for global list of hosts. */ unsigned long dlAccum ; /* cumulative deferLen */ unsigned int blNone ; /* number of times the backlog was 0 */ unsigned int blFull ; /* number of times the backlog was full */ unsigned int blQuartile[4] ; /* number of times in each quartile */ unsigned long blAccum ; /* cumulative backlog for computing mean */ unsigned int blCount ; /* the sample count */ }; /* A holder for the info we got out of the config file, but couldn't create the Host object for (normally due to lock-file problems).*/ typedef struct host_holder_s { HostParams params; struct host_holder_s *next ; } *HostHolder ; /* These numbers are as above, but for all hosts over the life of the process. */ long procArtsOffered ; long procArtsAccepted ; long procArtsNotWanted ; long procArtsRejected ; long procArtsDeferred ; long procArtsMissing ; double procArtsSizeAccepted ; double procArtsSizeRejected ; long procArtsToTape ; long procArtsFromTape ; static HostParams defaultParams=NULL; static HostHolder blockedHosts ; /* lists of hosts we can't lock */ static TimeoutId tryBlockedHostsId = 0 ; static time_t lastStatusLog ; /* * Host object private methods. */ static void articleGone (Host host, Connection cxn, Article article) ; static void hostStopSpooling (Host host) ; static void hostStartSpooling (Host host) ; static void hostLogStats (Host host, bool final) ; static void hostStatsTimeoutCbk (TimeoutId tid, void *data) ; static void hostDeferredArtCbk (TimeoutId tid, void *data) ; static void backlogToTape (Host host) ; static void queuesToTape (Host host) ; static bool amClosing (Host host) ; static void hostLogStatus (void) ; static void hostPrintStatus (Host host, FILE *fp) ; static int validateBool (FILE *fp, const char *name, int required, bool setval, scope * sc, unsigned int inh); static int validateReal (FILE *fp, const char *name, double low, double high, int required, double setval, scope * sc, unsigned int inh); static int validateInteger (FILE *fp, const char *name, long low, long high, int required, long setval, scope * sc, unsigned int inh); static HostParams newHostParams(HostParams p); static void freeHostParams(HostParams params); static HostHolder FindBlockedHost(const char *name); static void addBlockedHost(HostParams params); static void tryBlockedHosts(TimeoutId tid, void *data); static Host newHost (InnListener listener, HostParams p); static HostParams getHostInfo (void); static HostParams hostDetails (scope *s, char *name, bool isDefault, FILE *fp); static Host findHostByName (char *name) ; static void hostCleanup (void) ; static void hostAlterMaxConnections(Host host, unsigned int absMaxCxns, unsigned int maxCxns, bool makeConnect); /* article queue management functions */ static Article remHead (ProcQElem *head, ProcQElem *tail) ; static void queueArticle (Article article, ProcQElem *head, ProcQElem *tail, time_t when) ; static bool remArticle (Article article, ProcQElem *head, ProcQElem *tail) ; /* * Host class data */ /* if true then when a Host logs its stats, it has all its connections log theirs too. */ static bool logConnectionStats = (bool) LOG_CONNECTION_STATS ; /* The frequency in seconds with which a Host will log its stats. */ static time_t statsPeriod = STATS_PERIOD ; static time_t statsResetPeriod = STATS_RESET_PERIOD ; static Host gHostList = NULL ; static unsigned int gHostCount = 0 ; static unsigned int maxIpNameLen = 0 ; static unsigned int maxPeerNameLen = 0 ; unsigned int hostHighwater = HOST_HIGHWATER ; static time_t start ; static char startTime [30] ; /* for timeToString */ static pid_t myPid ; static char *statusFile = NULL ; static unsigned int dnsRetPeriod ; static unsigned int dnsExpPeriod ; bool genHtml = false ; /*******************************************************************/ /* PUBLIC FUNCTIONS */ /*******************************************************************/ /* function called when the config file is loaded */ int hostConfigLoadCbk (void *data) { int rval = 1, bval ; long iv ; FILE *fp = (FILE *) data ; char *p ; d_printf(1,"hostConfigLoadCbk\n"); if (defaultParams) { freeHostParams(defaultParams); defaultParams=NULL; } /* get optional global defaults */ if (getInteger (topScope,"dns-retry",&iv,NO_INHERIT)) { if (iv < 1) { rval = 0 ; logOrPrint (LOG_ERR,fp, "ME config: value of %s (%ld) in %s cannot be less" " than 1. Using %ld","dns-retry", iv,"global scope",(long)DNS_RETRY_PERIOD) ; iv = DNS_RETRY_PERIOD ; } } else iv = DNS_RETRY_PERIOD ; dnsRetPeriod = (unsigned int) iv ; if (getInteger (topScope,"dns-expire",&iv,NO_INHERIT)) { if (iv < 1) { rval = 0 ; logOrPrint (LOG_ERR,fp, "ME config: value of %s (%ld) in %s cannot be less" " than 1. Using %ld","dns-expire",iv, "global scope",(long)DNS_EXPIRE_PERIOD) ; iv = DNS_EXPIRE_PERIOD ; } } else iv = DNS_EXPIRE_PERIOD ; dnsExpPeriod = (unsigned int) iv ; if (getBool (topScope,"gen-html",&bval,NO_INHERIT)) genHtml = (bval ? true : false) ; else genHtml = GEN_HTML ; if (getString (topScope,"status-file",&p,NO_INHERIT)) { hostSetStatusFile (p) ; free (p) ; } else hostSetStatusFile (INNFEED_STATUS) ; if (getBool (topScope,"connection-stats",&bval,NO_INHERIT)) logConnectionStats = (bval ? true : false) ; else logConnectionStats = (LOG_CONNECTION_STATS ? true : false) ; if (getInteger (topScope,"host-queue-highwater", &iv,NO_INHERIT)) { if (iv < 0) { rval = 0 ; logOrPrint (LOG_ERR,fp, "ME config: value of %s (%ld) in %s cannot be less" " than 0. Using %ld","host-queue-highwater", iv,"global scope",(long) HOST_HIGHWATER) ; iv = HOST_HIGHWATER ; } } else iv = HOST_HIGHWATER ; hostHighwater = (unsigned int) iv ; if (getInteger (topScope,"stats-period",&iv,NO_INHERIT)) { if (iv < 0) { rval = 0 ; logOrPrint (LOG_ERR,fp, "ME config: value of %s (%ld) in %s cannot be less" " than 0. Using %ld","stats-period", iv,"global scope",(long)STATS_PERIOD) ; iv = STATS_PERIOD ; } } else iv = STATS_PERIOD ; statsPeriod = (unsigned int) iv ; if (getInteger (topScope,"stats-reset",&iv,NO_INHERIT)) { if (iv < 0) { rval = 0 ; logOrPrint (LOG_ERR,fp, "ME config: value of %s (%ld) in %s cannot be less" " than 0. Using %ld","stats-reset",iv, "global scope",(long)STATS_RESET_PERIOD) ; iv = STATS_RESET_PERIOD ; } } else iv = STATS_RESET_PERIOD ; statsResetPeriod = (unsigned int) iv ; defaultParams=hostDetails(topScope, NULL, true, fp); ASSERT(defaultParams!=NULL); return rval ; } /* * make a new HostParams structure copying an existing one * or from compiled defaults */ static HostParams newHostParams(HostParams p) { HostParams params; params = xmalloc (sizeof(struct host_param_s)) ; if (p != NULL) { /* Copy old stuff in */ memcpy ((char *) params, (char *) p, sizeof(struct host_param_s)); if (params->peerName) params->peerName = xstrdup(params->peerName); if (params->ipName) params->ipName = xstrdup(params->ipName); if (params->bindAddr) params->bindAddr = xstrdup(params->bindAddr); if (params->bindAddr6) params->bindAddr6 = xstrdup(params->bindAddr6); } else { /* Fill in defaults */ params->peerName=NULL; params->ipName=NULL; params->bindAddr=NULL; params->bindAddr6=NULL; params->articleTimeout=ARTTOUT; params->responseTimeout=RESPTOUT; params->initialConnections=INIT_CXNS; params->absMaxConnections=MAX_CXNS; params->maxChecks=MAX_Q_SIZE; params->portNum=PORTNUM; params->forceIPv4=FORCE_IPv4; params->closePeriod=CLOSE_PERIOD; params->dynamicMethod=METHOD_COMBINED; params->wantStreaming=STREAM; params->dropDeferred=false; params->minQueueCxn=false; params->lowPassLow=NOCHECKLOW; params->lowPassHigh=NOCHECKHIGH; params->lowPassFilter=FILTERVALUE; params->backlogLimit=BLOGLIMIT; params->backlogLimitHigh=BLOGLIMIT_HIGH ; params->backlogFactor=LIMIT_FUDGE ; params->dynBacklogFilter = BACKLOGFILTER ; params->dynBacklogLowWaterMark = BACKLOGLWM; params->dynBacklogHighWaterMark = BACKLOGHWM; params->backlogFeedFirst=false; params->username=NULL; params->password=NULL; } return (params); } /* * Free up a param structure */ static void freeHostParams(HostParams params) { ASSERT(params != NULL); if (params->peerName) free (params->peerName) ; if (params->ipName) free (params->ipName) ; if (params->bindAddr) free (params->bindAddr) ; if (params->bindAddr6) free (params->bindAddr6) ; free (params) ; } static void hostReconfigure(Host h, HostParams params) { unsigned int i; double oldBacklogFilter ; if (strcmp(h->params->ipName, params->ipName) != 0) { free (h->params->ipName) ; h->params->ipName = xstrdup (params->ipName) ; h->nextIpLookup = theTime () ; } /* Put in new parameters Unfortunately we can't blat on top of absMaxConnections as we need to do some resizing here */ ASSERT (h->params != NULL); oldBacklogFilter = h->params->dynBacklogFilter; i = h->params->absMaxConnections; /* keep old value */ /* Use this set of params and allocate, and free * up the old */ freeHostParams(h->params); h->params = params; h->params->absMaxConnections = i; /* restore old value */ /* If the backlog filter value has changed, reset the * filter as the value therein will be screwy */ if (h->params->dynBacklogFilter != oldBacklogFilter) h->backlogFilter = ((h->params->dynBacklogLowWaterMark + h->params->dynBacklogHighWaterMark) /200.0 /(1.0-h->params->dynBacklogFilter)); /* We call this anyway - it does nothing if the values * haven't changed. This is because doing things like * just changing "dynamic-method" requires this call * to be made */ hostAlterMaxConnections(h, h->params->absMaxConnections, h->params->absMaxConnections, false); for ( i = 0 ; i < MAXCONLIMIT(h->params->absMaxConnections) ; i++ ) if (h->connections[i] != NULL) cxnSetCheckThresholds (h->connections[i], h->params->lowPassLow, h->params->lowPassHigh, h->params->lowPassFilter) ; /* XXX how to handle initCxns change? */ } void configHosts (bool talkSelf) { Host nHost, h, q ; HostHolder hh, hi ; HostParams params; /* Remove the current blocked host list */ for (hh = blockedHosts, hi = NULL ; hh != NULL ; hh = hi) { freeHostParams(hh->params); hi = hh->next ; free (hh) ; } blockedHosts = NULL ; closeDroppedArticleFile () ; openDroppedArticleFile () ; while ((params = getHostInfo ()) !=NULL ) { h = findHostByName (params->peerName) ; /* We know the host isn't blocked as we cleared the blocked list */ /* Have we already got this host up and running ?*/ if ( h != NULL ) { hostReconfigure(h, params); h->removeOnReload = false ; /* Don't remove at the end */ } else { /* It's a host we haven't seen from the config file before */ nHost = newHost (mainListener, params); if (nHost == NULL) { addBlockedHost(params); warn ("ME locked cannot setup peer %s", params->peerName) ; } else { if (params->initialConnections == 0 && talkSelf) notice ("%s config ignored batch mode with initial" " connection count of 0", params->peerName) ; if ( !listenerAddPeer (mainListener,nHost) ) die ("failed to add a new peer\n") ; } } } for (h = gHostList; h != NULL; h = q) { q = h->next ; if (h->removeOnReload) { if (h->isDynamic) { /* change to the new default parameters */ params = newHostParams(defaultParams); ASSERT(params->peerName == NULL); ASSERT(params->ipName == NULL); ASSERT(h->params->peerName != NULL); ASSERT(h->params->ipName != NULL); params->peerName = xstrdup(h->params->peerName); params->ipName = xstrdup(h->params->ipName); hostReconfigure(h, params); h->removeOnReload = true; } else hostClose (h) ; /* h may be deleted in here. */ } else /* prime it for the next config file read */ h->removeOnReload = true ; } hostLogStatus () ; } static void hostAlterMaxConnections(Host host, unsigned int absMaxCxns, unsigned int maxCxns, bool makeConnect) { unsigned int lAbsMaxCxns; unsigned int i; /* Fix 0 unlimited case */ lAbsMaxCxns = MAXCONLIMIT(absMaxCxns); /* Don't accept 0 for maxCxns */ maxCxns=MAXCONLIMIT(maxCxns); if ( host->params->dynamicMethod == METHOD_STATIC) { /* If running static, ignore the maxCxns passed in, we'll just use absMaxCxns */ maxCxns = lAbsMaxCxns; } if ( maxCxns > lAbsMaxCxns) { /* ensure maxCxns is of the correct form */ maxCxns = lAbsMaxCxns; } if ((maxCxns < host->maxConnections) && (host->connections != NULL)) { /* We are going to have to nuke some connections, as the current max is now greater than the new max */ for ( i = host->maxConnections ; i > maxCxns ; i-- ) { /* XXX this is harsh, and arguably there could be a cleaner way of doing it. the problem being addressed by doing it this way is that eventually a connection closed cleanly via cxnClose can end up ultimately calling hostCxnDead after h->maxConnections has been lowered and the relevant arrays downsized. If trashing the old, unallocated space in hostCxnDead doesn't kill the process, the ASSERT against h->maxConnections surely will. */ if (host->connections[i - 1] != NULL) { cxnLogStats (host->connections [i-1], true) ; cxnNuke (host->connections[i-1]) ; host->connections[i-1] = NULL; } } host->maxConnections = maxCxns ; } if (host->connections) for (i = host->maxConnections ; i <= MAXCONLIMIT(host->params->absMaxConnections) ; i++) { /* Ensure we've got an empty values only beyond the maxConnection water mark. */ ASSERT (host->connections[i] == NULL); } if ((lAbsMaxCxns != MAXCONLIMIT(host->params->absMaxConnections)) || (host->connections == NULL)) { /* we need to change the size of the connection array */ if (host->connections == NULL) { /* not yet allocated */ host->connections = xcalloc (lAbsMaxCxns + 1, sizeof(Connection)) ; ASSERT (host->cxnActive == NULL); host->cxnActive = xcalloc (lAbsMaxCxns, sizeof(bool)) ; ASSERT (host->cxnSleeping == NULL) ; host->cxnSleeping = xcalloc (lAbsMaxCxns, sizeof(bool)) ; for (i = 0 ; i < lAbsMaxCxns ; i++) { host->connections [i] = NULL ; host->cxnActive[i] = false ; host->cxnSleeping[i] = false ; } host->connections[lAbsMaxCxns] = NULL; } else { host->connections = xrealloc (host->connections, sizeof(Connection) * (lAbsMaxCxns + 1)); host->cxnActive = xrealloc (host->cxnActive, sizeof(bool) * lAbsMaxCxns) ; host->cxnSleeping = xrealloc (host->cxnSleeping, sizeof(bool) * lAbsMaxCxns) ; if (lAbsMaxCxns > MAXCONLIMIT(host->params->absMaxConnections)) { for (i = MAXCONLIMIT(host->params->absMaxConnections) ; i < lAbsMaxCxns ; i++) { host->connections[i+1] = NULL; /* array always 1 larger */ host->cxnActive[i] = false ; host->cxnSleeping[i] = false ; } } } host->params->absMaxConnections = absMaxCxns; } /* if maximum was raised, establish the new connexions (but don't start using them). */ if ( maxCxns > host->maxConnections) { i = host->maxConnections ; /* need to set host->maxConnections before cxnWait() */ host->maxConnections = maxCxns; while ( i < maxCxns ) { host->cxnActive [i] = false ; host->cxnSleeping [i] = false ; /* create a new connection */ host->connections [i] = newConnection (host, i, host->params->ipName, host->params->articleTimeout, host->params->portNum, host->params->responseTimeout, host->params->closePeriod, host->params->lowPassLow, host->params->lowPassHigh, host->params->lowPassFilter) ; /* connect if low enough numbered, or we were forced to */ if ((i < host->params->initialConnections) || makeConnect) cxnConnect (host->connections [i]) ; else cxnWait (host->connections [i]) ; i++ ; } } } /* * Find a host on the blocked host list */ static HostHolder FindBlockedHost(const char *name) { HostHolder hh = blockedHosts; while (hh != NULL) if ((hh->params) && (hh->params->peerName) && (strcmp(name,hh->params->peerName) == 0)) return hh; else hh=hh->next; return NULL; } static void addBlockedHost(HostParams params) { HostHolder hh; hh = xmalloc (sizeof(struct host_holder_s)) ; /* Use this set of params */ hh->params = params; hh->next = blockedHosts ; blockedHosts = hh ; } /* * We iterate through the blocked host list and try and reconnect ones * where we couldn't get a lock */ static void tryBlockedHosts(TimeoutId tid UNUSED , void *data UNUSED ) { HostHolder hh,hi; HostParams params; hh = blockedHosts; /* Get start of our queue */ blockedHosts = NULL ; /* remove them all from the queue of hosts */ while (hh != NULL) { params = hh->params; hi= hh->next; free(hh); hh = hi; if (params && params->peerName) { if (findHostByName(params->peerName)!=NULL) { /* Wierd, someone's managed to start it when it's on * the blocked list. Just silently discard. */ freeHostParams(params); } else { Host nHost; nHost = newHost (mainListener, params); if (nHost == NULL) { addBlockedHost(params); warn ("ME locked cannot setup peer %s", params->peerName) ; } else { d_printf(1,"Unblocked host %s\n",params->peerName); if (params->initialConnections == 0 && listenerIsDummy(mainListener) /*talk to self*/) notice ("%s config ignored batch mode with initial" " connection count of 0", params->peerName) ; if ( !listenerAddPeer (mainListener,nHost) ) die ("failed to add a new peer\n") ; } } } } tryBlockedHostsId = prepareSleep(tryBlockedHosts, TRYBLOCKEDHOSTPERIOD, NULL); } /* * Create a new Host object with default parameters. Called by the * InnListener. */ Host newDefaultHost (InnListener listener, const char *name) { HostParams p; Host h = NULL; if (FindBlockedHost(name)==NULL) { p=newHostParams(defaultParams); ASSERT(p!=NULL); /* relies on fact listener and names are null in default*/ p->peerName=xstrdup(name); p->ipName=xstrdup(name); h=newHost (listener,p); if (h==NULL) { /* Couldn't get a lock - add to list of blocked peers */ addBlockedHost(p); warn ("ME locked cannot setup peer %s", p->peerName); return NULL; } h->isDynamic = true; h->removeOnReload = true; notice ("ME unconfigured peer %s added", p->peerName) ; } return h; } /* * Create a new host and attach the supplied param structure */ static bool inited = false ; static Host newHost (InnListener listener, HostParams p) { Host nh ; ASSERT (p->maxChecks > 0) ; if (!inited) { inited = true ; atexit (hostCleanup) ; } /* * Once only, init the first blocked host check */ if (tryBlockedHostsId==0) tryBlockedHostsId = prepareSleep(tryBlockedHosts, TRYBLOCKEDHOSTPERIOD, NULL); nh = xcalloc (1, sizeof(struct host_s)) ; nh->params = p; nh->listener = listener; nh->connections = NULL; /* We'll get these allocated later */ nh->cxnActive = NULL; nh->cxnSleeping = NULL; nh->activeCxns = 0 ; nh->sleepingCxns = 0 ; nh->blockedCxn = NULL ; nh->notThisCxn = NULL ; nh->queued = NULL ; nh->queuedTail = NULL ; nh->processed = NULL ; nh->processedTail = NULL ; nh->deferred = NULL ; nh->deferredTail = NULL ; nh->statsId = 0 ; nh->ChkCxnsId = 0 ; nh->deferredId = 0; nh->myTape = newTape (nh->params->peerName, listenerIsDummy (nh->listener)) ; if (nh->myTape == NULL) { /* tape couldn't be locked, probably */ free (nh->connections) ; free (nh->cxnActive) ; free (nh->cxnSleeping) ; free (nh) ; return NULL ; /* note we don't free up p */ } nh->backedUp = false ; nh->backlog = 0 ; nh->deferLen = 0 ; nh->loggedBacklog = false ; nh->loggedModeOn = false ; nh->loggedModeOff = false ; nh->notifiedChangedRemBlckd = false ; nh->removeOnReload = false ; /* ready for config file reload */ nh->isDynamic = false ; nh->artsOffered = 0 ; nh->artsOffered_checkpoint = 0 ; nh->artsAccepted = 0 ; nh->artsAccepted_checkpoint = 0 ; nh->artsNotWanted = 0 ; nh->artsNotWanted_checkpoint = 0 ; nh->artsRejected = 0 ; nh->artsRejected_checkpoint = 0 ; nh->artsDeferred = 0 ; nh->artsDeferred_checkpoint = 0 ; nh->artsMissing = 0 ; nh->artsMissing_checkpoint = 0 ; nh->artsToTape = 0 ; nh->artsToTape_checkpoint = 0 ; nh->artsQueueOverflow = 0 ; nh->artsCxnDrop = 0 ; nh->artsCxnDrop_checkpoint = 0 ; nh->artsHostSleep = 0 ; nh->artsHostSleep_checkpoint = 0 ; nh->artsHostClose = 0 ; nh->artsHostClose_checkpoint = 0 ; nh->artsFromTape = 0 ; nh->artsFromTape_checkpoint = 0 ; nh->artsSizeAccepted = 0 ; nh->artsSizeAccepted_checkpoint = 0 ; nh->artsSizeRejected = 0 ; nh->artsSizeRejected_checkpoint = 0 ; nh->artsProcLastPeriod = 0; nh->secsInLastPeriod = 0; nh->lastCheckPoint = 0; nh->lastSentCheckPoint = 0; nh->lastTotalCheckPoint = 0; nh->maxCxnChk = true; nh->lastMaxCxnTime = time(0); nh->lastChkTime = time(0); nh->nextCxnTimeChk = 30; nh->backlogFilter = ((nh->params->dynBacklogLowWaterMark + nh->params->dynBacklogHighWaterMark) /200.0 /(1.0-nh->params->dynBacklogFilter)); nh->gArtsOffered = 0 ; nh->gArtsAccepted = 0 ; nh->gArtsNotWanted = 0 ; nh->gArtsRejected = 0 ; nh->gArtsDeferred = 0 ; nh->gArtsMissing = 0 ; nh->gArtsToTape = 0 ; nh->gArtsQueueOverflow = 0 ; nh->gArtsCxnDrop = 0 ; nh->gArtsHostSleep = 0 ; nh->gArtsHostClose = 0 ; nh->gArtsFromTape = 0 ; nh->gArtsSizeAccepted = 0 ; nh->gArtsSizeRejected = 0 ; nh->gCxnQueue = 0 ; nh->gNoQueue = 0 ; nh->firstConnectTime = 0 ; nh->connectTime = 0 ; nh->connectTime_checkpoint = 0 ; nh->spoolTime = 0 ; nh->spoolTime_checkpoint = 0 ; nh->blNone = 0 ; nh->blFull = 0 ; nh->blQuartile[0] = nh->blQuartile[1] = nh->blQuartile[2] = nh->blQuartile[3] = 0 ; nh->dlAccum = 0; nh->blAccum = 0; nh->blCount = 0; nh->maxConnections = 0; /* we currently have no connections allocated */ /* Note that the following will override the initialCxns specified as maxCxns if we are on non-dyamic feed */ hostAlterMaxConnections(nh, nh->params->absMaxConnections, nh->params->initialConnections, false); nh->next = gHostList ; gHostList = nh ; gHostCount++ ; if (maxIpNameLen == 0) { start = theTime() ; timeToString (start, startTime, sizeof (startTime)) ; myPid = getpid() ; } if (strlen (nh->params->ipName) > maxIpNameLen) maxIpNameLen = strlen (nh->params->ipName) ; if (strlen (nh->params->peerName) > maxPeerNameLen) maxPeerNameLen = strlen (nh->params->peerName) ; return nh ; } struct sockaddr *hostIpAddr (Host host) { int i ; struct sockaddr **newIpAddrPtrs = NULL; struct sockaddr_storage *newIpAddrs = NULL; struct sockaddr *returnAddr; ASSERT(host->params != NULL); /* check to see if need to look up the host name */ if (host->nextIpLookup <= theTime()) { int gai_ret; struct addrinfo *res, *p; struct addrinfo hints; char port[20]; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; #ifdef HAVE_INET6 if (host->params->bindAddr && strcmp(host->params->bindAddr, "none") == 0) hints.ai_family = AF_INET6; if (host->params->bindAddr6 && strcmp(host->params->bindAddr6, "none") == 0) hints.ai_family = AF_INET; #endif hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_NUMERICSERV | AI_ADDRCONFIG; snprintf(port, sizeof(port), "%hu", host->params->portNum); gai_ret = getaddrinfo(host->params->ipName, port, &hints, &res); if (gai_ret != 0 || res == NULL) { warn ("%s can't resolve hostname %s: %s", host->params->peerName, host->params->ipName, gai_ret == 0 ? "no addresses returned" : gai_strerror(gai_ret)) ; } else { /* figure number of pointers that need space */ i = 0; for ( p = res ; p ; p = p->ai_next ) ++i; newIpAddrPtrs = (struct sockaddr **) xmalloc ( (i + 1) * sizeof(struct sockaddr *) ); newIpAddrs = (struct sockaddr_storage *) xmalloc ( i * sizeof(struct sockaddr_storage) ); i = 0; /* copy the addresses from the getaddrinfo linked list */ for( p = res ; p ; p = p->ai_next ) { memcpy( &newIpAddrs[i], p->ai_addr, p->ai_addrlen ); newIpAddrPtrs[i] = (struct sockaddr *)(&newIpAddrs[i]); ++i; } newIpAddrPtrs[i] = NULL ; freeaddrinfo( res ); } if (newIpAddrs) { if (host->ipAddrs) { if(host->ipAddrs[0]) free (host->ipAddrs[0]); free (host->ipAddrs) ; } host->ipAddrs = newIpAddrPtrs ; host->nextIpAddr = 0 ; host->nextIpLookup = theTime () + dnsExpPeriod ; } else { /* failed to setup new addresses */ host->nextIpLookup = theTime () + dnsRetPeriod ; } } if (host->ipAddrs) returnAddr = host->ipAddrs[host->nextIpAddr] ; else returnAddr = NULL ; return returnAddr ; } /* * Delete IPv4 addresses from the address list. */ void hostDeleteIpv4Addr (Host host) { int i, j; if (!host->ipAddrs) return; for (i = 0, j = 0; host->ipAddrs[i]; i++) { if (host->ipAddrs[i]->sa_family != AF_INET) host->ipAddrs[j++] = host->ipAddrs[i]; } host->ipAddrs[j] = 0; if (host->nextIpAddr >= j) host->nextIpAddr = 0; } void hostIpFailed (Host host) { if (host->ipAddrs) if (host->ipAddrs[++host->nextIpAddr] == NULL) host->nextIpAddr = 0 ; } void gPrintHostInfo (FILE *fp, unsigned int indentAmt) { Host h ; char indent [INDENT_BUFFER_SIZE] ; unsigned int i ; for (i = 0 ; i < MIN(INDENT_BUFFER_SIZE - 1,indentAmt) ; i++) indent [i] = ' ' ; indent [i] = '\0' ; fprintf (fp,"%sGlobal Host list : (count %d) {\n",indent,gHostCount) ; for (h = gHostList ; h != NULL ; h = h->next) printHostInfo (h,fp,indentAmt + INDENT_INCR) ; fprintf (fp,"%s}\n",indent) ; } void printHostInfo (Host host, FILE *fp, unsigned int indentAmt) { char dateString [30] ; char indent [INDENT_BUFFER_SIZE] ; unsigned int i ; ProcQElem qe ; double cnt = (host->blCount) ? (host->blCount) : 1.0; for (i = 0 ; i < MIN(INDENT_BUFFER_SIZE - 1,indentAmt) ; i++) indent [i] = ' ' ; indent [i] = '\0' ; fprintf (fp,"%sHost : %p {\n",indent,(void *) host) ; if (host == NULL) { fprintf (fp,"%s}\n",indent) ; return ; } fprintf (fp,"%s peer-name : %s\n",indent,host->params->peerName) ; fprintf (fp,"%s ip-name : %s\n",indent,host->params->ipName) ; fprintf (fp,"%s bindaddress : %s\n",indent, host->params->bindAddr ? host->params->bindAddr : "any") ; fprintf (fp,"%s bindaddress6 : %s\n",indent, host->params->bindAddr6 ? host->params->bindAddr6 : "any") ; fprintf (fp,"%s abs-max-connections : %d\n",indent, host->params->absMaxConnections) ; fprintf (fp,"%s active-connections : %d\n",indent,host->activeCxns) ; fprintf (fp,"%s sleeping-connections : %d\n",indent,host->sleepingCxns) ; fprintf (fp,"%s initial-connections : %d\n",indent, host->params->initialConnections) ; fprintf (fp,"%s want-streaming : %s\n",indent, boolToString (host->params->wantStreaming)) ; fprintf (fp,"%s drop-deferred : %s\n",indent, boolToString (host->params->dropDeferred)) ; fprintf (fp,"%s min-queue-connection : %s\n",indent, boolToString (host->params->minQueueCxn)) ; fprintf (fp,"%s remote-streams : %s\n",indent, boolToString (host->remoteStreams)) ; fprintf (fp,"%s max-checks : %d\n",indent,host->params->maxChecks) ; fprintf (fp,"%s article-timeout : %d\n",indent, host->params->articleTimeout) ; fprintf (fp,"%s response-timeout : %d\n",indent, host->params->responseTimeout) ; fprintf (fp,"%s close-period : %d\n",indent, host->params->closePeriod) ; fprintf (fp,"%s port : %d\n",indent,host->params->portNum) ; fprintf (fp,"%s dynamic-method : %d\n",indent, host->params->dynamicMethod) ; fprintf (fp,"%s dynamic-backlog-filter : %2.1f\n",indent, host->params->dynBacklogFilter) ; fprintf (fp,"%s dynamic-backlog-lwm : %2.1f\n",indent, host->params->dynBacklogLowWaterMark) ; fprintf (fp,"%s dynamic-backlog-hwm : %2.1f\n",indent, host->params->dynBacklogHighWaterMark) ; fprintf (fp,"%s no-check on : %2.1f\n",indent, host->params->lowPassHigh) ; fprintf (fp,"%s no-check off : %2.1f\n",indent, host->params->lowPassLow) ; fprintf (fp,"%s no-check filter : %2.1f\n",indent, host->params->lowPassFilter) ; fprintf (fp,"%s backlog-limit : %d\n",indent, host->params->backlogLimit) ; fprintf (fp,"%s backlog-limit-highwater : %d\n",indent, host->params->backlogLimitHigh) ; fprintf (fp,"%s backlog-factor : %2.1f\n",indent, host->params->backlogFactor) ; fprintf (fp,"%s max-connections : %d\n",indent, host->maxConnections) ; fprintf (fp,"%s backlog-feed-first : %s\n",indent, boolToString (host->params->backlogFeedFirst)) ; fprintf (fp,"%s statistics-id : %d\n",indent,host->statsId) ; fprintf (fp,"%s ChkCxns-id : %d\n",indent,host->ChkCxnsId) ; fprintf (fp,"%s deferred-id : %d\n",indent,host->deferredId) ; fprintf (fp,"%s backed-up : %s\n",indent,boolToString (host->backedUp)); fprintf (fp,"%s backlog : %d\n",indent,host->backlog) ; fprintf (fp,"%s deferLen : %d\n",indent,host->deferLen) ; fprintf (fp,"%s loggedModeOn : %s\n",indent, boolToString (host->loggedModeOn)) ; fprintf (fp,"%s loggedModeOff : %s\n",indent, boolToString (host->loggedModeOff)) ; fprintf (fp,"%s logged-backlog : %s\n",indent, boolToString (host->loggedBacklog)) ; fprintf (fp,"%s streaming-type changed : %s\n",indent, boolToString (host->notifiedChangedRemBlckd)) ; fprintf (fp,"%s articles offered : %d\n",indent,host->artsOffered) ; fprintf (fp,"%s articles accepted : %d\n",indent,host->artsAccepted) ; fprintf (fp,"%s articles not wanted : %d\n",indent, host->artsNotWanted) ; fprintf (fp,"%s articles rejected : %d\n",indent,host->artsRejected); fprintf (fp,"%s articles deferred : %d\n",indent,host->artsDeferred) ; fprintf (fp,"%s articles missing : %d\n",indent,host->artsMissing) ; fprintf (fp,"%s articles spooled : %d\n",indent,host->artsToTape) ; fprintf (fp,"%s because of queue overflow : %d\n",indent, host->artsQueueOverflow) ; fprintf (fp,"%s when the we closed the host : %d\n",indent, host->artsHostClose) ; fprintf (fp,"%s because the host was asleep : %d\n",indent, host->artsHostSleep) ; fprintf (fp,"%s articles unspooled : %d\n",indent,host->artsFromTape) ; fprintf (fp,"%s articles requeued from dropped connections : %d\n",indent, host->artsCxnDrop) ; fprintf (fp,"%s process articles offered : %d\n",indent, host->gArtsOffered) ; fprintf (fp,"%s process articles accepted : %d\n",indent, host->gArtsAccepted) ; fprintf (fp,"%s process articles not wanted : %d\n",indent, host->gArtsNotWanted) ; fprintf (fp,"%s process articles rejected : %d\n",indent, host->gArtsRejected); fprintf (fp,"%s process articles deferred : %d\n",indent, host->gArtsDeferred) ; fprintf (fp,"%s process articles missing : %d\n",indent, host->gArtsMissing) ; fprintf (fp,"%s process articles spooled : %d\n",indent, host->gArtsToTape) ; fprintf (fp,"%s because of queue overflow : %d\n",indent, host->gArtsQueueOverflow) ; fprintf (fp,"%s when the we closed the host : %d\n",indent, host->gArtsHostClose) ; fprintf (fp,"%s because the host was asleep : %d\n",indent, host->gArtsHostSleep) ; fprintf (fp,"%s process articles unspooled : %d\n",indent, host->gArtsFromTape) ; fprintf (fp,"%s process articles requeued from dropped connections : %d\n", indent, host->gArtsCxnDrop) ; fprintf (fp,"%s average (mean) defer length : %.1f\n", indent, (double) host->dlAccum / cnt) ; fprintf (fp,"%s average (mean) queue length : %.1f\n", indent, (double) host->blAccum / cnt) ; fprintf (fp,"%s percentage of the time empty : %.1f\n", indent, 100.0 * host->blNone / cnt) ; fprintf (fp,"%s percentage of the time >0%%-25%% : %.1f\n", indent, 100.0 * host->blQuartile[0] / cnt) ; fprintf (fp,"%s percentage of the time 25%%-50%% : %.1f\n", indent, 100.0 * host->blQuartile[1] / cnt) ; fprintf (fp,"%s percentage of the time 50%%-75%% : %.1f\n", indent, 100.0 * host->blQuartile[2] / cnt) ; fprintf (fp,"%s percentage of the time 75%%-<100%% : %.1f\n", indent, 100.0 * host->blQuartile[3] / cnt) ; fprintf (fp,"%s percentage of the time full : %.1f\n", indent, 100.0 * host->blFull / cnt) ; fprintf (fp,"%s number of samples : %u\n", indent, host->blCount) ; timeToString (host->firstConnectTime, dateString, sizeof (dateString)); fprintf (fp, "%s firstConnectTime : %s\n", indent, dateString); timeToString (host->connectTime, dateString, sizeof (dateString)); fprintf (fp, "%s connectTime : %s\n", indent, dateString); timeToString (host->spoolTime, dateString, sizeof (dateString)); fprintf (fp, "%s spoolTime : %s\n", indent, dateString); timeToString (host->lastSpoolTime, dateString, sizeof (dateString)); fprintf (fp, "%s last-spool-time : %s\n", indent, dateString); #if 0 fprintf (fp,"%s tape {\n",indent) ; printTapeInfo (host->myTape,fp,indentAmt + INDENT_INCR) ; fprintf (fp,"%s }\n",indent) ; #else fprintf (fp,"%s tape : %p\n",indent,(void *) host->myTape) ; #endif fprintf (fp,"%s QUEUED articles {\n",indent) ; for (qe = host->queued ; qe != NULL ; qe = qe->next) { #if 0 printArticleInfo (qe->article,fp,indentAmt + INDENT_INCR) ; #else fprintf (fp,"%s %p\n",indent,(void *) qe->article) ; #endif } fprintf (fp,"%s }\n",indent) ; fprintf (fp,"%s IN PROCESS articles {\n",indent) ; for (qe = host->processed ; qe != NULL ; qe = qe->next) { #if 0 printArticleInfo (qe->article,fp,indentAmt + INDENT_INCR) ; #else fprintf (fp,"%s %p\n",indent,(void *) qe->article) ; #endif } fprintf (fp,"%s }\n",indent) ; fprintf (fp,"%s DEFERRED articles {\n",indent) ; for (qe = host->deferred ; qe != NULL ; qe = qe->next) { #if 0 printArticleInfo (qe->article,fp,indentAmt + INDENT_INCR) ; #else fprintf (fp,"%s %p\n",indent,(void *) qe->article) ; #endif } fprintf (fp,"%s }\n",indent) ; fprintf (fp,"%s DEFERRED articles {\n",indent) ; for (qe = host->deferred ; qe != NULL ; qe = qe->next) { #if 0 printArticleInfo (qe->article,fp,indentAmt + INDENT_INCR) ; #else fprintf (fp,"%s %p\n",indent,(void *) qe->article) ; #endif } fprintf (fp,"%s }\n",indent) ; fprintf (fp,"%s Connections {\n",indent) ; for (i = 0 ; i < host->maxConnections ; i++) { #if 0 if (host->connections[i] != NULL) printCxnInfo (*cxn,fp,indentAmt + INDENT_INCR) ; #else fprintf (fp,"%s %p\n",indent,(void *) host->connections[i]) ; #endif } fprintf (fp,"%s }\n",indent) ; fprintf (fp,"%s Active Connections {\n%s ",indent,indent) ; for (i = 0 ; i < host->maxConnections ; i++) if (host->cxnActive[i]) fprintf (fp," [%d:%p]",i,(void *) host->connections[i]) ; fprintf (fp,"\n%s }\n",indent) ; fprintf (fp,"%s Sleeping Connections {\n%s ",indent,indent) ; for (i = 0 ; i < host->maxConnections ; i++) if (host->cxnSleeping[i]) fprintf (fp," [%d:%p]",i,(void *) host->connections[i]) ; fprintf (fp,"\n%s }\n",indent) ; fprintf (fp,"%s}\n",indent) ; } /* close down all the connections of the Host. All articles that are in * processes are still pushed out and then a QUIT is issued. The Host will * also spool all inprocess articles to tape incase the process is about to * be killed (they'll be refused next time around). When all Connections * report that they're gone, then the Host will delete itself. */ void hostClose (Host host) { unsigned int i ; unsigned int cxnCount ; d_printf (1,"Closing host %s\n",host->params->peerName) ; queuesToTape (host) ; delTape (host->myTape) ; host->myTape = NULL ; hostLogStats (host,true) ; clearTimer (host->statsId) ; clearTimer (host->ChkCxnsId) ; clearTimer (host->deferredId) ; host->connectTime = 0 ; /* when we call cxnTerminate() on the last Connection, the Host objects will end up getting deleted out from under us (via hostCxnGone()). If we are running with a malloc that scribbles over memory after freeing it, then we'd fail in the second for loop test. Trying to access host->maxConnections. */ for (i = 0, cxnCount = 0 ; i < host->maxConnections ; i++) cxnCount += (host->connections [i] != NULL ? 1 : 0) ; for (i = 0 ; i < cxnCount ; i++) if (host->connections[i] != NULL) cxnTerminate (host->connections [i]) ; } /* * check if host should get more connections opened, or some closed... */ void hostChkCxns(TimeoutId tid UNUSED, void *data) { Host host = (Host) data; unsigned int currArticles, currSentArticles, currTotalArticles, newMaxCxns ; double lastAPS, currAPS, percentTaken, ratio ; double backlogRatio, backlogMult; if(!host->maxCxnChk) return; ASSERT(host->params != NULL); if(host->secsInLastPeriod > 0) lastAPS = host->artsProcLastPeriod / (host->secsInLastPeriod * 1.0); else lastAPS = host->artsProcLastPeriod * 1.0; newMaxCxns = host->maxConnections; currArticles = (host->gArtsAccepted + host->gArtsRejected + (host->gArtsNotWanted / 4)) - host->lastCheckPoint ; host->lastCheckPoint = (host->gArtsAccepted + host->gArtsRejected + (host->gArtsNotWanted / 4)); currSentArticles = host->gArtsAccepted + host->gArtsRejected - host->lastSentCheckPoint ; host->lastSentCheckPoint = host->gArtsAccepted + host->gArtsRejected; currTotalArticles = host->gArtsAccepted + host->gArtsRejected + host->gArtsRejected + host->gArtsQueueOverflow - host->lastTotalCheckPoint ; host->lastTotalCheckPoint = host->gArtsAccepted + host->gArtsRejected + host->gArtsRejected + host->gArtsQueueOverflow ; currAPS = currArticles / (host->nextCxnTimeChk * 1.0) ; percentTaken = currSentArticles * 1.0 / ((currTotalArticles==0)?1:currTotalArticles); /* Get how full the queue is currently */ backlogRatio = (host->backlog * 1.0 / hostHighwater); backlogMult = 1.0/(1.0-host->params->dynBacklogFilter); d_printf(1,"%s hostChkCxns - entry filter=%3.3f blmult=%3.3f blratio=%3.3f\n",host->params->peerName,host->backlogFilter, backlogMult, backlogRatio); ratio = 0.0; /* ignore APS by default */ switch (host->params->dynamicMethod) { case METHOD_COMBINED: /* When a high % of articles is being taken, take notice of the * APS values. However for smaller %s, quickly start to ignore this * and concentrate on queue sizes */ ratio = percentTaken * percentTaken; /* nobreak; */ case METHOD_QUEUE: /* backlogFilter is an IIR filtered version of the backlogRatio. */ host->backlogFilter *= host->params->dynBacklogFilter; /* Penalise anything over the backlog HWM twice as severely * (otherwise we end up feeding some sites constantly * just below the HWM. This way random noise makes * such sites jump to one more connection * * Use factor (1-ratio) so if ratio is near 1 we ignore this */ if (backlogRatio>host->params->dynBacklogLowWaterMark/100.0) host->backlogFilter += (backlogRatio+1.0)/2.0 * (1.0-ratio); else host->backlogFilter += backlogRatio * (1.0-ratio); /* * Now bump it around for APS too */ if ((currAPS - lastAPS) >= 0.1) host->backlogFilter += ratio*((currAPS - lastAPS) + 1.0); else if ((currAPS - lastAPS) < -.2) host->backlogFilter -= ratio; d_printf(1,"%s hostChkCxns - entry hwm=%3.3f lwm=%3.3f new=%3.3f [%3.3f,%3.3f]\n", host->params->peerName,host->params->dynBacklogHighWaterMark, host->params->dynBacklogLowWaterMark,host->backlogFilter, (host->params->dynBacklogLowWaterMark * backlogMult / 100.0), (host->params->dynBacklogHighWaterMark * backlogMult / 100.0)); if (host->backlogFilter < (host->params->dynBacklogLowWaterMark * backlogMult / 100.0)) newMaxCxns--; else if (host->backlogFilter > (host->params->dynBacklogHighWaterMark * backlogMult / 100.0)) newMaxCxns++; break; case METHOD_STATIC: /* well not much to do, just check maxConnection = absMaxConnections */ ASSERT (host->maxConnections == MAXCONLIMIT(host->params->absMaxConnections)); break; case METHOD_APS: if ((currAPS - lastAPS) >= 0.1) newMaxCxns += (int)(currAPS - lastAPS) + 1 ; else if ((currAPS - lastAPS) < -.2) newMaxCxns--; break; } d_printf(1, "hostChkCxns: Chngs %f\n", currAPS - lastAPS); if (newMaxCxns < 1) newMaxCxns=1; if (newMaxCxns > MAXCONLIMIT(host->params->absMaxConnections)) newMaxCxns = MAXCONLIMIT(host->params->absMaxConnections); if (newMaxCxns != host->maxConnections) { notice ("%s hostChkCxns - maxConnections was %d now %d", host->params->peerName, host->maxConnections,newMaxCxns); host->backlogFilter= ((host->params->dynBacklogLowWaterMark + host->params->dynBacklogHighWaterMark) /200.0 * backlogMult); host->artsProcLastPeriod = currArticles ; host->secsInLastPeriod = host->nextCxnTimeChk ; /* Alter MaxConnections and in doing so ensure we connect new cxns immediately if we are adding stuff */ hostAlterMaxConnections(host, host->params->absMaxConnections, newMaxCxns, true); } if(host->nextCxnTimeChk <= 240) host->nextCxnTimeChk *= 2; else host->nextCxnTimeChk = 300; d_printf(1, "prepareSleep hostChkCxns, %d\n", host->nextCxnTimeChk); host->ChkCxnsId = prepareSleep(hostChkCxns, host->nextCxnTimeChk, host); } /* * have the Host transmit the Article if possible. */ void hostSendArticle (Host host, Article article) { ASSERT(host->params != NULL); if (host->spoolTime > 0) { /* all connections are asleep */ host->artsHostSleep++ ; host->gArtsHostSleep++ ; host->artsToTape++ ; host->gArtsToTape++ ; procArtsToTape++ ; tapeTakeArticle (host->myTape, article) ; return ; } /* at least one connection is feeding or waiting and there's no backlog */ if (host->queued == NULL) { unsigned int idx ; Article extraRef ; Connection cxn = NULL ; extraRef = artTakeRef (article) ; /* the referrence we give away */ /* stick on the queue of articles we've handed off--we're hopeful. */ queueArticle (article,&host->processed,&host->processedTail, 0) ; if (host->params->minQueueCxn) { Connection x_cxn = NULL ; unsigned int x_queue = host->params->maxChecks + 1 ; for (idx = 0 ; x_queue > 0 && idx < host->maxConnections ; idx++) if ((cxn = host->connections[idx]) != host->notThisCxn && cxn != NULL) { if (!host->cxnActive [idx]) { if (!host->cxnSleeping [idx]) { if (cxnTakeArticle (cxn, extraRef)) { host->gNoQueue++ ; return ; } else d_printf (1,"%s Inactive connection %d refused an article\n", host->params->peerName,idx) ; } } else { unsigned int queue = host->params->maxChecks - cxnQueueSpace (cxn) ; if (queue < x_queue) { x_queue = queue ; x_cxn = cxn ; } } } if (x_cxn != NULL && cxnTakeArticle (x_cxn, extraRef)) { if (x_queue == 0) host->gNoQueue++ ; else host->gCxnQueue += x_queue ; return ; } } else { /* first we try to give it to one of our active connections. We simply start at the bottom and work our way up. This way connections near the end of the list will get closed sooner from idleness. */ for (idx = 0 ; idx < host->maxConnections ; idx++) { if (host->cxnActive [idx] && (cxn = host->connections[idx]) != host->notThisCxn && cxn != NULL && cxnTakeArticle (cxn, extraRef)) { unsigned int queue = host->params->maxChecks - cxnQueueSpace (cxn) - 1; if (queue == 0) host->gNoQueue++ ; else host->gCxnQueue += queue ; return ; } } /* Wasn't taken so try to give it to one of the waiting connections. */ for (idx = 0 ; idx < host->maxConnections ; idx++) if (!host->cxnActive [idx] && !host->cxnSleeping [idx] && (cxn = host->connections[idx]) != host->notThisCxn && cxn != NULL) { if (cxnTakeArticle (cxn, extraRef)) { unsigned int queue = host->params->maxChecks - cxnQueueSpace (cxn) - 1; if (queue == 0) host->gNoQueue++ ; else host->gCxnQueue += queue ; return ; } else d_printf (1,"%s Inactive connection %d refused an article\n", host->params->peerName,idx) ; } } /* this'll happen if all connections are feeding and all their queues are full, or if those not feeding are asleep. */ d_printf (1, "Couldn't give the article to a connection\n") ; delArticle (extraRef) ; remArticle (article,&host->processed,&host->processedTail) ; /* cxn can be NULL if it has never been affected in the loop above. */ if (cxn == NULL || !cxnCheckstate (cxn)) { host->artsToTape++ ; host->gArtsToTape++ ; procArtsToTape++ ; tapeTakeArticle (host->myTape,article) ; return ; } } /* Either all the peer connection queues were full or we already had a backlog, so there was no sense in checking. */ queueArticle (article,&host->queued,&host->queuedTail, 0) ; host->backlog++ ; backlogToTape (host) ; } /* * called by the Host's connection when the remote is refusing postings * from us becasue we're not allowed (banner code 400). */ void hostCxnBlocked (Host host, Connection cxn, char *reason) { ASSERT(host->params != NULL); #ifndef NDEBUG { unsigned int i ; for (i = 0 ; i < host->maxConnections ; i++) if (host->connections [i] == cxn) ASSERT (host->cxnActive [i] == false) ; } #endif if (host->blockedReason == NULL) host->blockedReason = xstrdup (reason) ; if (host->activeCxns == 0 && host->spoolTime == 0) { host->blockedCxn = cxn ; /* to limit log notices */ notice ("%s remote cannot accept articles initial: %s", host->params->peerName, reason) ; } else if (host->activeCxns > 0 && !host->notifiedChangedRemBlckd) { notice ("%s remote cannot accept articles change: %s", host->params->peerName, reason) ; host->notifiedChangedRemBlckd = true ; } else if (host->spoolTime != 0 && host->blockedCxn == cxn) { notice ("%s remote cannot accept articles still: %s", host->params->peerName, reason) ; } } /* * Called by the Connection when it gets a response back to the MODE * STREAM command. It's now that we consider the connection usable. */ void hostRemoteStreams (Host host, Connection cxn, bool doesStreaming) { unsigned int i ; host->blockedCxn = NULL ; if (host->blockedReason != NULL) free (host->blockedReason) ; host->blockedReason = NULL ; /* we may have told the connection to quit while it was in the middle of connecting */ if (amClosing (host)) return ; if (host->connectTime == 0) /* first connection for this cycle. */ { if (doesStreaming && host->params->wantStreaming) notice ("%s remote MODE STREAM", host->params->peerName) ; else if (doesStreaming) notice ("%s remote MODE STREAM disabled", host->params->peerName) ; else notice ("%s remote MODE STREAM failed", host->params->peerName) ; if (host->spoolTime > 0) hostStopSpooling (host) ; /* set up the callback for statistics logging. */ if (host->statsId != 0) clearTimer (host->statsId) ; host->statsId = prepareSleep (hostStatsTimeoutCbk, statsPeriod, host) ; if (host->ChkCxnsId != 0) clearTimer (host->ChkCxnsId); host->ChkCxnsId = prepareSleep (hostChkCxns, 30, host) ; host->remoteStreams = (host->params->wantStreaming ? doesStreaming : false) ; host->connectTime = theTime() ; host->connectTime_checkpoint = host->connectTime ; if (host->firstConnectTime == 0) host->firstConnectTime = host->connectTime ; } else if (host->remoteStreams != doesStreaming && host->params->wantStreaming) notice ("%s remote MODE STREAM change", host->params->peerName) ; for (i = 0 ; i < host->maxConnections ; i++) if (host->connections [i] == cxn) { host->cxnActive [i] = true ; if (host->cxnSleeping [i]) host->sleepingCxns-- ; host->cxnSleeping [i] = false ; break ; } ASSERT (i != host->maxConnections) ; host->activeCxns++ ; hostLogStatus () ; } /* * Called by the connection when it is no longer connected to the * remote. Perhaps due to getting a code 400 to an IHAVE, or due to a * periodic close. */ void hostCxnDead (Host host, Connection cxn) { unsigned int i ; for (i = 0 ; i < host->maxConnections ; i++) if (host->connections [i] == cxn) { if (host->cxnActive [i]) /* won't be active if got 400 on banner */ { host->cxnActive [i] = false ; host->activeCxns-- ; if (!amClosing (host) && host->activeCxns == 0) { clearTimer (host->statsId) ; clearTimer (host->ChkCxnsId) ; hostLogStats (host,true) ; host->connectTime = 0 ; } } else if (host->cxnSleeping [i]) /* cxnNuke can be called on sleepers */ { host->cxnSleeping [i] = false ; host->sleepingCxns-- ; } break ; } ASSERT (i < host->maxConnections) ; hostLogStatus () ; } /* * Called by the Connection when it is going to sleep so the Host won't * bother trying to give it Articles */ void hostCxnSleeping (Host host, Connection cxn) { unsigned int i ; for (i = 0 ; i < host->maxConnections ; i++) if (host->connections [i] == cxn) { if (!host->cxnSleeping [i]) { host->cxnSleeping [i] = true ; host->sleepingCxns++ ; } if (host->spoolTime == 0 && host->sleepingCxns >= host->maxConnections) hostStartSpooling (host) ; break ; } ASSERT (i < host->maxConnections) ; hostLogStatus () ; } /* * Called by the Connection when it goes into the waiting state. */ void hostCxnWaiting (Host host, Connection cxn) { unsigned int i ; for (i = 0 ; i < host->maxConnections ; i++) if (host->connections [i] == cxn) { if (host->cxnSleeping [i]) host->sleepingCxns-- ; host->cxnSleeping [i] = false ; break ; } ASSERT (i < host->maxConnections) ; if (host->spoolTime > 0) hostStopSpooling (host) ; hostLogStatus () ; } /* * Called by the Connection when it is about to delete itself. */ bool hostCxnGone (Host host, Connection cxn) { unsigned int i; bool oneThere = false ; char msgstr[SMBUF] ; /* forget about the Connection and see if we are still holding any live connections still. */ for (i = 0 ; i < host->maxConnections ; i++) if (host->connections [i] == cxn) { if (!amClosing (host)) { warn ("%s:%d connection vanishing", host->params->peerName, i) ; } host->connections [i] = NULL ; if (host->cxnActive [i]) { host->cxnActive [i] = false ; host->activeCxns-- ; } else if (host->cxnSleeping [i]) { host->cxnSleeping [i] = false ; host->sleepingCxns-- ; } } else if (host->connections [i] != NULL) oneThere = true ; /* remove the host if it has no connexions */ if ( !oneThere ) { time_t now = theTime() ; unsigned int hostsLeft ; if (host->firstConnectTime > 0) { snprintf(msgstr, sizeof(msgstr), "accsize %.0f rejsize %.0f", host->gArtsSizeAccepted, host->gArtsSizeRejected); notice ("%s global seconds %ld offered %d accepted %d refused %d" " rejected %d missing %d %s spooled %d unspooled %d", host->params->peerName, (long) (now - host->firstConnectTime), host->gArtsOffered, host->gArtsAccepted, host->gArtsNotWanted, host->gArtsRejected, host->gArtsMissing, msgstr, host->gArtsToTape, host->gArtsFromTape) ; } hostsLeft = listenerHostGone (host->listener, host) ; delHost (host) ; if (hostsLeft == 0) { snprintf(msgstr, sizeof(msgstr), "accsize %.0f rejsize %.0f", procArtsSizeAccepted, procArtsSizeRejected); notice ("ME global seconds %ld offered %ld accepted %ld refused %ld" " rejected %ld missing %ld %s spooled %ld unspooled %ld", (long) (now - start), procArtsOffered, procArtsAccepted, procArtsNotWanted,procArtsRejected, procArtsMissing, msgstr, procArtsToTape, procArtsFromTape) ; } /* return true if that was the last host */ return (hostsLeft == 0 ? true : false) ; } /* return false because there is still at least one host (this one) */ return false ; } /* * The connections has offered an article to the remote. */ void hostArticleOffered (Host host, Connection cxn UNUSED) { host->artsOffered++ ; host->gArtsOffered++ ; procArtsOffered++ ; } /* * Article was succesfully transferred. */ void hostArticleAccepted (Host host, Connection cxn, Article article) { const char *filename = artFileName (article) ; const char *msgid = artMsgId (article) ; double len = artSize (article); d_printf (5,"Article %s (%s) was transferred\n", msgid, filename) ; host->artsAccepted++ ; host->gArtsAccepted++ ; procArtsAccepted++ ; host->artsSizeAccepted += len ; host->gArtsSizeAccepted += len ; procArtsSizeAccepted += len ; /* host has two references to the article here... the parameter `article' and the queue */ delArticle (article) ; /* drop the parameter reference */ if (!amClosing (host)) articleGone (host,cxn,article) ; /* and the one in the queue */ } /* * remote said no thanks to an article. */ void hostArticleNotWanted (Host host, Connection cxn, Article article) { const char *filename = artFileName (article) ; const char *msgid = artMsgId (article) ; d_printf (5,"Article %s (%s) was not wanted\n", msgid, filename) ; host->artsNotWanted++ ; host->gArtsNotWanted++ ; procArtsNotWanted++ ; /* host has two references to the article here... `article' and the queue */ delArticle (article) ; /* drop the `article' reference */ if (!amClosing (host)) articleGone (host,cxn,article) ; /* and the one in the queue */ } /* * remote rejected the article after it was was transferred */ void hostArticleRejected (Host host, Connection cxn, Article article) { const char *filename = artFileName (article) ; const char *msgid = artMsgId (article) ; double len = artSize (article); d_printf (5,"Article %s (%s) was rejected\n", msgid, filename) ; host->artsRejected++ ; host->gArtsRejected++ ; procArtsRejected++ ; host->artsSizeRejected += len ; host->gArtsSizeRejected += len ; procArtsSizeRejected += len ; /* host has two references to the article here... `article' and the queue */ delArticle (article) ; /* drop the `article' reference */ if (!amClosing (host)) articleGone (host,cxn,article) ; } /* * The remote wants us to retry the article later. */ void hostArticleDeferred (Host host, Connection cxn, Article article) { host->artsDeferred++ ; host->gArtsDeferred++ ; procArtsDeferred++ ; if (!amClosing (host)) { Article extraRef ; int deferTimeout = 5 ; /* XXX - should be tunable */ time_t now = theTime() ; extraRef = artTakeRef (article) ; /* hold a reference until requeued */ articleGone (host,cxn,article) ; /* drop from the queue */ if (host->deferred == NULL) { if (host->deferredId != 0) clearTimer (host->deferredId) ; host->deferredId = prepareSleep (hostDeferredArtCbk, deferTimeout, host) ; } queueArticle (article,&host->deferred,&host->deferredTail, now + deferTimeout) ; host->deferLen++ ; backlogToTape (host) ; delArticle (extraRef) ; } else delArticle(article); /*drop parameter reference if not sent to tape*/ } /* * The Connection is giving the article back to the Host, but it doesn't * want a new one in return. */ void hostTakeBackArticle (Host host, Connection cxn UNUSED, Article article) { if (!amClosing (host)) { Article extraRef ; host->artsCxnDrop++ ; host->gArtsCxnDrop++ ; extraRef = artTakeRef (article) ; /* hold a reference until requeued */ articleGone (host,NULL,article) ; /* drop from the queue */ host->notThisCxn = cxn; hostSendArticle (host, article) ; /* requeue it */ host->notThisCxn = NULL; delArticle (extraRef) ; } else delArticle(article); /*drop parameter reference if not sent to tape*/ } /* * The disk file for the article is no longer valid */ void hostArticleIsMissing (Host host, Connection cxn, Article article) { const char *filename = artFileName (article) ; const char *msgid = artMsgId (article) ; d_printf (5, "%s article is missing %s %s\n", host->params->peerName, msgid, filename) ; host->artsMissing++ ; host->gArtsMissing++ ; procArtsMissing++ ; /* host has two references to the article here... `article' and the queue */ delArticle (article) ; /* drop the `article' reference */ if (!amClosing (host)) articleGone (host,cxn,article) ; /* and the one in the queue */ } /* The Connection wants something to do. This is called by the Connection * after it has transferred an article. This is what keeps the pipes full * of data off the tapes if the input from inn is idle. */ bool hostGimmeArticle (Host host, Connection cxn) { Article article = NULL ; bool gaveSomething = false ; size_t amtToGive = cxnQueueSpace (cxn) ; /* may be more than one */ int feed = 0 ; if (amClosing (host)) { d_printf (5,"%s no article to give due to closing\n",host->params->peerName) ; return false ; } if (amtToGive == 0) d_printf (5,"%s Queue space is zero....\n",host->params->peerName) ; while (amtToGive > 0) { bool tookIt ; unsigned int queue = host->params->maxChecks - amtToGive ; if (host->params->backlogFeedFirst) { if ((article = getArticle (host->myTape)) != NULL) feed = 2; else if ((article = remHead (&host->queued,&host->queuedTail)) != NULL) feed = 1; else feed = 3; } else { if ((article = remHead (&host->queued,&host->queuedTail)) != NULL) feed = 1; else if ((article = getArticle (host->myTape)) != NULL) feed = 2; else feed = 3; } switch (feed) { case 1: host->backlog-- ; tookIt = cxnQueueArticle (cxn,artTakeRef (article)) ; ASSERT (tookIt == true) ; if (queue == 0) host->gNoQueue++ ; else host->gCxnQueue += queue ; queueArticle (article,&host->processed,&host->processedTail, 0) ; amtToGive-- ; gaveSomething = true ; break ; case 2: /* go to the tapes */ tookIt = cxnQueueArticle (cxn,artTakeRef (article)) ; ASSERT (tookIt == true) ; if (queue == 0) host->gNoQueue++ ; else host->gCxnQueue += queue ; host->artsFromTape++ ; host->gArtsFromTape++ ; procArtsFromTape++ ; queueArticle (article,&host->processed,&host->processedTail, 0) ; amtToGive-- ; gaveSomething = true ; break ; case 3: /* we had nothing left to give... */ if (host->processed == NULL) /* and if nothing outstanding... */ listenerHostIsIdle (host->listener,host) ; /* tell our owner */ amtToGive = 0 ; break ; } } return gaveSomething ; } /* * get the name that INN uses for this host */ const char *hostPeerName (Host host) { ASSERT (host != NULL) ; return host->params->peerName ; } /* * Get the IPv4 bindAddr. */ const char *hostBindAddr (Host host) { ASSERT (host != NULL) ; return host->params->bindAddr ; } /* * Get the IPv6 bindAddr6. */ const char *hostBindAddr6 (Host host) { ASSERT (host != NULL) ; return host->params->bindAddr6 ; } /* * get the username and password for authentication */ const char *hostUsername (Host host) { ASSERT (host != NULL) ; return host->params->username ; } const char *hostPassword (Host host) { ASSERT (host != NULL) ; return host->params->password ; } /* return true if the Connections for this host should attempt to do streaming. */ bool hostWantsStreaming (Host host) { return host->params->wantStreaming ; } unsigned int hostMaxChecks (Host host) { return host->params->maxChecks ; } bool hostDropDeferred (Host host) { return host->params->dropDeferred ; } /**********************************************************************/ /** CLASS FUNCTIONS **/ /**********************************************************************/ /* * Set the state of whether each Connection is told to log its stats when * its controlling Host logs its stats. */ void hostLogConnectionStats (bool val) { logConnectionStats = val ; } bool hostLogConnectionStatsP (void) { return logConnectionStats ; } /* * Called by one of the Host's Connection's when it (the Connection) * switches into or out of no-CHECK mode. */ void hostLogNoCheckMode (Host host, bool on, double low, double cur, double high) { if (on && host->loggedModeOn == false) { notice ("%s mode no-CHECK entered [%.2f,%.2f,%.2f]", host->params->peerName, low, cur, high) ; host->loggedModeOn = true ; } else if (!on && host->loggedModeOff == false) { notice ("%s mode no-CHECK exited [%.2f,%.2f,%.2f]", host->params->peerName, low, cur, high) ; host->loggedModeOff = true ; } } void hostSetStatusFile (const char *filename) { FILE *fp ; if (filename == NULL) die ("Can't set status file name with a NULL filename\n") ; else if (*filename == '\0') die ("Can't set status file name with an empty string\n") ; /* statusFile may already be initialized (several status-file: lines, * reload of the config file, shutdown process). */ free(statusFile); if (*filename == '/') statusFile = xstrdup (filename) ; else { if (genHtml) statusFile = concatpath (innconf->pathhttp,filename) ; else statusFile = concatpath (innconf->pathlog,filename) ; } if ((fp = fopen (statusFile,"w")) == NULL) { syslog (LOG_ERR,"Status file is not a valid pathname: %s", statusFile) ; free (statusFile) ; statusFile = NULL ; } else fclose (fp) ; } void gHostStats (void) { Host h ; time_t now = theTime() ; char msgstr[SMBUF] ; for (h = gHostList ; h != NULL ; h = h->next) if (h->firstConnectTime > 0) { snprintf(msgstr, sizeof(msgstr), "accsize %.0f rejsize %.0f", h->gArtsSizeAccepted, h->gArtsSizeRejected); notice ("%s global seconds %ld offered %d accepted %d refused %d" " rejected %d missing %d %s spooled %d unspooled %d", h->params->peerName, (long) (now - h->firstConnectTime), h->gArtsOffered, h->gArtsAccepted, h->gArtsNotWanted, h->gArtsRejected, h->gArtsMissing, msgstr, h->gArtsToTape, h->gArtsFromTape) ; } } /**********************************************************************/ /** PRIVATE FUNCTIONS **/ /**********************************************************************/ #define INHERIT 1 #define NO_INHERIT 0 static HostParams hostDetails (scope *s, char *name, bool isDefault, FILE *fp) { long iv ; int bv, vival, inherit ; HostParams p; char * q; double rv, l, h ; value * v; p=newHostParams(isDefault?NULL:defaultParams); if (isDefault) { ASSERT (name==NULL); } else { if (name) { p->peerName=xstrdup(name); } if (s != NULL) { if (getString (s,IP_NAME,&q,NO_INHERIT)) p->ipName = q ; else p->ipName = xstrdup (name) ; } if (getString (s,"username",&q,NO_INHERIT)) p->username = q; if (getString (s,"password",&q,NO_INHERIT)) p->password = q; if (p->username != NULL && p->password == NULL) logOrPrint (LOG_ERR,fp,"cannot find password for %s",p->peerName); if (p->username == NULL && p->password != NULL) logOrPrint (LOG_ERR,fp,"cannot find username for %s",p->peerName); } if (getString(s,"bindaddress",&q,isDefault?NO_INHERIT:INHERIT)) { if (p->bindAddr) free(p->bindAddr); p->bindAddr = q; } if (getString(s,"bindaddress6",&q,isDefault?NO_INHERIT:INHERIT)) { if (p->bindAddr6) free(p->bindAddr6); p->bindAddr6 = q; } if (p->bindAddr && strcmp(p->bindAddr, "none") == 0 && p->bindAddr6 && strcmp(p->bindAddr6, "none") == 0) { logOrPrint (LOG_ERR,fp,"cannot set both bindaddress and bindaddress6" " to \"none\" -- ignoring them for %s",p->peerName); free(p->bindAddr); free(p->bindAddr6); p->bindAddr = NULL; p->bindAddr6 = NULL; } /* check required global defaults are there and have good values */ #define GETINT(sc,f,n,min,max,req,val,inh) \ vival = validateInteger(f,n,min,max,req,val,sc,inh); \ if (isDefault) do{ \ if(vival==VALUE_WRONG_TYPE) \ { \ logOrPrint(LOG_CRIT,fp,"cannot continue"); \ exit(1); \ } \ else if(vival != VALUE_OK) \ val = 0; \ } while(0); \ iv = 0 ; \ getInteger (sc,n,&iv,inh) ; \ val = (unsigned int) iv ; #define GETREAL(sc,f,n,min,max,req,val,inh) \ vival = validateReal(f,n,min,max,req,val,sc,inh); \ if (isDefault) do{ \ if(vival==VALUE_WRONG_TYPE) \ { \ logOrPrint(LOG_CRIT,fp,"cannot continue"); \ exit(1); \ } \ else if(vival != VALUE_OK) \ rv = 0; \ } while(0); \ rv = 0 ; \ getReal (sc,n,&rv,inh) ; \ val = rv ; #define GETBOOL(sc,f,n,req,val,inh) \ vival = validateBool(f,n,req,val,sc,inh); \ if (isDefault) do{ \ if(vival==VALUE_WRONG_TYPE) \ { \ logOrPrint(LOG_CRIT,fp,"cannot continue"); \ exit(1); \ } \ else if(vival != VALUE_OK) \ bv = 0; \ } while(0); \ bv = 0 ; \ getBool (sc,n,&bv,inh) ; \ val = (bv ? true : false); inherit = isDefault?NO_INHERIT:INHERIT; GETINT(s,fp,"article-timeout",0,LONG_MAX,NOTREQ,p->articleTimeout, inherit); GETINT(s,fp,"response-timeout",0,LONG_MAX,NOTREQ,p->responseTimeout, inherit); GETINT(s,fp,"close-period",0,LONG_MAX,NOTREQ,p->closePeriod, inherit); GETINT(s,fp,"initial-connections",0,LONG_MAX,NOTREQ,p->initialConnections, inherit); GETINT(s,fp,"max-connections",0,LONG_MAX,NOTREQ,p->absMaxConnections, inherit); GETINT(s,fp,"max-queue-size",1,LONG_MAX,NOTREQ,p->maxChecks, inherit); GETBOOL(s,fp,"streaming",NOTREQ,p->wantStreaming, inherit); GETBOOL(s,fp,"drop-deferred",NOTREQ,p->dropDeferred, inherit); GETBOOL(s,fp,"min-queue-connection",NOTREQ,p->minQueueCxn, inherit); GETREAL(s,fp,"no-check-high",0.0,100.0,NOTREQ,p->lowPassHigh, inherit); GETREAL(s,fp,"no-check-low",0.0,100.0,NOTREQ,p->lowPassLow, inherit); GETREAL(s,fp,"no-check-filter",0.1,DBL_MAX,NOTREQ,p->lowPassFilter, inherit); GETINT(s,fp,"port-number",0,LONG_MAX,NOTREQ,p->portNum, inherit); GETINT(s,fp,"backlog-limit",0,LONG_MAX,NOTREQ,p->backlogLimit, inherit); GETBOOL(s,fp,"force-ipv4",NOTREQ,p->forceIPv4,inherit); if (p->forceIPv4) { if (p->bindAddr && strcmp(p->bindAddr, "none") == 0) { free(p->bindAddr); p->bindAddr = NULL; } if (p->bindAddr6) { free(p->bindAddr6); } /* Force bindaddress6: to "none". */ p->bindAddr6 = xstrdup("none"); } if (findValue (s,"backlog-factor",inherit) == NULL && findValue (s,"backlog-limit-highwater",inherit) == NULL) { /* This is not an error. A default value will be used. * * logOrPrint (LOG_ERR,fp, "ME config: must define at least one of backlog-factor" " and backlog-limit-highwater. Adding %s: %f", "backlog-factor", LIMIT_FUDGE) ; */ addReal (s,"backlog-factor",LIMIT_FUDGE) ; rv = 0 ; } GETBOOL(s,fp,"backlog-feed-first",NOTREQ,p->backlogFeedFirst, inherit); /* Innfeed should emit a warning if backlog-feed-first is set to "true" for any peer that doesn't have max-connections and initial-connections both set to "1" */ if ((p->backlogFeedFirst) && ((p->initialConnections <= 1) || (p->absMaxConnections != 1))) { if (p->peerName != NULL) logOrPrint (LOG_WARNING,fp, "ME config: innfeed will make more than one connection" " to peer %s, but backlog-feed-first is set", p->peerName); else logOrPrint (LOG_WARNING,fp, "ME config: innfeed will make more than one connection" " to peer, but backlog-feed-first is set"); } GETINT(s,fp,"backlog-limit-highwater",0,LONG_MAX,NOTREQNOADD,p->backlogLimitHigh, inherit); GETREAL(s,fp,"backlog-factor",1.0,DBL_MAX,NOTREQNOADD,p->backlogFactor, inherit); GETINT(s,fp,"dynamic-method",0,3,NOTREQ,p->dynamicMethod, inherit); GETREAL(s,fp,"dynamic-backlog-filter",0.0,DBL_MAX,NOTREQ,p->dynBacklogFilter, inherit); GETREAL(s,fp,"dynamic-backlog-low",0.0,100.0,NOTREQ,p->dynBacklogLowWaterMark, inherit); GETREAL(s,fp,"dynamic-backlog-high",0.0,100.0,NOTREQ,p->dynBacklogHighWaterMark, inherit); l=p->lowPassLow; h=p->lowPassHigh; if (l > h) { logOrPrint (LOG_ERR,fp, "ME config: no-check-low value greater than no-check-high" " (%f vs %f). Setting to %f and %f", l, h, NOCHECKLOW, NOCHECKHIGH) ; rv = 0 ; v = findValue (s,"no-check-low",NO_INHERIT) ; v->v.real_val = p->lowPassLow = NOCHECKLOW ; v = findValue (s,"no-check-high",NO_INHERIT) ; v->v.real_val = p->lowPassHigh = NOCHECKHIGH ; } else if (h - l < 5.0) logOrPrint (LOG_WARNING,fp, "ME config: no-check-low and no-check-high are close" " together (%f vs %f)",l,h) ; return p; } static HostParams getHostInfo (void) { static int idx = 0 ; value *v ; scope *s ; HostParams p=NULL; if (topScope == NULL) return p; while ((v = getNextPeer (&idx)) != NULL) { if (!ISPEER (v)) continue ; s = v->v.scope_val ; p=hostDetails(s,v->name,false,NULL); break ; } if (v == NULL) idx = 0 ; /* start over next time around */ return p; } /* * fully delete and clean up the Host object. */ void delHost (Host host) { Host h,q ; for (h = gHostList, q = NULL ; h != NULL ; q = h, h = h->next) if (h == host) { if (gHostList == h) gHostList = gHostList->next ; else q->next = h->next ; break ; } ASSERT (h != NULL) ; delTape (host->myTape) ; free (host->connections) ; free (host->cxnActive) ; free (host->cxnSleeping) ; free (host->params->peerName) ; free (host->params->ipName) ; if (host->ipAddrs) { if(host->ipAddrs[0]) free (host->ipAddrs[0]); free (host->ipAddrs) ; } free (host) ; gHostCount-- ; } static Host findHostByName (char *name) { Host h; for (h = gHostList; h != NULL; h = h->next) if ( strcmp(h->params->peerName, name) == 0 ) return h; return NULL; } /* * the article can be dropped from the process queue and the connection can * take a new article if there are any to be had. */ static void articleGone (Host host, Connection cxn, Article article) { if ( !remArticle (article,&host->processed,&host->processedTail) ) die ("remArticle in articleGone failed") ; delArticle (article) ; if (cxn != NULL) hostGimmeArticle (host,cxn) ; /* may not give anything over */ } /* * One of the Connections for this Host has reestablished itself, so stop * spooling article info to disk. */ static void hostStopSpooling (Host host) { ASSERT (host->spoolTime != 0) ; clearTimer (host->statsId) ; hostLogStats (host,true) ; host->spoolTime = 0 ; } /* * No connections are active and we're getting response 201 or 400 (or some * such) so that we need to start spooling article info to disk. */ static void hostStartSpooling (Host host) { ASSERT (host->spoolTime == 0) ; queuesToTape (host) ; hostLogStats (host,true) ; host->spoolTime = theTime() ; host->spoolTime_checkpoint = host->spoolTime ; if (host->firstConnectTime == 0) host->firstConnectTime = host->spoolTime ; /* don't want to log too frequently */ if (SPOOL_LOG_PERIOD > 0 && (host->spoolTime - host->lastSpoolTime) > SPOOL_LOG_PERIOD) { notice ("%s spooling no active connections", host->params->peerName) ; host->lastSpoolTime = host->spoolTime ; } host->connectTime = 0 ; host->notifiedChangedRemBlckd = false ; clearTimer (host->statsId) ; host->statsId = prepareSleep (hostStatsTimeoutCbk, statsPeriod, host) ; } /* * Time to log the statistics for the Host. If FINAL is true then the * counters will be reset. */ static void hostLogStats (Host host, bool final) { time_t now = theTime() ; time_t *startPeriod ; double cnt = (host->blCount) ? (host->blCount) : 1.0; if (host->spoolTime == 0 && host->connectTime == 0) return ; /* host has never connected and never started spooling. */ startPeriod = (host->spoolTime != 0 ? &host->spoolTime : &host->connectTime); if (now - *startPeriod >= statsResetPeriod) final = true ; if (host->spoolTime != 0) { /* Log a checkpoint in any case. */ notice("%s checkpoint seconds %ld spooled %d on_close %d sleeping %d", host->params->peerName, (long) (now - host->spoolTime_checkpoint), host->artsToTape - host->artsToTape_checkpoint, host->artsHostClose - host->artsHostClose_checkpoint, host->artsHostSleep - host->artsHostSleep_checkpoint); host->spoolTime_checkpoint = now; host->artsToTape_checkpoint = host->artsToTape; host->artsHostClose_checkpoint = host->artsHostClose; host->artsHostSleep_checkpoint = host->artsHostSleep; if (final) { notice("%s final seconds %ld spooled %d on_close %d sleeping %d", host->params->peerName, (long) (now - host->spoolTime), host->artsToTape, host->artsHostClose, host->artsHostSleep); } } else { /* Log a checkpoint in any case. * * Note that deferred and queue values are cumulative * (and not treated by innreport). */ notice("%s checkpoint seconds %ld offered %d accepted %d refused %d rejected %d" " missing %d accsize %.0f rejsize %.0f spooled %d on_close %d unspooled %d" " deferred %d/%.1f requeued %d" " queue %.1f/%d:%.0f,%.0f,%.0f,%.0f,%.0f,%.0f", host->params->peerName, (long) (now - host->connectTime_checkpoint), host->artsOffered - host->artsOffered_checkpoint, host->artsAccepted - host->artsAccepted_checkpoint, host->artsNotWanted - host->artsNotWanted_checkpoint, host->artsRejected - host->artsRejected_checkpoint, host->artsMissing - host->artsMissing_checkpoint, host->artsSizeAccepted - host->artsSizeAccepted_checkpoint, host->artsSizeRejected - host->artsSizeRejected_checkpoint, host->artsToTape - host->artsToTape_checkpoint, host->artsHostClose - host->artsHostClose_checkpoint, host->artsFromTape - host->artsFromTape_checkpoint, host->artsDeferred - host->artsDeferred_checkpoint, (double)host->dlAccum/cnt, host->artsCxnDrop - host->artsCxnDrop_checkpoint, (double)host->blAccum/cnt, hostHighwater, (100.0*host->blNone)/cnt, (100.0*host->blQuartile[0])/cnt, (100.0*host->blQuartile[1])/cnt, (100.0*host->blQuartile[2])/cnt, (100.0*host->blQuartile[3])/cnt, (100.0*host->blFull)/cnt); host->connectTime_checkpoint = now; host->artsOffered_checkpoint = host->artsOffered; host->artsAccepted_checkpoint = host->artsAccepted; host->artsNotWanted_checkpoint = host->artsNotWanted; host->artsRejected_checkpoint = host->artsRejected; host->artsMissing_checkpoint = host->artsMissing; host->artsSizeAccepted_checkpoint = host->artsSizeAccepted; host->artsSizeRejected_checkpoint = host->artsSizeRejected; host->artsToTape_checkpoint = host->artsToTape; host->artsHostClose_checkpoint = host->artsHostClose; host->artsFromTape_checkpoint = host->artsFromTape; host->artsDeferred_checkpoint = host->artsDeferred; host->artsCxnDrop_checkpoint = host->artsCxnDrop; if (final) { notice("%s final seconds %ld offered %d accepted %d refused %d rejected %d" " missing %d accsize %.0f rejsize %.0f spooled %d on_close %d unspooled %d" " deferred %d/%.1f requeued %d" " queue %.1f/%d:%.0f,%.0f,%.0f,%.0f,%.0f,%.0f", host->params->peerName, (long) (now - host->connectTime), host->artsOffered, host->artsAccepted, host->artsNotWanted, host->artsRejected, host->artsMissing, host->artsSizeAccepted, host->artsSizeRejected, host->artsToTape, host->artsHostClose, host->artsFromTape, host->artsDeferred, (double)host->dlAccum/cnt, host->artsCxnDrop, (double)host->blAccum/cnt, hostHighwater, (100.0*host->blNone)/cnt, (100.0*host->blQuartile[0])/cnt, (100.0*host->blQuartile[1])/cnt, (100.0*host->blQuartile[2])/cnt, (100.0*host->blQuartile[3])/cnt, (100.0*host->blFull)/cnt); } } if (logConnectionStats) { unsigned int i ; for (i = 0 ; i < host->maxConnections ; i++) if (host->connections [i] != NULL && host->cxnActive [i]) cxnLogStats (host->connections [i],final) ; } /* one 'spooling backlog' message per stats logging period */ host->loggedBacklog = false ; host->loggedModeOn = host->loggedModeOff = false ; if (final) { /* We also reset checkpoints because the same host structure * may be used again. */ host->artsOffered = 0 ; host->artsOffered_checkpoint = 0 ; host->artsAccepted = 0 ; host->artsAccepted_checkpoint = 0 ; host->artsNotWanted = 0 ; host->artsNotWanted_checkpoint = 0 ; host->artsRejected = 0 ; host->artsRejected_checkpoint = 0 ; host->artsDeferred = 0 ; host->artsDeferred_checkpoint = 0 ; host->artsMissing = 0 ; host->artsMissing_checkpoint = 0 ; host->artsToTape = 0 ; host->artsToTape_checkpoint = 0 ; host->artsQueueOverflow = 0 ; host->artsCxnDrop = 0 ; host->artsCxnDrop_checkpoint = 0 ; host->artsHostSleep = 0 ; host->artsHostSleep_checkpoint = 0 ; host->artsHostClose = 0 ; host->artsHostClose_checkpoint = 0 ; host->artsFromTape = 0 ; host->artsFromTape_checkpoint = 0 ; host->artsSizeAccepted = 0 ; host->artsSizeAccepted_checkpoint = 0 ; host->artsSizeRejected = 0 ; host->artsSizeRejected_checkpoint = 0 ; *startPeriod = theTime () ; /* in of case STATS_RESET_PERIOD */ } /* Reset these each log period. */ host->blNone = 0 ; host->blFull = 0 ; host->blQuartile[0] = host->blQuartile[1] = host->blQuartile[2] = host->blQuartile[3] = 0; host->dlAccum = 0; host->blAccum = 0; host->blCount = 0; #if 0 /* XXX turn this section on to get a snapshot at each log period. */ if (gPrintInfo != NULL) gPrintInfo () ; #endif } static double convsize(double size, char **tsize) { double dsize; static char tTB[]="TB"; static char tGB[]="GB"; static char tMB[]="MB"; static char tKB[]="KB"; static char tB []="B"; if (size/((double)1024*1024*1024*1000)>=1.) { dsize=size/((double)1024*1024*1024*1024); *tsize=tTB; } else if (size/(1024*1024*1000)>=1.) { dsize=size/(1024*1024*1024); *tsize=tGB; } else if (size/(1024*1000)>=1.) { dsize=size/(1024*1024); *tsize=tMB; } else if (size/1000>=1.) { dsize=size/1024; *tsize=tKB; } else { dsize=size; *tsize=tB; } return dsize; } /* * Log the status of the Hosts. */ static void hostLogStatus (void) { FILE *fp = NULL ; Host h ; bool anyToLog = false ; unsigned int peerNum = 0, actConn = 0, slpConn = 0, maxcon = 0 ; static bool logged = false ; static bool flogged = false ; if (statusFile == NULL && !logged) { syslog (LOG_ERR,"No status file to write to") ; logged = true ; return ; } logged = false ; for (h = gHostList ; h != NULL ; h = h->next) if (h->myTape != NULL) /* the host deletes its tape when it's closing */ { anyToLog = true ; peerNum++ ; actConn += h->activeCxns ; slpConn += h->sleepingCxns ; maxcon += h->maxConnections ; } if (!anyToLog) return ; lastStatusLog = theTime() ; TMRstart(TMR_STATUSFILE); if ((fp = fopen (statusFile,"w")) == NULL) { if ( !flogged ) syswarn ("ME oserr status file open: %s", statusFile) ; flogged = true ; } else { char timeString [30] ; time_t now ; long sec ; long offered ; double size, totalsize; char *tsize; flogged = false ; now = time (NULL) ; sec = (long) (now - start) ; timeToString (now, timeString, sizeof (timeString)) ; if (genHtml) { fprintf (fp, "\n" "\n" "\n" "\n" "\n") ; fprintf (fp, "\n"); fprintf (fp, "
\n");
	}

      fprintf (fp,"innfeed from %s\npid %d started %s\n\nUpdated: %s\n",
               INN_VERSION_STRING,(int) myPid,startTime,timeString) ;
      fprintf (fp,"Stats period: %-5ld    Stats reset: %ld\n",
               (long) statsPeriod, (long) statsResetPeriod);
      fprintf (fp,"(peers: %d active-cxns: %d sleeping-cxns: %d idle-cxns: %d)\n\n",
               peerNum, actConn, slpConn,(maxcon - (actConn + slpConn))) ;

      fprintf (fp,"Configuration file: %s\n\n",configFile) ;
      
      if (genHtml)
      {
        fprintf (fp, "
\n"); fprintf (fp,"
    \n"); for (h = gHostList ; h != NULL ; h = h->next) fprintf (fp,"
  • %s
  • \n", h->params->peerName, h->params->peerName); fprintf (fp,"
\n\n"); fprintf (fp,"
\n");
      }

      mainLogStatus (fp) ;
      listenerLogStatus (fp) ;

/*
Default peer configuration parameters:
    article timeout: 600       initial connections: 1
   response timeout: 300           max connections: 5
       close period: 6000               max checks: 25
     want streaming: true           dynamic method: 1
        no-check on: 95.0%     dynamic backlog low: 25%
       no-check off: 90.0%    dynamic backlog high: 50%
    no-check filter: 50.0   dynamic backlog filter: 0.7
  backlog low limit: 1024                 port num: 119
 backlog high limit: 1280       backlog feed first: false
     backlog factor: 1.1
*/
      fprintf(fp,"%sDefault peer configuration parameters:%s\n",
              genHtml ? "" : "", genHtml ? "" : "") ;
      fprintf(fp,"    article timeout: %-5d     initial connections: %d\n",
	    defaultParams->articleTimeout,
	    defaultParams->initialConnections) ;
      fprintf(fp,"   response timeout: %-5d         max connections: %d\n",
	    defaultParams->responseTimeout,
	    defaultParams->absMaxConnections) ;
      fprintf(fp,"  reconnection time: %-5d   max reconnection time: %d\n",
              init_reconnect_period, max_reconnect_period);
      fprintf(fp,"       close period: %-5d              max checks: %d\n",
	    defaultParams->closePeriod,
	    defaultParams->maxChecks) ;
      fprintf(fp,"   DNS retry period: %-5d       DNS expire period: %d\n", 
              dnsRetPeriod, dnsExpPeriod);
      fprintf(fp,"           port num: %-5d              force IPv4: %s\n",
              defaultParams->portNum,
              defaultParams->forceIPv4 ? "true " : "false");
      fprintf(fp,"     want streaming: %-5s          dynamic method: %d\n",
	    defaultParams->wantStreaming ? "true " : "false",
	    defaultParams->dynamicMethod) ;
      fprintf(fp,"        no-check on: %-2.1f%%     dynamic backlog low: %-2.1f%%\n",
	    defaultParams->lowPassHigh,
	    defaultParams->dynBacklogLowWaterMark) ;
      fprintf(fp,"       no-check off: %-2.1f%%    dynamic backlog high: %-2.1f%%\n",
	    defaultParams->lowPassLow,
	    defaultParams->dynBacklogHighWaterMark) ;
      fprintf(fp,"    no-check filter: %-2.1f   dynamic backlog filter: %-2.1f\n",
	    defaultParams->lowPassFilter,
	    defaultParams->dynBacklogFilter) ;
      fprintf(fp,"  backlog limit low: %-7d         drop-deferred: %s\n",
	    defaultParams->backlogLimit,
	    defaultParams->dropDeferred ? "true " : "false");
      fprintf(fp," backlog limit high: %-7d         min-queue-cxn: %s\n",
	    defaultParams->backlogLimitHigh,
	    defaultParams->minQueueCxn ? "true " : "false");
      fprintf(fp," backlog feed first: %s\n",
           defaultParams->backlogFeedFirst ? "true " : "false");
      fprintf(fp,"     backlog factor: %1.1f\n\n",
	    defaultParams->backlogFactor);

      tapeLogGlobalStatus (fp) ;

      fprintf (fp,"\n") ;
      fprintf(fp,"%sglobal (process)%s\n",
              genHtml ? "" : "", genHtml ? "" : "") ;
      
      fprintf (fp, "   seconds: %ld\n", sec) ;
      if (sec == 0) sec = 1 ;
      offered = procArtsOffered ? procArtsOffered : 1 ;
      totalsize = procArtsSizeAccepted+procArtsSizeRejected ;
      if (totalsize == 0) totalsize = 1. ;

      fprintf (fp, "   offered: %-5ld\t%6.2f art/s\n",
		procArtsOffered,
		(double)procArtsOffered/sec) ;
      fprintf (fp, "  accepted: %-5ld\t%6.2f art/s\t%5.1f%%\n",
		procArtsAccepted,
		(double)procArtsAccepted/sec,
		(double)procArtsAccepted*100./offered) ;
      fprintf (fp, "   refused: %-5ld\t%6.2f art/s\t%5.1f%%\n",
		procArtsNotWanted,
		(double)procArtsNotWanted/sec,
		(double)procArtsNotWanted*100./offered) ;
      fprintf (fp, "  rejected: %-5ld\t%6.2f art/s\t%5.1f%%\n",
		procArtsRejected,
		(double)procArtsRejected/sec,
		(double)procArtsRejected*100./offered) ;
      fprintf (fp, "   missing: %-5ld\t%6.2f art/s\t%5.1f%%\n",
		procArtsMissing,
		(double)procArtsMissing/sec,
		(double)procArtsMissing*100./offered) ;
      fprintf (fp, "  deferred: %-5ld\t%6.2f art/s\t%5.1f%%\n",
		procArtsDeferred,
		(double)procArtsDeferred/sec,
		(double)procArtsDeferred*100./offered) ;

      size=convsize(procArtsSizeAccepted, &tsize);
      fprintf (fp, "accpt size: %.3g %s", size, tsize) ;
      size=convsize(procArtsSizeAccepted/sec, &tsize);
      fprintf (fp, " \t%6.3g %s/s\t%5.1f%%\n",
		size, tsize,
		procArtsSizeAccepted*100./totalsize) ;

      size=convsize(procArtsSizeRejected, &tsize);
      fprintf (fp, "rejct size: %.3g %s", size, tsize) ;
      size=convsize(procArtsSizeRejected/sec, &tsize);
      fprintf (fp, " \t%6.3g %s/s\t%5.1f%%\n",
		size, tsize,
		procArtsSizeRejected*100./totalsize) ;

      fprintf (fp, "\n");

      for (h = gHostList ; h != NULL ; h = h->next)
        hostPrintStatus (h,fp) ;

      if (genHtml) 
	{
          fprintf (fp,"
\n") ; fprintf (fp,"\n") ; fprintf (fp,"\n") ; } fclose (fp) ; } TMRstop(TMR_STATUSFILE); } /* * This prints status information for each host. An example of the * format of the output is: * * sitename * Addr 1: IPv4 12.0.0.42 * seconds: 351 art. timeout: 400 ip name: foo.bar * offered: 1194 resp. timeout: 240 port: 119 * accepted: 178 want streaming: yes active cxns: 6 * refused: 948 is streaming: yes sleeping cxns: 0 * rejected: 31 max checks: 25 initial cxns: 5 * missing: 0 no-check on: 95.0% idle cxns: 4 * deferred: 0 no-check off: 95.0% max cxns: 8/10 * requeued: 0 no-check fltr: 50.0 queue length: 0.0/200 * spooled: 0 dynamic method: 0 empty: 100.0% *[overflow]: 0 dyn b'log low: 25% >0%-25%: 0.0% *[on_close]: 0 dyn b'log high: 50% 25%-50%: 0.0% *[sleeping]: 0 dyn b'log stat: 37% 50%-75%: 0.0% * unspooled: 0 dyn b'log fltr: 0.7 75%-<100%: 0.0% * no queue: 1234 avr.cxns queue: 0.0 full: 0.0% *accpt size: 121.1 MB drop-deferred: false defer length: 0 *rejct size: 27.1 MB min-queue-cxn: false * backlog low limit: 1000000 * backlog high limit: 2000000 (factor 2.0) * backlog shrinkage: 0 bytes (from current file) * offered: 1.13 art/s accepted: 0.69 art/s (101.71 KB/s) * refused: 0.01 art/s rejected: 0.42 art/s (145.11 KB/s) * missing 0 spooled 0 * */ static void hostPrintStatus (Host host, FILE *fp) { time_t now = theTime() ; double cnt = (host->blCount) ? (host->blCount) : 1.0; double size; char *tsize; char buf[]="1234.1 MB"; ASSERT (host != NULL) ; ASSERT (fp != NULL) ; if (genHtml) fprintf (fp,"%s",host->params->peerName, host->params->peerName); else fprintf (fp,"%s",host->params->peerName); if (host->blockedReason != NULL) fprintf (fp," (remote status: ``%s'')",host->blockedReason) ; fputc ('\n',fp) ; if (host->ipAddrs) { int i; char ip_addr[INET6_ADDRSTRLEN]; const char *family; for(i = 0; host->ipAddrs[i] != NULL; i++) { switch(host->ipAddrs[i]->sa_family) { case AF_INET: family = "IPv4"; break; #ifdef HAVE_INET6 case AF_INET6: family = "IPv6"; break; #endif default: family = "????"; break; } network_sockaddr_sprint(ip_addr, sizeof(ip_addr), host->ipAddrs[i]); fprintf(fp, " Addr %-2u: %-4.4s %s\n", i+1, family, ip_addr); } } fprintf (fp, " seconds: %-7ld art. timeout: %-5d ip name: %s\n", host->firstConnectTime > 0 ? (long)(now - host->firstConnectTime) : 0, host->params->articleTimeout, host->params->ipName) ; fprintf (fp, " offered: %-7ld resp. timeout: %-5d port: %d\n", (long) host->gArtsOffered, host->params->responseTimeout, host->params->portNum); fprintf (fp, " accepted: %-7ld want streaming: %s active cxns: %d\n", (long) host->gArtsAccepted, (host->params->wantStreaming ? "yes" : "no "), host->activeCxns) ; fprintf (fp, " refused: %-7ld is streaming: %s sleeping cxns: %d\n", (long) host->gArtsNotWanted, (host->remoteStreams ? "yes" : "no "), host->sleepingCxns) ; fprintf (fp, " rejected: %-7ld max checks: %-5d initial cxns: %d\n", (long) host->gArtsRejected, host->params->maxChecks, host->params->initialConnections) ; fprintf (fp, " missing: %-7ld no-check on: %-3.1f%% idle cxns: %d\n", (long) host->gArtsMissing, host->params->lowPassHigh, host->maxConnections - (host->activeCxns + host->sleepingCxns)) ; fprintf (fp, " deferred: %-7ld no-check off: %-3.1f%% max cxns: %d/%d\n", (long) host->gArtsDeferred, host->params->lowPassLow, host->maxConnections, host->params->absMaxConnections) ; fprintf (fp, " requeued: %-7ld no-check fltr: %-3.1f queue length: %-3.1f/%d\n", (long) host->gArtsCxnDrop, host->params->lowPassFilter, (double)host->blAccum / cnt, hostHighwater) ; fprintf (fp, " spooled: %-7ld dynamic method: %-5d empty: %-3.1f%%\n", (long) host->gArtsToTape, host->params->dynamicMethod, 100.0 * host->blNone / cnt) ; fprintf (fp, "[overflow]: %-7ld dyn b'log low: %-3.1f%% >0%%-25%%: %-3.1f%%\n", (long) host->gArtsQueueOverflow, host->params->dynBacklogLowWaterMark, 100.0 * host->blQuartile[0] / cnt) ; fprintf (fp, "[on_close]: %-7ld dyn b'log high: %-3.1f%% 25%%-50%%: %-3.1f%%\n", (long) host->gArtsHostClose, host->params->dynBacklogHighWaterMark, 100.0 * host->blQuartile[1] / cnt) ; fprintf (fp, "[sleeping]: %-7ld dyn b'log stat: %-3.1f%% 50%%-75%%: %-3.1f%%\n", (long) host->gArtsHostSleep, host->backlogFilter*100.0*(1.0-host->params->dynBacklogFilter), 100.0 * host->blQuartile[2] / cnt) ; fprintf (fp, " unspooled: %-7ld dyn b'log fltr: %-3.1f 75%%-<100%%: %-3.1f%%\n", (long) host->gArtsFromTape, host->params->dynBacklogLowWaterMark, 100.0 * host->blQuartile[3] / cnt) ; fprintf (fp, " no queue: %-7ld avr.cxns queue: %-3.1f full: %-3.1f%%\n", (long) host->gNoQueue, (double) host->gCxnQueue / (host->gArtsOffered ? host->gArtsOffered :1) , 100.0 * host->blFull / cnt) ; size=convsize(host->gArtsSizeAccepted, &tsize); snprintf(buf,sizeof(buf),"%.3g %s", size, tsize); fprintf (fp, "accpt size: %-8s drop-deferred: %-5s defer length: %-3.1f\n", buf, host->params->dropDeferred ? "true " : "false", (double)host->dlAccum / cnt) ; size=convsize(host->gArtsSizeRejected, &tsize); snprintf(buf,sizeof(buf),"%.3g %s", size, tsize); fprintf (fp, "rejct size: %-8s min-queue-cxn: %s\n", buf, host->params->minQueueCxn ? "true " : "false"); tapeLogStatus (host->myTape,fp) ; { time_t sec = (time_t) (now - host->connectTime); double or, ar, rr, jr; double ars, jrs; char *tars, *tjrs; if (sec != 0) { or = (double) host->artsOffered / (double) sec; ar = (double) host->artsAccepted / (double) sec; rr = (double) host->artsNotWanted / (double) sec; jr = (double) host->artsRejected / (double) sec; ars = convsize (host->artsSizeAccepted/sec, &tars); jrs = convsize (host->artsSizeRejected/sec, &tjrs); fprintf(fp, " offered: %5.2f art/s accepted: %5.2f art/s, %.3g %s/s\n", or, ar, ars, tars); fprintf(fp, " refused: %5.2f art/s rejected: %5.2f art/s, %.3g %s/s\n", rr, jr, jrs, tjrs); } fprintf(fp, " missing %d spooled %d\n", host->artsMissing, host->artsToTape); } #ifdef XXX_STATSHACK { time_t now = time(NULL), sec = (long) (now - host->connectTime); float or, ar, rr, jr; if (sec != 0) { or = (float) host->artsOffered / (float) sec; ar = (float) host->artsAccepted / (float) sec; rr = (float) host->artsNotWanted / (float) sec; jr = (float) host->artsRejected / (float) sec; fprintf(fp, "\t\tor %02.2f ar %02.2f rr %02.2f jr %02.2f\n", or, ar, rr, jr); } fprintf(fp, "\tmissing %d spooled %d\n", host->artsMissing,host->backlogSpooled); } #endif /* XXX_STATSHACK */ fprintf (fp, "\n\n"); } /* * The callback function for the statistics timer to call. */ static void hostStatsTimeoutCbk (TimeoutId tid UNUSED, void *data) { Host host = (Host) data ; time_t now = theTime () ; ASSERT (tid == host->statsId) ; if (!amClosing (host)) hostLogStats (host, false) ; if (now - lastStatusLog >= statsPeriod) hostLogStatus () ; host->statsId = prepareSleep (hostStatsTimeoutCbk, statsPeriod, host) ; } /* * The callback function for the deferred article timer to call. */ static void hostDeferredArtCbk (TimeoutId tid UNUSED, void *data) { Host host = (Host) data ; time_t now = theTime () ; Article article ; ASSERT (tid == host->deferredId) ; while (host->deferred && host->deferred->whenToRequeue <= now) { article = remHead (&host->deferred,&host->deferredTail) ; host->deferLen-- ; hostSendArticle (host, article) ; /* requeue it */ } if (host->deferred) host->deferredId = prepareSleep (hostDeferredArtCbk, host->deferred->whenToRequeue - now, host) ; else host->deferredId = 0; } /* if the host has too many unprocessed articles so we send some to the tape. */ static void backlogToTape (Host host) { Article article ; while ((host->backlog + host->deferLen) > hostHighwater) { if (!host->loggedBacklog) { host->loggedBacklog = true ; } if (host->deferred != NULL) { article = remHead (&host->deferred,&host->deferredTail) ; host->deferLen--; } else { article = remHead (&host->queued,&host->queuedTail) ; host->backlog--; } ASSERT(article != NULL); host->artsQueueOverflow++ ; host->gArtsQueueOverflow++ ; host->artsToTape++ ; host->gArtsToTape++ ; procArtsToTape++ ; tapeTakeArticle (host->myTape,article) ; } } /* * Returns true of the Host is in the middle of closing down. */ static bool amClosing (Host host) { return (host->myTape == NULL ? true : false) ; } /* * flush all queued articles all the way out to disk. */ static void queuesToTape (Host host) { Article art ; while ((art = remHead (&host->processed,&host->processedTail)) != NULL) { host->artsHostClose++ ; host->gArtsHostClose++ ; host->artsToTape++ ; host->gArtsToTape++ ; procArtsToTape++ ; tapeTakeArticle (host->myTape,art) ; } while ((art = remHead (&host->queued,&host->queuedTail)) != NULL) { host->backlog-- ; host->artsHostClose++ ; host->gArtsHostClose++ ; host->artsToTape++ ; host->gArtsToTape++ ; procArtsToTape++ ; tapeTakeArticle (host->myTape,art) ; } while ((art = remHead (&host->deferred,&host->deferredTail)) != NULL) { host->deferLen-- ; host->artsHostClose++ ; host->gArtsHostClose++ ; host->artsToTape++ ; host->gArtsToTape++ ; procArtsToTape++ ; tapeTakeArticle (host->myTape,art) ; } while ((art = remHead (&host->deferred,&host->deferredTail)) != NULL) { host->deferLen-- ; host->artsHostClose++ ; host->gArtsHostClose++ ; host->artsToTape++ ; host->gArtsToTape++ ; procArtsToTape++ ; tapeTakeArticle (host->myTape,art) ; } } #define QUEUE_ELEM_POOL_SIZE ((4096 - 2 * (sizeof (void *))) / (sizeof (struct proc_q_elem))) static ProcQElem queueElemPool ; /* * Add an article to the given queue. */ static void queueArticle (Article article, ProcQElem *head, ProcQElem *tail, time_t when) { ProcQElem elem ; if (queueElemPool == NULL) { unsigned int i ; queueElemPool = xmalloc (sizeof(struct proc_q_elem) * QUEUE_ELEM_POOL_SIZE) ; for (i = 0; i < QUEUE_ELEM_POOL_SIZE - 1; i++) queueElemPool[i] . next = &(queueElemPool [i + 1]) ; queueElemPool [QUEUE_ELEM_POOL_SIZE-1] . next = NULL ; } elem = queueElemPool ; ASSERT (elem != NULL) ; queueElemPool = queueElemPool->next ; elem->article = article ; elem->next = NULL ; elem->prev = *tail ; elem->whenToRequeue = when ; if (*tail != NULL) (*tail)->next = elem ; else *head = elem ; *tail = elem ; } /* * remove the article from the queue */ static bool remArticle (Article article, ProcQElem *head, ProcQElem *tail) { ProcQElem elem ; ASSERT (head != NULL) ; ASSERT (tail != NULL) ; /* we go backwards down the list--probably faster */ elem = *tail ; while (elem != NULL && elem->article != article) elem = elem->prev ; if (elem != NULL) { if (elem->prev != NULL) elem->prev->next = elem->next ; if (elem->next != NULL) elem->next->prev = elem->prev ; if (*head == elem) *head = elem->next ; if (*tail == elem) *tail = elem->prev ; elem->next = queueElemPool ; queueElemPool = elem ; return true ; } else return false ; } /* * remove the article that's at the head of the queue and return * it. Returns NULL if the queue is empty. */ static Article remHead (ProcQElem *head, ProcQElem *tail) { ProcQElem elem ; Article art ; ASSERT (head != NULL) ; ASSERT (tail != NULL) ; ASSERT ((*head == NULL && *tail == NULL) || (*head != NULL && *tail != NULL)) ; if (*head == NULL) return NULL ; elem = *head ; art = elem->article ; *head = elem->next ; if (elem->next != NULL) elem->next->prev = NULL ; if (*tail == elem) *tail = NULL ; elem->next = queueElemPool ; queueElemPool = elem ; return art ; } static int validateInteger (FILE *fp, const char *name, long low, long high, int required, long setval, scope * sc, unsigned int inh) { int rval = VALUE_OK ; value *v ; scope *s ; char *p = strrchr (name,':') ; v = findValue (sc,name,inh) ; if (v == NULL && required != NOTREQNOADD) { s = findScope (sc,name,0) ; addInteger (s,p ? p + 1 : name,setval) ; if (required == REQ) { rval = VALUE_MISSING ; logOrPrint (LOG_ERR,fp, "ME config: no definition for required key %s",name) ; } else logOrPrint (LOG_INFO,fp, "ME config: adding default value for key %s: %ld",name ,setval) ; } else if (v != NULL && v->type != intval) { rval = VALUE_WRONG_TYPE ; logOrPrint (LOG_ERR,fp,"ME config: value of %s is not an integer",name) ; } else if (v != NULL && low != LONG_MIN && v->v.int_val < low) { rval = VALUE_TOO_LOW ; logOrPrint (LOG_ERR,fp, "ME config: value of %s (%ld) in %s is lower than minimum" " of %ld. Using %ld",name,v->v.int_val, "global scope",low,low) ; v->v.int_val = low ; } else if (v != NULL && high != LONG_MAX && v->v.int_val > high) { rval = VALUE_TOO_HIGH ; logOrPrint(LOG_ERR,fp, "ME config: value of %s (%ld) in %s is higher than maximum" " of %ld. Using %ld",name,v->v.int_val, "global scope",high,high); v->v.int_val = high ; } return rval ; } static int validateReal (FILE *fp, const char *name, double low, double high, int required, double setval, scope * sc, unsigned int inh) { int rval = VALUE_OK ; value *v ; scope *s ; char *p = strrchr (name,':') ; v = findValue (sc,name,inh) ; if (v == NULL && required != NOTREQNOADD) { s = findScope (sc,name,0) ; addReal (s,p ? p + 1 : name,setval) ; if (required == REQ) { rval = VALUE_MISSING ; logOrPrint (LOG_ERR,fp, "ME config: no definition for required key %s",name) ; } else logOrPrint (LOG_INFO,fp, "ME config: adding default value for key %s: %f",name, setval) ; } else if (v != NULL && v->type != realval) { rval = VALUE_WRONG_TYPE ; logOrPrint (LOG_ERR,fp, "ME config: value of %s is not a floating point number", name) ; } else if (v != NULL && low != -DBL_MAX && v->v.real_val < low) { logOrPrint (LOG_ERR,fp, "ME config: value of %s (%f) is lower than minimum of %f", name,v->v.real_val,low) ; v->v.real_val = setval ; } else if (v != NULL && high != DBL_MAX && v->v.real_val > high) { logOrPrint (LOG_ERR,fp, "ME config: value of %s (%f) is higher than maximum of %f", name,v->v.real_val,high) ; v->v.real_val = setval ; } return rval ; } static int validateBool (FILE *fp, const char *name, int required, bool setval, scope * sc, unsigned int inh) { int rval = VALUE_OK ; value *v ; scope *s ; char *p = strrchr (name,':') ; v = findValue (sc,name,inh) ; if (v == NULL && required != NOTREQNOADD) { s = findScope (sc,name,0) ; addBoolean (s,p ? p + 1 : name, setval ? 1 : 0) ; if (required == REQ) { rval = VALUE_MISSING ; logOrPrint (LOG_ERR,fp, "ME config: no definition for required key %s",name) ; } else logOrPrint (LOG_INFO,fp, "ME config: adding default value for key %s: %s",name, (setval ? "true" : "false")) ; } else if (v != NULL && v->type != boolval) { rval = VALUE_WRONG_TYPE ; logOrPrint (LOG_ERR,fp,"ME config: value of %s is not a boolean",name) ; } return rval ; } void gCalcHostBlStat (void) { Host h ; for (h = gHostList ; h != NULL ; h = h->next) { h->dlAccum += h->deferLen ; h->blAccum += h->backlog ; if (h->backlog == 0) h->blNone++ ; else if (h->backlog >= (hostHighwater - h->deferLen)) h->blFull++ ; else h->blQuartile[(4*h->backlog) / (hostHighwater - h->deferLen)]++ ; h->blCount++ ; } } static void hostCleanup (void) { if (statusFile != NULL) free (statusFile) ; statusFile = NULL ; } inn-2.6.0/storage/0000755000175200017520000000000012575023701013363 5ustar iuliusiuliusinn-2.6.0/storage/tradindexed/0000755000175200017520000000000012575023701015656 5ustar iuliusiuliusinn-2.6.0/storage/tradindexed/ovmethod.mk0000644000175200017520000000047512575023702020043 0ustar iuliusiuliustradindexed/tdx-util.$(EXTOBJ): tradindexed/tdx-util.c $(LIBCC) $(CFLAGS) -c -o $@ tradindexed/tdx-util.c tradindexed/tdx-util: tradindexed/tdx-util.$(EXTOBJ) libstorage.$(EXTLIB) $(LIBHIST) $(LIBLD) $(LDFLAGS) -o $@ tradindexed/tdx-util.$(EXTOBJ) \ $(LIBSTORAGE) $(LIBHIST) $(LIBINN) $(STORAGE_LIBS) $(LIBS) inn-2.6.0/storage/tradindexed/tdx-data.c0000644000175200017520000007774612575023702017556 0ustar iuliusiulius/* $Id: tdx-data.c 9659 2014-08-30 08:08:11Z iulius $ ** ** Overview data file handling for the tradindexed overview method. ** ** Implements the handling of the .IDX and .DAT files for the tradindexed ** overview method. The .IDX files are flat arrays of binary structs ** specifying the offset in the data file of the overview data for a given ** article as well as the length of that data and some additional meta-data ** about that article. The .DAT files contain all of the overview data for ** that group in wire format. ** ** Externally visible functions have a tdx_ prefix; internal functions do ** not. (Externally visible unfortunately means everything that needs to be ** visible outside of this object file, not just interfaces exported to ** consumers of the overview API.) */ #include "config.h" #include "clibrary.h" #include "portable/mmap.h" #include #include #include #include "inn/fdflag.h" #include "inn/history.h" #include "inn/innconf.h" #include "inn/messages.h" #include "inn/mmap.h" #include "inn/libinn.h" #include "inn/ov.h" #include "ovinterface.h" #include "inn/storage.h" #include "tdx-private.h" #include "tdx-structure.h" /* Returned to callers as an opaque data type, this holds the information needed to manage a search in progress. */ struct search { ARTNUM limit; ARTNUM current; struct group_data *data; }; /* Internal prototypes. */ static char *group_path(const char *group); static int file_open(const char *base, const char *suffix, bool writable, bool append); static bool file_open_index(struct group_data *, const char *suffix); static bool file_open_data(struct group_data *, const char *suffix); static void *map_file(int fd, size_t length, const char *base, const char *suffix); static bool map_index(struct group_data *data); static bool map_data(struct group_data *data); static void unmap_index(struct group_data *data); static void unmap_data(struct group_data *data); static ARTNUM index_base(ARTNUM artnum); /* ** Determine the path to the data files for a particular group and return ** it. Allocates memory which the caller is responsible for freeing. */ static char * group_path(const char *group) { char *path, *p; size_t length; const char *gp; /* The path of the data files for news.groups is dir/n/g/news.groups. In other words, the first letter of each component becomes a directory. The length of the path is therefore the length of the base overview directory path, one character for the slash, two characters for the first letter and initial slash, two characters for each hierarchical level of the group, and then the length of the group name. For robustness, we want to handle leading or multiple consecutive periods. We only recognize a new hierarchical level after a string of periods (which doesn't end the group name). */ length = strlen(innconf->pathoverview); for (gp = group; *gp != '\0'; gp++) if (*gp == '.') { if (gp[1] == '.' || gp[0] == '\0') continue; length += 2; } length += 1 + 2 + strlen(group) + 1; path = xmalloc(length); strlcpy(path, innconf->pathoverview, length); p = path + strlen(innconf->pathoverview); /* Generate the hierarchical directories. */ if (*group != '.' && *group != '\0') { *p++ = '/'; *p++ = *group; } for (gp = strchr(group, '.'); gp != NULL; gp = strchr(gp, '.')) { gp++; if (gp == group + 1) continue; if (*gp != '\0' && *gp != '.' && *gp != '/') { *p++ = '/'; *p++ = *gp; } } *p++ = '/'; /* Finally, append the group name to the generated path and then replace all slashes with commas. Commas have the advantage of being clearly illegal in newsgroup names because of the syntax of the Newsgroups header, but aren't shell metacharacters. */ strlcpy(p, group, length - (p - path)); for (; *p != '\0'; p++) if (*p == '/') *p = ','; return path; } /* ** Open a data file for a group. Takes the base portion of the file, the ** suffix, a bool saying whether or not the file is being opened for write, ** and a bool saying whether to open it for append. Returns the file ** descriptor. */ static int file_open(const char *base, const char *suffix, bool writable, bool append) { char *file; int flags, fd; file = concat(base, ".", suffix, (char *) 0); flags = writable ? (O_RDWR | O_CREAT) : O_RDONLY; if (append) flags |= O_APPEND; fd = open(file, flags, ARTFILE_MODE); if (fd < 0 && writable && errno == ENOENT) { char *p = strrchr(file, '/'); *p = '\0'; if (!MakeDirectory(file, true)) { syswarn("tradindexed: cannot create directory %s", file); free(file); return -1; } *p = '/'; fd = open(file, flags, ARTFILE_MODE); } if (fd < 0) { if (errno != ENOENT) syswarn("tradindexed: cannot open %s", file); free(file); return -1; } free(file); return fd; } /* ** Open the index file for a group. Takes an optional suffix to use instead ** of IDX (used primarily for expiring). */ static bool file_open_index(struct group_data *data, const char *suffix) { struct stat st; if (suffix == NULL) suffix = "IDX"; if (data->indexfd >= 0) close(data->indexfd); data->indexfd = file_open(data->path, suffix, data->writable, false); if (data->indexfd < 0) return false; if (fstat(data->indexfd, &st) < 0) { syswarn("tradindexed: cannot stat %s.%s", data->path, suffix); close(data->indexfd); return false; } data->indexinode = st.st_ino; fdflag_close_exec(data->indexfd, true); return true; } /* ** Open the data file for a group. Takes an optional suffix to use instead ** of DAT (used primarily for expiring). */ static bool file_open_data(struct group_data *data, const char *suffix) { if (suffix == NULL) suffix = "DAT"; if (data->datafd >= 0) close(data->datafd); data->datafd = file_open(data->path, suffix, data->writable, true); if (data->datafd < 0) return false; fdflag_close_exec(data->datafd, true); return true; } /* ** Open a particular group. Allocates a new struct group_data that should be ** passed to tdx_data_close() when the caller is done with it. */ struct group_data * tdx_data_new(const char *group, bool writable) { struct group_data *data; data = xmalloc(sizeof(struct group_data)); data->path = group_path(group); data->writable = writable; data->remapoutoforder = false; data->high = 0; data->base = 0; data->indexfd = -1; data->datafd = -1; data->index = NULL; data->data = NULL; data->indexlen = 0; data->datalen = 0; data->indexinode = 0; data->refcount = 0; return data; } /* ** Open the index and data files for a group. */ bool tdx_data_open_files(struct group_data *data) { unmap_index(data); unmap_data(data); data->index = NULL; data->data = NULL; if (!file_open_index(data, NULL)) goto fail; if (!file_open_data(data, NULL)) goto fail; return true; fail: if (data->indexfd >= 0) close(data->indexfd); if (data->datafd >= 0) close(data->datafd); return false; } /* ** Map a data file (either index or data), or read in all of the data in the ** file if we're avoiding mmap. Takes the base and suffix of the file for ** error reporting. */ static void * map_file(int fd, size_t length, const char *base, const char *suffix) { char *data; if (length == 0) return NULL; if (!innconf->tradindexedmmap) { ssize_t status; data = xmalloc(length); status = read(fd, data, length); if ((size_t) status != length) { syswarn("tradindexed: cannot read data file %s.%s", base, suffix); free(data); return NULL; } } else { data = mmap(NULL, length, PROT_READ, MAP_SHARED, fd, 0); if (data == MAP_FAILED) { syswarn("tradindexed: cannot mmap %s.%s", base, suffix); return NULL; } } return data; } /* ** Memory map the index file. */ static bool map_index(struct group_data *data) { struct stat st; int r; r = fstat(data->indexfd, &st); if (r == -1) { if (errno == ESTALE) { r = file_open_index(data, NULL); } else { syswarn("tradindexed: cannot stat %s.IDX", data->path); } } if (r == -1) return false; data->indexlen = st.st_size; data->index = map_file(data->indexfd, data->indexlen, data->path, "IDX"); return (data->index == NULL && data->indexlen > 0) ? false : true; } /* ** Memory map the data file. */ static bool map_data(struct group_data *data) { struct stat st; int r; r = fstat(data->datafd, &st); if (r == -1) { if (errno == ESTALE) { r = file_open_data(data, NULL); } else { syswarn("tradindexed: cannot stat %s.DAT", data->path); } } if (r == -1) return false; data->datalen = st.st_size; data->data = map_file(data->datafd, data->datalen, data->path, "DAT"); return (data->data == NULL && data->indexlen > 0) ? false : true; } /* ** Unmap a data file or free the memory copy if we're not using mmap. Takes ** the memory to free or unmap, the length for munmap, and the name base and ** suffix for error reporting. */ static void unmap_file(void *data, off_t length, const char *base, const char *suffix) { if (data == NULL) return; if (!innconf->tradindexedmmap) free(data); else { if (munmap(data, length) < 0) syswarn("tradindexed: cannot munmap %s.%s", base, suffix); } return; } /* ** Unmap the index file. */ static void unmap_index(struct group_data *data) { unmap_file(data->index, data->indexlen, data->path, "IDX"); data->index = NULL; } /* ** Unmap the data file. */ static void unmap_data(struct group_data *data) { unmap_file(data->data, data->datalen, data->path, "DAT"); data->data = NULL; } /* ** Determine if the file handle associated with the index table is stale */ static bool stale_index(struct group_data *data) { struct stat st; int r; r = fstat(data->indexfd, &st); return r == -1 && errno == ESTALE; } /* ** Determine if the file handle associated with the data table is stale */ static bool stale_data(struct group_data *data) { struct stat st; int r; r = fstat(data->datafd, &st); return r == -1 && errno == ESTALE; } /* ** Retrieves the article metainformation stored in the index table (all the ** stuff we can return without opening the data file). Takes the article ** number and returns a pointer to the index entry. Also takes the high ** water mark from the group index; this is used to decide whether to attempt ** remapping of the index file if the current high water mark is too low. */ const struct index_entry * tdx_article_entry(struct group_data *data, ARTNUM article, ARTNUM high) { struct index_entry *entry; ARTNUM offset; if (article > data->high && high > data->high) { unmap_index(data); map_index(data); data->high = high; /* Mark the data file to be remapped for the next opensearch. */ data->remapoutoforder = true; } else if (innconf->nfsreader && stale_index(data)) unmap_index(data); if (data->index == NULL) if (!map_index(data)) return NULL; if (article < data->base) return NULL; offset = article - data->base; if (offset >= data->indexlen / sizeof(struct index_entry)) return NULL; entry = data->index + offset; if (entry->length == 0) return NULL; return entry; } /* ** Begin an overview search. In addition to the bounds of the search, we ** also take the high water mark from the group index; this is used to decide ** whether or not to attempt remapping of the index file if the current high ** water mark is too low. */ struct search * tdx_search_open(struct group_data *data, ARTNUM start, ARTNUM end, ARTNUM high) { struct search *search; if (end < data->base) return NULL; if (end < start) return NULL; if ((end > data->high && high > data->high) || data->remapoutoforder) { data->remapoutoforder = false; unmap_data(data); unmap_index(data); map_index(data); data->high = high; } if (start > data->high) return NULL; if (innconf->nfsreader && stale_index(data)) unmap_index(data); if (data->index == NULL) if (!map_index(data)) return NULL; if (innconf->nfsreader && stale_data(data)) unmap_data(data); if (data->data == NULL) if (!map_data(data)) return NULL; search = xmalloc(sizeof(struct search)); search->limit = end - data->base; search->current = (start < data->base) ? 0 : start - data->base; search->data = data; search->data->refcount++; return search; } /* ** Return the next record in a search. */ bool tdx_search(struct search *search, struct article *artdata) { struct index_entry *entry; size_t max; if (search == NULL || search->data == NULL) return false; if (search->data->index == NULL || search->data->data == NULL) return false; max = (search->data->indexlen / sizeof(struct index_entry)) - 1; entry = search->data->index + search->current; while (search->current <= search->limit && search->current <= max) { if (entry->length != 0) break; search->current++; entry++; } if (search->current > search->limit || search->current > max) return false; /* There is a small chance that remapping the data file could make this offset accessible, but changing the memory location in the middle of a search conflicts with what we return for OVSTATICSEARCH. And it seems not to be an issue in limited testing, although write caching that leads to on-disk IDX and DAT being out of sync could trigger a problem here. */ if (entry->offset + entry->length > search->data->datalen) { search->data->remapoutoforder = true; warn("Invalid or inaccessible entry for article %lu in %s.IDX:" " offset %lu length %lu datalength %lu", search->current + search->data->base, search->data->path, (unsigned long) entry->offset, (unsigned long) entry->length, (unsigned long) search->data->datalen); return false; } artdata->number = search->current + search->data->base; artdata->overview = search->data->data + entry->offset; artdata->overlen = entry->length; artdata->token = entry->token; artdata->arrived = entry->arrived; artdata->expires = entry->expires; search->current++; return true; } /* ** End an overview search. */ void tdx_search_close(struct search *search) { if (search->data != NULL) { search->data->refcount--; if (search->data->refcount == 0) tdx_data_close(search->data); } free(search); } /* ** Given an article number, return an index base appropriate for that article ** number. This includes a degree of slop so that we don't have to ** constantly repack if the article numbers are clustered around a particular ** value but don't come in order. */ static ARTNUM index_base(ARTNUM artnum) { return (artnum > 128) ? (artnum - 128) : 1; } /* ** Store the data for a single article into the overview files for a group. ** Assumes any necessary repacking has already been done. If the base value ** in the group_data structure is 0, assumes this is the first time we've ** written overview information to this group and sets it appropriately. */ bool tdx_data_store(struct group_data *data, const struct article *article) { struct index_entry entry; off_t offset; if (!data->writable) return false; if (data->base == 0) data->base = index_base(article->number); if (data->base > article->number) { warn("tradindexed: cannot add %lu to %s.IDX, base == %lu", article->number, data->path, data->base); return false; } /* Write out the data and fill in the index entry. */ memset(&entry, 0, sizeof(entry)); if (xwrite(data->datafd, article->overview, article->overlen) < 0) { syswarn("tradindexed: cannot append %lu of data for %lu to %s.DAT", (unsigned long) article->overlen, article->number, data->path); return false; } entry.offset = lseek(data->datafd, 0, SEEK_CUR); if (entry.offset < 0) { syswarn("tradindexed: cannot get offset for article %lu in %s.DAT", article->number, data->path); return false; } entry.length = article->overlen; entry.offset -= entry.length; entry.arrived = article->arrived; entry.expires = article->expires; entry.token = article->token; /* Write out the index entry. */ offset = (article->number - data->base) * sizeof(struct index_entry); if (xpwrite(data->indexfd, &entry, sizeof(entry), offset) < 0) { syswarn("tradindexed: cannot write index record for %lu in %s.IDX", article->number, data->path); return false; } return true; } /* ** Cancels a particular article by removing its index entry. The data will ** still be present in the data file until the next expire run, but it won't ** be returned by searches. */ bool tdx_data_cancel(struct group_data *data, ARTNUM artnum) { static const struct index_entry empty; off_t offset; if (!data->writable) return false; if (data->base == 0 || artnum < data->base || artnum > data->high) return false; offset = (artnum - data->base) * sizeof(struct index_entry); if (xpwrite(data->indexfd, &empty, sizeof(empty), offset) < 0) { syswarn("tradindexed: cannot cancel index record for %lu in %s.IDX", artnum, data->path); return false; } return true; } /* ** Start the process of packing a group (rewriting its index file so that it ** uses a different article base). Takes the article number of an article ** that needs to be written to the index file and is below the current base. ** Returns the true success and false on failure, and sets data->base to the ** new article base and data->indexinode to the new inode number. At the ** conclusion of this routine, the new index file has been created, but it ** has not yet been moved into place; that is done by tdx_data_pack_finish. */ bool tdx_data_pack_start(struct group_data *data, ARTNUM artnum) { ARTNUM base; unsigned long delta; int fd; char *idxfile; struct stat st; if (!data->writable) return false; if (data->base <= artnum) { warn("tradindexed: tdx_data_pack_start called unnecessarily"); return false; } /* Open the new index file. */ base = index_base(artnum); delta = data->base - base; fd = file_open(data->path, "IDX-NEW", true, false); if (fd < 0) return false; if (fstat(fd, &st) < 0) { warn("tradindexed: cannot stat %s.IDX-NEW", data->path); goto fail; } /* For convenience, memory map the old index file. */ unmap_index(data); if (!map_index(data)) goto fail; /* Write the contents of the old index file to the new index file. */ if (lseek(fd, delta * sizeof(struct index_entry), SEEK_SET) < 0) { syswarn("tradindexed: cannot seek in %s.IDX-NEW", data->path); goto fail; } if (xwrite(fd, data->index, data->indexlen) < 0) { syswarn("tradindexed: cannot write to %s.IDX-NEW", data->path); goto fail; } if (close(fd) < 0) { syswarn("tradindexed: cannot close %s.IDX-NEW", data->path); goto fail; } data->base = base; data->indexinode = st.st_ino; return true; fail: if (fd >= 0) { close(fd); idxfile = concat(data->path, ".IDX-NEW", (char *) 0); if (unlink(idxfile) < 0) syswarn("tradindexed: cannot unlink %s", idxfile); free(idxfile); } return false; } /* ** Finish the process of packing a group by replacing the new index with the ** old index. Also reopen the index file and update indexinode to keep our ** caller from having to close and reopen the index file themselves. */ bool tdx_data_pack_finish(struct group_data *data) { char *newidx, *idx; if (!data->writable) return false; newidx = concat(data->path, ".IDX-NEW", (char *) 0); idx = concat(data->path, ".IDX", (char *) 0); if (rename(newidx, idx) < 0) { syswarn("tradindexed: cannot rename %s to %s", newidx, idx); unlink(newidx); free(newidx); free(idx); return false; } else { free(newidx); free(idx); if (!file_open_index(data, NULL)) return false; return true; } } /* ** Open the data files for a group data rebuild, and return a struct ** group_data for the new files. Calling this function doesn't interfere ** with the existing data for the group. Either tdx_data_rebuild_abort or ** tdx_data_rebuild_finish should be called on the returned struct group_data ** when the caller is done. */ struct group_data * tdx_data_rebuild_start(const char *group) { struct group_data *data; data = tdx_data_new(group, true); tdx_data_delete(group, "-NEW"); if (!file_open_index(data, "IDX-NEW")) goto fail; if (!file_open_data(data, "DAT-NEW")) goto fail; return data; fail: tdx_data_delete(group, "-NEW"); tdx_data_close(data); return NULL; } /* ** Finish a rebuild by renaming the new index and data files to their ** permanent names. */ bool tdx_data_rebuild_finish(const char *group) { char *base, *newidx, *bakidx, *idx, *newdat, *dat; bool saved = false; base = group_path(group); idx = concat(base, ".IDX", (char *) 0); newidx = concat(base, ".IDX-NEW", (char *) 0); bakidx = concat(base, ".IDX-BAK", (char *) 0); dat = concat(base, ".DAT", (char *) 0); newdat = concat(base, ".DAT-NEW", (char *) 0); free(base); if (rename(idx, bakidx) < 0) { syswarn("tradindexed: cannot rename %s to %s", idx, bakidx); goto fail; } else { saved = true; } if (rename(newidx, idx) < 0) { syswarn("tradindexed: cannot rename %s to %s", newidx, idx); goto fail; } if (rename(newdat, dat) < 0) { syswarn("tradindexed: cannot rename %s to %s", newdat, dat); goto fail; } if (unlink(bakidx) < 0) syswarn("tradindexed: cannot remove backup %s", bakidx); free(idx); free(newidx); free(bakidx); free(dat); free(newdat); return true; fail: if (saved && rename(bakidx, idx) < 0) syswarn("tradindexed: cannot restore old index %s", bakidx); free(idx); free(newidx); free(bakidx); free(dat); free(newdat); return false; } /* ** Do the main work of expiring a group. Step through each article in the ** group, only writing the unexpired entries out to the new group. There's ** probably some room for optimization here for newsgroups that don't expire ** so that the files don't have to be rewritten, or newsgroups where all the ** data at the end of the file is still good and just needs to be moved ** as-is. */ bool tdx_data_expire_start(const char *group, struct group_data *data, struct group_entry *index, struct history *history) { struct group_data *new_data; struct search *search; struct article article; ARTNUM high; new_data = tdx_data_rebuild_start(group); if (new_data == NULL) return false; index->indexinode = new_data->indexinode; /* Try to make sure that the search range is okay for even an empty group so that we can treat all errors on opening a search as errors. */ high = index->high > 0 ? index->high : data->base; new_data->high = high; search = tdx_search_open(data, data->base, high, high); if (search == NULL) goto fail; /* Loop through all of the articles in the group, adding the ones that are still valid to the new index. */ while (tdx_search(search, &article)) { ARTHANDLE *ah; if (!SMprobe(EXPENSIVESTAT, &article.token, NULL) || OVstatall) { ah = SMretrieve(article.token, RETR_STAT); if (ah == NULL) continue; SMfreearticle(ah); } else { if (!OVhisthasmsgid(history, article.overview)) continue; } if (innconf->groupbaseexpiry) if (OVgroupbasedexpire(article.token, group, article.overview, article.overlen, article.arrived, article.expires)) continue; if (!tdx_data_store(new_data, &article)) goto fail; if (index->base == 0) { index->base = new_data->base; index->low = article.number; } if (article.number > index->high) index->high = article.number; index->count++; } /* Done; the rest happens in tdx_data_rebuild_finish. */ tdx_data_close(new_data); return true; fail: tdx_data_delete(group, "-NEW"); tdx_data_close(new_data); return false; } /* ** Close the data files for a group and free the data structure. */ void tdx_data_close(struct group_data *data) { unmap_index(data); unmap_data(data); if (data->indexfd >= 0) close(data->indexfd); if (data->datafd >= 0) close(data->datafd); free(data->path); free(data); } /* ** Delete the data files for a particular group, called when that group is ** deleted from the server. Takes an optional suffix, which if present is ** appended to the ends of the file names (used by expire to delete the -NEW ** versions of the files). */ void tdx_data_delete(const char *group, const char *suffix) { char *path, *idx, *dat; path = group_path(group); idx = concat(path, ".IDX", suffix, (char *) 0); dat = concat(path, ".DAT", suffix, (char *) 0); if (unlink(idx) < 0 && errno != ENOENT) syswarn("tradindexed: cannot unlink %s", idx); if (unlink(dat) < 0 && errno != ENOENT) syswarn("tradindexed: cannot unlink %s", dat); free(dat); free(idx); free(path); } /* ** RECOVERY AND AUDITING ** ** All code below this point is not used in the normal operations of the ** overview method. Instead, it's code to dump various data structures or ** audit them for consistency, used by recovery tools and inspection tools. */ /* ** Dump the index file for a given group in human-readable format. */ void tdx_data_index_dump(struct group_data *data, FILE *output) { ARTNUM current; struct index_entry *entry, *end; if (data->index == NULL) if (!map_index(data)) return; current = data->base; end = data->index + (data->indexlen / sizeof(struct index_entry)); for (entry = data->index; entry < end; entry++) { fprintf(output, "%lu %lu %lu %lu %lu %s\n", current, (unsigned long) entry->offset, (unsigned long) entry->length, (unsigned long) entry->arrived, (unsigned long) entry->expires, TokenToText(entry->token)); current++; } } /* ** Audit a specific index entry for a particular article. If there's ** anything wrong with it, we delete it; to repair a particular group, it's ** best to just regenerate it from scratch. */ static void entry_audit(struct group_data *data, struct index_entry *entry, const char *group, ARTNUM article, bool fix) { struct index_entry new_entry; off_t offset; if (entry->length < 0) { warn("tradindexed: negative length %d in %s:%lu", entry->length, group, article); if (fix) goto clear; return; } if (entry->offset > data->datalen || entry->length > data->datalen) { warn("tradindexed: offset %lu or length %lu out of bounds for %s:%lu", (unsigned long) entry->offset, (unsigned long) entry->length, group, article); if (fix) goto clear; return; } if (entry->offset + entry->length > data->datalen) { warn("tradindexed: offset %lu plus length %lu out of bounds for" " %s:%lu", (unsigned long) entry->offset, (unsigned long) entry->length, group, article); if (fix) goto clear; return; } if (!overview_check(data->data + entry->offset, entry->length, article)) { warn("tradindexed: malformed overview data for %s:%lu", group, article); if (fix) goto clear; } return; clear: new_entry = *entry; new_entry.offset = 0; new_entry.length = 0; offset = (entry - data->index) * sizeof(struct index_entry); if (xpwrite(data->indexfd, &new_entry, sizeof(new_entry), offset) != 0) warn("tradindexed: unable to repair %s:%lu", group, article); } /* ** Audit the data for a particular group. Takes the index entry from the ** group.index file and optionally corrects any problems with the data or the ** index entry based on the contents of the data. */ void tdx_data_audit(const char *group, struct group_entry *index, bool fix) { struct group_data *data; struct index_entry *entry; long count; off_t expected; unsigned long entries, current; ARTNUM low = 0; bool changed = false; data = tdx_data_new(group, true); if (!tdx_data_open_files(data)) return; if (!map_index(data)) goto end; if (!map_data(data)) goto end; /* Check the inode of the index. */ if (data->indexinode != index->indexinode) { warn("tradindexed: index inode mismatch for %s: %lu != %lu", group, (unsigned long) data->indexinode, (unsigned long) index->indexinode); if (fix) { index->indexinode = data->indexinode; changed = true; } } /* Check the index size. */ entries = data->indexlen / sizeof(struct index_entry); expected = entries * sizeof(struct index_entry); if (data->indexlen != expected) { warn("tradindexed: %lu bytes of trailing trash in %s.IDX", (unsigned long)(data->indexlen - expected), data->path); if (fix) { unmap_index(data); if (ftruncate(data->indexfd, expected) < 0) syswarn("tradindexed: cannot truncate %s.IDX", data->path); if (!map_index(data)) goto end; } } /* Now iterate through all of the index entries. In addition to checking each one individually, also count the number of valid entries to check the count in the index and verify that the low water mark is correct. */ for (current = 0, count = 0; current < entries; current++) { entry = &data->index[current]; if (entry->length == 0) continue; entry_audit(data, entry, group, index->base + current, fix); if (entry->length != 0) { if (low == 0) low = index->base + current; count++; } } if (index->low != low && entries != 0) { warn("tradindexed: low water mark incorrect for %s: %lu != %lu", group, low, index->low); if (fix) { index->low = low; changed = true; } } if (index->count != count) { warn("tradindexed: count incorrect for %s: %lu != %lu", group, (unsigned long) count, (unsigned long) index->count); if (fix) { index->count = count; changed = true; } } /* All done. Close things down and flush the data we changed, if necessary. */ if (changed) inn_msync_page(index, sizeof(*index), MS_ASYNC); end: tdx_data_close(data); } inn-2.6.0/storage/tradindexed/tradindexed.h0000644000175200017520000000270612575023702020330 0ustar iuliusiulius/* $Id: tradindexed.h 7585 2006-11-21 09:37:51Z eagle $ ** ** Public interface for the tradindexed overview method. ** ** The exact API specified here must match the expectations of the overview ** API. Any changes here have to be made to all the overview methods at the ** same time. */ #ifndef TRADINDEXED_H #define TRADINDEXED_H 1 #include "config.h" #include #include "inn/ov.h" #include "inn/storage.h" BEGIN_DECLS bool tradindexed_open(int mode); bool tradindexed_groupstats(const char *group, int *low, int *high, int *count, int *flag); bool tradindexed_groupadd(const char *group, ARTNUM low, ARTNUM high, char *flag); bool tradindexed_groupdel(const char *group); bool tradindexed_add(const char *group, ARTNUM artnum, TOKEN token, char *data, int length, time_t arrived, time_t expires); bool tradindexed_cancel(const char *group, ARTNUM artnum); void *tradindexed_opensearch(const char *group, int low, int high); bool tradindexed_search(void *handle, ARTNUM *artnum, char **data, int *length, TOKEN *token, time_t *arrived); void tradindexed_closesearch(void *handle); bool tradindexed_getartinfo(const char *group, ARTNUM artnum, TOKEN *token); bool tradindexed_expiregroup(const char *group, int *low, struct history *); bool tradindexed_ctl(OVCTLTYPE type, void *val); void tradindexed_close(void); END_DECLS #endif /* TRADINDEXED_H */ inn-2.6.0/storage/tradindexed/tdx-structure.h0000644000175200017520000001457212575023702020676 0ustar iuliusiulius/* $Id: tdx-structure.h 9857 2015-05-14 13:19:07Z iulius $ ** ** Data structures for the tradindexed overview method. ** ** This header defines the data structures used by the tradindexed overview ** method. Currently, these data structures are read and written directly to ** disk (and the disk files are therefore endian-dependent and possibly ** architecture-dependent due to structure padding). This will eventually be ** fixed. ** ** The structure of a tradindexed overview spool is as follows: At the root ** of the spool is a group.index file composed of a struct group_header ** followed by some number of struct group_entry's, one for each group plus ** possibly some number of free entries linked to a free list that's headed ** in the struct index_header. Each entry corresponds to a particular ** newsgroup carried by the server and stores the high and low article ** numbers for that group, its status flag, and the base of the index file ** for each group. ** ** The storage of the group.index file implements a hash table with chaining; ** in other words, there is a table indexed by hash value stored in the ** header that points to the starts of the chains, new entries are appended ** to the end of the file and added to the hash table, and if they collide ** with an existing entry are instead linked to the appropriate hash chain. ** ** The overview information for each group is stored in a pair of files named ** .IDX and .DAT. These files are found in a subdirectory ** formed by taking the first letter of component of the newsgroup name as ** a directory name; in other words, news.announce.newgroups overview data is ** stored in /n/a/n/news.announce.newgroups.{IDX,DAT}. The ** .DAT file contains the individual overview entries, one per line, stored ** in wire format (in other words, suitable for dumping directly across the ** network to a client in response to an OVER command). The overview data ** stored in that file may be out of order. ** ** The .IDX file consists of a series of struct index_entry's, one for each ** overview entry stored in the .DAT file. Each index entry stores the ** offset of the data for one article in the .DAT file and its length, along ** with some additional metainformation about the article used to drive ** article expiration. The .IDX file is addressed like an array; the first ** entry corresponds to the article with the number stored in the base field ** of the group_entry for that newsgroup in the group.index file and each ** entry stores the data for the next consecutive article. Index entries may ** be tagged as deleted if that article has been deleted or expired. */ #ifndef INN_TDX_STRUCTURE_H #define INN_TDX_STRUCTURE_H 1 #include "config.h" #include #include "inn/libinn.h" #include "inn/storage.h" /* A location in group.index (this many records past the end of the header of the file). There's no reason for this to be a struct, but that can't be changed until the format of the group.index file is changed to be architecture-independent since putting it into a struct may have changed the alignment or padding on some architectures. */ struct loc { int recno; }; /* The hard-coded constant size of the hash table for group.index. This need not be a power of two and has no special constraints. Changing this at present will break backward compatibility with group.index files written by previous versions of the code. */ #define TDX_HASH_SIZE (16 * 1024) /* A magic number for the group.index file so that we can later change the format in a backward-compatible fashion. This magic number stands for "fifo feed". */ #define TDX_MAGIC (~(0xf1f0f33d)) /* The header at the top of group.index. magic contains GROUPHEADERMAGIC always; hash contains pointers to the heads of the entry chains, and freelist points to a linked list of free entries (entries that were used for groups that have since been deleted). */ struct group_header { int magic; struct loc hash[TDX_HASH_SIZE]; struct loc freelist; }; /* An entry for a particular group. Note that a good bit of active file information is duplicated here, and depending on the portion of INN asking questions, sometimes the main active file is canonical and sometimes the overview data is canonical. This needs to be rethought at some point. Groups are matched based on the MD5 hash of their name. This may prove inadequate in the future. Ideally, INN really needs to assign unique numbers to each group, which could then be used here as well as in tradspool rather than having to do hacks like using a hash of the group name or constructing one's own number to name mapping like tradspool does. Unfortunately, this ideally requires a non-backward-compatible change to the active file format. Several of these elements aren't used. This structure, like the others, cannot be changed until the whole format of the group.index file is changed since it's currently read as binary structs directly from disk. */ struct group_entry { HASH hash; /* MD5 hash of the group name. */ HASH alias; /* Intended to point to the group this group is an alias for. Not currently used. */ ARTNUM high; /* High article number in the group. */ ARTNUM low; /* Low article number in the group. */ ARTNUM base; /* Article number of the first entry in the .IDX index file for the group. */ int count; /* Number of articles in group. */ int flag; /* Posting/moderation status. */ time_t deleted; /* When this group was deleted, or 0 if the group is still valid. */ ino_t indexinode; /* The inode of the index file for the group, used to detect when the file has been recreated and swapped out. */ struct loc next; /* Next block in this chain. */ }; /* An entry in the per-group .IDX index file. */ struct index_entry { off_t offset; int length; time_t arrived; time_t expires; /* Expiration time from Expires: header. */ TOKEN token; }; #endif /* INN_TDX_STRUCTURE_H */ inn-2.6.0/storage/tradindexed/tdx-util.c0000644000175200017520000003610312575023702017600 0ustar iuliusiulius/* $Id: tdx-util.c 9019 2010-03-19 21:27:15Z iulius $ ** ** Utility for managing a tradindexed overview spool. ** ** This utility can manipulate a tradindexed overview spool in various ways, ** including some ways that are useful for recovery from crashes. It allows ** the user to view the contents of the various data structures that ** tradindexed stores on disk. */ #include "config.h" #include "clibrary.h" #include #include #include #include "inn/buffer.h" #include "inn/libinn.h" #include "inn/history.h" #include "inn/innconf.h" #include "inn/messages.h" #include "inn/newsuser.h" #include "inn/ov.h" #include "inn/paths.h" #include "inn/vector.h" #include "inn/wire.h" #include "ovinterface.h" #include "tdx-private.h" #include "tdx-structure.h" /* ** Dump the main index data, either all of it or that for a particular group ** if the group argument is non-NULL. */ static void dump_index(const char *group) { struct group_index *index; index = tdx_index_open(OV_READ); if (index == NULL) return; if (group == NULL) tdx_index_dump(index, stdout); else { const struct group_entry *entry; entry = tdx_index_entry(index, group); if (entry == NULL) { warn("cannot find group %s", group); return; } tdx_index_print(group, entry, stdout); } tdx_index_close(index); } /* ** Dump the data index file for a particular group. */ static void dump_group_index(const char *group) { struct group_index *index; struct group_entry *entry; struct group_data *data; index = tdx_index_open(OV_READ); if (index == NULL) return; entry = tdx_index_entry(index, group); if (entry == NULL) { warn("cannot find group %s in the index", group); return; } data = tdx_data_open(index, group, entry); if (data == NULL) { warn("cannot open group %s", group); return; } tdx_data_index_dump(data, stdout); tdx_data_close(data); tdx_index_close(index); } /* ** Dump the overview data for a particular group. If number is 0, dump the ** overview data for all current articles; otherwise, only dump the data for ** that particular article. Include the article number, token, arrived time, ** and expires time (if any) in the overview data as additional fields ** normally. If overchan is true, instead write out the data in the format ** that overchan would expect as input. */ static void dump_overview(const char *group, ARTNUM low, ARTNUM high, bool overchan) { struct group_index *index; struct group_data *data; struct group_entry *entry; struct article article; struct search *search; char *p; char datestring[256]; index = tdx_index_open(OV_READ); if (index == NULL) return; entry = tdx_index_entry(index, group); if (entry == NULL) { warn("cannot find group %s", group); return; } data = tdx_data_open(index, group, entry); if (data == NULL) { warn("cannot open group %s", group); return; } data->refcount++; if (low == 0) low = entry->low; if (high == 0) high = entry->high; search = tdx_search_open(data, low, high, entry->high); if (search == NULL) { if (low == high) puts("Article not found"); else warn("cannot open search in %s: %lu - %lu", group, low, high); return; } while (tdx_search(search, &article)) { if (overchan) { p = memchr(article.overview, '\t', article.overlen - 3); if (p == NULL) continue; p++; printf("%s %lu %lu ", TokenToText(article.token), (unsigned long) article.arrived, (unsigned long) article.expires); fwrite(p, article.overlen - 2 - (p - article.overview), 1, stdout); } else { fwrite(article.overview, article.overlen - 2, 1, stdout); printf("\tArticle: %lu\tToken: %s", article.number, TokenToText(article.token)); makedate(article.arrived, true, datestring, sizeof(datestring)); printf("\tArrived: %s", datestring); if (article.expires != 0) { makedate(article.expires, true, datestring, sizeof(datestring)); printf("\tExpires: %s", datestring); } } printf("\n"); } tdx_search_close(search); tdx_data_close(data); tdx_index_close(index); } /* ** Check a string to see if its a valid number. */ static bool check_number(const char *string) { const char *p; for (p = string; *p != '\0'; p++) if (!isdigit((unsigned char) *p)) return false; return true; } /* ** Find the message ID in the group overview data and return a copy of it. ** Caller is responsible for freeing. */ static char * extract_messageid(const char *overview) { const char *p, *end; int count; for (p = overview, count = 0; count < 4; count++) { p = strchr(p + 1, '\t'); if (p == NULL) return NULL; } p++; end = strchr(p, '\t'); if (end == NULL) return NULL; return xstrndup(p, end - p); } /* ** Compare two file names assuming they're numbers, used to sort the list of ** articles numerically. Suitable for use as a comparison function for ** qsort. */ static int file_compare(const void *p1, const void *p2) { const char *file1 = *((const char * const *) p1); const char *file2 = *((const char * const *) p2); ARTNUM n1, n2; n1 = strtoul(file1, NULL, 10); n2 = strtoul(file2, NULL, 10); if (n1 > n2) return 1; else if (n1 < n2) return -1; else return 0; } /* ** Get a list of articles in a directory, sorted by article number. */ static struct vector * article_list(const char *directory) { DIR *articles; struct dirent *file; struct vector *list; list = vector_new(); articles = opendir(directory); if (articles == NULL) sysdie("cannot open directory %s", directory); while ((file = readdir(articles)) != NULL) { if (!check_number(file->d_name)) continue; vector_add(list, file->d_name); } closedir(articles); qsort(list->strings, list->count, sizeof(list->strings[0]), file_compare); return list; } /* ** Create a new newsgroup in the overview database. Takes the group flag and ** the high and low water marks. */ static void group_create(const char *group, ARTNUM low, ARTNUM high, char flag) { struct group_index *index; index = tdx_index_open(true); if (index == NULL) die("cannot open group index"); if (low == 0) low = 1; if (!tdx_index_add(index, group, low, high, &flag)) die("cannot create group %s", group); tdx_index_close(index); } /* ** Rebuild the overview data for a particular group. Takes a path to a ** directory containing all the articles, as individual files, that should be ** in that group. The names of the files should be the article numbers in ** the group. */ static void group_rebuild(const char *group, const char *path) { char *filename, *histpath, *article, *wireformat, *p; size_t size, length, file; int flags; struct buffer *overview = NULL; struct vector *extra, *files; struct history *history; struct group_index *index; struct group_data *data; struct group_entry *entry, info; struct article artdata; struct stat st; index = tdx_index_open(OV_READ); if (index == NULL) die("cannot open group index"); entry = tdx_index_entry(index, group); if (entry == NULL) { if (!tdx_index_add(index, group, 1, 0, NF_FLAG_OK_STRING)) die("cannot create group %s", group); entry = tdx_index_entry(index, group); if (entry == NULL) die("cannot find group %s", group); } info = *entry; data = tdx_data_rebuild_start(group); if (data == NULL) die("cannot start data rebuild for %s", group); if (!tdx_index_rebuild_start(index, entry)) die("cannot start index rebuild for %s", group); histpath = concatpath(innconf->pathdb, INN_PATH_HISTORY); flags = HIS_RDONLY | HIS_ONDISK; history = HISopen(histpath, innconf->hismethod, flags); if (history == NULL) sysdie("cannot open history %s", histpath); free(histpath); extra = overview_extra_fields(true); files = article_list(path); info.count = 0; info.high = 0; info.low = 0; for (file = 0; file < files->count; file++) { filename = concatpath(path, files->strings[file]); article = ReadInFile(filename, &st); size = st.st_size; if (article == NULL) { syswarn("cannot read in %s", filename); free(filename); continue; } /* Check to see if the article is not in wire format. If it isn't, convert it. We only check the first line ending. */ p = strchr(article, '\n'); if (p != NULL && (p == article || p[-1] != '\r')) { wireformat = wire_from_native(article, size, &length); free(article); article = wireformat; size = length; } artdata.number = strtoul(files->strings[file], NULL, 10); if (artdata.number > info.high) info.high = artdata.number; if (artdata.number < info.low || info.low == 0) info.low = artdata.number; info.count++; overview = overview_build(artdata.number, article, size, extra, overview); artdata.overview = overview->data; artdata.overlen = overview->left; p = extract_messageid(overview->data); if (p == NULL) { warn("cannot find message ID in %s", filename); free(filename); free(article); continue; } if (HISlookup(history, p, &artdata.arrived, NULL, &artdata.expires, &artdata.token)) { if (!tdx_data_store(data, &artdata)) warn("cannot store data for %s", filename); } else { warn("cannot find article %s in history", p); } free(p); free(filename); free(article); } vector_free(files); vector_free(extra); info.indexinode = data->indexinode; info.base = data->base; if (!tdx_index_rebuild_finish(index, entry, &info)) die("cannot update group index for %s", group); if (!tdx_data_rebuild_finish(group)) die("cannot finish rebuilding data for group %s", group); tdx_data_close(data); HISclose(history); } /* ** Parse an article number or range, returning the low and high values in the ** provided arguments. Returns true if the number or range was parsed ** successfully, false otherwise. Allows such constructs as "-", "20-", or ** "-50" and leaves the unspecified ends of the range set to zero. ** ** This code is similar to code in nnrpd and possibly should move into libinn ** as common code. */ static bool parse_range(char *range, ARTNUM *low, ARTNUM *high) { char *hyphen, *end; *low = 0; *high = 0; hyphen = strchr(range, '-'); if (hyphen == NULL) { *low = strtoul(range, &end, 10); if (*low == 0 || *end != '\0') return false; *high = *low; return true; } else { *hyphen = '\0'; if (*range != '\0') { *low = strtoul(range, &end, 10); if (*low == 0 || *end != '\0') return false; } if (hyphen[1] != '\0') { *high = strtoul(hyphen + 1, &end, 10); if (*high == 0 || *end != '\0') return false; } *hyphen = '-'; return true; } } /* ** Main routine. Load inn.conf, parse the arguments, and dispatch to the ** appropriate function. */ int main(int argc, char *argv[]) { int option; char flag = 'y'; char mode = '\0'; const char *newsgroup = NULL; const char *path = NULL; ARTNUM artlow = 0; ARTNUM arthigh = 0; message_program_name = "tdx-util"; if (!innconf_read(NULL)) exit(1); /* Parse options. */ opterr = 0; while ((option = getopt(argc, argv, "a:f:n:p:AFR:cgiOo")) != EOF) { switch (option) { case 'a': if (!parse_range(optarg, &artlow, &arthigh)) die("invalid article number or range %s", optarg); break; case 'f': flag = optarg[0]; break; case 'n': newsgroup = optarg; break; case 'p': innconf->pathoverview = xstrdup(optarg); break; case 'A': if (mode != '\0') die("only one mode option allowed"); mode = 'A'; break; case 'F': if (mode != '\0') die("only one mode option allowed"); mode = 'F'; break; case 'R': if (mode != '\0') die("only one mode option allowed"); mode = 'R'; path = optarg; break; case 'c': if (mode != '\0') die("only one mode option allowed"); mode = 'c'; break; case 'g': if (mode != '\0') die("only one mode option allowed"); mode = 'g'; break; case 'i': if (mode != '\0') die("only one mode option allowed"); mode = 'i'; break; case 'O': if (mode != '\0') die("only one mode option allowed"); mode = 'O'; break; case 'o': if (mode != '\0') die("only one mode option allowed"); mode = 'o'; break; default: die("invalid option %c", optopt); break; } } /* Some modes require a group be specified. */ if (strchr("cgoOR", mode) != NULL && newsgroup == NULL) die("group must be specified for -%c", mode); /* Run the specified function. */ switch (mode) { case 'A': tdx_index_audit(false); break; case 'F': if (getenv("INN_TESTSUITE") == NULL) ensure_news_user_grp(true, true); tdx_index_audit(true); break; case 'R': if (getenv("INN_TESTSUITE") == NULL) ensure_news_user_grp(true, true); group_rebuild(newsgroup, path); break; case 'c': if (getenv("INN_TESTSUITE") == NULL) ensure_news_user_grp(true, true); group_create(newsgroup, artlow, arthigh, flag); break; case 'i': dump_index(newsgroup); break; case 'g': dump_group_index(newsgroup); break; case 'O': dump_overview(newsgroup, artlow, arthigh, true); break; case 'o': dump_overview(newsgroup, artlow, arthigh, false); break; default: die("a mode option must be specified"); break; } exit(0); } inn-2.6.0/storage/tradindexed/ovmethod.config0000644000175200017520000000024312575023702020672 0ustar iuliusiuliusname = tradindexed number = 2 sources = tdx-cache.c tdx-group.c tdx-data.c tradindexed.c extra-sources = tdx-util.c programs = tdx-util inn-2.6.0/storage/tradindexed/tdx-group.c0000644000175200017520000013076012575023702017763 0ustar iuliusiulius/* $Id: tdx-group.c 9859 2015-05-14 13:25:42Z iulius $ ** ** Group index handling for the tradindexed overview method. ** ** Implements the handling of the group.index file for the tradindexed ** overview method. This file contains an entry for every group and stores ** the high and low article marks and the base article numbers for each ** individual group index file. ** ** Externally visible functions have a tdx_ prefix; internal functions do ** not. (Externally visible unfortunately means everything that needs to be ** visible outside of this object file, not just interfaces exported to ** consumers of the overview API.) ** ** This code has to support readers and writers sharing the same files, and ** we want to avoid locking where possible since locking may be very slow ** (such as over NFS). Each group has two data files (and one has to get the ** right index file for a given data file or get mangled results) and one ** piece of data in the main index file required to interpret the individual ** index file, namely the article base of that index. ** ** We can make the following assumptions: ** ** - The high water mark for a group is monotonically increasing; in other ** words, the highest numbered article in a group won't ever decrease. ** ** - While the article base may either increase or decrease, it will never ** change unless the inode of the index file on disk also changes, since ** changing the base requires rewriting the index file. ** ** - No two files will have the same inode (this requirement should be safe ** even in strange Unix file formats, since the files are all in the same ** directory). ** ** We therefore use the following procedure to update the data: The high ** water mark may be changed at any time but surrounded in a write lock. The ** base may only be changed as part of an index rebuild. To do an index ** rebuild, we follow the following procedure: ** ** 1) Obtain a write lock on the group entry in the main index. ** 2) Write out new index and data files to new temporary file names. ** 3) Store the new index inode into the main index. ** 4) Update the high, low, and base article numbers in the main index. ** 5) Rename the data file to its correct name. ** 6) Rename the index file to its correct name. ** 7) Release the write lock. ** ** We use the following procedure to read the data: ** ** 1) Open the group data files (both index and data). ** 2) Store copies of the current high water mark and base in variables. ** 3) Check to be sure the index inode matches the master index file. ** ** If it does match, then we have a consistent set of data, since the high ** water mark and base values have to match the index we have (the inode ** value is updated first). It may not be the most current set of data, but ** since we have those index and data files open, even if they're later ** rebuilt we'll continue looking at the same files. They may have further ** data appended to them, but that's safe. ** ** If the index inode doesn't match, someone's rebuilt the file while we were ** trying to open it. Continue with the following procedure: ** ** 4) Close the data files that we opened. ** 5) Obtain a read lock on the group entry in the main index. ** 6) Reopen the data files. ** 7) Grab the current high water mark and base. ** 8) Release the read lock. ** ** In other words, if there appears to be contention, we fall back to using ** locking so that we don't try to loop (which also avoids an infinite loop ** in the event of corruption of the main index). ** ** Note that once we have a consistent set of data files open, we don't need ** to aggressively check for new data files until someone asks for an article ** outside the range of articles that we know about. We may be working from ** outdated data files, but the most we'll miss is a cancel or an expiration ** run. Overview data doesn't change; new data is appended and old data is ** expired. We can afford to check only every once in a while, just to be ** sure that we're not going to hand out overview data for a bunch of expired ** articles. */ #include "config.h" #include "clibrary.h" #include "portable/mmap.h" #include #include #include #include #include #include "inn/fdflag.h" #include "inn/hashtab.h" #include "inn/innconf.h" #include "inn/messages.h" #include "inn/mmap.h" #include "inn/qio.h" #include "inn/vector.h" #include "inn/libinn.h" #include "inn/paths.h" #include "tdx-private.h" #include "tdx-structure.h" /* Returned to callers as an opaque data type, this stashes all of the information about an open group.index file. */ struct group_index { char *path; int fd; bool writable; struct group_header *header; struct group_entry *entries; int count; }; /* Forward declaration. */ struct hashmap; /* Internal prototypes. */ static int index_entry_count(size_t size); static size_t index_file_size(int count); static bool index_lock(int fd, enum inn_locktype type); static bool index_lock_group(int fd, ptrdiff_t offset, enum inn_locktype); static bool index_map(struct group_index *); static bool index_maybe_remap(struct group_index *, long loc); static void index_unmap(struct group_index *); static bool index_expand(struct group_index *); static long index_find(struct group_index *, const char *group); /* ** Given a file size, return the number of group entries that it contains. */ static int index_entry_count(size_t size) { return (size - sizeof(struct group_header)) / sizeof(struct group_entry); } /* ** Given a number of group entries, return the required file size. */ static size_t index_file_size(int count) { return sizeof(struct group_header) + count * sizeof(struct group_entry); } /* ** Lock the hash table for the group index, used to acquire global locks on ** the group index when updating it. */ static bool index_lock(int fd, enum inn_locktype type) { bool status; status = inn_lock_range(fd, type, true, 0, sizeof(struct group_header)); if (!status) syswarn("tradindexed: cannot %s index hash table", (type == INN_LOCK_UNLOCK) ? "unlock" : "lock"); return status; } /* ** Lock the group entry for a particular group. Takes the offset of that ** group entry from the start of the group entries (not the start of the ** file; we have to add the size of the group header). Used for coordinating ** updates of the data for a group. */ static bool index_lock_group(int fd, ptrdiff_t offset, enum inn_locktype type) { bool status; size_t size; size = sizeof(struct group_entry); offset = offset * size + sizeof(struct group_header); status = inn_lock_range(fd, type, true, offset, size); if (!status) syswarn("tradindexed: cannot %s group entry at %lu", (type == INN_LOCK_UNLOCK) ? "unlock" : "lock", (unsigned long) offset); return status; } /* ** Memory map (or read into memory) the key portions of the group.index ** file. Takes a struct group_index to fill in and returns true on success ** and false on failure. */ static bool index_map(struct group_index *index) { if (!innconf->tradindexedmmap && index->writable) { warn("tradindexed: cannot open for writing without mmap"); return false; } if (!innconf->tradindexedmmap) { ssize_t header_size; ssize_t entry_size; header_size = sizeof(struct group_header); entry_size = index->count * sizeof(struct group_entry); index->header = xmalloc(header_size); index->entries = xmalloc(entry_size); if (read(index->fd, index->header, header_size) != header_size) { syswarn("tradindexed: cannot read header from %s", index->path); goto fail; } if (read(index->fd, index->entries, entry_size) != entry_size) { syswarn("tradindexed: cannot read entries from %s", index->path); goto fail; } return true; fail: free(index->header); free(index->entries); index->header = NULL; index->entries = NULL; return false; } else { char *data; size_t size; int flag = PROT_READ; if (index->writable) flag = PROT_READ | PROT_WRITE; size = index_file_size(index->count); data = mmap(NULL, size, flag, MAP_SHARED, index->fd, 0); if (data == MAP_FAILED) { syswarn("tradindexed: cannot mmap %s", index->path); return false; } index->header = (struct group_header *)(void *) data; index->entries = (struct group_entry *) (void *)(data + sizeof(struct group_header)); return true; } } static bool file_open_group_index(struct group_index *index, struct stat *st) { int open_mode; index->header = NULL; open_mode = index->writable ? O_RDWR | O_CREAT : O_RDONLY; index->fd = open(index->path, open_mode, ARTFILE_MODE); if (index->fd < 0) { syswarn("tradindexed: cannot open %s", index->path); goto fail; } if (fstat(index->fd, st) < 0) { syswarn("tradindexed: cannot fstat %s", index->path); goto fail; } fdflag_close_exec(index->fd, true); return true; fail: if (index->fd >= 0) { close(index->fd); index->fd = -1; } return false; } /* ** Given a group location, remap the index file if our existing mapping isn't ** large enough to include that group. (This can be the case when another ** writer is appending entries to the group index.) Returns true on success ** (which includes "did not need to remap") and false on failure. */ static bool index_maybe_remap(struct group_index *index, long loc) { struct stat st; int count; int r; if (loc < index->count) return true; /* Don't remap if remapping wouldn't actually help. */ r = fstat(index->fd, &st); if (r == -1) { if (errno == ESTALE) { index_unmap(index); if (!file_open_group_index(index, &st)) return false; } else { syswarn("tradindexed: cannot stat %s", index->path); return false; } } count = index_entry_count(st.st_size); if (count < loc && index->header != NULL) return true; /* Okay, remapping will actually help. */ index_unmap(index); index->count = count; return index_map(index); } /* ** Unmap the index file, either in preparation for closing the overview ** method or to get ready to remap it. We warn about failures to munmap but ** don't do anything about them; there isn't much that we can do. */ static void index_unmap(struct group_index *index) { if (index->header == NULL) return; if (!innconf->tradindexedmmap) { free(index->header); free(index->entries); } else { if (munmap(index->header, index_file_size(index->count)) < 0) syswarn("tradindexed: cannot munmap %s", index->path); } index->header = NULL; index->entries = NULL; } /* ** Expand the group.index file to hold more entries; also used to build the ** initial file. The caller is expected to lock the group index. */ static bool index_expand(struct group_index *index) { int i; index_unmap(index); index->count += 1024; if (ftruncate(index->fd, index_file_size(index->count)) < 0) { syswarn("tradindexed: cannot expand %s", index->path); return false; } /* If mapping the index fails, we've already extended it but we haven't done anything with the new portion of the file. That means that it's all zeroes, which means that it contains index entries who all think their next entry is entry 0. We don't want to leave things in this state (particularly if this was the first expansion of the index file, in which case entry 0 points to entry 0 and our walking functions may go into infinite loops). Undo the file expansion. */ if (!index_map(index)) { index->count -= 1024; if (ftruncate(index->fd, index_file_size(index->count)) < 0) { syswarn("tradindexed: cannot shrink %s", index->path); } return false; } /* If the magic isn't right, assume this is a new index file. */ if (index->header->magic != TDX_MAGIC) { index->header->magic = TDX_MAGIC; index->header->freelist.recno = -1; for (i = 0; i < TDX_HASH_SIZE; i++) index->header->hash[i].recno = -1; } /* Walk the new entries back to front, adding them to the free list. */ for (i = index->count - 1; i >= index->count - 1024; i--) { index->entries[i].next = index->header->freelist; index->header->freelist.recno = i; } inn_msync_page(index->header, index_file_size(index->count), MS_ASYNC); return true; } /* ** Open the group.index file and allocate a new struct for it, returning a ** pointer to that struct. Takes a bool saying whether or not the overview ** should be opened for write. */ struct group_index * tdx_index_open(bool writable) { struct group_index *index; struct stat st; index = xmalloc(sizeof(struct group_index)); index->path = concatpath(innconf->pathoverview, "group.index"); index->writable = writable; if (!file_open_group_index(index, &st)) { goto fail; } if ((size_t) st.st_size > sizeof(struct group_header)) { index->count = index_entry_count(st.st_size); if (!index_map(index)) goto fail; } else { index->count = 0; if (index->writable) { if (st.st_size > 0) warn("tradindexed: recreating truncated %s", index->path); if (!index_expand(index)) goto fail; } else { index->header = NULL; index->entries = NULL; } } return index; fail: tdx_index_close(index); return NULL; } /* ** Given a group name hash, return an index into the hash table in the ** group.index header. */ static long index_bucket(HASH hash) { unsigned int bucket; memcpy(&bucket, &hash, sizeof(bucket)); return bucket % TDX_HASH_SIZE; } /* ** Given a pointer to a group entry, return its location number. */ static long entry_loc(const struct group_index *index, const struct group_entry *entry) { return entry - index->entries; } /* ** Splice out a particular group entry. Takes the entry and a pointer to the ** location where a pointer to it is stored. */ static void entry_splice(struct group_entry *entry, int *parent) { *parent = entry->next.recno; entry->next.recno = -1; inn_msync_page(parent, sizeof(*parent), MS_ASYNC); } /* ** Add a new entry to the appropriate hash chain. */ static void index_add(struct group_index *index, struct group_entry *entry) { long bucket, loc; bucket = index_bucket(entry->hash); loc = entry_loc(index, entry); if (loc == index->header->hash[bucket].recno) { warn("tradindexed: refusing to add a loop for %ld in bucket %ld", loc, bucket); return; } entry->next.recno = index->header->hash[bucket].recno; index->header->hash[bucket].recno = entry_loc(index, entry); inn_msync_page(&index->header->hash[bucket], sizeof(struct loc), MS_ASYNC); inn_msync_page(entry, sizeof(*entry), MS_ASYNC); } /* ** Find a group in the index file, returning the group number for that group ** or -1 if the group can't be found. */ static long index_find(struct group_index *index, const char *group) { HASH hash; long loc; if (index->header == NULL || index->entries == NULL) return -1; hash = Hash(group, strlen(group)); if (innconf->nfsreader && !index_maybe_remap(index, LONG_MAX)) return -1; loc = index->header->hash[index_bucket(hash)].recno; while (loc >= 0) { struct group_entry *entry; if (loc >= index->count) { if (!index_maybe_remap(index, loc)) { return -1; } if (loc >= index->count) { syswarn("tradindexed: entry %ld out of range", loc); return -1; } } entry = index->entries + loc; if (entry->deleted == 0) if (memcmp(&hash, &entry->hash, sizeof(hash)) == 0) return loc; if (loc == entry->next.recno) { syswarn("tradindexed: index loop for entry %ld", loc); return -1; } loc = entry->next.recno; } return -1; } /* ** Add a given entry to the free list. */ static void freelist_add(struct group_index *index, struct group_entry *entry) { entry->next.recno = index->header->freelist.recno; index->header->freelist.recno = entry_loc(index, entry); inn_msync_page(&index->header->freelist, sizeof(struct loc), MS_ASYNC); inn_msync_page(entry, sizeof(*entry), MS_ASYNC); } /* ** Find an entry by hash value (rather than group name) and splice it out of ** whatever chain it might belong to. This function is called by both ** index_unlink and index_audit_group. Locking must be done by the caller. ** Returns the group location of the spliced group. */ static long index_unlink_hash(struct group_index *index, HASH hash) { int *parent; long current; parent = &index->header->hash[index_bucket(hash)].recno; current = *parent; while (current >= 0) { struct group_entry *entry; if (current >= index->count) { if (!index_maybe_remap(index, current)) { return -1; } parent = &index->header->hash[index_bucket(hash)].recno; current = *parent; if (current < 0 || current >= index->count) { syswarn("tradindexed: entry %ld out of range", current); return -1; } } entry = &index->entries[current]; if (entry->deleted == 0) if (memcmp(&hash, &entry->hash, sizeof(hash)) == 0) { entry_splice(entry, parent); return current; } if (current == entry->next.recno) { syswarn("tradindexed: index loop for entry %ld", current); return -1; } parent = &entry->next.recno; current = *parent; } return -1; } /* ** Like index_find, but also removes that entry out of whatever chain it ** might belong to. This function is called by tdx_index_delete. Locking ** must be done by the caller. */ static long index_unlink(struct group_index *index, const char *group) { HASH hash; hash = Hash(group, strlen(group)); return index_unlink_hash(index, hash); } /* ** Return the information stored about a given group in the group index. */ struct group_entry * tdx_index_entry(struct group_index *index, const char *group) { long loc; struct group_entry *entry; loc = index_find(index, group); if (loc == -1) return NULL; entry = index->entries + loc; if (innconf->tradindexedmmap && innconf->nfsreader) inn_msync_page(entry, sizeof *entry, MS_INVALIDATE); return entry; } /* ** Add a new newsgroup to the group.index file. Takes the newsgroup name, ** its high and low water marks, and the newsgroup flag. Note that aliased ** newsgroups are not currently handled. If the group already exists, just ** update the flag (not the high and low water marks). */ bool tdx_index_add(struct group_index *index, const char *group, ARTNUM low, ARTNUM high, const char *flag) { HASH hash; long loc; struct group_entry *entry; struct group_data *data; if (!index->writable) return false; /* If the group already exists, update the flag as necessary and then we're all done. */ loc = index_find(index, group); if (loc != -1) { entry = &index->entries[loc]; if (entry->flag != *flag) { entry->flag = *flag; inn_msync_page(entry, sizeof(*entry), MS_ASYNC); } return true; } index_lock(index->fd, INN_LOCK_WRITE); /* Find a free entry. If we don't have any free space, make some. */ if (index->header->freelist.recno == -1) if (!index_expand(index)) { index_lock(index->fd, INN_LOCK_UNLOCK); return false; } loc = index->header->freelist.recno; index->header->freelist.recno = index->entries[loc].next.recno; inn_msync_page(&index->header->freelist, sizeof(struct loc), MS_ASYNC); /* Initialize the entry. */ entry = &index->entries[loc]; hash = Hash(group, strlen(group)); entry->hash = hash; entry->low = (low == 0 && high != 0) ? high + 1 : low; entry->high = high; entry->deleted = 0; entry->base = 0; entry->count = 0; entry->flag = *flag; data = tdx_data_new(group, index->writable); if (!tdx_data_open_files(data)) warn("tradindexed: unable to create data files for %s", group); entry->indexinode = data->indexinode; tdx_data_close(data); index_add(index, entry); index_lock(index->fd, INN_LOCK_UNLOCK); return true; } /* ** Delete a group index entry. */ bool tdx_index_delete(struct group_index *index, const char *group) { long loc; struct group_entry *entry; if (!index->writable) return false; /* Lock the header for the entire operation, mostly as prevention against interfering with ongoing audits (which lock while they're running). */ index_lock(index->fd, INN_LOCK_WRITE); /* Splice out the entry and mark it as deleted. */ loc = index_unlink(index, group); if (loc == -1) { index_lock(index->fd, INN_LOCK_UNLOCK); return false; } entry = &index->entries[loc]; entry->deleted = time(NULL); HashClear(&entry->hash); /* Add the entry to the free list. */ freelist_add(index, entry); index_lock(index->fd, INN_LOCK_UNLOCK); /* Delete the group data files for this group. */ tdx_data_delete(group, NULL); return true; } /* ** Close an open handle to the group index file, freeing the group_index ** structure at the same time. The argument to this function becomes invalid ** after this call. */ void tdx_index_close(struct group_index *index) { index_unmap(index); if (index->fd >= 0) { close(index->fd); index->fd = -1; } free(index->path); free(index); } /* ** Open the data files for a particular group. The interface to this has to ** be in this file because we have to lock the group and retry if the inode ** of the opened index file doesn't match the one recorded in the group index ** file. Optionally take a pointer to the group index entry if the caller ** has already gone to the work of finding it. */ struct group_data * tdx_data_open(struct group_index *index, const char *group, struct group_entry *entry) { struct group_data *data; ARTNUM high, base; ptrdiff_t offset; if (entry == NULL) { entry = tdx_index_entry(index, group); if (entry == NULL) return NULL; } offset = entry - index->entries; data = tdx_data_new(group, index->writable); /* Check to see if the inode of the index file matches. If it doesn't, this probably means that as we were opening the index file, someone else rewrote it (either expire or repack). Obtain a lock and try again. If there's still a mismatch, go with what we get; there's some sort of corruption. This code is very sensitive to order and parallelism. See the comment at the beginning of this file for methodology. */ if (!tdx_data_open_files(data)) goto fail; high = entry->high; base = entry->base; if (entry->indexinode != data->indexinode) { index_lock_group(index->fd, offset, INN_LOCK_READ); if (!tdx_data_open_files(data)) { index_lock_group(index->fd, offset, INN_LOCK_UNLOCK); goto fail; } if (entry->indexinode != data->indexinode) warn("tradindexed: index inode mismatch for %s", group); high = entry->high; base = entry->base; index_lock_group(index->fd, offset, INN_LOCK_UNLOCK); } data->high = high; data->base = base; return data; fail: tdx_data_close(data); return NULL; } /* ** Add an overview record for a particular article. Takes the group entry, ** the open overview data structure, and the information about the article ** and returns true on success, false on failure. This function calls ** tdx_data_store to do most of the real work and then updates the index ** information. */ bool tdx_data_add(struct group_index *index, struct group_entry *entry, struct group_data *data, const struct article *article) { ARTNUM old_base; ino_t old_inode; ptrdiff_t offset = entry - index->entries; if (!index->writable) return false; index_lock_group(index->fd, offset, INN_LOCK_WRITE); /* Make sure we have the most current data files and that we have the right base article number. */ if (entry->indexinode != data->indexinode) { if (!tdx_data_open_files(data)) goto fail; if (entry->indexinode != data->indexinode) warn("tradindexed: index inode mismatch for %s", HashToText(entry->hash)); data->base = entry->base; } /* If the article number is too low to store in the group index, repack the group with a lower base index. */ if (entry->base > article->number) { if (!tdx_data_pack_start(data, article->number)) goto fail; old_inode = entry->indexinode; old_base = entry->base; entry->indexinode = data->indexinode; entry->base = data->base; inn_msync_page(entry, sizeof(*entry), MS_ASYNC); if (!tdx_data_pack_finish(data)) { entry->base = old_base; entry->indexinode = old_inode; inn_msync_page(entry, sizeof(*entry), MS_ASYNC); goto fail; } } /* Store the data. */ if (!tdx_data_store(data, article)) goto fail; if (entry->base == 0) entry->base = data->base; if (entry->low == 0 || entry->low > article->number) entry->low = article->number; if (entry->high < article->number) entry->high = article->number; entry->count++; /* Used to know that we have to remap the data file owing to our OVSTATICSEARCH (an article whose number is lower than the highest has been added at the end of the file). */ if (data->high > article->number) data->remapoutoforder = true; inn_msync_page(entry, sizeof(*entry), MS_ASYNC); index_lock_group(index->fd, offset, INN_LOCK_UNLOCK); return true; fail: index_lock_group(index->fd, offset, INN_LOCK_UNLOCK); return false; } /* ** Start a rebuild of the group data for a newsgroup. Right now, all this ** does is lock the group index entry. */ bool tdx_index_rebuild_start(struct group_index *index, struct group_entry *entry) { ptrdiff_t offset; offset = entry - index->entries; return index_lock_group(index->fd, offset, INN_LOCK_WRITE); } /* ** Finish a rebuild of the group data for a newsgroup. Takes the old and new ** entry and writes the data from the new entry into the group index, and ** then unlocks it. */ bool tdx_index_rebuild_finish(struct group_index *index, struct group_entry *entry, struct group_entry *new) { ptrdiff_t offset; ino_t new_inode; new_inode = new->indexinode; new->indexinode = entry->indexinode; *entry = *new; entry->indexinode = new_inode; new->indexinode = new_inode; inn_msync_page(entry, sizeof(*entry), MS_ASYNC); offset = entry - index->entries; index_lock_group(index->fd, offset, INN_LOCK_UNLOCK); return true; } /* ** Expire a single newsgroup. Most of the work is done by tdx_data_expire*, ** but this routine has the responsibility to do locking (the same as would ** be done for repacking, since the group base may change) and updating the ** group entry. */ bool tdx_expire(const char *group, ARTNUM *low, struct history *history) { struct group_index *index; struct group_entry *entry; struct group_entry new_entry; struct group_data *data = NULL; ptrdiff_t offset; ARTNUM old_base; ino_t old_inode; index = tdx_index_open(true); if (index == NULL) return false; entry = tdx_index_entry(index, group); if (entry == NULL) { tdx_index_close(index); return false; } tdx_index_rebuild_start(index, entry); /* tdx_data_expire_start builds the new IDX and DAT files and fills in the struct group_entry that was passed to it. tdx_data_rebuild_finish does the renaming of the new files to the final file names. */ new_entry = *entry; new_entry.low = 0; new_entry.count = 0; new_entry.base = 0; data = tdx_data_open(index, group, entry); if (data == NULL) goto fail; if (!tdx_data_expire_start(group, data, &new_entry, history)) goto fail; old_inode = entry->indexinode; old_base = entry->base; entry->indexinode = new_entry.indexinode; entry->base = new_entry.base; inn_msync_page(entry, sizeof(*entry), MS_ASYNC); tdx_data_close(data); if (!tdx_data_rebuild_finish(group)) { entry->base = old_base; entry->indexinode = old_inode; inn_msync_page(entry, sizeof(*entry), MS_ASYNC); goto fail; } /* Almost done. Update the group index. If there are no articles in the group, the low water mark should be one more than the high water mark. */ if (new_entry.low == 0) new_entry.low = new_entry.high + 1; tdx_index_rebuild_finish(index, entry, &new_entry); if (low != NULL) *low = entry->low; tdx_index_close(index); return true; fail: offset = entry - index->entries; index_lock_group(index->fd, offset, INN_LOCK_UNLOCK); if (data != NULL) tdx_data_close(data); tdx_index_close(index); return false; } /* ** RECOVERY AND AUDITING ** ** All code below this point is not used in the normal operations of the ** overview method. Instead, it's code to dump various data structures or ** audit them for consistency, used by recovery tools and inspection tools. */ /* Holds a newsgroup name and its hash, used to form a hash table mapping newsgroup hash values to the actual names. */ struct hashmap { HASH hash; char *name; char flag; }; /* Holds information needed by hash traversal functions. Right now, this is just the pointer to the group index and a flag saying whether to fix problems or not. */ struct audit_data { struct group_index *index; bool fix; }; /* ** Hash table functions for the mapping from group hashes to names. */ static unsigned long hashmap_hash(const void *entry) { unsigned long hash; const struct hashmap *group = entry; memcpy(&hash, &group->hash, sizeof(hash)); return hash; } static const void * hashmap_key(const void *entry) { return &((const struct hashmap *) entry)->hash; } static bool hashmap_equal(const void *key, const void *entry) { const HASH *first = key; const HASH *second; second = &((const struct hashmap *) entry)->hash; return memcmp(first, second, sizeof(HASH)) == 0; } static void hashmap_delete(void *entry) { struct hashmap *group = entry; free(group->name); free(group); } /* ** Construct a hash table of group hashes to group names by scanning the ** active file. Returns the constructed hash table. */ static struct hash * hashmap_load(void) { struct hash *hash; QIOSTATE *active; char *activepath, *line; struct cvector *data = NULL; struct stat st; size_t hash_size; struct hashmap *group; HASH grouphash; activepath = concatpath(innconf->pathdb, INN_PATH_ACTIVE); active = QIOopen(activepath); free(activepath); if (active == NULL) return NULL; if (fstat(QIOfileno(active), &st) < 0) hash_size = 32 * 1024; else hash_size = st.st_size / 30; hash = hash_create(hash_size, hashmap_hash, hashmap_key, hashmap_equal, hashmap_delete); line = QIOread(active); while (line != NULL) { data = cvector_split_space(line, data); if (data->count != 4) { warn("tradindexed: malformed active file line %s", line); continue; } group = xmalloc(sizeof(struct hashmap)); group->name = xstrdup(data->strings[0]); group->flag = data->strings[3][0]; grouphash = Hash(group->name, strlen(group->name)); memcpy(&group->hash, &grouphash, sizeof(HASH)); hash_insert(hash, &group->hash, group); line = QIOread(active); } if (data != NULL) cvector_free(data); QIOclose(active); return hash; } /* ** Print the stored information about a single group in human-readable form ** to stdout. The format is: ** ** name high low base count flag deleted inode ** ** all on one line. Name is passed into this function. */ void tdx_index_print(const char *name, const struct group_entry *entry, FILE *output) { fprintf(output, "%s %lu %lu %lu %lu %c %lu %lu\n", name, entry->high, entry->low, entry->base, (unsigned long) entry->count, entry->flag, (unsigned long) entry->deleted, (unsigned long) entry->indexinode); } /* ** Dump the complete contents of the group.index file in human-readable form ** to the specified file, one line per group. */ void tdx_index_dump(struct group_index *index, FILE *output) { int bucket; long current; struct group_entry *entry; struct hash *hashmap; struct hashmap *group; char *name; if (index->header == NULL || index->entries == NULL) return; hashmap = hashmap_load(); for (bucket = 0; bucket < TDX_HASH_SIZE; bucket++) { current = index->header->hash[bucket].recno; while (current != -1) { if (!index_maybe_remap(index, current)) return; entry = index->entries + current; name = NULL; if (hashmap != NULL) { group = hash_lookup(hashmap, &entry->hash); if (group != NULL) name = group->name; } if (name == NULL) name = HashToText(entry->hash); tdx_index_print(name, entry, output); if (current == entry->next.recno) { warn("tradindexed: index loop for entry %ld", current); return; } current = entry->next.recno; } } if (hashmap != NULL) hash_free(hashmap); } /* ** Audit a particular group entry location to ensure that it points to a ** valid entry within the group index file. Takes a pointer to the location, ** the number of the location, a pointer to the group entry if any (if not, ** the location is assumed to be part of the header hash table), and a flag ** saying whether to fix problems that are found. */ static void index_audit_loc(struct group_index *index, int *loc, long number, struct group_entry *entry, bool fix) { bool error = false; if (*loc >= index->count) { warn("tradindexed: out of range index %d in %s %ld", *loc, (entry == NULL ? "bucket" : "entry"), number); error = true; } if (*loc < 0 && *loc != -1) { warn("tradindexed: invalid negative index %d in %s %ld", *loc, (entry == NULL ? "bucket" : "entry"), number); error = true; } if (entry != NULL && *loc == number) { warn("tradindexed: index loop for entry %ld", number); error = true; } if (fix && error) { *loc = -1; inn_msync_page(loc, sizeof(*loc), MS_ASYNC); } } /* ** Check an entry to see if it was actually deleted. Make sure that all the ** information is consistent with a deleted group if it's not and the fix ** flag is set. */ static void index_audit_deleted(struct group_entry *entry, long number, bool fix) { if (entry->deleted != 0 && !HashEmpty(entry->hash)) { warn("tradindexed: entry %ld has a delete time but a non-zero hash", number); if (fix) { HashClear(&entry->hash); inn_msync_page(entry, sizeof(*entry), MS_ASYNC); } } } /* ** Audit the group header for any inconsistencies. This checks the ** reachability of all of the group entries, makes sure that deleted entries ** are on the free list, and otherwise checks the linked structure of the ** whole file. The data in individual entries is not examined. If the ** second argument is true, also attempt to fix inconsistencies. */ static void index_audit_header(struct group_index *index, bool fix) { long bucket, current; struct group_entry *entry; int *parent, *next; bool *reachable; /* First, walk all of the regular hash buckets, making sure that all of the group location pointers are valid and sane, that all groups that have been deleted are correctly marked as such, and that all groups are in their correct hash chain. Build reachability information as we go, used later to ensure that all group entries are reachable. */ reachable = xcalloc(index->count, sizeof(bool)); for (bucket = 0; bucket < TDX_HASH_SIZE; bucket++) { parent = &index->header->hash[bucket].recno; index_audit_loc(index, parent, bucket, NULL, fix); current = *parent; while (current >= 0 && current < index->count) { entry = &index->entries[current]; next = &entry->next.recno; if (entry->deleted == 0 && bucket != index_bucket(entry->hash)) { warn("tradindexed: entry %ld is in bucket %ld instead of its" " correct bucket %ld", current, bucket, index_bucket(entry->hash)); if (fix) { entry_splice(entry, parent); next = parent; } } else { if (reachable[current]) warn("tradindexed: entry %ld is reachable from multiple" " paths", current); reachable[current] = true; } index_audit_deleted(entry, current, fix); index_audit_loc(index, &entry->next.recno, current, entry, fix); if (entry->deleted != 0) { warn("tradindexed: entry %ld is deleted but not in the free" " list", current); if (fix) { entry_splice(entry, parent); next = parent; reachable[current] = false; } } if (*next == current) break; parent = next; current = *parent; } } /* Now, walk the free list. Make sure that each group in the free list is actually deleted, and update the reachability information. */ index_audit_loc(index, &index->header->freelist.recno, 0, NULL, fix); parent = &index->header->freelist.recno; current = *parent; while (current >= 0 && current < index->count) { entry = &index->entries[current]; index_audit_deleted(entry, current, fix); reachable[current] = true; if (!HashEmpty(entry->hash) && entry->deleted == 0) { warn("tradindexed: undeleted entry %ld in free list", current); if (fix) { entry_splice(entry, parent); reachable[current] = false; } } index_audit_loc(index, &entry->next.recno, current, entry, fix); if (entry->next.recno == current) break; parent = &entry->next.recno; current = *parent; } /* Finally, check all of the unreachable entries and if fix is true, try to reattach them in the appropriate location. */ for (current = 0; current < index->count; current++) if (!reachable[current]) { warn("tradindexed: unreachable entry %ld", current); if (fix) { entry = &index->entries[current]; if (!HashEmpty(entry->hash) && entry->deleted == 0) index_add(index, entry); else { HashClear(&entry->hash); entry->deleted = 0; freelist_add(index, entry); } } } /* All done. */ free(reachable); } /* ** Audit a particular group entry for any inconsistencies. This doesn't ** check any of the structure, or whether the group is deleted, just the data ** as stored in the group data files (mostly by calling tdx_data_audit to do ** the real work). Note that while the low water mark may be updated, the ** high water mark is left unchanged. */ static void index_audit_group(struct group_index *index, struct group_entry *entry, struct hash *hashmap, bool fix) { struct hashmap *group; ptrdiff_t offset; offset = entry - index->entries; index_lock_group(index->fd, offset, INN_LOCK_WRITE); group = hash_lookup(hashmap, &entry->hash); if (group == NULL) { warn("tradindexed: group %ld not found in active file", entry_loc(index, entry)); if (fix) { index_unlink_hash(index, entry->hash); HashClear(&entry->hash); entry->deleted = time(NULL); freelist_add(index, entry); } } else { if (entry->flag != group->flag) { entry->flag = group->flag; inn_msync_page(entry, sizeof(*entry), MS_ASYNC); } tdx_data_audit(group->name, entry, fix); } index_lock_group(index->fd, offset, INN_LOCK_UNLOCK); } /* ** Check to be sure that a given group exists in the overview index, and if ** missing, adds it. Assumes that the index isn't locked, since it calls the ** normal functions for adding new groups (this should only be called after ** the index has already been repaired, for the same reason). Called as a ** hash traversal function, walking the hash table of groups from the active ** file. */ static void index_audit_active(void *value, void *cookie) { struct hashmap *group = value; struct audit_data *data = cookie; struct group_entry *entry; entry = tdx_index_entry(data->index, group->name); if (entry == NULL) { warn("tradindexed: group %s missing from overview", group->name); if (data->fix) tdx_index_add(data->index, group->name, 0, 0, &group->flag); } } /* ** Audit the group index for any inconsistencies. If the argument is true, ** also attempt to fix those inconsistencies. */ void tdx_index_audit(bool fix) { struct group_index *index; struct stat st; off_t expected; int count; struct hash *hashmap; long bucket; struct group_entry *entry; struct audit_data data; index = tdx_index_open(true); if (index == NULL) return; /* Keep a lock on the header through the whole audit process. This will stall any newgroups or rmgroups, but not normal article reception. We don't want the structure of the group entries changing out from under us, although we don't mind if the data does until we're validating that particular group. */ index_lock(index->fd, INN_LOCK_WRITE); /* Make sure the size looks sensible. */ if (fstat(index->fd, &st) < 0) { syswarn("tradindexed: cannot fstat %s", index->path); return; } count = index_entry_count(st.st_size); expected = index_file_size(count); if (expected != st.st_size) { syswarn("tradindexed: %ld bytes of trailing trash in %s", (unsigned long) (st.st_size - expected), index->path); if (fix) if (ftruncate(index->fd, expected) < 0) syswarn("tradindexed: cannot truncate %s", index->path); } index_maybe_remap(index, count); /* Okay everything is now mapped and happy. Validate the header. */ index_audit_header(index, fix); index_lock(index->fd, INN_LOCK_UNLOCK); /* Walk all the group entries and check them individually. To do this, we need to map hashes to group names, so load a hash of the active file to do that resolution. */ hashmap = hashmap_load(); if (hashmap == NULL) { warn("tradindexed: cannot hash active file"); return; } data.index = index; data.fix = fix; hash_traverse(hashmap, index_audit_active, &data); for (bucket = 0; bucket < index->count; bucket++) { entry = &index->entries[bucket]; if (HashEmpty(entry->hash) || entry->deleted != 0) continue; index_audit_group(index, entry, hashmap, fix); } hash_free(hashmap); } inn-2.6.0/storage/tradindexed/tradindexed.c0000644000175200017520000002665412575023702020333 0ustar iuliusiulius/* $Id: tradindexed.c 8947 2010-02-06 11:28:56Z iulius $ ** ** Interface implementation for the tradindexed overview method. ** ** This code converts between the internal interface used by the tradindexed ** implementation and the interface expected by the INN overview API. The ** internal interface is in some cases better suited to the data structures ** that the tradindexed overview method uses, and this way the internal ** interface can be kept isolated from the external interface. (There are ** also some operations that can be performed entirely in the interface ** layer.) */ #include "config.h" #include "clibrary.h" #include "inn/innconf.h" #include "inn/messages.h" #include "inn/libinn.h" #include "inn/ov.h" #include "inn/storage.h" #include "tdx-private.h" #include "tdx-structure.h" #include "tradindexed.h" /* This structure holds all of the data about the open overview files. We can eventually pass one of these structures back to the caller of open when the overview API is more object-oriented. */ struct tradindexed { struct group_index *index; struct cache *cache; bool cutoff; }; /* Global data about the open tradindexed method. */ static struct tradindexed *tradindexed; /* ** Helper function to open a group_data structure via the cache, inserting it ** into the cache if it wasn't found in the cache. */ static struct group_data * data_cache_open(struct tradindexed *global, const char *group, struct group_entry *entry) { struct group_data *data; data = tdx_cache_lookup(global->cache, entry->hash); if (data == NULL) { data = tdx_data_open(global->index, group, entry); if (data == NULL) return NULL; tdx_cache_insert(global->cache, entry->hash, data); } return data; } /* ** Helper function to reopen the data files and remove the old entry from the ** cache if we think that might help better fulfill a search. */ static struct group_data * data_cache_reopen(struct tradindexed *global, const char *group, struct group_entry *entry) { struct group_data *data; tdx_cache_delete(global->cache, entry->hash); data = tdx_data_open(global->index, group, entry); if (data == NULL) return NULL; tdx_cache_insert(global->cache, entry->hash, data); return data; } /* ** Open the overview method. */ bool tradindexed_open(int mode) { unsigned long cache_size, fdlimit; if (tradindexed != NULL) { warn("tradindexed: overview method already open"); return false; } tradindexed = xmalloc(sizeof(struct tradindexed)); tradindexed->index = tdx_index_open((mode & OV_WRITE) ? true : false); tradindexed->cutoff = false; /* Use a cache size of two for read-only connections. We may want to rethink the limitation of the cache for reading later based on real-world experience. */ cache_size = (mode & OV_WRITE) ? innconf->overcachesize : 1; fdlimit = getfdlimit(); if (fdlimit > 0 && fdlimit < cache_size * 2) { warn("tradindexed: not enough file descriptors for an overview cache" " size of %lu; increase rlimitnofile or decrease overcachesize" " to at most %lu", cache_size, fdlimit / 2); cache_size = (fdlimit > 2) ? fdlimit / 2 : 1; } tradindexed->cache = tdx_cache_create(cache_size); return (tradindexed->index == NULL) ? false : true; } /* ** Get statistics about a group. Convert between the multiple pointer API ** and the structure API used internally. */ bool tradindexed_groupstats(const char *group, int *low, int *high, int *count, int *flag) { const struct group_entry *entry; if (tradindexed == NULL || tradindexed->index == NULL) { warn("tradindexed: overview method not initialized"); return false; } entry = tdx_index_entry(tradindexed->index, group); if (entry == NULL) return false; if (low != NULL) *low = entry->low; if (high != NULL) *high = entry->high; if (count != NULL) *count = entry->count; if (flag != NULL) *flag = entry->flag; return true; } /* ** Add a new newsgroup to the index. */ bool tradindexed_groupadd(const char *group, ARTNUM low, ARTNUM high, char *flag) { if (tradindexed == NULL || tradindexed->index == NULL) { warn("tradindexed: overview method not initialized"); return false; } return tdx_index_add(tradindexed->index, group, low, high, flag); } /* ** Delete a newsgroup from the index. */ bool tradindexed_groupdel(const char *group) { if (tradindexed == NULL || tradindexed->index == NULL) { warn("tradindexed: overview method not initialized"); return false; } return tdx_index_delete(tradindexed->index, group); } /* ** Add data about a single article. Convert between the multiple argument ** API and the structure API used internally, and also implement low article ** cutoff if that was requested. */ bool tradindexed_add(const char *group, ARTNUM artnum, TOKEN token, char *data, int length, time_t arrived, time_t expires) { struct article article; struct group_data *group_data; struct group_entry *entry; if (tradindexed == NULL || tradindexed->index == NULL) { warn("tradindexed: overview method not initialized"); return false; } /* Get the group index entry and don't do any work if cutoff is set and the article number is lower than the low water mark for the group. */ entry = tdx_index_entry(tradindexed->index, group); if (entry == NULL) return true; if (tradindexed->cutoff && entry->low > artnum) return true; /* Fill out the article data structure. */ article.number = artnum; article.overview = data; article.overlen = length; article.token = token; article.arrived = arrived; article.expires = expires; /* Open the appropriate data structures, using the cache. */ group_data = data_cache_open(tradindexed, group, entry); if (group_data == NULL) return false; return tdx_data_add(tradindexed->index, entry, group_data, &article); } /* ** Cancel an article. We do this by blanking out its entry in the group ** index, making the data inaccessible. The next expiration run will remove ** the actual data. */ bool tradindexed_cancel(const char *group, ARTNUM artnum) { struct group_entry *entry; struct group_data *data; if (tradindexed == NULL || tradindexed->index == NULL) { warn("tradindexed: overview method not initialized"); return false; } entry = tdx_index_entry(tradindexed->index, group); if (entry == NULL) return false; data = data_cache_open(tradindexed, group, entry); if (data == NULL) return false; if (artnum > data->high) { data = data_cache_reopen(tradindexed, group, entry); if (data == NULL) return false; } return tdx_data_cancel(data, artnum); } /* ** Open an overview search. Open the appropriate group and then start a ** search in it. */ void * tradindexed_opensearch(const char *group, int low, int high) { struct group_entry *entry; struct group_data *data; if (tradindexed == NULL || tradindexed->index == NULL) { warn("tradindexed: overview method not initialized"); return NULL; } entry = tdx_index_entry(tradindexed->index, group); if (entry == NULL) return NULL; data = data_cache_open(tradindexed, group, entry); if (data == NULL) return NULL; if (entry->base != data->base) if (data->base > (ARTNUM) low && entry->base < data->base) { data = data_cache_reopen(tradindexed, group, entry); if (data == NULL) return NULL; } return tdx_search_open(data, low, high, entry->high); } /* ** Get the next article returned by a search. Convert between the multiple ** pointer API and the structure API we use internally. */ bool tradindexed_search(void *handle, ARTNUM *artnum, char **data, int *length, TOKEN *token, time_t *arrived) { struct article article; if (tradindexed == NULL || tradindexed->index == NULL) { warn("tradindexed: overview method not initialized"); return false; } if (!tdx_search(handle, &article)) return false; if (artnum != NULL) *artnum = article.number; if (data != NULL) *data = (char *) article.overview; if (length != NULL) *length = article.overlen; if (token != NULL) *token = article.token; if (arrived != NULL) *arrived = article.arrived; return true; } /* ** Close an overview search. */ void tradindexed_closesearch(void *handle) { tdx_search_close(handle); } /* ** Get information for a single article. Open the appropriate group and then ** convert from the pointer API to the struct API used internally. */ bool tradindexed_getartinfo(const char *group, ARTNUM artnum, TOKEN *token) { struct group_entry *entry; struct group_data *data; const struct index_entry *index_entry; if (tradindexed == NULL || tradindexed->index == NULL) { warn("tradindexed: overview method not initialized"); return false; } entry = tdx_index_entry(tradindexed->index, group); if (entry == NULL) return false; data = data_cache_open(tradindexed, group, entry); if (data == NULL) return false; if (entry->base != data->base) if (data->base > artnum && entry->base <= artnum) { data = data_cache_reopen(tradindexed, group, entry); if (data == NULL) return false; } index_entry = tdx_article_entry(data, artnum, entry->high); if (index_entry == NULL) return false; if (token != NULL) *token = index_entry->token; return true; } /* ** Expire a single newsgroup. */ bool tradindexed_expiregroup(const char *group, int *low, struct history *history) { ARTNUM new_low; bool status; /* tradindexed doesn't have any periodic cleanup. */ if (group == NULL) return true; status = tdx_expire(group, &new_low, history); if (status && low != NULL) *low = (int) new_low; return status; } /* ** Set various options or query various paramaters for the overview method. ** The interface is, at present, not particularly sane. */ bool tradindexed_ctl(OVCTLTYPE type, void *val) { int *i; bool *b; OVSORTTYPE *sort; if (tradindexed == NULL) { warn("tradindexed: overview method not initialized"); return false; } switch (type) { case OVSPACE: i = (int *) val; *i = -1; return true; case OVSORT: sort = (OVSORTTYPE *) val; *sort = OVNEWSGROUP; return true; case OVCUTOFFLOW: b = (bool *) val; tradindexed->cutoff = *b; return true; case OVSTATICSEARCH: i = (int *) val; *i = false; return true; case OVCACHEKEEP: case OVCACHEFREE: b = (bool *) val; *b = false; return true; default: return false; } } /* ** Close the overview method. */ void tradindexed_close(void) { if (tradindexed != NULL) { if (tradindexed->index != NULL) tdx_index_close(tradindexed->index); if (tradindexed->cache != NULL) tdx_cache_free(tradindexed->cache); free(tradindexed); tradindexed = NULL; } } inn-2.6.0/storage/tradindexed/tdx-private.h0000644000175200017520000001360512575023702020304 0ustar iuliusiulius/* $Id: tdx-private.h 8458 2009-05-12 06:12:42Z iulius $ ** ** Private APIs for the tradindexed overview method. */ #ifndef INN_TDX_PRIVATE_H #define INN_TDX_PRIVATE_H 1 #include "config.h" #include #include #include "inn/libinn.h" #include "inn/storage.h" /* Forward declarations to avoid unnecessary includes. */ struct history; /* Opaque data structure used by the cache. */ struct cache; /* Opaque data structure returned by group index functions. */ struct group_index; /* Opaque data structure returned by search functions. */ struct search; /* All of the information about an open set of group data files. */ struct group_data { char *path; bool writable; bool remapoutoforder; ARTNUM high; ARTNUM base; int indexfd; int datafd; struct index_entry *index; char *data; off_t indexlen; off_t datalen; ino_t indexinode; int refcount; }; /* All of the data about an article, used as the return of the search functions. This is just cleaner than passing back all of the information that's used by the regular interface. */ struct article { ARTNUM number; const char *overview; size_t overlen; TOKEN token; time_t arrived; time_t expires; }; BEGIN_DECLS /* tdx-cache.c */ /* Create a new cache with the given number of entries. */ struct cache *tdx_cache_create(unsigned int size); /* Look up a given newsgroup hash in the cache, returning the group_data struct for its open data files if present. */ struct group_data *tdx_cache_lookup(struct cache *, HASH); /* Insert a new group_data struct into the cache. */ void tdx_cache_insert(struct cache *, HASH, struct group_data *); /* Delete a group entry from the cache. */ void tdx_cache_delete(struct cache *, HASH); /* Free the cache and its resources. */ void tdx_cache_free(struct cache *); /* tdx-group.c */ /* Open the group index and return an opaque data structure to use for further queries. */ struct group_index *tdx_index_open(bool writable); /* Return the stored information about a single newsgroup. */ struct group_entry *tdx_index_entry(struct group_index *, const char *group); /* Print the contents of a single group entry in human-readable form. */ void tdx_index_print(const char *name, const struct group_entry *, FILE *); /* Add a new newsgroup to the index file. */ bool tdx_index_add(struct group_index *, const char *group, ARTNUM low, ARTNUM high, const char *flag); /* Delete a newsgroup from the index file. */ bool tdx_index_delete(struct group_index *, const char *group); /* Dump the contents of the index file to stdout in human-readable form. */ void tdx_index_dump(struct group_index *, FILE *); /* Audit all of the overview data, optionally trying to fix it. */ void tdx_index_audit(bool fix); /* Close the open index file and dispose of the opaque data structure. */ void tdx_index_close(struct group_index *); /* Open the overview information for a particular group. */ struct group_data *tdx_data_open(struct group_index *, const char *group, struct group_entry *); /* Add a new overview entry. */ bool tdx_data_add(struct group_index *, struct group_entry *, struct group_data *, const struct article *); /* Handle rebuilds of the data for a particular group. Call _start first and then _finish when done, with the new group_entry information. */ bool tdx_index_rebuild_start(struct group_index *, struct group_entry *); bool tdx_index_rebuild_finish(struct group_index *, struct group_entry *, struct group_entry *new); /* Expire a single group. */ bool tdx_expire(const char *group, ARTNUM *low, struct history *); /* tdx-data.c */ /* Create a new group data structure. */ struct group_data *tdx_data_new(const char *group, bool writable); /* Open the data files for a group. */ bool tdx_data_open_files(struct group_data *); /* Return the metadata about a particular article in a group. */ const struct index_entry *tdx_article_entry(struct group_data *, ARTNUM article, ARTNUM high); /* Create, perform, and close a search. */ struct search *tdx_search_open(struct group_data *, ARTNUM start, ARTNUM end, ARTNUM high); bool tdx_search(struct search *, struct article *); void tdx_search_close(struct search *); /* Store article data. */ bool tdx_data_store(struct group_data *, const struct article *); /* Cancel an entry. */ bool tdx_data_cancel(struct group_data *, ARTNUM); /* Start a repack of the files for a newsgroup. */ bool tdx_data_pack_start(struct group_data *, ARTNUM); /* Complete a repack of the files for a newsgroup. */ bool tdx_data_pack_finish(struct group_data *); /* Manage a rebuild of the data files for a particular group. Until tdx_data_rebuild_finish is called, anything stored into the returned struct group_data will have no effect on the data for that group. Does not handle updating the index entries; that must be done separately. */ struct group_data *tdx_data_rebuild_start(const char *group); bool tdx_data_rebuild_finish(const char *group); /* Start the expiration of a newsgroup and do most of the work, filling out the provided group_entry struct. Complete with tdx_data_rebuild_finish. */ bool tdx_data_expire_start(const char *group, struct group_data *, struct group_entry *, struct history *); /* Dump the contents of the index file for a group. */ void tdx_data_index_dump(struct group_data *, FILE *); /* Audit the data for a particular group, optionally trying to fix it. */ void tdx_data_audit(const char *group, struct group_entry *, bool fix); /* Close the open data files for a group and free the structure. */ void tdx_data_close(struct group_data *); /* Delete the data files for a group. */ void tdx_data_delete(const char *group, const char *suffix); END_DECLS #endif /* INN_TDX_PRIVATE_H */ inn-2.6.0/storage/tradindexed/tdx-cache.c0000644000175200017520000001174412575023702017672 0ustar iuliusiulius/* $Id: tdx-cache.c 8993 2010-03-14 20:41:19Z iulius $ ** ** Cache functions for open overview data files. ** ** This code maintains a cache of open overview data files to avoid some of ** the overhead involved in closing and reopening files. All opens and ** closes should go through this code, and the hit ratio is tracked to check ** cache effectiveness. */ #include "config.h" #include "clibrary.h" #include #include "inn/hashtab.h" #include "inn/messages.h" #include "inn/libinn.h" #include "inn/storage.h" #include "tdx-private.h" /* Returned to callers as an opaque data type, this struct holds all of the information about the cache. */ struct cache { struct hash *hashtable; unsigned int count; unsigned int max; unsigned long queries; unsigned long hits; }; /* A cache entry, holding a group_data struct and some additional information used to do cache lookups and to choose what to drop from the cache. */ struct cache_entry { struct group_data *data; HASH hash; time_t lastused; }; /* ** The hash function for a cache_entry. Just return as much of the group ** hash as can fit in an unsigned long. */ static unsigned long entry_hash(const void *key) { const HASH *hash = key; unsigned long bucket; memcpy(&bucket, hash, sizeof(bucket)); return bucket; } /* ** Given a cache_entry, return its key. */ static const void * entry_key(const void *data) { const struct cache_entry *entry = (const struct cache_entry *) data; return &entry->hash; } /* ** Check to see if two entries are equal. */ static bool entry_equal(const void *key, const void *data) { const HASH *hash = (const HASH *) key; const struct cache_entry *entry = (const struct cache_entry *) data; return (memcmp(hash, &entry->hash, sizeof(HASH)) == 0); } /* ** Free a cache entry. */ static void entry_delete(void *data) { struct cache_entry *entry = (struct cache_entry *) data; entry->data->refcount--; if (entry->data->refcount == 0) tdx_data_close(entry->data); free(entry); } /* ** Called by hash_traverse, this function finds the oldest entry with the ** smallest refcount and stores it in the provided pointer so that it can be ** freed. This is used when the cache is full to drop the least useful ** entry. */ static void entry_find_oldest(void *data, void *cookie) { struct cache_entry *entry = (struct cache_entry *) data; struct cache_entry **oldest = (struct cache_entry **) cookie; if (*oldest == NULL) { *oldest = entry; return; } if (entry->data->refcount > (*oldest)->data->refcount) return; if (entry->lastused > (*oldest)->lastused) return; *oldest = data; } /* ** Create a new cache with the given size. */ struct cache * tdx_cache_create(unsigned int size) { struct cache *cache; cache = xmalloc(sizeof(struct cache)); cache->count = 0; cache->max = size; cache->queries = 0; cache->hits = 0; cache->hashtable = hash_create(size * 4 / 3, entry_hash, entry_key, entry_equal, entry_delete); return cache; } /* ** Look up a particular entry and return it. */ struct group_data * tdx_cache_lookup(struct cache *cache, HASH hash) { struct cache_entry *entry; cache->queries++; entry = hash_lookup(cache->hashtable, &hash); if (entry != NULL) { cache->hits++; entry->lastused = time(NULL); } return (entry == NULL) ? NULL : entry->data; } /* ** Insert a new entry, clearing out the oldest entry if the cache is ** currently full. */ void tdx_cache_insert(struct cache *cache, HASH hash, struct group_data *data) { struct cache_entry *entry; if (cache->count == cache->max) { struct cache_entry *oldest = NULL; hash_traverse(cache->hashtable, entry_find_oldest, &oldest); if (oldest == NULL) { warn("tradindexed: unable to find oldest cache entry"); return; } else { if (!hash_delete(cache->hashtable, &oldest->hash)) { warn("tradindexed: cannot delete oldest cache entry"); return; } } cache->count--; } entry = xmalloc(sizeof(struct cache_entry)); entry->data = data; entry->hash = hash; entry->lastused = time(NULL); if (!hash_insert(cache->hashtable, &entry->hash, entry)) { warn("tradindexed: duplicate cache entry for %s", HashToText(hash)); free(entry); } else { entry->data->refcount++; cache->count++; } } /* ** Delete an entry from the cache. */ void tdx_cache_delete(struct cache *cache, HASH hash) { if (!hash_delete(cache->hashtable, &hash)) { warn("tradindexed: unable to remove cache entry for %s", HashToText(hash)); } else { cache->count--; } } /* ** Delete the cache and all of the resources that it's holding open. */ void tdx_cache_free(struct cache *cache) { hash_free(cache->hashtable); free(cache); } inn-2.6.0/storage/ovinterface.h0000644000175200017520000000362412575023702016047 0ustar iuliusiulius/* $Id: ovinterface.h 9194 2011-04-16 08:08:54Z iulius $ ** ** Overview interface header */ #ifndef OVINTERFACE_H #define OVINTERFACE_H 1 #include "config.h" #include "inn/history.h" #include "inn/ov.h" #include "inn/storage.h" struct buffer; struct vector; typedef struct overview_method { const char *name; bool (*open)(int mode); bool (*groupstats)(const char *group, int *lo, int *hi, int *count, int *flag); bool (*groupadd)(const char *group, ARTNUM lo, ARTNUM hi, char *flag); bool (*groupdel)(const char *group); bool (*add)(const char *group, ARTNUM artnum, TOKEN token, char *data, int len, time_t arrived, time_t expires); bool (*cancel)(const char *group, ARTNUM artnum); void *(*opensearch)(const char *group, int low, int high); bool (*search)(void *handle, ARTNUM *artnum, char **data, int *len, TOKEN *token, time_t *arrived); void (*closesearch)(void *handle); bool (*getartinfo)(const char *group, ARTNUM artnum, TOKEN *token); bool (*expiregroup)(const char *group, int *lo, struct history *h); bool (*ctl)(OVCTLTYPE type, void *val); void (*close)(void); } OV_METHOD; bool OVgroupbasedexpire(TOKEN token, const char *group, const char *data, int len, time_t arrived, time_t expires); bool OVhisthasmsgid(struct history *, const char *data); void OVEXPremove(TOKEN token, bool deletedgroups, char **xref, int ngroups); void OVEXPcleanup(void); extern bool OVstatall; extern time_t OVnow; extern char *ACTIVE; extern FILE *EXPunlinkfile; extern bool OVignoreselfexpire; extern bool OVusepost; extern bool OVkeep; extern bool OVearliest; extern bool OVquiet; extern time_t OVrealnow; extern long EXPprocessed; extern long EXPunlinked; extern long EXPoverindexdrop; #define DEFAULT_MAX_XREF_LEN 8192 #endif /* OVINTERFACE_H */ inn-2.6.0/storage/methods.c0000644000175200017520000000313712575023702015177 0ustar iuliusiulius/* This file is automatically generated by buildconfig. */ #include "interface.h" #include "methods.h" #include "cnfs/cnfs.h" #include "timecaf/timecaf.h" #include "timehash/timehash.h" #include "tradspool/tradspool.h" #include "trash/trash.h" STORAGE_METHOD storage_methods[5] = { { "cnfs", TOKEN_CNFS, cnfs_init, cnfs_store, cnfs_retrieve, cnfs_next, cnfs_freearticle, cnfs_cancel, cnfs_ctl, cnfs_flushcacheddata, cnfs_printfiles, cnfs_explaintoken, cnfs_shutdown }, { "timecaf", TOKEN_TIMECAF, timecaf_init, timecaf_store, timecaf_retrieve, timecaf_next, timecaf_freearticle, timecaf_cancel, timecaf_ctl, timecaf_flushcacheddata, timecaf_printfiles, timecaf_explaintoken, timecaf_shutdown }, { "timehash", TOKEN_TIMEHASH, timehash_init, timehash_store, timehash_retrieve, timehash_next, timehash_freearticle, timehash_cancel, timehash_ctl, timehash_flushcacheddata, timehash_printfiles, timehash_explaintoken, timehash_shutdown }, { "tradspool", TOKEN_TRADSPOOL, tradspool_init, tradspool_store, tradspool_retrieve, tradspool_next, tradspool_freearticle, tradspool_cancel, tradspool_ctl, tradspool_flushcacheddata, tradspool_printfiles, tradspool_explaintoken, tradspool_shutdown }, { "trash", TOKEN_TRASH, trash_init, trash_store, trash_retrieve, trash_next, trash_freearticle, trash_cancel, trash_ctl, trash_flushcacheddata, trash_printfiles, trash_explaintoken, trash_shutdown } }; inn-2.6.0/storage/interface.c0000644000175200017520000005371612575023702015504 0ustar iuliusiulius/* $Id: interface.c 9353 2011-08-20 20:08:29Z iulius $ ** ** Storage Manager interface. */ #include "config.h" #include "clibrary.h" #include #include #include #include "conffile.h" #include "inn/innconf.h" #include "inn/messages.h" #include "inn/wire.h" #include "interface.h" #include "inn/libinn.h" #include "methods.h" #include "inn/paths.h" typedef enum {INIT_NO, INIT_DONE, INIT_FAIL} INITTYPE; typedef struct { INITTYPE initialized; bool configured; bool selfexpire; bool expensivestat; } METHOD_DATA; METHOD_DATA method_data[NUM_STORAGE_METHODS]; static STORAGE_SUB *subscriptions = NULL; static unsigned int typetoindex[256]; int SMerrno; char *SMerrorstr = NULL; static bool Initialized = false; bool SMopenmode = false; bool SMpreopen = false; /* ** Checks to see if the token is valid. */ bool IsToken(const char *text) { const char *p; if (!text) return false; if (strlen(text) != (sizeof(TOKEN) * 2) + 2) return false; if (text[0] != '@') return false; /* Make sure the token ends with '@' and contains no other '@' * besides its first and its last char. */ if (strchr(text + 1, '@') != text + (sizeof(TOKEN) * 2) + 1) return false; for (p = text + 1; *p != '@'; p++) { /* Accept only [0-9] and uppercase [A-F]. */ if (!isxdigit((unsigned char) *p) || toupper((unsigned char) *p) != (unsigned char) *p) return false; } return true; } /* ** Converts a token to a textual representation for error messages ** and the like. */ char * TokenToText(const TOKEN token) { static const char hex[] = "0123456789ABCDEF"; static char result[(sizeof(TOKEN) * 2) + 3]; const char *p; char *q; size_t i; result[0] = '@'; for (q = result + 1, p = (const char *) &token, i = 0; i < sizeof(TOKEN); i++, p++) { *q++ = hex[(*p & 0xF0) >> 4]; *q++ = hex[*p & 0x0F]; } *q++ = '@'; *q++ = '\0'; return result; } /* ** Converts a hex digit to an int. ** Uppercase the character to always obtain the right answer, though a lowercase ** character should not be present in a token -- and is refused by IsToken(). */ static int hextodec(const int c) { return isdigit((unsigned char) c) ? (c - '0') : ((toupper((unsigned char) c) - 'A') + 10); } /* ** Converts a textual representation of a token back to a native ** representation. */ TOKEN TextToToken(const char *text) { const char *p; char *q; int i; TOKEN token; /* Return an empty token (with only '0' chars) if the text is * not a valid token. */ if (!IsToken(text)) { memset(&token, 0, sizeof(TOKEN)); } else { /* First char is a '@'. */ p = &text[1]; for (q = (char *)&token, i = 0; i != sizeof(TOKEN); i++) { q[i] = (hextodec(*p) << 4) + hextodec(*(p + 1)); p += 2; } } return token; } /* ** get Xref header without pathhost */ static char * GetXref(ARTHANDLE *art) { const char *p, *p1; const char *q; char *buff; bool Nocr = false; p = wire_findheader(art->data, art->len, "xref", true); if (p == NULL) return NULL; q = p; for (p1 = NULL; p < art->data + art->len; p++) { if (p1 != (char *)NULL && *p1 == '\r' && *p == '\n') { Nocr = false; break; } if (*p == '\n') { Nocr = true; break; } p1 = p; } if (p >= art->data + art->len) return NULL; if (!Nocr) p = p1; /* skip pathhost */ for (; (*q == ' ') && (q < p); q++); if (q == p) return NULL; if ((q = memchr(q, ' ', p - q)) == NULL) return NULL; for (q++; (*q == ' ') && (q < p); q++); if (q == p) return NULL; buff = xmalloc(p - q + 1); memcpy(buff, q, p - q); buff[p - q] = '\0'; return buff; } /* ** Split newsgroup and returns artnum ** or 0 if there are no newsgroup. */ static ARTNUM GetGroups(char *Xref) { char *p; if ((p = strchr(Xref, ':')) == NULL) return 0; *p++ = '\0'; return ((ARTNUM)atoi(p)); } STORAGE_SUB *SMGetConfig(STORAGETYPE type, STORAGE_SUB *sub) { if (sub == (STORAGE_SUB *)NULL) sub = subscriptions; else sub = sub->next; for (;sub != NULL; sub = sub->next) { if (sub->type == type) { return sub; } } return (STORAGE_SUB *)NULL; } static time_t ParseTime(char *tmbuf) { char *startnum; time_t ret; int tmp; ret = 0; startnum = tmbuf; while (*tmbuf) { if (!isdigit((unsigned char) *tmbuf)) { tmp = atol(startnum); switch (*tmbuf) { case 'M': ret += tmp*60*60*24*31; break; case 'd': ret += tmp*60*60*24; break; case 'h': ret += tmp*60*60; break; case 'm': ret += tmp*60; break; case 's': ret += tmp; break; default: return(0); } startnum = tmbuf+1; } tmbuf++; } return(ret); } #define SMlbrace 1 #define SMrbrace 2 #define SMmethod 10 #define SMgroups 11 #define SMsize 12 #define SMclass 13 #define SMexpire 14 #define SMoptions 15 #define SMexactmatch 16 static CONFTOKEN smtoks[] = { { SMlbrace, (char *) "{" }, { SMrbrace, (char *) "}" }, { SMmethod, (char *) "method" }, { SMgroups, (char *) "newsgroups:" }, { SMsize, (char *) "size:" }, { SMclass, (char *) "class:" }, { SMexpire, (char *) "expires:" }, { SMoptions, (char *) "options:" }, { SMexactmatch, (char *) "exactmatch:" }, { 0, NULL } }; /* Open the config file and parse it, generating the policy data */ static bool SMreadconfig(void) { CONFFILE *f; CONFTOKEN *tok; int type; int i; char *p; char *q; char *path; char *method = NULL; char *pattern = NULL; size_t minsize = 0; size_t maxsize = 0; time_t minexpire = 0; time_t maxexpire = 0; int class = 0; STORAGE_SUB *sub = NULL; STORAGE_SUB *prev = NULL; char *options = 0; int inbrace; bool exactmatch = false; /* if innconf isn't already read in, do so. */ if (innconf == NULL) { if (!innconf_read(NULL)) { SMseterror(SMERR_INTERNAL, "ReadInnConf() failed"); return false; } } for (i = 0; i < NUM_STORAGE_METHODS; i++) { method_data[i].initialized = INIT_NO; method_data[i].configured = false; } path = concatpath(innconf->pathetc, INN_PATH_STORAGECTL); f = CONFfopen(path); if (f == NULL) { SMseterror(SMERR_UNDEFINED, NULL); syswarn("SM: cant open %s", path); free(path); return false; } free(path); inbrace = 0; while ((tok = CONFgettoken(smtoks, f)) != NULL) { if (!inbrace) { if (tok->type != SMmethod) { SMseterror(SMERR_CONFIG, "Expected 'method' keyword"); warn("SM: expected 'method' keyword, line %d", f->lineno); return false; } if ((tok = CONFgettoken(0, f)) == NULL) { SMseterror(SMERR_CONFIG, "Expected method name"); warn("SM: expected method name, line %d", f->lineno); return false; } method = xstrdup(tok->name); if ((tok = CONFgettoken(smtoks, f)) == NULL || tok->type != SMlbrace) { SMseterror(SMERR_CONFIG, "Expected '{'"); warn("SM: Expected '{', line %d", f->lineno); return false; } inbrace = 1; /* initialize various params to defaults. */ minsize = 0; maxsize = 0; /* zero means no limit */ class = 0; pattern = NULL; options = NULL; minexpire = 0; maxexpire = 0; exactmatch = false; } else { type = tok->type; if (type == SMrbrace) inbrace = 0; else { if ((tok = CONFgettoken(0, f)) == NULL) { SMseterror(SMERR_CONFIG, "Keyword with no value"); warn("SM: keyword with no value, line %d", f->lineno); return false; } p = tok->name; switch(type) { case SMgroups: if (pattern) free(pattern); pattern = xstrdup(tok->name); break; case SMsize: minsize = strtoul(p, NULL, 10); if ((p = strchr(p, ',')) != NULL) { p++; maxsize = strtoul(p, NULL, 10); } break; case SMclass: class = atoi(p); if (class > NUM_STORAGE_CLASSES) { SMseterror(SMERR_CONFIG, "Storage class too large"); warn("SM: storage class larger than %d, line %d", NUM_STORAGE_CLASSES, f->lineno); return false; } break; case SMexpire: q = strchr(p, ','); if (q) *q++ = 0; minexpire = ParseTime(p); if (q) maxexpire = ParseTime(q); break; case SMoptions: if (options) free(options); options = xstrdup(p); break; case SMexactmatch: if (strcasecmp(p, "true") == 0 || strcasecmp(p, "yes") == 0 || strcasecmp(p, "on") == 0) exactmatch = true; break; default: SMseterror(SMERR_CONFIG, "Unknown keyword in method declaration"); warn("SM: Unknown keyword in method declaration, line %d:" " %s", f->lineno, tok->name); free(method); return false; break; } } } if (!inbrace) { /* just finished a declaration */ sub = xmalloc(sizeof(STORAGE_SUB)); sub->type = TOKEN_EMPTY; for (i = 0; i < NUM_STORAGE_METHODS; i++) { if (!strcasecmp(method, storage_methods[i].name)) { sub->type = storage_methods[i].type; method_data[i].configured = true; break; } } if (sub->type == TOKEN_EMPTY) { SMseterror(SMERR_CONFIG, "Invalid storage method name"); warn("SM: no configured storage methods are named '%s'", method); free(options); free(sub); return false; } if (!pattern) { SMseterror(SMERR_CONFIG, "pattern not defined"); warn("SM: no pattern defined"); free(options); free(sub); return false; } sub->pattern = pattern; sub->minsize = minsize; sub->maxsize = maxsize; sub->class = class; sub->options = options; sub->minexpire = minexpire; sub->maxexpire = maxexpire; sub->exactmatch = exactmatch; free(method); method = 0; if (!prev) subscriptions = sub; if (prev) prev->next = sub; prev = sub; sub->next = NULL; } } CONFfclose(f); return true; } /* ** setup storage api environment (open mode etc.) */ bool SMsetup(SMSETUP type, void *value) { if (Initialized) return false; switch (type) { case SM_RDWR: SMopenmode = *(bool *)value; break; case SM_PREOPEN: SMpreopen = *(bool *)value; break; default: return false; } return true; } /* ** Calls the setup function for all of the configured methods and returns ** true if they all initialize ok, false if they don't */ bool SMinit(void) { int i; bool allok = true; static bool once = false; SMATTRIBUTE smattr; if (Initialized) return true; Initialized = true; if (!SMreadconfig()) { SMshutdown(); Initialized = false; return false; } for (i = 0; i < NUM_STORAGE_METHODS; i++) { if (method_data[i].configured) { if (method_data[i].configured && storage_methods[i].init(&smattr)) { method_data[i].initialized = INIT_DONE; method_data[i].selfexpire = smattr.selfexpire; method_data[i].expensivestat = smattr.expensivestat; } else { method_data[i].initialized = INIT_FAIL; method_data[i].selfexpire = false; method_data[i].expensivestat = true; warn("SM: storage method '%s' failed initialization", storage_methods[i].name); allok = false; } } typetoindex[storage_methods[i].type] = i; } if (!allok) { SMshutdown(); Initialized = false; SMseterror(SMERR_UNDEFINED, "one or more storage methods failed initialization"); warn("SM: one or more storage methods failed initialization"); return false; } if (!once && atexit(SMshutdown) < 0) { SMshutdown(); Initialized = false; SMseterror(SMERR_UNDEFINED, NULL); return false; } once = true; return true; } static bool InitMethod(STORAGETYPE method) { SMATTRIBUTE smattr; if (!Initialized) if (!SMreadconfig()) { Initialized = false; return false; } Initialized = true; if (method_data[method].initialized == INIT_DONE) return true; if (method_data[method].initialized == INIT_FAIL) return false; if (!method_data[method].configured) { method_data[method].initialized = INIT_FAIL; SMseterror(SMERR_UNDEFINED, "storage method is not configured"); return false; } if (!storage_methods[method].init(&smattr)) { method_data[method].initialized = INIT_FAIL; method_data[method].selfexpire = false; method_data[method].expensivestat = true; SMseterror(SMERR_UNDEFINED, "Could not initialize storage method late"); return false; } method_data[method].initialized = INIT_DONE; method_data[method].selfexpire = smattr.selfexpire; method_data[method].expensivestat = smattr.expensivestat; return true; } static bool MatchGroups(const char *g, int len, const char *pattern, bool exactmatch) { char *group, *groups, *q; int i, lastwhite; enum uwildmat matched; bool wanted = false; q = groups = xmalloc(len + 1); for (lastwhite = -1, i = 0 ; i < len ; i++) { /* trim white chars */ if (g[i] == '\r' || g[i] == '\n' || g[i] == ' ' || g[i] == '\t') { if (lastwhite + 1 != i) *q++ = ' '; lastwhite = i; } else *q++ = g[i]; } *q = '\0'; group = strtok(groups, " ,"); while (group != NULL) { q = strchr(group, ':'); if (q != NULL) *q = '\0'; matched = uwildmat_poison(group, pattern); if (matched == UWILDMAT_POISON || (exactmatch && !matched)) { free(groups); return false; } if (matched == UWILDMAT_MATCH) wanted = true; group = strtok(NULL, " ,"); } free(groups); return wanted; } STORAGE_SUB *SMgetsub(const ARTHANDLE article) { STORAGE_SUB *sub; if (article.len == 0) { SMseterror(SMERR_BADHANDLE, NULL); return NULL; } if (article.groups == NULL) return NULL; for (sub = subscriptions; sub != NULL; sub = sub->next) { if (!(method_data[typetoindex[sub->type]].initialized == INIT_FAIL) && (article.len >= sub->minsize) && (!sub->maxsize || (article.len <= sub->maxsize)) && (!sub->minexpire || article.expires >= sub->minexpire) && (!sub->maxexpire || (article.expires <= sub->maxexpire)) && MatchGroups(article.groups, article.groupslen, sub->pattern, sub->exactmatch)) { if (InitMethod(typetoindex[sub->type])) return sub; } } errno = 0; SMseterror(SMERR_NOMATCH, "no matching entry in storage.conf"); return NULL; } TOKEN SMstore(const ARTHANDLE article) { STORAGE_SUB *sub; TOKEN result; if (!SMopenmode) { memset(&result, 0, sizeof(result)); result.type = TOKEN_EMPTY; SMseterror(SMERR_INTERNAL, "read only storage api"); return result; } result.type = TOKEN_EMPTY; if ((sub = SMgetsub(article)) == NULL) { return result; } return storage_methods[typetoindex[sub->type]].store(article, sub->class); } ARTHANDLE *SMretrieve(const TOKEN token, const RETRTYPE amount) { ARTHANDLE *art; if (method_data[typetoindex[token.type]].initialized == INIT_FAIL) { SMseterror(SMERR_UNINIT, NULL); return NULL; } if (method_data[typetoindex[token.type]].initialized == INIT_NO && !InitMethod(typetoindex[token.type])) { warn("SM: could not find token type or method was not initialized" " (%d)", token.type); SMseterror(SMERR_UNINIT, NULL); return NULL; } art = storage_methods[typetoindex[token.type]].retrieve(token, amount); if (art) art->nextmethod = 0; return art; } ARTHANDLE * SMnext(ARTHANDLE *article, const RETRTYPE amount) { unsigned char i; int start; ARTHANDLE *newart; if (article == NULL) start = 0; else start= article->nextmethod; if (method_data[start].initialized == INIT_FAIL) { SMseterror(SMERR_UNINIT, NULL); return NULL; } if (method_data[start].initialized == INIT_NO && method_data[start].configured && !InitMethod(start)) { SMseterror(SMERR_UNINIT, NULL); return NULL; } for (i = start, newart = NULL; i < NUM_STORAGE_METHODS; i++) { if (method_data[i].configured && (newart = storage_methods[i].next(article, amount)) != (ARTHANDLE *)NULL) { newart->nextmethod = i; break; } else article = NULL; } return newart; } void SMfreearticle(ARTHANDLE *article) { if (method_data[typetoindex[article->type]].initialized == INIT_FAIL) { return; } if (method_data[typetoindex[article->type]].initialized == INIT_NO && !InitMethod(typetoindex[article->type])) { warn("SM: can't free article with uninitialized method"); return; } storage_methods[typetoindex[article->type]].freearticle(article); } bool SMcancel(TOKEN token) { if (!SMopenmode) { SMseterror(SMERR_INTERNAL, "read only storage api"); return false; } if (method_data[typetoindex[token.type]].initialized == INIT_FAIL) { SMseterror(SMERR_UNINIT, NULL); return false; } if (method_data[typetoindex[token.type]].initialized == INIT_NO && !InitMethod(typetoindex[token.type])) { SMseterror(SMERR_UNINIT, NULL); warn("SM: can't cancel article with uninitialized method"); return false; } return storage_methods[typetoindex[token.type]].cancel(token); } bool SMprobe(PROBETYPE type, TOKEN *token, void *value) { struct artngnum *ann; ARTHANDLE *art; switch (type) { case SELFEXPIRE: return (method_data[typetoindex[token->type]].selfexpire); case SMARTNGNUM: if (method_data[typetoindex[token->type]].initialized == INIT_FAIL) { SMseterror(SMERR_UNINIT, NULL); return false; } if (method_data[typetoindex[token->type]].initialized == INIT_NO && !InitMethod(typetoindex[token->type])) { SMseterror(SMERR_UNINIT, NULL); warn("SM: can't probe article with uninitialized method"); return false; } if ((ann = (struct artngnum *)value) == NULL) return false; ann->groupname = NULL; if (storage_methods[typetoindex[token->type]].ctl(type, token, value)) { if (ann->artnum != 0) { /* set by storage method */ return true; } else { art = storage_methods[typetoindex[token->type]].retrieve(*token, RETR_HEAD); if (art == NULL) { if (ann->groupname != NULL) free(ann->groupname); storage_methods[typetoindex[token->type]].freearticle(art); return false; } if ((ann->groupname = GetXref(art)) == NULL) { if (ann->groupname != NULL) free(ann->groupname); storage_methods[typetoindex[token->type]].freearticle(art); return false; } storage_methods[typetoindex[token->type]].freearticle(art); if ((ann->artnum = GetGroups(ann->groupname)) == 0) { if (ann->groupname != NULL) free(ann->groupname); return false; } return true; } } else { return false; } case EXPENSIVESTAT: return (method_data[typetoindex[token->type]].expensivestat); default: return false; } } bool SMflushcacheddata(FLUSHTYPE type) { int i; for (i = 0; i < NUM_STORAGE_METHODS; i++) { if (method_data[i].initialized == INIT_DONE && !storage_methods[i].flushcacheddata(type)) warn("SM: can't flush cached data method '%s'", storage_methods[i].name); } return true; } void SMprintfiles(FILE *file, TOKEN token, char **xref, int ngroups) { if (method_data[typetoindex[token.type]].initialized == INIT_FAIL) return; if (method_data[typetoindex[token.type]].initialized == INIT_NO && !InitMethod(typetoindex[token.type])) { SMseterror(SMERR_UNINIT, NULL); warn("SM: can't print files for article with uninitialized method"); return; } storage_methods[typetoindex[token.type]].printfiles(file, token, xref, ngroups); } /* ** Print a clear, decoded information on a token. */ char * SMexplaintoken(const TOKEN token) { return storage_methods[typetoindex[token.type]].explaintoken(token); } void SMshutdown(void) { int i; STORAGE_SUB *old; if (!Initialized) return; for (i = 0; i < NUM_STORAGE_METHODS; i++) if (method_data[i].initialized == INIT_DONE) { storage_methods[i].shutdown(); method_data[i].initialized = INIT_NO; method_data[i].configured = false; } while (subscriptions) { old = subscriptions; subscriptions = subscriptions->next; free(old->pattern); free(old->options); free(old); } Initialized = false; } void SMseterror(int errornum, const char *error) { if (SMerrorstr != NULL) free(SMerrorstr); if (errornum == SMERR_UNDEFINED && errno == ENOENT) errornum = SMERR_NOENT; SMerrno = errornum; if (error == NULL) { switch (SMerrno) { case SMERR_UNDEFINED: error = strerror(errno); break; case SMERR_INTERNAL: error = "Internal error"; break; case SMERR_NOENT: error = "Token not found"; break; case SMERR_TOKENSHORT: error = "Configured token size too small"; break; case SMERR_NOBODY: error = "No article body found"; break; case SMERR_UNINIT: error = "Storage manager is not initialized"; break; case SMERR_CONFIG: error = "Error reading config file"; break; case SMERR_BADHANDLE: error = "Bad article handle"; break; case SMERR_BADTOKEN: error = "Bad token"; break; case SMERR_NOMATCH: error = "No matching entry in storage.conf"; break; default: error = "Undefined error"; } } SMerrorstr = xstrdup(error); } inn-2.6.0/storage/ovmethods.h0000644000175200017520000000034112575023702015543 0ustar iuliusiulius/* This file is automatically generated by buildconfig */ #ifndef OVMETHODS_H #define OVMETHODS_H 1 #include "ovinterface.h" #define NUM_OV_METHODS 3 extern OV_METHOD ov_methods[NUM_OV_METHODS]; #endif /* OVMETHODS_H */ inn-2.6.0/storage/Make.methods0000644000175200017520000000262112575023702015627 0ustar iuliusiulius# This file is automatically generated by buildconfig METHOD_SOURCES = buffindexed/buffindexed.c buffindexed/shmem.c cnfs/cnfs.c \ ovdb/ovdb.c timecaf/caf.c timecaf/timecaf.c \ timehash/timehash.c tradindexed/tdx-cache.c \ tradindexed/tdx-data.c tradindexed/tdx-group.c \ tradindexed/tradindexed.c tradspool/tradspool.c \ trash/trash.c EXTRA_SOURCES = tradindexed/tdx-util.c PROGRAMS = buffindexed/buffindexed_d tradindexed/tdx-util ## Included from buffindexed/ovmethod.mk # This rule requires a compiler that supports -o with -c. Since it's normally # used by developers, that should be acceptable. buffindexed/buffindexed_d.$(EXTOBJ): buffindexed/buffindexed.c $(LIBCC) $(CFLAGS) -DBUFF_DEBUG -c -o $@ buffindexed/buffindexed.c buffindexed/buffindexed_d: buffindexed/buffindexed_d.$(EXTOBJ) libstorage.$(EXTLIB) $(LIBHIST) $(LIBLD) $(LDFLAGS) -o $@ buffindexed/buffindexed_d.$(EXTOBJ) \ $(LIBSTORAGE) $(LIBHIST) $(LIBINN) $(STORAGE_LIBS) $(LIBS) ## Included from tradindexed/ovmethod.mk tradindexed/tdx-util.$(EXTOBJ): tradindexed/tdx-util.c $(LIBCC) $(CFLAGS) -c -o $@ tradindexed/tdx-util.c tradindexed/tdx-util: tradindexed/tdx-util.$(EXTOBJ) libstorage.$(EXTLIB) $(LIBHIST) $(LIBLD) $(LDFLAGS) -o $@ tradindexed/tdx-util.$(EXTOBJ) \ $(LIBSTORAGE) $(LIBHIST) $(LIBINN) $(STORAGE_LIBS) $(LIBS) inn-2.6.0/storage/overview.c0000644000175200017520000002664512575023702015413 0ustar iuliusiulius/* $Id: overview.c 9658 2014-08-28 18:59:18Z iulius $ ** ** The implementation of the overview API. ** ** This code handles calls to the overview API by passing them along to the ** appropriate underlying overview method, as well as implementing those ** portions of the overview subsystem that are independent of storage ** method. ** ** This is currently just a wrapper around the old API still used internally ** by the overview methods. Eventually, the changes in API will be pushed ** down into the overview method implementations. */ #include "config.h" #include "clibrary.h" #include #include #include "inn/buffer.h" #include "inn/innconf.h" #include "inn/messages.h" #include "inn/overview.h" #include "inn/wire.h" #include "inn/vector.h" #include "inn/libinn.h" #include "inn/ov.h" #include "ovinterface.h" #include "ovmethods.h" /* This struct is opaque to callers and is returned by overview_open. It encapsulates any internal state used by the overview subsystem. */ struct overview { int mode; bool cutoff; struct buffer *overdata; struct cvector *groups; struct overview_method *method; void *private; }; /* ** Open overview. Figure out which overview method we're using and ** initialize it. Allocate a new overview struct, flesh it out, and return ** it to the caller. Takes some combination of OV_READ and OV_WRITE. */ struct overview * overview_open(int mode) { int i; struct overview *overview; bool status; /* Basic sanity checks. */ if (innconf == NULL) if (!innconf_read(NULL)) return NULL; if (!innconf->enableoverview) { warn("enableoverview is not true"); return NULL; } if (innconf->ovmethod == NULL) { warn("ovmethod is not defined"); return NULL; } assert((mode & (OV_READ | OV_WRITE)) == mode); /* Locate the overview method we're using. */ for (i = 0; i < NUM_OV_METHODS; i++) if (strcmp(innconf->ovmethod, ov_methods[i].name) == 0) break; if (i == NUM_OV_METHODS) { warn("%s is not a known overview method", innconf->ovmethod); return NULL; } /* We have enough information to initialize the method. */ status = ov_methods[i].open(mode); if (!status) return NULL; overview = xmalloc(sizeof(struct overview)); overview->mode = mode; overview->cutoff = false; overview->overdata = NULL; overview->groups = NULL; overview->method = &ov_methods[i]; overview->private = NULL; return overview; } /* ** Close overview. Takes the overview struct corresponding to the method to ** close and frees the resources allocated to it. */ void overview_close(struct overview *overview) { if (overview == NULL) return; overview->method->close(); free(overview); } /* ** Retrieve information about a group and put it into the supplied struct. ** Returns false if the group wasn't found or some other error occurred. */ bool overview_group(struct overview *overview, const char *group, struct overview_group *stats) { int lo, hi, count, flag; bool status; status = overview->method->groupstats(group, &lo, &hi, &count, &flag); if (!status) return false; stats->high = hi; stats->low = lo; stats->count = count; stats->flag = flag; return true; } /* ** Add a new newsgroup to the overview database. Takes the high and low ** marks and the flag from the provided struct (count is ignored). Returns ** false on error. */ bool overview_group_add(struct overview *overview, const char *group, struct overview_group *stats) { return overview->method->groupadd(group, stats->low, stats->high, &stats->flag); } /* ** Remove a group from the overview database. The overview data for that ** group will generally be purged immediately. Returns false on error. */ bool overview_group_delete(struct overview *overview, const char *group) { return overview->method->groupdel(group); } /* ** Add overview data for a particular article in a particular group. Returns ** false on error. */ bool overview_add(struct overview *overview, const char *group, struct overview_data *data) { /* We have to add the article number to the beginning of the overview data and CRLF to the end. Use the overdata buffer as temporary storage space. */ if (overview->overdata == NULL) { overview->overdata = buffer_new(); buffer_resize(overview->overdata, data->overlen + 13); } buffer_sprintf(overview->overdata, "%ld\t", data->number); buffer_append(overview->overdata, data->overview, data->overlen); buffer_append(overview->overdata, "\r\n", 2); /* Call the underlying method. */ return overview->method->add(group, data->number, data->token, overview->overdata->data, overview->overdata->left, data->arrived, data->expires); } /* ** Add overview data for an article using the provided Xref information (sans ** the leading hostname) to determine which groups and article numbers. The ** data will be added to each. Return true only if the overview was ** successfully stored in every group. Don't make a big fuss over invalid ** Xref entries; just silently skip over them. ** ** I hate having to support this API, but both makehistory and overchan need ** it, so there's no point in making both of them implement it separately. */ bool overview_add_xref(struct overview *overview, const char *xref, struct overview_data *data) { char *xref_copy; const char *group; char *p, *end; size_t i; bool success = true; xref_copy = xstrdup(xref); p = strchr(xref_copy, '\n'); if (p != NULL) *p = '\0'; overview->groups = cvector_split_space(xref_copy, overview->groups); for (i = 0; i < overview->groups->count; i++) { group = overview->groups->strings[i]; p = (char *) strchr(group, ':'); if (p == NULL || p == group || p[1] == '-') continue; *p = '\0'; errno = 0; data->number = strtoul(p + 1, &end, 10); if (data->number == 0 || *end != '\0' || errno == ERANGE) continue; success = success && overview_add(overview, group, data); } return success; } /* ** Cancel a message from a particular group. */ bool overview_cancel(struct overview *overview, const char *group, ARTNUM artnum) { return overview->method->cancel(group, artnum); } /* ** Cancel a message from all groups based on Xref information. This is ** hideously ugly, since there's no easy way to go from a token to a ** newsgroup name and article number. We retrieve the head of the article, ** find the Xref header, and then parse it. Articles without an Xref header ** lose. */ bool overview_cancel_xref(struct overview *overview, TOKEN token) { ARTHANDLE *art; const char *xref, *xrefend, *group; size_t xreflen, i; char *xref_copy, *p, *end; ARTNUM artnum; art = SMretrieve(token, RETR_HEAD); if (art == NULL) return false; xref = wire_findheader(art->data, art->len, "Xref", true); if (xref == NULL) goto fail; xrefend = wire_endheader(xref, art->data + art->len - 1); if (xrefend == NULL) goto fail; xreflen = xrefend - xref + 1; xref_copy = xstrndup(xref, xreflen); SMfreearticle(art); overview->groups = cvector_split_space(xref_copy, overview->groups); for (i = 0; i < overview->groups->count; i++) { group = overview->groups->strings[i]; p = (char *) strchr(group, ':'); if (p == NULL || p == group || p[1] == '-') continue; *p = '\0'; errno = 0; artnum = strtoul(p + 1, &end, 10); if (artnum == 0 || *end != '\0' || errno == ERANGE) continue; /* Don't worry about the return status; the article may have already expired out of some or all of the groups. */ overview_cancel(overview, group, artnum); } free(xref_copy); return true; fail: SMfreearticle(art); return false; } /* ** Open a new overview search, which is used to retrieve overview records ** between the given low and high article numbers. Returns an opaque handle ** or NULL if the search fails. */ void * overview_search_open(struct overview *overview, const char *group, ARTNUM low, ARTNUM high) { return overview->method->opensearch(group, low, high); } /* ** Retrieve the next overview record in the given overview search. Returns ** false if no more articles are found. */ bool overview_search(struct overview *overview, void *handle, struct overview_data *data) { ARTNUM number; char *overdata; int length; TOKEN token; time_t arrived; bool status; status = overview->method->search(handle, &number, &overdata, &length, &token, &arrived); if (!status) return false; data->number = number; data->overview = overdata; data->overlen = length; data->token = token; data->arrived = arrived; data->expires = 0; return true; } /* ** Close an open search. */ void overview_search_close(struct overview *overview, void *handle) { overview->method->closesearch(handle); } /* ** Given the group and article number, retrieve the token for the article ** from overview. Returns false if the article isn't found. */ bool overview_token(struct overview *overview, const char *group, ARTNUM number, TOKEN *token) { return overview->method->getartinfo(group, number, token); } /* ** Expire a single group and flesh out the statistics portion of the provided ** expiration configuration struct. Returns false if group expiration fails ** for some reason. */ bool overview_expire(struct overview *overview, const char *group, ARTNUM *low, struct overview_expire *data) { int newlow; bool status; EXPprocessed = 0; EXPunlinked = 0; EXPoverindexdrop = 0; status = overview->method->expiregroup(group, &newlow, data->history); data->processed += EXPprocessed; data->dropped += EXPunlinked; data->indexdropped += EXPoverindexdrop; if (status) *low = newlow; return status; } /* ** Get the current overview configuration, filling out the provided struct. */ void overview_config_get(struct overview *overview, struct overview_config *config) { int i; OVSORTTYPE sort; config->mode = overview->mode; overview->method->ctl(OVSORT, &sort); config->sorted = (sort == OVNEWSGROUP); overview->method->ctl(OVSTATICSEARCH, &i); config->persistant = i; config->cutoff = overview->cutoff; } /* ** Set the current overview configuration (which right now means only the ** cutoff setting). Right now, this can never fail; the return status is for ** future work. */ bool overview_config_set(struct overview *overview, struct overview_config *config) { overview->cutoff = config->cutoff; return overview->method->ctl(OVCUTOFFLOW, &overview->cutoff); } /* ** Return the free space of the overview method as a percentage, or -1 if ** that concept isn't meaningful for the overview method. */ float overview_free_space(struct overview *overview) { int space; if (overview->method->ctl(OVSPACE, &space)) return space; else return -1; } inn-2.6.0/storage/cnfs/0000755000175200017520000000000012575023701014314 5ustar iuliusiuliusinn-2.6.0/storage/cnfs/cnfs-private.h0000644000175200017520000000774212575023702017101 0ustar iuliusiulius/* $Id: cnfs-private.h 8599 2009-08-29 08:34:46Z iulius $ ** ** CNFS disk/file mode header file. */ #ifndef CNFS_PRIVATE_H #define CNFS_PRIVATE_H 1 #include #include #define _PATH_CYCBUFFCONFIG "cycbuff.conf" /* Page boundary on which to mmap() the CNFS article usage header. Should be a multiple of the pagesize for all the architectures you expect might need access to your CNFS buffer. If you don't expect to share your buffer across several platforms, you can use 'pagesize' here. */ #define CNFS_HDR_PAGESIZE 16384 #define CNFS_MAGICV1 "Cycbuff" /* CNFSMASIZ bytes */ #define CNFS_MAGICV2 "CBuf1" /* CNFSMASIZ bytes */ #define CNFS_MAGICV3 "CBuf3" /* CNFSMASIZ bytes */ #define CNFS_MAGICV4 "CBuf4" /* CNFSMASIZ bytes */ #define CNFS_DFL_BLOCKSIZE 4096 /* Unit block size we'll work with */ #define CNFS_MAX_BLOCKSIZE 16384 /* Max unit block size */ /* Amount of data stored at beginning of CYCBUFF before the bitfield */ #define CNFS_BEFOREBITF 512 /* Rounded up to CNFS_HDR_PAGESIZE */ struct metacycbuff; /* Definition comes below */ #define CNFSMAXCYCBUFFNAME 8 #define CNFSMASIZ 8 #define CNFSNASIZ 16 /* Effective size is 9, not 16 */ #define CNFSPASIZ 64 #define CNFSLASIZ 16 /* Match length of ASCII hex off_t representation */ typedef struct _CYCBUFF { char name[CNFSNASIZ];/* Symbolic name */ char path[CNFSPASIZ];/* Path to file */ off_t len; /* Length of writable area, in bytes */ off_t free; /* Offset (relative to byte 0 of file) to first freely available byte */ time_t updated; /* Time of last update to header */ int fd; /* file descriptor for this cycbuff */ uint32_t cyclenum; /* Number of current cycle, 0 = invalid */ int magicver; /* Magic version number */ void * bitfield; /* Bitfield for article in use */ off_t minartoffset; /* The minimum offset allowed for article storage */ bool needflush; /* true if CYCBUFFEXTERN is needed to be flushed */ int blksz; /* Blocksize */ struct _CYCBUFF *next; bool currentbuff; /* true if this cycbuff is currently used */ char metaname[CNFSNASIZ];/* Symbolic name of meta */ int order; /* Order in meta, start from 1 not 0 */ } CYCBUFF; /* ** A structure suitable for thwapping onto disk in a quasi-portable way. ** We assume that sizeof(CYCBUFFEXTERN) < CNFS_BLOCKSIZE. */ typedef struct { char magic[CNFSMASIZ]; char name[CNFSNASIZ]; char path[CNFSPASIZ]; char lena[CNFSLASIZ]; /* ASCII version of len */ char freea[CNFSLASIZ]; /* ASCII version of free */ char updateda[CNFSLASIZ]; /* ASCII version of updated */ char cyclenuma[CNFSLASIZ]; /* ASCII version of cyclenum */ char metaname[CNFSNASIZ]; char orderinmeta[CNFSLASIZ]; char currentbuff[CNFSMASIZ]; char blksza[CNFSLASIZ]; /* ASCII version of blksz */ } CYCBUFFEXTERN; #define METACYCBUFF_UPDATE 25 #define REFRESH_INTERVAL 30 typedef enum {INTERLEAVE, SEQUENTIAL} METAMODE; typedef struct metacycbuff { char *name; /* Symbolic name of the pool */ int count; /* Number of files/devs in this pool */ CYCBUFF **members; /* Member cycbuffs */ int memb_next; /* Index to next member to write onto */ unsigned long write_count; /* Number of writes since last header flush */ struct metacycbuff *next; METAMODE metamode; } METACYCBUFF; typedef struct _CNFSEXPIRERULES { STORAGECLASS class; METACYCBUFF *dest; struct _CNFSEXPIRERULES *next; } CNFSEXPIRERULES; typedef struct { long size; /* Size of the article */ time_t arrived; /* This is the time when article arrived */ STORAGECLASS class; /* storage class */ } CNFSARTHEADER; /* uncomment below for old cnfs spool */ /* #ifdef OLD_CNFS */ typedef struct { long zottf; /* This should always be 0x01234*/ long size; /* Size of the article */ char m_id[64]; /* We'll only store up to 63 bytes of the Message-ID, that should be good enough */ } oldCNFSARTHEADER; #endif /* !CNFS_PRIVATE_H */ inn-2.6.0/storage/cnfs/cnfs.h0000644000175200017520000000133212575023702015416 0ustar iuliusiulius/* $Id: cnfs.h 8817 2009-11-17 18:57:19Z iulius $ ** ** Storage manager module header for Cyclic News File System method. */ #ifndef __CNFS_H__ #define __CNFS_H__ bool cnfs_init(SMATTRIBUTE *attr); TOKEN cnfs_store(const ARTHANDLE article, const STORAGECLASS class); ARTHANDLE *cnfs_retrieve(const TOKEN token, const RETRTYPE amount); ARTHANDLE *cnfs_next(ARTHANDLE *article, const RETRTYPE amount); void cnfs_freearticle(ARTHANDLE *article); bool cnfs_cancel(TOKEN token); bool cnfs_ctl(PROBETYPE type, TOKEN *token, void *value); bool cnfs_flushcacheddata(FLUSHTYPE type); void cnfs_printfiles(FILE *file, TOKEN token, char **xref, int ngroups); char *cnfs_explaintoken(const TOKEN token); void cnfs_shutdown(void); #endif inn-2.6.0/storage/cnfs/method.config0000644000175200017520000000005412575023702016763 0ustar iuliusiuliusname = cnfs number = 3 sources = cnfs.c inn-2.6.0/storage/cnfs/cnfs.c0000644000175200017520000016213712575023702015424 0ustar iuliusiulius/* $Id: cnfs.c 9873 2015-05-23 11:35:34Z iulius $ ** ** Storage manager module for Cyclic News File System method. */ #include "config.h" #include "clibrary.h" #include "portable/mmap.h" #include #include #include #if HAVE_LIMITS_H # include #endif #include #include #include #ifdef HAVE_SYS_TIME_H # include #endif #include #include "inn/fdflag.h" #include "inn/innconf.h" #include "inn/messages.h" #include "inn/mmap.h" #include "inn/wire.h" #include "interface.h" #include "inn/libinn.h" #include "methods.h" #include "inn/paths.h" #include "cnfs.h" #include "cnfs-private.h" /* Temporary until cnfs_mapcntl is handled like msync_page. Make MS_ASYNC disappear on platforms that don't have it. */ #ifndef MS_ASYNC # define MS_ASYNC 0 #endif /* We can give a more descriptive error below about not having largefile support if the platform has EOVERFLOW; on other platforms some other errno will be used and so we won't know when to give the descriptive error. Oh well. */ #ifndef EOVERFLOW # define EOVERFLOW 0 #endif typedef struct { /**** Stuff to be cleaned up when we're done with the article */ char *base; /* Base of mmap()ed art */ int len; /* Length of article (and thus mmap()ed art */ CYCBUFF *cycbuff; /* pointer to current CYCBUFF */ off_t offset; /* offset to current article */ bool rollover; /* true if the search is rollovered */ } PRIV_CNFS; static CYCBUFF *cycbufftab = (CYCBUFF *)NULL; static METACYCBUFF *metacycbufftab = (METACYCBUFF *)NULL; static CNFSEXPIRERULES *metaexprulestab = (CNFSEXPIRERULES *)NULL; static long pagesize = 0; static int metabuff_update = METACYCBUFF_UPDATE; static int refresh_interval = REFRESH_INTERVAL; static CYCBUFF *CNFSgetcycbuffbyname(char *name); /* ** The token is @03nnxxxxxxxxxxxxxxxxyyyyyyyyzzzzzzzz@ ** where "03" is the cnfs method number, ** "nn" the hexadecimal value of the storage class, ** "xxxxxxxxxxxxxxxx" the name of the cyclic buffer (as defined ** in /cycbuff.conf), ** "yyyyyyyy" the block, ** "zzzzzzzz" the cyclic number. */ char * cnfs_explaintoken(const TOKEN token) { char *text; CYCBUFF *cycbuff; char cycbuffname[CNFSMAXCYCBUFFNAME+1]; unsigned int blksz; uint32_t block; uint32_t cycnum; snprintf(cycbuffname, sizeof(cycbuffname), "%s", token.token); if ((cycbuff = CNFSgetcycbuffbyname(cycbuffname)) == NULL) { blksz = CNFS_DFL_BLOCKSIZE; } else { blksz = (unsigned int) cycbuff->blksz; } memcpy(&block, &token.token[8], sizeof(block)); memcpy(&cycnum, &token.token[12], sizeof(cycnum)); xasprintf(&text, "method=cnfs class=%u buffer=%s block=%lu blocksize=%u cycnum=%lu file=%s", (unsigned int) token.class, cycbuffname, (unsigned long) ntohl(block), blksz, (unsigned long) ntohl(cycnum), cycbuff ? cycbuff->path : ""); return text; } static TOKEN CNFSMakeToken(char *cycbuffname, off_t offset, int blksz, uint32_t cycnum, STORAGECLASS class) { TOKEN token; uint32_t uint32; token.type = TOKEN_CNFS; token.class = class; memcpy(token.token, cycbuffname, CNFSMAXCYCBUFFNAME); uint32 = htonl(offset / blksz); memcpy(&token.token[8], &uint32, sizeof(uint32)); uint32 = htonl(cycnum); memcpy(&token.token[12], &uint32, sizeof(uint32)); return token; } /* ** NOTE: We assume that cycbuffname is 9 bytes long. */ static bool CNFSBreakToken(TOKEN token, char *cycbuffname, uint32_t *blk, uint32_t *cycnum) { uint32_t uint32; if (cycbuffname == NULL || blk == NULL || cycnum == NULL) { if (cycbuffname == NULL) { warn("CNFS: BreakToken: invalid argument"); } else { warn("CNFS: BreakToken: invalid argument: %s", cycbuffname); } SMseterror(SMERR_INTERNAL, "BreakToken: invalid argument"); return false; } memcpy(cycbuffname, token.token, CNFSMAXCYCBUFFNAME); *(cycbuffname + CNFSMAXCYCBUFFNAME) = '\0'; /* Just to be paranoid */ memcpy(&uint32, &token.token[8], sizeof(uint32)); *blk = ntohl(uint32); memcpy(&uint32, &token.token[12], sizeof(uint32)); *cycnum = ntohl(uint32); return true; } static char hextbl[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; /* ** CNFSofft2hex -- Given an argument of type off_t, return ** a static ASCII string representing its value in hexadecimal. ** ** If "leadingzeros" is true, the number returned will have leading 0's. */ static char * CNFSofft2hex(off_t offset, bool leadingzeros) { static char buf[24]; char *p; if (sizeof(off_t) <= sizeof(unsigned long)) { snprintf(buf, sizeof(buf), (leadingzeros) ? "%016lx" : "%lx", (unsigned long) offset); } else { int i; for (i = 0; i < CNFSLASIZ; i++) buf[i] = '0'; /* Pad with zeros to start */ for (i = CNFSLASIZ - 1; i >= 0; i--) { buf[i] = hextbl[offset & 0xf]; offset >>= 4; } } if (! leadingzeros) { for (p = buf; *p == '0'; p++) ; if (*p != '\0') return p; else return p - 1; /* We converted a "0" and then bypassed all the zeros */ } else return buf; } /* ** CNFShex2offt -- Given an ASCII string containing a hexadecimal representation ** of a off_t, return a off_t. */ static off_t CNFShex2offt(char *hex) { if (sizeof(off_t) <= 4) { unsigned long rpofft; /* I'm lazy */ sscanf(hex, "%lx", &rpofft); return rpofft; } else { char diff; off_t n = 0; for (; *hex != '\0'; hex++) { if (*hex >= '0' && *hex <= '9') diff = '0'; else if (*hex >= 'a' && *hex <= 'f') diff = 'a' - 10; else if (*hex >= 'A' && *hex <= 'F') diff = 'A' - 10; else { /* ** We used to have a syslog() message here, but the case ** where we land here because of a ":" happens, er, often. */ break; } n += (*hex - diff); if (isalnum((unsigned char) *(hex + 1))) n <<= 4; } return n; } } static bool CNFSflushhead(CYCBUFF *cycbuff) { CYCBUFFEXTERN rpx; if (!cycbuff->needflush) return true; if (!SMopenmode) { warn("CNFS: CNFSflushhead: attempted flush whilst read only"); return false; } memset(&rpx, 0, sizeof(CYCBUFFEXTERN)); if (cycbuff->magicver == 3 || cycbuff->magicver == 4) { cycbuff->updated = time(NULL); if (cycbuff->magicver == 3) strncpy(rpx.magic, CNFS_MAGICV3, strlen(CNFS_MAGICV3)); else strncpy(rpx.magic, CNFS_MAGICV4, strlen(CNFS_MAGICV4)); strncpy(rpx.name, cycbuff->name, CNFSNASIZ); strncpy(rpx.path, cycbuff->path, CNFSPASIZ); /* Don't use sprintf() directly ... the terminating '\0' causes grief */ strncpy(rpx.lena, CNFSofft2hex(cycbuff->len, true), CNFSLASIZ); strncpy(rpx.freea, CNFSofft2hex(cycbuff->free, true), CNFSLASIZ); strncpy(rpx.cyclenuma, CNFSofft2hex(cycbuff->cyclenum, true), CNFSLASIZ); strncpy(rpx.updateda, CNFSofft2hex(cycbuff->updated, true), CNFSLASIZ); strncpy(rpx.metaname, cycbuff->metaname, CNFSNASIZ); strncpy(rpx.orderinmeta, CNFSofft2hex(cycbuff->order, true), CNFSLASIZ); if (cycbuff->currentbuff) { strncpy(rpx.currentbuff, "TRUE", CNFSMASIZ); } else { strncpy(rpx.currentbuff, "FALSE", CNFSMASIZ); } strncpy(rpx.blksza, CNFSofft2hex(cycbuff->blksz, true), CNFSLASIZ); memcpy(cycbuff->bitfield, &rpx, sizeof(CYCBUFFEXTERN)); msync(cycbuff->bitfield, cycbuff->minartoffset, MS_ASYNC); cycbuff->needflush = false; } else { warn("CNFS: CNFSflushhead: bogus magicver for %s: %d", cycbuff->name, cycbuff->magicver); return false; } return true; } static void CNFSshutdowncycbuff(CYCBUFF *cycbuff) { if (cycbuff == (CYCBUFF *)NULL) return; if (cycbuff->needflush) { notice("CNFS: CNFSshutdowncycbuff: flushing %s", cycbuff->name); CNFSflushhead(cycbuff); } if (cycbuff->bitfield != NULL) { munmap(cycbuff->bitfield, cycbuff->minartoffset); cycbuff->bitfield = NULL; } if (cycbuff->fd >= 0) close(cycbuff->fd); cycbuff->fd = -1; } static void CNFScleancycbuff(void) { CYCBUFF *cycbuff, *nextcycbuff; for (cycbuff = cycbufftab; cycbuff != (CYCBUFF *)NULL;) { CNFSshutdowncycbuff(cycbuff); nextcycbuff = cycbuff->next; free(cycbuff); cycbuff = nextcycbuff; } cycbufftab = (CYCBUFF *)NULL; } static void CNFScleanmetacycbuff(void) { METACYCBUFF *metacycbuff, *nextmetacycbuff; for (metacycbuff = metacycbufftab; metacycbuff != (METACYCBUFF *)NULL;) { nextmetacycbuff = metacycbuff->next; free(metacycbuff->members); free(metacycbuff->name); free(metacycbuff); metacycbuff = nextmetacycbuff; } metacycbufftab = (METACYCBUFF *)NULL; } static void CNFScleanexpirerule(void) { CNFSEXPIRERULES *metaexprule, *nextmetaexprule; for (metaexprule = metaexprulestab; metaexprule != (CNFSEXPIRERULES *)NULL;) { nextmetaexprule = metaexprule->next; free(metaexprule); metaexprule = nextmetaexprule; } metaexprulestab = (CNFSEXPIRERULES *)NULL; } static CYCBUFF *CNFSgetcycbuffbyname(char *name) { CYCBUFF *cycbuff; if (name == NULL) return NULL; for (cycbuff = cycbufftab; cycbuff != (CYCBUFF *)NULL; cycbuff = cycbuff->next) if (strcmp(name, cycbuff->name) == 0) return cycbuff; return NULL; } static METACYCBUFF *CNFSgetmetacycbuffbyname(char *name) { METACYCBUFF *metacycbuff; if (name == NULL) return NULL; for (metacycbuff = metacycbufftab; metacycbuff != (METACYCBUFF *)NULL; metacycbuff = metacycbuff->next) if (strcmp(name, metacycbuff->name) == 0) return metacycbuff; return NULL; } static void CNFSflushallheads(void) { CYCBUFF *cycbuff; for (cycbuff = cycbufftab; cycbuff != (CYCBUFF *)NULL; cycbuff = cycbuff->next) { if (cycbuff->needflush) notice("CNFS: CNFSflushallheads: flushing %s", cycbuff->name); CNFSflushhead(cycbuff); } } /* ** CNFSReadFreeAndCycle() -- Read from disk the current values of CYCBUFF's ** free pointer and cycle number. Return 1 on success, 0 otherwise. */ static void CNFSReadFreeAndCycle(CYCBUFF *cycbuff) { CYCBUFFEXTERN rpx; char buf[64]; memcpy(&rpx, cycbuff->bitfield, sizeof(CYCBUFFEXTERN)); /* Sanity checks are not needed since CNFSinit_disks() has already done. */ strncpy(buf, rpx.freea, CNFSLASIZ); buf[CNFSLASIZ] = '\0'; cycbuff->free = CNFShex2offt(buf); strncpy(buf, rpx.updateda, CNFSLASIZ); buf[CNFSLASIZ] = '\0'; cycbuff->updated = CNFShex2offt(buf); strncpy(buf, rpx.cyclenuma, CNFSLASIZ); buf[CNFSLASIZ] = '\0'; cycbuff->cyclenum = CNFShex2offt(buf); return; } static bool CNFSparse_part_line(char *l) { char *p; struct stat sb; off_t len; CYCBUFF *cycbuff, *tmp; /* Symbolic cnfs partition name */ if ((p = strchr(l, ':')) == NULL || p - l <= 0 || p - l > CNFSMAXCYCBUFFNAME - 1) { warn("CNFS: bad cycbuff name in line '%s'", l); return false; } *p = '\0'; if (CNFSgetcycbuffbyname(l) != NULL) { *p = ':'; warn("CNFS: duplicate cycbuff name in line '%s'", l); return false; } cycbuff = xmalloc(sizeof(CYCBUFF)); memset(cycbuff->name, '\0', CNFSNASIZ); strlcpy(cycbuff->name, l, CNFSNASIZ); l = ++p; /* Path to cnfs partition */ if ((p = strchr(l, ':')) == NULL || p - l <= 0 || p - l > CNFSPASIZ - 1) { warn("CNFS: bad pathname in line '%s'", l); free(cycbuff); return false; } *p = '\0'; memset(cycbuff->path, '\0', CNFSPASIZ); strlcpy(cycbuff->path, l, CNFSPASIZ); if (stat(cycbuff->path, &sb) < 0) { if (errno == EOVERFLOW) { warn("CNFS: file '%s': Overflow (probably >2GB without largefile" " support), ignoring '%s' cycbuff", cycbuff->path, cycbuff->name); } else { warn("CNFS: file '%s': %s, ignoring '%s' cycbuff", cycbuff->path, strerror(errno), cycbuff->name); } free(cycbuff); return false; } l = ++p; /* Length/size of symbolic partition */ len = strtoul(l, NULL, 10) * (off_t)1024; /* This value in KB in decimal */ if (S_ISREG(sb.st_mode) && len != sb.st_size) { if (sizeof(CYCBUFFEXTERN) > (size_t) sb.st_size) { notice("CNFS: length must be at least '%lu' for '%s' cycbuff(%lu bytes)", (unsigned long) sizeof(CYCBUFFEXTERN), cycbuff->name, (unsigned long) sb.st_size); free(cycbuff); return false; } } cycbuff->len = len; cycbuff->fd = -1; cycbuff->next = (CYCBUFF *)NULL; cycbuff->needflush = false; cycbuff->bitfield = NULL; cycbuff->minartoffset = 0; if (cycbufftab == (CYCBUFF *)NULL) cycbufftab = cycbuff; else { for (tmp = cycbufftab; tmp->next != (CYCBUFF *)NULL; tmp = tmp->next); tmp->next = cycbuff; } /* Done! */ return true; } static bool CNFSparse_metapart_line(char *l) { char *p, *cycbuff, *q = l; CYCBUFF *rp; METACYCBUFF *metacycbuff, *tmp; /* Symbolic metacycbuff name */ if ((p = strchr(l, ':')) == NULL || p - l <= 0) { warn("CNFS: bad partition name in line '%s'", l); return false; } *p = '\0'; if (CNFSgetmetacycbuffbyname(l) != NULL) { *p = ':'; warn("CNFS: duplicate metabuff name in line '%s'", l); return false; } metacycbuff = xmalloc(sizeof(METACYCBUFF)); metacycbuff->members = (CYCBUFF **)NULL; metacycbuff->count = 0; metacycbuff->name = xstrdup(l); metacycbuff->next = (METACYCBUFF *)NULL; metacycbuff->metamode = INTERLEAVE; l = ++p; if ((p = strchr(l, ':')) != NULL) { if (p - l <= 0) { warn("CNFS: bad mode in line '%s'", q); return false; } if (strcmp(++p, "INTERLEAVE") == 0) metacycbuff->metamode = INTERLEAVE; else if (strcmp(p, "SEQUENTIAL") == 0) metacycbuff->metamode = SEQUENTIAL; else { warn("CNFS: unknown mode in line '%s'", q); return false; } *--p = '\0'; } /* Cycbuff list */ while ((p = strchr(l, ',')) != NULL && p - l > 0) { *p = '\0'; cycbuff = l; l = ++p; if ((rp = CNFSgetcycbuffbyname(cycbuff)) == NULL) { warn("CNFS: bogus cycbuff '%s' (metacycbuff '%s')", cycbuff, metacycbuff->name); free(metacycbuff->members); free(metacycbuff->name); free(metacycbuff); return false; } if (metacycbuff->count == 0) metacycbuff->members = xmalloc(sizeof(CYCBUFF *)); else metacycbuff->members = xrealloc(metacycbuff->members, (metacycbuff->count + 1) * sizeof(CYCBUFF *)); metacycbuff->members[metacycbuff->count++] = rp; } /* Gotta deal with the last cycbuff on the list */ cycbuff = l; if ((rp = CNFSgetcycbuffbyname(cycbuff)) == NULL) { warn("CNFS: bogus cycbuff '%s' (metacycbuff '%s')", cycbuff, metacycbuff->name); free(metacycbuff->members); free(metacycbuff->name); free(metacycbuff); return false; } else { if (metacycbuff->count == 0) metacycbuff->members = xmalloc(sizeof(CYCBUFF *)); else metacycbuff->members = xrealloc(metacycbuff->members, (metacycbuff->count + 1) * sizeof(CYCBUFF *)); metacycbuff->members[metacycbuff->count++] = rp; } if (metacycbuff->count == 0) { warn("CNFS: no cycbuffs assigned to cycbuff '%s'", metacycbuff->name); free(metacycbuff->name); free(metacycbuff); return false; } if (metacycbufftab == (METACYCBUFF *)NULL) metacycbufftab = metacycbuff; else { for (tmp = metacycbufftab; tmp->next != (METACYCBUFF *)NULL; tmp = tmp->next); tmp->next = metacycbuff; } /* DONE! */ return true; } static bool CNFSparse_groups_line(void) { METACYCBUFF *mrp; STORAGE_SUB *sub = (STORAGE_SUB *)NULL; CNFSEXPIRERULES *metaexprule, *tmp; sub = SMGetConfig(TOKEN_CNFS, sub); for (;sub != (STORAGE_SUB *)NULL; sub = SMGetConfig(TOKEN_CNFS, sub)) { if (sub->options == (char *)NULL) { warn("CNFS: storage.conf options field is missing"); CNFScleanexpirerule(); return false; } if ((mrp = CNFSgetmetacycbuffbyname(sub->options)) == NULL) { warn("CNFS: storage.conf options field '%s' undefined", sub->options); CNFScleanexpirerule(); return false; } metaexprule = xmalloc(sizeof(CNFSEXPIRERULES)); metaexprule->class = sub->class; metaexprule->dest = mrp; metaexprule->next = (CNFSEXPIRERULES *)NULL; if (metaexprulestab == (CNFSEXPIRERULES *)NULL) metaexprulestab = metaexprule; else { for (tmp = metaexprulestab; tmp->next != (CNFSEXPIRERULES *)NULL; tmp = tmp->next); tmp->next = metaexprule; } } /* DONE! */ return true; } /* ** CNFSinit_disks -- Finish initializing cycbufftab ** Called by "innd" only -- we open (and keep) a read/write ** file descriptor for each CYCBUFF. ** ** Calling this function repeatedly shouldn't cause any harm ** speed-wise or bug-wise, as long as the caller is accessing the ** CYCBUFFs _read-only_. If innd calls this function repeatedly, ** bad things will happen. */ static bool CNFSinit_disks(CYCBUFF *cycbuff) { char buf[64]; CYCBUFFEXTERN *rpx; int fd; int tonextblock; off_t tmpo; off_t minartoffset; bool oneshot; /* ** Discover the state of our cycbuffs. If any of them are in icky shape, ** duck shamelessly & return false. */ if (cycbuff != (CYCBUFF *)NULL) oneshot = true; else { oneshot = false; cycbuff = cycbufftab; } for (; cycbuff != (CYCBUFF *)NULL; cycbuff = cycbuff->next) { if (strcmp(cycbuff->path, "/dev/null") == 0) { warn("CNFS: ERROR opening '%s' is not available", cycbuff->path); return false; } if (cycbuff->fd < 0) { if ((fd = open(cycbuff->path, SMopenmode ? O_RDWR : O_RDONLY)) < 0) { syswarn("CNFS: ERROR opening '%s' O_RDONLY", cycbuff->path); return false; } else { fdflag_close_exec(fd, true); cycbuff->fd = fd; } } errno = 0; cycbuff->bitfield = mmap(NULL, CNFS_HDR_PAGESIZE, SMopenmode ? (PROT_READ | PROT_WRITE) : PROT_READ, MAP_SHARED, cycbuff->fd, 0); if (cycbuff->bitfield == MAP_FAILED || errno != 0) { syswarn("CNFS: CNFSinitdisks: mmap for %s offset %d len %ld failed", cycbuff->path, 0, (long) cycbuff->minartoffset); cycbuff->bitfield = NULL; return false; } /* ** Much of this checking from previous revisions is (probably) bogus ** & buggy & particularly icky & unupdated. Use at your own risk. :-) */ rpx = (CYCBUFFEXTERN *)cycbuff->bitfield; cycbuff->magicver = 0; if (strncmp(rpx->magic, CNFS_MAGICV3, strlen(CNFS_MAGICV3)) == 0) { cycbuff->magicver = 3; cycbuff->blksz = 512; } if (strncmp(rpx->magic, CNFS_MAGICV4, strlen(CNFS_MAGICV4)) == 0) cycbuff->magicver = 4; if (cycbuff->magicver >= 3) { if (strncmp(rpx->name, cycbuff->name, CNFSNASIZ) != 0) { warn("CNFS: Mismatch 3: read %s for cycbuff %s", rpx->name, cycbuff->name); return false; } if (strncmp(rpx->path, cycbuff->path, CNFSPASIZ) != 0) { warn("CNFS: Path mismatch: read %s for cycbuff %s", rpx->path, cycbuff->path); } strncpy(buf, rpx->lena, CNFSLASIZ); buf[CNFSLASIZ] = '\0'; tmpo = CNFShex2offt(buf); if (tmpo != cycbuff->len) { warn("CNFS: Mismatch: read 0x%s length for cycbuff %s", CNFSofft2hex(tmpo, false), cycbuff->path); return false; } strncpy(buf, rpx->freea, CNFSLASIZ); buf[CNFSLASIZ] = '\0'; cycbuff->free = CNFShex2offt(buf); strncpy(buf, rpx->updateda, CNFSLASIZ); buf[CNFSLASIZ] = '\0'; cycbuff->updated = CNFShex2offt(buf); strncpy(buf, rpx->cyclenuma, CNFSLASIZ); buf[CNFSLASIZ] = '\0'; cycbuff->cyclenum = CNFShex2offt(buf); strncpy(cycbuff->metaname, rpx->metaname, CNFSLASIZ); strncpy(buf, rpx->orderinmeta, CNFSLASIZ); cycbuff->order = CNFShex2offt(buf); if (strncmp(rpx->currentbuff, "TRUE", CNFSMASIZ) == 0) { cycbuff->currentbuff = true; } else cycbuff->currentbuff = false; if (cycbuff->magicver > 3) { strncpy(buf, rpx->blksza, CNFSLASIZ); buf[CNFSLASIZ] = '\0'; cycbuff->blksz = CNFShex2offt(buf); } if (cycbuff->blksz < 512 || cycbuff->blksz > CNFS_MAX_BLOCKSIZE || 2 * (cycbuff->blksz / 2) != cycbuff->blksz) { warn("CNFS: Invalid: read 0x%s blocksize for cycbuff %s", CNFSofft2hex(cycbuff->blksz, false), cycbuff->path); return false; } } else { notice("CNFS: no magic cookie found for cycbuff %s, initializing", cycbuff->name); cycbuff->magicver = 4; cycbuff->free = cycbuff->minartoffset; cycbuff->updated = 0; cycbuff->cyclenum = 1; cycbuff->currentbuff = true; cycbuff->order = 0; /* to indicate this is newly added cycbuff */ cycbuff->needflush = true; cycbuff->blksz = CNFS_DFL_BLOCKSIZE; cycbuff->free = 0; memset(cycbuff->metaname, '\0', CNFSLASIZ); } /* ** The minimum article offset will be the size of the bitfield itself, ** len / (blocksize * 8), plus however many additional blocks the CYCBUFF ** external header occupies ... then round up to the next block. */ minartoffset = cycbuff->len / (cycbuff->blksz * 8) + CNFS_BEFOREBITF; tonextblock = CNFS_HDR_PAGESIZE - (minartoffset & (CNFS_HDR_PAGESIZE - 1)); cycbuff->minartoffset = minartoffset + tonextblock; munmap(cycbuff->bitfield, CNFS_HDR_PAGESIZE); errno = 0; cycbuff->bitfield = mmap(NULL, cycbuff->minartoffset, SMopenmode ? (PROT_READ | PROT_WRITE) : PROT_READ, MAP_SHARED, cycbuff->fd, 0); if (cycbuff->bitfield == MAP_FAILED || errno != 0) { warn("CNFS: CNFSinitdisks: mmap for %s offset %d len %ld failed: %m", cycbuff->path, 0, (long) cycbuff->minartoffset); cycbuff->bitfield = NULL; return false; } if (cycbuff->free == 0) cycbuff->free = cycbuff->minartoffset; if (cycbuff->needflush && !CNFSflushhead(cycbuff)) return false; if (oneshot) break; } return true; } static bool CNFS_setcurrent(METACYCBUFF *metacycbuff) { CYCBUFF *cycbuff; int i, currentcycbuff = 0, order = -1; bool foundcurrent = false; for (i = 0 ; i < metacycbuff->count ; i++) { cycbuff = metacycbuff->members[i]; if (strncmp(cycbuff->metaname, metacycbuff->name, CNFSNASIZ) != 0) { /* this cycbuff is moved from other metacycbuff , or is new */ cycbuff->order = i + 1; cycbuff->currentbuff = false; strncpy(cycbuff->metaname, metacycbuff->name, CNFSLASIZ); cycbuff->needflush = true; continue; } if (foundcurrent == false && cycbuff->currentbuff == true) { currentcycbuff = i; foundcurrent = true; } if (foundcurrent == false || order == -1 || order > cycbuff->order) { /* this cycbuff is a candidate for current cycbuff */ currentcycbuff = i; order = cycbuff->order; } if (cycbuff->order != i + 1) { /* cycbuff order seems to be changed */ cycbuff->order = i + 1; cycbuff->needflush = true; } } /* If no current cycbuff found (say, all our cycbuffs are new) default to 0 */ if (foundcurrent == false) { currentcycbuff = 0; } for (i = 0 ; i < metacycbuff->count ; i++) { cycbuff = metacycbuff->members[i]; if (currentcycbuff == i && cycbuff->currentbuff == false) { cycbuff->currentbuff = true; cycbuff->needflush = true; } if (currentcycbuff != i && cycbuff->currentbuff == true) { cycbuff->currentbuff = false; cycbuff->needflush = true; } if (cycbuff->needflush == true && !CNFSflushhead(cycbuff)) return false; } metacycbuff->memb_next = currentcycbuff; return true; } /* ** CNFSread_config() -- Read the cnfs partition/file configuration file. ** ** Oh, for the want of Perl! My parser probably shows that I don't use ** C all that often anymore.... */ static bool CNFSread_config(void) { char *path, *config, *from, *to, **ctab = (char **)NULL; int ctab_free = 0; /* Index to next free slot in ctab */ int ctab_i; bool metacycbufffound = false; bool cycbuffupdatefound = false; bool refreshintervalfound = false; int update, refresh; path = concatpath(innconf->pathetc, _PATH_CYCBUFFCONFIG); config = ReadInFile(path, NULL); if (config == NULL) { syswarn("CNFS: cannot read %s", path); free(config); free(path); return false; } free(path); for (from = to = config; *from; ) { if (*from == '#') { /* Comment line? */ while (*from && *from != '\n') from++; /* Skip past it */ from++; continue; /* Back to top of loop */ } if (*from == '\n') { /* End or just a blank line? */ from++; continue; /* Back to top of loop */ } if (ctab_free == 0) ctab = xmalloc(sizeof(char *)); else ctab = xrealloc(ctab, (ctab_free + 1) * sizeof(char *)); /* If we're here, we've got the beginning of a real entry */ ctab[ctab_free++] = to = from; while (1) { if (*from && *from == '\\' && *(from + 1) == '\n') { from += 2; /* Skip past backslash+newline */ while (*from && isspace((unsigned char) *from)) from++; continue; } if (*from && *from != '\n') *to++ = *from++; if (*from == '\n') { *to++ = '\0'; from++; break; } if (! *from) break; } } for (ctab_i = 0; ctab_i < ctab_free; ctab_i++) { if (strncmp(ctab[ctab_i], "cycbuff:", 8) == 0) { if (metacycbufffound) { warn("CNFS: all cycbuff entries shoud be before metacycbuff" " entries"); free(config); free(ctab); return false; } if (!CNFSparse_part_line(ctab[ctab_i] + 8)) { free(config); free(ctab); return false; } } else if (strncmp(ctab[ctab_i], "metacycbuff:", 12) == 0) { metacycbufffound = true; if (!CNFSparse_metapart_line(ctab[ctab_i] + 12)) { free(config); free(ctab); return false; } } else if (strncmp(ctab[ctab_i], "cycbuffupdate:", 14) == 0) { if (cycbuffupdatefound) { warn("CNFS: duplicate cycbuffupdate entries"); free(config); free(ctab); return false; } cycbuffupdatefound = true; update = atoi(ctab[ctab_i] + 14); if (update < 0) { warn("CNFS: invalid cycbuffupdate"); free(config); free(ctab); return false; } if (update == 0) metabuff_update = METACYCBUFF_UPDATE; else metabuff_update = update; } else if (strncmp(ctab[ctab_i], "refreshinterval:", 16) == 0) { if (refreshintervalfound) { warn("CNFS: duplicate refreshinterval entries"); free(config); free(ctab); return false; } refreshintervalfound = true; refresh = atoi(ctab[ctab_i] + 16); if (refresh < 0) { warn("CNFS: invalid refreshinterval"); free(config); free(ctab); return false; } if (refresh == 0) refresh_interval = REFRESH_INTERVAL; else refresh_interval = refresh; } else { warn("CNFS: bogus metacycbuff config line '%s' ignored", ctab[ctab_i]); } } free(config); free(ctab); if (!CNFSparse_groups_line()) { return false; } if (cycbufftab == (CYCBUFF *)NULL) { warn("CNFS: zero cycbuffs defined"); return false; } if (metacycbufftab == (METACYCBUFF *)NULL) { warn("CNFS: zero metacycbuffs defined"); return false; } return true; } /* Figure out what page an address is in and flush those pages */ static void cnfs_mapcntl(void *p, size_t length, int flags) { char *start, *end; start = (char *)((size_t)p & ~(size_t)(pagesize - 1)); end = (char *)((size_t)((char *)p + length + pagesize) & ~(size_t)(pagesize - 1)); if (flags == MS_INVALIDATE) { msync(start, end - start, flags); } else { static char *sstart, *send; /* Don't thrash the system with msync()s - keep the last value * and check each time, only if the pages which we should * flush change actually flush the previous ones. Calling * cnfs_mapcntl(NULL, 0, MS_ASYNC) then flushes the final * piece. */ if (start != sstart || end != send) { if (sstart != NULL && send != NULL) { msync(sstart, send - sstart, flags); } sstart = start; send = end; } } } /* ** Bit arithmetic by brute force. ** ** XXXYYYXXX WARNING: the code below is not endian-neutral! */ typedef unsigned long ULONG; static int CNFSUsedBlock(CYCBUFF *cycbuff, off_t offset, bool set_operation, bool setbitvalue) { off_t blocknum; off_t longoffset; int bitoffset; /* From the 'left' side of the long */ static int uninitialized = 1; static int longsize = sizeof(long); int i; ULONG bitlong, on, off, mask; static ULONG onarray[64], offarray[64]; ULONG *where; if (uninitialized) { on = 1; off = on; off ^= ULONG_MAX; for (i = (longsize * 8) - 1; i >= 0; i--) { onarray[i] = on; offarray[i] = off; on <<= 1; off = on; off ^= ULONG_MAX; } uninitialized = 0; } /* We allow bit-setting under minartoffset, but it better be false */ if ((offset < cycbuff->minartoffset && setbitvalue) || offset > cycbuff->len) { char bufoff[64], bufmin[64], bufmax[64]; SMseterror(SMERR_INTERNAL, NULL); strlcpy(bufoff, CNFSofft2hex(offset, false), sizeof(bufoff)); strlcpy(bufmin, CNFSofft2hex(cycbuff->minartoffset, false), sizeof(bufmin)); strlcpy(bufmax, CNFSofft2hex(cycbuff->len, false), sizeof(bufmax)); warn("CNFS: CNFSUsedBlock: invalid offset %s, min = %s, max = %s", bufoff, bufmin, bufmax); return 0; } if (offset % cycbuff->blksz != 0) { SMseterror(SMERR_INTERNAL, NULL); warn("CNFS: CNFSsetusedbitbyrp: offset %s not on %d-byte block" " boundary", CNFSofft2hex(offset, false), cycbuff->blksz); return 0; } blocknum = offset / cycbuff->blksz; longoffset = blocknum / (longsize * 8); bitoffset = blocknum % (longsize * 8); where = (ULONG *)cycbuff->bitfield + (CNFS_BEFOREBITF / longsize) + longoffset; bitlong = *where; if (set_operation) { if (setbitvalue) { mask = onarray[bitoffset]; bitlong |= mask; } else { mask = offarray[bitoffset]; bitlong &= mask; } *where = bitlong; if (innconf->nfswriter) { cnfs_mapcntl(where, sizeof *where, MS_ASYNC); } return 2; /* XXX Clean up return semantics */ } /* It's a read operation */ mask = onarray[bitoffset]; /* * return bitlong & mask; doesn't work if sizeof(ulong) > sizeof(int) */ if ( bitlong & mask ) return 1; else return 0; } static int CNFSArtMayBeHere(CYCBUFF *cycbuff, off_t offset, uint32_t cycnum) { static time_t lastupdate = 0; CYCBUFF *tmp; if (SMpreopen && !SMopenmode) { if ((time(NULL) - lastupdate) > refresh_interval) { /* XXX Changed to refresh every 30sec - cmo*/ for (tmp = cycbufftab; tmp != (CYCBUFF *)NULL; tmp = tmp->next) { CNFSReadFreeAndCycle(tmp); } lastupdate = time(NULL); } else if (cycnum == cycbuff->cyclenum + 1) { /* rollover ? */ CNFSReadFreeAndCycle(cycbuff); } } /* ** The current cycle number may have advanced since the last time we ** checked it, so use a ">=" check instead of "==". Our intent is ** avoid a false negative response, *not* a false positive response. */ if (! (cycnum == cycbuff->cyclenum || (cycnum == cycbuff->cyclenum - 1 && offset > cycbuff->free) || (cycnum + 1 == 0 && cycbuff->cyclenum == 2 && offset > cycbuff->free))) { /* We've been overwritten */ return 0; } return CNFSUsedBlock(cycbuff, offset, false, false); } bool cnfs_init(SMATTRIBUTE *attr) { METACYCBUFF *metacycbuff; CYCBUFF *cycbuff; if (attr == NULL) { warn("CNFS: attr is NULL"); SMseterror(SMERR_INTERNAL, "attr is NULL"); return false; } attr->selfexpire = true; attr->expensivestat = false; if (innconf == NULL) { if (!innconf_read(NULL)) { warn("CNFS: innconf_read failed"); SMseterror(SMERR_INTERNAL, "ReadInnConf() failed"); return false; } } if (pagesize == 0) { pagesize = getpagesize(); if (pagesize == -1) { syswarn("CNFS: getpagesize failed"); SMseterror(SMERR_INTERNAL, "getpagesize failed"); pagesize = 0; return false; } if ((pagesize > CNFS_HDR_PAGESIZE) || (CNFS_HDR_PAGESIZE % pagesize)) { warn("CNFS: CNFS_HDR_PAGESIZE (%d) is not a multiple of" " pagesize (%ld)", CNFS_HDR_PAGESIZE, pagesize); SMseterror(SMERR_INTERNAL, "CNFS_HDR_PAGESIZE not multiple of pagesize"); return false; } } if (STORAGE_TOKEN_LENGTH < 16) { warn("CNFS: token length is less than 16 bytes"); SMseterror(SMERR_TOKENSHORT, NULL); return false; } if (!CNFSread_config()) { CNFScleancycbuff(); CNFScleanmetacycbuff(); CNFScleanexpirerule(); SMseterror(SMERR_INTERNAL, NULL); return false; } if (!CNFSinit_disks(NULL)) { CNFScleancycbuff(); CNFScleanmetacycbuff(); CNFScleanexpirerule(); SMseterror(SMERR_INTERNAL, NULL); return false; } for (metacycbuff = metacycbufftab; metacycbuff != (METACYCBUFF *)NULL; metacycbuff = metacycbuff->next) { metacycbuff->memb_next = 0; metacycbuff->write_count = 0; /* Let's not forget this */ if (metacycbuff->metamode == SEQUENTIAL) /* mark current cycbuff */ if (CNFS_setcurrent(metacycbuff) == false) { CNFScleancycbuff(); CNFScleanmetacycbuff(); CNFScleanexpirerule(); SMseterror(SMERR_INTERNAL, NULL); return false; } } if (!SMpreopen) { for (cycbuff = cycbufftab; cycbuff != (CYCBUFF *)NULL; cycbuff = cycbuff->next) { CNFSshutdowncycbuff(cycbuff); } } return true; } TOKEN cnfs_store(const ARTHANDLE article, const STORAGECLASS class) { TOKEN token; CYCBUFF *cycbuff = NULL; METACYCBUFF *metacycbuff = NULL; int i; static char buf[1024]; static char alignbuf[CNFS_MAX_BLOCKSIZE]; char *artcycbuffname; off_t artoffset, middle; uint32_t artcyclenum; CNFSARTHEADER cah; static struct iovec *iov; static int iovcnt; int tonextblock; CNFSEXPIRERULES *metaexprule; off_t left; size_t totlen; for (metaexprule = metaexprulestab; metaexprule != (CNFSEXPIRERULES *)NULL; metaexprule = metaexprule->next) { if (metaexprule->class == class) break; } if (metaexprule == (CNFSEXPIRERULES *)NULL) { SMseterror(SMERR_INTERNAL, "no rules match"); warn("CNFS: no matches for group '%s'", buf); memset(&token, 0, sizeof(token)); token.type = TOKEN_EMPTY; return token; } metacycbuff = metaexprule->dest; cycbuff = metacycbuff->members[metacycbuff->memb_next]; if (cycbuff == NULL) { SMseterror(SMERR_INTERNAL, "no cycbuff found"); warn("CNFS: no cycbuff found for %d", metacycbuff->memb_next); token.type = TOKEN_EMPTY; return token; } else if (!SMpreopen && !CNFSinit_disks(cycbuff)) { SMseterror(SMERR_INTERNAL, "cycbuff initialization fail"); warn("CNFS: cycbuff '%s' initialization fail", cycbuff->name); token.type = TOKEN_EMPTY; return token; } /* cycbuff->free should have already been aligned by the last write, but realign it just to be sure. */ tonextblock = cycbuff->blksz - (cycbuff->free & (cycbuff->blksz - 1)); if (tonextblock != cycbuff->blksz) cycbuff->free += tonextblock; /* Article too big? */ if (cycbuff->len - cycbuff->free < cycbuff->blksz + 1) left = 0; else left = cycbuff->len - cycbuff->free - cycbuff->blksz - 1; if ((off_t) article.len > left) { for (middle = cycbuff->free ;middle < cycbuff->len - cycbuff->blksz - 1; middle += cycbuff->blksz) { CNFSUsedBlock(cycbuff, middle, true, false); } if (innconf->nfswriter) { cnfs_mapcntl(NULL, 0, MS_ASYNC); } cycbuff->free = cycbuff->minartoffset; cycbuff->cyclenum++; if (cycbuff->magicver <= 3) { if (cycbuff->cyclenum == 0) cycbuff->cyclenum += 2; /* cnfs_next() needs this */ } else { if ((cycbuff->cyclenum & 0xFFFFFF) == 0) /* 24 bits max */ cycbuff->cyclenum = 2; /* cnfs_next() needs this */ } cycbuff->needflush = true; if (metacycbuff->metamode == INTERLEAVE) { CNFSflushhead(cycbuff); /* Flush, just for giggles */ notice("CNFS: cycbuff %s rollover to cycle 0x%x... remain calm", cycbuff->name, cycbuff->cyclenum); } else { /* SEQUENTIAL */ cycbuff->currentbuff = false; CNFSflushhead(cycbuff); /* Flush, just for giggles */ if (!SMpreopen) CNFSshutdowncycbuff(cycbuff); metacycbuff->memb_next = (metacycbuff->memb_next + 1) % metacycbuff->count; cycbuff = metacycbuff->members[metacycbuff->memb_next]; notice("CNFS: metacycbuff %s cycbuff is moved to %s remain calm", metacycbuff->name, cycbuff->name); if (!SMpreopen && !CNFSinit_disks(cycbuff)) { SMseterror(SMERR_INTERNAL, "cycbuff initialization fail"); warn("CNFS: cycbuff '%s' initialization fail", cycbuff->name); token.type = TOKEN_EMPTY; return token; } cycbuff->currentbuff = true; cycbuff->needflush = true; CNFSflushhead(cycbuff); /* Flush, just for giggles */ } } /* Ah, at least we know all three important data */ artcycbuffname = cycbuff->name; artoffset = cycbuff->free; artcyclenum = cycbuff->cyclenum; memset(&cah, 0, sizeof(cah)); cah.size = htonl(article.len); if (article.arrived == (time_t)0) cah.arrived = htonl(time(NULL)); else cah.arrived = htonl(article.arrived); cah.class = class; if (lseek(cycbuff->fd, artoffset, SEEK_SET) < 0) { SMseterror(SMERR_INTERNAL, "lseek failed"); syswarn("CNFS: lseek failed for '%s' offset 0x%s", cycbuff->name, CNFSofft2hex(artoffset, false)); token.type = TOKEN_EMPTY; if (!SMpreopen) CNFSshutdowncycbuff(cycbuff); return token; } if (iovcnt == 0) { iov = xmalloc((article.iovcnt + 2) * sizeof(struct iovec)); iovcnt = article.iovcnt + 2; } else if (iovcnt < article.iovcnt + 2) { iov = xrealloc(iov, (article.iovcnt + 2) * sizeof(struct iovec)); iovcnt = article.iovcnt + 2; } iov[0].iov_base = (char *) &cah; iov[0].iov_len = sizeof(cah); totlen = iov[0].iov_len; for (i = 1; i <= article.iovcnt; i++) { iov[i].iov_base = article.iov[i-1].iov_base; iov[i].iov_len = article.iov[i-1].iov_len; totlen += iov[i].iov_len; } if ((totlen & (cycbuff->blksz - 1)) != 0) { /* Want to xwritev an exact multiple of cycbuff->blksz */ iov[i].iov_base = alignbuf; iov[i].iov_len = cycbuff->blksz - (totlen & (cycbuff->blksz - 1)); totlen += iov[i].iov_len; i++; } if (xwritev(cycbuff->fd, iov, i) < 0) { SMseterror(SMERR_INTERNAL, "cnfs_store() xwritev() failed"); syswarn("CNFS: cnfs_store xwritev failed for '%s' offset 0x%s", artcycbuffname, CNFSofft2hex(artoffset, false)); token.type = TOKEN_EMPTY; if (!SMpreopen) CNFSshutdowncycbuff(cycbuff); return token; } cycbuff->needflush = true; /* Now that the article is written, advance the free pointer & flush */ cycbuff->free += totlen; /* ** If cycbuff->free > cycbuff->len, don't worry. The next cnfs_store() ** will detect the situation & wrap around correctly. */ if (metacycbuff->metamode == INTERLEAVE) metacycbuff->memb_next = (metacycbuff->memb_next + 1) % metacycbuff->count; if (++metacycbuff->write_count % metabuff_update == 0) { for (i = 0; i < metacycbuff->count; i++) { CNFSflushhead(metacycbuff->members[i]); } } CNFSUsedBlock(cycbuff, artoffset, true, true); for (middle = artoffset + cycbuff->blksz; middle < cycbuff->free; middle += cycbuff->blksz) { CNFSUsedBlock(cycbuff, middle, true, false); } if (innconf->nfswriter) { cnfs_mapcntl(NULL, 0, MS_ASYNC); } if (!SMpreopen) CNFSshutdowncycbuff(cycbuff); return CNFSMakeToken(artcycbuffname, artoffset, cycbuff->blksz, artcyclenum, class); } ARTHANDLE *cnfs_retrieve(const TOKEN token, const RETRTYPE amount) { char cycbuffname[9]; off_t offset; uint32_t cycnum; uint32_t block; CYCBUFF *cycbuff; ARTHANDLE *art; CNFSARTHEADER cah; PRIV_CNFS *private; char *p; long pagefudge; off_t cycsize, mmapoffset; static TOKEN ret_token; static bool nomessage = false; int plusoffset = 0; if (token.type != TOKEN_CNFS) { SMseterror(SMERR_INTERNAL, NULL); return NULL; } if (! CNFSBreakToken(token, cycbuffname, &block, &cycnum)) { /* SMseterror() should have already been called */ return NULL; } if ((cycbuff = CNFSgetcycbuffbyname(cycbuffname)) == NULL) { SMseterror(SMERR_NOENT, NULL); if (!nomessage) { warn("CNFS: cnfs_retrieve: token %s: bogus cycbuff name:" " %s:0x%s:%d", TokenToText(token), cycbuffname, CNFSofft2hex(block, false), cycnum); nomessage = true; } return NULL; } if (!SMpreopen && !CNFSinit_disks(cycbuff)) { SMseterror(SMERR_INTERNAL, "cycbuff initialization fail"); warn("CNFS: cycbuff '%s' initialization fail", cycbuff->name); return NULL; } offset = (off_t)block * cycbuff->blksz; if (! CNFSArtMayBeHere(cycbuff, offset, cycnum)) { SMseterror(SMERR_NOENT, NULL); if (!SMpreopen) CNFSshutdowncycbuff(cycbuff); return NULL; } art = xmalloc(sizeof(ARTHANDLE)); art->type = TOKEN_CNFS; if (amount == RETR_STAT) { art->data = NULL; art->len = 0; art->private = NULL; ret_token = token; art->token = &ret_token; if (!SMpreopen) CNFSshutdowncycbuff(cycbuff); return art; } /* ** Because we don't know the length of the article (yet), we'll ** just mmap() a chunk of memory which is guaranteed to be larger ** than the largest article can be. ** XXX Because the max article size can be changed, we could get into hot ** XXX water here. So, to be safe, we double MAX_ART_SIZE and add enough ** XXX extra for the pagesize fudge factor and CNFSARTHEADER structure. */ if (pread(cycbuff->fd, &cah, sizeof(cah), offset) != sizeof(cah)) { SMseterror(SMERR_UNDEFINED, "read failed"); syswarn("CNFS: could not read token %s %s:0x%s:%d", TokenToText(token), cycbuffname, CNFSofft2hex(offset, false), cycnum); free(art); if (!SMpreopen) CNFSshutdowncycbuff(cycbuff); return NULL; } #ifdef OLD_CNFS if(cah.size == htonl(0x1234) && ntohl(cah.arrived) < time(NULL)-10*365*24*3600) { oldCNFSARTHEADER cahh; *(CNFSARTHEADER *)&cahh = cah; if(pread(cycbuff->fd, ((char *)&cahh)+sizeof(CNFSARTHEADER), sizeof(oldCNFSARTHEADER)-sizeof(CNFSARTHEADER), offset+sizeof(cah)) != sizeof(oldCNFSARTHEADER)-sizeof(CNFSARTHEADER)) { SMseterror(SMERR_UNDEFINED, "read2 failed"); syswarn("CNFS: could not read2 token %s %s:0x%s:%ld: %m", TokenToText(token), cycbuffname, CNFSofft2hex(offset, false), cycnum); free(art); if (!SMpreopen) CNFSshutdowncycbuff(cycbuff); return NULL; } cah.size = cahh.size; cah.arrived = htonl(time(NULL)); cah.class = 0; plusoffset = sizeof(oldCNFSARTHEADER)-sizeof(CNFSARTHEADER); } #endif /* OLD_CNFS */ if (offset > cycbuff->len - cycbuff->blksz - (off_t) ntohl(cah.size) - 1) { if (!SMpreopen) { SMseterror(SMERR_UNDEFINED, "CNFSARTHEADER size overflow"); warn("CNFS: could not match article size token %s %s:0x%s:%d: %ld", TokenToText(token), cycbuffname, CNFSofft2hex(offset, false), cycnum, (long) ntohl(cah.size)); free(art); CNFSshutdowncycbuff(cycbuff); return NULL; } CNFSReadFreeAndCycle(cycbuff); if (offset > cycbuff->len - cycbuff->blksz - (off_t) ntohl(cah.size) - 1) { SMseterror(SMERR_UNDEFINED, "CNFSARTHEADER size overflow"); warn("CNFS: could not match article size token %s %s:0x%s:%d: %ld", TokenToText(token), cycbuffname, CNFSofft2hex(offset, false), cycnum, (long) ntohl(cah.size)); free(art); return NULL; } } /* checking the bitmap to ensure cah.size is not broken was dropped */ cycsize = ntohl(cah.size); if (innconf->cnfscheckfudgesize != 0 && innconf->maxartsize != 0 && ((unsigned) cycsize > innconf->maxartsize + innconf->cnfscheckfudgesize)) { char buf1[24]; strlcpy(buf1, CNFSofft2hex(cycbuff->free, false), sizeof(buf1)); SMseterror(SMERR_UNDEFINED, "CNFSARTHEADER fudge size overflow"); warn("CNFS: fudge size overflows bitmaps %s %s:0x%s: %ld", TokenToText(token), cycbuffname, buf1, (long) ntohl(cah.size)); if (!SMpreopen) CNFSshutdowncycbuff(cycbuff); free(art); return NULL; } private = xmalloc(sizeof(PRIV_CNFS)); art->private = (void *)private; art->arrived = ntohl(cah.arrived); offset += sizeof(cah) + plusoffset; if (innconf->articlemmap) { pagefudge = offset % pagesize; mmapoffset = offset - pagefudge; private->len = pagefudge + ntohl(cah.size); if ((private->base = mmap(NULL, private->len, PROT_READ, MAP_SHARED, cycbuff->fd, mmapoffset)) == MAP_FAILED) { SMseterror(SMERR_UNDEFINED, "mmap failed"); syswarn("CNFS: could not mmap token %s %s:0x%s:%d", TokenToText(token), cycbuffname, CNFSofft2hex(offset, false), cycnum); free(art->private); free(art); if (!SMpreopen) CNFSshutdowncycbuff(cycbuff); return NULL; } mmap_invalidate(private->base, private->len); if (amount == RETR_ALL) madvise(private->base, private->len, MADV_WILLNEED); else madvise(private->base, private->len, MADV_SEQUENTIAL); } else { private->base = xmalloc(ntohl(cah.size)); pagefudge = 0; if (pread(cycbuff->fd, private->base, ntohl(cah.size), offset) < 0) { SMseterror(SMERR_UNDEFINED, "read failed"); syswarn("CNFS: could not read token %s %s:0x%s:%d", TokenToText(token), cycbuffname, CNFSofft2hex(offset, false), cycnum); free(private->base); free(art->private); free(art); if (!SMpreopen) CNFSshutdowncycbuff(cycbuff); return NULL; } } ret_token = token; art->token = &ret_token; art->len = ntohl(cah.size); if (amount == RETR_ALL) { art->data = innconf->articlemmap ? private->base + pagefudge : private->base; if (!SMpreopen) CNFSshutdowncycbuff(cycbuff); return art; } if ((p = wire_findbody(innconf->articlemmap ? private->base + pagefudge : private->base, art->len)) == NULL) { SMseterror(SMERR_NOBODY, NULL); if (innconf->articlemmap) munmap(private->base, private->len); else free(private->base); free(art->private); free(art); if (!SMpreopen) CNFSshutdowncycbuff(cycbuff); return NULL; } if (amount == RETR_HEAD) { if (innconf->articlemmap) { art->data = private->base + pagefudge; art->len = p - private->base - pagefudge; } else { art->data = private->base; art->len = p - private->base; } /* Headers end just before the first empty line (\r\n). */ art->len = art->len - 2; if (!SMpreopen) CNFSshutdowncycbuff(cycbuff); return art; } if (amount == RETR_BODY) { art->data = p; if (innconf->articlemmap) art->len = art->len - (p - private->base - pagefudge); else art->len = art->len - (p - private->base); if (!SMpreopen) CNFSshutdowncycbuff(cycbuff); return art; } SMseterror(SMERR_UNDEFINED, "Invalid retrieve request"); if (innconf->articlemmap) munmap(private->base, private->len); else free(private->base); free(art->private); free(art); if (!SMpreopen) CNFSshutdowncycbuff(cycbuff); return NULL; } void cnfs_freearticle(ARTHANDLE *article) { PRIV_CNFS *private; if (!article) return; if (article->private) { private = (PRIV_CNFS *)article->private; if (innconf->articlemmap) munmap(private->base, private->len); else free(private->base); free(private); } free(article); } bool cnfs_cancel(TOKEN token) { char cycbuffname[9]; off_t offset; uint32_t cycnum; uint32_t block; CYCBUFF *cycbuff; if (token.type != TOKEN_CNFS) { SMseterror(SMERR_INTERNAL, NULL); return false; } if (! CNFSBreakToken(token, cycbuffname, &block, &cycnum)) { SMseterror(SMERR_INTERNAL, NULL); /* SMseterror() should have already been called */ return false; } if ((cycbuff = CNFSgetcycbuffbyname(cycbuffname)) == NULL) { SMseterror(SMERR_INTERNAL, "bogus cycbuff name"); return false; } if (!SMpreopen && !CNFSinit_disks(cycbuff)) { SMseterror(SMERR_INTERNAL, "cycbuff initialization fail"); warn("CNFS: cycbuff '%s' initialization fail", cycbuff->name); return false; } offset = (off_t)block * cycbuff->blksz; if (! (cycnum == cycbuff->cyclenum || (cycnum == cycbuff->cyclenum - 1 && offset > cycbuff->free) || (cycnum + 1 == 0 && cycbuff->cyclenum == 2 && offset > cycbuff->free))) { SMseterror(SMERR_NOENT, NULL); if (!SMpreopen) CNFSshutdowncycbuff(cycbuff); return false; } if (CNFSUsedBlock(cycbuff, offset, false, false) == 0) { SMseterror(SMERR_NOENT, NULL); if (!SMpreopen) CNFSshutdowncycbuff(cycbuff); return false; } CNFSUsedBlock(cycbuff, offset, true, false); if (innconf->nfswriter) { cnfs_mapcntl(NULL, 0, MS_ASYNC); } if (!SMpreopen) CNFSshutdowncycbuff(cycbuff); return true; } ARTHANDLE * cnfs_next(ARTHANDLE *article, const RETRTYPE amount) { ARTHANDLE *art; CYCBUFF *cycbuff; PRIV_CNFS priv, *private; off_t middle = 0, limit; CNFSARTHEADER cah; off_t offset; long pagefudge, blockfudge; static TOKEN token; int tonextblock; off_t mmapoffset; char *p; int plusoffset = 0; if (article == NULL) { if ((cycbuff = cycbufftab) == NULL) return NULL; priv.offset = 0; priv.rollover = false; priv.len = 0; priv.base = NULL; priv.cycbuff = NULL; } else { priv = *(PRIV_CNFS *)article->private; free(article->private); free(article); if (innconf->articlemmap) munmap(priv.base, priv.len); else { /* In the case we return art->data = NULL, we * must not free an already stale pointer. -mibsoft@mibsoftware.com */ if (priv.base) { free(priv.base); priv.base = 0; } } cycbuff = priv.cycbuff; } for (;cycbuff != (CYCBUFF *)NULL; cycbuff = cycbuff->next, priv.offset = 0) { if (!SMpreopen && !CNFSinit_disks(cycbuff)) { SMseterror(SMERR_INTERNAL, "cycbuff initialization fail"); continue; } if (priv.rollover && priv.offset >= cycbuff->free) { priv.offset = 0; if (!SMpreopen) CNFSshutdowncycbuff(cycbuff); continue; } if (priv.offset == 0) { if (cycbuff->cyclenum == 1) { priv.offset = cycbuff->minartoffset; priv.rollover = true; } else { priv.offset = cycbuff->free; priv.rollover = false; } } if (!priv.rollover) { for (middle = priv.offset ;middle < cycbuff->len - cycbuff->blksz - 1; middle += cycbuff->blksz) { if (CNFSUsedBlock(cycbuff, middle, false, false) != 0) break; } if (middle >= cycbuff->len - cycbuff->blksz - 1) { priv.rollover = true; middle = cycbuff->minartoffset; } break; } else { for (middle = priv.offset ;middle < cycbuff->free; middle += cycbuff->blksz) { if (CNFSUsedBlock(cycbuff, middle, false, false) != 0) break; } if (middle >= cycbuff->free) { middle = 0; if (!SMpreopen) CNFSshutdowncycbuff(cycbuff); continue; } else break; } } if (cycbuff == (CYCBUFF *)NULL) return (ARTHANDLE *)NULL; offset = middle; if (pread(cycbuff->fd, &cah, sizeof(cah), offset) != sizeof(cah)) { if (!SMpreopen) CNFSshutdowncycbuff(cycbuff); return (ARTHANDLE *)NULL; } #ifdef OLD_CNFS if(cah.size == htonl(0x1234) && ntohl(cah.arrived) < time(NULL)-10*365*24*3600) { oldCNFSARTHEADER cahh; *(CNFSARTHEADER *)&cahh = cah; if(pread(cycbuff->fd, ((char *)&cahh)+sizeof(CNFSARTHEADER), sizeof(oldCNFSARTHEADER)-sizeof(CNFSARTHEADER), offset+sizeof(cah)) != sizeof(oldCNFSARTHEADER)-sizeof(CNFSARTHEADER)) { if (!SMpreopen) CNFSshutdowncycbuff(cycbuff); return (ARTHANDLE *)NULL; } cah.size = cahh.size; cah.arrived = htonl(time(NULL)); cah.class = 0; plusoffset = sizeof(oldCNFSARTHEADER)-sizeof(CNFSARTHEADER); } #endif /* OLD_CNFS */ art = xmalloc(sizeof(ARTHANDLE)); private = xmalloc(sizeof(PRIV_CNFS)); art->private = (void *)private; art->type = TOKEN_CNFS; *private = priv; private->cycbuff = cycbuff; private->offset = middle; if (cycbuff->len - cycbuff->free < (off_t) ntohl(cah.size) + cycbuff->blksz + 1) { private->offset += cycbuff->blksz; art->data = NULL; art->len = 0; art->token = NULL; if (!SMpreopen) CNFSshutdowncycbuff(cycbuff); return art; } /* check the bitmap to ensure cah.size is not broken */ blockfudge = (sizeof(cah) + plusoffset + ntohl(cah.size)) % cycbuff->blksz; limit = private->offset + sizeof(cah) + plusoffset + ntohl(cah.size) - blockfudge + cycbuff->blksz; if (offset < cycbuff->free) { for (middle = offset + cycbuff->blksz; (middle < cycbuff->free) && (middle < limit); middle += cycbuff->blksz) { if (CNFSUsedBlock(cycbuff, middle, false, false) != 0) /* Bitmap set. This article assumes to be broken */ break; } if ((middle > cycbuff->free) || (middle != limit)) { private->offset = middle; art->data = NULL; art->len = 0; art->token = NULL; if (!SMpreopen) CNFSshutdowncycbuff(cycbuff); return art; } } else { for (middle = offset + cycbuff->blksz; (middle < cycbuff->len) && (middle < limit); middle += cycbuff->blksz) { if (CNFSUsedBlock(cycbuff, middle, false, false) != 0) /* Bitmap set. This article assumes to be broken */ break; } if ((middle >= cycbuff->len) || (middle != limit)) { private->offset = middle; art->data = NULL; art->len = 0; art->token = NULL; if (!SMpreopen) CNFSshutdowncycbuff(cycbuff); return art; } } if (innconf->cnfscheckfudgesize != 0 && innconf->maxartsize != 0 && ((unsigned int) ntohl(cah.size) > innconf->maxartsize + innconf->cnfscheckfudgesize)) { art->data = NULL; art->len = 0; art->token = NULL; private->base = 0; if (!SMpreopen) CNFSshutdowncycbuff(cycbuff); return art; } private->offset += (off_t) ntohl(cah.size) + sizeof(cah) + plusoffset; tonextblock = cycbuff->blksz - (private->offset & (cycbuff->blksz - 1)); private->offset += (off_t) tonextblock; art->arrived = ntohl(cah.arrived); token = CNFSMakeToken(cycbuff->name, offset, cycbuff->blksz, (offset > cycbuff->free) ? cycbuff->cyclenum - 1 : cycbuff->cyclenum, cah.class); art->token = &token; offset += sizeof(cah) + plusoffset; if (innconf->articlemmap) { pagefudge = offset % pagesize; mmapoffset = offset - pagefudge; private->len = pagefudge + ntohl(cah.size); if ((private->base = mmap(0, private->len, PROT_READ, MAP_SHARED, cycbuff->fd, mmapoffset)) == MAP_FAILED) { art->data = NULL; art->len = 0; art->token = NULL; if (!SMpreopen) CNFSshutdowncycbuff(cycbuff); return art; } mmap_invalidate(private->base, private->len); madvise(private->base, private->len, MADV_SEQUENTIAL); } else { private->base = xmalloc(ntohl(cah.size)); pagefudge = 0; if (pread(cycbuff->fd, private->base, ntohl(cah.size), offset) < 0) { art->data = NULL; art->len = 0; art->token = NULL; if (!SMpreopen) CNFSshutdowncycbuff(cycbuff); free(private->base); private->base = 0; return art; } } art->len = ntohl(cah.size); if (amount == RETR_ALL) { art->data = innconf->articlemmap ? private->base + pagefudge : private->base; if (!SMpreopen) CNFSshutdowncycbuff(cycbuff); return art; } if ((p = wire_findbody(innconf->articlemmap ? private->base + pagefudge : private->base, art->len)) == NULL) { art->data = NULL; art->len = 0; art->token = NULL; if (!SMpreopen) CNFSshutdowncycbuff(cycbuff); return art; } if (amount == RETR_HEAD) { if (innconf->articlemmap) { art->data = private->base + pagefudge; art->len = p - private->base - pagefudge; } else { art->data = private->base; art->len = p - private->base; } /* Headers end just before the first empty line (\r\n). */ art->len = art->len - 2; if (!SMpreopen) CNFSshutdowncycbuff(cycbuff); return art; } if (amount == RETR_BODY) { art->data = p; if (innconf->articlemmap) art->len = art->len - (p - private->base - pagefudge); else art->len = art->len - (p - private->base); if (!SMpreopen) CNFSshutdowncycbuff(cycbuff); return art; } art->data = NULL; art->len = 0; art->token = NULL; if (!SMpreopen) CNFSshutdowncycbuff(cycbuff); return art; } bool cnfs_ctl(PROBETYPE type, TOKEN *token UNUSED, void *value) { struct artngnum *ann; switch (type) { case SMARTNGNUM: if ((ann = (struct artngnum *)value) == NULL) return false; /* make SMprobe() call cnfs_retrieve() */ ann->artnum = 0; return true; default: return false; } } bool cnfs_flushcacheddata(FLUSHTYPE type) { if (type == SM_ALL || type == SM_HEAD) CNFSflushallheads(); return true; } void cnfs_printfiles(FILE *file, TOKEN token, char **xref UNUSED, int ngroups UNUSED) { fprintf(file, "%s\n", TokenToText(token)); } void cnfs_shutdown(void) { CNFScleancycbuff(); CNFScleanmetacycbuff(); CNFScleanexpirerule(); } inn-2.6.0/storage/buffindexed/0000755000175200017520000000000012575023701015646 5ustar iuliusiuliusinn-2.6.0/storage/buffindexed/ovmethod.mk0000644000175200017520000000074412575023702020032 0ustar iuliusiulius# This rule requires a compiler that supports -o with -c. Since it's normally # used by developers, that should be acceptable. buffindexed/buffindexed_d.$(EXTOBJ): buffindexed/buffindexed.c $(LIBCC) $(CFLAGS) -DBUFF_DEBUG -c -o $@ buffindexed/buffindexed.c buffindexed/buffindexed_d: buffindexed/buffindexed_d.$(EXTOBJ) libstorage.$(EXTLIB) $(LIBHIST) $(LIBLD) $(LDFLAGS) -o $@ buffindexed/buffindexed_d.$(EXTOBJ) \ $(LIBSTORAGE) $(LIBHIST) $(LIBINN) $(STORAGE_LIBS) $(LIBS) inn-2.6.0/storage/buffindexed/shmem.c0000644000175200017520000002327612575023702017136 0ustar iuliusiulius/* ** $Id: shmem.c 9906 2015-06-22 18:00:00Z iulius $ */ /* shared memory control utility */ #include "config.h" #include "clibrary.h" #include #include #include #include #include #include #include #include #include "inn/messages.h" #include "inn/libinn.h" #include "shmem.h" #ifndef MAP_FAILED #define MAP_FAILED ((caddr_t)-1) #endif static int smcGetSemaphore(const char *name) { key_t kt = ftok( (char *)name, 0 ); int id = semget(kt, 0, S_IRWXU|S_IRWXG|S_IRWXO); if (id < 0) { syswarn("semget failed to get semaphore for %s", name); } return id; } static int smcCreateSemaphore(const char *name) { key_t kt = ftok( (char *)name, 0 ); int id = semget(kt, 2, IPC_CREAT|S_IRWXU|S_IRWXG|S_IRWXO); if (id < 0) { if (errno == EACCES || errno == EINVAL) { /* looks like a wrong semaphore exists. remove it. */ id = semget(kt, 0, S_IRWXU|S_IRWXG|S_IRWXO); if (id < 0) { /* couldn't even retrieve it. */ syswarn("cant get semaphore using %s", name); return id; } /* try to remove it */ #ifdef HAVE_UNION_SEMUN { union semun semArg; semArg.val = 1; if (semctl(id, 0, IPC_RMID, semArg) < 0) { syswarn("cant remove semaphore %s", name); return -1; } } #else if (semctl(id, 0, IPC_RMID, NULL) < 0) { syswarn("cant remove semaphore %s", name); return -1; } #endif /* and retry creating it */ id = semget(kt, 2, IPC_CREAT|S_IRWXU|S_IRWXG|S_IRWXO); } } if (id < 0) syswarn("cant create semaphore using %s", name); return id; } int smcGetExclusiveLock(smcd_t *this) { struct sembuf sops[3] = { {0, 0, SEM_UNDO}, /* wait for exclusive lock. */ {0, 1, SEM_UNDO}, /* lock */ {1, 0, SEM_UNDO} /* wait for shared lock */ }; /* Get a lock for the buffer. Try again if it fails because our SIGHUP may interrupt this semop() call */ if (semop(this->semap, sops, 3) < 0 && semop(this->semap, sops, 3) < 0) { syswarn("semop failed to getExclusiveLock"); return(-1); } return(0); } int smcGetSharedLock(smcd_t *this) { struct sembuf sops[2] = { {0, 0, SEM_UNDO}, /* wait for exclusive lock. */ {1, 1, SEM_UNDO} /* increase access count */ }; /* Get a lock for the buffer. Try again if it fails because our SIGHUP may interrupt this semop() call */ if (semop(this->semap, sops, 2) < 0 && semop(this->semap, sops, 2) < 0) { syswarn("semop failed to getSharedLock"); return(-1); } return(0); } int smcReleaseSharedLock(smcd_t *this) { struct sembuf sops = { 1, -1, SEM_UNDO|IPC_NOWAIT }; /* Release the lock */ if (semop(this->semap, &sops, 1) < 0) { syswarn("semop failed to release shared lock"); return(-1); } return(0); } int smcReleaseExclusiveLock(smcd_t *this) { struct sembuf sops = { 0, -1, SEM_UNDO|IPC_NOWAIT }; /* Release the lock */ if (semop(this->semap, &sops, 1) < 0) { syswarn("semop failed to release exclusive lock"); return(-1); } return(0); } /* ** Get an existing shared memory buffer */ smcd_t* smcGetShmemBuffer(const char *name, int size) { int shmid, semap; smcd_t *this; caddr_t addr; key_t fk = ftok( (char *)name, 0 ); /* create shared memory buffer */ shmid = shmget(fk, size, S_IRWXU|S_IRGRP|S_IROTH); if (shmid < 0) { /* this is normal */ return NULL; } /* attach to shared memory buffer */ if ((addr = (caddr_t)shmat(shmid,0,0)) == MAP_FAILED) { syswarn("cant attach shared memory"); if (shmctl(shmid, IPC_RMID, 0) < 0) syswarn("cant remove shared memory"); return NULL; } /* Get control semaphore */ if ((semap = smcGetSemaphore(name)) < 0) { warn("failed to get semaphore for key %s", name); if (shmdt(addr) < 0) syswarn("cant detach shared memory"); if (shmctl(shmid, IPC_RMID, 0) < 0) syswarn("cant remove shared memory"); return NULL; } this = xmalloc(sizeof(smcd_t)); this->addr = addr; this->size = size; this->shmid = shmid; this->semap = semap; /* This makes news log file huge if enabled */ debug("got shmid %d semap %d addr %p size %d", shmid, semap, (void *) addr, size); return this; } /* ** Create a shared memory buffer */ smcd_t* smcCreateShmemBuffer(const char *name, int size) { int shmid, semap; smcd_t *this; caddr_t addr; key_t fk = ftok( (char *)name, 0 ); /* create shared memory buffer */ shmid = shmget(fk, size, IPC_CREAT|S_IRWXU|S_IRGRP|S_IROTH); if (shmid < 0) { /* try to get existing segment */ shmid = shmget(fk, 4, S_IRWXU|S_IRGRP|S_IROTH); if (shmid >= 0) { syswarn("shmem segment already exists name %s", name); /* try to delete old segment */ if (shmctl(shmid, IPC_RMID, 0) < 0) { syswarn("cant delete old memory segment"); return NULL; } notice("recreating another shmem segment"); shmid = shmget(fk, size, IPC_CREAT|S_IRWXU|S_IRGRP|S_IROTH); } } if (shmid < 0) { syswarn("cant create shared memory segment"); return NULL; } /* attach to shared memory buffer */ if ((addr = (caddr_t)shmat(shmid,0,0)) == MAP_FAILED) { syswarn("cant attach shared memory"); if (shmctl(shmid, IPC_RMID, 0) < 0) syswarn("cant remove shared memory"); return NULL; } /* clear the data */ memset( addr, 0, size ); /* Create control semaphore */ if ((semap = smcCreateSemaphore(name)) < 0) { warn("failed to create semaphore for %s", name); if (shmdt(addr) < 0) syswarn("cant detach shared memory"); if (shmctl(shmid, IPC_RMID, 0) < 0) syswarn("cant remove shared memory"); return NULL; } this = xmalloc(sizeof(smcd_t)); this->addr = addr; this->size = size; this->shmid = shmid; this->semap = semap; debug("created shmid %d semap %d addr %p size %d", shmid, semap, (void *) addr, size); return this; } void smcClose( smcd_t *this ) { struct shmid_ds buf; if (this->addr != MAP_FAILED) { /* detach shared memory segment */ if (shmdt(this->addr) < 0) syswarn("cant detach shared memory segment"); this->addr = MAP_FAILED; } /* delete shm if no one has attached it */ if ( shmctl(this->shmid, IPC_STAT, &buf) < 0) { syswarn("cant stat shmid %d", this->shmid); } else if ( buf.shm_nattch == 0 ) { if (shmctl(this->shmid, IPC_RMID, 0) < 0) syswarn("cant delete shmid %d", this->shmid); else debug("shmid %d deleted", this->shmid); /* Delete the semaphore too */ #ifdef HAVE_UNION_SEMUN { union semun semArg; semArg.val = 0; if (semctl(this->semap, 0, IPC_RMID, semArg) < 0) { syswarn("can't remove semaphore %d", this->semap); } } #else if (semctl(this->semap, 0, IPC_RMID, NULL) < 0) { syswarn("can't remove semaphore %d", this->semap); } #endif } free( this ); } #ifdef _TEST_ /* Check if the testfile exists. If the file is absent create one with size 1M, and fill the contents with all zero. for (i=0; i<100; i++) add 1 to the content; */ static const char* testfile = "testfile"; #define TESTSIZE ( 1024 * 1024 ) #define MAXCOUNT 100 static smcd_t *this; static void myexit( void ) { if( this ) { smcClose( this ); } } int main( int argc, char** argv ) { struct stat st; int fd, i, k; int *x; int len, xmin, xmax; struct flock fl; atexit( myexit ); openlog( "shmemtest", LOG_PID, LOG_DAEMON ); /* open the testfile */ fd = creat(testfile, 0660); if( fd < 0 ) { printf( "cant open %s", testfile ); exit(1); } /* lock the file */ if( flock( fd, LOCK_EX ) < 0 ) { printf( "cant get flock" ); exit(1); } /* try to get shared memory buffer */ this = smcGetShmemBuffer(testfile, TESTSIZE); if( !this ) { /* because there's no shared memory, create one. */ this = smcCreateShmemBuffer(testfile, TESTSIZE); if( !this ) { printf( "cant create shmem buffer" ); exit(1); } } /* unlock the file */ if( flock( fd, LOCK_UN ) < 0 ) { printf( "cant unflock %s", testfile ); exit(1); } x = (int *)this->addr; len = this->size / sizeof(int); for( k=0; kaddr, this->size) != this->size ) { printf( "cant write" ); exit(1); } if( smcReleaseExclusiveLock( this ) ) { printf( "cant release exclusive lock" ); exit(1); } } /* write the minimum and maximum */ xmin = xmax = x[0]; for( i=1; i xmax ) xmax = x[i]; } printf( "min %d max %d\n", xmin, xmax ); return(0); } #endif /* _TEST_ */ inn-2.6.0/storage/buffindexed/buffindexed.c0000644000175200017520000020650012575023702020301 0ustar iuliusiulius/* $Id: buffindexed.c 9902 2015-06-20 14:10:08Z iulius $ ** ** Overview buffer and index method. */ /* ** Buffindexed using shared memory on ovbuff by Sang-yong Suh ** ** During the recent discussions in inn-workers, Alex Kiernan found ** that inn_lock_range() is not working for MMAPed file. This explains ** why buffindexed has long outstanding bugs such as "could not MMAP...". ** ** This version corrects the file locking error by using shared memory. ** The bitfield of each buffer file is loaded into memory, and is shared ** by all programs such as innd, expireover, makehistory, and overchan. ** The locking problem is handled by semaphore. */ #include "config.h" #include "clibrary.h" #include "portable/mmap.h" #include #include #include #include #if HAVE_LIMITS_H # include #endif #include #include #include "inn/innconf.h" #include "inn/messages.h" #include "inn/libinn.h" #include "inn/ov.h" #include "inn/paths.h" #include "ovinterface.h" #include "inn/storage.h" #include "inn/fdflag.h" /* Yes. I know that it violates INN coding style. However, this allows * me to compile this new version without reconfiguring INN. * If all goes well, shmem.c should go to $INN/lib, and shmem.h should * go to $INN/include. */ #include "shmem.h" #include "buffindexed.h" #define OVBUFF_MAGIC "ovbuff" #define OVBUFF_VERSION 2 /* ** Because ovbuff bitfields are residing in memory, we don't have to ** do file write for each update. Instead we'll do it at every ** OVBUFF_SYNC_COUNT updates. */ #define OVBUFF_SYNC_COUNT (innconf->icdsynccount * 10 + 1) /* #define OVBUFF_SYNC_COUNT 1 */ /* ovbuff header */ #define OVBUFFMASIZ 8 #define OVBUFFNASIZ 16 #define OVBUFFLASIZ 16 #define OVBUFFPASIZ 64 #define OVMAXCYCBUFFNAME 8 #define OV_HDR_PAGESIZE 16384 #define OV_BEFOREBITF (1 * OV_BLOCKSIZE) #define OV_BLOCKSIZE 8192 #define OV_FUDGE 1024 #define OV_OFFSET(block) (block*(off_t) OV_BLOCKSIZE) /* ovblock pointer */ typedef struct _OV { unsigned int blocknum; short index; } OV; /* ovbuff header */ typedef struct { char magic[OVBUFFMASIZ]; char path[OVBUFFPASIZ]; char indexa[OVBUFFLASIZ]; /* ASCII version of index */ char lena[OVBUFFLASIZ]; /* ASCII version of len */ char totala[OVBUFFLASIZ]; /* ASCII version of total */ char useda[OVBUFFLASIZ]; /* ASCII version of used */ char freea[OVBUFFLASIZ]; /* ASCII version of free */ char updateda[OVBUFFLASIZ]; /* ASCII version of updated */ /* * The following parts will be synced to the bitfield */ int version; /* magic version number */ unsigned int freeblk; /* next free block number */ unsigned int usedblk; /* number of used blocks */ } OVBUFFHEAD; /* ovbuff info */ typedef struct _OVBUFF { unsigned int index; /* ovbuff (partition or file) */ char path[OVBUFFPASIZ]; /* Path to file */ int fd; /* file descriptor for this ovbuff */ off_t len; /* Length of writable area, in bytes */ off_t base; /* Offset (relative to byte 0 of file) to base block */ unsigned int freeblk; /* next free block number no freeblk left if equals totalblk */ unsigned int totalblk; /* number of total blocks */ unsigned int usedblk; /* number of used blocks */ time_t updated; /* Time of last update to header */ void * bitfield; /* Bitfield for ovbuff block in use */ unsigned long dirty; /* OVBUFFHEAD dirty count */ struct _OVBUFF *next; /* next ovbuff */ int nextchunk; /* next chunk */ smcd_t *smc; /* shared mem control data */ #ifdef OV_DEBUG struct ov_trace_array *trace; #endif /* OV_DEBUG */ } OVBUFF; typedef struct _OVINDEXHEAD { OV next; /* next block */ ARTNUM low; /* lowest article number in the index */ ARTNUM high; /* highest article number in the index */ } OVINDEXHEAD; typedef struct _OVINDEX { ARTNUM artnum; /* article number */ unsigned int blocknum; /* overview data block number */ short index; /* overview data block index */ TOKEN token; /* token for this article */ off_t offset; /* offset from the top in the block */ int len; /* length of the data */ time_t arrived; /* arrived time of article */ time_t expires; /* expire time of article */ } OVINDEX; #define OVINDEXMAX ((OV_BLOCKSIZE-sizeof(OVINDEXHEAD))/sizeof(OVINDEX)) typedef struct _OVBLOCK { OVINDEXHEAD ovindexhead; /* overview index header */ OVINDEX ovindex[OVINDEXMAX]; /* overview index */ } OVBLOCK; typedef struct _OVBLKS { OVBLOCK *ovblock; void * addr; int len; OV indexov; } OVBLKS; /* Data structure for specifying a location in the group index */ typedef struct { int recno; /* Record number in group index */ } GROUPLOC; #ifdef OV_DEBUG struct ov_trace { time_t occupied; time_t freed; GROUPLOC gloc; }; #define OV_TRACENUM 10 struct ov_trace_array { int max; int cur; struct ov_trace *ov_trace; }; struct ov_name_table { char *name; int recno; struct ov_name_table *next; }; static struct ov_name_table *name_table = NULL; #endif /* OV_DEBUG */ #define GROUPHEADERHASHSIZE (16 * 1024) #define GROUPHEADERMAGIC (~(0xf1f0f33d)) typedef struct { int magic; GROUPLOC hash[GROUPHEADERHASHSIZE]; GROUPLOC freelist; } GROUPHEADER; /* The group is matched based on the MD5 of the grouname. This may prove to be inadequate in the future, if so, the right thing to do is to is probably just to add a SHA1 hash into here also. We get a really nice benefit from this being fixed length, we should try to keep it that way. */ typedef struct { HASH hash; /* MD5 hash of the group name */ HASH alias; /* If not empty then this is the hash of the group that this group is an alias for */ ARTNUM high; /* High water mark in group */ ARTNUM low; /* Low water mark in group */ int count; /* Number of articles in group */ int flag; /* Posting/Moderation Status */ time_t expired; /* When last expiry */ time_t deleted; /* When this was deleted, 0 otherwise */ GROUPLOC next; /* Next block in this chain */ OV baseindex; /* base index buff */ OV curindex; /* current index buff */ int curindexoffset; /* current index offset for this ovbuff */ ARTNUM curhigh; /* High water mark in group */ ARTNUM curlow; /* Low water mark in group */ OV curdata; /* current offset for this ovbuff */ off_t curoffset; /* current offset for this ovbuff */ } GROUPENTRY; typedef struct _GIBLIST { OV ov; struct _GIBLIST *next; } GIBLIST; typedef struct _GDB { OV datablk; void * addr; void * data; int len; bool mmapped; struct _GDB *next; } GROUPDATABLOCK; typedef struct { char *group; ARTNUM lo; ARTNUM hi; int cur; bool needov; GROUPLOC gloc; int count; GROUPDATABLOCK gdb; /* used for caching current block */ } OVSEARCH; #define GROUPDATAHASHSIZE 25 static GROUPDATABLOCK *groupdatablock[GROUPDATAHASHSIZE]; typedef enum {PREPEND_BLK, APPEND_BLK} ADDINDEX; typedef enum {SRCH_FRWD, SRCH_BKWD} SRCH; #define _PATH_OVBUFFCONFIG "buffindexed.conf" static long pagesize = 0; static OVBUFF *ovbufftab = NULL; static OVBUFF *ovbuffnext = NULL; static int GROUPfd; static GROUPHEADER *GROUPheader = NULL; static GROUPENTRY *GROUPentries = NULL; static int GROUPcount = 0; static GROUPLOC GROUPemptyloc = { -1 }; #define NULLINDEX (-1) static OV ovnull = { 0, NULLINDEX }; typedef unsigned long ULONG; static ULONG onarray[64], offarray[64]; static int longsize = sizeof(long); static bool Nospace; static bool Needunlink; static bool Cutofflow; static bool Cache; static OVSEARCH *Cachesearch; static int ovbuffmode; static GROUPLOC GROUPnewnode(void); static bool GROUPremapifneeded(GROUPLOC loc); static void GROUPLOCclear(GROUPLOC *loc); static bool GROUPLOCempty(GROUPLOC loc); static bool GROUPlockhash(enum inn_locktype type); static bool GROUPlock(GROUPLOC gloc, enum inn_locktype type); static off_t GROUPfilesize(int count); static bool GROUPexpand(int mode); static void *ovopensearch(const char *group, ARTNUM low, ARTNUM high, bool needov); static void ovclosesearch(void *handle, bool freeblock); static OVINDEX *Gib; static GIBLIST *Giblist; static int Gibcount; #ifdef MMAP_MISSES_WRITES #define PWRITE(fd, buf, nbyte, offset) mmapwrite(fd, buf, nbyte, offset) #else #define PWRITE(fd, buf, nbyte, offset) pwrite(fd, buf, nbyte, offset) #endif #ifdef MMAP_MISSES_WRITES /* With HP/UX, you definitely do not want to mix mmap-accesses of a file with read()s and write()s of the same file */ static off_t mmapwrite(int fd, void *buf, off_t nbyte, off_t offset) { int pagefudge, len; off_t mmapoffset; void * addr; pagefudge = offset % pagesize; mmapoffset = offset - pagefudge; len = pagefudge + nbyte; if ((addr = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, mmapoffset)) == MAP_FAILED) { return -1; } memcpy(addr+pagefudge, buf, nbyte); munmap(addr, len); return nbyte; } #endif /* MMAP_MISSES_WRITES */ static bool ovparse_part_line(char *l) { char *p; struct stat sb; off_t len, base; int tonextblock; OVBUFF *ovbuff, *tmp = ovbufftab; /* ovbuff partition name */ if ((p = strchr(l, ':')) == NULL || p - l <= 0 || p - l > OVMAXCYCBUFFNAME - 1) { warn("buffindexed: bad index in line '%s'", l); return false; } *p = '\0'; ovbuff = xmalloc(sizeof(OVBUFF)); ovbuff->index = strtoul(l, NULL, 10); for (; tmp != (OVBUFF *)NULL; tmp = tmp->next) { if (tmp->index == ovbuff->index) { warn("buffindexed: dupulicate index in line '%s'", l); free(ovbuff); return false; } } l = ++p; /* Path to ovbuff partition */ if ((p = strchr(l, ':')) == NULL || p - l <= 0 || p - l > OVBUFFPASIZ - 1) { warn("buffindexed: bad pathname in line '%s'", l); free(ovbuff); return false; } *p = '\0'; memset(ovbuff->path, '\0', OVBUFFPASIZ); strlcpy(ovbuff->path, l, OVBUFFPASIZ); if (stat(ovbuff->path, &sb) < 0) { warn("buffindexed: file '%s' does not exist, ignoring '%d'", ovbuff->path, ovbuff->index); free(ovbuff); return false; } l = ++p; /* Length/size of symbolic partition in KB */ len = strtoul(l, NULL, 10) * (off_t) 1024; /* ** The minimum article offset will be the size of the bitfield itself, ** len / (blocksize * 8), plus however many additional blocks the OVBUFFHEAD ** external header occupies ... then round up to the next block. */ base = len / (OV_BLOCKSIZE * 8) + OV_BEFOREBITF; tonextblock = OV_HDR_PAGESIZE - (base & (OV_HDR_PAGESIZE - 1)); ovbuff->base = base + tonextblock; if (S_ISREG(sb.st_mode) && (len != sb.st_size || ovbuff->base > sb.st_size)) { if (len != sb.st_size) notice("buffindexed: length mismatch '%lu' for index '%d' (%lu bytes)", (unsigned long) len, ovbuff->index, (unsigned long) sb.st_size); if (ovbuff->base > sb.st_size) notice("buffindexed: length must be at least '%lu' for index '%d' (%lu bytes)", (unsigned long) ovbuff->base, ovbuff->index, (unsigned long) sb.st_size); free(ovbuff); return false; } ovbuff->len = len; ovbuff->fd = -1; ovbuff->next = (OVBUFF *)NULL; ovbuff->dirty = 0; ovbuff->bitfield = NULL; ovbuff->nextchunk = 1; if (ovbufftab == (OVBUFF *)NULL) ovbufftab = ovbuff; else { for (tmp = ovbufftab; tmp->next != (OVBUFF *)NULL; tmp = tmp->next); tmp->next = ovbuff; } return true; } /* ** ovbuffread_config() -- Read the overview partition/file configuration file. */ static bool ovbuffread_config(void) { char *path, *config, *from, *to, **ctab = (char **)NULL; int ctab_free = 0; /* Index to next free slot in ctab */ int ctab_i; path = concatpath(innconf->pathetc, _PATH_OVBUFFCONFIG); config = ReadInFile(path, NULL); if (config == NULL) { warn("buffindexed: cannot read %s", path); free(config); free(path); return false; } free(path); for (from = to = config; *from; ) { if (*from == '#') { /* Comment line? */ while (*from && *from != '\n') from++; /* Skip past it */ from++; continue; /* Back to top of loop */ } if (*from == '\n') { /* End or just a blank line? */ from++; continue; /* Back to top of loop */ } if (ctab_free == 0) ctab = xmalloc(sizeof(char *)); else ctab = xrealloc(ctab, (ctab_free + 1) * sizeof(char *)); /* If we're here, we've got the beginning of a real entry */ ctab[ctab_free++] = to = from; while (1) { if (*from && *from == '\\' && *(from + 1) == '\n') { from += 2; /* Skip past backslash+newline */ while (*from && isspace((unsigned char) *from)) from++; continue; } if (*from && *from != '\n') *to++ = *from++; if (*from == '\n') { *to++ = '\0'; from++; break; } if (! *from) break; } } for (ctab_i = 0; ctab_i < ctab_free; ctab_i++) { if (!ovparse_part_line(ctab[ctab_i])) { free(config); free(ctab); return false; } } free(config); free(ctab); if (ovbufftab == (OVBUFF *)NULL) { warn("buffindexed: no buffindexed defined"); return false; } return true; } static char hextbl[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; static char *offt2hex(off_t offset, bool leadingzeros) { static char buf[24]; char *p; if (sizeof(off_t) <= sizeof(unsigned long)) { snprintf(buf, sizeof(buf), (leadingzeros) ? "%016lx" : "%lx", (unsigned long) offset); } else { int i; for (i = 0; i < OVBUFFLASIZ; i++) buf[i] = '0'; /* Pad with zeros to start */ for (i = OVBUFFLASIZ - 1; i >= 0; i--) { buf[i] = hextbl[offset & 0xf]; offset >>= 4; } } if (!leadingzeros) { for (p = buf; *p == '0'; p++) ; if (*p != '\0') return p; else return p - 1; /* We converted a "0" and then bypassed all the zeros */ } else return buf; } static off_t hex2offt(char *hex) { if (sizeof(off_t) <= 4) { unsigned long rpofft; sscanf(hex, "%lx", &rpofft); return rpofft; } else { char diff; off_t n = 0; for (; *hex != '\0'; hex++) { if (*hex >= '0' && *hex <= '9') diff = '0'; else if (*hex >= 'a' && *hex <= 'f') diff = 'a' - 10; else if (*hex >= 'A' && *hex <= 'F') diff = 'A' - 10; else { /* ** We used to have a syslog() message here, but the case ** where we land here because of a ":" happens, er, often. */ break; } n += (*hex - diff); if (isalnum((unsigned char) *(hex + 1))) n <<= 4; } return n; } } static void ovreadhead(OVBUFF *ovbuff) { OVBUFFHEAD *x = (OVBUFFHEAD *)ovbuff->bitfield; ovbuff->freeblk = x->freeblk; ovbuff->usedblk = x->usedblk; return; } static void ovflushhead(OVBUFF *ovbuff) { OVBUFFHEAD rpx; /* skip time consuming data conversion and write call */ if (ovbuff->dirty < OVBUFF_SYNC_COUNT) { OVBUFFHEAD *x = (OVBUFFHEAD *)ovbuff->bitfield; x->freeblk = ovbuff->freeblk; x->usedblk = ovbuff->usedblk; return; } memset(&rpx, 0, sizeof(OVBUFFHEAD)); ovbuff->updated = time(NULL); strncpy(rpx.magic, OVBUFF_MAGIC, strlen(OVBUFF_MAGIC)); strncpy(rpx.path, ovbuff->path, OVBUFFPASIZ); /* Don't use sprintf() directly ... the terminating '\0' causes grief */ strncpy(rpx.indexa, offt2hex(ovbuff->index, true), OVBUFFLASIZ); strncpy(rpx.lena, offt2hex(ovbuff->len, true), OVBUFFLASIZ); strncpy(rpx.totala, offt2hex(ovbuff->totalblk, true), OVBUFFLASIZ); strncpy(rpx.useda, offt2hex(ovbuff->usedblk, true), OVBUFFLASIZ); strncpy(rpx.freea, offt2hex(ovbuff->freeblk, true), OVBUFFLASIZ); strncpy(rpx.updateda, offt2hex(ovbuff->updated, true), OVBUFFLASIZ); rpx.version = OVBUFF_VERSION; rpx.freeblk = ovbuff->freeblk; rpx.usedblk = ovbuff->usedblk; memcpy(ovbuff->bitfield, &rpx, sizeof(OVBUFFHEAD)); if (pwrite(ovbuff->fd, ovbuff->bitfield, ovbuff->base, 0) != ovbuff->base) syswarn("buffindexed: ovflushhead: cant flush on %s", ovbuff->path); ovbuff->dirty = 0; return; } static bool ovlock(OVBUFF *ovbuff, enum inn_locktype type) { int ret; smcd_t *smc = ovbuff->smc; if (type == INN_LOCK_WRITE) { ret = smcGetExclusiveLock(smc); smc->locktype = (int)INN_LOCK_WRITE; } else if (type == INN_LOCK_READ) { ret = smcGetSharedLock(smc); smc->locktype = (int)INN_LOCK_READ; } else if (smc->locktype == (int)INN_LOCK_WRITE) { ret = smcReleaseExclusiveLock(smc); } else { ret = smcReleaseSharedLock(smc); } return (ret == 0); } static bool ovbuffinit_disks(void) { OVBUFF *ovbuff = ovbufftab; char buf[64]; OVBUFFHEAD *rpx, dpx; int fd; unsigned int i; off_t tmpo; smcd_t *smc; static bool atexit_registered = false; /* * Register the exit callback to sync the bitfield to the disk */ if (!atexit_registered) { atexit(buffindexed_close); atexit_registered = true; } /* ** Discover the state of our ovbuffs. If any of them are in icky shape, ** duck shamelessly & return false. */ for (; ovbuff != (OVBUFF *)NULL; ovbuff = ovbuff->next) { if (ovbuff->fd < 0) { if ((fd = open(ovbuff->path, ovbuffmode & OV_WRITE ? O_RDWR : O_RDONLY)) < 0) { syswarn("buffindexed: ERROR opening '%s'", ovbuff->path); return false; } else { fdflag_close_exec(fd, true); ovbuff->fd = fd; } } /* get shared memory buffer */ smc = smcGetShmemBuffer(ovbuff->path, ovbuff->base); if (!smc) { /* No shared memory exists, create one. */ smc = smcCreateShmemBuffer(ovbuff->path, ovbuff->base); if (!smc) { syswarn("buffindexed: ovinitdisks: cant create shmem for %s len %lu", ovbuff->path, (unsigned long) ovbuff->base); return false; } } ovbuff->smc = smc; ovbuff->bitfield = smc->addr; rpx = (OVBUFFHEAD *)ovbuff->bitfield; /* lock the buffer */ ovlock(ovbuff, ovbuffmode & OV_WRITE ? INN_LOCK_WRITE : INN_LOCK_READ); if (pread(ovbuff->fd, &dpx, sizeof(OVBUFFHEAD), 0) < 0) { syswarn("buffindexed: cant read from %s", ovbuff->path); ovlock(ovbuff, INN_LOCK_UNLOCK); return false; } /* * check validity of the disk data */ if (strncmp(dpx.magic, OVBUFF_MAGIC, strlen(OVBUFF_MAGIC)) == 0 && strncmp(dpx.path, ovbuff->path, OVBUFFPASIZ) == 0 ) { strncpy(buf, dpx.indexa, OVBUFFLASIZ); buf[OVBUFFLASIZ] = '\0'; i = hex2offt(buf); if (i != ovbuff->index) { warn("buffindexed: Mismatch: index '%d' for buffindexed %s", i, ovbuff->path); ovlock(ovbuff, INN_LOCK_UNLOCK); return false; } strncpy(buf, dpx.lena, OVBUFFLASIZ); buf[OVBUFFLASIZ] = '\0'; tmpo = hex2offt(buf); if (tmpo != ovbuff->len) { warn("buffindexed: Mismatch: read 0x%s length for buffindexed %s", offt2hex(tmpo, false), ovbuff->path); ovlock(ovbuff, INN_LOCK_UNLOCK); return false; } /* * compare shared memory with disk data. */ if (strncmp(dpx.magic, rpx->magic, strlen(OVBUFF_MAGIC)) != 0 || strncmp(dpx.path, rpx->path, OVBUFFPASIZ) != 0 || strncmp(dpx.indexa, rpx->indexa, OVBUFFLASIZ) != 0 || strncmp(dpx.lena, rpx->lena, OVBUFFLASIZ) != 0 ) { /* * Load shared memory with disk data. */ if (pread(ovbuff->fd, rpx, ovbuff->base, 0) < 0) { syswarn("buffindexed: cant read from %s", ovbuff->path); ovlock(ovbuff, INN_LOCK_UNLOCK); return false; } } strncpy(buf, dpx.totala, OVBUFFLASIZ); buf[OVBUFFLASIZ] = '\0'; ovbuff->totalblk = hex2offt(buf); if (rpx->version == 0) { /* no binary data available. use character data */ strncpy(buf, rpx->useda, OVBUFFLASIZ); buf[OVBUFFLASIZ] = '\0'; ovbuff->usedblk = hex2offt(buf); strncpy(buf, rpx->freea, OVBUFFLASIZ); buf[OVBUFFLASIZ] = '\0'; ovbuff->freeblk = hex2offt(buf); } else { /* use binary data. The first reason is the speed. and the second reason is the other partner is not synced. */ ovbuff->usedblk = rpx->usedblk; ovbuff->freeblk = rpx->freeblk; } Needunlink = false; } else { /* * Initialize the contents of the shared memory */ memset(rpx, 0, ovbuff->base); ovbuff->totalblk = (ovbuff->len - ovbuff->base)/OV_BLOCKSIZE; if (ovbuff->totalblk < 1) { warn("buffindexed: too small length '%lu' for buffindexed %s", (unsigned long) ovbuff->len, ovbuff->path); ovlock(ovbuff, INN_LOCK_UNLOCK); return false; } ovbuff->usedblk = 0; ovbuff->freeblk = 0; ovbuff->updated = 0; ovbuff->dirty = OVBUFF_SYNC_COUNT + 1; notice("buffindexed: no magic cookie found for ovbuff %d, initializing", ovbuff->index); ovflushhead(ovbuff); } #ifdef OV_DEBUG ovbuff->trace = xcalloc(ovbuff->totalblk, sizeof(ov_trace_array)); #endif /* OV_DEBUG */ ovlock(ovbuff, INN_LOCK_UNLOCK); } return true; } static int ovusedblock(OVBUFF *ovbuff, int blocknum, bool set_operation, bool setbitvalue) { off_t longoffset; int bitoffset; /* From the 'left' side of the long */ ULONG bitlong, mask; longoffset = blocknum / (sizeof(long) * 8); bitoffset = blocknum % (sizeof(long) * 8); bitlong = *((ULONG *) ovbuff->bitfield + (OV_BEFOREBITF / sizeof(long)) + longoffset); if (set_operation) { if (setbitvalue) { mask = onarray[bitoffset]; bitlong |= mask; } else { mask = offarray[bitoffset]; bitlong &= mask; } *((ULONG *) ovbuff->bitfield + (OV_BEFOREBITF / sizeof(long)) + longoffset) = bitlong; return 2; /* XXX Clean up return semantics */ } /* It's a read operation */ mask = onarray[bitoffset]; /* return bitlong & mask; doesn't work if sizeof(ulong) > sizeof(int) */ if (bitlong & mask) return 1; else return 0; } static void ovnextblock(OVBUFF *ovbuff) { int i, j, last, lastbit, left; ULONG *table; last = ovbuff->totalblk/(sizeof(long) * 8); if ((left = ovbuff->totalblk % (sizeof(long) * 8)) != 0) { last++; } table = ((ULONG *) ovbuff->bitfield + (OV_BEFOREBITF / sizeof(long))); /* For tighter placement look for unused block from the beginning on * every run. */ for (i = 0 ; i < last ; i++) { if ((table[i] ^ ~0) != 0) break; } if (i == last) { ovbuff->freeblk = ovbuff->totalblk; return; } if ((i - 1) >= 0 && (last - 1 == i) && left != 0) { lastbit = left; } else { lastbit = sizeof(long) * 8; } for (j = 0 ; j < lastbit ; j++) { if ((table[i] & onarray[j]) == 0) break; } if (j == lastbit) { ovbuff->freeblk = ovbuff->totalblk; return; } ovbuff->freeblk = i * sizeof(long) * 8 + j; ovbuff->nextchunk = i + 1; if (i == last) ovbuff->nextchunk = 0; return; } static OVBUFF *getovbuff(OV ov) { OVBUFF *ovbuff = ovbufftab; for (; ovbuff != NULL; ovbuff = ovbuff->next) { if (ovbuff->index == (unsigned int) ov.index) return ovbuff; } return NULL; } #ifdef OV_DEBUG static OV ovblocknew(GROUPENTRY *ge) { #else static OV ovblocknew(void) { #endif /* OV_DEBUG */ OVBUFF *ovbuff; OV ov; bool done = false; #ifdef OV_DEBUG int recno; struct ov_trace_array *trace; #endif /* OV_DEBUG */ if (ovbuffnext == NULL) ovbuffnext = ovbufftab; /* * We will try to recover broken overview possibly due to unsync. * The recovering is inactive for OV_DEBUG mode. */ retry: for (ovbuff = ovbuffnext ; ovbuff != (OVBUFF *)NULL ; ovbuff = ovbuff->next) { ovlock(ovbuff, INN_LOCK_WRITE); ovreadhead(ovbuff); if (ovbuff->totalblk != ovbuff->usedblk && ovbuff->freeblk == ovbuff->totalblk) { ovnextblock(ovbuff); } if (ovbuff->totalblk == ovbuff->usedblk || ovbuff->freeblk == ovbuff->totalblk) { /* no space left for this ovbuff */ ovlock(ovbuff, INN_LOCK_UNLOCK); continue; } break; } if (ovbuff == NULL) { for (ovbuff = ovbufftab ; ovbuff != ovbuffnext ; ovbuff = ovbuff->next) { ovlock(ovbuff, INN_LOCK_WRITE); ovreadhead(ovbuff); if (ovbuff->totalblk == ovbuff->usedblk || ovbuff->freeblk == ovbuff->totalblk) { /* no space left for this ovbuff */ ovlock(ovbuff, INN_LOCK_UNLOCK); continue; } break; } if (ovbuff == ovbuffnext) { Nospace = true; return ovnull; } } #ifdef OV_DEBUG recno = ((char *)ge - (char *)&GROUPentries[0])/sizeof(GROUPENTRY); if (ovusedblock(ovbuff, ovbuff->freeblk, false, true)) { warn("buffindexed: 0x%08x trying to occupy new block(%d, %d), but already occupied", recno, ovbuff->index, ovbuff->freeblk); buffindexed_close(); abort(); } trace = &ovbuff->trace[ovbuff->freeblk]; if (trace->ov_trace == NULL) { trace->ov_trace = xcalloc(OV_TRACENUM, sizeof(struct ov_trace)); trace->max = OV_TRACENUM; } else if (trace->cur + 1 == trace->max) { trace->max += OV_TRACENUM; trace->ov_trace = xrealloc(trace->ov_trace, trace->max * sizeof(struct ov_trace)); memset(&trace->ov_trace[trace->cur], '\0', sizeof(struct ov_trace) * (trace->max - trace->cur)); } if (trace->ov_trace[trace->cur].occupied != 0) { trace->cur++; } trace->ov_trace[trace->cur].gloc.recno = recno; trace->ov_trace[trace->cur].occupied = time(NULL); #endif /* OV_DEBUG */ ov.index = ovbuff->index; ov.blocknum = ovbuff->freeblk; #ifndef OV_DEBUG if (ovusedblock(ovbuff, ovbuff->freeblk, false, true)) { notice("buffindexed: fixing invalid free block(%d, %d).", ovbuff->index, ovbuff->freeblk); } else done = true; #endif /* OV_DEBUG */ /* mark it as allocated */ ovusedblock(ovbuff, ov.blocknum, true, true); ovnextblock(ovbuff); ovbuff->usedblk++; ovbuff->dirty++; ovflushhead(ovbuff); ovlock(ovbuff, INN_LOCK_UNLOCK); ovbuffnext = ovbuff->next; if (ovbuffnext == NULL) ovbuffnext = ovbufftab; if (!done) goto retry; return ov; } #ifdef OV_DEBUG static void ovblockfree(OV ov, GROUPENTRY *ge) { #else static void ovblockfree(OV ov) { #endif /* OV_DEBUG */ OVBUFF *ovbuff; #ifdef OV_DEBUG int recno; struct ov_trace_array *trace; #endif /* OV_DEBUG */ if (ov.index == NULLINDEX) return; if ((ovbuff = getovbuff(ov)) == NULL) return; ovlock(ovbuff, INN_LOCK_WRITE); #ifdef OV_DEBUG recno = ((char *)ge - (char *)&GROUPentries[0])/sizeof(GROUPENTRY); if (!ovusedblock(ovbuff, ov.blocknum, false, false)) { warn("buffindexed: 0x%08x trying to free block(%d, %d), but already freed", recno, ov.index, ov.blocknum); buffindexed_close(); abort(); } trace = &ovbuff->trace[ov.blocknum]; if (trace->ov_trace == NULL) { trace->ov_trace = xcalloc(OV_TRACENUM, sizeof(struct ov_trace)); trace->max = OV_TRACENUM; } else if (trace->cur + 1 == trace->max) { trace->max += OV_TRACENUM; trace->ov_trace = xrealloc(trace->ov_trace, trace->max * sizeof(struct ov_trace)); memset(&trace->ov_trace[trace->cur], '\0', sizeof(struct ov_trace) * (trace->max - trace->cur)); } if (trace->ov_trace[trace->cur].freed != 0) { trace->cur++; } trace->ov_trace[trace->cur].freed = time(NULL); trace->ov_trace[trace->cur].gloc.recno = recno; trace->cur++; #endif /* OV_DEBUG */ #ifndef OV_DEBUG if (!ovusedblock(ovbuff, ov.blocknum, false, false)) { notice("buffindexed: trying to free block(%d, %d), but already freed.", ov.index, ov.blocknum); } #endif ovusedblock(ovbuff, ov.blocknum, true, false); ovreadhead(ovbuff); if (ovbuff->freeblk == ovbuff->totalblk) ovbuff->freeblk = ov.blocknum; ovbuff->usedblk--; ovbuff->dirty++; ovflushhead(ovbuff); ovlock(ovbuff, INN_LOCK_UNLOCK); return; } bool buffindexed_open(int mode) { char *groupfn; struct stat sb; int i, flag = 0; static int uninitialized = 1; ULONG on, off; if (uninitialized) { on = 1; off = on; off ^= ULONG_MAX; for (i = (longsize * 8) - 1; i >= 0; i--) { onarray[i] = on; offarray[i] = off; on <<= 1; off = on; off ^= ULONG_MAX; } uninitialized = 0; } ovbuffmode = mode; if (pagesize == 0) { pagesize = getpagesize(); if (pagesize == -1) { syswarn("buffindexed: getpagesize failed"); pagesize = 0; return false; } if ((pagesize > OV_HDR_PAGESIZE) || (OV_HDR_PAGESIZE % pagesize)) { warn("buffindexed: OV_HDR_PAGESIZE (%d) is not a multiple of pagesize (%ld)", OV_HDR_PAGESIZE, pagesize); return false; } } memset(&groupdatablock, '\0', sizeof(groupdatablock)); if (!ovbuffread_config()) { return false; } Needunlink = true; if (!ovbuffinit_disks()) { return false; } groupfn = concatpath(innconf->pathdb, "group.index"); if (Needunlink && unlink(groupfn) == 0) { notice("buffindexed: all buffers are brandnew, unlink '%s'", groupfn); } GROUPfd = open(groupfn, ovbuffmode & OV_WRITE ? O_RDWR | O_CREAT : O_RDONLY, 0660); if (GROUPfd < 0) { syswarn("buffindexed: Could not create %s", groupfn); free(groupfn); return false; } if (fstat(GROUPfd, &sb) < 0) { syswarn("buffindexed: Could not fstat %s", groupfn); free(groupfn); close(GROUPfd); return false; } if (sb.st_size > (off_t) sizeof(GROUPHEADER)) { if (mode & OV_READ) flag |= PROT_READ; if (mode & OV_WRITE) { /* * Note: below mapping of groupheader won't work unless we have * both PROT_READ and PROT_WRITE perms. */ flag |= PROT_WRITE|PROT_READ; } GROUPcount = (sb.st_size - sizeof(GROUPHEADER)) / sizeof(GROUPENTRY); GROUPheader = mmap(0, GROUPfilesize(GROUPcount), flag, MAP_SHARED, GROUPfd, 0); if (GROUPheader == MAP_FAILED) { syswarn("buffindexed: Could not mmap %s in buffindexed_open", groupfn); free(groupfn); close(GROUPfd); return false; } GROUPentries = (void *) &GROUPheader[1]; } else { GROUPcount = 0; if (!GROUPexpand(mode)) { free(groupfn); close(GROUPfd); return false; } } fdflag_close_exec(GROUPfd, true); free(groupfn); Cutofflow = false; return true; } static GROUPLOC GROUPfind(const char *group, bool Ignoredeleted) { HASH grouphash; unsigned int i; GROUPLOC loc; grouphash = Hash(group, strlen(group)); memcpy(&i, &grouphash, sizeof(i)); loc = GROUPheader->hash[i % GROUPHEADERHASHSIZE]; GROUPremapifneeded(loc); while (!GROUPLOCempty(loc)) { if (GROUPentries[loc.recno].deleted == 0 || Ignoredeleted) { if (memcmp(&grouphash, &GROUPentries[loc.recno].hash, sizeof(HASH)) == 0) { return loc; } } loc = GROUPentries[loc.recno].next; } return GROUPemptyloc; } bool buffindexed_groupstats(const char *group, int *lo, int *hi, int *count, int *flag) { GROUPLOC gloc; gloc = GROUPfind(group, false); if (GROUPLOCempty(gloc)) { return false; } GROUPlock(gloc, INN_LOCK_READ); if (lo != NULL) *lo = GROUPentries[gloc.recno].low; if (hi != NULL) *hi = GROUPentries[gloc.recno].high; if (count != NULL) *count = GROUPentries[gloc.recno].count; if (flag != NULL) *flag = GROUPentries[gloc.recno].flag; GROUPlock(gloc, INN_LOCK_UNLOCK); return true; } static void setinitialge(GROUPENTRY *ge, HASH grouphash, char *flag, GROUPLOC next, ARTNUM lo, ARTNUM hi) { ge->hash = grouphash; if (lo != 0) ge->low = lo; ge->high = hi; ge->expired = ge->deleted = ge->count = 0; ge->flag = *flag; ge->baseindex = ge->curindex = ge->curdata = ovnull; ge->curindexoffset = ge->curoffset = 0; ge->next = next; } bool buffindexed_groupadd(const char *group, ARTNUM lo, ARTNUM hi, char *flag) { unsigned int i; HASH grouphash; GROUPLOC gloc; GROUPENTRY *ge; #ifdef OV_DEBUG struct ov_name_table *ntp; #endif /* OV_DEBUG */ gloc = GROUPfind(group, true); if (!GROUPLOCempty(gloc)) { ge = &GROUPentries[gloc.recno]; if (GROUPentries[gloc.recno].deleted != 0) { grouphash = Hash(group, strlen(group)); setinitialge(ge, grouphash, flag, ge->next, lo, hi); } else { ge->flag = *flag; } return true; } grouphash = Hash(group, strlen(group)); memcpy(&i, &grouphash, sizeof(i)); i = i % GROUPHEADERHASHSIZE; GROUPlockhash(INN_LOCK_WRITE); gloc = GROUPnewnode(); ge = &GROUPentries[gloc.recno]; setinitialge(ge, grouphash, flag, GROUPheader->hash[i], lo, hi); GROUPheader->hash[i] = gloc; #ifdef OV_DEBUG ntp = xmalloc(sizeof(struct ov_name_table)); memset(ntp, '\0', sizeof(struct ov_name_table)); ntp->name = xstrdup(group); ntp->recno = gloc.recno; if (name_table == NULL) name_table = ntp; else { ntp->next = name_table; name_table = ntp; } #endif /* OV_DEBUG */ GROUPlockhash(INN_LOCK_UNLOCK); return true; } static off_t GROUPfilesize(int count) { return ((off_t) count * sizeof(GROUPENTRY)) + sizeof(GROUPHEADER); } /* Check if the given GROUPLOC refers to GROUPENTRY that we don't have mmap'ed, ** if so then see if the file has been grown by another writer and remmap */ static bool GROUPremapifneeded(GROUPLOC loc) { struct stat sb; if (loc.recno < GROUPcount) return true; if (fstat(GROUPfd, &sb) < 0) return false; if (GROUPfilesize(GROUPcount) >= sb.st_size) return true; if (GROUPheader) { if (munmap((void *)GROUPheader, GROUPfilesize(GROUPcount)) < 0) { syswarn("buffindexed: Could not munmap group.index in GROUPremapifneeded"); return false; } } GROUPcount = (sb.st_size - sizeof(GROUPHEADER)) / sizeof(GROUPENTRY); GROUPheader = mmap(0, GROUPfilesize(GROUPcount), PROT_READ | PROT_WRITE, MAP_SHARED, GROUPfd, 0); if (GROUPheader == MAP_FAILED) { syswarn("buffindexed: Could not mmap group.index in GROUPremapifneeded"); return false; } GROUPentries = (void *) &GROUPheader[1]; return true; } /* This function does not need to lock because it's callers are expected to do so */ static bool GROUPexpand(int mode) { int i; int flag = 0; if (GROUPheader) { if (munmap((void *)GROUPheader, GROUPfilesize(GROUPcount)) < 0) { syswarn("buffindexed: Could not munmap group.index in GROUPexpand"); return false; } } GROUPcount += 1024; if (ftruncate(GROUPfd, GROUPfilesize(GROUPcount)) < 0) { syswarn("buffindexed: Could not extend group.index"); return false; } if (mode & OV_READ) flag |= PROT_READ; if (mode & OV_WRITE) { /* * Note: below check of magic won't work unless we have both PROT_READ * and PROT_WRITE perms. */ flag |= PROT_WRITE|PROT_READ; } GROUPheader = mmap(0, GROUPfilesize(GROUPcount), flag, MAP_SHARED, GROUPfd, 0); if (GROUPheader == MAP_FAILED) { syswarn("buffindexed: Could not mmap group.index in GROUPexpand"); return false; } GROUPentries = (void *) &GROUPheader[1]; if (GROUPheader->magic != GROUPHEADERMAGIC) { GROUPheader->magic = GROUPHEADERMAGIC; GROUPLOCclear(&GROUPheader->freelist); for (i = 0; i < GROUPHEADERHASHSIZE; i++) GROUPLOCclear(&GROUPheader->hash[i]); } /* Walk the new entries from the back to the front, adding them to the freelist */ for (i = GROUPcount - 1; (GROUPcount - 1024) <= i; i--) { GROUPentries[i].next = GROUPheader->freelist; GROUPheader->freelist.recno = i; } return true; } static GROUPLOC GROUPnewnode(void) { GROUPLOC loc; /* If we didn't find any free space, then make some */ if (GROUPLOCempty(GROUPheader->freelist)) { if (!GROUPexpand(ovbuffmode)) { return GROUPemptyloc; } } assert(!GROUPLOCempty(GROUPheader->freelist)); loc = GROUPheader->freelist; GROUPheader->freelist = GROUPentries[GROUPheader->freelist.recno].next; return loc; } bool buffindexed_groupdel(const char *group) { GROUPLOC gloc; GROUPENTRY *ge; gloc = GROUPfind(group, false); if (GROUPLOCempty(gloc)) { return true; } GROUPlock(gloc, INN_LOCK_WRITE); ge = &GROUPentries[gloc.recno]; ge->deleted = time(NULL); HashClear(&ge->hash); GROUPlock(gloc, INN_LOCK_UNLOCK); return true; } static void GROUPLOCclear(GROUPLOC *loc) { loc->recno = -1; } static bool GROUPLOCempty(GROUPLOC loc) { return (loc.recno < 0); } static bool GROUPlockhash(enum inn_locktype type) { return inn_lock_range(GROUPfd, type, true, 0, sizeof(GROUPHEADER)); } static bool GROUPlock(GROUPLOC gloc, enum inn_locktype type) { return inn_lock_range(GROUPfd, type, true, sizeof(GROUPHEADER) + (sizeof(GROUPENTRY) * gloc.recno), sizeof(GROUPENTRY)); } #ifdef OV_DEBUG static bool ovsetcurindexblock(GROUPENTRY *ge, GROUPENTRY *georig) { #else static bool ovsetcurindexblock(GROUPENTRY *ge) { #endif /* OV_DEBUG */ OVBUFF *ovbuff; OV ov; OVINDEXHEAD ovindexhead; /* there is no index */ #ifdef OV_DEBUG ov = ovblocknew(georig ? georig : ge); #else ov = ovblocknew(); #endif /* OV_DEBUG */ if (ov.index == NULLINDEX) { warn("buffindexed: ovsetcurindexblock could not get new block"); return false; } if ((ovbuff = getovbuff(ov)) == NULL) { warn("buffindexed: ovsetcurindexblock could not get ovbuff block for new, %d, %d", ov.index, ov.blocknum); return false; } ovindexhead.next = ovnull; ovindexhead.low = 0; ovindexhead.high = 0; if (PWRITE(ovbuff->fd, &ovindexhead, sizeof(OVINDEXHEAD), ovbuff->base + OV_OFFSET(ov.blocknum)) != sizeof(OVINDEXHEAD)) { syswarn("buffindexed: could not write index record index '%d', blocknum" " '%d'", ge->curindex.index, ge->curindex.blocknum); return true; } if (ge->baseindex.index == NULLINDEX) { ge->baseindex = ov; } else { if ((ovbuff = getovbuff(ge->curindex)) == NULL) return false; if (!ovusedblock(ovbuff, ge->curindex.blocknum, false, false)) { warn("buffindexed: block(%d, %d) not occupied (index)", ovbuff->index, ge->curindex.blocknum); #ifdef OV_DEBUG abort(); #else /* OV_DEBUG */ /* fix it */ ovusedblock(ovbuff, ge->curindex.blocknum, true, true); #endif /* OV_DEBUG */ } ovindexhead.next = ov; ovindexhead.low = ge->curlow; ovindexhead.high = ge->curhigh; if (PWRITE(ovbuff->fd, &ovindexhead, sizeof(OVINDEXHEAD), ovbuff->base + OV_OFFSET(ge->curindex.blocknum)) != sizeof(OVINDEXHEAD)) { syswarn("buffindexed: could not write index record index '%d', blocknum" " '%d'", ge->curindex.index, ge->curindex.blocknum); return false; } } ge->curindex = ov; ge->curindexoffset = 0; ge->curlow = 0; ge->curhigh = 0; return true; } #ifdef OV_DEBUG static bool ovaddrec(GROUPENTRY *ge, ARTNUM artnum, TOKEN token, char *data, int len, time_t arrived, time_t expires, GROUPENTRY *georig) { #else static bool ovaddrec(GROUPENTRY *ge, ARTNUM artnum, TOKEN token, char *data, int len, time_t arrived, time_t expires) { #endif /* OV_DEBUG */ OV ov; OVINDEX ie; OVBUFF *ovbuff; OVINDEXHEAD ovindexhead; bool needupdate = false; #ifdef OV_DEBUG int recno; #endif /* OV_DEBUG */ Nospace = false; if (OV_BLOCKSIZE < len) { warn("buffindexed: overview data must be under %d (%d)", OV_BLOCKSIZE, len); return false; } if (ge->curdata.index == NULLINDEX) { /* no data block allocated */ #ifdef OV_DEBUG ov = ovblocknew(georig ? georig : ge); #else ov = ovblocknew(); #endif /* OV_DEBUG */ if (ov.index == NULLINDEX) { warn("buffindexed: ovaddrec could not get new block"); return false; } if ((ovbuff = getovbuff(ov)) == NULL) { warn("buffindexed: ovaddrec could not get ovbuff block for new, %d, %d, %ld", ov.index, ov.blocknum, artnum); return false; } ge->curdata = ov; ge->curoffset = 0; } else if ((ovbuff = getovbuff(ge->curdata)) == NULL) return false; else if (OV_BLOCKSIZE - ge->curoffset < len) { /* too short to store data, allocate new block */ #ifdef OV_DEBUG ov = ovblocknew(georig ? georig : ge); #else ov = ovblocknew(); #endif /* OV_DEBUG */ if (ov.index == NULLINDEX) { warn("buffindexed: ovaddrec could not get new block"); return false; } if ((ovbuff = getovbuff(ov)) == NULL) { warn("buffindexed: ovaddrec could not get ovbuff block for new, %d, %d, %ld", ov.index, ov.blocknum, artnum); return false; } ge->curdata = ov; ge->curoffset = 0; } if (!ovusedblock(ovbuff, ge->curdata.blocknum, false, false)) { warn("buffindexed: block(%d, %d) not occupied", ovbuff->index, ge->curdata.blocknum); #ifdef OV_DEBUG buffindexed_close(); abort(); #else /* OV_DEBUG */ /* fix it */ ovusedblock(ovbuff, ge->curdata.blocknum, true, true); #endif /* OV_DEBUG */ } if (PWRITE(ovbuff->fd, data, len, ovbuff->base + OV_OFFSET(ge->curdata.blocknum) + ge->curoffset) != len) { syswarn("buffindexed: could not append overview record index '%d'," " blocknum '%d'", ge->curdata.index, ge->curdata.blocknum); return false; } memset(&ie, '\0', sizeof(ie)); ie.artnum = artnum; ie.len = len; ie.index = ge->curdata.index; ie.blocknum = ge->curdata.blocknum; ie.offset = ge->curoffset; ie.token = token; ie.arrived = arrived; ie.expires = expires; if (ge->baseindex.index == NULLINDEX || ge->curindexoffset == OVINDEXMAX) { #ifdef OV_DEBUG if (!ovsetcurindexblock(ge, georig)) { #else if (!ovsetcurindexblock(ge)) { #endif /* OV_DEBUG */ warn("buffindexed: could not set current index"); return false; } } if ((ovbuff = getovbuff(ge->curindex)) == NULL) return false; if (!ovusedblock(ovbuff, ge->curindex.blocknum, false, false)) { warn("buffindexed: block(%d, %d) not occupied (index)", ovbuff->index, ge->curindex.blocknum); #ifdef OV_DEBUG buffindexed_close(); abort(); #else /* OV_DEBUG */ /* fix this */ ovusedblock(ovbuff, ge->curindex.blocknum, true, true); #endif /* OV_DEBUG */ } if (PWRITE(ovbuff->fd, &ie, sizeof(ie), ovbuff->base + OV_OFFSET(ge->curindex.blocknum) + sizeof(OVINDEXHEAD) + sizeof(ie) * ge->curindexoffset) != sizeof(ie)) { syswarn("buffindexed: could not write index record index '%d', blocknum" " '%d'", ge->curindex.index, ge->curindex.blocknum); return true; } if ((ge->curlow <= 0) || (ge->curlow > artnum)) { ge->curlow = artnum; needupdate = true; } if ((ge->curhigh <= 0) || (ge->curhigh < artnum)) { ge->curhigh = artnum; needupdate = true; } if (needupdate) { ovindexhead.next = ovnull; ovindexhead.low = ge->curlow; ovindexhead.high = ge->curhigh; if (PWRITE(ovbuff->fd, &ovindexhead, sizeof(OVINDEXHEAD), ovbuff->base + OV_OFFSET(ge->curindex.blocknum)) != sizeof(OVINDEXHEAD)) { syswarn("buffindexed: could not write index record index '%d', blocknum" " '%d'", ge->curindex.index, ge->curindex.blocknum); return true; } } if ((ge->low <= 0) || (ge->low > artnum)) ge->low = artnum; if ((ge->high <= 0) || (ge->high < artnum)) ge->high = artnum; ge->curindexoffset++; ge->curoffset += len; ge->count++; return true; } bool buffindexed_add(const char *group, ARTNUM artnum, TOKEN token, char *data, int len, time_t arrived, time_t expires) { GROUPLOC gloc; GROUPENTRY *ge; if (len > OV_BLOCKSIZE) { warn("buffindexed: overview data is too large %d", len); return true; } gloc = GROUPfind(group, false); if (GROUPLOCempty(gloc)) { return true; } GROUPlock(gloc, INN_LOCK_WRITE); /* prepend block(s) if needed. */ ge = &GROUPentries[gloc.recno]; if (Cutofflow && ge->low > artnum) { GROUPlock(gloc, INN_LOCK_UNLOCK); return true; } #ifdef OV_DEBUG if (!ovaddrec(ge, artnum, token, data, len, arrived, expires, NULL)) { #else if (!ovaddrec(ge, artnum, token, data, len, arrived, expires)) { #endif /* OV_DEBUG */ if (Nospace) { GROUPlock(gloc, INN_LOCK_UNLOCK); warn("buffindexed: no space left for buffer, adding '%s'", group); return false; } warn("buffindexed: could not add overview for '%s'", group); } GROUPlock(gloc, INN_LOCK_UNLOCK); return true; } bool buffindexed_cancel(const char *group UNUSED, ARTNUM artnum UNUSED) { return true; } #ifdef OV_DEBUG static void freegroupblock(GROUPENTRY *ge) { #else static void freegroupblock(void) { #endif /* OV_DEBUG */ GROUPDATABLOCK *gdb; int i; GIBLIST *giblist; for (giblist = Giblist ; giblist != NULL ; giblist = giblist->next) { #ifdef OV_DEBUG ovblockfree(giblist->ov, ge); #else ovblockfree(giblist->ov); #endif /* OV_DEBUG */ } for (i = 0 ; i < GROUPDATAHASHSIZE ; i++) { for (gdb = groupdatablock[i] ; gdb != NULL ; gdb = gdb->next) { #ifdef OV_DEBUG ovblockfree(gdb->datablk, ge); #else ovblockfree(gdb->datablk); #endif /* OV_DEBUG */ } } } static void ovgroupunmap(void) { GROUPDATABLOCK *gdb, *gdbnext; int i; GIBLIST *giblist, *giblistnext; for (i = 0 ; i < GROUPDATAHASHSIZE ; i++) { for (gdb = groupdatablock[i] ; gdb != NULL ; gdb = gdbnext) { gdbnext = gdb->next; free(gdb); } groupdatablock[i] = NULL; } for (giblist = Giblist ; giblist != NULL ; giblist = giblistnext) { giblistnext = giblist->next; free(giblist); } Giblist = NULL; if (!Cache && (Gib != NULL)) { free(Gib); Gib = NULL; if (Cachesearch != NULL) { free(Cachesearch->group); free(Cachesearch); Cachesearch = NULL; } } } static void insertgdb(OV *ov, GROUPDATABLOCK *gdb) { gdb->next = groupdatablock[(ov->index + ov->blocknum) % GROUPDATAHASHSIZE]; groupdatablock[(ov->index + ov->blocknum) % GROUPDATAHASHSIZE] = gdb; return; } static GROUPDATABLOCK *searchgdb(OV *ov) { GROUPDATABLOCK *gdb; gdb = groupdatablock[(ov->index + ov->blocknum) % GROUPDATAHASHSIZE]; for (; gdb != NULL ; gdb = gdb->next) { if (ov->index == gdb->datablk.index && ov->blocknum == gdb->datablk.blocknum) break; } return gdb; } static int INDEXcompare(const void *p1, const void *p2) { const OVINDEX *oi1 = p1; const OVINDEX *oi2 = p2; return oi1->artnum - oi2->artnum; } static bool ovgroupmmap(GROUPENTRY *ge, ARTNUM low, ARTNUM high, bool needov) { OV ov = ge->baseindex; OVBUFF *ovbuff; GROUPDATABLOCK *gdb; int pagefudge, limit, i, count, len; off_t offset, mmapoffset; OVBLOCK *ovblock; void * addr; GIBLIST *giblist; if (low > high) { Gibcount = 0; return true; } Gibcount = ge->count; if (Gibcount == 0) return true; Gib = xmalloc(Gibcount * sizeof(OVINDEX)); count = 0; while (ov.index != NULLINDEX) { ovbuff = getovbuff(ov); if (ovbuff == NULL) { warn("buffindexed: ovgroupmmap ovbuff is null(ovindex is %d, ovblock is %d", ov.index, ov.blocknum); ovgroupunmap(); return false; } offset = ovbuff->base + OV_OFFSET(ov.blocknum); pagefudge = offset % pagesize; mmapoffset = offset - pagefudge; len = pagefudge + OV_BLOCKSIZE; if ((addr = mmap(NULL, len, PROT_READ, MAP_SHARED, ovbuff->fd, mmapoffset)) == MAP_FAILED) { syswarn("buffindexed: ovgroupmmap could not mmap index block"); ovgroupunmap(); return false; } ovblock = (void *)((char *)addr + pagefudge); if (ov.index == ge->curindex.index && ov.blocknum == ge->curindex.blocknum) { limit = ge->curindexoffset; } else { limit = OVINDEXMAX; } for (i = 0 ; i < limit ; i++) { if (Gibcount == count) { Gibcount += OV_FUDGE; Gib = xrealloc(Gib, Gibcount * sizeof(OVINDEX)); } Gib[count++] = ovblock->ovindex[i]; } giblist = xmalloc(sizeof(GIBLIST)); giblist->ov = ov; giblist->next = Giblist; Giblist = giblist; ov = ovblock->ovindexhead.next; munmap(addr, len); } Gibcount = count; qsort(Gib, Gibcount, sizeof(OVINDEX), INDEXcompare); /* Remove duplicates. */ for (i = 0; i < Gibcount - 1; i++) { if (Gib[i].artnum == Gib[i+1].artnum) { /* lower position is removed */ Gib[i].artnum = 0; } } if (!needov) return true; count = 0; for (i = 0 ; i < Gibcount ; i++) { if (Gib[i].artnum == 0 || Gib[i].artnum < low || Gib[i].artnum > high) continue; ov.index = Gib[i].index; ov.blocknum = Gib[i].blocknum; gdb = searchgdb(&ov); if (gdb != NULL) continue; ovbuff = getovbuff(ov); if (ovbuff == NULL) continue; gdb = xmalloc(sizeof(GROUPDATABLOCK)); gdb->datablk = ov; gdb->next = NULL; gdb->mmapped = false; insertgdb(&ov, gdb); count++; } if (count == 0) return true; if ((unsigned) count * OV_BLOCKSIZE > innconf->keepmmappedthreshold * 1024) /* large retrieval, mmap is done in ovsearch() */ return true; /* Data blocks are being mmapped, not copied. */ for (i = 0 ; i < GROUPDATAHASHSIZE ; i++) { for (gdb = groupdatablock[i] ; gdb != NULL ; gdb = gdb->next) { ov = gdb->datablk; ovbuff = getovbuff(ov); offset = ovbuff->base + OV_OFFSET(ov.blocknum); pagefudge = offset % pagesize; mmapoffset = offset - pagefudge; gdb->len = pagefudge + OV_BLOCKSIZE; if ((gdb->addr = mmap(NULL, gdb->len, PROT_READ, MAP_SHARED, ovbuff->fd, mmapoffset)) == MAP_FAILED) { syswarn("buffindexed: ovgroupmmap could not mmap data block"); free(gdb); ovgroupunmap(); return false; } gdb->data = (char *)gdb->addr + pagefudge; gdb->mmapped = true; } } return true; } static void * ovopensearch(const char *group, ARTNUM low, ARTNUM high, bool needov) { GROUPLOC gloc; GROUPENTRY *ge; OVSEARCH *search; gloc = GROUPfind(group, false); if (GROUPLOCempty(gloc)) return NULL; ge = &GROUPentries[gloc.recno]; if (low < ge->low) low = ge->low; if (high > ge->high) high = ge->high; if (!ovgroupmmap(ge, low, high, needov)) { return NULL; } search = xmalloc(sizeof(OVSEARCH)); search->hi = high; search->lo = low; search->cur = 0; search->group = xstrdup(group); search->needov = needov; search->gloc = gloc; search->count = ge->count; search->gdb.mmapped = false; return (void *)search; } void * buffindexed_opensearch(const char *group, int low, int high) { GROUPLOC gloc; void *handle; if (Gib != NULL) { free(Gib); Gib = NULL; if (Cachesearch != NULL) { free(Cachesearch->group); free(Cachesearch); Cachesearch = NULL; } } gloc = GROUPfind(group, false); if (GROUPLOCempty(gloc)) { return NULL; } GROUPlock(gloc, INN_LOCK_WRITE); if ((handle = ovopensearch(group, low, high, true)) == NULL) GROUPlock(gloc, INN_LOCK_UNLOCK); return(handle); } static bool ovsearch(void *handle, ARTNUM *artnum, char **data, int *len, TOKEN *token, time_t *arrived, time_t *expires) { OVSEARCH *search = (OVSEARCH *)handle; OV srchov; GROUPDATABLOCK *gdb; off_t offset, mmapoffset; OVBUFF *ovbuff; int pagefudge; bool newblock; if (search->cur == Gibcount) { return false; } while (Gib[search->cur].artnum == 0 || Gib[search->cur].artnum < search->lo) { search->cur++; if (search->cur == Gibcount) return false; } if (Gib[search->cur].artnum > search->hi) return false; if (search->needov) { if (Gib[search->cur].index == NULLINDEX) { if (len) *len = 0; if (artnum) *artnum = Gib[search->cur].artnum; } else { if (artnum) *artnum = Gib[search->cur].artnum; if (len) *len = Gib[search->cur].len; if (arrived) *arrived = Gib[search->cur].arrived; if (expires) *expires = Gib[search->cur].expires; if (data) { srchov.index = Gib[search->cur].index; srchov.blocknum = Gib[search->cur].blocknum; gdb = searchgdb(&srchov); if (gdb == NULL) { if (len) *len = 0; search->cur++; return true; } if (!gdb->mmapped) { /* block needs to be mmapped */ if (search->gdb.mmapped) { /* check previous mmapped area */ if (search->gdb.datablk.blocknum != srchov.blocknum || search->gdb.datablk.index != srchov.index) { /* different one, release previous one */ munmap(search->gdb.addr, search->gdb.len); newblock = true; } else newblock = false; } else newblock = true; if (newblock) { search->gdb.datablk.blocknum = srchov.blocknum; search->gdb.datablk.index = srchov.index; ovbuff = getovbuff(srchov); offset = ovbuff->base + OV_OFFSET(srchov.blocknum); pagefudge = offset % pagesize; mmapoffset = offset - pagefudge; search->gdb.len = pagefudge + OV_BLOCKSIZE; if ((search->gdb.addr = mmap(NULL, search->gdb.len, PROT_READ, MAP_SHARED, ovbuff->fd, mmapoffset)) == MAP_FAILED) { syswarn("buffindexed: ovsearch could not mmap data block"); return false; } gdb->data = search->gdb.data = (char *)search->gdb.addr + pagefudge; search->gdb.mmapped = true; } } *data = (char *)gdb->data + Gib[search->cur].offset; } } } if (token) { if (Gib[search->cur].index == NULLINDEX && !search->needov) { search->cur++; return false; } *token = Gib[search->cur].token; } search->cur++; return true; } bool buffindexed_search(void *handle, ARTNUM *artnum, char **data, int *len, TOKEN *token, time_t *arrived) { return(ovsearch(handle, artnum, data, len, token, arrived, NULL)); } static void ovclosesearch(void *handle, bool freeblock) { OVSEARCH *search = (OVSEARCH *)handle; GROUPDATABLOCK *gdb; int i; #ifdef OV_DEBUG GROUPENTRY *ge; GROUPLOC gloc; #endif /* OV_DEBUG */ for (i = 0 ; i < GROUPDATAHASHSIZE ; i++) { for (gdb = groupdatablock[i] ; gdb != NULL ; gdb = gdb->next) { if (gdb->mmapped) munmap(gdb->addr, gdb->len); } } if (search->gdb.mmapped) munmap(search->gdb.addr, search->gdb.len); if (freeblock) { #ifdef OV_DEBUG gloc = GROUPfind(search->group, false); if (!GROUPLOCempty(gloc)) { ge = &GROUPentries[gloc.recno]; freegroupblock(ge); } #else freegroupblock(); #endif /* OV_DEBUG */ } ovgroupunmap(); if (Cache) { Cachesearch = search; } else { free(search->group); free(search); } return; } void buffindexed_closesearch(void *handle) { OVSEARCH *search = (OVSEARCH *)handle; GROUPLOC gloc; gloc = search->gloc; ovclosesearch(handle, false); GROUPlock(gloc, INN_LOCK_UNLOCK); } /* get token from sorted index */ static bool gettoken(ARTNUM artnum, TOKEN *token) { int i, j, offset, limit; offset = 0; limit = Gibcount; for (i = (limit - offset) / 2 ; i > 0 ; i = (limit - offset) / 2) { if (Gib[offset + i].artnum == artnum) { *token = Gib[offset + i].token; return true; } else if (Gib[offset + i].artnum == 0) { /* case for duplicated index */ for (j = offset + i - 1; j >= offset ; j --) { if (Gib[j].artnum != 0) break; } if (j < offset) { /* article not found */ return false; } if (Gib[j].artnum == artnum) { *token = Gib[j].token; return true; } else if (Gib[j].artnum < artnum) { /* limit is not changed */ offset += i + 1; } else { /* offset is not changed */ limit = j; } } else if (Gib[offset + i].artnum < artnum) { /* limit is unchanged */ offset += i + 1; } else { /* offset is unchanged */ limit = offset + i; } } /* i == 0 */ if (Gib[offset].artnum != artnum) { /* article not found */ return false; } *token = Gib[offset].token; return true; } bool buffindexed_getartinfo(const char *group, ARTNUM artnum, TOKEN *token) { GROUPLOC gloc; void *handle; bool retval, grouplocked = false; if (Gib != NULL) { if (Cachesearch != NULL && strcmp(Cachesearch->group, group) != 0) { free(Gib); Gib = NULL; free(Cachesearch->group); free(Cachesearch); Cachesearch = NULL; } else { if (gettoken(artnum, token)) return true; else { /* examine to see if overview index are increased */ gloc = GROUPfind(group, false); if (GROUPLOCempty(gloc)) { return false; } GROUPlock(gloc, INN_LOCK_WRITE); if ((Cachesearch != NULL) && (GROUPentries[gloc.recno].count == Cachesearch->count)) { /* no new overview data is stored */ GROUPlock(gloc, INN_LOCK_UNLOCK); return false; } else { grouplocked = true; free(Gib); Gib = NULL; if (Cachesearch != NULL) { free(Cachesearch->group); free(Cachesearch); Cachesearch = NULL; } } } } } if (!grouplocked) { gloc = GROUPfind(group, false); if (GROUPLOCempty(gloc)) { return false; } GROUPlock(gloc, INN_LOCK_WRITE); } if (!(handle = ovopensearch(group, artnum, artnum, false))) { GROUPlock(gloc, INN_LOCK_UNLOCK); return false; } retval = buffindexed_search(handle, NULL, NULL, NULL, token, NULL); ovclosesearch(handle, false); GROUPlock(gloc, INN_LOCK_UNLOCK); return retval; } bool buffindexed_expiregroup(const char *group, int *lo, struct history *h) { void *handle; GROUPENTRY newge, *ge; GROUPLOC gloc, next; char *data; int i, len; TOKEN token; ARTNUM artnum, low, high; ARTHANDLE *ah; char flag; HASH hash; time_t arrived, expires; OVSEARCH search; if (group == NULL) { for (i = 0 ; i < GROUPheader->freelist.recno ; i++) { gloc.recno = i; GROUPlock(gloc, INN_LOCK_WRITE); ge = &GROUPentries[gloc.recno]; if (ge->expired >= OVrealnow || ge->count == 0) { GROUPlock(gloc, INN_LOCK_UNLOCK); continue; } if (!ovgroupmmap(ge, ge->low, ge->high, true)) { GROUPlock(gloc, INN_LOCK_UNLOCK); warn("buffindexed: could not mmap overview for hidden groups(%d)", i); continue; } search.hi = ge->high; search.lo = ge->low; search.cur = 0; search.needov = true; while (ovsearch((void *)&search, NULL, &data, &len, &token, &arrived, &expires)) { if (innconf->groupbaseexpiry) /* assuming "." is not real newsgroup */ OVgroupbasedexpire(token, ".", data, len, arrived, expires); } #ifdef OV_DEBUG freegroupblock(ge); #else freegroupblock(); #endif ovgroupunmap(); ge->expired = time(NULL); ge->count = 0; ge->baseindex = ge->curindex = ge->curdata = ovnull; GROUPlock(gloc, INN_LOCK_UNLOCK); } return true; } gloc = GROUPfind(group, false); if (GROUPLOCempty(gloc)) { return false; } GROUPlock(gloc, INN_LOCK_WRITE); ge = &GROUPentries[gloc.recno]; if (ge->count == 0) { ge->low = ge->high + 1; if (lo != NULL) *lo = ge->low; ge->expired = time(NULL); GROUPlock(gloc, INN_LOCK_UNLOCK); return true; } flag = ge->flag; hash = ge->hash; next = ge->next; low = ge->low; high = ge->high; newge.low = 0; setinitialge(&newge, hash, &flag, next, 0, high); if ((handle = ovopensearch(group, low, high, true)) == NULL) { ge->expired = time(NULL); GROUPlock(gloc, INN_LOCK_UNLOCK); warn("buffindexed: could not open overview for '%s'", group); return false; } while (ovsearch(handle, &artnum, &data, &len, &token, &arrived, &expires)) { ah = NULL; if (len == 0) continue; if (!SMprobe(EXPENSIVESTAT, &token, NULL) || OVstatall) { if ((ah = SMretrieve(token, RETR_STAT)) == NULL) continue; SMfreearticle(ah); } else { if (!OVhisthasmsgid(h, data)) continue; } if (innconf->groupbaseexpiry && OVgroupbasedexpire(token, group, data, len, arrived, expires)) continue; #ifdef OV_DEBUG if (!ovaddrec(&newge, artnum, token, data, len, arrived, expires, ge)) { #else if (!ovaddrec(&newge, artnum, token, data, len, arrived, expires)) { #endif /* OV_DEBUG */ /* Old group cannot be freed. */ ovclosesearch(handle, false); ge->expired = time(NULL); GROUPlock(gloc, INN_LOCK_UNLOCK); warn("buffindexed: not enough room to expire overview for group '%s'", group); /* Clean just reserved overview entries. */ if (!ovgroupmmap(&newge, newge.low, newge.high, true)) { warn("buffindexed: cannot prepare free operation"); return false; } freegroupblock(); ovgroupunmap(); return false; } } if (newge.low == 0) { /* No article for the group. */ newge.low = newge.high; } if (newge.count == 0) { /* The low water mark should be one more than the high water mark * when there is no article in the group. */ newge.low = newge.high + 1; } *ge = newge; if (lo != NULL) { *lo = ge->low; } ovclosesearch(handle, true); ge->expired = time(NULL); GROUPlock(gloc, INN_LOCK_UNLOCK); return true; } bool buffindexed_ctl(OVCTLTYPE type, void *val) { int total, used, *i, j; OVBUFF *ovbuff = ovbufftab; OVSORTTYPE *sorttype; bool *boolval; GROUPDATABLOCK *gdb; switch (type) { case OVSPACE: for (total = used = 0 ; ovbuff != (OVBUFF *)NULL ; ovbuff = ovbuff->next) { ovlock(ovbuff, INN_LOCK_READ); ovreadhead(ovbuff); total += ovbuff->totalblk; used += ovbuff->usedblk; ovlock(ovbuff, INN_LOCK_UNLOCK); } i = (int *)val; *i = (used * 100) / total; return true; case OVSORT: sorttype = (OVSORTTYPE *)val; *sorttype = OVNOSORT; return true; case OVCUTOFFLOW: Cutofflow = *(bool *)val; return true; case OVSTATICSEARCH: i = (int *)val; *i = true; for (j = 0 ; j < GROUPDATAHASHSIZE ; j++) { for (gdb = groupdatablock[j] ; gdb != NULL ; gdb = gdb->next) { if (gdb->mmapped) { *i = false; return true; } } } return true; case OVCACHEKEEP: Cache = *(bool *)val; return true; case OVCACHEFREE: boolval = (bool *)val; *boolval = true; if (Gib != NULL) { free(Gib); Gib = NULL; if (Cachesearch != NULL) { free(Cachesearch->group); free(Cachesearch); Cachesearch = NULL; } } return true; default: return false; } } void buffindexed_close(void) { struct stat sb; OVBUFF *ovbuff = NULL; #ifdef OV_DEBUG FILE *F = NULL; pid_t pid; char *path = NULL; int i,j; struct ov_trace_array *trace; struct ov_name_table *ntp; size_t length; #endif /* OV_DEBUG */ #ifdef OV_DEBUG ovbuff = ovbufftab; for (; ovbuff != (OVBUFF *)NULL; ovbuff = ovbuff->next) { for (i = 0 ; i < ovbuff->totalblk ; i++) { trace = &ovbuff->trace[i]; if (trace->ov_trace == NULL) continue; for (j = 0 ; j <= trace->cur && j < trace->max ; j++) { if (trace->ov_trace[j].occupied != 0 || trace->ov_trace[j].freed != 0) { if (F == NULL) { length = strlen(innconf->pathtmp) + 11; path = xmalloc(length); pid = getpid(); snprintf(path, length, "%s/%d", innconf->pathtmp, pid); if ((F = fopen(path, "w")) == NULL) { syswarn("buffindexed: could not open %s", path); break; } } fprintf(F, "%d: % 6d, % 2d: 0x%08x, % 10d, % 10d\n", ovbuff->index, i, j, trace->ov_trace[j].gloc.recno, trace->ov_trace[j].occupied, trace->ov_trace[j].freed); } } } } if ((ntp = name_table) != NULL) { if (F == NULL) { length = strlen(innconf->pathtmp) + 11; path = xmalloc(length); pid = getpid(); sprintf(path, length, "%s/%d", innconf->pathtmp, pid); if ((F = fopen(path, "w")) == NULL) { syswarn("buffindexed: could not open %s", path); } } if (F != NULL) { while(ntp) { fprintf(F, "0x%08x: %s\n", ntp->recno, ntp->name); ntp = ntp->next; } } } if (F != NULL) fclose(F); if (path != NULL) free(path); #endif /* OV_DEBUG */ if (Gib != NULL) { free(Gib); Gib = NULL; if (Cachesearch != NULL) { free(Cachesearch->group); free(Cachesearch); Cachesearch = NULL; } } if (fstat(GROUPfd, &sb) < 0) return; close(GROUPfd); if (GROUPheader) { if (munmap((void *)GROUPheader, GROUPfilesize(GROUPcount)) < 0) { syswarn("buffindexed: could not munmap group.index in buffindexed_close"); return; } GROUPheader = NULL; } /* sync the bit field */ ovbuff = ovbufftab; for (; ovbuff != (OVBUFF *)NULL; ovbuff = ovbuffnext) { if (ovbuff->dirty != 0) { ovbuff->dirty = OVBUFF_SYNC_COUNT + 1; ovflushhead(ovbuff); } ovbuffnext = ovbuff->next; if (ovbuff->smc != NULL) smcClose(ovbuff->smc); if (ovbuff->fd >= 0) close(ovbuff->fd); free(ovbuff); } ovbufftab = NULL; ovbuffnext = NULL; } #ifdef BUFF_DEBUG static int countgdb(void) { int i, count = 0; GROUPDATABLOCK *gdb; for (i = 0 ; i < GROUPDATAHASHSIZE ; i++) { for (gdb = groupdatablock[i] ; gdb != NULL ; gdb = gdb->next) count++; } return count; } int main(int argc, char **argv) { char *group, flag[2], buff[OV_BLOCKSIZE]; int lo, hi, count, flags, i; OVSEARCH *search; GROUPENTRY *ge; GROUPLOC gloc; GIBLIST *giblist; if (argc != 2) { fprintf(stderr, "only one argument can be specified\n"); exit(1); } /* if innconf isn't already read in, do so. */ if (innconf == NULL) { if (!innconf_read(NULL)) { fprintf(stderr, "reading inn.conf failed\n"); exit(1); } } if (!buffindexed_open(OV_READ)) { fprintf(stderr, "buffindexed_open failed\n"); exit(1); } fprintf(stdout, "GROUPheader->freelist.recno is %d(0x%08x)\n", GROUPheader->freelist.recno, GROUPheader->freelist.recno); group = argv[1]; if (isdigit((unsigned char) *group)) { gloc.recno = atoi(group); ge = &GROUPentries[gloc.recno]; fprintf(stdout, "left articles are %d for %d, last expiry is %ld\n", ge->count, gloc.recno, (long) ge->expired); if (ge->count == 0) { GROUPlock(gloc, INN_LOCK_UNLOCK); exit(0); } if (!ovgroupmmap(ge, ge->low, ge->high, true)) { fprintf(stderr, "ovgroupmmap failed\n"); GROUPlock(gloc, INN_LOCK_UNLOCK); } for (giblist = Giblist, i = 0 ; giblist != NULL ; giblist = giblist->next, i++); fprintf(stdout, "%d index block(s)\n", i); fprintf(stdout, "%d data block(s)\n", countgdb()); for (giblist = Giblist ; giblist != NULL ; giblist = giblist->next) { fprintf(stdout, " % 8d(% 5d)\n", giblist->ov.blocknum, giblist->ov.index); } for (i = 0 ; i < Gibcount ; i++) { if (Gib[i].artnum == 0) continue; if (Gib[i].index == NULLINDEX) fprintf(stdout, " %lu empty\n", (unsigned long) Gib[i].offset); else { fprintf(stdout, " %lu %d\n", (unsigned long) Gib[i].offset, Gib[i].len); } } ovgroupunmap(); GROUPlock(gloc, INN_LOCK_UNLOCK); exit(0); } gloc = GROUPfind(group, false); if (GROUPLOCempty(gloc)) { fprintf(stderr, "gloc is null\n"); } GROUPlock(gloc, INN_LOCK_READ); ge = &GROUPentries[gloc.recno]; fprintf(stdout, "base %d(%d), cur %d(%d), expired at %s\n", ge->baseindex.blocknum, ge->baseindex.index, ge->curindex.blocknum, ge->curindex.index, ge->expired == 0 ? "none\n" : ctime(&ge->expired)); if (!buffindexed_groupstats(group, &lo, &hi, &count, &flags)) { fprintf(stderr, "buffindexed_groupstats failed for group %s\n", group); exit(1); } flag[0] = (char)flags; flag[1] = '\0'; fprintf(stdout, "%s: low is %d, high is %d, count is %d, flag is '%s'\n", group, lo, hi, count, flag); if ((search = (OVSEARCH *)ovopensearch(group, lo, hi, true)) == NULL) { fprintf(stderr, "ovopensearch failed for group %s\n", group); exit(1); } fprintf(stdout, " gloc is %d(0x%08x)\n", search->gloc.recno, search->gloc.recno); for (giblist = Giblist, i = 0 ; giblist != NULL ; giblist = giblist->next, i++); fprintf(stdout, "%d index block(s)\n", i); fprintf(stdout, "%d data block(s)\n", countgdb()); for (giblist = Giblist ; giblist != NULL ; giblist = giblist->next) { fprintf(stdout, " % 8d(% 5d)\n", giblist->ov.blocknum, giblist->ov.index); } for (i = 0 ; i < Gibcount ; i++) { if (Gib[i].artnum == 0) continue; if (Gib[i].index == NULLINDEX) fprintf(stdout, " %lu empty\n", (unsigned long) Gib[i].offset); else { fprintf(stdout, " %lu %d\n", (unsigned long) Gib[i].offset, Gib[i].len); } } { ARTNUM artnum; char *data; int len; TOKEN token; while (buffindexed_search((void *)search, &artnum, &data, &len, &token, NULL)) { if (len == 0) fprintf(stdout, "%lu: len is 0\n", artnum); else { memcpy(buff, data, len); buff[len] = '\0'; fprintf(stdout, "%lu: %s\n", artnum, buff); } } } return 0; } #endif /* BUFF_DEBUG */ inn-2.6.0/storage/buffindexed/buffindexed.h0000644000175200017520000000222012575023702020277 0ustar iuliusiulius/* $Id: buffindexed.h 9782 2015-01-07 21:34:22Z iulius $ */ #ifndef _BUFFINDEXED_H_ #define _BUFFINDEXED_H_ #include "config.h" BEGIN_DECLS bool buffindexed_open(int mode); bool buffindexed_groupstats(const char *group, int *lo, int *hi, int *count, int *flag); bool buffindexed_groupadd(const char *group, ARTNUM lo, ARTNUM hi, char *flag); bool buffindexed_groupdel(const char *group); bool buffindexed_add(const char *group, ARTNUM artnum, TOKEN token, char *data, int len, time_t arrived, time_t expires); bool buffindexed_cancel(const char *group, ARTNUM artnum); void *buffindexed_opensearch(const char *group, int low, int high); bool buffindexed_search(void *handle, ARTNUM *artnum, char **data, int *len, TOKEN *token, time_t *arrived); void buffindexed_closesearch(void *handle); bool buffindexed_getartinfo(const char *group, ARTNUM artnum, TOKEN *token); bool buffindexed_expiregroup(const char *group, int *lo, struct history *h); bool buffindexed_ctl(OVCTLTYPE type, void *val); void buffindexed_close(void); END_DECLS #endif /* _BUFFINDEXED_H_ */ inn-2.6.0/storage/buffindexed/shmem.h0000644000175200017520000000126612575023702017136 0ustar iuliusiulius/* ** shared memory control utility */ #ifndef SHMEM_H #define SHMEM_H #include typedef struct { caddr_t addr; /* attached shared memory address */ size_t size; /* size of the shared memory */ int shmid; /* shared memory segment id */ int semap; /* semaphore id */ int locktype; /* current lock type */ } smcd_t; int smcGetExclusiveLock(smcd_t *this); int smcGetSharedLock(smcd_t *this); int smcReleaseSharedLock(smcd_t *this); int smcReleaseExclusiveLock(smcd_t *this); smcd_t* smcGetShmemBuffer(const char *name, int mapSize); smcd_t* smcCreateShmemBuffer(const char *name, int mapSize); void smcClose( smcd_t *this ); #endif /* SHMEM_H */ inn-2.6.0/storage/buffindexed/ovmethod.config0000644000175200017520000000013312575023702020660 0ustar iuliusiuliusname = buffindexed number = 3 sources = buffindexed.c shmem.c programs = buffindexed_d inn-2.6.0/storage/ovdb/0000755000175200017520000000000012575023701014315 5ustar iuliusiuliusinn-2.6.0/storage/ovdb/ovdb.h0000644000175200017520000000175212575023702015426 0ustar iuliusiulius/* $Id: ovdb.h 9782 2015-01-07 21:34:22Z iulius $ */ #ifndef _OVDB_H_ #define _OVDB_H_ #include "config.h" BEGIN_DECLS bool ovdb_open(int mode); bool ovdb_groupstats(const char *group, int *lo, int *hi, int *count, int *flag); bool ovdb_groupadd(const char *group, ARTNUM lo, ARTNUM hi, char *flag); bool ovdb_groupdel(const char *group); bool ovdb_add(const char *group, ARTNUM artnum, TOKEN token, char *data, int len, time_t arrived, time_t expires); bool ovdb_cancel(const char *group, ARTNUM artnum); void *ovdb_opensearch(const char *group, int low, int high); bool ovdb_search(void *handle, ARTNUM *artnum, char **data, int *len, TOKEN *token, time_t *arrived); void ovdb_closesearch(void *handle); bool ovdb_getartinfo(const char *group, ARTNUM artnum, TOKEN *token); bool ovdb_expiregroup(const char *group, int *lo, struct history *h); bool ovdb_ctl(OVCTLTYPE type, void *val); void ovdb_close(void); END_DECLS #endif /* _OVDB_H_ */ inn-2.6.0/storage/ovdb/ovdb.c0000644000175200017520000021544712575023702015431 0ustar iuliusiulius/* * ovdb.c * ovdb 2.00 * Overview storage using Berkeley DB 4.4 or higher * * 2004-02-17 : Need to track search cursors, since it's possible that * ovdb_closesearch does not get called. We now close * any cursors still open in ovdb_close, or they'd be in * the database indefinitely causing deadlocks. * 2002-08-13 : Change BOOL to bool, remove portability to < 2.4. * 2002-08-11 : Cleaned up use of sprintf and fixed a bunch of warnings. * 2002-02-28 : Update getartinfo for the overview API change in 2.4. This * breaks compatibility with INN 2.3.x.... * 2000-12-12 : Add support for Berkeley DB DB_SYSTEM_MEM option, controlled * : by ovdb.conf 'useshm' and 'shmkey' * 2000-11-27 : Update for DB 3.2.x compatibility * 2000-11-13 : New 'readserver' feature * 2000-10-10 : ovdb_search now closes the cursor right after the last * record is read. * 2000-10-05 : artnum member of struct datakey changed from ARTNUM to u_int32_t. * OS's where sizeof(long)==8 will have to rebuild their databases * after this update. * 2000-10-05 : from Dan Riley: struct datakey needs to be zero'd, for * 64-bit OSs where the struct has internal padding bytes. * 2000-09-29 : ovdb_expiregroup can now fix incorrect counts; use new * inn/version.h so can have same ovdb.c for 2.3.0, 2.3.1, and 2.4 * 2000-09-28 : low mark in ovdb_expiregroup still wasn't right * 2000-09-27 : Further improvements to ovdb_expiregroup: restructured the * loop; now updates groupinfo as it goes along rather than * counting records at the end, which prevents a possible * deadlock. * 2000-09-19 : *lo wasn't being set in ovdb_expiregroup * 2000-09-15 : added ovdb_check_user(); tweaked some error msgs; fixed an * improper use of RENEW * 2000-08-28 : New major release: version 2.00 (beta) * + "groupsbyname" and "groupstats" databases replaced with "groupinfo". * + ovdb_recover, ovdb_upgrade, and dbprocs are now deprecated; their * functionality is now in ovdb_init and ovdb_monitor. * + ovdb_init can upgrade a database from the old version of ovdb to * work with this version. * + Rewrote ovdb_expiregroup(); it can now re-write OV data rather * than simply deleting old keys (which can leave 'holes' that result * in inefficient disk-space use). * + Add "nocompact" to ovdb.conf, which controls whether ovdb_expiregroup() * rewrites OV data. * + No longer needs the Berkeley DB tools db_archive, db_checkpoint, and * db_deadlock. That functionality is now in ovdb_monitor. * + ovdb_open() won't succeed if ovdb_monitor is not running. This will * prevent the problems that happen if the database is not regularly * checkpointed and deadlock-tested. * + Internal group IDs (32-bit ints) are now reused. * + Add "maxlocks" to ovdb.conf, which will set the DB lk_max parameter. * + Pull "test" code out into ovdb_stat. ovdb_stat will also provide * functionality similar to the Berkeley DB "db_stat" command. * + Update docs: write man pages for the new ovdb_* commands; update * ovdb.pod * * 2000-07-11 : fix possible alignment problem; add test code * 2000-07-07 : bugfix: timestamp handling * 2000-06-10 : Modified groupnum() interface; fix ovdb_add() to return false * for certain groupnum() errors * 2000-06-08 : Added Berkeley DB 3.1.x compatibility * 2000-04-09 : Tweak some default parameters; store aliased group info * 2000-03-29 : Add DB_RMW flag to the 'get' of get-modify-put sequences * 2000-02-17 : Update expire behavior to be consistent with current * ov3 and buffindexed * 2000-01-13 : Fix to make compatible with unmodified nnrpd/article.c * 2000-01-04 : Added data versioning * 1999-12-20 : Added Berkeley DB 3.x compatibility * 1999-12-06 : First Release -- H. Kehoe */ #include "config.h" #include "clibrary.h" #include "portable/socket.h" #include #include #ifdef HAVE_LIMITS_H # include #endif #include #ifdef HAVE_SYS_SELECT_H # include #endif #ifndef HAVE_ZLIB_H # undef HAVE_ZLIB #endif #ifdef HAVE_ZLIB # include # define MAX_UNZIP_SZ 100000 # define COMPRESS_MIN 600 #endif #ifdef HAVE_SYS_TIME_H # include #endif #include #include "conffile.h" #include "inn/fdflag.h" #include "inn/innconf.h" #include "inn/libinn.h" #include "inn/messages.h" #include "inn/newsuser.h" #include "inn/paths.h" #include "inn/storage.h" #ifdef HAVE_UNIX_DOMAIN_SOCKETS # include "portable/socket-unix.h" #endif #include "inn/ov.h" #include "ovinterface.h" #include "ovdb.h" #include "ovdb-private.h" #ifndef HAVE_BDB /* Provide stub functions if we don't have db */ bool ovdb_open(int mode UNUSED) { warn("OVDB: ovdb support not enabled"); return false; } bool ovdb_groupstats(const char *group UNUSED, int *lo UNUSED, int *hi UNUSED, int *count UNUSED, int *flag UNUSED) { return false; } bool ovdb_groupadd(const char *group UNUSED, ARTNUM lo UNUSED, ARTNUM hi UNUSED, char *flag UNUSED) { return false; } bool ovdb_groupdel(const char *group UNUSED) { return false; } bool ovdb_add(const char *group UNUSED, ARTNUM artnum UNUSED, TOKEN token UNUSED, char *data UNUSED, int len UNUSED, time_t arrived UNUSED, time_t expires UNUSED) { return false; } bool ovdb_cancel(const char *group UNUSED, ARTNUM artnum UNUSED) { return false; } void *ovdb_opensearch(const char *group UNUSED, int low UNUSED, int high UNUSED) { return NULL; } bool ovdb_search(void *handle UNUSED, ARTNUM *artnum UNUSED, char **data UNUSED, int *len UNUSED, TOKEN *token UNUSED, time_t *arrived UNUSED) { return false; } void ovdb_closesearch(void *handle UNUSED) { } bool ovdb_getartinfo(const char *group UNUSED, ARTNUM artnum UNUSED, TOKEN *token UNUSED) { return false; } bool ovdb_expiregroup(const char *group UNUSED, int *lo UNUSED, struct history *h UNUSED) { return false; } bool ovdb_ctl(OVCTLTYPE type UNUSED, void *val UNUSED) { return false; } void ovdb_close(void) { } #else /* HAVE_BDB */ #define EXPIREGROUP_TXN_SIZE 100 #define DELETE_TXN_SIZE 500 int ovdb_data_ver = 0; struct ovdb_conf ovdb_conf; DB_ENV *OVDBenv = NULL; static int OVDBmode; static bool Cutofflow; static DB **dbs = NULL; static int oneatatime = 0; static int current_db = -1; static time_t eo_start = 0; static int clientmode = 0; static DB *groupinfo = NULL; static DB *groupaliases = NULL; #define OVDBtxn_nosync 1 #define OVDBnumdbfiles 2 #define OVDBpagesize 3 #define OVDBcachesize 4 #define OVDBminkey 5 #define OVDBmaxlocks 6 #define OVDBnocompact 7 #define OVDBreadserver 8 #define OVDBnumrsprocs 9 #define OVDBmaxrsconn 10 #define OVDBuseshm 11 #define OVDBshmkey 12 #define OVDBcompress 13 #define OVDBncache 14 static CONFTOKEN toks[] = { { OVDBtxn_nosync, (char *) "txn_nosync" }, { OVDBnumdbfiles, (char *) "numdbfiles" }, { OVDBpagesize, (char *) "pagesize" }, { OVDBcachesize, (char *) "cachesize" }, { OVDBminkey, (char *) "minkey" }, { OVDBmaxlocks, (char *) "maxlocks" }, { OVDBnocompact, (char *) "nocompact" }, { OVDBreadserver, (char *) "readserver" }, { OVDBnumrsprocs, (char *) "numrsprocs" }, { OVDBmaxrsconn, (char *) "maxrsconn" }, { OVDBuseshm, (char *) "useshm" }, { OVDBshmkey, (char *) "shmkey" }, { OVDBcompress, (char *) "compress" }, { OVDBncache, (char *) "ncache" }, { 0, NULL } }; #define _PATH_OVDBCONF "ovdb.conf" /*********** readserver functions ***********/ static int clientfd = -1; /* read client send and recieve functions. */ static int csend(const void *data, int n) { ssize_t status; if (n == 0) return 0; status = xwrite(clientfd, data, n); if (status < 0) syswarn("OVDB: rc: cant write"); return status; } static int crecv(void *data, int n) { int r, p = 0; if(n == 0) return 0; while(p < n) { r = read(clientfd, (char *)data + p, n - p); if(r <= 0) { if(r < 0 && errno == EINTR) continue; syswarn("OVDB: rc: cant read"); clientfd = -1; exit(1); } p+= r; } return p; } /* Attempt to connect to the readserver. If anything fails, we return -1 so that ovdb_open can open the database directly. */ static int client_connect(void) { ssize_t r; size_t p = 0; char *path; #ifdef HAVE_UNIX_DOMAIN_SOCKETS struct sockaddr_un sa; #else struct sockaddr_in sa; #endif char banner[sizeof(OVDB_SERVER_BANNER)]; fd_set fds; struct timeval timeout; #ifdef HAVE_UNIX_DOMAIN_SOCKETS clientfd = socket(AF_UNIX, SOCK_STREAM, 0); #else clientfd = socket(AF_INET, SOCK_STREAM, 0); #endif if(clientfd < 0) { syswarn("OVDB: socket"); return -1; } memset(&sa, 0, sizeof sa); #ifdef HAVE_UNIX_DOMAIN_SOCKETS sa.sun_family = AF_UNIX; path = concatpath(innconf->pathrun, OVDB_SERVER_SOCKET); strlcpy(sa.sun_path, path, sizeof(sa.sun_path)); free(path); r = connect(clientfd, (struct sockaddr *) &sa, SUN_LEN(&sa)); #else sa.sin_family = AF_INET; sa.sin_port = 0; sa.sin_addr.s_addr = htonl(0x7f000001UL); bind(clientfd, (struct sockaddr *) &sa, sizeof sa); sa.sin_port = htons(OVDB_SERVER_PORT); r = connect(clientfd, (struct sockaddr *) &sa, sizeof sa); #endif if(r != 0) { syswarn("OVDB: rc: cant connect to server"); close(clientfd); clientfd = -1; return -1; } while(p < sizeof(OVDB_SERVER_BANNER)) { FD_ZERO(&fds); FD_SET(clientfd, &fds); timeout.tv_sec = 30; timeout.tv_usec = 0; r = select(clientfd+1, &fds, NULL, NULL, &timeout); if(r < 0) { if(errno == EINTR) continue; syswarn("OVDB: select"); close(clientfd); clientfd = -1; return -1; } if(r == 0) { warn("OVDB: rc: timeout waiting for server"); close(clientfd); clientfd = -1; return -1; } r = read(clientfd, banner + p, sizeof(OVDB_SERVER_BANNER) - p); if(r <= 0) { if(r < 0 && errno == EINTR) continue; syswarn("OVDB: rc: cant read"); close(clientfd); clientfd = -1; return -1; } p+= r; } if(memcmp(banner, OVDB_SERVER_BANNER, sizeof(OVDB_SERVER_BANNER))) { warn("OVDB: rc: unknown reply from server"); close(clientfd); clientfd = -1; return -1; } return 0; } static void client_disconnect(void) { struct rs_cmd rs; if (clientfd != -1) { rs.what = CMD_QUIT; csend(&rs, sizeof(rs)); } clientfd = -1; } /*********** internal functions ***********/ static bool conf_bool_val(char *str, bool *value) { if(strcasecmp(str, "on") == 0 || strcasecmp(str, "true") == 0 || strcasecmp(str, "yes") == 0) { *value = true; return true; } if(strcasecmp(str, "off") == 0 || strcasecmp(str, "false") == 0 || strcasecmp(str, "no") == 0) { *value = false; return true; } return false; } static bool conf_long_val(char *str, long *value) { long v; errno = 0; v = strtol(str, NULL, 10); if(v == 0 && errno != 0) { return false; } *value = v; return true; } void read_ovdb_conf(void) { static int confread = 0; int done = 0; char *path; CONFFILE *f; CONFTOKEN *tok; bool b; long l; if(confread) return; /* defaults */ ovdb_conf.home = innconf->pathoverview; ovdb_conf.txn_nosync = 1; ovdb_conf.numdbfiles = 32; ovdb_conf.pagesize = 8192; ovdb_conf.cachesize = 8000 * 1024; ovdb_conf.ncache = 1; ovdb_conf.minkey = 0; ovdb_conf.maxlocks = 4000; ovdb_conf.nocompact = 1; ovdb_conf.readserver = 0; ovdb_conf.numrsprocs = 5; ovdb_conf.maxrsconn = 0; ovdb_conf.useshm = 0; ovdb_conf.shmkey = 6400; ovdb_conf.compress = 0; path = concatpath(innconf->pathetc, _PATH_OVDBCONF); f = CONFfopen(path); free(path); if(f) { while(!done && (tok = CONFgettoken(toks, f))) { switch(tok->type) { case OVDBtxn_nosync: tok = CONFgettoken(0, f); if(!tok) { done = 1; continue; } if(conf_bool_val(tok->name, &b)) { ovdb_conf.txn_nosync = b; } break; case OVDBnumdbfiles: tok = CONFgettoken(0, f); if(!tok) { done = 1; continue; } if(conf_long_val(tok->name, &l) && l > 0) { ovdb_conf.numdbfiles = l; } break; case OVDBpagesize: tok = CONFgettoken(0, f); if(!tok) { done = 1; continue; } if(conf_long_val(tok->name, &l) && l > 0) { ovdb_conf.pagesize = l; } break; case OVDBcachesize: tok = CONFgettoken(0, f); if(!tok) { done = 1; continue; } if(conf_long_val(tok->name, &l) && l > 0) { ovdb_conf.cachesize = l * 1024; } break; case OVDBncache: tok = CONFgettoken(0, f); if(!tok) { done = 1; continue; } if(conf_long_val(tok->name, &l) && l > 0) { ovdb_conf.ncache = l; } break; case OVDBminkey: tok = CONFgettoken(0, f); if(!tok) { done = 1; continue; } if(conf_long_val(tok->name, &l) && l > 1) { ovdb_conf.minkey = l; } break; case OVDBmaxlocks: tok = CONFgettoken(0, f); if(!tok) { done = 1; continue; } if(conf_long_val(tok->name, &l) && l > 0) { ovdb_conf.maxlocks = l; } break; case OVDBnocompact: tok = CONFgettoken(0, f); if(!tok) { done = 1; continue; } if(conf_long_val(tok->name, &l) && l >= 0) { ovdb_conf.nocompact = l; } break; case OVDBreadserver: tok = CONFgettoken(0, f); if(!tok) { done = 1; continue; } if(conf_bool_val(tok->name, &b)) { ovdb_conf.readserver = b; } break; case OVDBnumrsprocs: tok = CONFgettoken(0, f); if(!tok) { done = 1; continue; } if(conf_long_val(tok->name, &l) && l > 0) { ovdb_conf.numrsprocs = l; } break; case OVDBmaxrsconn: tok = CONFgettoken(0, f); if(!tok) { done = 1; continue; } if(conf_long_val(tok->name, &l) && l >= 0) { ovdb_conf.maxrsconn = l; } break; case OVDBuseshm: tok = CONFgettoken(0, f); if(!tok) { done = 1; continue; } if(conf_bool_val(tok->name, &b)) { ovdb_conf.useshm = b; } break; case OVDBshmkey: tok = CONFgettoken(0, f); if(!tok) { done = 1; continue; } if(conf_long_val(tok->name, &l) && l >= 0) { ovdb_conf.shmkey = l; } break; case OVDBcompress: #ifdef HAVE_ZLIB tok = CONFgettoken(0, f); if(!tok) { done = 1; continue; } if(conf_bool_val(tok->name, &b)) { ovdb_conf.compress = b; } #endif break; } } CONFfclose(f); } /* If user did not specify minkey, choose one based on pagesize */ if(ovdb_conf.minkey == 0) { if(ovdb_conf.compress) { ovdb_conf.minkey = ovdb_conf.pagesize / 1500; } else { ovdb_conf.minkey = ovdb_conf.pagesize / 2600; } if(ovdb_conf.minkey < 2) ovdb_conf.minkey = 2; } confread = 1; } /* Function that db will use to report errors */ static void OVDBerror(const DB_ENV *dbenv UNUSED, const char *db_errpfx UNUSED, const char *buffer) { warn("OVDB: %s", buffer); } static u_int32_t _db_flags = 0; static int open_db_file(int which) { int ret; char name[10]; DB_TXN *tid; if(dbs[which] != NULL) return 0; snprintf(name, sizeof(name), "ov%05d", which); ret = db_create(&(dbs[which]), OVDBenv, 0); if (ret != 0) return ret; if(ovdb_conf.minkey > 0) (dbs[which])->set_bt_minkey(dbs[which], ovdb_conf.minkey); if(ovdb_conf.pagesize > 0) (dbs[which])->set_pagesize(dbs[which], ovdb_conf.pagesize); TXN_START_NORETRY(t_open_db_file, tid); ret = (dbs[which])->open(dbs[which], tid, name, NULL, DB_BTREE, _db_flags, 0666); if (ret == 0) TXN_COMMIT(t_open_db_file, tid); if (ret != 0) { (dbs[which])->close(dbs[which], 0); dbs[which] = NULL; return ret; } return 0; } static void close_db_file(int which) { if(which == -1 || dbs[which] == NULL) return; dbs[which]->close(dbs[which], 0); dbs[which] = NULL; } static int which_db(const char *group) { HASH grouphash; unsigned int i; grouphash = Hash(group, strlen(group)); memcpy(&i, &grouphash, sizeof(i)); return i % ovdb_conf.numdbfiles; } static DB *get_db_bynum(int which) { int ret; if(which >= ovdb_conf.numdbfiles) return NULL; if(oneatatime) { if(which != current_db && current_db != -1) close_db_file(current_db); ret = open_db_file(which); if (ret != 0) warn("OVDB: open_db_file failed: %s", db_strerror(ret)); current_db = which; } return(dbs[which]); } int ovdb_getgroupinfo(const char *group, struct groupinfo *gi, int ignoredeleted, DB_TXN *tid, int getflags) { int ret; DBT key, val; if(group == NULL) /* just in case */ return DB_NOTFOUND; memset(&key, 0, sizeof key); memset(&val, 0, sizeof val); key.data = (char *) group; key.size = strlen(group); val.data = gi; val.ulen = sizeof(struct groupinfo); val.flags = DB_DBT_USERMEM; ret = groupinfo->get(groupinfo, tid, &key, &val, getflags); if (ret != 0) return ret; if(val.size != sizeof(struct groupinfo)) { warn("OVDB: wrong size for %s groupinfo (%u)", group, val.size); return DB_NOTFOUND; } if(ignoredeleted && (gi->status & GROUPINFO_DELETED)) return DB_NOTFOUND; return 0; } #define GROUPID_MAX_FREELIST 10240 #define GROUPID_MIN_FREELIST 100 /* allocate a new group ID and return in gno */ /* must be used in a transaction */ static int groupid_new(group_id_t *gno, DB_TXN *tid) { DBT key, val; int ret, n; group_id_t newgno, *freelist, one; memset(&key, 0, sizeof key); memset(&val, 0, sizeof val); key.data = (char *) "!groupid_freelist"; key.size = sizeof("!groupid_freelist"); ret = groupinfo->get(groupinfo, tid, &key, &val, DB_RMW); if (ret != 0) { if(ret == DB_NOTFOUND) { val.size = sizeof(group_id_t); val.data = &one; one = 1; } else { return ret; } } if(val.size % sizeof(group_id_t)) { warn("OVDB: invalid size (%d) for !groupid_freelist", val.size); return EINVAL; } n = val.size / sizeof(group_id_t); freelist = xmalloc(n * sizeof(group_id_t)); memcpy(freelist, val.data, val.size); if(n <= GROUPID_MIN_FREELIST ) { newgno = freelist[n-1]; (freelist[n-1])++; val.data = freelist; } else { newgno = freelist[0]; val.data = &(freelist[1]); val.size -= sizeof(group_id_t); } ret = groupinfo->put(groupinfo, tid, &key, &val, 0); if (ret != 0) { free(freelist); return ret; } free(freelist); *gno = newgno; return 0; } /* mark given group ID as "unused" */ /* must be used in a transaction */ static int groupid_free(group_id_t gno, DB_TXN *tid) { DBT key, val; int ret, n, i; group_id_t *freelist; memset(&key, 0, sizeof key); memset(&val, 0, sizeof val); key.data = (char *) "!groupid_freelist"; key.size = sizeof("!groupid_freelist"); ret = groupinfo->get(groupinfo, tid, &key, &val, DB_RMW); if (ret != 0) { return ret; } if(val.size % sizeof(group_id_t)) { warn("OVDB: invalid size (%d) for !groupid_freelist", val.size); return EINVAL; } n = val.size / sizeof(group_id_t); if(n > GROUPID_MAX_FREELIST) return 0; freelist = xmalloc((n + 1) * sizeof(group_id_t)); memcpy(freelist, val.data, val.size); if(gno >= freelist[n-1]) { /* shouldn't happen */ free(freelist); return 0; } for(i = 0; i < n-1; i++) { if(gno == freelist[i]) { /* already on freelist */ free(freelist); return 0; } } freelist[n] = freelist[n-1]; freelist[n-1] = gno; val.data = freelist; val.size += sizeof(group_id_t); ret = groupinfo->put(groupinfo, tid, &key, &val, 0); free(freelist); return ret; } /* Must be called outside of a transaction because it makes its own transactions */ static int delete_all_records(int whichdb, group_id_t gno) { DB *db; DBC *dbcursor; DBT key, val; struct datakey dk; int count; int ret = 0; DB_TXN *tid; memset(&key, 0, sizeof key); memset(&val, 0, sizeof val); memset(&dk, 0, sizeof dk); db = get_db_bynum(whichdb); if(db == NULL) return DB_NOTFOUND; dk.groupnum = gno; dk.artnum = 0; while(1) { TXN_START(t_del, tid); /* get a cursor to traverse the ov records and delete them */ ret = db->cursor(db, tid, &dbcursor, 0); switch(ret) { case 0: break; case TRYAGAIN: TXN_RETRY(t_del, tid); default: TXN_ABORT(t_del, tid); warn("OVDB: delete_all_records: db->cursor: %s", db_strerror(ret)); return ret; } key.data = &dk; key.size = sizeof dk; val.flags = DB_DBT_PARTIAL; for(count = 0; count < DELETE_TXN_SIZE; count++) { ret = dbcursor->c_get(dbcursor, &key, &val, count ? DB_NEXT : DB_SET_RANGE); switch (ret) { case 0: break; case DB_NOTFOUND: dbcursor->c_close(dbcursor); TXN_COMMIT(t_del, tid); return 0; case TRYAGAIN: dbcursor->c_close(dbcursor); TXN_RETRY(t_del, tid); default: warn("OVDB: delete_all_records: DBcursor->c_get: %s", db_strerror(ret)); dbcursor->c_close(dbcursor); TXN_ABORT(t_del, tid); return ret; } if(key.size == sizeof dk && memcmp(key.data, &gno, sizeof gno)) { break; } ret = dbcursor->c_del(dbcursor, 0); switch (ret) { case 0: case DB_KEYEMPTY: break; case TRYAGAIN: dbcursor->c_close(dbcursor); TXN_RETRY(t_del, tid); default: warn("OVDB: delete_all_records: DBcursor->c_del: %s", db_strerror(ret)); dbcursor->c_close(dbcursor); TXN_ABORT(t_del, tid); return ret; } } dbcursor->c_close(dbcursor); TXN_COMMIT(t_del, tid); if(count < DELETE_TXN_SIZE) { break; } } return 0; } /* Make a temporary groupinfo key using the given db number and group ID. Must be called in a transaction */ static int mk_temp_groupinfo(int whichdb, group_id_t gno, DB_TXN *tid) { char keystr[1 + sizeof gno]; DBT key, val; struct groupinfo gi; int ret; memset(&key, 0, sizeof key); memset(&val, 0, sizeof val); memset(&gi, 0, sizeof gi); keystr[0] = 0; memcpy(keystr + 1, &gno, sizeof gno); gi.current_db = whichdb; gi.current_gid = gno; gi.status = GROUPINFO_DELETED; key.data = keystr; key.size = sizeof keystr; val.data = &gi; val.size = sizeof gi; ret = groupinfo->put(groupinfo, tid, &key, &val, 0); switch (ret) { case 0: break; default: warn("OVDB: mk_temp_groupinfo: groupinfo->put: %s", db_strerror(ret)); case TRYAGAIN: return ret; } return 0; } /* Delete a temporary groupinfo key created by mk_temp_groupid, then frees the group id. Must NOT be called within a transaction. */ static int rm_temp_groupinfo(group_id_t gno) { char keystr[1 + sizeof gno]; DB_TXN *tid; DBT key; int ret = 0; memset(&key, 0, sizeof key); keystr[0] = 0; memcpy(keystr + 1, &gno, sizeof gno); key.data = keystr; key.size = sizeof keystr; TXN_START(t_tmp, tid); ret = groupinfo->del(groupinfo, tid, &key, 0); switch(ret) { case 0: case DB_NOTFOUND: break; case TRYAGAIN: TXN_RETRY(t_tmp, tid); default: TXN_ABORT(t_tmp, tid); warn("OVDB: rm_temp_groupinfo: groupinfo->del: %s", db_strerror(ret)); return ret; } switch(groupid_free(gno, tid)) { case 0: break; case TRYAGAIN: TXN_RETRY(t_tmp, tid); default: TXN_ABORT(t_tmp, tid); warn("OVDB: rm_temp_groupinfo: groupid_free: %s", db_strerror(ret)); return ret; } TXN_COMMIT(t_tmp, tid); return 0; } /* This function deletes overview records for deleted or forgotton groups */ /* argument: 0 = process deleted groups 1 = process forgotton groups */ static bool delete_old_stuff(int forgotton) { DBT key, val; DBC *cursor; DB_TXN *tid; struct groupinfo gi; char **dellist = NULL; size_t *dellistsz = NULL; int listlen, listcount = 0; int i, ret = 0; TXN_START(t_dellist, tid); if (dellist != NULL) { for (i = 0; i < listcount; ++i) free(dellist[i]); free(dellist); } if (dellistsz != NULL) free(dellistsz); listlen = 20; listcount = 0; dellist = xmalloc(listlen * sizeof(char *)); dellistsz = xmalloc(listlen * sizeof(size_t)); memset(&key, 0, sizeof key); memset(&val, 0, sizeof val); val.data = &gi; val.ulen = val.dlen = sizeof gi; val.flags = DB_DBT_USERMEM | DB_DBT_PARTIAL; /* get a cursor to traverse all of the groupinfo records */ ret = groupinfo->cursor(groupinfo, tid, &cursor, 0); if (ret != 0) { warn("OVDB: delete_old_stuff: groupinfo->cursor: %s", db_strerror(ret)); free(dellist); free(dellistsz); return false; } while((ret = cursor->c_get(cursor, &key, &val, DB_NEXT)) == 0) { if(key.size == sizeof("!groupid_freelist") && !strcmp("!groupid_freelist", key.data)) continue; if(val.size != sizeof(struct groupinfo)) { warn("OVDB: delete_old_stuff: wrong size for groupinfo record"); continue; } if((!forgotton && (gi.status & GROUPINFO_DELETED)) || (forgotton && (gi.expired < eo_start))) { dellist[listcount] = xmalloc(key.size); memcpy(dellist[listcount], key.data, key.size); dellistsz[listcount] = key.size; listcount++; if(listcount >= listlen) { listlen += 20; dellist = xrealloc(dellist, listlen * sizeof(char *)); dellistsz = xrealloc(dellistsz, listlen * sizeof(size_t)); } } } cursor->c_close(cursor); switch (ret) { case 0: case DB_NOTFOUND: TXN_COMMIT(t_dellist, tid); break; case TRYAGAIN: TXN_RETRY(t_dellist, tid); default: TXN_ABORT(t_dellist, tid); warn("OVDB: delete_old_stuff: cursor->c_get: %s", db_strerror(ret)); for (i = 0; i < listcount; ++i) free(dellist[i]); free(dellist); free(dellistsz); return false; } for(i = 0; i < listcount; i++) { TXN_START(t_dos, tid); /* Can't use ovdb_getgroupinfo here */ key.data = dellist[i]; key.size = dellistsz[i]; val.data = &gi; val.ulen = sizeof(struct groupinfo); val.flags = DB_DBT_USERMEM; ret = groupinfo->get(groupinfo, tid, &key, &val, 0); switch (ret) { case 0: break; case TRYAGAIN: TXN_RETRY(t_dos, tid); case DB_NOTFOUND: TXN_ABORT(t_dos, tid); continue; default: warn("OVDB: delete_old_stuff: groupinfo->get: %s", db_strerror(ret)); TXN_ABORT(t_dos, tid); continue; } if(val.size != sizeof(struct groupinfo)) { TXN_ABORT(t_dos, tid); continue; } /* This if() is true if this ISN'T a key created by mk_temp_groupinfo */ if(*((char *)key.data) != 0 || key.size != 1 + sizeof(group_id_t)) { switch(mk_temp_groupinfo(gi.current_db, gi.current_gid, tid)) { case 0: break; case TRYAGAIN: TXN_RETRY(t_dos, tid); default: TXN_ABORT(t_dos, tid); continue; } if(gi.status & GROUPINFO_MOVING) { switch(mk_temp_groupinfo(gi.new_db, gi.new_gid, tid)) { case 0: break; case TRYAGAIN: TXN_RETRY(t_dos, tid); default: TXN_ABORT(t_dos, tid); continue; } } /* delete the corresponding groupaliases record (if exists) */ switch(groupaliases->del(groupaliases, tid, &key, 0)) { case 0: case DB_NOTFOUND: break; case TRYAGAIN: TXN_RETRY(t_dos, tid); default: TXN_ABORT(t_dos, tid); continue; } switch(groupinfo->del(groupinfo, tid, &key, 0)) { case 0: case DB_NOTFOUND: break; case TRYAGAIN: TXN_RETRY(t_dos, tid); default: TXN_ABORT(t_dos, tid); continue; } } TXN_COMMIT(t_dos, tid); if(delete_all_records(gi.current_db, gi.current_gid) == 0) { rm_temp_groupinfo(gi.current_gid); } if(gi.status & GROUPINFO_MOVING) { if(delete_all_records(gi.new_db, gi.new_gid) == 0) { rm_temp_groupinfo(gi.new_gid); } } } for(i = 0; i < listcount; i++) free(dellist[i]); free(dellist); free(dellistsz); return true; } static int count_records(struct groupinfo *gi) { int ret; DB *db; DBC *cursor; DBT key, val; struct datakey dk; u_int32_t artnum, newlow = 0; memset(&key, 0, sizeof key); memset(&val, 0, sizeof val); memset(&dk, 0, sizeof dk); db = get_db_bynum(gi->current_db); if(db == NULL) return DB_NOTFOUND; dk.groupnum = gi->current_gid; dk.artnum = 0; key.data = &dk; key.size = key.ulen = sizeof dk; key.flags = DB_DBT_USERMEM; val.flags = DB_DBT_PARTIAL; gi->count = 0; ret = db->cursor(db, NULL, &cursor, 0); if (ret) return ret; ret = cursor->c_get(cursor, &key, &val, DB_SET_RANGE); switch (ret) { case 0: case DB_NOTFOUND: break; default: cursor->c_close(cursor); return ret; } while(ret == 0 && key.size == sizeof(dk) && dk.groupnum == gi->current_gid) { artnum = ntohl(dk.artnum); if(newlow == 0 || newlow > artnum) newlow = artnum; if(artnum > gi->high) gi->high = artnum; gi->count++; ret = cursor->c_get(cursor, &key, &val, DB_NEXT); } cursor->c_close(cursor); if(gi->count == 0) gi->low = gi->high + 1; else gi->low = newlow; if(ret == DB_NOTFOUND) return 0; return ret; } /* * Locking: OVopen() calls ovdb_getlock(OVDB_LOCK_NORMAL). This * aquires a read (shared) lock on the lockfile. Multiple processes * can have this file locked at the same time. That way, if there * are any processes that are using the database, the lock file will * have one or more shared locks on it. * * ovdb_init, when starting, calls ovdb_getlock(OVDB_LOCK_EXCLUSIVE). * This tries to get a write (exclusive) lock, which will fail if * anyone has a shared lock. This way, ovdb_init can tell if there * are any processes using the database. If not, and the excl. lock * succeeds, ovdb_init is free to do DB_RUNRECOVER. * * ovdb_getlock() in the "normal" lock mode calls ovdb_check_monitor, * which looks for the OVDB_MONITOR_PIDFILE. If said file does not * exist, or the PID in it does not exist, it will fail. This will * prevent OVopen() from opening the database if ovdb_monitor is not running. * * The OVDB_LOCK_ADMIN mode is used by ovdb_monitor to get a shared lock * without testing the pidfile. */ static int lockfd = -1; bool ovdb_getlock(int mode) { if(lockfd == -1) { char *lockfn = concatpath(innconf->pathrun, OVDB_LOCKFN); lockfd = open(lockfn, mode == OVDB_LOCK_NORMAL ? O_RDWR : O_CREAT|O_RDWR, 0660); if(lockfd == -1) { free(lockfn); if(errno == ENOENT) warn("OVDB: can not open database unless ovdb_monitor is" " running"); return false; } fdflag_close_exec(lockfd, true); free(lockfn); } else return true; if(mode == OVDB_LOCK_NORMAL) { if(!ovdb_check_pidfile(OVDB_MONITOR_PIDFILE)) { warn("OVDB: can not open database unless ovdb_monitor is" " running"); return false; } } if(mode == OVDB_LOCK_EXCLUSIVE) { if(!inn_lock_file(lockfd, INN_LOCK_WRITE, false)) { close(lockfd); lockfd = -1; return false; } return true; } else { if(!inn_lock_file(lockfd, INN_LOCK_READ, false)) { close(lockfd); lockfd = -1; return false; } return true; } } bool ovdb_releaselock(void) { bool r; if(lockfd == -1) return true; r = inn_lock_file(lockfd, INN_LOCK_UNLOCK, false); close(lockfd); lockfd = -1; return r; } bool ovdb_check_pidfile(const char *file) { int f, pid; char buf[SMBUF]; char *pidfn = concatpath(innconf->pathrun, file); f = open(pidfn, O_RDONLY); if(f == -1) { if(errno != ENOENT) syswarn("OVDB: can't open %s", pidfn); free(pidfn); return false; } memset(buf, 0, SMBUF); if(read(f, buf, SMBUF-1) < 0) { syswarn("OVDB: can't read from %s", pidfn); free(pidfn); close(f); return false; } close(f); free(pidfn); pid = atoi(buf); if(pid <= 1) { return false; } if(kill(pid, 0) < 0 && errno == ESRCH) { return false; } return true; } /* Make sure the effective uid is that of the runasuser user. */ bool ovdb_check_user(void) { static int result = -1; if(result == -1) { int rv; uid_t uid; rv = get_news_uid_gid(&uid, false, false); if (rv != 0) { syswarn("OVDB: can't resolve runasuser user to a UID"); return false; } result = (uid == geteuid()); } return result; } static int check_version(void) { int ret; DB *vdb; DBT key, val; u_int32_t dv; /* open version db */ ret = db_create(&vdb, OVDBenv, 0); if (ret != 0) { warn("OVDB: open: db_create: %s", db_strerror(ret)); return ret; } ret = vdb->open(vdb, NULL, "version", NULL, DB_BTREE, _db_flags, 0666); if (ret != 0) { vdb->close(vdb, 0); warn("OVDB: open: version->open: %s", db_strerror(ret)); return ret; } memset(&key, 0, sizeof key); memset(&val, 0, sizeof val); key.data = (char *) "dataversion"; key.size = sizeof("dataversion"); ret = vdb->get(vdb, NULL, &key, &val, 0); if (ret != 0) { if(ret != DB_NOTFOUND) { warn("OVDB: open: can't retrieve version: %s", db_strerror(ret)); vdb->close(vdb, 0); return ret; } } if(ret == DB_NOTFOUND || val.size != sizeof dv) { dv = ovdb_conf.compress ? DATA_VERSION_COMPRESS : DATA_VERSION; if(!(OVDBmode & OV_WRITE)) { vdb->close(vdb, 0); return EACCES; } val.data = &dv; val.size = sizeof dv; ret = vdb->put(vdb, NULL, &key, &val, 0); if (ret != 0) { warn("OVDB: open: can't store version: %s", db_strerror(ret)); vdb->close(vdb, 0); return ret; } } else memcpy(&dv, val.data, sizeof dv); if(dv > DATA_VERSION_COMPRESS) { warn("OVDB: can't open database: unknown version %d", dv); vdb->close(vdb, 0); return EINVAL; } if(dv < DATA_VERSION) { warn("OVDB: database is an old version, please run ovdb_init"); vdb->close(vdb, 0); return EINVAL; } #ifndef HAVE_ZLIB if(dv == DATA_VERSION_COMPRESS) { warn("OVDB: database is compressed but INN was not built with zlib"); vdb->close(vdb, 0); return EINVAL; } #endif if(ovdb_conf.compress && dv == DATA_VERSION && (OVDBmode & OV_WRITE)) { /* "Upgrade" the database to indicate there may be compressed records */ dv = DATA_VERSION_COMPRESS; val.data = &dv; val.size = sizeof dv; ret = vdb->put(vdb, NULL, &key, &val, 0); if (ret != 0) { warn("OVDB: open: can't store version: %s", db_strerror(ret)); vdb->close(vdb, 0); return ret; } } ovdb_data_ver = dv; /* The version db also stores some config values, which will override the corresponding ovdb.conf setting. */ key.data = (char *) "numdbfiles"; key.size = sizeof("numdbfiles"); ret = vdb->get(vdb, NULL, &key, &val, 0); if (ret != 0) { if(ret != DB_NOTFOUND) { warn("OVDB: open: can't retrieve numdbfiles: %s", db_strerror(ret)); vdb->close(vdb, 0); return ret; } } if(ret == DB_NOTFOUND || val.size != sizeof(ovdb_conf.numdbfiles)) { if(!(OVDBmode & OV_WRITE)) { vdb->close(vdb, 0); return EACCES; } val.data = &(ovdb_conf.numdbfiles); val.size = sizeof(ovdb_conf.numdbfiles); ret = vdb->put(vdb, NULL, &key, &val, 0); if (ret != 0) { warn("OVDB: open: can't store numdbfiles: %s", db_strerror(ret)); vdb->close(vdb, 0); return ret; } } else { memcpy(&(ovdb_conf.numdbfiles), val.data, sizeof(ovdb_conf.numdbfiles)); } vdb->close(vdb, 0); return 0; } int ovdb_open_berkeleydb(int mode, int flags) { int ret; u_int32_t ai_flags = DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN; OVDBmode = mode; read_ovdb_conf(); if(OVDBenv != NULL) return 0; /* already opened */ if(OVDBmode & OV_WRITE) { _db_flags |= DB_CREATE; ai_flags |= DB_CREATE; } else { _db_flags |= DB_RDONLY; } if(flags & OVDB_RECOVER) ai_flags |= DB_RECOVER; ret = db_env_create(&OVDBenv, 0); if (ret != 0) { warn("OVDB: db_env_create: %s", db_strerror(ret)); return ret; } if((flags & (OVDB_UPGRADE | OVDB_RECOVER)) == (OVDB_UPGRADE | OVDB_RECOVER)) ai_flags |= DB_PRIVATE; if(!(ai_flags & DB_PRIVATE)) { if(ovdb_conf.useshm) ai_flags |= DB_SYSTEM_MEM; OVDBenv->set_shm_key(OVDBenv, ovdb_conf.shmkey); } OVDBenv->set_errcall(OVDBenv, OVDBerror); OVDBenv->set_cachesize(OVDBenv, 0, ovdb_conf.cachesize, ovdb_conf.ncache); OVDBenv->set_lk_max_locks(OVDBenv, ovdb_conf.maxlocks); OVDBenv->set_lk_max_lockers(OVDBenv, ovdb_conf.maxlocks); OVDBenv->set_lk_max_objects(OVDBenv, ovdb_conf.maxlocks); if(ovdb_conf.txn_nosync) OVDBenv->set_flags(OVDBenv, DB_TXN_NOSYNC, 1); if((flags & (OVDB_UPGRADE | OVDB_RECOVER)) != OVDB_UPGRADE) { ret = OVDBenv->open(OVDBenv, ovdb_conf.home, ai_flags, 0666); if (ret != 0) { OVDBenv->close(OVDBenv, 0); OVDBenv = NULL; warn("OVDB: OVDBenv->open: %s", db_strerror(ret)); return ret; } } return 0; } bool ovdb_open(int mode) { int i, ret; DB_TXN *tid; if(OVDBenv != NULL || clientmode) { warn("OVDB: ovdb_open called more than once"); return false; } read_ovdb_conf(); if(ovdb_conf.readserver && mode == OV_READ) clientmode = 1; if(mode & OVDB_SERVER) clientmode = 0; if(clientmode) { if(client_connect() == 0) return true; clientmode = 0; } if(!ovdb_check_user()) { warn("OVDB: must be running as runasuser user to access overview"); return false; } if(!ovdb_getlock(OVDB_LOCK_NORMAL)) { syswarn("OVDB: ovdb_getlock failed"); return false; } if(ovdb_open_berkeleydb(mode, 0) != 0) return false; if(check_version() != 0) return false; if(mode & OV_WRITE || mode & OVDB_SERVER) { oneatatime = 0; } else { oneatatime = 1; } dbs = xcalloc(ovdb_conf.numdbfiles, sizeof(DB *)); if(!oneatatime) { for(i = 0; i < ovdb_conf.numdbfiles; i++) { ret = open_db_file(i); if (ret != 0) { warn("OVDB: open_db_file failed: %s", db_strerror(ret)); return false; } } } ret = db_create(&groupinfo, OVDBenv, 0); if (ret != 0) { warn("OVDB: open: db_create: %s", db_strerror(ret)); return false; } TXN_START_NORETRY(t_open_groupinfo, tid); ret = groupinfo->open(groupinfo, tid, "groupinfo", NULL, DB_BTREE, _db_flags, 0666); if (ret == 0) TXN_COMMIT(t_open_groupinfo, tid); if (ret != 0) { groupinfo->close(groupinfo, 0); warn("OVDB: open: groupinfo->open: %s", db_strerror(ret)); return false; } ret = db_create(&groupaliases, OVDBenv, 0); if (ret != 0) { warn("OVDB: open: db_create: %s", db_strerror(ret)); return false; } TXN_START_NORETRY(t_open_groupaliases, tid); ret = groupaliases->open(groupaliases, tid, "groupaliases", NULL, DB_HASH, _db_flags, 0666); if (ret == 0) TXN_COMMIT(t_open_groupaliases, tid); if (ret != 0) { groupaliases->close(groupaliases, 0); warn("OVDB: open: groupaliases->open: %s", db_strerror(ret)); return false; } Cutofflow = false; return true; } bool ovdb_groupstats(const char *group, int *lo, int *hi, int *count, int *flag) { int ret; struct groupinfo gi; if (clientmode) { struct rs_cmd rs; struct rs_groupstats repl; rs.what = CMD_GROUPSTATS; rs.grouplen = strlen(group)+1; if (csend(&rs, sizeof(rs)) < 0) return false; if (csend(group, rs.grouplen) < 0) return false; crecv(&repl, sizeof(repl)); if(repl.status != CMD_GROUPSTATS) return false; /* we don't use the alias yet, but the OV API will be extended at some point so that the alias is returned also */ if(repl.aliaslen != 0) { char *buf = xmalloc(repl.aliaslen); crecv(buf, repl.aliaslen); free(buf); } if(lo) *lo = repl.lo; if(hi) *hi = repl.hi; if(count) *count = repl.count; if(flag) *flag = repl.flag; return true; } ret = ovdb_getgroupinfo(group, &gi, true, NULL, 0); switch (ret) { case 0: break; case DB_NOTFOUND: return false; default: warn("OVDB: ovdb_getgroupinfo failed: %s", db_strerror(ret)); return false; } if(lo != NULL) *lo = gi.low; if(hi != NULL) *hi = gi.high; if(count != NULL) *count = gi.count; if(flag != NULL) *flag = gi.flag; return true; } bool ovdb_groupadd(const char *group, ARTNUM lo, ARTNUM hi, char *flag) { DBT key, val; struct groupinfo gi; DB_TXN *tid; int ret = 0; int new; memset(&key, 0, sizeof key); memset(&val, 0, sizeof val); TXN_START(t_groupadd, tid); if(tid==NULL) return false; new = 0; ret = ovdb_getgroupinfo(group, &gi, false, tid, DB_RMW); switch (ret) { case DB_NOTFOUND: new = 1; case 0: break; case TRYAGAIN: TXN_RETRY(t_groupadd, tid); default: TXN_ABORT(t_groupadd, tid); warn("OVDB: ovdb_getgroupinfo failed: %s", db_strerror(ret)); return false; } if(!new && (gi.status & GROUPINFO_DELETED) && !(gi.status & GROUPINFO_EXPIRING) && !(gi.status & GROUPINFO_MOVING)) { int s, c = 0; char g[MED_BUFFER]; strlcpy(g, group, sizeof(g)); s = strlen(g) + 1; key.data = g; key.size = s + sizeof(int); do { c++; memcpy(g+s, &c, sizeof(int)); ret = groupinfo->get(groupinfo, tid, &key, &val, 0); } while(ret == 0); if(ret == TRYAGAIN) { TXN_RETRY(t_groupadd, tid); } val.data = &gi; val.size = sizeof(gi); ret = groupinfo->put(groupinfo, tid, &key, &val, 0); switch (ret) { case 0: break; case TRYAGAIN: TXN_RETRY(t_groupadd, tid); default: TXN_ABORT(t_groupadd, tid); warn("OVDB: groupinfo->put: %s", db_strerror(ret)); return false; } key.data = (char *) group; key.size = strlen(group); ret = groupinfo->del(groupinfo, tid, &key, 0); switch (ret) { case 0: break; case TRYAGAIN: TXN_RETRY(t_groupadd, tid); default: TXN_ABORT(t_groupadd, tid); warn("OVDB: groupinfo->del: %s", db_strerror(ret)); return false; } new = 1; } if(new) { ret = groupid_new(&gi.current_gid, tid); switch (ret) { case 0: break; case TRYAGAIN: TXN_RETRY(t_groupadd, tid); default: TXN_ABORT(t_groupadd, tid); warn("OVDB: groupid_new: %s", db_strerror(ret)); return false; } gi.low = lo ? lo : 1; gi.high = hi; gi.count = 0; gi.flag = *flag; gi.expired = time(NULL); gi.expiregrouppid = 0; gi.current_db = gi.new_db = which_db(group); gi.new_gid = gi.current_gid; gi.status = 0; } else { gi.flag = *flag; } key.data = (char *) group; key.size = strlen(group); val.data = &gi; val.size = sizeof gi; ret = groupinfo->put(groupinfo, tid, &key, &val, 0); switch (ret) { case 0: break; case TRYAGAIN: TXN_RETRY(t_groupadd, tid); default: TXN_ABORT(t_groupadd, tid); warn("OVDB: groupadd: groupinfo->put: %s", db_strerror(ret)); return false; } if(*flag == NF_FLAG_ALIAS) { key.data = (char *) group; key.size = strlen(group); val.data = flag + 1; val.size = strcspn(flag, "\n") - 1; switch(ret = groupaliases->put(groupaliases, tid, &key, &val, 0)) { case 0: break; case TRYAGAIN: TXN_RETRY(t_groupadd, tid); default: TXN_ABORT(t_groupadd, tid); warn("OVDB: groupadd: groupaliases->put: %s", db_strerror(ret)); return false; } } TXN_COMMIT(t_groupadd, tid); return true; } bool ovdb_groupdel(const char *group) { DBT key, val; struct groupinfo gi; DB_TXN *tid; int ret = 0; memset(&key, 0, sizeof key); memset(&val, 0, sizeof val); /* We only need to set the deleted flag in groupinfo to prevent readers from seeing this group. The actual overview records aren't deleted now, since that could take a significant amount of time (and innd is who normally calls this function). The expireover run will clean up the deleted groups. */ TXN_START(t_groupdel, tid); if(tid==NULL) return false; ret = ovdb_getgroupinfo(group, &gi, true, tid, DB_RMW); switch (ret) { case DB_NOTFOUND: return true; case 0: break; case TRYAGAIN: TXN_RETRY(t_groupdel, tid); default: warn("OVDB: ovdb_getgroupinfo failed: %s", db_strerror(ret)); TXN_ABORT(t_groupdel, tid); return false; } gi.status |= GROUPINFO_DELETED; key.data = (char *) group; key.size = strlen(group); val.data = &gi; val.size = sizeof gi; switch(ret = groupinfo->put(groupinfo, tid, &key, &val, 0)) { case 0: break; case TRYAGAIN: TXN_RETRY(t_groupdel, tid); default: TXN_ABORT(t_groupdel, tid); warn("OVDB: groupadd: groupinfo->put: %s", db_strerror(ret)); return false; } switch(ret = groupaliases->del(groupaliases, tid, &key, 0)) { case 0: case DB_NOTFOUND: break; case TRYAGAIN: TXN_RETRY(t_groupdel, tid); default: warn("OVDB: groupdel: groupaliases->del: %s", db_strerror(ret)); TXN_ABORT(t_groupdel, tid); return false; } TXN_COMMIT(t_groupdel, tid); return true; } bool ovdb_add(const char *group, ARTNUM artnum, TOKEN token, char *data, int len, time_t arrived, time_t expires) { static size_t databuflen = 0; static char *databuf; #ifdef HAVE_ZLIB uLong c_sz = 0; #else #define c_sz 0 #endif DB *db; DBT key, val; DB_TXN *tid; struct groupinfo gi; struct datakey dk; int ret = 0; memset(&dk, 0, sizeof dk); if(databuflen == 0) { databuflen = BIG_BUFFER; databuf = xmalloc(databuflen); } #ifdef HAVE_ZLIB if(ovdb_conf.compress) { /* Allow for worst-case compression */ c_sz = len + (len / 1000) + 20; } #endif if(databuflen < len + sizeof(struct ovdata) + c_sz) { databuflen = len + sizeof(struct ovdata) + c_sz; databuf = xrealloc(databuf, databuflen); } /* Hmm... Berkeley DB needs something like a 'struct iovec' so that we don't have to make a new buffer and copy everything in to it. */ ((struct ovdata *)databuf)->token = token; ((struct ovdata *)databuf)->arrived = arrived; ((struct ovdata *)databuf)->expires = expires; #ifdef HAVE_ZLIB if(ovdb_conf.compress && len > COMPRESS_MIN) { uint32_t sz; c_sz -= sizeof(uint32_t); ret = compress( (Bytef *)(databuf + sizeof(struct ovdata) + sizeof(uint32_t)), &c_sz, (Bytef *)data, (uLong)len); if(ret != Z_OK) { memcpy(databuf + sizeof(struct ovdata), data, len); len += sizeof(struct ovdata); } else { sz = htonl((uint32_t)len); memcpy(databuf + sizeof(struct ovdata), &sz, sizeof(uint32_t)); /* The following line is mostly paranoia. Just want to make sure that the first byte is 0 (it should be 0 anyway), so ovdb_search recognizes this as compressed data. */ *(databuf + sizeof(struct ovdata)) = 0; len = c_sz + sizeof(struct ovdata) + sizeof(uint32_t); } } else { #endif memcpy(databuf + sizeof(struct ovdata), data, len); len += sizeof(struct ovdata); #ifdef HAVE_ZLIB } #endif memset(&key, 0, sizeof key); memset(&val, 0, sizeof val); /* start the transaction */ TXN_START(t_add, tid); if(tid==NULL) return false; /* first, retrieve groupinfo */ ret = ovdb_getgroupinfo(group, &gi, true, tid, DB_RMW); switch (ret) { case 0: break; case DB_NOTFOUND: TXN_ABORT(t_add, tid); return true; case TRYAGAIN: TXN_RETRY(t_add, tid); default: TXN_ABORT(t_add, tid); warn("OVDB: add: ovdb_getgroupinfo: %s", db_strerror(ret)); return false; } /* adjust groupinfo */ if(Cutofflow && gi.low > artnum) { TXN_ABORT(t_add, tid); return true; } if(gi.low == 0 || gi.low > artnum) gi.low = artnum; if(gi.high < artnum) gi.high = artnum; gi.count++; /* store groupinfo */ key.data = (char *) group; key.size = strlen(group); val.data = &gi; val.size = sizeof gi; ret = groupinfo->put(groupinfo, tid, &key, &val, 0); switch (ret) { case 0: break; case TRYAGAIN: TXN_RETRY(t_add, tid); default: TXN_ABORT(t_add, tid); warn("OVDB: add: groupinfo->put: %s", db_strerror(ret)); return false; } /* store overview */ db = get_db_bynum(gi.current_db); if(db == NULL) { TXN_ABORT(t_add, tid); return false; } dk.groupnum = gi.current_gid; dk.artnum = htonl((u_int32_t)artnum); key.data = &dk; key.size = sizeof dk; val.data = databuf; val.size = len; ret = db->put(db, tid, &key, &val, 0); switch (ret) { case 0: break; case TRYAGAIN: TXN_RETRY(t_add, tid); default: TXN_ABORT(t_add, tid); warn("OVDB: add: db->put: %s", db_strerror(ret)); return false; } if(artnum < gi.high && gi.status & GROUPINFO_MOVING) { /* If the GROUPINFO_MOVING flag is set, then expireover is writing overview records under a new groupid. If this overview record is not at the highmark, we need to also store it under the new groupid */ db = get_db_bynum(gi.new_db); if(db == NULL) { TXN_ABORT(t_add, tid); return false; } dk.groupnum = gi.new_gid; ret = db->put(db, tid, &key, &val, 0); switch (ret) { case 0: break; case TRYAGAIN: TXN_RETRY(t_add, tid); default: TXN_ABORT(t_add, tid); warn("OVDB: add: db->put: %s", db_strerror(ret)); return false; } } TXN_COMMIT(t_add, tid); return true; } bool ovdb_cancel(const char *group UNUSED, ARTNUM artnum UNUSED) { return true; } #ifdef HAVE_ZLIB static char * myuncompress(char *buf, size_t buflen, size_t *newlen) { static char *dbuf = NULL; static uLongf dbuflen = 0, ulen; uint32_t sz; int ret; memcpy(&sz, buf, sizeof(sz)); sz = ntohl(sz); if(sz >= dbuflen) { if(dbuflen == 0) { dbuflen = sz + 512; dbuf = xmalloc(dbuflen); } else { dbuflen = sz + 512; dbuf = xrealloc(dbuf, dbuflen); } } ulen = dbuflen - 1; ret = uncompress((Bytef *)dbuf, &ulen, (Bytef *)(buf + sizeof(uint32_t)), buflen - sizeof(uint32_t)); if(ret != Z_OK) { warn("OVDB: uncompress failed"); return NULL; } dbuf[ulen] = 0; /* paranoia */ if(newlen) *newlen = ulen; return dbuf; } #endif struct ovdbsearch { DBC *cursor; group_id_t gid; u_int32_t firstart; u_int32_t lastart; int state; }; /* Even though nnrpd only does one search at a time, a read server process could do many concurrent searches; hence we must keep track of an arbitrary number of open searches */ static struct ovdbsearch **searches = NULL; static int nsearches = 0; static int maxsearches = 0; void * ovdb_opensearch(const char *group, int low, int high) { DB *db; struct ovdbsearch *s; struct groupinfo gi; int ret; if(clientmode) { struct rs_cmd rs; struct rs_opensrch repl; rs.what = CMD_OPENSRCH; rs.grouplen = strlen(group)+1; rs.artlo = low; rs.arthi = high; if (csend(&rs, sizeof(rs)) < 0) return NULL; if (csend(group, rs.grouplen) < 0) return NULL; crecv(&repl, sizeof(repl)); if(repl.status != CMD_OPENSRCH) return NULL; return repl.handle; } ret = ovdb_getgroupinfo(group, &gi, true, NULL, 0); switch (ret) { case 0: break; case DB_NOTFOUND: return NULL; default: warn("OVDB: ovdb_getgroupinfo failed: %s", db_strerror(ret)); return NULL; } s = xmalloc(sizeof(struct ovdbsearch)); db = get_db_bynum(gi.current_db); if(db == NULL) { free(s); return NULL; } ret = db->cursor(db, NULL, &(s->cursor), 0); if (ret != 0) { warn("OVDB: opensearch: s->db->cursor: %s", db_strerror(ret)); free(s); return NULL; } s->gid = gi.current_gid; s->firstart = low; s->lastart = high; s->state = 0; if(searches == NULL) { nsearches = 0; maxsearches = 50; searches = xmalloc(sizeof(struct ovdbsearch *) * maxsearches); } if(nsearches == maxsearches) { maxsearches += 50; searches = xrealloc(searches, sizeof(struct ovdbsearch *) * maxsearches); } searches[nsearches] = s; nsearches++; return (void *)s; } bool ovdb_search(void *handle, ARTNUM *artnum, char **data, int *len, TOKEN *token, time_t *arrived) { struct ovdbsearch *s = (struct ovdbsearch *)handle; DBT key, val; u_int32_t flags; struct ovdata ovd; uint32_t sz = 0; struct datakey dk; int ret; char *dp; if (clientmode) { struct rs_cmd rs; struct rs_srch repl; static char *databuf; static int buflen = 0; rs.what = CMD_SRCH; rs.handle = handle; if (csend(&rs, sizeof(rs)) < 0) return false; if (crecv(&repl, sizeof(repl)) < 0) return false; if(repl.status != CMD_SRCH) return false; if(repl.len > buflen) { if(buflen == 0) { buflen = repl.len + 512; databuf = xmalloc(buflen); } else { buflen = repl.len + 512; databuf = xrealloc(databuf, buflen); } } crecv(databuf, repl.len); if(artnum) *artnum = repl.artnum; if(token) *token = repl.token; if(arrived) *arrived = repl.arrived; if(len) *len = repl.len; if(data) *data = databuf; return true; } switch(s->state) { case 0: flags = DB_SET_RANGE; memset(&dk, 0, sizeof dk); dk.groupnum = s->gid; dk.artnum = htonl(s->firstart); s->state = 1; break; case 1: flags = DB_NEXT; break; case 2: s->state = 3; return false; default: warn("OVDB: OVsearch called again after false return"); return false; } memset(&key, 0, sizeof key); memset(&val, 0, sizeof val); key.data = &dk; key.size = key.ulen = sizeof(struct datakey); key.flags = DB_DBT_USERMEM; if(!data && !len) { /* caller doesn't need data, so we don't have to retrieve it all */ val.flags |= DB_DBT_PARTIAL; if(token || arrived) val.dlen = sizeof(struct ovdata); } switch(ret = s->cursor->c_get(s->cursor, &key, &val, flags)) { case 0: break; case DB_NOTFOUND: s->state = 3; s->cursor->c_close(s->cursor); s->cursor = NULL; return false; default: warn("OVDB: search: c_get: %s", db_strerror(ret)); s->state = 3; s->cursor->c_close(s->cursor); s->cursor = NULL; return false; } if(key.size != sizeof(struct datakey)) { s->state = 3; s->cursor->c_close(s->cursor); s->cursor = NULL; return false; } if(dk.groupnum != s->gid || ntohl(dk.artnum) > s->lastart) { s->state = 3; s->cursor->c_close(s->cursor); s->cursor = NULL; return false; } if( ((len || data) && val.size <= sizeof(struct ovdata)) || ((token || arrived) && val.size < sizeof(struct ovdata)) ) { warn("OVDB: search: bad value length"); s->state = 3; s->cursor->c_close(s->cursor); s->cursor = NULL; return false; } if(ntohl(dk.artnum) == s->lastart) { s->state = 2; s->cursor->c_close(s->cursor); s->cursor = NULL; } if(artnum) *artnum = ntohl(dk.artnum); if(token || arrived) memcpy(&ovd, val.data, sizeof(struct ovdata)); if(token) *token = ovd.token; if(arrived) *arrived = ovd.arrived; dp = (char *)val.data + sizeof(struct ovdata); #ifdef HAVE_ZLIB if(val.size >= (sizeof(struct ovdata) + sizeof(uint32_t)) && *dp == 0) { /* data is compressed */ memcpy(&sz, dp, sizeof(uint32_t)); sz = ntohl(sz); if(sz > MAX_UNZIP_SZ) { /* sanity check */ warn("OVDB: search: bogus sz: %d", sz); sz = 0; } } #endif if(len) { if(sz) { *len = sz; } else { *len = val.size - sizeof(struct ovdata); } } if(data) { #ifdef HAVE_ZLIB if(sz && val.size > sizeof(struct ovdata) + sizeof(uint32_t)) { *data = myuncompress(dp, val.size - sizeof(struct ovdata), NULL); if(*data == NULL) { s->state = 3; s->cursor->c_close(s->cursor); s->cursor = NULL; return false; } } else #endif *data = dp; } return true; } void ovdb_closesearch(void *handle) { int i; if(clientmode) { struct rs_cmd rs; rs.what = CMD_CLOSESRCH; rs.handle = handle; csend(&rs, sizeof(rs)); /* no reply is sent for a CMD_CLOSESRCH */ } else { struct ovdbsearch *s = (struct ovdbsearch *)handle; if(s->cursor) s->cursor->c_close(s->cursor); for(i = 0; i < nsearches; i++) { if(s == searches[i]) { break; } } nsearches--; for( ; i < nsearches; i++) { searches[i] = searches[i+1]; } free(handle); } } bool ovdb_getartinfo(const char *group, ARTNUM artnum, TOKEN *token) { int ret, cdb = 0; group_id_t cgid = 0; DB *db; DBT key, val; struct ovdata ovd; struct datakey dk; struct groupinfo gi; int pass = 0; if(clientmode) { struct rs_cmd rs; struct rs_artinfo repl; rs.what = CMD_ARTINFO; rs.grouplen = strlen(group)+1; rs.artlo = artnum; if (csend(&rs, sizeof(rs)) < 0) return false; if (csend(group, rs.grouplen) < 0) return false; crecv(&repl, sizeof(repl)); if(repl.status != CMD_ARTINFO) return false; if(token) *token = repl.token; return true; } while(1) { ret = ovdb_getgroupinfo(group, &gi, true, NULL, 0); switch (ret) { case 0: break; case DB_NOTFOUND: return false; default: warn("OVDB: ovdb_getgroupinfo failed: %s", db_strerror(ret)); return false; } if(pass) { /* This was our second groupinfo retrieval; because the article retrieval came up empty. If the group ID hasn't changed since the first groupinfo retrieval, we can assume the article is definitely not there. Otherwise, we'll try to retrieve it the article again. */ if(cdb == gi.current_db && cgid == gi.current_gid) return false; } db = get_db_bynum(gi.current_db); if(db == NULL) return false; memset(&dk, 0, sizeof dk); dk.groupnum = gi.current_gid; dk.artnum = htonl((u_int32_t)artnum); memset(&key, 0, sizeof key); memset(&val, 0, sizeof val); key.data = &dk; key.size = sizeof dk; /* caller doesn't need data, so we don't have to retrieve it all */ val.flags = DB_DBT_PARTIAL; if(token) val.dlen = sizeof(struct ovdata); switch(ret = db->get(db, NULL, &key, &val, 0)) { case 0: case DB_NOTFOUND: break; default: warn("OVDB: getartinfo: db->get: %s", db_strerror(ret)); return false; } if(ret == DB_NOTFOUND) { /* If the group is being moved (i.e., its group ID is going to change), there's a chance the article is now under the new ID. So we'll grab the groupinfo again to check for that. */ if(!pass && (gi.status & GROUPINFO_MOVING)) { cdb = gi.current_db; cgid = gi.current_gid; pass++; continue; } return false; } break; } if(token && val.size < sizeof(struct ovdata)) { warn("OVDB: getartinfo: data too short"); return false; } if(token) { memcpy(&ovd, val.data, sizeof(struct ovdata)); *token = ovd.token; } return true; } bool ovdb_expiregroup(const char *group, int *lo, struct history *h) { DB *db, *ndb = NULL; DBT key, val, nkey, gkey, gval; DB_TXN *tid; DBC *cursor = NULL; int ret = 0, delete, old_db = 0, cleanup; struct groupinfo gi; struct ovdata ovd; struct datakey dk, ndk; group_id_t old_gid = 0; ARTHANDLE *ah; u_int32_t artnum = 0, currentart, lowest; int i, compact, done, currentcount, newcount; if(eo_start == 0) { eo_start = time(NULL); delete_old_stuff(0); /* remove deleted groups first */ } /* Special case: when called with NULL group, we're to clean out * records for forgotton groups (groups removed from the active file * but not from overview). * This happens at the end of the expireover run, and only if all * of the groups in the active file have been processed. * delete_old_stuff(1) will remove groups that are in ovdb but * have not been processed during this run. */ if(group == NULL) return delete_old_stuff(1); memset(&key, 0, sizeof key); memset(&nkey, 0, sizeof nkey); memset(&val, 0, sizeof val); memset(&dk, 0, sizeof dk); memset(&ndk, 0, sizeof ndk); TXN_START(t_expgroup_1, tid); if(tid==NULL) return false; cleanup = 0; ret = ovdb_getgroupinfo(group, &gi, true, tid, DB_RMW); switch (ret) { case 0: break; case TRYAGAIN: TXN_RETRY(t_expgroup_1, tid); default: warn("OVDB: expiregroup: ovdb_getgroupinfo failed: %s", db_strerror(ret)); case DB_NOTFOUND: TXN_ABORT(t_expgroup_1, tid); return false; } if(gi.status & GROUPINFO_EXPIRING) { /* is there another expireover working on this group? */ ret = kill(gi.expiregrouppid, 0); switch(ret) { case 0: case EPERM: TXN_ABORT(t_expgroup_1, tid); return false; } /* a previous expireover run must've died. We'll clean up after it */ if(gi.status & GROUPINFO_MOVING) { cleanup = 1; old_db = gi.new_db; old_gid = gi.new_gid; ret = mk_temp_groupinfo(old_db, old_gid, tid); switch(ret) { case 0: break; case TRYAGAIN: TXN_RETRY(t_expgroup_1, tid); default: TXN_ABORT(t_expgroup_1, tid); return false; } gi.status &= ~GROUPINFO_MOVING; } } if(gi.count < ovdb_conf.nocompact || ovdb_conf.nocompact == 0) compact = 1; else compact = 0; if(gi.count == 0) compact = 0; db = get_db_bynum(gi.current_db); if(db == NULL) { TXN_ABORT(t_expgroup_1, tid); return false; } gi.status |= GROUPINFO_EXPIRING; gi.expiregrouppid = getpid(); if(compact) { gi.status |= GROUPINFO_MOVING; gi.new_db = gi.current_db; ndb = db; ret = groupid_new(&gi.new_gid, tid); switch (ret) { case 0: break; case TRYAGAIN: TXN_RETRY(t_expgroup_1, tid); default: TXN_ABORT(t_expgroup_1, tid); warn("OVDB: expiregroup: groupid_new: %s", db_strerror(ret)); return false; } } key.data = (char *) group; key.size = strlen(group); val.data = &gi; val.size = sizeof gi; ret = groupinfo->put(groupinfo, tid, &key, &val, 0); switch (ret) { case 0: break; case TRYAGAIN: TXN_RETRY(t_expgroup_1, tid); default: TXN_ABORT(t_expgroup_1, tid); warn("OVDB: expiregroup: groupinfo->put: %s", db_strerror(ret)); return false; } TXN_COMMIT(t_expgroup_1, tid); if(cleanup) { if(delete_all_records(old_db, old_gid) == 0) { rm_temp_groupinfo(old_gid); } } /* * The following loop iterates over the OV records for the group in * "batches", to limit transaction sizes. * * loop { * start transaction * get groupinfo * process EXPIREGROUP_TXN_SIZE records * write updated groupinfo * commit transaction * } */ currentart = 0; lowest = currentcount = 0; memset(&gkey, 0, sizeof gkey); memset(&gval, 0, sizeof gval); gkey.data = (char *) group; gkey.size = strlen(group); gval.data = &gi; gval.size = sizeof gi; while(1) { TXN_START(t_expgroup_loop, tid); if(tid==NULL) return false; done = 0; newcount = 0; ret = ovdb_getgroupinfo(group, &gi, false, tid, DB_RMW); switch (ret) { case 0: break; case TRYAGAIN: TXN_RETRY(t_expgroup_loop, tid); default: TXN_ABORT(t_expgroup_loop, tid); warn("OVDB: expiregroup: ovdb_getgroupinfo: %s", db_strerror(ret)); return false; } ret = db->cursor(db, tid, &cursor, 0); switch (ret) { case 0: break; case TRYAGAIN: TXN_RETRY(t_expgroup_loop, tid); default: TXN_ABORT(t_expgroup_loop, tid); warn("OVDB: expiregroup: db->cursor: %s", db_strerror(ret)); return false; } dk.groupnum = gi.current_gid; dk.artnum = htonl(currentart); key.data = &dk; key.size = key.ulen = sizeof dk; key.flags = DB_DBT_USERMEM; for(i=0; i < EXPIREGROUP_TXN_SIZE; i++) { ret = cursor->c_get(cursor, &key, &val, i == 0 ? DB_SET_RANGE : DB_NEXT); switch (ret) { case 0: case DB_NOTFOUND: break; case TRYAGAIN: cursor->c_close(cursor); TXN_RETRY(t_expgroup_loop, tid); default: cursor->c_close(cursor); TXN_ABORT(t_expgroup_loop, tid); warn("OVDB: expiregroup: c_get: %s", db_strerror(ret)); return false; } /* stop if: there are no more keys, an unknown key is reached, or reach a different group */ if(ret == DB_NOTFOUND || key.size != sizeof dk || dk.groupnum != gi.current_gid) { done++; break; } artnum = ntohl(dk.artnum); delete = 0; if(val.size < sizeof ovd) { delete = 1; /* must be corrupt, just delete it */ } else { char *p = (char *)val.data + sizeof(ovd); size_t sz = val.size - sizeof(ovd); #ifdef HAVE_ZLIB if(*p == 0) p = myuncompress(p, sz, &sz); if(p == NULL) { p = (char *) ""; delete = 1; } #endif memcpy(&ovd, val.data, sizeof ovd); ah = NULL; if (!SMprobe(EXPENSIVESTAT, &ovd.token, NULL) || OVstatall) { if((ah = SMretrieve(ovd.token, RETR_STAT)) == NULL) { delete = 1; } else SMfreearticle(ah); } else { if (!delete && !OVhisthasmsgid(h, p)) { delete = 1; } } if (!delete && innconf->groupbaseexpiry && OVgroupbasedexpire(ovd.token, group, p, sz, ovd.arrived, ovd.expires)) { delete = 1; } } if(delete) { if(!compact) { switch(ret = cursor->c_del(cursor, 0)) { case 0: case DB_NOTFOUND: case DB_KEYEMPTY: break; case TRYAGAIN: cursor->c_close(cursor); TXN_RETRY(t_expgroup_loop, tid); default: cursor->c_close(cursor); TXN_ABORT(t_expgroup_loop, tid); warn("OVDB: expiregroup: c_del: %s", db_strerror(ret)); return false; } } if(gi.count > 0) gi.count--; } else { if(compact) { ndk.groupnum = gi.new_gid; ndk.artnum = dk.artnum; nkey.data = &ndk; nkey.size = sizeof ndk; switch(ret = ndb->put(ndb, tid, &nkey, &val, 0)) { case 0: break; case TRYAGAIN: cursor->c_close(cursor); TXN_RETRY(t_expgroup_loop, tid); default: cursor->c_close(cursor); TXN_ABORT(t_expgroup_loop, tid); warn("OVDB: expiregroup: ndb->put: %s", db_strerror(ret)); return false; } } newcount++; if (lowest != (u_int32_t) -1) if (lowest == 0 || artnum < lowest) lowest = artnum; } } /* end of for loop */ if(cursor->c_close(cursor) == TRYAGAIN) { TXN_RETRY(t_expgroup_loop, tid); } if(lowest != 0 && lowest != (u_int32_t) -1) gi.low = lowest; if(done) { if(compact) { old_db = gi.current_db; gi.current_db = gi.new_db; old_gid = gi.current_gid; gi.current_gid = gi.new_gid; gi.status &= ~GROUPINFO_MOVING; ret = mk_temp_groupinfo(old_db, old_gid, tid); switch(ret) { case 0: break; case TRYAGAIN: TXN_RETRY(t_expgroup_loop, tid); default: TXN_ABORT(t_expgroup_loop, tid); return false; } } gi.status &= ~GROUPINFO_EXPIRING; gi.expired = time(NULL); if(gi.count == 0 && lowest == 0) gi.low = gi.high+1; } ret = groupinfo->put(groupinfo, tid, &gkey, &gval, 0); switch (ret) { case 0: break; case TRYAGAIN: TXN_RETRY(t_expgroup_loop, tid); default: TXN_ABORT(t_expgroup_loop, tid); warn("OVDB: expiregroup: groupinfo->put: %s", db_strerror(ret)); return false; } TXN_COMMIT(t_expgroup_loop, tid); currentcount += newcount; if(lowest != 0) lowest = (u_int32_t) -1; if(done) break; currentart = artnum+1; } if(compact) { if(delete_all_records(old_db, old_gid) == 0) { rm_temp_groupinfo(old_gid); } } if(currentcount != gi.count) { notice("OVDB: expiregroup: recounting %s", group); TXN_START(t_expgroup_recount, tid); if(tid == NULL) return false; switch(ret = ovdb_getgroupinfo(group, &gi, false, tid, DB_RMW)) { case 0: break; case TRYAGAIN: TXN_RETRY(t_expgroup_recount, tid); default: TXN_ABORT(t_expgroup_recount, tid); warn("OVDB: expiregroup: ovdb_getgroupinfo: %s", db_strerror(ret)); return false; } if(count_records(&gi) != 0) { TXN_ABORT(t_expgroup_recount, tid); return false; } ret = groupinfo->put(groupinfo, tid, &gkey, &gval, 0); switch (ret) { case 0: break; case TRYAGAIN: TXN_RETRY(t_expgroup_recount, tid); default: TXN_ABORT(t_expgroup_recount, tid); warn("OVDB: expiregroup: groupinfo->put: %s", db_strerror(ret)); return false; } TXN_COMMIT(t_expgroup_recount, tid); } if(lo) *lo = gi.low; return true; } bool ovdb_ctl(OVCTLTYPE type, void *val) { int *i; OVSORTTYPE *sorttype; bool *boolval; switch (type) { case OVSPACE: i = (int *)val; *i = -1; return true; case OVSORT: sorttype = (OVSORTTYPE *)val; *sorttype = OVNEWSGROUP; return true; case OVCUTOFFLOW: Cutofflow = *(bool *)val; return true; case OVSTATICSEARCH: i = (int *)val; *i = true; return true; case OVCACHEKEEP: case OVCACHEFREE: boolval = (bool *)val; *boolval = false; return true; default: return false; } } void ovdb_close_berkeleydb(void) { if(OVDBenv) { /* close db environment */ OVDBenv->close(OVDBenv, 0); OVDBenv = NULL; } } void ovdb_close(void) { int i; if(clientmode) { client_disconnect(); return; } while(searches != NULL && nsearches) { ovdb_closesearch(searches[0]); } if(searches != NULL) { free(searches); searches = NULL; } if(dbs) { /* close databases */ for(i = 0; i < ovdb_conf.numdbfiles; i++) close_db_file(i); free(dbs); dbs = NULL; } if(groupinfo) { groupinfo->close(groupinfo, 0); groupinfo = NULL; } if(groupaliases) { groupaliases->close(groupaliases, 0); groupaliases = NULL; } ovdb_close_berkeleydb(); ovdb_releaselock(); } #endif /* HAVE_BDB */ inn-2.6.0/storage/ovdb/ovdb-private.h0000644000175200017520000001246412575023702017100 0ustar iuliusiulius#ifndef HAVE_DB_H # undef HAVE_BDB #endif #ifdef HAVE_BDB #include #if DB_VERSION_MAJOR < 4 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR < 3) #error "Need Berkeley DB 4.4 or higher" #endif /* * How data is stored: * * Each group is assigned an integer ID. The mapping between a group name * and its ID is stored in the groupinfo DB. Overview data itself * is stored in one or more btree DBs. The specific DB file that is used * to store data for a certain group is chosen by taking the hash of the * group name, copying the first bytes of the hash into an int, and then * modding the int value to the number of DBs. * * Each group has one groupinfo structure in the groupinfo DB, whose key * is the newsgroup name. The overview records for the group have a * 'struct datakey' as their keys, which consists of the group ID (in * native byteorder) followed by the article number in network byteorder. * The reason for storing the article number in net byte order (big-endian) * is that the keys will sort correctly using Berkeley DB's default sort * function (basically, a memcmp). * * The overview records consist of a 'struct ovdata' followed by the actual * overview data. The struct ovdata contains the token and arrival time. * If compression is enabled, overview records larger than COMPRESS_MIN * get compressed using zlib. A compressed record has the same 'struct * ovdata', but is followed by a uint32_t in network byteorder (which is * the length of the uncompressed data), and then followed by the compressed * data. Since overview data never starts with a null byte, a compressed * record is identified by the presense of a null byte immediately after * the struct ovdata (which is part of the uint32_t). */ struct ovdb_conf { char *home; /* path to directory where db files are stored */ int txn_nosync; /* whether to pass DB_TXN_NOSYNC to db_appinit */ int numdbfiles; size_t cachesize; int ncache; size_t pagesize; int minkey; int maxlocks; int nocompact; int readserver; int numrsprocs; int maxrsconn; int useshm; int shmkey; int compress; }; typedef u_int32_t group_id_t; struct groupinfo { ARTNUM low; ARTNUM high; int count; int flag; time_t expired; /* when this group was last touched by expiregroup */ group_id_t current_gid; /* group ID */ group_id_t new_gid; /* pending ID (expireover) */ int current_db; /* which DB file the records are in */ int new_db; /* pending DB file */ pid_t expiregrouppid; /* PID of expireover process */ int status; }; #define GROUPINFO_DELETED 1 #define GROUPINFO_EXPIRING (1<<1) #define GROUPINFO_MOVING (1<<2) #define GROUPINFO_MOVE_REQUESTED (1<<3) /*NYI*/ struct datakey { group_id_t groupnum; /* must be the first member of this struct */ u_int32_t artnum; }; struct ovdata { TOKEN token; time_t arrived; time_t expires; }; #define DATA_VERSION 2 #define DATA_VERSION_COMPRESS 3 extern struct ovdb_conf ovdb_conf; extern int ovdb_data_ver; extern DB_ENV *OVDBenv; void read_ovdb_conf(void); int ovdb_open_berkeleydb(int mode, int flags); void ovdb_close_berkeleydb(void); int ovdb_getgroupinfo(const char *group, struct groupinfo *gi, int ignoredeleted, DB_TXN *tid, int getflags); #define OVDB_RECOVER 1 #define OVDB_UPGRADE 2 #define OVDB_LOCK_NORMAL 0 #define OVDB_LOCK_ADMIN 1 #define OVDB_LOCK_EXCLUSIVE 2 bool ovdb_getlock(int mode); bool ovdb_releaselock(void); bool ovdb_check_pidfile(const char *file); bool ovdb_check_user(void); #define OVDB_LOCKFN "ovdb.sem" #define OVDB_MONITOR_PIDFILE "ovdb_monitor.pid" #define OVDB_SERVER_PIDFILE "ovdb_server.pid" #define SPACES " " /* read server stuff */ #define CMD_QUIT 0x01 #define CMD_GROUPSTATS 0x02 #define CMD_OPENSRCH 0x03 #define CMD_SRCH 0x04 #define CMD_CLOSESRCH 0x05 #define CMD_ARTINFO 0x06 #define CMD_MASK 0x0F #define RPLY_OK 0x00 #define RPLY_ERROR 0x10 #define OVDB_SERVER (1<<4) #define OVDB_SERVER_BANNER "ovdb read protocol 1" #define OVDB_SERVER_PORT 32323 /* only used if don't have unix domain sockets */ #define OVDB_SERVER_SOCKET "ovdb.server" struct rs_cmd { uint32_t what; uint32_t grouplen; uint32_t artlo; uint32_t arthi; void * handle; }; struct rs_groupstats { uint32_t status; int lo; int hi; int count; int flag; uint32_t aliaslen; /* char alias */ }; struct rs_opensrch { uint32_t status; void * handle; }; struct rs_srch { uint32_t status; ARTNUM artnum; TOKEN token; time_t arrived; int len; /* char data */ }; struct rs_artinfo { uint32_t status; TOKEN token; }; /* Used when TXN_RETRY will never be called, to avoid a warning about an unused label. */ #define TXN_START_NORETRY(label, tid) \ { \ int txn_ret; \ txn_ret = OVDBenv->txn_begin(OVDBenv, NULL, &tid, 0); \ if (txn_ret != 0) { \ warn("OVDB: " #label " txn_begin: %s", db_strerror(ret)); \ tid = NULL; \ } \ } #define TXN_START(label, tid) label: TXN_START_NORETRY(label, tid) #define TXN_RETRY(label, tid) \ { (tid)->abort(tid); goto label; } #define TXN_ABORT(label, tid) (tid)->abort(tid) #define TXN_COMMIT(label, tid) (tid)->commit(tid, 0) #define TRYAGAIN DB_LOCK_DEADLOCK #endif /* HAVE_BDB */ inn-2.6.0/storage/ovdb/ovmethod.config0000644000175200017520000000005412575023702017331 0ustar iuliusiuliusname = ovdb number = 4 sources = ovdb.c inn-2.6.0/storage/overdata.c0000644000175200017520000003064512575023702015345 0ustar iuliusiulius/* $Id: overdata.c 9019 2010-03-19 21:27:15Z iulius $ ** ** Overview data processing. ** ** Here be routines for creating and checking the overview data, the ** tab-separated list of overview fields. */ #include "config.h" #include "clibrary.h" #include #include "inn/buffer.h" #include "inn/innconf.h" #include "inn/messages.h" #include "inn/wire.h" #include "inn/vector.h" #include "inn/libinn.h" #include "ovinterface.h" /* The standard overview fields. The order of these fields is important. */ static const char * const fields[] = { "Subject", "From", "Date", "Message-ID", "References", "Bytes", "Lines" }; /* ** Return a vector of the standard overview fields. Note there is no ** way to free up the resulting data structure. */ const struct cvector * overview_fields(void) { static struct cvector *list = NULL; if (list == NULL) { unsigned int field; list = cvector_new(); cvector_resize(list, ARRAY_SIZE(fields)); for (field = 0; field < ARRAY_SIZE(fields); ++field) { cvector_add(list, fields[field]); } } return list; } /* ** Return a vector of the additional fields over the standard ones. ** The order of these fields is important. ** ** Xref: is mandatory for INN and we make it the first extra field ** after the seven overview fields defined in RFC 3977. ** ** Caller is responsible for freeing the vector. */ struct vector * overview_extra_fields(bool hidden) { struct vector *list = NULL; unsigned int i; list = vector_new(); if (hidden) { vector_resize(list, innconf->extraoverviewadvertised->count + innconf->extraoverviewhidden->count + 1); } else { vector_resize(list, innconf->extraoverviewadvertised->count + 1); } vector_add(list, "Xref"); if (innconf->extraoverviewadvertised->strings != NULL) { for (i = 0; i < innconf->extraoverviewadvertised->count; i++) { if (innconf->extraoverviewadvertised->strings[i] != NULL) { vector_add(list, innconf->extraoverviewadvertised->strings[i]); } } } if (hidden) { if (innconf->extraoverviewhidden->strings != NULL) { for (i = 0; i < innconf->extraoverviewhidden->count; i++) { if (innconf->extraoverviewhidden->strings[i] != NULL) { vector_add(list, innconf->extraoverviewhidden->strings[i]); } } } } return list; } /* ** Given an article, its length, the name of a header, and a buffer to append ** the data to, append header data for that header to the overview data ** that's being constructed. Doesn't append any data if the header isn't ** found. */ static void build_header(const char *article, size_t length, const char *header, struct buffer *overview) { ptrdiff_t size; size_t offset; const char *data, *end, *p; data = wire_findheader(article, length, header, false); if (data == NULL) return; end = wire_endheader(data, article + length - 1); if (end == NULL) return; /* Someone managed to break their server so that they were appending multiple Xref headers, and INN had a bug where it wouldn't notice this and reject the article. Just in case, see if there are multiple Xref headers and use the last one. */ if (strcasecmp(header, "xref") == 0) { const char *next = end + 1; while (next != NULL) { next = wire_findheader(next, length - (next - article), header, false); if (next != NULL) { data = next; end = wire_endheader(data, article + length - 1); if (end == NULL) return; } } } size = end - data + 1; offset = overview->used + overview->left; buffer_resize(overview, offset + size); for (p = data; p <= end; p++) { if (*p == '\r' && p[1] == '\n') { p++; continue; } if (*p == '\0' || *p == '\t' || *p == '\n' || *p == '\r') overview->data[offset++] = ' '; else overview->data[offset++] = *p; overview->left++; } } /* ** Given an article number, an article, and a vector of additional headers, ** generate overview data into the provided buffer. If the buffer parameter ** is NULL, a new buffer is allocated. The article should be in wire format. ** Returns the buffer containing the overview data. */ struct buffer * overview_build(ARTNUM number, const char *article, size_t length, const struct vector *extra, struct buffer *overview) { unsigned int field; char buffer[32]; snprintf(buffer, sizeof(buffer), "%lu", number); if (overview == NULL) overview = buffer_new(); buffer_set(overview, buffer, strlen(buffer)); for (field = 0; field < ARRAY_SIZE(fields); field++) { buffer_append(overview, "\t", 1); if (field == 5) { snprintf(buffer, sizeof(buffer), "%lu", (unsigned long) length); buffer_append(overview, buffer, strlen(buffer)); } else build_header(article, length, fields[field], overview); } if (extra != NULL) { for (field = 0; field < extra->count; field++) { buffer_append(overview, "\t", 1); buffer_append(overview, extra->strings[field], strlen(extra->strings[field])); buffer_append(overview, ": ", 2); build_header(article, length, extra->strings[field], overview); } } buffer_append(overview, "\r\n", 2); return overview; } /* ** Check whether a given string is a valid number. */ static bool valid_number(const char *string) { const char *p; for (p = string; *p != '\0'; p++) if (!isdigit((unsigned char) *p)) return false; return true; } /* ** Check whether a given string is a valid overview string (doesn't contain ** CR or LF, and if the second argument is true must be preceded by a header ** name, colon, and space). Allow CRLF at the end of the data, but don't ** require it. */ static bool valid_overview_string(const char *string, bool full) { const unsigned char *p; /* RFC 5322 says that header fields must consist of printable ASCII characters (characters between 33 and 126, inclusive) excluding colon. We also allow high-bit characters, just in case, but not DEL. */ p = (const unsigned char *) string; if (full) { if (*p == '\0' || (*p == '\r' && p[1] == '\n' && p[2] == '\0')) return true; for (; *p != '\0' && *p != ':'; p++) if (!isgraph((unsigned char) *p)) return false; if (*p != ':') return false; p++; if (*p != ' ') return false; } for (p++; *p != '\0'; p++) { if (*p == '\r' && p[1] == '\n' && p[2] == '\0') break; if (*p == '\r' || *p == '\n') return false; } return true; } /* ** Check the given overview data and make sure it's well-formed. Extension ** headers are not checked against LIST OVERVIEW.FMT (having a different set of ** extension headers doesn't make the data invalid), but the presence of the ** standard fields is checked. Also checked is whether the article number in ** the data matches the passed article number. Returns true if the data is ** okay, false otherwise. */ bool overview_check(const char *data, size_t length, ARTNUM article) { char *copy; struct cvector *overview; ARTNUM overnum; size_t i; copy = xstrndup(data, length); overview = cvector_split(copy, '\t', NULL); /* The actual checks. We don't verify all of the data, since that data may be malformed in the article, but we do check to be sure that the fields that should be numbers are numbers. That should catch most positional errors. We can't check Lines yet since right now INN is still accepting the value from the post verbatim. */ if (overview->count < 8) goto fail; if (!valid_number(overview->strings[0])) goto fail; overnum = strtoul(overview->strings[0], NULL, 10); if (overnum != article) goto fail; if (!valid_number(overview->strings[6])) goto fail; for (i = 1; i < 6; i++) if (!valid_overview_string(overview->strings[i], false)) goto fail; for (i = 8; i < overview->count; i++) if (!valid_overview_string(overview->strings[i], true)) goto fail; cvector_free(overview); free(copy); return true; fail: cvector_free(overview); free(copy); return false; } /* ** Given an overview header, return the offset of the field within the ** overview data, or -1 if the field is not present in the overview schema ** for this installation. */ int overview_index(const char *field, const struct vector *extra) { size_t i; for (i = 0; i < ARRAY_SIZE(fields); i++) if (strcasecmp(field, fields[i]) == 0) return i; for (i = 0; i < extra->count; i++) if (strcasecmp(field, extra->strings[i]) == 0) return i + ARRAY_SIZE(fields); return -1; } /* ** Given an overview header line, split out a vector pointing at each ** of the components (within line), returning a pointer to the ** vector. If the vector initially passed in is NULL, a new vector is ** created, otherwise the existing one is filled in. ** ** A member `n' of the vector is of length (vector->strings[n+1] - ** vector->strings[n] - 1). Note that the last member of the vector ** will always point beyond (line + length). */ struct cvector * overview_split(const char *line, size_t length, ARTNUM *number, struct cvector *vector) { const char *p = NULL; if (vector == NULL) { vector = cvector_new(); } else { cvector_clear(vector); } while (line != NULL) { /* The first field is the article number. */ if (p == NULL) { if (number != NULL) { *number = atoi(line); } } else { cvector_add(vector, line); } p = memchr(line, '\t', length); if (p != NULL) { /* Skip over the tab. */ ++p; /* And calculate the remaining length. */ length -= (p - line); } else { /* Add in a pointer to beyond the end of the final component, so you can always calculate the length; overview lines are always terminated with \r\n, so the -1 ends up chopping those off. */ cvector_add(vector, line + length - 1); } line = p; } return vector; } /* ** Given an overview vector (from overview_split), return a copy of ** the member which the caller is interested in (and must free). ** The name and order of standard overview fields are fixed so we ** can request them by their index. */ char * overview_get_standard_header(const struct cvector *vector, unsigned int element) { char *field = NULL; size_t len; const char *p; size_t max = ARRAY_SIZE(fields) - 1; /* vector ends with an additional pointer beyond the end of the final component. It therefore has a count of elements increased by 1. */ if ((element + 1) >= vector->count || element > max) { warn("request for invalid standard overview field %d", element); return NULL; } p = vector->strings[element]; len = vector->strings[element + 1] - vector->strings[element] - 1; field = xstrndup(p, len); return field; } /* ** Given an overview vector (from overview_split), return a copy of ** the member which the caller is interested in (and must free). ** The order of extra overview fields may vary so we walk all the ** extra headers to find the requested field. */ char * overview_get_extra_header(const struct cvector *vector, const char *header) { char *field = NULL; size_t i, len; const char *p; size_t headerlen = strlen(header); /* vector ends with an additional pointer beyond the end of the final component. It therefore has a count of elements increased by 1. */ for (i = ARRAY_SIZE(fields); i < vector->count - 1; i++) { if (strncasecmp(header, vector->strings[i], headerlen) == 0) { p = vector->strings[i] + headerlen; /* Check for ": " after the name of the header. */ if ((*p++ == ':') && (*p++ == ' ')) { len = vector->strings[i + 1] - p - 1; field = xstrndup(p, len); return field; } } } /* The required header was not found in the extra overview fields. */ return NULL; } inn-2.6.0/storage/tradspool/0000755000175200017520000000000012575023701015372 5ustar iuliusiuliusinn-2.6.0/storage/tradspool/README.tradspool0000644000175200017520000000407612575023702020270 0ustar iuliusiuliusThis storage manager attempts to implement the 'traditional' INN storage layout, i.e. a message crossposted to alt.fan.james-brister and rec.pets.wombats will be written as a file in /alt/fan/james-brister/nnnnn and a symlink pointing to the above file in /rec/pets/wombats/mmmmmm where nnnnn and mmmmmm are the article numbers that article has in each of those two newsgroups. (Actually, in the traditional spool form, the link could be either a symlink or a regular link). The storage token data for a tradspool stored article is a 16-byte block. The storage token contains two ints; the first one is a number telling what the name of the "primary" newsgroup is for this article, the second one telling what article number the article has in that newsgroup. The mapping between newsgroup name and number is given by a database in the file /tradspool.map This file is a straight ASCII file listing newsgroup names and numbers, and will be automatically generated by innd if one does not exist already. This database is read in automatically by any program that uses this storage manager module, and is updated by innd whenever a new newsgroup is encountered. Other programs (like innfeed) check the mod time of that database every 5 minutes to see if they need to recheck it for any new newsgroups that might have been added. Should the database become corrupted, simply shutting down news, removing the database, and doing a makehistory will recreate the database. It should, in principle, be possible to write a perl script to recreate just the database from just the spool files and history files without doing a full makehistory. Currently the storage manager code works, although not perhaps as fast as it could. The expiration code is somewhat unwieldy; since the storage token does not have enough space to hold all the newsgroups an article is posted to, when expiration is done SMCancel() has to open the article to find out what other newsgroups the article is posted to. Eurggh. Suggestions for a better scheme are welcome. inn-2.6.0/storage/tradspool/method.config0000644000175200017520000000006612575023702020044 0ustar iuliusiuliusname = tradspool number = 5 sources = tradspool.c inn-2.6.0/storage/tradspool/tradspool.h0000644000175200017520000000150612575023702017555 0ustar iuliusiulius/* $Id: tradspool.h 8817 2009-11-17 18:57:19Z iulius $ ** ** Storage manager module header for traditional spool format. */ #ifndef __TRADSPOOL_H__ #define __TRADSPOOL_H__ #include "config.h" #include "interface.h" bool tradspool_init(SMATTRIBUTE *attr); TOKEN tradspool_store(const ARTHANDLE article, const STORAGECLASS class); ARTHANDLE *tradspool_retrieve(const TOKEN token, const RETRTYPE amount); ARTHANDLE *tradspool_next(ARTHANDLE *article, const RETRTYPE amount); void tradspool_freearticle(ARTHANDLE *article); bool tradspool_cancel(TOKEN token); bool tradspool_ctl(PROBETYPE type, TOKEN *token, void *value); bool tradspool_flushcacheddata(FLUSHTYPE type); void tradspool_printfiles(FILE *file, TOKEN token, char **xref, int ngroups); char *tradspool_explaintoken(const TOKEN token); void tradspool_shutdown(void); #endif inn-2.6.0/storage/tradspool/tradspool.c0000644000175200017520000010501212575023702017545 0ustar iuliusiulius/* $Id: tradspool.c 9841 2015-05-02 16:27:37Z iulius $ ** ** Storage manager module for traditional spool format. */ #include "config.h" #include "clibrary.h" #include "portable/mmap.h" #include #include #include #include #include #include #include /* Needed for htonl() and friends on AIX 4.1. */ #include #include "inn/innconf.h" #include "inn/messages.h" #include "inn/qio.h" #include "inn/wire.h" #include "inn/libinn.h" #include "inn/paths.h" #include "methods.h" #include "tradspool.h" typedef struct { char *artbase; /* start of the article data -- may be mmaped */ unsigned int artlen; /* art length. */ int nextindex; char *curdirname; DIR *curdir; struct _ngtent *ngtp; bool mmapped; } PRIV_TRADSPOOL; /* ** The 64-bit hashed representation of a newsgroup name that gets stashed ** in each token. */ #define HASHEDNGLEN 8 typedef struct { char hash[HASHEDNGLEN]; } HASHEDNG; /* ** We have two structures here for facilitating newsgroup name->number mapping ** and number->name mapping. NGTable is a hash table based on hashing the ** newsgroup name, and is used to give the name->number mapping. NGTree is ** a binary tree, indexed by newsgroup number, used for the number->name ** mapping. */ #define NGT_SIZE 2048 typedef struct _ngtent { char *ngname; /* HASHEDNG hash; XXX */ unsigned long ngnumber; struct _ngtent *next; struct _ngtreenode *node; } NGTENT; typedef struct _ngtreenode { unsigned long ngnumber; struct _ngtreenode *left, *right; NGTENT *ngtp; } NGTREENODE; NGTENT *NGTable[NGT_SIZE]; unsigned long MaxNgNumber = 0; NGTREENODE *NGTree; bool NGTableUpdated; /* set to true if we've added any entries since reading in the database file */ static char * TokenToPath(TOKEN token); /* ** Convert all .s to /s in a newsgroup name. Modifies the passed string ** inplace. */ static void DeDotify(char *ngname) { char *p = ngname; for ( ; *p ; ++p) { if (*p == '.') *p = '/'; } return; } /* ** Hash a newsgroup name to an 8-byte. Basically, we convert all .s to ** /s (so it doesn't matter if we're passed the spooldir name or newsgroup ** name) and then call Hash to MD5 the mess, then take 4 bytes worth of ** data from the front of the hash. This should be good enough for our ** purposes. */ static HASHEDNG HashNGName(char *ng) { HASH hash; HASHEDNG return_hash; char *p; p = xstrdup(ng); DeDotify(p); hash = Hash(p, strlen(p)); free(p); memcpy(return_hash.hash, hash.hash, HASHEDNGLEN); return return_hash; } #if 0 /* XXX */ /* compare two hashes */ static int CompareHash(HASHEDNG *h1, HASHEDNG *h2) { int i; for (i = 0 ; i < HASHEDNGLEN ; ++i) { if (h1->hash[i] != h2->hash[i]) { return h1->hash[i] - h2->hash[i]; } } return 0; } #endif /* Add a new newsgroup name to the NG table. */ static void AddNG(char *ng, unsigned long number) { char *p; unsigned int h; HASHEDNG hash; NGTENT *ngtp, **ngtpp; NGTREENODE *newnode, *curnode, **nextnode; p = xstrdup(ng); DeDotify(p); /* canonicalize p to standard (/) form. */ hash = HashNGName(p); h = (unsigned char)hash.hash[0]; h = h + (((unsigned char)hash.hash[1])<<8); h = h % NGT_SIZE; ngtp = NGTable[h]; ngtpp = &NGTable[h]; while (true) { if (ngtp == NULL) { /* ng wasn't in table, add new entry. */ NGTableUpdated = true; ngtp = xmalloc(sizeof(NGTENT)); ngtp->ngname = p; /* note: we store canonicalized name */ /* ngtp->hash = hash XXX */ ngtp->next = NULL; /* assign a new NG number if needed (not given) */ if (number == 0) { number = ++MaxNgNumber; } ngtp->ngnumber = number; /* link new table entry into the hash table chain. */ *ngtpp = ngtp; /* Now insert an appropriate record into the binary tree */ newnode = xmalloc(sizeof(NGTREENODE)); newnode->left = newnode->right = (NGTREENODE *) NULL; newnode->ngnumber = number; newnode->ngtp = ngtp; ngtp->node = newnode; if (NGTree == NULL) { /* tree was empty, so put our one element in and return */ NGTree = newnode; return; } else { nextnode = &NGTree; while (*nextnode) { curnode = *nextnode; if (curnode->ngnumber < number) { nextnode = &curnode->right; } else if (curnode->ngnumber > number) { nextnode = &curnode->left; } else { /* Error, same number is already in NGtree (shouldn't happen!) */ warn("tradspool: AddNG: duplicate newsgroup number in" " NGtree: %ld (%s)", number, p); return; } } *nextnode = newnode; return; } } else if (strcmp(ngtp->ngname, p) == 0) { /* entry in table already, so return */ free(p); return; #if 0 /* XXX */ } else if (CompareHash(&ngtp->hash, &hash) == 0) { /* eep! we hit a hash collision. */ warn("tradspool: AddNG: hash collision %s/%s", ngtp->ngname, p); free(p); return; #endif } else { /* not found yet, so advance to next entry in chain */ ngtpp = &(ngtp->next); ngtp = ngtp->next; } } } /* find a newsgroup table entry, given only the name. */ static NGTENT * FindNGByName(char *ngname) { NGTENT *ngtp; unsigned int h; HASHEDNG hash; char *p; p = xstrdup(ngname); DeDotify(p); /* canonicalize p to standard (/) form. */ hash = HashNGName(p); h = (unsigned char)hash.hash[0]; h = h + (((unsigned char)hash.hash[1])<<8); h = h % NGT_SIZE; ngtp = NGTable[h]; while (ngtp) { if (strcmp(p, ngtp->ngname) == 0) { free(p); return ngtp; } ngtp = ngtp->next; } free(p); return NULL; } /* find a newsgroup/spooldir name, given only the newsgroup number */ static char * FindNGByNum(unsigned long ngnumber) { NGTENT *ngtp; NGTREENODE *curnode; curnode = NGTree; while (curnode) { if (curnode->ngnumber == ngnumber) { ngtp = curnode->ngtp; return ngtp->ngname; } if (curnode->ngnumber < ngnumber) { curnode = curnode->right; } else { curnode = curnode->left; } } /* not in tree, return NULL */ return NULL; } #define _PATH_TRADSPOOLNGDB "tradspool.map" #define _PATH_NEWTSNGDB "tradspool.map.new" /* dump DB to file. */ static void DumpDB(void) { char *fname, *fnamenew; NGTENT *ngtp; unsigned int i; FILE *out; if (!SMopenmode) return; /* don't write if we're not in read/write mode. */ if (!NGTableUpdated) return; /* no need to dump new DB */ fname = concatpath(innconf->pathspool, _PATH_TRADSPOOLNGDB); fnamenew = concatpath(innconf->pathspool, _PATH_NEWTSNGDB); if ((out = fopen(fnamenew, "w")) == NULL) { syswarn("tradspool: DumpDB: can't write %s", fnamenew); free(fname); free(fnamenew); return; } for (i = 0 ; i < NGT_SIZE ; ++i) { ngtp = NGTable[i]; for ( ; ngtp ; ngtp = ngtp->next) { fprintf(out, "%s %lu\n", ngtp->ngname, ngtp->ngnumber); } } if (fclose(out) < 0) { syswarn("tradspool: DumpDB: can't close %s", fnamenew); free(fname); free(fnamenew); return; } if (rename(fnamenew, fname) < 0) { syswarn("tradspool: DumpDB: can't rename %s", fnamenew); free(fname); free(fnamenew); return; } free(fname); free(fnamenew); NGTableUpdated = false; /* reset modification flag. */ return; } /* ** Init NGTable from saved database file and from active. Note that ** entries in the database file get added first, and get their specifications ** of newsgroup number from there. */ static bool ReadDBFile(void) { char *fname; QIOSTATE *qp; char *line; char *p; unsigned long number; fname = concatpath(innconf->pathspool, _PATH_TRADSPOOLNGDB); if ((qp = QIOopen(fname)) == NULL) { /* only warn if db not found. */ notice("tradspool: mapping file %s not found", fname); } else { while ((line = QIOread(qp)) != NULL) { p = strchr(line, ' '); if (p == NULL) { warn("tradspool: corrupt line in active: %s", line); QIOclose(qp); free(fname); return false; } *p++ = 0; number = atol(p); AddNG(line, number); if (MaxNgNumber < number) MaxNgNumber = number; } QIOclose(qp); } free(fname); return true; } static bool ReadActiveFile(void) { char *fname; QIOSTATE *qp; char *line; char *p; fname = concatpath(innconf->pathdb, INN_PATH_ACTIVE); if ((qp = QIOopen(fname)) == NULL) { syswarn("tradspool: can't open %s", fname); free(fname); return false; } while ((line = QIOread(qp)) != NULL) { p = strchr(line, ' '); if (p == NULL) { syswarn("tradspool: corrupt line in active: %s", line); QIOclose(qp); free(fname); return false; } *p = 0; AddNG(line, 0); } QIOclose(qp); free(fname); /* dump any newly added changes to database */ DumpDB(); return true; } static bool InitNGTable(void) { if (!ReadDBFile()) return false; /* ** set NGTableUpdated to false; that way we know if the load of active or ** any AddNGs later on did in fact add new entries to the db. */ NGTableUpdated = false; if (!SMopenmode) /* don't read active unless write mode. */ return true; return ReadActiveFile(); } /* ** Routine called to check every so often to see if we need to reload the ** database and add in any new groups that have been added. This is primarily ** for the benefit of innfeed in funnel mode, which otherwise would never ** get word that any new newsgroups had been added. */ #define RELOAD_TIME_CHECK 600 static void CheckNeedReloadDB(bool force) { static time_t lastcheck, oldlastcheck, now; struct stat sb; char *fname; now = time(NULL); if (!force && lastcheck + RELOAD_TIME_CHECK > now) return; oldlastcheck = lastcheck; lastcheck = now; fname = concatpath(innconf->pathspool, _PATH_TRADSPOOLNGDB); if (stat(fname, &sb) < 0) { free(fname); return; } free(fname); if (sb.st_mtime > oldlastcheck) { /* add any newly added ngs to our in-memory copy of the db. */ ReadDBFile(); } } /* Init routine, called by SMinit */ bool tradspool_init(SMATTRIBUTE *attr) { if (attr == NULL) { warn("tradspool: attr is NULL"); SMseterror(SMERR_INTERNAL, "attr is NULL"); return false; } /* Though a few parts of tradspool handle both values for storeonxref, * the implementation when storeonxref is false is not complete. * For instance, CrackXref() is still called on the Newsgroups: header * once, which obviously fails. */ if (!innconf->storeonxref) { warn("tradspool: storeonxref needs to be true"); SMseterror(SMERR_INTERNAL, "storeonxref needs to be true"); return false; } attr->selfexpire = false; attr->expensivestat = true; return InitNGTable(); } /* ** The token is @05nnxxxxxxxxyyyyyyyy0000000000000000@ ** where "05" is the tradspool method number, ** "nn" the hexadecimal value of the storage class, ** "xxxxxxxx" the name of the primary newsgroup (as defined ** in /tradspool.map), ** "yyyyyyyy" the article number in the primary newsgroup. ** ** innconf->patharticles + '/news/group/path/yyyyyyyy' ** where "news/group/path" is the path of the primary newsgroup ** (as defined in /tradspool.map), ** "yyyyyyyy" the article number in the primary newsgroup. */ char * tradspool_explaintoken(const TOKEN token) { char *text; char *path; uint32_t ngnum; uint32_t artnum; memcpy(&ngnum, &token.token[0], sizeof(ngnum)); memcpy(&artnum, &token.token[4], sizeof(artnum)); path = TokenToPath(token); xasprintf(&text, "method=tradspool class=%u ngnum=%lu artnum=%lu file=%s", (unsigned int) token.class, (unsigned long) ntohl(ngnum), (unsigned long) ntohl(artnum), path != NULL ? path : ""); if (path != NULL) free(path); return text; } /* ** Make a token for an article given the primary newsgroup name and ** article number. */ static TOKEN MakeToken(char *ng, unsigned long artnum, STORAGECLASS class) { TOKEN token; NGTENT *ngtp; unsigned long num; memset(&token, '\0', sizeof(token)); token.type = TOKEN_TRADSPOOL; token.class = class; /* If not already in the NG Table, be sure to add this ng! This way we * catch things like newsgroups added since startup. */ if ((ngtp = FindNGByName(ng)) == NULL) { AddNG(ng, 0); DumpDB(); /* Flush to disk so other programs can see the change. */ ngtp = FindNGByName(ng); } num = ngtp->ngnumber; num = htonl(num); memcpy(token.token, &num, sizeof(num)); artnum = htonl(artnum); memcpy(&token.token[sizeof(num)], &artnum, sizeof(artnum)); return token; } /* ** Convert a token back to a pathname. */ static char * TokenToPath(TOKEN token) { unsigned long ngnum; unsigned long artnum; char *ng, *path; size_t length; CheckNeedReloadDB(false); memcpy(&ngnum, &token.token[0], sizeof(ngnum)); memcpy(&artnum, &token.token[sizeof(ngnum)], sizeof(artnum)); artnum = ntohl(artnum); ngnum = ntohl(ngnum); ng = FindNGByNum(ngnum); if (ng == NULL) { CheckNeedReloadDB(true); ng = FindNGByNum(ngnum); if (ng == NULL) return NULL; } length = strlen(ng) + 20 + strlen(innconf->patharticles); path = xmalloc(length); snprintf(path, length, "%s/%s/%lu", innconf->patharticles, ng, artnum); return path; } /* ** Crack an Xref: line apart into separate strings, each of the form "ng:artnum". ** Return in "num" the number of newsgroups found. */ static char ** CrackXref(char *xref, unsigned int *lenp) { char *p; char **xrefs; char *q; unsigned int len, xrefsize; len = 0; xrefsize = 5; xrefs = xmalloc(xrefsize * sizeof(char *)); /* no path element should exist, nor heading white spaces exist */ p = xref; while (true) { /* check for EOL */ /* shouldn't ever hit null w/o hitting a \r\n first, but best to be paranoid */ if (*p == '\n' || *p == '\r' || *p == 0) { /* hit EOL, return. */ *lenp = len; return xrefs; } /* skip to next space or EOL */ for (q=p; *q && *q != ' ' && *q != '\n' && *q != '\r' ; ++q) ; xrefs[len] = xstrndup(p, q - p); if (++len == xrefsize) { /* grow xrefs if needed. */ xrefsize *= 2; xrefs = xrealloc(xrefs, xrefsize * sizeof(char *)); } p = q; /* skip spaces */ for ( ; *p == ' ' ; p++) ; } } TOKEN tradspool_store(const ARTHANDLE article, const STORAGECLASS class) { char **xrefs; char *xrefhdr; TOKEN token; unsigned int numxrefs; char *ng, *p, *onebuffer; unsigned long artnum; char *path, *linkpath, *dirname; int fd; size_t used; char *nonwfarticle; /* copy of article converted to non-wire format */ unsigned int i; size_t length, nonwflen; memset(&token, 0, sizeof(token)); xrefhdr = article.groups; if ((xrefs = CrackXref(xrefhdr, &numxrefs)) == NULL || numxrefs == 0) { token.type = TOKEN_EMPTY; SMseterror(SMERR_UNDEFINED, "bogus Xref: header"); if (xrefs != NULL) free(xrefs); return token; } if ((p = strchr(xrefs[0], ':')) == NULL) { token.type = TOKEN_EMPTY; SMseterror(SMERR_UNDEFINED, "bogus Xref: header"); for (i = 0 ; i < numxrefs; ++i) free(xrefs[i]); free(xrefs); return token; } *p++ = '\0'; ng = xrefs[0]; DeDotify(ng); artnum = atol(p); token = MakeToken(ng, artnum, class); length = strlen(innconf->patharticles) + strlen(ng) + 32; path = xmalloc(length); snprintf(path, length, "%s/%s/%lu", innconf->patharticles, ng, artnum); /* following chunk of code boldly stolen from timehash.c :-) */ if ((fd = open(path, O_CREAT|O_EXCL|O_WRONLY, ARTFILE_MODE)) < 0) { p = strrchr(path, '/'); *p = '\0'; if (!MakeDirectory(path, true)) { syswarn("tradspool: could not create directory %s", path); token.type = TOKEN_EMPTY; free(path); SMseterror(SMERR_UNDEFINED, NULL); for (i = 0 ; i < numxrefs; ++i) free(xrefs[i]); free(xrefs); return token; } else { *p = '/'; if ((fd = open(path, O_CREAT|O_EXCL|O_WRONLY, ARTFILE_MODE)) < 0) { SMseterror(SMERR_UNDEFINED, NULL); syswarn("tradspool: could not open %s", path); token.type = TOKEN_EMPTY; free(path); for (i = 0 ; i < numxrefs; ++i) free(xrefs[i]); free(xrefs); return token; } } } if (innconf->wireformat) { if (xwritev(fd, article.iov, article.iovcnt) != (ssize_t) article.len) { SMseterror(SMERR_UNDEFINED, NULL); syswarn("tradspool: error writing to %s", path); close(fd); token.type = TOKEN_EMPTY; unlink(path); free(path); for (i = 0 ; i < numxrefs; ++i) free(xrefs[i]); free(xrefs); return token; } } else { onebuffer = xmalloc(article.len); for (used = i = 0 ; i < (unsigned int) article.iovcnt ; i++) { memcpy(&onebuffer[used], article.iov[i].iov_base, article.iov[i].iov_len); used += article.iov[i].iov_len; } nonwfarticle = wire_to_native(onebuffer, used, &nonwflen); free(onebuffer); if (write(fd, nonwfarticle, nonwflen) != (ssize_t) nonwflen) { free(nonwfarticle); SMseterror(SMERR_UNDEFINED, NULL); syswarn("tradspool: error writing to %s", path); close(fd); token.type = TOKEN_EMPTY; unlink(path); free(path); for (i = 0 ; i < numxrefs; ++i) free(xrefs[i]); free(xrefs); return token; } free(nonwfarticle); } close(fd); /* Blah, this is ugly. Have to make symlinks under other pathnames for * backwards compatiblility purposes. */ if (numxrefs > 1) { for (i = 1; i < numxrefs ; ++i) { if ((p = strchr(xrefs[i], ':')) == NULL) continue; *p++ = '\0'; ng = xrefs[i]; DeDotify(ng); artnum = atol(p); length = strlen(innconf->patharticles) + strlen(ng) + 32; linkpath = xmalloc(length); snprintf(linkpath, length, "%s/%s/%lu", innconf->patharticles, ng, artnum); if (link(path, linkpath) < 0) { p = strrchr(linkpath, '/'); *p = '\0'; dirname = xstrdup(linkpath); *p = '/'; if (!MakeDirectory(dirname, true) || link(path, linkpath) < 0) { if (symlink(path, linkpath) < 0) { SMseterror(SMERR_UNDEFINED, NULL); syswarn("tradspool: could not symlink %s to %s", path, linkpath); token.type = TOKEN_EMPTY; free(dirname); free(linkpath); free(path); for (i = 0 ; i < numxrefs; ++i) free(xrefs[i]); free(xrefs); return token; } } free(dirname); } free(linkpath); } } free(path); for (i = 0 ; i < numxrefs; ++i) free(xrefs[i]); free(xrefs); return token; } static ARTHANDLE * OpenArticle(const char *path, RETRTYPE amount) { int fd; PRIV_TRADSPOOL *private; char *p; struct stat sb; ARTHANDLE *art; char *wfarticle; size_t wflen; if (amount == RETR_STAT) { if (access(path, R_OK) < 0) { SMseterror(SMERR_UNDEFINED, NULL); return NULL; } art = xmalloc(sizeof(ARTHANDLE)); art->type = TOKEN_TRADSPOOL; art->data = NULL; art->len = 0; art->private = NULL; return art; } if ((fd = open(path, O_RDONLY)) < 0) { SMseterror(SMERR_UNDEFINED, NULL); return NULL; } art = xmalloc(sizeof(ARTHANDLE)); art->type = TOKEN_TRADSPOOL; if (fstat(fd, &sb) < 0) { SMseterror(SMERR_UNDEFINED, NULL); syswarn("tradspool: could not fstat article %s", path); free(art); close(fd); return NULL; } art->arrived = sb.st_mtime; private = xmalloc(sizeof(PRIV_TRADSPOOL)); art->private = (void *)private; private->artlen = sb.st_size; if (innconf->articlemmap) { if ((private->artbase = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) { SMseterror(SMERR_UNDEFINED, NULL); syswarn("tradspool: could not mmap article %s", path); free(art->private); free(art); close(fd); return NULL; } if (amount == RETR_ALL) madvise(private->artbase, sb.st_size, MADV_WILLNEED); else madvise(private->artbase, sb.st_size, MADV_SEQUENTIAL); /* consider coexisting both wireformatted and nonwireformatted */ p = memchr(private->artbase, '\n', private->artlen); if (p == NULL || p == private->artbase) { SMseterror(SMERR_UNDEFINED, NULL); syswarn("tradspool: apparently corrupt article %s", path); munmap(private->artbase, private->artlen); free(art->private); free(art); close(fd); return NULL; } if (p[-1] == '\r') { private->mmapped = true; } else { wfarticle = wire_from_native(private->artbase, private->artlen, &wflen); munmap(private->artbase, private->artlen); private->artbase = wfarticle; private->artlen = wflen; private->mmapped = false; } } else { private->mmapped = false; private->artbase = xmalloc(private->artlen); if (read(fd, private->artbase, private->artlen) < 0) { SMseterror(SMERR_UNDEFINED, NULL); syswarn("tradspool: could not read article %s", path); free(private->artbase); free(art->private); free(art); close(fd); return NULL; } p = memchr(private->artbase, '\n', private->artlen); if (p == NULL || p == private->artbase) { SMseterror(SMERR_UNDEFINED, NULL); syswarn("tradspool: apparently corrupt article %s", path); free(art->private); free(art); close(fd); return NULL; } if (p[-1] != '\r') { /* need to make a wireformat copy of the article */ wfarticle = wire_from_native(private->artbase, private->artlen, &wflen); free(private->artbase); private->artbase = wfarticle; private->artlen = wflen; } } close(fd); private->ngtp = NULL; private->curdir = NULL; private->curdirname = NULL; private->nextindex = -1; if (amount == RETR_ALL) { art->data = private->artbase; art->len = private->artlen; return art; } if (((p = wire_findbody(private->artbase, private->artlen)) == NULL)) { if (private->mmapped) munmap(private->artbase, private->artlen); else free(private->artbase); SMseterror(SMERR_NOBODY, NULL); free(art->private); free(art); return NULL; } if (amount == RETR_HEAD) { art->data = private->artbase; art->len = p - private->artbase; /* Headers end just before the first empty line (\r\n). */ art->len = art->len - 2; return art; } if (amount == RETR_BODY) { art->data = p; art->len = private->artlen - (p - private->artbase); return art; } SMseterror(SMERR_UNDEFINED, "Invalid retrieve request"); if (private->mmapped) munmap(private->artbase, private->artlen); else free(private->artbase); free(art->private); free(art); return NULL; } ARTHANDLE * tradspool_retrieve(const TOKEN token, const RETRTYPE amount) { char *path; ARTHANDLE *art; static TOKEN ret_token; if (token.type != TOKEN_TRADSPOOL) { SMseterror(SMERR_INTERNAL, NULL); return NULL; } if ((path = TokenToPath(token)) == NULL) { SMseterror(SMERR_NOENT, NULL); return NULL; } if ((art = OpenArticle(path, amount)) != (ARTHANDLE *)NULL) { ret_token = token; art->token = &ret_token; } free(path); return art; } void tradspool_freearticle(ARTHANDLE *article) { PRIV_TRADSPOOL *private; if (article == NULL) return; if (article->private) { private = (PRIV_TRADSPOOL *) article->private; if (private->mmapped) munmap(private->artbase, private->artlen); else free(private->artbase); if (private->curdir) closedir(private->curdir); free(private->curdirname); free(private); } free(article); } bool tradspool_cancel(TOKEN token) { char **xrefs; char *xrefhdr; ARTHANDLE *article; unsigned int numxrefs; char *ng, *p; char *path, *linkpath; unsigned int i; bool result = true; unsigned long artnum; size_t length; if ((path = TokenToPath(token)) == NULL) { SMseterror(SMERR_UNDEFINED, NULL); free(path); return false; } /* Ooooh, this is gross. To find the symlinks pointing to this article, * we open the article and grab its Xref: line (since the token isn't long * enough to store this info on its own). This is *not* going to do * good things for performance of fastrm... -- rmtodd */ if ((article = OpenArticle(path, RETR_HEAD)) == NULL) { free(path); SMseterror(SMERR_UNDEFINED, NULL); return false; } xrefhdr = wire_findheader(article->data, article->len, "Xref", true); if (xrefhdr == NULL) { /* for backwards compatibility; there is no Xref unless crossposted for 1.4 and 1.5 */ if (unlink(path) < 0) result = false; free(path); tradspool_freearticle(article); return result; } if ((xrefs = CrackXref(xrefhdr, &numxrefs)) == NULL || numxrefs == 0) { if (xrefs != NULL) free(xrefs); free(path); tradspool_freearticle(article); SMseterror(SMERR_UNDEFINED, NULL); return false; } tradspool_freearticle(article); for (i = 1 ; i < numxrefs ; ++i) { if ((p = strchr(xrefs[i], ':')) == NULL) continue; *p++ = '\0'; ng = xrefs[i]; DeDotify(ng); artnum = atol(p); length = strlen(innconf->patharticles) + strlen(ng) + 32; linkpath = xmalloc(length); snprintf(linkpath, length, "%s/%s/%lu", innconf->patharticles, ng, artnum); /* repeated unlinkings of a crossposted article may fail on account of the file no longer existing without it truly being an error */ if (unlink(linkpath) < 0) if (errno != ENOENT || i == 1) result = false; free(linkpath); } if (unlink(path) < 0) if (errno != ENOENT || numxrefs == 1) result = false; free(path); for (i = 0 ; i < numxrefs ; ++i) free(xrefs[i]); free(xrefs); return result; } /* ** Find entries for possible articles in dir. "dir" (directory name "dirname"). ** The dirname is needed so we can do stats in the directory to disambiguate ** files from symlinks and directories. */ static struct dirent * FindDir(DIR *dir, char *dirname) { struct dirent *de; int i; bool flag; char *path; struct stat sb; size_t length; unsigned char namelen; while ((de = readdir(dir)) != NULL) { namelen = strlen(de->d_name); for (i = 0, flag = true ; i < namelen ; ++i) { if (!isdigit((unsigned char) de->d_name[i])) { flag = false; break; } } if (!flag) continue; /* if not all digits, skip this entry. */ length = strlen(dirname) + namelen + 2; path = xmalloc(length); strlcpy(path, dirname, length); strlcat(path, "/", length); strlcat(path, de->d_name, length); if (lstat(path, &sb) < 0) { free(path); continue; } free(path); if (!S_ISREG(sb.st_mode)) continue; return de; } return NULL; } ARTHANDLE * tradspool_next(ARTHANDLE *article, const RETRTYPE amount) { PRIV_TRADSPOOL priv; PRIV_TRADSPOOL *newpriv; char *path, *linkpath; struct dirent *de; ARTHANDLE *art; unsigned long artnum; unsigned int i; static TOKEN token; char **xrefs; char *xrefhdr, *ng, *p, *expires, *x; unsigned int numxrefs; STORAGE_SUB *sub; size_t length; if (article == NULL) { priv.ngtp = NULL; priv.curdir = NULL; priv.curdirname = NULL; priv.nextindex = -1; } else { priv = *(PRIV_TRADSPOOL *) article->private; free(article->private); free(article); if (priv.artbase != NULL) { if (priv.mmapped) munmap(priv.artbase, priv.artlen); else free(priv.artbase); } } while (!priv.curdir || ((de = FindDir(priv.curdir, priv.curdirname)) == NULL)) { if (priv.curdir) { closedir(priv.curdir); priv.curdir = NULL; free(priv.curdirname); priv.curdirname = NULL; } /* Advance ngtp to the next entry, if it exists, otherwise start * searching down another ngtable hashchain. */ while (priv.ngtp == NULL || (priv.ngtp = priv.ngtp->next) == NULL) { /* ** note that at the start of a search nextindex is -1, so the inc. ** makes nextindex 0, as it should be. */ priv.nextindex++; if (priv.nextindex >= NGT_SIZE) { /* ran off the end of the table, so return. */ return NULL; } priv.ngtp = NGTable[priv.nextindex]; if (priv.ngtp != NULL) break; } priv.curdirname = concatpath(innconf->patharticles, priv.ngtp->ngname); priv.curdir = opendir(priv.curdirname); } path = concatpath(priv.curdirname, de->d_name); i = strlen(priv.curdirname); /* get the article number while we're here, we'll need it later. */ artnum = atol(&path[i+1]); art = OpenArticle(path, amount); if (art == (ARTHANDLE *)NULL) { art = xmalloc(sizeof(ARTHANDLE)); art->type = TOKEN_TRADSPOOL; art->data = NULL; art->len = 0; art->private = xmalloc(sizeof(PRIV_TRADSPOOL)); art->expires = 0; art->groups = NULL; art->groupslen = 0; newpriv = (PRIV_TRADSPOOL *) art->private; newpriv->artbase = NULL; } else { /* Skip linked (not symlinked) crossposted articles. This algorithm is rather questionable; it only works if the first group/number combination listed in the Xref header is the canonical path. This will always be true for spools created by this implementation, but for traditional INN 1.x servers, articles are expired indepedently from each group and may expire out of the first listed newsgroup before other groups. This algorithm will orphan such articles, not adding them to history. The bit of skipping articles by setting the length of the article to zero is also rather suspect, and I'm not sure what implications that might have for the callers of SMnext. Basically, this whole area really needs to be rethought. */ xrefhdr = wire_findheader(art->data, art->len, "Xref", true); if (xrefhdr != NULL) { if ((xrefs = CrackXref(xrefhdr, &numxrefs)) == NULL || numxrefs == 0) { art->len = 0; } else { /* assumes first one is the original */ if ((p = strchr(xrefs[1], ':')) != NULL) { *p++ = '\0'; ng = xrefs[1]; DeDotify(ng); artnum = atol(p); length = strlen(innconf->patharticles) + strlen(ng) + 32; linkpath = xmalloc(length); snprintf(linkpath, length, "%s/%s/%lu", innconf->patharticles, ng, artnum); if (strcmp(path, linkpath) != 0) { /* this is linked article, skip it */ art->len = 0; } free(linkpath); } } for (i = 0 ; i < numxrefs ; ++i) free(xrefs[i]); free(xrefs); if (innconf->storeonxref) { /* skip path element */ if ((xrefhdr = strchr(xrefhdr, ' ')) == NULL) { art->groups = NULL; art->groupslen = 0; } else { for (xrefhdr++; *xrefhdr == ' '; xrefhdr++); art->groups = xrefhdr; for (p = xrefhdr ; (*p != '\n') && (*p != '\r') ; p++); art->groupslen = p - xrefhdr; } } } if (xrefhdr == NULL || !innconf->storeonxref) { ng = wire_findheader(art->data, art->len, "Newsgroups", true); if (ng == NULL) { art->groups = NULL; art->groupslen = 0; } else { art->groups = ng; for (p = ng ; (*p != '\n') && (*p != '\r') ; p++); art->groupslen = p - ng; } } expires = wire_findheader(art->data, art->len, "Expires", true); if (expires == NULL) { art->expires = 0; } else { /* optionally parse expire header */ for (p = expires + 1; (*p != '\n') && (*(p - 1) != '\r'); p++) ; x = xmalloc(p - expires); memcpy(x, expires, p - expires - 1); x[p - expires - 1] = '\0'; art->expires = parsedate_rfc5322_lax(x); if (art->expires == (time_t) -1) art->expires = 0; else art->expires -= time(NULL); free(x); } /* for backwards compatibility; assumes no Xref unless crossposted for 1.4 and 1.5: just fall through */ } newpriv = (PRIV_TRADSPOOL *) art->private; newpriv->nextindex = priv.nextindex; newpriv->curdir = priv.curdir; newpriv->curdirname = priv.curdirname; newpriv->ngtp = priv.ngtp; if ((sub = SMgetsub(*art)) == NULL || sub->type != TOKEN_TRADSPOOL) { /* maybe storage.conf is modified, after receiving article */ token = MakeToken(priv.ngtp->ngname, artnum, 0); /* Only log an error if art->len is non-zero, since otherwise we get all the ones skipped via the hard-link skipping algorithm commented above. */ if (art->len > 0) warn("tradspool: can't determine class of %s: %s", TokenToText(token), SMerrorstr); } else { token = MakeToken(priv.ngtp->ngname, artnum, sub->class); } art->token = &token; free(path); return art; } static void FreeNGTree(void) { unsigned int i; NGTENT *ngtp, *nextngtp; for (i = 0 ; i < NGT_SIZE ; i++) { ngtp = NGTable[i]; for ( ; ngtp != NULL ; ngtp = nextngtp) { nextngtp = ngtp->next; free(ngtp->ngname); free(ngtp->node); free(ngtp); } NGTable[i] = NULL; } MaxNgNumber = 0; NGTree = NULL; } bool tradspool_ctl(PROBETYPE type, TOKEN *token, void *value) { struct artngnum *ann; unsigned long ngnum; unsigned long artnum; char *ng, *p; switch (type) { case SMARTNGNUM: if ((ann = (struct artngnum *)value) == NULL) return false; CheckNeedReloadDB(false); memcpy(&ngnum, &token->token[0], sizeof(ngnum)); memcpy(&artnum, &token->token[sizeof(ngnum)], sizeof(artnum)); artnum = ntohl(artnum); ngnum = ntohl(ngnum); ng = FindNGByNum(ngnum); if (ng == NULL) { CheckNeedReloadDB(true); ng = FindNGByNum(ngnum); if (ng == NULL) return false; } ann->groupname = xstrdup(ng); for (p = ann->groupname; *p != 0; p++) if (*p == '/') *p = '.'; ann->artnum = (ARTNUM)artnum; return true; default: return false; } } bool tradspool_flushcacheddata(FLUSHTYPE type UNUSED) { return true; } void tradspool_printfiles(FILE *file, TOKEN token UNUSED, char **xref, int ngroups) { int i; char *path, *p; for (i = 0; i < ngroups; i++) { path = xstrdup(xref[i]); for (p = path; *p != '\0'; p++) if (*p == '.' || *p == ':') *p = '/'; fprintf(file, "%s\n", path); free(path); } } void tradspool_shutdown(void) { DumpDB(); FreeNGTree(); } inn-2.6.0/storage/ov.c0000644000175200017520000002501412575023702014156 0ustar iuliusiulius/* $Id: ov.c 9201 2011-06-11 06:01:07Z iulius $ ** ** The implementation of the overview API. ** ** This code handles calls to the overview API by passing them along to the ** appropriate underlying overview method, as well as implementing those ** portions of the overview subsystem that are independent of storage ** method. */ #include "config.h" #include "clibrary.h" #include #include #include #include #include #include "inn/innconf.h" #include "inn/messages.h" #include "inn/wire.h" #include "inn/vector.h" #include "inn/libinn.h" #include "inn/ov.h" #include "ovinterface.h" #include "ovmethods.h" static bool OVdelayrm; static OV_METHOD ov; time_t OVrealnow = 0; bool OVstatall = false; bool OVopen(int mode) { int i; bool val; if (ov.open) /* already opened */ return true; /* if innconf isn't already read in, do so. */ if (innconf == NULL) if (!innconf_read(NULL)) return false; if (!innconf->enableoverview) { warn("enableoverview is not true"); return false; } if (innconf->ovmethod == NULL) { warn("ovmethod is not defined"); return false; } for (i=0;iovmethod, ov_methods[i].name)) break; } if (i == NUM_OV_METHODS) { warn("%s is not found for ovmethod", innconf->ovmethod); return false; } ov = ov_methods[i]; val = (*ov.open)(mode); if (atexit(OVclose) < 0) { OVclose(); return false; } return val; } bool OVgroupstats(char *group, int *lo, int *hi, int *count, int *flag) { if (!ov.open) { /* must be opened */ warn("ovopen must be called first"); return false; } return ((*ov.groupstats)(group, lo, hi, count, flag)); } bool OVgroupadd(char *group, ARTNUM lo, ARTNUM hi, char *flag) { /* lomark should never be changed in each ovmethod if lo is 0 */ if (!ov.open) { /* must be opened */ warn("ovopen must be called first"); return false; } return ((*ov.groupadd)(group, lo, hi, flag)); } bool OVgroupdel(char *group) { if (!ov.open) { /* must be opened */ warn("ovopen must be called first"); return false; } return ((*ov.groupdel)(group)); } OVADDRESULT OVadd(TOKEN token, char *data, int len, time_t arrived, time_t expires) { char *next, *nextcheck; static char *xrefdata, *patcheck, *overdata; char *xrefstart = NULL; char *xrefend; static int xrefdatalen = 0, overdatalen = 0; bool found = false; int xreflen; int i; char *group; ARTNUM artnum; enum uwildmat groupmatch; if (!ov.open) { /* Must be opened. */ warn("ovopen must be called first"); return OVADDFAILED; } /* * Find last Xref: in the overview line. Note we need to find the *last* * Xref:, since there have been corrupted articles on Usenet with Xref: * fragments stuck in other header lines. The last Xref: is guaranteed * to be from our server. */ for (next = data; ((len - (next - data)) > 6 ) && ((next = memchr(next, 'X', len - (next - data))) != NULL); ) { /* Check that Xref: is at the beginning of an overview field. */ if (memcmp(next, "Xref: ", 6) == 0 && next != data && next[-1] == '\t') { found = true; xrefstart = next; } next++; } if (!found) return OVADDFAILED; next = xrefstart; for (i = 0; (i < 2) && (next < (data + len)); i++) { if ((next = memchr(next, ' ', len - (next - data))) == NULL) return OVADDFAILED; next++; } xreflen = len - (next - data); /* * If there are other fields beyond Xref: in overview, then * we must find Xref:'s end, or data following is misinterpreted. */ if ((xrefend = memchr(next, '\t', xreflen)) != NULL) xreflen = xrefend - next; if (xrefdatalen == 0) { xrefdatalen = BIG_BUFFER; xrefdata = xmalloc(xrefdatalen); if (innconf->ovgrouppat != NULL) patcheck = xmalloc(xrefdatalen); } if (xreflen > xrefdatalen) { xrefdatalen = xreflen; xrefdata = xrealloc(xrefdata, xrefdatalen + 1); if (innconf->ovgrouppat != NULL) patcheck = xrealloc(patcheck, xrefdatalen + 1); } if (overdatalen == 0) { overdatalen = BIG_BUFFER; overdata = xmalloc(overdatalen); } if (len + 16 > overdatalen) { overdatalen = len + 16; overdata = xrealloc(overdata, overdatalen); } if (innconf->ovgrouppat != NULL) { memcpy(patcheck, next, xreflen); patcheck[xreflen] = '\0'; for (group = patcheck; group && *group; group = memchr(nextcheck, ' ', xreflen - (nextcheck - patcheck))) { while (isspace((unsigned char) *group)) group++; if ((nextcheck = memchr(group, ':', xreflen - (group - patcheck))) == NULL) return OVADDFAILED; *nextcheck++ = '\0'; groupmatch = uwildmat_poison(group, innconf->ovgrouppat); if (groupmatch == UWILDMAT_POISON) { return OVADDGROUPNOMATCH; } else if (groupmatch == UWILDMAT_FAIL) { if (!SMprobe(SELFEXPIRE, &token, NULL) && innconf->groupbaseexpiry) { /* This article will never be expired, since it does not * have self expiry function in stored method and * groupbaseexpiry is true. */ return OVADDFAILED; } } } } memcpy(xrefdata, next, xreflen); xrefdata[xreflen] = '\0'; for (group = xrefdata; group && *group; group = memchr(next, ' ', xreflen - (next - xrefdata))) { /* Parse the Xref: part into group name and article number. */ while (isspace((unsigned char) *group)) group++; if ((next = memchr(group, ':', xreflen - (group - xrefdata))) == NULL) return OVADDFAILED; *next++ = '\0'; artnum = atoi(next); if (artnum <= 0) continue; /* Skip overview generation according to ovgrouppat. */ if (innconf->ovgrouppat != NULL && uwildmat_poison(group, innconf->ovgrouppat) != UWILDMAT_MATCH) { continue; } sprintf(overdata, "%ld\t", artnum); i = strlen(overdata); memcpy(overdata + i, data, len); i += len; memcpy(overdata + i, "\r\n", 2); i += 2; if(! (*ov.add)(group, artnum, token, overdata, i, arrived, expires)) return OVADDFAILED; } return OVADDCOMPLETED; } bool OVcancel(TOKEN token) { ARTHANDLE *art; const char *xref, *xrefend, *group; size_t xreflen, i; char *xref_copy, *p; ARTNUM artnum; struct cvector *groups; if (!ov.open) { /* must be opened */ warn("ovopen must be called first"); return false; } /* There's no easy way to go from a token to the group and article number pairs that we need to do this. Retrieve the article and find the Xref header and then parse it to figure out what to cancel. Articles without Xref headers lose. */ art = SMretrieve(token, RETR_HEAD); if (art == NULL) return false; xref = wire_findheader(art->data, art->len, "Xref", true); if (xref == NULL) goto fail; xrefend = wire_endheader(xref, art->data + art->len - 1); if (xrefend == NULL) goto fail; xreflen = xrefend - xref + 1; xref_copy = xstrndup(xref, xreflen); SMfreearticle(art); groups = cvector_split_space(xref_copy, NULL); for (i = 0; i < groups->count; i++) { group = groups->strings[i]; p = (char *) strchr(group, ':'); if (p == NULL || p == group || p[1] == '-') continue; *p = '\0'; errno = 0; artnum = strtoul(p + 1, NULL, 10); if (artnum == 0 || errno == ERANGE) continue; /* Don't worry about the return status; the article may have already expired out of some or all of the groups. */ (*ov.cancel)(group, artnum); } free(xref_copy); cvector_free(groups); return true; fail: SMfreearticle(art); return false; } void * OVopensearch(char *group, int low, int high) { if (!ov.open) { /* must be opened */ warn("ovopen must be called first"); return false; } return ((*ov.opensearch)(group, low, high)); } bool OVsearch(void *handle, ARTNUM *artnum, char **data, int *len, TOKEN *token, time_t *arrived) { if (!ov.open) { /* must be opened */ warn("ovopen must be called first"); return false; } return ((*ov.search)(handle, artnum, data, len, token, arrived)); } void OVclosesearch(void *handle) { if (!ov.open) { /* must be opened */ warn("ovopen must be called first"); return; } (*ov.closesearch)(handle); return; } bool OVgetartinfo(char *group, ARTNUM artnum, TOKEN *token) { if (!ov.open) { /* must be opened */ warn("ovopen must be called first"); return false; } return ((*ov.getartinfo)(group, artnum, token)); } bool OVexpiregroup(char *group, int *lo, struct history *h) { if (!ov.open) { /* must be opened */ warn("ovopen must be called first"); return false; } return ((*ov.expiregroup)(group, lo, h)); } bool OVctl(OVCTLTYPE type, void *val) { if (!ov.open) { /* must be opened */ warn("ovopen must be called first"); return false; } switch (type) { case OVGROUPBASEDEXPIRE: if (!innconf->groupbaseexpiry) { warn("OVGROUPBASEDEXPIRE is not allowed if groupbaseexpiry is" " false"); return false; } if (((OVGE *)val)->delayrm) { if ((((OVGE *)val)->filename == NULL) || (strlen(((OVGE *)val)->filename) == 0)) { warn("file name must be specified"); return false; } if ((EXPunlinkfile = fopen(((OVGE *)val)->filename, "w")) == NULL) { syswarn("fopen: %s failed", ((OVGE *)val)->filename); return false; } } OVdelayrm = ((OVGE *)val)->delayrm; OVusepost = ((OVGE *)val)->usepost; OVrealnow = ((OVGE *)val)->now; OVnow = ((OVGE *)val)->now + (time_t)((OVGE *)val)->timewarp; OVquiet = ((OVGE *)val)->quiet; OVkeep = ((OVGE *)val)->keep; OVearliest = ((OVGE *)val)->earliest; OVignoreselfexpire = ((OVGE *)val)->ignoreselfexpire; return true; case OVSTATALL: OVstatall = *(bool *)val; return true; default: return ((*ov.ctl)(type, val)); } } void OVclose(void) { if (!ov.open) return; (*ov.close)(); memset(&ov, '\0', sizeof(ov)); OVEXPcleanup(); } inn-2.6.0/storage/Makefile0000644000175200017520000004304712575023702015034 0ustar iuliusiulius## $Id: Makefile 9919 2015-07-12 13:47:45Z iulius $ include ../Makefile.global # This version number should be increased with every change to the library # source following the rules laid out in the libtool manual. This will also # force the file name of the shared library to change so that one can # recover from make update. We can't use .OLD extensions for the shared # library since ldconfig will think .OLD sorts after the regular library and # will point the binaries at the old library. LTVERSION = 3:0:0 top = .. CFLAGS = $(GCFLAGS) -I. $(BDB_CPPFLAGS) SOURCES = expire.c interface.c methods.c ov.c overdata.c overview.c \ ovmethods.c $(METHOD_SOURCES) OBJECTS = $(SOURCES:.c=.o) LOBJECTS = $(OBJECTS:.o=.lo) .SUFFIXES: .lo all: library programs # Included here after the all target, since additional rules are defined in # Make.methods to be sure that we recurse properly to build the methods. include Make.methods warnings: $(MAKE) COPT='$(WARNINGS)' all install: all $(LI_LPUB) libstorage.$(EXTLIB) $D$(PATHLIB)/libstorage.$(EXTLIB) for F in $(PROGRAMS) ; do \ $(LI_XPRI) $$F $D$(PATHBIN)/`basename $$F` ; \ done bootstrap: Make.methods library: libstorage.$(EXTLIB) programs: $(PROGRAMS) clean clobber distclean: rm -f *.o *.lo */*.o */*.lo libstorage.la libstorage.a rm -f $(PROGRAMS) libstorage_pure_*.a .pure rm -f buildconfig libstorage$(PROFSUFFIX).a rm -rf .libs */.libs maintclean: distclean rm -f Make.methods methods.c methods.h ovmethods.c ovmethods.h $(FIXSCRIPT): @echo Run configure before running make. See INSTALL for details. @exit 1 libstorage.la: $(OBJECTS) $(LIBINN) $(LIBLD) $(LDFLAGS) -o $@ $(LOBJECTS) \ $(LIBINN) $(STORAGE_LIBS) $(LIBS) \ -rpath $(PATHLIB) -version-info $(LTVERSION) libstorage.a: $(OBJECTS) ar r $@ $(OBJECTS) $(RANLIB) libstorage.a # Make.methods is included in the distribution tarball since some non-GNU # makes can't deal with including a non-existent file, so don't depend on # it. The dependencies aren't entirely accurate; you really want to re-run # buildconfig each time a new subdirectory is added to the directory. But # adding a dependency on . is a bit too non-portable for my taste and causes # too many rebuilds. Make.methods methods.h ovmethods.c ovmethods.h methods.c: buildconfig ./buildconfig buildconfig: buildconfig.in $(FIXSCRIPT) $(FIXSCRIPT) -i buildconfig.in .c.o .c.lo: $(LIBCC) $(CFLAGS) $(CCOUTPUT) ovtest: ov.c libstorage.$(EXTLIB) $(LIBINN) $(CC) $(CFLAGS) -D_TEST_ -o ovtest ov.c \ libstorage.$(EXTLIB) $(LIBINN) $(STORAGE_LIBS) $(LIBS) $(LIBINN): ; (cd ../lib ; $(MAKE)) $(LIBHIST): ; (cd ../history ; $(MAKE)) ## Profiling. The rules are a bit brute-force, but good enough. profiled: libstorage$(PROFSUFFIX).a libstorage$(PROFSUFFIX).a: $(SOURCES) rm -f $(OBJECTS) $(MAKEPROFILING) libstorage.a mv libstorage.a libstorage$(PROFSUFFIX).a $(RANLIB) libstorage$(PROFSUFFIX).a rm -f $(OBJECTS) ## Dependencies. Default list, below, is probably good enough. depend: Makefile $(SOURCES) $(EXTRA_SOURCES) $(MAKEDEPEND) '$(CFLAGS)' $(SOURCES) $(EXTRA_SOURCES) # DO NOT DELETE THIS LINE -- make depend depends on it. expire.o: expire.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/innconf.h \ ../include/inn/libinn.h ../include/inn/xmalloc.h ../include/inn/xwrite.h \ ../include/inn/ov.h ../include/inn/history.h ../include/inn/storage.h \ ../include/inn/options.h ovinterface.h ../include/inn/storage.h \ ../include/inn/paths.h ../include/inn/vector.h interface.o: interface.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/conffile.h \ ../include/portable/macros.h ../include/inn/innconf.h \ ../include/inn/messages.h ../include/inn/wire.h interface.h \ ../include/inn/storage.h ../include/inn/options.h \ ../include/inn/libinn.h ../include/inn/xmalloc.h ../include/inn/xwrite.h \ methods.h ../include/inn/paths.h methods.o: methods.c interface.h ../include/config.h \ ../include/inn/defines.h ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h \ ../include/inn/storage.h ../include/inn/options.h methods.h cnfs/cnfs.h \ timecaf/timecaf.h interface.h timehash/timehash.h tradspool/tradspool.h \ trash/trash.h ov.o: ov.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/innconf.h \ ../include/inn/messages.h ../include/inn/wire.h ../include/inn/vector.h \ ../include/inn/libinn.h ../include/inn/xmalloc.h ../include/inn/xwrite.h \ ../include/inn/ov.h ../include/inn/history.h ../include/inn/storage.h \ ../include/inn/options.h ovinterface.h ../include/inn/storage.h \ ovmethods.h overdata.o: overdata.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/buffer.h \ ../include/inn/innconf.h ../include/inn/messages.h ../include/inn/wire.h \ ../include/inn/vector.h ../include/inn/libinn.h ../include/inn/xmalloc.h \ ../include/inn/xwrite.h ovinterface.h ../include/inn/history.h \ ../include/inn/ov.h ../include/inn/storage.h ../include/inn/options.h \ ../include/inn/storage.h overview.o: overview.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/buffer.h \ ../include/inn/innconf.h ../include/inn/messages.h \ ../include/inn/overview.h ../include/inn/storage.h \ ../include/inn/options.h ../include/inn/wire.h ../include/inn/vector.h \ ../include/inn/libinn.h ../include/inn/xmalloc.h ../include/inn/xwrite.h \ ../include/inn/ov.h ../include/inn/history.h ../include/inn/storage.h \ ovinterface.h ovmethods.h ovmethods.o: ovmethods.c ovinterface.h ../include/config.h \ ../include/inn/defines.h ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h \ ../include/inn/history.h ../include/inn/ov.h ../include/inn/storage.h \ ../include/inn/options.h ../include/inn/storage.h \ buffindexed/buffindexed.h ovdb/ovdb.h tradindexed/tradindexed.h buffindexed/buffindexed.o: buffindexed/buffindexed.c ../include/config.h \ ../include/inn/defines.h ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/portable/mmap.h \ ../include/inn/innconf.h ../include/inn/messages.h \ ../include/inn/libinn.h ../include/inn/xmalloc.h ../include/inn/xwrite.h \ ../include/inn/ov.h ../include/inn/history.h ../include/inn/storage.h \ ../include/inn/options.h ../include/inn/paths.h ovinterface.h \ ../include/inn/storage.h ../include/inn/fdflag.h \ ../include/inn/portable-socket.h ../include/inn/portable-getaddrinfo.h \ ../include/inn/portable-getnameinfo.h buffindexed/shmem.h \ buffindexed/buffindexed.h buffindexed/shmem.o: buffindexed/shmem.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/messages.h \ ../include/inn/libinn.h ../include/inn/xmalloc.h ../include/inn/xwrite.h \ buffindexed/shmem.h cnfs/cnfs.o: cnfs/cnfs.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/portable/mmap.h \ ../include/inn/fdflag.h ../include/inn/portable-socket.h \ ../include/inn/portable-getaddrinfo.h \ ../include/inn/portable-getnameinfo.h ../include/inn/innconf.h \ ../include/inn/messages.h ../include/inn/mmap.h ../include/inn/wire.h \ interface.h ../include/inn/storage.h ../include/inn/options.h \ ../include/inn/libinn.h ../include/inn/xmalloc.h ../include/inn/xwrite.h \ methods.h interface.h ../include/inn/paths.h cnfs/cnfs.h \ cnfs/cnfs-private.h ovdb/ovdb.o: ovdb/ovdb.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/portable/socket.h \ ../include/portable/macros.h ../include/portable/getaddrinfo.h \ ../include/portable/getnameinfo.h ../include/conffile.h \ ../include/portable/macros.h ../include/inn/fdflag.h \ ../include/inn/portable-socket.h ../include/inn/innconf.h \ ../include/inn/libinn.h ../include/inn/xmalloc.h ../include/inn/xwrite.h \ ../include/inn/messages.h ../include/inn/newsuser.h \ ../include/inn/paths.h ../include/inn/storage.h ../include/inn/options.h \ ../include/portable/socket-unix.h ../include/inn/ov.h \ ../include/inn/history.h ../include/inn/storage.h ovinterface.h \ ovdb/ovdb.h ovdb/ovdb-private.h timecaf/caf.o: timecaf/caf.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/messages.h \ ../include/inn/libinn.h ../include/inn/xmalloc.h ../include/inn/xwrite.h \ timecaf/caf.h timecaf/timecaf.o: timecaf/timecaf.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/portable/mmap.h timecaf/caf.h \ ../include/inn/fdflag.h ../include/inn/portable-socket.h \ ../include/inn/portable-getaddrinfo.h \ ../include/inn/portable-getnameinfo.h ../include/inn/innconf.h \ ../include/inn/messages.h ../include/inn/wire.h ../include/inn/libinn.h \ ../include/inn/xmalloc.h ../include/inn/xwrite.h methods.h interface.h \ ../include/inn/storage.h ../include/inn/options.h timecaf/timecaf.h \ interface.h ../include/inn/paths.h timehash/timehash.o: timehash/timehash.c ../include/config.h \ ../include/inn/defines.h ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/portable/mmap.h \ ../include/inn/innconf.h ../include/inn/messages.h ../include/inn/wire.h \ ../include/inn/libinn.h ../include/inn/xmalloc.h ../include/inn/xwrite.h \ methods.h interface.h ../include/inn/storage.h ../include/inn/options.h \ ../include/inn/paths.h timehash/timehash.h interface.h tradindexed/tdx-cache.o: tradindexed/tdx-cache.c ../include/config.h \ ../include/inn/defines.h ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/hashtab.h \ ../include/inn/messages.h ../include/inn/libinn.h \ ../include/inn/xmalloc.h ../include/inn/xwrite.h \ ../include/inn/storage.h ../include/inn/options.h \ tradindexed/tdx-private.h tradindexed/tdx-data.o: tradindexed/tdx-data.c ../include/config.h \ ../include/inn/defines.h ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/portable/mmap.h \ ../include/inn/fdflag.h ../include/inn/portable-socket.h \ ../include/inn/portable-getaddrinfo.h \ ../include/inn/portable-getnameinfo.h ../include/inn/history.h \ ../include/inn/innconf.h ../include/inn/messages.h ../include/inn/mmap.h \ ../include/inn/libinn.h ../include/inn/xmalloc.h ../include/inn/xwrite.h \ ../include/inn/ov.h ../include/inn/storage.h ../include/inn/options.h \ ovinterface.h ../include/inn/storage.h tradindexed/tdx-private.h \ tradindexed/tdx-structure.h tradindexed/tdx-group.o: tradindexed/tdx-group.c ../include/config.h \ ../include/inn/defines.h ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/portable/mmap.h \ ../include/inn/fdflag.h ../include/inn/portable-socket.h \ ../include/inn/portable-getaddrinfo.h \ ../include/inn/portable-getnameinfo.h ../include/inn/hashtab.h \ ../include/inn/innconf.h ../include/inn/messages.h ../include/inn/mmap.h \ ../include/inn/qio.h ../include/inn/vector.h ../include/inn/libinn.h \ ../include/inn/xmalloc.h ../include/inn/xwrite.h ../include/inn/paths.h \ tradindexed/tdx-private.h ../include/inn/storage.h \ ../include/inn/options.h tradindexed/tdx-structure.h tradindexed/tradindexed.o: tradindexed/tradindexed.c ../include/config.h \ ../include/inn/defines.h ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/innconf.h \ ../include/inn/messages.h ../include/inn/libinn.h \ ../include/inn/xmalloc.h ../include/inn/xwrite.h ../include/inn/ov.h \ ../include/inn/history.h ../include/inn/storage.h \ ../include/inn/options.h ../include/inn/storage.h \ tradindexed/tdx-private.h tradindexed/tdx-structure.h \ tradindexed/tradindexed.h tradspool/tradspool.o: tradspool/tradspool.c ../include/config.h \ ../include/inn/defines.h ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/portable/mmap.h \ ../include/inn/innconf.h ../include/inn/messages.h ../include/inn/qio.h \ ../include/inn/wire.h ../include/inn/libinn.h ../include/inn/xmalloc.h \ ../include/inn/xwrite.h ../include/inn/paths.h methods.h interface.h \ ../include/inn/storage.h ../include/inn/options.h tradspool/tradspool.h \ interface.h trash/trash.o: trash/trash.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/libinn.h \ ../include/inn/xmalloc.h ../include/inn/xwrite.h methods.h interface.h \ ../include/inn/storage.h ../include/inn/options.h trash/trash.h \ interface.h tradindexed/tdx-util.o: tradindexed/tdx-util.c ../include/config.h \ ../include/inn/defines.h ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/buffer.h \ ../include/inn/libinn.h ../include/inn/xmalloc.h ../include/inn/xwrite.h \ ../include/inn/history.h ../include/inn/innconf.h \ ../include/inn/messages.h ../include/inn/newsuser.h ../include/inn/ov.h \ ../include/inn/storage.h ../include/inn/options.h ../include/inn/paths.h \ ../include/inn/vector.h ../include/inn/wire.h ovinterface.h \ ../include/inn/storage.h tradindexed/tdx-private.h \ tradindexed/tdx-structure.h inn-2.6.0/storage/methods.h0000644000175200017520000000072412575023702015203 0ustar iuliusiulius/* This file is automatically generated by buildconfig */ #ifndef METHODS_H #define METHODS_H 1 #include "interface.h" #define NUM_STORAGE_METHODS 5 #define TOKEN_CNFS 3 #define TOKEN_TIMECAF 4 #define TOKEN_TIMEHASH 2 #define TOKEN_TRADSPOOL 5 #define TOKEN_TRASH 0 extern STORAGE_METHOD storage_methods[NUM_STORAGE_METHODS]; #endif /* METHODS_H */ inn-2.6.0/storage/buildconfig.in0000644000175200017520000002055112575023702016204 0ustar iuliusiulius#! /usr/bin/perl ## $Id: buildconfig.in 8817 2009-11-17 18:57:19Z iulius $ ## ## Generate linkage code and makefiles for storage and overview methods. ## ## Goes through all subdirectories of the current directory and finds ## directories that are either storage methods or overview methods. Builds ## methods.[ch] and ovmethods.[ch] as well as makefile stubs. require 5.003; use strict; use vars qw(@OVERVIEW @STORAGE); # Storage API functions. @STORAGE = qw(init store retrieve next freearticle cancel ctl flushcacheddata printfiles explaintoken shutdown); # Overview API functions. @OVERVIEW = qw(open groupstats groupadd groupdel add cancel opensearch search closesearch getartinfo expiregroup ctl close); # Used to make heredocs more readable. sub unquote { my ($string) = @_; $string =~ s/^:( {0,7}|\t)//gm; $string } # Parse a method.config file for a storage method, putting information about # that storage method into the given hash ref. sub parse_config { my ($dir, $file, $config) = @_; local $_; $$config{sources} ||= []; $$config{extra} ||= []; $$config{programs} ||= []; $$config{makefiles} ||= []; open (CONFIG, "$dir/$file") or die "Can't open $dir/$file: $!\n"; while () { s/^\s+//; s/\s+$//; if (/^name\s*=\s*(\S+)$/) { my $method = $1; die "$dir/$file: $method has already been defined\n" if (defined $$config{method}{$method}); $$config{method}{$method} = $dir; } elsif (/^number\s*=\s*(\d+)$/) { my $number = $1; if (defined $$config{number}{$number}) { die "$dir/$file: method number $number was already " . "allocated in $$config{number}{$number}\n"; } $$config{number}{$dir} = $number; } elsif (/^sources\s*=\s*(.*)/) { my $sources = $1; my @sources = split (' ', $sources); push (@{ $$config{sources} }, map { "$dir/$_" } @sources); } elsif (/^extra-sources\s*=\s*(.*)/) { my $extra = $1; my @extra = split (' ', $extra); push (@{ $$config{extra} }, map { "$dir/$_" } @extra); } elsif (/^programs\s*=\s*(.*)/) { my $programs = $1; my @programs = split (' ', $programs); push (@{ $$config{programs} }, map { "$dir/$_" } @programs); } else { warn "$dir/$file: ignoring unknown line: $_\n"; } } # If there is a makefile fragment in the directory, note it. if (-f "$dir/method.mk") { push (@{ $$config{makefiles} }, "$dir/method.mk"); } elsif (-f "$dir/ovmethod.mk") { push (@{ $$config{makefiles} }, "$dir/ovmethod.mk"); } } # Write out include directives for a list of files. sub write_includes { my ($fh, $config) = @_; my $method; for $method (sort keys %{ $$config{method} }) { my $path = $$config{method}{$method}; print $fh qq(\#include "$path/$method.h"\n); } } # Write out the method struct. sub write_methods { my ($fh, $config, $prefix, @funcs) = @_; my ($notfirst, $method); for $method (sort keys %{ $$config{method} }) { print $fh "\n},\n" if $notfirst; print $fh qq(\{\n "$method"); print $fh ', ', $prefix, '_', uc ($method) if $prefix; for (@funcs) { print $fh ",\n ${method}_$_"; } $notfirst++; } print $fh "\n}\n};\n\n"; } # Write out the constant defines for methods. sub write_constants { my ($fh, $config, $prefix) = @_; my $method; for $method (sort keys %{ $$config{method} }) { printf $fh "#define ${prefix}_%-30s%d\n", uc ($method), $$config{number}{$$config{method}{$method}}; } } # Write out methods.c and methods.h for the interface to the storage # methods. sub write_storage { my $storage = shift; open (DEF, '> methods.c.new') or die "Can't create methods.c.new: $!\n"; print DEF unquote (<<'EOE'); : /* This file is automatically generated by buildconfig. */ : : #include "interface.h" : #include "methods.h" EOE my $method; write_includes (\*DEF, $storage); print DEF "\nSTORAGE_METHOD storage_methods[", scalar (keys %{ $$storage{method} }), "] = {\n"; write_methods (\*DEF, $storage, 'TOKEN', @STORAGE); close DEF; rename ('methods.c.new', 'methods.c'); open (H, '> methods.h.new') or die "Can't open methods.h.new: $!\n"; print H unquote (<<'EOE'); : /* This file is automatically generated by buildconfig */ : : #ifndef METHODS_H : #define METHODS_H 1 : : #include "interface.h" : EOE print H '#define NUM_STORAGE_METHODS ', scalar (keys %{ $$storage{method} }), "\n\n"; write_constants (\*H, $storage, 'TOKEN'); print H unquote (<<'EOE'); : : extern STORAGE_METHOD storage_methods[NUM_STORAGE_METHODS]; : : #endif /* METHODS_H */ EOE close H; rename ('methods.h.new', 'methods.h'); } # Write out ovmethods.c and ovmethods.h for the interface to the overview # methods. sub write_overview { my $overview = shift; open (DEF, '> ovmethods.c.new') or die "Can't create ovmethods.c.new: $!\n"; print DEF unquote (<<'EOE'); : /* This file is automatically generated by buildconfig. */ : : #include "ovinterface.h" EOE write_includes (\*DEF, $overview); print DEF "\nOV_METHOD ov_methods[", scalar (keys %{ $$overview{method} }), "] = {\n"; write_methods (\*DEF, $overview, undef, @OVERVIEW); close DEF; rename ('ovmethods.c.new', 'ovmethods.c'); open (H, '> ovmethods.h.new') or die "Can't open ovmethods.h.new: $!\n"; print H unquote (<<'EOE'); : /* This file is automatically generated by buildconfig */ : : #ifndef OVMETHODS_H : #define OVMETHODS_H 1 : : #include "ovinterface.h" : EOE print H '#define NUM_OV_METHODS ', scalar (keys %{ $$overview{method} }), "\n"; print H unquote (<<'EOE'); : : extern OV_METHOD ov_methods[NUM_OV_METHODS]; : : #endif /* OVMETHODS_H */ EOE close H; rename ('ovmethods.h.new', 'ovmethods.h'); } # Return a string setting a makefile variable. Tab over the = properly and # wrap to fit our coding standards. sub makefile_var { my ($variable, @values) = @_; my $output; $output = sprintf ("%-15s =", $variable); my $column = 17; for (@values) { if ($column > 17 && 77 - $column < length ($_)) { $output .= " \\\n" . ' ' x 17; $column = 17; } $output .= " $_"; $column += 1 + length ($_); } $output .= "\n"; return $output; } # Write out the makefile fragment for overview and storage methods. sub write_makefile { my ($dirs, $sources, $extra, $programs, $makefiles) = @_; open (MAKE, '> Make.methods.new') or die "Can't create Make.methods.new: $!\n"; print MAKE "# This file is automatically generated by buildconfig\n\n"; print MAKE makefile_var ('METHOD_SOURCES', @$sources); print MAKE makefile_var ('EXTRA_SOURCES', @$extra); print MAKE makefile_var ('PROGRAMS', @$programs); for (@$makefiles) { print MAKE "\n\n## Included from $_\n\n"; open (FRAG, $_) or die "Can't open $_: $!\n"; print MAKE ; close FRAG; } rename ('Make.methods.new', 'Make.methods'); } my ($dir, %storage, %overview); if (!-d 'cnfs') { if (-d 'storage/cnfs') { chdir 'storage' or die "Can't chdir to storage: $!\n"; } else { die "Can't find storage directory (looking for storage/cnfs)\n"; } } opendir (D, ".") or die "Can't open current directory: $!\n"; my @dirs = sort readdir D; for $dir (@dirs) { if (-e "$dir/method.config") { parse_config ($dir, 'method.config', \%storage); } if (-e "$dir/ovmethod.config") { parse_config ($dir, 'ovmethod.config', \%overview); } } write_storage (\%storage); write_overview (\%overview); @dirs = (sort values %{ $storage{method} }, sort values %{ $overview{method} }); my @sources = (sort @{ $storage{sources} }, sort @{ $overview{sources} }); my @extra = (sort @{ $storage{extra} }, sort @{ $overview{extra} }); my @programs = sort (@{ $storage{programs} }, @{ $overview{programs} }); my @makefiles = sort (@{ $storage{makefiles} }, @{ $overview{makefiles} }); write_makefile (\@dirs, \@sources, \@extra, \@programs, \@makefiles); inn-2.6.0/storage/timehash/0000755000175200017520000000000012575023701015165 5ustar iuliusiuliusinn-2.6.0/storage/timehash/timehash.c0000644000175200017520000003702612575023702017144 0ustar iuliusiulius/* $Id: timehash.c 9870 2015-05-17 17:23:07Z iulius $ ** ** Storage manager module for timehash method. */ #include "config.h" #include "clibrary.h" #include "portable/mmap.h" #include #include #include #include #include #include #include #include "inn/innconf.h" #include "inn/messages.h" #include "inn/wire.h" #include "inn/libinn.h" #include "methods.h" #include "inn/paths.h" #include "timehash.h" typedef struct { char *base; /* Base of the mmaped file */ int len; /* Length of the file */ DIR *top; /* Open handle on the top level directory */ DIR *sec; /* Open handle on the 2nd level directory */ DIR *ter; /* Open handle on the third level directory */ DIR *artdir; /* Open handle on the article directory */ struct dirent *topde; /* dirent entry for the last entry retrieved in top */ struct dirent *secde; /* dirent entry for the last entry retrieved in sec */ struct dirent *terde; /* dirent entry for the last entry retrieved in ter */ } PRIV_TIMEHASH; typedef enum {FIND_DIR, FIND_ART, FIND_TOPDIR} FINDTYPE; static int SeqNum = 0; /* ** The token is @02nnaabbccddyyyy00000000000000000000@ ** where "02" is the timehash method number, ** "nn" the hexadecimal value of the storage class, ** "aabbccdd" the arrival time in hexadecimal, ** "yyyy" the hexadecimal sequence number seqnum. ** ** innconf->patharticles + '/time-nn/bb/cc/yyyy-aadd' ** where "nn" is the hexadecimal value of the storage class, ** "aabbccdd" the arrival time in hexadecimal, ** "yyyy" the hexadecimal sequence number seqnum. */ char * timehash_explaintoken(const TOKEN token) { char *text; uint32_t arrival; uint16_t seqnum; memcpy(&arrival, &token.token[0], sizeof(arrival)); memcpy(&seqnum, &token.token[4], sizeof(seqnum)); xasprintf(&text, "method=timehash class=%u time=%lu seqnum=%lu file=%s/time-%02x/%02x/%02x/%04x-%02x%02x", (unsigned int) token.class, (unsigned long) ntohl(arrival), (unsigned long) ntohs(seqnum), innconf->patharticles, token.class, (ntohl(arrival) >> 16) & 0xff, (ntohl(arrival) >> 8) & 0xff, ntohs(seqnum), (ntohl(arrival) >> 24) & 0xff, ntohl(arrival) & 0xff); return text; } static TOKEN MakeToken(time_t now, int seqnum, STORAGECLASS class, TOKEN *oldtoken) { TOKEN token; uint32_t i; uint16_t s; if (oldtoken == (TOKEN *)NULL) memset(&token, '\0', sizeof(token)); else memcpy(&token, oldtoken, sizeof(token)); token.type = TOKEN_TIMEHASH; token.class = class; i = htonl(now); memcpy(token.token, &i, sizeof(i)); s = htons(seqnum & 0xffff); memcpy(&token.token[sizeof(i)], &s, sizeof(s)); return token; } static void BreakToken(TOKEN token, time_t *now, int *seqnum) { uint32_t i; uint16_t s = 0; memcpy(&i, token.token, sizeof(i)); memcpy(&s, &token.token[sizeof(i)], sizeof(s)); *now = ntohl(i); *seqnum = (int)ntohs(s); } static char *MakePath(time_t now, int seqnum, const STORAGECLASS class) { char *path; size_t length; length = strlen(innconf->patharticles) + 32; path = xmalloc(length); snprintf(path, length, "%s/time-%02x/%02x/%02x/%04x-%04x", innconf->patharticles, class, (unsigned int)((now >> 16) & 0xff), (unsigned int)((now >> 8) & 0xff), seqnum, (unsigned int)((now & 0xff) | ((now >> 16 & 0xff00)))); return path; } static TOKEN *PathToToken(char *path) { int n; unsigned int tclass, t1, t2, t3, seqnum; STORAGECLASS class; time_t now; static TOKEN token; n = sscanf(path, "time-%02x/%02x/%02x/%04x-%04x", &tclass, &t1, &t2, &seqnum, &t3); if (n != 5) return (TOKEN *)NULL; now = ((t1 << 16) & 0xff0000) | ((t2 << 8) & 0xff00) | ((t3 << 16) & 0xff000000) | (t3 & 0xff); class = tclass; token = MakeToken(now, seqnum, class, (TOKEN *)NULL); return &token; } bool timehash_init(SMATTRIBUTE *attr) { if (attr == NULL) { warn("timehash: attr is NULL"); SMseterror(SMERR_INTERNAL, "attr is NULL"); return false; } attr->selfexpire = false; attr->expensivestat = true; if (STORAGE_TOKEN_LENGTH < 6) { warn("timehash: token length is less than six bytes"); SMseterror(SMERR_TOKENSHORT, NULL); return false; } return true; } TOKEN timehash_store(const ARTHANDLE article, const STORAGECLASS class) { char *path; char *p; time_t now; TOKEN token; int fd; ssize_t result; int seq; int i; if (article.arrived == (time_t)0) now = time(NULL); else now = article.arrived; memset(&token, 0, sizeof(token)); for (i = 0; i < 0x10000; i++) { seq = SeqNum; SeqNum = (SeqNum + 1) & 0xffff; path = MakePath(now, seq, class); if ((fd = open(path, O_CREAT|O_EXCL|O_WRONLY, ARTFILE_MODE)) < 0) { if (errno == EEXIST) continue; p = strrchr(path, '/'); *p = '\0'; if (!MakeDirectory(path, true)) { syswarn("timehash: could not make directory %s", path); token.type = TOKEN_EMPTY; free(path); SMseterror(SMERR_UNDEFINED, NULL); return token; } else { *p = '/'; if ((fd = open(path, O_CREAT|O_EXCL|O_WRONLY, ARTFILE_MODE)) < 0) { SMseterror(SMERR_UNDEFINED, NULL); syswarn("timehash: could not create %s", path); token.type = TOKEN_EMPTY; free(path); return token; } } } break; } if (i == 0x10000) { SMseterror(SMERR_UNDEFINED, NULL); warn("timehash: all sequence numbers for time %lu and class %d are" " reserved", (unsigned long) now, class); token.type = TOKEN_EMPTY; free(path); return token; } result = xwritev(fd, article.iov, article.iovcnt); if (result != (ssize_t) article.len) { SMseterror(SMERR_UNDEFINED, NULL); syswarn("timehash: error writing %s", path); close(fd); token.type = TOKEN_EMPTY; unlink(path); free(path); return token; } close(fd); free(path); return MakeToken(now, seq, class, article.token); } static ARTHANDLE *OpenArticle(const char *path, RETRTYPE amount) { int fd; PRIV_TIMEHASH *private; char *p; struct stat sb; ARTHANDLE *art; if (amount == RETR_STAT) { if (access(path, R_OK) < 0) { SMseterror(SMERR_UNDEFINED, NULL); return NULL; } art = xmalloc(sizeof(ARTHANDLE)); art->type = TOKEN_TIMEHASH; art->data = NULL; art->len = 0; art->private = NULL; return art; } if ((fd = open(path, O_RDONLY)) < 0) { SMseterror(SMERR_UNDEFINED, NULL); return NULL; } art = xmalloc(sizeof(ARTHANDLE)); art->type = TOKEN_TIMEHASH; if (fstat(fd, &sb) < 0) { SMseterror(SMERR_UNDEFINED, NULL); syswarn("timehash: could not fstat article"); free(art); return NULL; } private = xmalloc(sizeof(PRIV_TIMEHASH)); art->private = (void *)private; private->len = sb.st_size; if (innconf->articlemmap) { if ((private->base = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) { SMseterror(SMERR_UNDEFINED, NULL); syswarn("timehash: could not mmap article"); free(art->private); free(art); return NULL; } if (amount == RETR_ALL) madvise(private->base, sb.st_size, MADV_WILLNEED); else madvise(private->base, sb.st_size, MADV_SEQUENTIAL); } else { private->base = xmalloc(private->len); if (read(fd, private->base, private->len) < 0) { SMseterror(SMERR_UNDEFINED, NULL); syswarn("timehash: could not read article"); free(private->base); free(art->private); free(art); return NULL; } } close(fd); private->top = NULL; private->sec = NULL; private->ter = NULL; private->artdir = NULL; private->topde = NULL; private->secde = NULL; private->terde = NULL; if (amount == RETR_ALL) { art->data = private->base; art->len = private->len; return art; } if ((p = wire_findbody(private->base, private->len)) == NULL) { SMseterror(SMERR_NOBODY, NULL); if (innconf->articlemmap) munmap(private->base, private->len); else free(private->base); free(art->private); free(art); return NULL; } if (amount == RETR_HEAD) { art->data = private->base; art->len = p - private->base; /* Headers end just before the first empty line (\r\n). */ art->len = art->len - 2; return art; } if (amount == RETR_BODY) { art->data = p; art->len = private->len - (p - private->base); return art; } SMseterror(SMERR_UNDEFINED, "Invalid retrieve request"); if (innconf->articlemmap) munmap(private->base, private->len); else free(private->base); free(art->private); free(art); return NULL; } ARTHANDLE *timehash_retrieve(const TOKEN token, const RETRTYPE amount) { time_t now; int seqnum; char *path; ARTHANDLE *art; static TOKEN ret_token; if (token.type != TOKEN_TIMEHASH) { SMseterror(SMERR_INTERNAL, NULL); return NULL; } BreakToken(token, &now, &seqnum); path = MakePath(now, seqnum, token.class); if ((art = OpenArticle(path, amount)) != (ARTHANDLE *)NULL) { art->arrived = now; ret_token = token; art->token = &ret_token; } free(path); return art; } void timehash_freearticle(ARTHANDLE *article) { PRIV_TIMEHASH *private; if (!article) return; if (article->private) { private = (PRIV_TIMEHASH *)article->private; if (innconf->articlemmap) munmap(private->base, private->len); else free(private->base); if (private->top) closedir(private->top); if (private->sec) closedir(private->sec); if (private->ter) closedir(private->ter); if (private->artdir) closedir(private->artdir); free(private); } free(article); } bool timehash_cancel(TOKEN token) { time_t now; int seqnum; char *path; int result; BreakToken(token, &now, &seqnum); path = MakePath(now, seqnum, token.class); result = unlink(path); free(path); if (result < 0) { SMseterror(SMERR_UNDEFINED, NULL); return false; } return true; } static struct dirent *FindDir(DIR *dir, FINDTYPE type) { struct dirent *de; while ((de = readdir(dir)) != NULL) { if (type == FIND_TOPDIR) if ((strlen(de->d_name) == 7) && (strncmp(de->d_name, "time-", 5) == 0) && isxdigit((unsigned char) de->d_name[5]) && isxdigit((unsigned char) de->d_name[6])) return de; if (type == FIND_DIR) if ((strlen(de->d_name) == 2) && isxdigit((unsigned char) de->d_name[0]) && isxdigit((unsigned char) de->d_name[1])) return de; if (type == FIND_ART) if ((strlen(de->d_name) == 9) && isxdigit((unsigned char) de->d_name[0]) && isxdigit((unsigned char) de->d_name[1]) && isxdigit((unsigned char) de->d_name[2]) && isxdigit((unsigned char) de->d_name[3]) && isxdigit((unsigned char) de->d_name[5]) && isxdigit((unsigned char) de->d_name[6]) && isxdigit((unsigned char) de->d_name[7]) && isxdigit((unsigned char) de->d_name[8]) && (de->d_name[4] == '-')) return de; } return NULL; } ARTHANDLE * timehash_next(ARTHANDLE *article, const RETRTYPE amount) { PRIV_TIMEHASH priv; PRIV_TIMEHASH *newpriv; char *path; struct dirent *de; ARTHANDLE *art; int seqnum; size_t length; length = strlen(innconf->patharticles) + 32; path = xmalloc(length); if (article == NULL) { priv.top = NULL; priv.sec = NULL; priv.ter = NULL; priv.artdir = NULL; priv.topde = NULL; priv.secde = NULL; priv.terde = NULL; } else { priv = *(PRIV_TIMEHASH *)article->private; free(article->private); free(article); if (priv.base != NULL) { if (innconf->articlemmap) munmap(priv.base, priv.len); else free(priv.base); } } while (!priv.artdir || ((de = FindDir(priv.artdir, FIND_ART)) == NULL)) { if (priv.artdir) { closedir(priv.artdir); priv.artdir = NULL; } while (!priv.ter || ((priv.terde = FindDir(priv.ter, FIND_DIR)) == NULL)) { if (priv.ter) { closedir(priv.ter); priv.ter = NULL; } while (!priv.sec || ((priv.secde = FindDir(priv.sec, FIND_DIR)) == NULL)) { if (priv.sec) { closedir(priv.sec); priv.sec = NULL; } if (!priv.top || ((priv.topde = FindDir(priv.top, FIND_TOPDIR)) == NULL)) { if (priv.top) { /* end of search */ closedir(priv.top); priv.top = NULL; free(path); return NULL; } snprintf(path, length, "%s", innconf->patharticles); if ((priv.top = opendir(path)) == NULL) { SMseterror(SMERR_UNDEFINED, NULL); free(path); return NULL; } if ((priv.topde = FindDir(priv.top, FIND_TOPDIR)) == NULL) { SMseterror(SMERR_UNDEFINED, NULL); closedir(priv.top); free(path); return NULL; } } snprintf(path, length, "%s/%s", innconf->patharticles, priv.topde->d_name); if ((priv.sec = opendir(path)) == NULL) continue; } snprintf(path, length, "%s/%s/%s", innconf->patharticles, priv.topde->d_name, priv.secde->d_name); if ((priv.ter = opendir(path)) == NULL) continue; } snprintf(path, length, "%s/%s/%s/%s", innconf->patharticles, priv.topde->d_name, priv.secde->d_name, priv.terde->d_name); if ((priv.artdir = opendir(path)) == NULL) continue; } if (de == NULL) return NULL; snprintf(path, length, "%s/%s/%s/%s/%s", innconf->patharticles, priv.topde->d_name, priv.secde->d_name, priv.terde->d_name, de->d_name); art = OpenArticle(path, amount); if (art == (ARTHANDLE *)NULL) { art = xmalloc(sizeof(ARTHANDLE)); art->type = TOKEN_TIMEHASH; art->data = NULL; art->len = 0; art->private = xmalloc(sizeof(PRIV_TIMEHASH)); newpriv = (PRIV_TIMEHASH *)art->private; newpriv->base = NULL; } newpriv = (PRIV_TIMEHASH *)art->private; newpriv->top = priv.top; newpriv->sec = priv.sec; newpriv->ter = priv.ter; newpriv->artdir = priv.artdir; newpriv->topde = priv.topde; newpriv->secde = priv.secde; newpriv->terde = priv.terde; snprintf(path, length, "%s/%s/%s/%s", priv.topde->d_name, priv.secde->d_name, priv.terde->d_name, de->d_name); art->token = PathToToken(path); BreakToken(*art->token, &(art->arrived), &seqnum); free(path); return art; } bool timehash_ctl(PROBETYPE type, TOKEN *token UNUSED, void *value) { struct artngnum *ann; switch (type) { case SMARTNGNUM: if ((ann = (struct artngnum *)value) == NULL) return false; /* make SMprobe() call timehash_retrieve() */ ann->artnum = 0; return true; default: return false; } } bool timehash_flushcacheddata(FLUSHTYPE type UNUSED) { return true; } void timehash_printfiles(FILE *file, TOKEN token, char **xref UNUSED, int ngroups UNUSED) { time_t now; int seqnum; char *path; BreakToken(token, &now, &seqnum); path = MakePath(now, seqnum, token.class); fprintf(file, "%s\n", path); } void timehash_shutdown(void) { } inn-2.6.0/storage/timehash/timehash.h0000644000175200017520000000145712575023702017150 0ustar iuliusiulius/* $Id: timehash.h 8817 2009-11-17 18:57:19Z iulius $ ** ** Storage manager module header for timehash method. */ #ifndef __TIMEHASH_H__ #define __TIMEHASH_H__ #include "config.h" #include "interface.h" bool timehash_init(SMATTRIBUTE *attr); TOKEN timehash_store(const ARTHANDLE article, const STORAGECLASS class); ARTHANDLE *timehash_retrieve(const TOKEN token, const RETRTYPE amount); ARTHANDLE *timehash_next(ARTHANDLE *article, const RETRTYPE amount); void timehash_freearticle(ARTHANDLE *article); bool timehash_cancel(TOKEN token); bool timehash_ctl(PROBETYPE type, TOKEN *token, void *value); bool timehash_flushcacheddata(FLUSHTYPE type); void timehash_printfiles(FILE *file, TOKEN token, char **xref, int ngroups); char *timehash_explaintoken(const TOKEN token); void timehash_shutdown(void); #endif inn-2.6.0/storage/timehash/method.config0000644000175200017520000000006412575023702017635 0ustar iuliusiuliusname = timehash number = 2 sources = timehash.c inn-2.6.0/storage/ovmethods.c0000644000175200017520000000222412575023702015540 0ustar iuliusiulius/* This file is automatically generated by buildconfig. */ #include "ovinterface.h" #include "buffindexed/buffindexed.h" #include "ovdb/ovdb.h" #include "tradindexed/tradindexed.h" OV_METHOD ov_methods[3] = { { "buffindexed", buffindexed_open, buffindexed_groupstats, buffindexed_groupadd, buffindexed_groupdel, buffindexed_add, buffindexed_cancel, buffindexed_opensearch, buffindexed_search, buffindexed_closesearch, buffindexed_getartinfo, buffindexed_expiregroup, buffindexed_ctl, buffindexed_close }, { "ovdb", ovdb_open, ovdb_groupstats, ovdb_groupadd, ovdb_groupdel, ovdb_add, ovdb_cancel, ovdb_opensearch, ovdb_search, ovdb_closesearch, ovdb_getartinfo, ovdb_expiregroup, ovdb_ctl, ovdb_close }, { "tradindexed", tradindexed_open, tradindexed_groupstats, tradindexed_groupadd, tradindexed_groupdel, tradindexed_add, tradindexed_cancel, tradindexed_opensearch, tradindexed_search, tradindexed_closesearch, tradindexed_getartinfo, tradindexed_expiregroup, tradindexed_ctl, tradindexed_close } }; inn-2.6.0/storage/timecaf/0000755000175200017520000000000012575023701014773 5ustar iuliusiuliusinn-2.6.0/storage/timecaf/caf.h0000644000175200017520000001427312575023702015705 0ustar iuliusiulius/* $Id: caf.h 8625 2009-09-07 09:05:20Z iulius $ ** ** Declarations needed for handling CAF (Crunched Article Files) ** Written by Richard Todd (rmtodd@mailhost.ecn.uoknor.edu) 3/24/96 */ /* ** Format of a crunched article file: ** Header: */ typedef struct _CAFHEADER { char Magic[4]; /* Magic Number "CRMT" */ ARTNUM Low; /* lowest article in the file */ ARTNUM NumSlots; /* number of articles there are room for in the TOC */ ARTNUM High; /* last article actually present in the file */ size_t Free; /* amount of space currently unused (freed by cancels/expires) */ off_t StartDataBlock; /* offset of first article data block. */ unsigned int BlockSize; /* unit of allocation for CAF files. */ size_t FreeZoneTabSize; /* amount of space taken up by the free zone table. */ size_t FreeZoneIndexSize; /* size taken up by the "index" part of the free zone table. */ time_t LastCleaned; /* note time of last cleaning. */ int spare[3]; } CAFHEADER; #define CAF_MAGIC "CRMT" #define CAF_MAGIC_LEN 4 #define CAF_DEFAULT_BLOCKSIZE 512 /* ** Then the table of free blocks. The table is FreeZoneTabSize bytes ** long. First comes a "first-level" or "index" bitmap, taking up the ** space from the end of the CAFHEADER to the end of the first ** block, i.e. FreeZoneIndexSize. The rest of the table is a big bitmap ** listing free blocks in the 'data' portion of the CAF file. ** ** In the "index" bitmap: LSB of bitmap byte 0 is 1 if there are any 1s ** (free blocks) listed in the first block of the big bitmap, and 0 if there ** are no 1s in that block. The remaining bits of the index bitmap ** correspond to the remaining blocks of the big bitmap accordingly. ** The idea is that from the index bitmap one can tell which part of the ** main bitmap is likely to have free blocks w/o having to read the entire ** main bitmap. ** ** As for the main bitmap, each bit is 1 if the corresponding data ** block (BlockSize bytes) is free. LSB of bitmap byte 0 corresponds ** to the block @ offset StartDataBlock, and all the rest follow on ** accordingly. ** ** Note that the main part of the bitmap is *always* FreeZoneIndexSize*8 ** blocks long, no matter how big the CAF file is. The table of free blocks ** is almost always sparse. Also note that blocks past EOF in the CAF file ** are *not* considered free. If the CAF article write routines fail to ** find free space in the free block bitmaps, they will always attempt to ** extend the CAF file instead. */ #define CAF_DEFAULT_FZSIZE (CAF_DEFAULT_BLOCKSIZE-sizeof(CAFHEADER)) #define CAF_MIN_FZSIZE 128 /* ** (Note: the CAFBITMAP structure isn't what's actually stored on disk ** in the free bitmap region, this is just a convenient structure to ** keep the bitmap size and StartBlockOffset together with the bitmap ** w/o having keep passing the original CAFHEADER to every routine ** that wants it. The bitmap structure contains the first (index) bitmap, ** as well as pointers to structures for each block of the main bitmap that ** has been read into memory. */ typedef struct _CAFBITMAP { off_t StartDataBlock; off_t MaxDataBlock; /* can only handle offsets < this with this bitmap. */ size_t FreeZoneTabSize; size_t FreeZoneIndexSize; size_t BytesPerBMB; /* size of chunk, in bytes, that any given BMBLK can map. */ unsigned int BlockSize; unsigned int NumBMB; /* size of Blocks array. */ struct _CAFBMB **Blocks; char *Bits; } CAFBITMAP; typedef struct _CAFBMB { off_t StartDataBlock; off_t MaxDataBlock; int Dirty; /* 1 if this BMB has had any bits changed. */ char *BMBBits; } CAFBMB; /* ** Next in the file are the TOC (Table of Contents) entries. Each TOC ** entry describes an article. */ typedef struct _CAFTOCENT { off_t Offset; size_t Size; time_t ModTime; } CAFTOCENT; /* ** And then after the NumSlots TOC Entries, the actual articles, one after ** another, always starting at offsets == 0 mod BlockSize. */ /* ** Number of slots to put in TOC by default. Can be raised if we ever get ** more than 256*1024=262144 articles in a file (frightening thought). */ #define CAF_DEFAULT_TOC_SIZE (256 * 1024) /* ** Default extension name for CAF file in the news spool dir. */ #define CAF_NAME "CF" extern int CAFOpenArtRead(const char *cfpath, ARTNUM art, size_t *len); extern int CAFOpenArtWrite(char *cfpath, ARTNUM *art, int WaitLock, size_t size); extern int CAFStartWriteFd(int fd, ARTNUM *art, size_t size); extern int CAFFinishWriteFd(int fd); extern int CAFFinishArtWrite(int fd); extern int CAFCreateCAFFile(char *cfpath, ARTNUM lowart, ARTNUM tocsize, size_t cfsize, int nolink, char *temppath, size_t pathlen); extern const char *CAFErrorStr(void); extern CAFTOCENT *CAFReadTOC(char *cfpath, CAFHEADER *ch); extern int CAFRemoveMultArts(char *cfpath, unsigned int narts, ARTNUM *arts); extern int CAFStatArticle(char *path, ARTNUM art, struct stat *st); #ifdef CAF_INNARDS /* ** Functions used internally by caf.c, and by the cleaner program, and cafls ** but probably aren't useful/desirable to be used by others. */ extern int CAFOpenReadTOC(char *cfpath, CAFHEADER *ch, CAFTOCENT **tocpp); extern int CAFReadHeader(int fd, CAFHEADER *h); extern off_t CAFRoundOffsetUp(off_t offt, unsigned int bsize); extern CAFBITMAP * CAFReadFreeBM(int fd, CAFHEADER *h); extern void CAFDisposeBitmap(CAFBITMAP *cbm); /* ** Note: CAFIsBlockFree needs the fd, since blocks of the free bitmap may ** need to be fetched from disk. */ extern int CAFIsBlockFree(CAFBITMAP *bm, int fd, off_t block); #endif extern int caf_error; /* last error encountered by library. */ extern int caf_errno; /* saved value of errno here if I/O error hit by lib. */ #define CAF_ERR_IO 1 /* generic I/O error, check caf_errno for details */ #define CAF_ERR_BADFILE 2 /* corrupt file */ #define CAF_ERR_ARTNOTHERE 3 /* article not in the database */ #define CAF_ERR_CANTCREATECAF 4 /* can't create the CAF file, see errno. */ #define CAF_ERR_FILEBUSY 5 /* file locked by someone else. */ #define CAF_ERR_ARTWONTFIT 6 /* outside the range in the TOC */ #define CAF_ERR_ARTALREADYHERE 7 /* tried to create an article that was already here. */ #define CAF_ERR_BOGUSPATH 8 /* pathname not parseable. */ inn-2.6.0/storage/timecaf/README.CAF0000644000175200017520000001737412575023702016260 0ustar iuliusiuliusThe timecaf storage manager is like the timehash storage manager, except that it stores multiple articles in one file. The file format is called CAF (for "crunched article file", putting multiple articles together into one big file), and uses a library 'caf.c' dating back from the pre-storage manager days when I made a locally-hacked version of INN1.5 that used this code in order to boost performance on my system. Originally I had planned to do one big file per newsgroup, but it turns out that a time-based file layout rather than newsgroup-name-based is a. more efficient and b. much easier to fit into the current storage manager interface paradigm. Anyway, the pathnames for the files are of the form /timecaf-nn/bb/aacc.CF where 'nn' is the numeric storage class (same as in 'timehash') and the file contains all articles written during the interval from (time_t) 0xaabbcc00 to 0xaabbccFF. The way expiration works on the 'timecaf' storage manager is a bit complicated. When articles are expired or cancelled (via SMcancel()) they are at first just marked as expired in the .CF file -- no actual disk space is freed at first. But if fastrm/SMcancel() notices that a certain amount of space has been marked as free, then it will do a sort of garbage collection pass on the file, writing out a new file containing only the articles from the old file that have not yet expired and removing the old file. If fastrm notices that *all* the articles in a file have been expired, it just deletes the file and doesn't create a new one. This means that if your setup has newsgroups with differing expiration lengths put in the same timecaf storage class, everything will work ok but your expire runs will spend some extra time copying files about. In my experience this hasn't been too much of a problem. If you find that it is a problem, you may wish to consider dividing up your spool layout so each storage class gets newsgroups that expire at more-or-less the same time, or putting *.binaries in their own storage class. Some advantages and disadvantages compared to the 'timehash' and 'CNFS' storage methods: timecaf is somewhat faster than timehash in writing articles (the tests I did on the old news.ecn.uoknor.edu showed a roughly 4x improvement in artwrite speed). This is presumably due to improved locality of reference and not having to open/close article files all the time but only every 4 minutes or so. Artcancel speed, on the other hand, is not much different, because cancel requests have terrible locality of reference. Expire times seem to be generally somewhat faster than timehash as well, even given the extra copying overhead mentioned above. Timecaf is probably slower than CNFS, but I haven't had a chance to do any comparison tests. Timecaf does share the feature with timehash that you can get much more fine-tuned control of your expire times (on a group-by-group basis, if needed) than you can with CNFS. Down below is an old README telling more about the implementation details of the CAF file format. Most people won't care about this, but if you're curious, read on; it also tells some of the historical developments that went on in this code. I've been running some version of this code off and on for the past two years, and have been running it as a storage manager module for the past few months, so I'm pretty sure of its stability. Richard Todd (rmtodd@mailhost.ecn.ou.edu/rmtodd@servalan.servalan.com) Implementation details (format of a CAF file) and some design rationale: Look at caf.h for the details, but basically, the layout is something like this. Each CAF file has a blocksize associated with it (usually 512 bytes, but it can vary). The layout of a CAF file is as follows: 1. Header (~300 bytes) containing information like low and high article numbers, amount of free space, blocksize. 2. Free space bitmap (size given by the FreeZoneTabSize field of the header). Its index is ~212 bytes (in fact, it is exactly between the end of the header and the end of the block). Its main part depends on the length of the index (n bits for the index => n bytes for the main free space bitmap, therefore ~1696 blocks, thus ~868KB). Consequently, the maximal length of a CAF file is ~3.5GB. 3. CAFTOCENTs (CAF Table of Contents Entries), 1 per article storable in the file. Each CAFTOCENT gives the article's size, creation time, and offset in the CAF file. Usually space is alloted in the CAF file for 262144 CAFTOCENTs, even if the # of articles in the CAF file is nowhere near that amount. The unused CAFTOCENTs are all zeros, and this means CAF files are almost always sparse. 4. Articles, always stored starting at blocksize boundaries. When fastrm is told to remove an article, the article is not actually removed as such, it is merely marked as non-existent (the CAFTOCENT is zeroed), and the blocks taken up by the article are marked as 'free' in the free space bitmap. When innd writes an article to a CAF file, it first looks to see if the CAF file has any free blocks in a contiguous chunk large enough to handle the article, and if so writes the article into those blocks and marks those blocks as being in use. If there is no suitable free space chunk in the CAF file, then innd merely appends the article to the end of the CAF file and records the article's position in the TOC. [Given the way the CAF code is currently used by the timecaf storage manager, it's almost always the case that we're appending to the end of the file.] A note on the free bitmap portion of the CAF file: it's not just a simple bitmap (each bit of the bitmap tells whether a data block is in use or free). First there is an 'index' bitmap which tells which blocks of the 'main' bitmap have free blocks listed in them, and then a 'main' bitmap which tells whether the data blocks are in use or free. This setup means that we can have bitmaps for CAF files as large as 8GB (in fact, it is now about 3.5GB, see computation above -- note by Julien Elie), while still being able to find free space by only reading the 'index' bitmap and one block of the 'main' bitmap. (Previous versions of the CAF code had just a 'main' bitmap and scaled the blocksize up when CAF files got large; this became rather, um, non-optimal when control.cancel started to hit hundreds of thousands of articles and 8K blocksizes.) In practice, CAF files over 2GB or 4GB may be a problem because of unsigned/signed long problems, and ones over 4GB are probably impossible on anything besides an Alpha unless you track down all the places in innd where they assume off_t is a long and fix it to work with long longs. At some point I'd also like to try some other, more efficient directory layout for the CAF files, as opposed to the old /var/spool/news/newsgroup/name/component/ scheme. At the time I started implementing this, it seemed like it'd be too much of a hassle to change this in INN as it stands. I'm hoping that changing this assumption (that newsgroup a.b.c is always in directory a/b/c) will be easier once INN acquires a nice interface for specifying alternate storage managers. [It is and it isn't; as I said, we've currently abandoned all relationship between newsgroup names and CAF file names, which provided a sizable jump in performance. Before that, I had changed the code so that the CAF file for, e.g., alt.tv.babylon-5 will now be /var/spool/news/alt/tv/babylon-5.CF -- note the final . instead of a /. This pretty much bypasses the need for the 'terminal' layer of directories to be read, and means that these directory blocks will not be fighting with other blocks for the limited space available in the buffer cache. This provides more of an improvement than you might think; throuput on news.ecn.uoknor.edu went from 160,000 articles/day to >200,000 articles/day with this patch, and this is on an aging 32M 486/66.] inn-2.6.0/storage/timecaf/timecaf.c0000644000175200017520000006155612575023702016565 0ustar iuliusiulius/* $Id: timecaf.c 9870 2015-05-17 17:23:07Z iulius $ ** ** Like the timehash storage method (and heavily inspired by it), but uses ** the CAF library to store multiple articles in a single file. */ #include "config.h" #include "clibrary.h" #include "portable/mmap.h" #include #include #include #include #include #include #include "caf.h" #include "inn/fdflag.h" #include "inn/innconf.h" #include "inn/messages.h" #include "inn/wire.h" #include "inn/libinn.h" #include "methods.h" #include "timecaf.h" #include "inn/paths.h" /* Needed for htonl() and friends on AIX 4.1. */ #include typedef struct { char *artdata; /* start of the article data -- may be mmaped */ char *mmapbase; /* actual start of mmaped region (on pagesize bndry, not necessarily == artdaya */ unsigned int artlen; /* art length. */ size_t mmaplen; /* length of mmap region. */ DIR *top; /* open handle on top level dir. */ DIR *sec; /* open handle on the 2nd level directory */ DIR *ter; /* open handle on 3rd level dir. */ struct dirent *topde; /* last entry we got from top */ struct dirent *secde; /* last entry we got from sec */ struct dirent *terde; /* last entry we got from sec */ CAFTOCENT *curtoc; ARTNUM curartnum; CAFHEADER curheader; } PRIV_TIMECAF; /* current path/fd for an open CAF file */ typedef struct { char *path; /* path to file. */ int fd; /* open fd -- -1 if no file currently open. */ } CAFOPENFILE; static CAFOPENFILE ReadingFile, WritingFile; static char *DeletePath; static ARTNUM *DeleteArtnums; static unsigned int NumDeleteArtnums, MaxDeleteArtnums; typedef enum {FIND_DIR, FIND_CAF, FIND_TOPDIR} FINDTYPE; /* ** Structures for the cache for stat information (to make expireover etc.) ** faster. ** ** The first structure contains the TOC info for a single CAF file. The 2nd ** one has pointers to the info for up to 256 CAF files, indexed ** by the 2nd least significant byte of the arrival time. */ struct caftoccacheent { CAFTOCENT *toc; CAFHEADER header; }; typedef struct caftoccacheent CAFTOCCACHEENT; struct caftocl1cache { CAFTOCCACHEENT *entries[256]; }; typedef struct caftocl1cache CAFTOCL1CACHE; /* ** and similar structures indexed by the 3rd and 4th bytes of the arrival time. ** pointing to the lower level structures. Note that the top level structure ** (the one indexed by the MSByte of the timestamp) is likely to have only ** one active pointer, unless your spool keeps more than 194 days of articles, ** but it doesn't cost much to keep that one structure around and keep the ** code general. */ struct caftocl2cache { CAFTOCL1CACHE *l1ptr[256]; }; typedef struct caftocl2cache CAFTOCL2CACHE; struct caftocl3cache { CAFTOCL2CACHE *l2ptr[256]; }; typedef struct caftocl3cache CAFTOCL3CACHE; static CAFTOCL3CACHE *TOCCache[256]; /* indexed by storage class! */ static int TOCCacheHits, TOCCacheMisses; /* ** The token is @04nn00aabbccyyyyxxxx0000000000000000@ ** where "04" is the timecaf method number, ** "nn" the hexadecimal value of the storage class, ** "aabbccdd" the arrival time in hexadecimal (dd is unused), ** "xxxxyyyy" the hexadecimal sequence number seqnum. ** ** innconf->patharticles + '/timecaf-nn/bb/aacc.CF' ** where "nn" is the hexadecimal value of the storage class, ** "aabbccdd" the arrival time in hexadecimal (dd is unused). */ char * timecaf_explaintoken(const TOKEN token) { char *text; uint32_t arrival; uint16_t seqnum1; uint16_t seqnum2; memcpy(&arrival, &token.token[0], sizeof(arrival)); memcpy(&seqnum1, &token.token[4], sizeof(seqnum1)); memcpy(&seqnum2, &token.token[6], sizeof(seqnum2)); xasprintf(&text, "method=timecaf class=%u time=%lu seqnum=%lu file=%s/timecaf-%02x/%02x/%02x%02x.CF", (unsigned int) token.class, ((unsigned long) (ntohl(arrival))) << 8, ((unsigned long) ntohs(seqnum1)) + (((unsigned long) ntohs(seqnum2)) << 16), innconf->patharticles, token.class, (ntohl(arrival) >> 8) & 0xff, (ntohl(arrival) >> 16) & 0xff, ntohl(arrival) & 0xff); return text; } static TOKEN MakeToken(time_t now, ARTNUM seqnum, STORAGECLASS class, TOKEN *oldtoken) { TOKEN token; uint32_t i; uint16_t s; if (oldtoken == (TOKEN *)NULL) memset(&token, '\0', sizeof(token)); else memcpy(&token, oldtoken, sizeof(token)); token.type = TOKEN_TIMECAF; token.class = class; i = htonl(now); memcpy(token.token, &i, sizeof(i)); s = htons(seqnum & 0xffff); memcpy(&token.token[sizeof(i)], &s, sizeof(s)); s = htons((seqnum >> 16) & 0xffff); memcpy(&token.token[sizeof(i)+sizeof(s)], &s, sizeof(s)); return token; } static void BreakToken(TOKEN token, time_t *now, ARTNUM *seqnum) { uint32_t i; uint16_t s1 = 0; uint16_t s2 = 0; memcpy(&i, token.token, sizeof(i)); memcpy(&s1, &token.token[sizeof(i)], sizeof(s1)); memcpy(&s2, &token.token[sizeof(i)+sizeof(s1)], sizeof(s2)); *now = ntohl(i); *seqnum = (ARTNUM)((ntohs(s2) << 16) + ntohs(s1)); } /* ** Note: the time here is really "time>>8", i.e. a timestamp that's been ** shifted right by 8 bits. */ static char *MakePath(time_t now, const STORAGECLASS class) { char *path; size_t length; length = strlen(innconf->patharticles) + 32; path = xmalloc(length); snprintf(path, length, "%s/timecaf-%02x/%02x/%02x%02x.CF", innconf->patharticles, class, (unsigned int)((now >> 8) & 0xff), (unsigned int)((now >> 16) & 0xff), (unsigned int)(now & 0xff)); return path; } static TOKEN *PathNumToToken(char *path, ARTNUM artnum) { int n; unsigned int tclass, t1, t2; STORAGECLASS class; time_t timestamp; static TOKEN token; n = sscanf(path, "timecaf-%02x/%02x/%04x.CF", &tclass, &t1, &t2); if (n != 3) return (TOKEN *)NULL; timestamp = ((t1 << 8) & 0xff00) | ((t2 << 8) & 0xff0000) | ((t2 << 0) & 0xff); class = tclass; token = MakeToken(timestamp, artnum, class, (TOKEN *)NULL); return &token; } bool timecaf_init(SMATTRIBUTE *attr) { if (attr == NULL) { warn("timecaf: attr is NULL"); SMseterror(SMERR_INTERNAL, "attr is NULL"); return false; } attr->selfexpire = false; attr->expensivestat = false; if (STORAGE_TOKEN_LENGTH < 6) { warn("timecaf: token length is less than six bytes"); SMseterror(SMERR_TOKENSHORT, NULL); return false; } ReadingFile.fd = WritingFile.fd = -1; ReadingFile.path = WritingFile.path = (char *)NULL; return true; } /* ** Routines for managing the 'TOC cache' (cache of TOCs of various CAF files) ** ** Attempt to look up a given TOC entry in the cache. Takes the timestamp ** as arguments. */ static CAFTOCCACHEENT * CheckTOCCache(time_t timestamp, STORAGECLASS tokenclass) { CAFTOCL2CACHE *l2; CAFTOCL1CACHE *l1; CAFTOCCACHEENT *cent; unsigned char tmp; if (TOCCache[tokenclass] == NULL) return NULL; /* cache is empty */ tmp = (timestamp>>16) & 0xff; l2 = TOCCache[tokenclass]->l2ptr[tmp]; if (l2 == NULL) return NULL; tmp = (timestamp>>8) & 0xff; l1 = l2->l1ptr[tmp]; if (l1 == NULL) return NULL; tmp = (timestamp) & 0xff; cent = l1->entries[tmp]; ++TOCCacheHits; return cent; } /* ** Add given TOC and header to the cache. Assume entry is not already in ** cache. */ static CAFTOCCACHEENT * AddTOCCache(time_t timestamp, CAFTOCENT *toc, CAFHEADER head, STORAGECLASS tokenclass) { CAFTOCL2CACHE *l2; CAFTOCL1CACHE *l1; CAFTOCCACHEENT *cent; unsigned char tmp; int i; if (TOCCache[tokenclass] == NULL) { TOCCache[tokenclass] = xmalloc(sizeof(CAFTOCL3CACHE)); for (i = 0 ; i < 256 ; ++i) TOCCache[tokenclass]->l2ptr[i] = NULL; } tmp = (timestamp>>16) & 0xff; l2 = TOCCache[tokenclass]->l2ptr[tmp]; if (l2 == NULL) { TOCCache[tokenclass]->l2ptr[tmp] = l2 = xmalloc(sizeof(CAFTOCL2CACHE)); for (i = 0 ; i < 256 ; ++i) l2->l1ptr[i] = NULL; } tmp = (timestamp>>8) & 0xff; l1 = l2->l1ptr[tmp]; if (l1 == NULL) { l2->l1ptr[tmp] = l1 = xmalloc(sizeof(CAFTOCL1CACHE)); for (i = 0 ; i < 256 ; ++i) l1->entries[i] = NULL; } tmp = (timestamp) & 0xff; cent = xmalloc(sizeof(CAFTOCCACHEENT)); l1->entries[tmp] = cent; cent->header = head; cent->toc = toc; ++TOCCacheMisses; return cent; } /* ** Do stating of an article, going thru the TOC cache if possible. */ static ARTHANDLE * StatArticle(time_t timestamp, ARTNUM artnum, STORAGECLASS tokenclass) { CAFTOCCACHEENT *cent; CAFTOCENT *toc; CAFHEADER head; char *path; CAFTOCENT *tocentry; ARTHANDLE *art; cent = CheckTOCCache(timestamp,tokenclass); if (cent == NULL) { path = MakePath(timestamp, tokenclass); toc = CAFReadTOC(path, &head); if (toc == NULL) { if (caf_error == CAF_ERR_ARTNOTHERE) { SMseterror(SMERR_NOENT, NULL); } else { SMseterror(SMERR_UNDEFINED, NULL); } free(path); return NULL; } cent = AddTOCCache(timestamp, toc, head, tokenclass); free(path); } /* check current TOC for the given artnum. */ if (artnum < cent->header.Low || artnum > cent->header.High) { SMseterror(SMERR_NOENT, NULL); return NULL; } tocentry = &(cent->toc[artnum - cent->header.Low]); if (tocentry->Size == 0) { /* no article with that article number present */ SMseterror(SMERR_NOENT, NULL); return NULL; } /* stat is a success, so build a null art struct to represent that. */ art = xmalloc(sizeof(ARTHANDLE)); art->type = TOKEN_TIMECAF; art->data = NULL; art->len = 0; art->private = NULL; return art; } static void CloseOpenFile(CAFOPENFILE *foo) { if (foo->fd >= 0) { close(foo->fd); foo->fd = -1; free(foo->path); foo->path = NULL; } } TOKEN timecaf_store(const ARTHANDLE article, const STORAGECLASS class) { char *path; char *p; time_t now; time_t timestamp; TOKEN token; int fd; ssize_t result; ARTNUM art; if (article.arrived == (time_t)0) now = time(NULL); else now = article.arrived; timestamp = now>>8; art = 0; /* magic: 0=="next available article number. */ memset(&token, 0, sizeof(token)); path = MakePath(timestamp, class); /* check to see if we have this CAF file already open. */ if (WritingFile.fd < 0 || strcmp(WritingFile.path, path) != 0) { /* we're writing to a different file, close old one and start new one. */ CloseOpenFile(&WritingFile); fd = CAFOpenArtWrite(path, &art, true, article.len); if (fd < 0) { if (caf_error == CAF_ERR_IO && caf_errno == ENOENT) { /* directories in the path don't exist, try creating them. */ p = strrchr(path, '/'); *p = '\0'; if (!MakeDirectory(path, true)) { syswarn("timecaf: could not create directory %s", path); token.type = TOKEN_EMPTY; free(path); SMseterror(SMERR_UNDEFINED, NULL); token.type = TOKEN_EMPTY; return token; } else { *p = '/'; fd = CAFOpenArtWrite(path, &art, true, article.len); if (fd < 0) { warn("timecaf: could not OpenArtWrite %s/%ld: %s", path, art, CAFErrorStr()); SMseterror(SMERR_UNDEFINED, NULL); free(path); token.type = TOKEN_EMPTY; return token; } } } else { warn("timecaf: could not OpenArtWrite %s/%ld: %s", path, art, CAFErrorStr()); SMseterror(SMERR_UNDEFINED, NULL); free(path); token.type = TOKEN_EMPTY; return token; } } } else { /* can reuse existing fd, assuming all goes well. */ fd = WritingFile.fd; /* nuke extraneous copy of path to avoid mem leaks. */ free(path); path = WritingFile.path; if (CAFStartWriteFd(fd, &art, article.len) < 0) { warn("timecaf: could not OpenArtWriteFd %s/%ld: %s", path, art, CAFErrorStr()); SMseterror(SMERR_UNDEFINED, NULL); free(path); token.type = TOKEN_EMPTY; return token; } } WritingFile.fd = fd; WritingFile.path = path; fdflag_close_exec(fd, true); result = xwritev(fd, article.iov, article.iovcnt); if (result != (ssize_t) article.len) { SMseterror(SMERR_UNDEFINED, NULL); syswarn("timecaf: error writing %s", path); token.type = TOKEN_EMPTY; CloseOpenFile(&WritingFile); return token; } if (CAFFinishArtWrite(fd) < 0) { SMseterror(SMERR_UNDEFINED, NULL); warn("timecaf: error writing %s: %s", path, CAFErrorStr()); token.type = TOKEN_EMPTY; CloseOpenFile(&WritingFile); return token; } return MakeToken(timestamp, art, class, article.token); } /* Get a handle to article artnum in CAF-file path. */ static ARTHANDLE *OpenArticle(const char *path, ARTNUM artnum, const RETRTYPE amount) { int fd; PRIV_TIMECAF *private; char *p; size_t len; ARTHANDLE *art; static long pagesize = 0; if (pagesize == 0) { pagesize = getpagesize(); if (pagesize < 0) { syswarn("timecaf: getpagesize failed"); pagesize = 0; return NULL; } } /* XXX need to figure some way to cache open fds or something? */ if ((fd = CAFOpenArtRead(path, artnum, &len)) < 0) { if (caf_error == CAF_ERR_ARTNOTHERE) { SMseterror(SMERR_NOENT, NULL); } else { SMseterror(SMERR_UNDEFINED, NULL); } return NULL; } art = xmalloc(sizeof(ARTHANDLE)); art->type = TOKEN_TIMECAF; if (amount == RETR_STAT) { art->data = NULL; art->len = 0; art->private = NULL; close(fd); return art; } private = xmalloc(sizeof(PRIV_TIMECAF)); art->private = (void *)private; private->artlen = len; if (innconf->articlemmap) { off_t curoff, tmpoff; size_t delta; curoff = lseek(fd, (off_t) 0, SEEK_CUR); delta = curoff % pagesize; tmpoff = curoff - delta; private->mmaplen = len + delta; if ((private->mmapbase = mmap(NULL, private->mmaplen, PROT_READ, MAP_SHARED, fd, tmpoff)) == MAP_FAILED) { SMseterror(SMERR_UNDEFINED, NULL); syswarn("timecaf: could not mmap article"); free(art->private); free(art); return NULL; } mmap_invalidate(private->mmapbase, private->mmaplen); if (amount == RETR_ALL) madvise(private->mmapbase, private->mmaplen, MADV_WILLNEED); else madvise(private->mmapbase, private->mmaplen, MADV_SEQUENTIAL); private->artdata = private->mmapbase + delta; } else { private->artdata = xmalloc(private->artlen); if (read(fd, private->artdata, private->artlen) < 0) { SMseterror(SMERR_UNDEFINED, NULL); syswarn("timecaf: could not read article"); free(private->artdata); free(art->private); free(art); return NULL; } } close(fd); private->top = NULL; private->sec = NULL; private->ter = NULL; private->curtoc = NULL; private->curartnum = 0; private->topde = NULL; private->secde = NULL; private->terde = NULL; if (amount == RETR_ALL) { art->data = private->artdata; art->len = private->artlen; return art; } if ((p = wire_findbody(private->artdata, private->artlen)) == NULL) { SMseterror(SMERR_NOBODY, NULL); if (innconf->articlemmap) munmap(private->mmapbase, private->mmaplen); else free(private->artdata); free(art->private); free(art); return NULL; } if (amount == RETR_HEAD) { art->data = private->artdata; art->len = p - private->artdata; /* Headers end just before the first empty line (\r\n). */ art->len = art->len - 2; return art; } if (amount == RETR_BODY) { art->data = p + 4; art->len = art->len - (private->artdata - p - 4); return art; } SMseterror(SMERR_UNDEFINED, "Invalid retrieve request"); if (innconf->articlemmap) munmap(private->mmapbase, private->mmaplen); else free(private->artdata); free(art->private); free(art); return NULL; } ARTHANDLE *timecaf_retrieve(const TOKEN token, const RETRTYPE amount) { time_t timestamp; ARTNUM artnum; char *path; ARTHANDLE *art; static TOKEN ret_token; time_t now; if (token.type != TOKEN_TIMECAF) { SMseterror(SMERR_INTERNAL, NULL); return NULL; } BreakToken(token, ×tamp, &artnum); /* ** Do a possible shortcut on RETR_STAT requests, going through the "TOC cache" ** we mentioned above. We only try to go through the TOC Cache under these ** conditions: ** 1) SMpreopen is true (so we're "preopening" the TOCs). ** 2) the timestamp is older than the timestamp corresponding to current ** time. Any timestamp that matches current time (to within 256 seconds) ** would be in a CAF file that innd is actively ** writing, in which case we would not want to cache the TOC for that ** CAF file. */ if (SMpreopen && amount == RETR_STAT) { now = time(NULL); if (timestamp < ((now >> 8) & 0xffffff)) { return StatArticle(timestamp, artnum, token.class); } } path = MakePath(timestamp, token.class); if ((art = OpenArticle(path, artnum, amount)) != (ARTHANDLE *)NULL) { art->arrived = timestamp<<8; /* XXX not quite accurate arrival time, ** but getting a more accurate one would ** require more fiddling with CAF innards. */ ret_token = token; art->token = &ret_token; } free(path); return art; } void timecaf_freearticle(ARTHANDLE *article) { PRIV_TIMECAF *private; if (!article) return; if (article->private) { private = (PRIV_TIMECAF *)article->private; if (innconf->articlemmap) munmap(private->mmapbase, private->mmaplen); else free(private->artdata); if (private->top) closedir(private->top); if (private->sec) closedir(private->sec); if (private->ter) closedir(private->ter); if (private->curtoc) free(private->curtoc); free(private); } free(article); } /* Do cancels of all the article ids collected for a given pathname. */ static void DoCancels(void) { if (DeletePath != NULL) { if (NumDeleteArtnums != 0) { /* ** Murgle. If we are trying to cancel something out of the ** currently open-for-writing file, we need to close it before ** doing CAFRemove... */ if (WritingFile.path != NULL && strcmp(WritingFile.path, DeletePath) == 0) { CloseOpenFile(&WritingFile); } /* XXX should really check err. code here, but not much we can really do. */ CAFRemoveMultArts(DeletePath, NumDeleteArtnums, DeleteArtnums); free(DeleteArtnums); DeleteArtnums = NULL; NumDeleteArtnums = MaxDeleteArtnums = 0; } free(DeletePath); DeletePath = NULL; } } bool timecaf_cancel(TOKEN token) { time_t now; ARTNUM seqnum; char *path; BreakToken(token, &now, &seqnum); path = MakePath(now, token.class); if (DeletePath == NULL) { DeletePath = path; } else if (strcmp(DeletePath, path) != 0) { /* different path, so flush all pending cancels. */ DoCancels(); DeletePath = path; } else { free(path); /* free redundant copy of path */ } if (NumDeleteArtnums >= MaxDeleteArtnums) { /* allocate/expand storage for artnums. */ if (MaxDeleteArtnums == 0) { MaxDeleteArtnums = 100; } else { MaxDeleteArtnums *= 2; } DeleteArtnums = xrealloc(DeleteArtnums, MaxDeleteArtnums * sizeof(ARTNUM)); } DeleteArtnums[NumDeleteArtnums++] = seqnum; return true; } static struct dirent *FindDir(DIR *dir, FINDTYPE type) { struct dirent *de; while ((de = readdir(dir)) != NULL) { if (type == FIND_TOPDIR) if ((strlen(de->d_name) == 10) && (strncmp(de->d_name, "timecaf-", 8) == 0) && isxdigit((unsigned char) de->d_name[8]) && isxdigit((unsigned char) de->d_name[9])) return de; if (type == FIND_DIR) if ((strlen(de->d_name) == 2) && isxdigit((unsigned char) de->d_name[0]) && isxdigit((unsigned char) de->d_name[1])) return de; if (type == FIND_CAF) if ((strlen(de->d_name) == 7) && isxdigit((unsigned char) de->d_name[0]) && isxdigit((unsigned char) de->d_name[1]) && isxdigit((unsigned char) de->d_name[2]) && isxdigit((unsigned char) de->d_name[3]) && (de->d_name[4] == '.') && (de->d_name[5] == 'C') && (de->d_name[6] == 'F')) return de; } return NULL; } /* Grovel thru a CAF table-of-contents finding the next still-existing article */ static int FindNextArt(const CAFHEADER *head, CAFTOCENT *toc, ARTNUM *artp) { ARTNUM art; CAFTOCENT *tocp; art = *artp; if (art == 0) { art = head->Low - 1; /* we never use art # 0, so 0 is a flag to start searching at the beginning */ } while (true) { art++; if (art > head->High) return false; /* ran off the end of the TOC */ tocp = &toc[art - head->Low]; if (tocp->Size != 0) { /* got a valid article */ *artp = art; return true; } } } ARTHANDLE * timecaf_next(ARTHANDLE *article, const RETRTYPE amount) { PRIV_TIMECAF priv, *newpriv; char *path; ARTHANDLE *art; size_t length; length = strlen(innconf->patharticles) + 32; path = xmalloc(length); if (article == NULL) { priv.top = NULL; priv.sec = NULL; priv.ter = NULL; priv.curtoc = NULL; priv.topde = NULL; priv.secde = NULL; priv.terde = NULL; } else { priv = *(PRIV_TIMECAF *)article->private; free(article->private); free(article); if (innconf->articlemmap) munmap(priv.mmapbase, priv.mmaplen); else free(priv.artdata); } while (priv.curtoc == NULL || !FindNextArt(&priv.curheader, priv.curtoc, &priv.curartnum)) { if (priv.curtoc) { free(priv.curtoc); priv.curtoc = NULL; } while (!priv.ter || ((priv.terde = FindDir(priv.ter, FIND_CAF)) == NULL)) { if (priv.ter) { closedir(priv.ter); priv.ter = NULL; } while (!priv.sec || ((priv.secde = FindDir(priv.sec, FIND_DIR)) == NULL)) { if (priv.sec) { closedir(priv.sec); priv.sec = NULL; } if (!priv.top || ((priv.topde = FindDir(priv.top, FIND_TOPDIR)) == NULL)) { if (priv.top) { /* end of search */ closedir(priv.top); priv.top = NULL; free(path); return NULL; } snprintf(path, length, "%s", innconf->patharticles); if ((priv.top = opendir(path)) == NULL) { SMseterror(SMERR_UNDEFINED, NULL); free(path); return NULL; } if ((priv.topde = FindDir(priv.top, FIND_TOPDIR)) == NULL) { SMseterror(SMERR_UNDEFINED, NULL); closedir(priv.top); free(path); return NULL; } } snprintf(path, length, "%s/%s", innconf->patharticles, priv.topde->d_name); if ((priv.sec = opendir(path)) == NULL) continue; } snprintf(path, length, "%s/%s/%s", innconf->patharticles, priv.topde->d_name, priv.secde->d_name); if ((priv.ter = opendir(path)) == NULL) continue; } snprintf(path, length, "%s/%s/%s/%s", innconf->patharticles, priv.topde->d_name, priv.secde->d_name, priv.terde->d_name); if ((priv.curtoc = CAFReadTOC(path, &priv.curheader)) == NULL) continue; priv.curartnum = 0; } snprintf(path, length, "%s/%s/%s/%s", innconf->patharticles, priv.topde->d_name, priv.secde->d_name, priv.terde->d_name); art = OpenArticle(path, priv.curartnum, amount); if (art == (ARTHANDLE *)NULL) { art = xmalloc(sizeof(ARTHANDLE)); art->type = TOKEN_TIMECAF; art->data = NULL; art->len = 0; art->private = xmalloc(sizeof(PRIV_TIMECAF)); } newpriv = (PRIV_TIMECAF *)art->private; newpriv->top = priv.top; newpriv->sec = priv.sec; newpriv->ter = priv.ter; newpriv->topde = priv.topde; newpriv->secde = priv.secde; newpriv->terde = priv.terde; newpriv->curheader = priv.curheader; newpriv->curtoc = priv.curtoc; newpriv->curartnum = priv.curartnum; snprintf(path, length, "%s/%s/%s", priv.topde->d_name, priv.secde->d_name, priv.terde->d_name); art->token = PathNumToToken(path, priv.curartnum); art->arrived = priv.curtoc[priv.curartnum - priv.curheader.Low].ModTime; free(path); return art; } bool timecaf_ctl(PROBETYPE type, TOKEN *token UNUSED, void *value) { struct artngnum *ann; switch (type) { case SMARTNGNUM: if ((ann = (struct artngnum *)value) == NULL) return false; /* make SMprobe() call timecaf_retrieve() */ ann->artnum = 0; return true; default: return false; } } bool timecaf_flushcacheddata(FLUSHTYPE type) { if (type == SM_ALL || type == SM_CANCELLEDART) DoCancels(); return true; } void timecaf_printfiles(FILE *file, TOKEN token, char **xref UNUSED, int ngroups UNUSED) { fprintf(file, "%s\n", TokenToText(token)); } void timecaf_shutdown(void) { CloseOpenFile(&WritingFile); DoCancels(); } inn-2.6.0/storage/timecaf/caf.c0000644000175200017520000013611712575023702015702 0ustar iuliusiulius/* $Id: caf.c 9492 2013-06-25 17:13:31Z iulius $ ** ** Library routines needed for handling CAF (Crunched Article Files) ** Written by Richard Todd (rmtodd@mailhost.ecn.uoknor.edu) 3/24/96, ** modified extensively since then. Altered to work with storage manager ** in INN1.8 by rmtodd 3/27/98. */ #include "config.h" #include "clibrary.h" #include #include #include #include #include "inn/messages.h" #include "inn/libinn.h" #define CAF_INNARDS #include "caf.h" /* following code lifted from inndf.c */ #ifdef HAVE_STATVFS #include /* specific includes */ /* XXX is there a 'fstatvfs'? I don't have such a system to check--rmtodd*/ #define STATFUNCT fstatvfs /* function call */ #define STATSTRUC statvfs /* structure name */ #define STATAVAIL f_bavail /* blocks available */ #define STATMULTI f_frsize /* fragment size/block size */ #define STATINODE f_favail /* inodes available */ #define STATTYPES u_long /* type of f_bavail etc */ #define STATFORMT "%lu" /* format string to match */ #define STATFORMTPAD "%*lu" /* format string to match */ #endif /* HAVE_STATVFS */ #ifdef HAVE_STATFS #ifdef HAVE_SYS_VFS_H #include #endif /* HAVE_SYS_VFS_H */ #ifdef HAVE_SYS_PARAM_H #include #endif /* HAVE_SYS_PARAM_H */ #ifdef HAVE_SYS_MOUNT_H #include #endif /* HAVE_SYS_MOUNT_H */ #define STATFUNCT fstatfs #define STATSTRUC statfs #define STATAVAIL f_bavail #define STATMULTI f_bsize #define STATINODE f_ffree; #define STATTYPES long #define STATFORMT "%ld" #define STATFORMTPAD "%*ld" #endif /* HAVE_STATFS */ int CAFClean(char *path, int verbose, double PercentFreeThreshold); int caf_error = 0; int caf_errno = 0; /* check assertions in code (lifted from lib/malloc.c) */ #define ASSERT(p) do { if (!(p)) botch(__FILE__, __LINE__, #p); } while (0) static void botch(const char *f, int l, const char *s) { fprintf(stderr, "assertion botched: %s:%d:%s\n", f,l,s); fflush(stderr); /* if stderr writing to file--needed? */ abort(); } /* set error code appropriately. */ static void CAFError(int code) { caf_error = code; if (caf_error == CAF_ERR_IO) { caf_errno = errno; } } /* ** Wrapper around read that calls CAFError if needed. 0 for success, -1 for failure. */ static int OurRead(int fd, void *buf, size_t n) { ssize_t rval; rval = read(fd, buf, n); if (rval < 0) { CAFError(CAF_ERR_IO); return -1; } if ((size_t) rval < n) { /* not enough data! */ CAFError(CAF_ERR_BADFILE); return -1; } return 0; } /* Same as OurRead except for writes. */ static int OurWrite(int fd, const void *buf, size_t n) { ssize_t rval; rval = write(fd, buf, n); if (rval < 0) { CAFError(CAF_ERR_IO); return -1; } if ((size_t) rval < n) { /* not enough data written */ CAFError(CAF_ERR_IO); return -1; } return 0; } /* ** Given an fd, read in a CAF_HEADER from a file. Ret. 0 on success. */ int CAFReadHeader(int fd, CAFHEADER *h) { /* probably already at start anyway, but paranoia is good. */ if (lseek(fd, 0L, SEEK_SET) < 0) { CAFError(CAF_ERR_IO); return -1; } if (OurRead(fd, h, sizeof(CAFHEADER)) < 0) return -1; if (strncmp(h->Magic, CAF_MAGIC, CAF_MAGIC_LEN) != 0) { CAFError(CAF_ERR_BADFILE); return -1; } return 0; } /* ** Seek to the TOC entry for a given article. As usual, -1 for error, 0 succ. */ static int CAFSeekTOCEnt(int fd, CAFHEADER *head, ARTNUM art) { off_t offset; offset = sizeof(CAFHEADER) + head->FreeZoneTabSize; offset += (art - head->Low) * sizeof(CAFTOCENT); if (lseek(fd, offset, SEEK_SET) < 0) { CAFError(CAF_ERR_IO); return -1; } return 0; } /* ** Fetch the TOC entry for a given article. As usual -1 for error, 0 success. */ static int CAFGetTOCEnt(int fd, CAFHEADER *head, ARTNUM art, CAFTOCENT *tocp) { if (CAFSeekTOCEnt(fd, head, art) < 0) { return -1; } if (OurRead(fd, tocp, sizeof(CAFTOCENT)) < 0) return -1; return 0; } /* ** Round an offset up to the next highest block boundary. Needs the CAFHEADER ** to find out what the blocksize is. */ off_t CAFRoundOffsetUp(off_t off, unsigned int blocksize) { off_t off2; /* Zero means default blocksize, though we shouldn't need this for long, as all new CAF files will have BlockSize set. */ if (blocksize == 0) { blocksize = CAF_DEFAULT_BLOCKSIZE; } off2 = ((off + blocksize - 1) / blocksize) * blocksize; return off2; } /* ** Dispose of an already-allocated CAFBITMAP. */ void CAFDisposeBitmap(CAFBITMAP *bm) { unsigned int i; CAFBMB *bmb; for (i = 0 ; i < bm->NumBMB ; ++i) { if (bm->Blocks[i]) { bmb = bm->Blocks[i]; if (bmb->BMBBits) free(bmb->BMBBits); free(bmb); } } free(bm->Blocks); free(bm->Bits); free(bm); } /* ** Read the index bitmap from a CAF file, return a CAFBITMAP structure. */ /* define this instead of littering all our formulas with semi-mysterious 8s. */ #define BYTEWIDTH 8 CAFBITMAP * CAFReadFreeBM(int fd, CAFHEADER *h) { size_t i; struct stat statbuf; CAFBITMAP *bm; if (lseek(fd, sizeof(CAFHEADER), SEEK_SET) < 0) { CAFError(CAF_ERR_IO); return NULL; } bm = xmalloc(sizeof(CAFBITMAP)); bm->FreeZoneTabSize = h->FreeZoneTabSize; bm->FreeZoneIndexSize = h->FreeZoneIndexSize; bm->NumBMB = BYTEWIDTH * bm->FreeZoneIndexSize; bm->BytesPerBMB = (h->BlockSize) * (h->BlockSize * BYTEWIDTH); bm->BlockSize = h->BlockSize; bm->Blocks = xmalloc(bm->NumBMB * sizeof(CAFBMB *)); bm->Bits = xmalloc(bm->FreeZoneIndexSize); for (i = 0 ; i < bm->NumBMB ; ++i) { bm->Blocks[i] = NULL; } if (OurRead(fd, bm->Bits, bm->FreeZoneIndexSize) < 0) { CAFDisposeBitmap(bm); return NULL; } bm->StartDataBlock = h->StartDataBlock; if (fstat(fd, &statbuf) < 0) { /* it'd odd for this to fail, but paranoia is good for the soul. */ CAFError(CAF_ERR_IO); CAFDisposeBitmap(bm); return NULL; } /* round st_size down to a mult. of BlockSize */ bm->MaxDataBlock = (statbuf.st_size / bm->BlockSize) * bm->BlockSize + bm->BlockSize; /* (note: MaxDataBlock points to the block *after* the last block of the file. */ return bm; } /* ** Fetch a given bitmap block into memory, and make the CAFBITMAP point to ** the new BMB appropriately. Return NULL on failure, and the BMB * on success. */ static CAFBMB * CAFFetchBMB(unsigned int blkno, int fd, CAFBITMAP *bm) { CAFBMB *newbmb; ASSERT(blkno < bm->NumBMB); /* if already in memory, don't need to do anything. */ if (bm->Blocks[blkno]) return bm->Blocks[blkno]; newbmb = xmalloc(sizeof(CAFBMB)); newbmb->Dirty = 0; newbmb->StartDataBlock = bm->StartDataBlock + blkno*(bm->BytesPerBMB); newbmb->MaxDataBlock = newbmb->StartDataBlock + bm->BytesPerBMB; if (newbmb->MaxDataBlock > bm->MaxDataBlock) { /* limit the per-BMB MaxDataBlock to that for the bitmap as a whole */ newbmb->MaxDataBlock = bm->MaxDataBlock; } newbmb->BMBBits = xmalloc(bm->BlockSize); if (lseek(fd, (blkno + 1) * bm->BlockSize, SEEK_SET) < 0) { free(newbmb->BMBBits); free(newbmb); CAFError(CAF_ERR_IO); return NULL; } if (OurRead(fd, newbmb->BMBBits, bm->BlockSize) < 0) { free(newbmb->BMBBits); free(newbmb); return NULL; } bm->Blocks[blkno] = newbmb; return newbmb; } /* ** Flush out (if needed) a BMB to disk. Return 0 on success, -1 on failure. */ static int CAFFlushBMB(unsigned int blkno, int fd, CAFBITMAP *bm) { CAFBMB *bmb; ASSERT(blkno < bm->NumBMB); if (bm->Blocks[blkno] == NULL) return 0; /* nothing to do. */ bmb = bm->Blocks[blkno]; if (!bmb->Dirty) return 0; if (lseek(fd, (blkno + 1) * bm->BlockSize, SEEK_SET) < 0) { CAFError(CAF_ERR_IO); return -1; } if (OurWrite(fd, bmb->BMBBits, bm->BlockSize) < 0) return -1; bmb->Dirty = 0; return 0; } /* ** Write the free bit map to the CAF file. Return 0 on success, -1 on failure. */ static int CAFWriteFreeBM(int fd, CAFBITMAP *bm) { size_t blkno; for (blkno = 0 ; blkno < bm->NumBMB ; ++blkno) { if (CAFFlushBMB(blkno, fd, bm) < 0) { return -1; } } if (lseek(fd, sizeof(CAFHEADER), SEEK_SET) < 0) { CAFError(CAF_ERR_IO); return -1; } if(OurWrite(fd, bm->Bits, bm->FreeZoneIndexSize) < 0) return -1; return 0; } /* ** Determine if a block at a given offset is free. Return 1 if it is, 0 ** otherwise. */ int CAFIsBlockFree(CAFBITMAP *bm, int fd, off_t block) { unsigned int ind; char mask; int blkno; CAFBMB *bmb; /* round block down to BlockSize boundary. */ block = block - (block % bm->BlockSize); /* if < Start, always return 0 (should never happen in real usage) */ if (block < bm->StartDataBlock) return 0; /* if off the end, also return 0. */ if (block >= bm->MaxDataBlock) return 0; /* find blk # of appropriate BMB */ blkno = (block - bm->StartDataBlock) / bm->BytesPerBMB; bmb = CAFFetchBMB(blkno, fd, bm); /* ick. not a lot we can do here if this fails. */ if (bmb == NULL) return 0; /* Sanity checking that we have the right BMB. */ ASSERT(block >= bmb->StartDataBlock); ASSERT(block < bmb->MaxDataBlock); ind = ((block - bmb->StartDataBlock) / bm->BlockSize) / BYTEWIDTH; mask = 1 << (((block - bmb->StartDataBlock) / bm->BlockSize) % BYTEWIDTH); ASSERT(ind < bm->BlockSize); return ((bmb->BMBBits[ind]) & mask) != 0; } /* ** Check if a bitmap chunk is all zeros or not. */ static int IsMapAllZero(char *data, int len) { int i; for (i = 0 ; i < len ; ++i) { if (data[i] != 0) return 0; } return 1; } /* Set the free bitmap entry for a given block to be a given value (1 or 0). */ static void CAFSetBlockFree(CAFBITMAP *bm, int fd, off_t block, int isfree) { unsigned int ind; char mask; int blkno; CAFBMB *bmb; int allzeros; /* round block down to BlockSize boundary. */ block = block - (block % bm->BlockSize); /* if < Start, always return (should never happen in real usage) */ if (block < bm->StartDataBlock) return; /* if off the end, also return. */ if (block >= bm->MaxDataBlock) return; /* find blk # of appropriate BMB */ blkno = (block - bm->StartDataBlock) / bm->BytesPerBMB; bmb = CAFFetchBMB(blkno, fd, bm); /* ick. not a lot we can do here if this fails. */ if (bmb == NULL) return; /* Sanity checking that we have the right BMB. */ ASSERT(block >= bmb->StartDataBlock); ASSERT(block < bmb->MaxDataBlock); ind = ((block - bmb->StartDataBlock) / bm->BlockSize) / BYTEWIDTH; mask = 1 << (((block - bmb->StartDataBlock) / bm->BlockSize) % BYTEWIDTH); ASSERT(ind < bm->BlockSize); if (isfree) { bmb->BMBBits[ind] |= mask; /* set bit */ } else { bmb->BMBBits[ind] &= ~mask; /* clear bit. */ } bmb->Dirty = 1; /* now have to set top level (index) bitmap appropriately */ allzeros = IsMapAllZero(bmb->BMBBits, bm->BlockSize); ind = blkno/BYTEWIDTH; mask = 1 << (blkno % BYTEWIDTH); if (allzeros) { bm->Bits[ind] &= ~mask; /* clear bit */ } else { bm->Bits[ind] |= mask; } return; } /* ** Search a freebitmap to find n contiguous free blocks. Returns 0 for ** failure, offset of starting block if successful. ** XXX does not attempt to find chunks that span BMB boundaries. This is ** messy to fix. ** (Actually I think this case works, as does the case when it tries to find ** a block bigger than BytesPerBMB. Testing reveals that it does seem to work, ** though not optimally (some BMBs will get scanned several times). */ static off_t CAFFindFreeBlocks(CAFBITMAP *bm, int fd, unsigned int n) { off_t startblk, curblk; unsigned int i, ind, blkno, j; unsigned int bmblkno, k, l; CAFBMB *bmb; /* Iterate over all bytes and all bits in the toplevel bitmap. */ for (k = 0 ; k < bm->FreeZoneIndexSize ; ++k) { if (bm->Bits[k] == 0) continue; for (l = 0; l < BYTEWIDTH ; ++l) { if ((bm->Bits[k] & (1 << l)) != 0) { /* found a bit set! fetch the BMB. */ bmblkno = k*BYTEWIDTH + l; bmb = CAFFetchBMB(bmblkno, fd, bm); if (bmb == NULL) return 0; curblk = bmb->StartDataBlock; while (curblk < bmb->MaxDataBlock) { blkno = (curblk - bmb->StartDataBlock)/(bm->BlockSize); ind = blkno/BYTEWIDTH; if (bmb->BMBBits[ind] == 0) { /* nothing set in this byte, skip this byte and move on. */ blkno = (ind+1)*BYTEWIDTH; curblk = blkno*bm->BlockSize + bmb->StartDataBlock; continue; } /* scan rest of current byte for 1 bits */ for (j = blkno % BYTEWIDTH ; j < BYTEWIDTH ; j++, curblk += bm->BlockSize) { if ((bmb->BMBBits[ind] & (1 << j)) != 0) break; } if (j == BYTEWIDTH) continue; /* found a 1 bit, set startblk to be locn of corresponding free blk. */ startblk = curblk; curblk += bm->BlockSize; /* scan for n blocks in a row. */ for (i = 1 ; i < n ; ++i, curblk += bm->BlockSize) { if (!CAFIsBlockFree(bm, fd, curblk)) break; } if (i == n) return startblk; /* otherwise curblk points to a non-free blk, continue searching from there. */ continue; } } } } return 0; } /* ** Open a CAF file for reading and seek to the start of a given article. ** Take as args the CAF file pathname, article #, and a pointer to where ** the art. length can be returned. */ int CAFOpenArtRead(const char *path, ARTNUM art, size_t *len) { CAFHEADER head; int fd; CAFTOCENT tocent; struct stat st; if ( (fd = open(path, O_RDONLY)) < 0) { /* ** if ENOENT (not there), just call this "article not found", ** otherwise it's a more serious error and stash the errno. */ if (errno == ENOENT) { CAFError(CAF_ERR_ARTNOTHERE); } else { CAFError(CAF_ERR_IO); } return -1; } /* Fetch the header */ if (CAFReadHeader(fd, &head) < 0) { close(fd); return -1; } /* Is the requested article even in the file? */ if (art < head.Low || art > head.High) { CAFError(CAF_ERR_ARTNOTHERE); close(fd); return -1; } if (CAFGetTOCEnt(fd, &head, art, &tocent) < 0) { close(fd); return -1; } if (tocent.Size == 0) { /* empty/otherwise not present article */ CAFError(CAF_ERR_ARTNOTHERE); close(fd); return -1; } if (lseek(fd, tocent.Offset, SEEK_SET) < 0) { CAFError(CAF_ERR_IO); close(fd); return -1; } /* I'm not sure if this fstat is worth the speed hit, but unless we check here, we may simply segfault when we try to access mmap'd space beyond the end of the file. I think robustness wins. */ if (fstat(fd, &st) == 0) if (tocent.Size + tocent.Offset > (size_t) st.st_size) { CAFError(CAF_ERR_IO); close(fd); return -1; } *len = tocent.Size; return fd; } /* ** variables for keeping track of currently pending write. ** FIXME: assumes only one article open for writing at a time. */ static int CAF_fd_write; static ARTNUM CAF_artnum_write; static off_t CAF_startoffset_write; static CAFHEADER CAF_header_write; static CAFBITMAP *CAF_free_bitmap_write; static unsigned int CAF_numblks_write; /* ** Given estimated size of CAF file (i.e., the size of the old CAF file found ** by cafclean), find an "optimal" blocksize (one big enough so that the ** default FreeZoneTabSize can cover the entire file in order not to "lose" ** free space and not be able to reuse it. ** (Currently only returns the first multiple of CAF_DEFAULT_BLOCKSIZE that ** allows to have at least CAF_MIN_FZSIZE bytes of index, as with the new 2-level ** bitmaps, the FreeZoneTabSize that results from a 512-byte blocksize can ** handle any file with <7.3G of data. Yow!) */ static unsigned int CAFFindOptimalBlocksize(ARTNUM tocsize UNUSED, size_t cfsize) { /* No size given, use default. */ if (cfsize == 0) { return (((sizeof(CAFHEADER) + CAF_MIN_FZSIZE)/CAF_DEFAULT_BLOCKSIZE + 1) *CAF_DEFAULT_BLOCKSIZE); } return (((sizeof(CAFHEADER) + CAF_MIN_FZSIZE)/CAF_DEFAULT_BLOCKSIZE + 1) *CAF_DEFAULT_BLOCKSIZE); } /* ** Create an empty CAF file. Used by CAFOpenArtWrite. ** Must be careful here and create the new CAF file under a temp name and then ** link it into place, to avoid possible race conditions. ** Note: CAFCreateCAFFile returns fd locked, also to avoid race conds. ** New args added for benefit of the cleaner program: "nolink", a flag that ** tells it not to bother with the link business, and "temppath", a pointer ** to a buffer that (if non-null) gets the pathname of the temp file copied ** to it. "estcfsize", if nonzero, is an estimate of what the CF filesize will ** be, used to automatically select a good blocksize. */ int CAFCreateCAFFile(char *cfpath, ARTNUM artnum, ARTNUM tocsize, size_t estcfsize, int nolink, char *temppath, size_t pathlen) { CAFHEADER head; int fd; char path[SPOOLNAMEBUFF]; char finalpath[SPOOLNAMEBUFF]; off_t offset; char nulls[1]; strlcpy(finalpath, cfpath, sizeof(finalpath)); /* create path with PID attached */ snprintf(path, sizeof(path), "%s.%lu", cfpath, (unsigned long) getpid()); /* ** Shouldn't be anyone else with our pid trying to write to the temp. ** file, but there might be an old one lying around. Nuke it. ** (yeah, I'm probably being overly paranoid.) */ if (unlink(path) < 0 && errno != ENOENT) { CAFError(CAF_ERR_IO); return -1; } if ((fd = open(path, O_CREAT|O_EXCL|O_RDWR, 0666)) < 0) { CAFError(CAF_ERR_IO); return -1; } /* Initialize the header. */ strncpy(head.Magic, CAF_MAGIC, CAF_MAGIC_LEN); head.Low = artnum; head.High = artnum; head.NumSlots = tocsize; head.Free = 0; head.BlockSize = CAFFindOptimalBlocksize(tocsize, estcfsize); head.FreeZoneIndexSize = head.BlockSize - sizeof(CAFHEADER); head.FreeZoneTabSize = head.FreeZoneIndexSize + head.BlockSize*head.FreeZoneIndexSize*BYTEWIDTH; head.StartDataBlock = CAFRoundOffsetUp(sizeof(CAFHEADER) + head.FreeZoneTabSize + tocsize*sizeof(CAFTOCENT), head.BlockSize); head.spare[0] = head.spare[1] = head.spare[2] = 0; if (OurWrite(fd, &head, sizeof(head)) < 0) { close(fd); return -1; } offset = sizeof(CAFHEADER) + head.FreeZoneTabSize + sizeof(CAFTOCENT) * tocsize; if (lseek(fd, offset, SEEK_SET) < 0) { CAFError(CAF_ERR_IO); return -1; } /* ** put a null after the TOC as a 'placeholder', so that we'll have a sparse ** file and that EOF will be at where the articles should start going. */ nulls[0] = 0; if (OurWrite(fd, nulls, 1) < 0) { close(fd); return -1; } /* shouldn't be anyone else locking our file, since temp file has unique PID-based name ... */ if (!inn_lock_file(fd, INN_LOCK_WRITE, false)) { CAFError(CAF_ERR_IO); close(fd); return -1; } if (nolink) { if (temppath != NULL) { strlcpy(temppath, path, pathlen); } return fd; } /* ** Try to link to the real one. NOTE: we may get EEXIST here, which we ** will handle specially in OpenArtWrite. */ if (link(path, finalpath) < 0) { CAFError(CAF_ERR_IO); /* bounced on the link attempt, go ahead and unlink the temp file and return. */ unlink(path); close(fd); return -1; } /* ** Unlink the temp. link. Do we really care if this fails? XXX ** Not sure what we can do anyway. */ unlink(path); return fd; } /* ** Try to open a CAF file for writing a given article. Return an fd to ** write to (already positioned to the right place to write at) if successful, ** else -1 on error. if LockFlag is true, we wait for a lock on the file, ** otherwise we fail if we can't lock it. If size is != 0, we try to allocate ** a chunk from free space in the CAF instead of writing at the end of the ** file. Artp is a pointer to the article number to use; if the article number ** is zero, the next free article # ("High"+1) will be used, and *artp will ** be set accordingly. Once the CAF file is open/created, CAFStartWriteFd() ** does the remaining dirty work. */ int CAFOpenArtWrite(char *path, ARTNUM *artp, int waitlock, size_t size) { int fd; while (true) { /* try to open the file and lock it. */ if ((fd = open(path, O_RDWR)) < 0) { /* if ENOENT, try creating CAF file, otherwise punt. */ if (errno != ENOENT) { CAFError(CAF_ERR_IO); return -1; } else { /* ** the *artp? business is so that if *artp==0, we set initial ** article # to 1. */ fd = CAFCreateCAFFile(path, (*artp ? *artp : 1), CAF_DEFAULT_TOC_SIZE, 0, 0, NULL, 0); /* ** XXX possible race condition here, so we check to see if ** create failed because of EEXIST. If so, we go back to top ** of loop, because someone else was trying to create at the ** same time. ** Is this the best way to solve this? ** (Hmm. this condition should be quite rare, occuring only ** when two different programs are simultaneously doing ** CAFOpenArtWrite()s, and no CF file exists previously.) */ if (fd < 0) { if (caf_errno == EEXIST) { /* ignore the error and try again */ continue; } return -1; /* other error, assume caf_errno set properly. */ } /* ** break here, because CreateCAFFile does ** lock fd, so we don't need to flock it ourselves. */ break; } } /* try a nonblocking lock attempt first. */ if (inn_lock_file(fd, INN_LOCK_WRITE, false)) break; if (!waitlock) { CAFError(CAF_ERR_FILEBUSY); close(fd); /* keep from leaking fds. */ return -1; } /* wait around to try and get a lock. */ inn_lock_file(fd, INN_LOCK_WRITE, true); /* ** and then close and reopen the file, in case someone changed the ** file out from under us. */ close(fd); } return CAFStartWriteFd(fd, artp, size); } /* ** Like CAFOpenArtWrite(), except we assume the CAF file is already ** open/locked, and we have an open fd to it. */ int CAFStartWriteFd(int fd, ARTNUM *artp, size_t size) { CAFHEADER head; CAFTOCENT tocent; off_t offset, startoffset; unsigned int numblks = 0; CAFBITMAP *freebm; ARTNUM art; /* fd is open to the CAF file, open for write and locked. */ /* Fetch the header */ if (CAFReadHeader(fd, &head) < 0) { close(fd); return -1; } /* check for zero article number and handle accordingly. */ art = *artp; if (art == 0) { /* assign next highest article number. */ art = head.High + 1; /* and pass to caller. */ *artp = art; } /* Is the requested article even in the file? */ if (art < head.Low || art >= head.Low + head.NumSlots) { CAFError(CAF_ERR_ARTWONTFIT); close(fd); return -1; } /* ** Get the CAFTOCENT for that article, but only if article# is in the range ** Low <= art# <= High. If art# > High, use a zero CAFTOCENT. This means ** that in cases where the CAF file is inconsistent due to a crash --- ** the CAFTOCENT shows an article as being existent, but the header ** doesn't show that article as being in the currently valid range --- ** the header value "wins" and we assume the article does not exist. ** This avoids problems with "half-existent" articles that showed up ** in the CAF TOC, but were never picked up by ctlinnd renumber ''. */ /* (Note: We already checked above that art >= head.Low.) */ if (art > head.High) { /* clear the tocent */ memset(&tocent, 0, sizeof(tocent)); } else { if (CAFGetTOCEnt(fd, &head, art, &tocent) < 0) { close(fd); return -1; } } if (tocent.Size != 0) { /* article is already here */ CAFError(CAF_ERR_ARTALREADYHERE); close(fd); return -1; } startoffset = 0; freebm = NULL; if (size != 0 && (freebm = CAFReadFreeBM(fd, &head)) != NULL) { numblks = (size + head.BlockSize - 1) / head.BlockSize; startoffset = CAFFindFreeBlocks(freebm, fd, numblks); if (startoffset == 0) { CAFDisposeBitmap(freebm); freebm = NULL; } } if (startoffset == 0) { /* ** No size given or free space not available, so ** seek to EOF to prepare to start writing article. */ if ((offset = lseek(fd, 0, SEEK_END)) < 0) { CAFError(CAF_ERR_IO); close(fd); return -1; } /* and round up offset to a block boundary. */ startoffset = CAFRoundOffsetUp(offset, head.BlockSize); } /* Seek to starting offset for the new artiicle. */ if (lseek(fd, startoffset, SEEK_SET) < 0) { CAFError(CAF_ERR_IO); close(fd); return -1; } /* stash data for FinishArtWrite's use. */ CAF_fd_write = fd; CAF_artnum_write = art; CAF_startoffset_write = startoffset; CAF_header_write = head; CAF_free_bitmap_write = freebm; CAF_numblks_write = numblks; return fd; } /* ** write out TOC entries for the previous article. Note that we do *not* ** (as was previously done) close the fd; this allows reuse of the fd to write ** another article to this CAF file w/o an (soemwhat expensive) open(). */ int CAFFinishArtWrite(int fd) { off_t curpos; CAFTOCENT tocentry; off_t curblk; CAFHEADER *headp; unsigned int i; /* blah, really should handle multiple pending OpenArtWrites. */ if (fd != CAF_fd_write) { warn("CAF: fd mismatch in FinishArtWrite"); abort(); } headp = &CAF_header_write; /* Find out where we left off writing in the file. */ if ((curpos = lseek(fd, 0, SEEK_CUR)) < 0) { CAFError(CAF_ERR_IO); CAF_fd_write = 0; return -1; } /* Write the new TOC entry. */ if (CAFSeekTOCEnt(fd, headp, CAF_artnum_write) < 0) { CAF_fd_write = 0; return -1; } tocentry.Offset = CAF_startoffset_write; tocentry.Size = curpos - CAF_startoffset_write; tocentry.ModTime = time((time_t *)NULL); if (OurWrite(fd, &tocentry, sizeof(CAFTOCENT)) < 0) { CAF_fd_write = 0; return -1; } /* if needed, update free bitmap. */ if (CAF_free_bitmap_write != NULL) { /* Paranoia: check to make sure we didn't write more than we said we would. */ if (tocentry.Size > CAF_numblks_write * headp->BlockSize) { /* ** for now core dump (might as well, if we've done this the CAF ** file is probably thoroughly hosed anyway.) */ warn("CAF: article written overran declared size"); abort(); } curblk = CAF_startoffset_write; for (i = 0 ; i < CAF_numblks_write ; ++i, curblk += headp->BlockSize) { CAFSetBlockFree(CAF_free_bitmap_write, fd, curblk, 0); } if (CAFWriteFreeBM(fd, CAF_free_bitmap_write) < 0){ CAFError(CAF_ERR_IO); CAF_fd_write = 0; return -1; } CAFDisposeBitmap(CAF_free_bitmap_write); /* and update the Free value in the header. */ headp->Free -= CAF_numblks_write * headp->BlockSize; } if (CAF_artnum_write > headp->High || CAF_free_bitmap_write) { /* need to update header. */ if (CAF_artnum_write > headp->High) { headp->High = CAF_artnum_write; } if (lseek(fd, 0, SEEK_SET) < 0) { CAFError(CAF_ERR_IO); CAF_fd_write = 0; return -1; } if (OurWrite(fd, headp, sizeof(CAFHEADER)) < 0) { CAF_fd_write = 0; return -1; } } #if 0 if (close(fd) < 0) { CAFError(CAF_ERR_IO); CAF_fd_write =0; return -1; } #endif CAF_fd_write = 0; return 0; } /* ** return a string containing a description of the error. ** Warning: uses a static buffer, or possibly a static string. */ static char errbuf[512]; const char * CAFErrorStr(void) { if (caf_error == CAF_ERR_IO || caf_error == CAF_ERR_CANTCREATECAF) { snprintf(errbuf, sizeof(errbuf), "%s errno=%s\n", (caf_error == CAF_ERR_IO) ? "CAF_ERR_IO" : "CAF_ERR_CANTCREATECAF", strerror(errno)); return errbuf; } else { switch(caf_error) { case CAF_ERR_BADFILE: return "CAF_ERR_BADFILE"; case CAF_ERR_ARTNOTHERE: return "CAF_ERR_ARTNOTHERE"; case CAF_ERR_FILEBUSY: return "CAF_ERR_FILEBUSY"; case CAF_ERR_ARTWONTFIT: return "CAF_ERR_ARTWONTFIT"; case CAF_ERR_ARTALREADYHERE: return "CAF_ERR_ARTALREADYHERE"; case CAF_ERR_BOGUSPATH: return "CAF_ERR_BOGUSPATH"; default: snprintf(errbuf, sizeof(errbuf), "CAF error %d", caf_error); return errbuf; } } } /* ** Open a CAF file, snarf the TOC entries for all the articles inside, ** and close the file. NOTE: returns the header for the CAF file in ** the storage pointed to by *ch. Dynamically allocates storage for ** the TOC entries, which should be freed by the caller when the ** caller's done with it. Return NULL on failure. ** ** This function calls CAFOpenReadTOC(dir, ch, &tocp), which does most ** (practically all) of the dirty work. CAFOpenReadTOC leaves the fd open ** (and returns it); this is needed by cafls. CAFReadTOC() closes the fd ** after CAFOpenReadTOC() is done with it. */ CAFTOCENT * CAFReadTOC(char *path, CAFHEADER *ch) { CAFTOCENT *tocp; int fd; if ((fd = CAFOpenReadTOC(path, ch, &tocp)) < 0) { return NULL; /* some sort of error happened */ } close(fd); return tocp; } int CAFOpenReadTOC(char *path, CAFHEADER *ch, CAFTOCENT **tocpp) { int fd; int nb; CAFTOCENT *tocp; off_t offset; if ( (fd = open(path, O_RDONLY)) < 0) { /* ** if ENOENT (not there), just call this "article not found", ** otherwise it's a more serious error and stash the errno. */ if (errno == ENOENT) { CAFError(CAF_ERR_ARTNOTHERE); } else { CAFError(CAF_ERR_IO); } return -1; } /* Fetch the header */ if (CAFReadHeader(fd, ch) < 0) { close(fd); return -1; } /* Allocate memory for TOC. */ tocp = xmalloc((ch->High - ch->Low + 1) * sizeof(CAFTOCENT)); nb = (sizeof(CAFTOCENT))*(ch->High - ch->Low + 1); /* # bytes to read for toc. */ /* seek to beginning of TOC */ offset = sizeof(CAFHEADER) + ch->FreeZoneTabSize; if (lseek(fd, offset, SEEK_SET) < 0) { CAFError(CAF_ERR_IO); return -1; } if (OurRead(fd, tocp, nb) < 0) { return -1; } /* read TOC successfully, return fd and stash tocp where we were told to */ *tocpp = tocp; return fd; } /* ** Cancel/expire articles from a CAF file. This involves zeroing the Size ** field of the TOC entry, and updating the Free field of the CAF header. ** note that no disk space is actually freed by this process; space will only ** be returned to the OS when the cleaner daemon runs on the CAF file. */ int CAFRemoveMultArts(char *path, unsigned int narts, ARTNUM *artnums) { int fd; CAFHEADER head; CAFTOCENT tocent; CAFBITMAP *freebitmap; ARTNUM art; unsigned int numblksfreed, i, j; off_t curblk; int errorfound = false; while (true) { /* try to open the file and lock it */ if ((fd = open(path, O_RDWR)) < 0) { /* if ENOENT, CAF file isn't there, so return ARTNOTHERE, otherwise it's an I/O error. */ if (errno != ENOENT) { CAFError(CAF_ERR_IO); return -1; } else { CAFError(CAF_ERR_ARTNOTHERE); return -1; } } /* try a nonblocking lock attempt first. */ if (inn_lock_file(fd, INN_LOCK_WRITE, false)) break; /* wait around to try and get a lock. */ inn_lock_file(fd, INN_LOCK_WRITE, true); /* ** and then close and reopen the file, in case someone changed the ** file out from under us. */ close(fd); } /* got the file, open for write and locked. */ /* Fetch the header */ if (CAFReadHeader(fd, &head) < 0) { close(fd); return -1; } if ((freebitmap = CAFReadFreeBM(fd, &head)) == NULL) { close(fd); return -1; } for (j = 0 ; j < narts ; ++j) { art = artnums[j]; /* Is the requested article even in the file? */ if (art < head.Low || art > head.High) { CAFError(CAF_ERR_ARTNOTHERE); errorfound = true; continue; /* don't abandon the whole remove if just one art is missing */ } if (CAFGetTOCEnt(fd, &head, art, &tocent) < 0) { close(fd); CAFDisposeBitmap(freebitmap); return -1; } if (tocent.Size == 0) { CAFError(CAF_ERR_ARTNOTHERE); errorfound = true; continue; /* don't abandon the whole remove if just one art is missing */ } numblksfreed = (tocent.Size + head.BlockSize - 1) / head.BlockSize; /* Mark all the blocks as free. */ for (curblk = tocent.Offset, i = 0 ; i < numblksfreed; ++i, curblk += head.BlockSize) { CAFSetBlockFree(freebitmap, fd, curblk, 1); } /* Note the amount of free space added. */ head.Free += numblksfreed * head.BlockSize; /* and mark the tocent as a deleted entry. */ tocent.Size = 0; if (CAFSeekTOCEnt(fd, &head, art) < 0) { close(fd); CAFDisposeBitmap(freebitmap); return -1; } if (OurWrite(fd, &tocent, sizeof(CAFTOCENT)) < 0) { close(fd); CAFDisposeBitmap(freebitmap); return -1; } } if (CAFWriteFreeBM(fd, freebitmap) < 0) { close(fd); CAFDisposeBitmap(freebitmap); return -1; } /* dispose of bitmap storage. */ CAFDisposeBitmap(freebitmap); /* need to update header. */ if (lseek(fd, 0, SEEK_SET) < 0) { CAFError(CAF_ERR_IO); return -1; } if (OurWrite(fd, &head, sizeof(CAFHEADER)) < 0) { return -1; } if (close(fd) < 0) { CAFError(CAF_ERR_IO); return -1; } if (CAFClean(path, 0, 10.0) < 0) errorfound=true; return errorfound ? -1 : 0; } /* ** Do a fake stat() of a CAF-stored article. Both 'inpaths' and 'innfeed' ** find this functionality useful, so we've added a function to do this. ** Caveats: not all of the stat structure is filled in, only these items: ** st_mode, st_size, st_atime, st_ctime, st_mtime. (Note: ** atime==ctime==mtime always, as we don't track times of CAF reads.) */ int CAFStatArticle(char *path, ARTNUM art, struct stat *stbuf) { CAFHEADER head; int fd; CAFTOCENT tocent; if ( (fd = open(path, O_RDONLY)) < 0) { /* ** if ENOENT (not there), just call this "article not found", ** otherwise it's a more serious error and stash the errno. */ if (errno == ENOENT) { CAFError(CAF_ERR_ARTNOTHERE); } else { CAFError(CAF_ERR_IO); } return -1; } /* Fetch the header */ if (CAFReadHeader(fd, &head) < 0) { close(fd); return -1; } /* Is the requested article even in the file? */ if (art < head.Low || art > head.High) { CAFError(CAF_ERR_ARTNOTHERE); close(fd); return -1; } if (CAFGetTOCEnt(fd, &head, art, &tocent) < 0) { close(fd); return -1; } if (tocent.Size == 0) { /* empty/otherwise not present article */ CAFError(CAF_ERR_ARTNOTHERE); close(fd); return -1; } /* done with file, can close it. */ close(fd); memset(stbuf, 0, sizeof(struct stat)); stbuf->st_mode = S_IFREG | 0444; stbuf->st_size = tocent.Size; stbuf->st_atime = stbuf->st_ctime = stbuf->st_mtime = tocent.ModTime; return 0; } /* ** Taken from the old 'cafclean' program. ** Function to clean a single CAF file. ** Possibly the ugliest function I've ever written in my life. */ /* ** We try to keep the total TOC size this many times larger than the actual ** amount of TOC data in use so as not to have to reclean or compact the TOC ** so often. */ #define TOC_CLEAN_RATIO 10 /* ** ditto, but for compacting, we want to force a compacting if the High art# ** wanders into the top nth of the TOC slots. */ #define TOC_COMPACT_RATIO 5 int CAFClean(char *path, int verbose, double PercentFreeThreshold) { char *newpath; size_t pathlen; CAFHEADER head, newhead; int fdin, fdout; ARTNUM newlow; ARTNUM i; CAFTOCENT *tocarray, *tocp; CAFTOCENT *newtocarray, *newtocp; size_t newtocsize; FILE *infile, *outfile; off_t startoffset, newstartoffset; char buf[BUFSIZ]; int nbytes; size_t ncur; int n; unsigned int blocksize; char *zerobuff; struct stat statbuf; size_t datasize; double percentfree; int toc_needs_expansion; int toc_needs_compacting; #ifdef STATFUNCT struct STATSTRUC fsinfo; unsigned long num_diskblocks_needed; #endif /* allocate buffer for newpath */ pathlen = strlen(path) + 10; newpath = xmalloc(pathlen); while (true) { /* try to open the file and lock it. */ if ((fdin = open(path, O_RDWR)) < 0) { /* ** if ENOENT, obviously no CAF file is here, so just return, ** otherwise report an error. */ if (errno != ENOENT) { CAFError(CAF_ERR_IO); return -1; } else { return 0; } } /* try a nonblocking lock attempt first. */ if (inn_lock_file(fdin, INN_LOCK_WRITE, false)) break; /* wait around to try and get a lock. */ inn_lock_file(fdin, INN_LOCK_WRITE, true); /* ** and then close and reopen the file, in case someone changed the ** file out from under us. */ close(fdin); } /* got the file, open for write and locked. */ /* Fetch the header */ if (CAFReadHeader(fdin, &head) < 0) { close(fdin); return -1; } /* Stat the file to see how big it is */ if (fstat(fdin, &statbuf) < 0) { close(fdin); CAFError(CAF_ERR_IO); perror(path); return -1; } /* compute amount of actual data in file. */ datasize = statbuf.st_size - head.StartDataBlock; if (datasize <= 0) { /* nothing in the file, set percentfree==0 so won't bother cleaning */ percentfree = 0; } else { percentfree = (100.0 * head.Free) / datasize; } /* ** Grumble, we need to read the TOC now even before we clean, just so ** we can decide if a clean or a compaction is needed. */ lseek(fdin, 0L, SEEK_SET); /* make input file stdio-buffered. */ if ((infile = fdopen(fdin, "r+")) == NULL) { CAFError(CAF_ERR_IO); close(fdin); return -1; } /* Allocate memory for TOC. */ tocarray = xmalloc((head.High - head.Low + 1) * sizeof(CAFTOCENT)); fseeko(infile, (off_t) (sizeof(CAFHEADER) + head.FreeZoneTabSize), SEEK_SET); n = fread(tocarray, sizeof(CAFTOCENT), (head.High - head.Low + 1), infile); if (n < 0) { CAFError(CAF_ERR_IO); fclose(infile); free(tocarray); free(newpath); return -1; } if ((unsigned long) n < (head.High - head.Low +1)) { CAFError(CAF_ERR_BADFILE); fclose(infile); free(tocarray); free(newpath); return -1; } /* Scan to see what the new lower bound for CAF file should be. */ newlow = head.High + 1; for (tocp = tocarray, i = head.Low; i <= head.High; ++tocp, ++i) { if (tocp->Size != 0) { newlow = i; break; } } /* ** if newlow is head.High+1, the TOC is completely empty and we can ** just remove the entire file. */ if (newlow == head.High + 1) { unlink(path); fclose(infile); free(tocarray); free(newpath); return 0; } /* ** Ah. NOW we get to decide if we need a clean! ** Clean if either ** 1) the absolute freespace threshold is crossed ** 2) the percent free threshold is crossed. ** 3) The CAF TOC is over 10% full (assume it needs to be expanded, ** so we force a clean) ** Note that even if we do not need a clean, we may need a compaction ** if the high article number is in the top nth of the TOC. */ toc_needs_expansion = 0; if ( (head.High - newlow) >= head.NumSlots/TOC_CLEAN_RATIO) { toc_needs_expansion = 1; } toc_needs_compacting = 0; if ( (head.Low + head.NumSlots - head.NumSlots/TOC_COMPACT_RATIO) <= head.High) { toc_needs_compacting = 1; } if ( (percentfree < PercentFreeThreshold) && (!toc_needs_expansion) ) { /* no cleaning, but do we need a TOC compaction ? */ if (toc_needs_compacting) { int delta; CAFTOCENT *tocp2; if (verbose) { printf("Compacting %s: Free=%lu (%f%%)\n", path, (unsigned long) head.Free, percentfree); } delta = newlow - head.Low; /* slide TOC array down delta units. */ for (i = newlow, tocp = tocarray, tocp2 = tocarray+delta; i <= head.High ; ++i) { *tocp++ = *tocp2++; } head.Low = newlow; /* note we don't set LastCleaned, this doesn't count a a clean. */ /* (XXX: do we need a LastCompacted as well? might be nice.) */ /* write new header on top of old */ fseeko(infile, 0, SEEK_SET); if (fwrite(&head, sizeof(CAFHEADER), 1, infile) < 1) { CAFError(CAF_ERR_IO); free(tocarray); free(newpath); fclose(infile); return -1; } /* ** this next fseeko might actually fail, because we have buffered ** stuff that might fail on write. */ if (fseeko(infile, sizeof(CAFHEADER) + head.FreeZoneTabSize, SEEK_SET) < 0) { perror(path); free(tocarray); free(newpath); fclose(infile); return -1; } if (fwrite(tocarray, sizeof(CAFTOCENT), head.High - newlow + 1, infile) < head.High - newlow + 1 || fflush(infile) < 0) { CAFError(CAF_ERR_IO); free(tocarray); free(newpath); fclose(infile); return -1; } /* all done, return. */ fclose(infile); free(tocarray); free(newpath); return 0; } else { /* need neither full cleaning nor compaction, so return. */ if (verbose) { printf("Not cleaning %s: Free=%lu (%f%%)\n", path, (unsigned long) head.Free, percentfree); } fclose(infile); free(tocarray); free(newpath); return 0; } } /* ** If OS supports it, try to check for free space and skip this file if ** not enough free space on this filesystem. */ #ifdef STATFUNCT if (STATFUNCT(fdin, &fsinfo) >= 0) { /* compare avail # blocks to # blocks needed for current file. ** # blocks needed is approximately ** datasize/blocksize + (size of the TOC)/blocksize ** + Head.BlockSize/blocksize, but we need to take rounding ** into account. */ #define RoundIt(n) (CAFRoundOffsetUp((n), fsinfo.STATMULTI) / fsinfo.STATMULTI) num_diskblocks_needed = RoundIt((head.High - head.Low + 1)*sizeof(CAFTOCENT)) + RoundIt(datasize - head.Free) + RoundIt(head.BlockSize); if (num_diskblocks_needed > (unsigned long) fsinfo.STATAVAIL) { if (verbose) { printf("CANNOT clean %s: needs %lu blocks, only %lu avail.\n", path, num_diskblocks_needed, (unsigned long) fsinfo.f_bavail); } fclose(infile); free(tocarray); free(newpath); return 0; } } #endif if (verbose) { printf("Am cleaning %s: Free=%lu (%f%%) %s\n", path, (unsigned long) head.Free, percentfree, toc_needs_expansion ? "(Expanding TOC)" : ""); } /* decide on proper size for new TOC */ newtocsize = CAF_DEFAULT_TOC_SIZE; if (head.High - newlow > newtocsize/TOC_CLEAN_RATIO) { newtocsize = TOC_CLEAN_RATIO*(head.High - newlow); } /* try to create new CAF file with some temp. pathname */ /* note: new CAF file is created in flocked state. */ if ((fdout = CAFCreateCAFFile(path, newlow, newtocsize, statbuf.st_size, 1, newpath, pathlen)) < 0) { fclose(infile); free(tocarray); free(newpath); return -1; } if ((outfile = fdopen(fdout, "w+")) == NULL) { CAFError(CAF_ERR_IO); fclose(infile); free(tocarray); unlink(newpath); free(newpath); return -1; } newtocarray = xcalloc((head.High - newlow + 1), sizeof(CAFTOCENT)); if (fseeko(outfile, 0, SEEK_SET) < 0) { perror(newpath); free(tocarray); free(newtocarray); fclose(infile); fclose(outfile); unlink(newpath); free(newpath); return -1; } /* read in the CAFheader from the new file. */ if (fread(&newhead, sizeof(CAFHEADER), 1, outfile) < 1) { perror(newpath); free(tocarray); free(newtocarray); fclose(infile); fclose(outfile); unlink(newpath); free(newpath); return -1; } /* initialize blocksize, zeroes buffer. */ blocksize = newhead.BlockSize; if (blocksize == 0) { blocksize=CAF_DEFAULT_BLOCKSIZE; } zerobuff = xcalloc(blocksize, 1); /* seek to end of output file/place to start writing new articles */ fseeko(outfile, 0, SEEK_END); startoffset = ftello(outfile); startoffset = CAFRoundOffsetUp(startoffset, blocksize); fseeko(outfile, (off_t) startoffset, SEEK_SET); /* ** Note: startoffset will always give the start offset of the next ** art to be written to the outfile. */ /* ** Loop over all arts in old TOC, copy arts that are still here to new ** file and new TOC. */ for (tocp = tocarray, i = head.Low; i <= head.High; ++tocp, ++i) { if (tocp->Size != 0) { newtocp = &newtocarray[i - newlow]; newtocp->Offset = startoffset; newtocp->Size = tocp->Size; newtocp->ModTime = tocp->ModTime; /* seek to right place in input. */ fseeko(infile, (off_t) tocp->Offset, SEEK_SET); nbytes = tocp->Size; while (nbytes > 0) { ncur = (nbytes > BUFSIZ) ? BUFSIZ : nbytes; if (fread(buf, sizeof(char), ncur, infile) < ncur || fwrite(buf, sizeof(char), ncur, outfile) < ncur) { if (feof(infile)) { CAFError(CAF_ERR_BADFILE); } else { CAFError(CAF_ERR_IO); } errorexit: fclose(infile); fclose(outfile); free(tocarray); free(newtocarray); free(zerobuff); unlink(newpath); free(newpath); return -1; } nbytes -= ncur; } /* startoffset = ftello(outfile); */ startoffset += tocp->Size; newstartoffset = CAFRoundOffsetUp(startoffset, blocksize); /* fseeko(outfile, (off_t) startoffset, SEEK_SET); */ /* but we don't want to call fseeko, since that seems to always force a write(2) syscall, even when the new location would still be inside stdio's buffer. */ if (newstartoffset - startoffset > 0) { ncur = newstartoffset - startoffset; if (fwrite(zerobuff, sizeof(char), ncur, outfile) < ncur) { /* write failed, must be disk error of some sort. */ perror(newpath); goto errorexit; /* yeah, it's a goto. eurggh. */ } } startoffset = newstartoffset; } } free(tocarray); /* don't need this guy anymore. */ free(zerobuff); /* ** set up new file header, TOC. ** this next fseeko might actually fail, because we have buffered stuff ** that might fail on write. */ if (fseeko(outfile, 0, SEEK_SET) < 0) { perror(newpath); free(newtocarray); fclose(infile); fclose(outfile); unlink(newpath); free(newpath); return -1; } /* Change what we need in new file's header. */ newhead.Low = newlow; newhead.High = head.High; newhead.LastCleaned = time((time_t *) NULL); /* newhead.NumSlots = newtocsize; */ /* newhead.Free = 0; */ if (fwrite(&newhead, sizeof(CAFHEADER), 1, outfile) < 1) { CAFError(CAF_ERR_IO); free(newtocarray); fclose(infile); fclose(outfile); unlink(newpath); free(newpath); return -1; } /* ** this next fseeko might actually fail, because we have buffered stuff ** that might fail on write. */ if (fseeko(outfile, sizeof(CAFHEADER) + newhead.FreeZoneTabSize, SEEK_SET) < 0) { perror(newpath); free(newtocarray); fclose(infile); fclose(outfile); unlink(newpath); free(newpath); return -1; } if (fwrite(newtocarray, sizeof(CAFTOCENT), head.High - newlow + 1, outfile) < head.High - newlow + 1 || fflush(outfile) < 0) { CAFError(CAF_ERR_IO); free(newtocarray); fclose(infile); fclose(outfile); unlink(newpath); free(newpath); return -1; } if (rename(newpath, path) < 0) { CAFError(CAF_ERR_IO); free(newtocarray); free(newpath); fclose(infile); fclose(outfile); /* if can't rename, probably no point in trying to unlink newpath, is there? */ return -1; } /* written and flushed newtocarray, can safely fclose and get out of here! */ free(newtocarray); free(newpath); fclose(outfile); fclose(infile); return 0; } inn-2.6.0/storage/timecaf/method.config0000644000175200017520000000007012575023702017440 0ustar iuliusiuliusname = timecaf number = 4 sources = caf.c timecaf.c inn-2.6.0/storage/timecaf/timecaf.h0000644000175200017520000000160612575023702016560 0ustar iuliusiulius/* $Id: timecaf.h 8817 2009-11-17 18:57:19Z iulius $ ** ** timecaf -- like the timehash storage method (and heavily inspired ** by it), but uses the CAF library to store multiple articles in a ** single file. */ #ifndef __TIMECAF_H__ #define __TIMECAF_H__ #include "config.h" #include "interface.h" bool timecaf_init(SMATTRIBUTE *attr); TOKEN timecaf_store(const ARTHANDLE article, const STORAGECLASS class); ARTHANDLE *timecaf_retrieve(const TOKEN token, const RETRTYPE amount); ARTHANDLE *timecaf_next(ARTHANDLE *article, const RETRTYPE amount); void timecaf_freearticle(ARTHANDLE *article); bool timecaf_cancel(TOKEN token); bool timecaf_ctl(PROBETYPE type, TOKEN *token, void *value); bool timecaf_flushcacheddata(FLUSHTYPE type); void timecaf_printfiles(FILE *file, TOKEN token, char **xref, int ngroups); char *timecaf_explaintoken(const TOKEN token); void timecaf_shutdown(void); #endif inn-2.6.0/storage/expire.c0000644000175200017520000005620312575023702015032 0ustar iuliusiulius/* $Id: expire.c 9896 2015-06-14 10:09:47Z iulius $ ** ** Code for overview-driven expiration. ** ** In order to expire on a per-newsgroup (instead of per-storage-class) ** basis, one has to use overview-driven expiration. This contains all of ** the code to do that. It provides OVgroupbasedexpire and OVhisthasmsgid ** for the use of various overview methods. */ #include "config.h" #include "clibrary.h" #include #include #include "inn/innconf.h" #include "inn/libinn.h" #include "inn/ov.h" #include "ovinterface.h" #include "inn/paths.h" #include "inn/storage.h" #include "inn/vector.h" enum KRP {Keep, Remove, Poison}; /* Statistics */ long EXPprocessed; long EXPunlinked; long EXPoverindexdrop; #define NGH_HASH(Name, p, j) \ for (p = Name, j = 0; *p; ) j = (j << 5) + j + *p++ #define NGH_SIZE 2048 #define NGH_BUCKET(j) &NGHtable[j & (NGH_SIZE - 1)] #define OVFMT_UNINIT -2 #define OVFMT_NODATE -1 #define OVFMT_NOXREF -1 static int Dateindex = OVFMT_UNINIT; static int Xrefindex = OVFMT_UNINIT; static int Messageidindex = OVFMT_UNINIT; typedef struct _NEWSGROUP { char *Name; char *Rest; unsigned long Last; unsigned long Lastpurged; /* These fields are new. */ time_t Keep; time_t Default; time_t Purge; /* X flag => remove entire article when it expires in this group */ bool Poison; } NEWSGROUP; typedef struct _NGHASH { int Size; int Used; NEWSGROUP **Groups; } NGHASH; #define MAGIC_TIME 49710. typedef struct _BADGROUP { struct _BADGROUP *Next; char *Name; } BADGROUP; /* ** Information about the schema of the news overview files. */ typedef struct _ARTOVERFIELD { char *Header; int Length; bool HasHeader; bool NeedsHeader; } ARTOVERFIELD; static BADGROUP *EXPbadgroups; static int nGroups; static NEWSGROUP *Groups; static NEWSGROUP EXPdefault; static NGHASH NGHtable[NGH_SIZE]; static char **arts; static enum KRP *krps; static ARTOVERFIELD * ARTfields; static int ARTfieldsize; static bool ReadOverviewfmt = false; /* FIXME: The following variables are shared between this file and ov.c. This should be cleaned up with a better internal interface. */ time_t OVnow; char * ACTIVE; FILE * EXPunlinkfile; bool OVignoreselfexpire; bool OVusepost; bool OVkeep; bool OVearliest; bool OVquiet; /* ** Hash a newsgroup and see if we get it. */ static NEWSGROUP * NGfind(char *Name) { char *p; int i; unsigned int j; NEWSGROUP **ngp; char c; NGHASH *htp; NGH_HASH(Name, p, j); htp = NGH_BUCKET(j); for (c = *Name, ngp = htp->Groups, i = htp->Used; --i >= 0; ngp++) if (c == ngp[0]->Name[0] && strcmp(Name, ngp[0]->Name) == 0) return ngp[0]; return NULL; } /* ** Sorting predicate to put newsgroups in rough order of their activity. */ static int NGcompare(const void *p1, const void *p2) { const NEWSGROUP * const * ng1 = p1; const NEWSGROUP * const * ng2 = p2; return ng1[0]->Last - ng2[0]->Last; } /* ** Split a line at a specified field separator into a vector and return ** the number of fields found, or -1 on error. */ static int EXPsplit(char *p, char sep, char **argv, int count) { int i; if (!p) return 0; while (*p == sep) ++p; if (!*p) return 0; if (!p) return 0; for (i = 1, *argv++ = p; *p; ) if (*p++ == sep) { p[-1] = '\0'; for (; *p == sep; p++); if (!*p) return i; if (++i == count) /* Overflow. */ return -1; *argv++ = p; } return i; } /* ** Build the newsgroup structures from the active file. */ static void BuildGroups(char *active) { NGHASH *htp; NEWSGROUP *ngp; char *p; char *q; int i; unsigned j; int lines; int NGHbuckets; char *fields[5]; /* Count the number of groups. */ for (p = active, i = 0; (p = strchr(p, '\n')) != NULL; p++, i++) continue; nGroups = i; Groups = xmalloc(i * sizeof(NEWSGROUP)); /* Set up the default hash buckets. */ NGHbuckets = i / NGH_SIZE; if (NGHbuckets == 0) NGHbuckets = 1; for (i = NGH_SIZE, htp = NGHtable; --i >= 0; htp++) { htp->Size = NGHbuckets; htp->Groups = xmalloc(htp->Size * sizeof(NEWSGROUP *)); htp->Used = 0; } /* Fill in the array. */ lines = 0; for (p = active, ngp = Groups, i = nGroups; --i >= 0; ngp++, p = q + 1) { lines++; if ((q = strchr(p, '\n')) == NULL) { fprintf(stderr, "%s: line %d missing newline\n", ACTIVE, lines); exit(1); } if (*p == '.') continue; *q = '\0'; if (EXPsplit(p, ' ', fields, ARRAY_SIZE(fields)) != 4) { fprintf(stderr, "%s: line %d wrong number of fields\n", ACTIVE, lines); exit(1); } ngp->Name = fields[0]; ngp->Last = atol(fields[1]); ngp->Rest = fields[3]; /* Find the right bucket for the group, make sure there is room. */ NGH_HASH(ngp->Name, p, j); htp = NGH_BUCKET(j); if (htp->Used >= htp->Size) { htp->Size += NGHbuckets; htp->Groups = xrealloc(htp->Groups, htp->Size * sizeof(NEWSGROUP *)); } htp->Groups[htp->Used++] = ngp; } /* Sort each hash bucket. */ for (i = NGH_SIZE, htp = NGHtable; --i >= 0; htp++) if (htp->Used > 1) qsort(htp->Groups, htp->Used, sizeof htp->Groups[0], NGcompare); /* Ok, now change our use of the Last field. Set them all to maxint. */ for (i = NGH_SIZE, htp = NGHtable; --i >= 0; htp++) { NEWSGROUP **ngpa; int k; for (ngpa = htp->Groups, k = htp->Used; --k >= 0; ngpa++) { ngpa[0]->Last = ~(unsigned long) 0; ngpa[0]->Lastpurged = 0; } } } /* ** Parse a number field converting it into a "when did this start?". ** This makes the "keep it" tests fast, but inverts the logic of ** just about everything you expect. Print a message and return false ** on error. */ static bool EXPgetnum(int line, char *word, time_t *v, const char *name) { char *p; bool SawDot; double d; if (strcasecmp(word, "never") == 0) { *v = (time_t)0; return true; } /* Check the number. We don't have strtod yet. */ for (p = word; ISWHITE(*p); p++) continue; if (*p == '+' || *p == '-') p++; for (SawDot = false; *p; p++) if (*p == '.') { if (SawDot) break; SawDot = true; } else if (!isdigit((unsigned char) *p)) break; if (*p) { fprintf(stderr, "Line %d, bad `%c' character in %s field\n", line, *p, name); return false; } d = atof(word); if (d > MAGIC_TIME) *v = (time_t)0; else *v = OVnow - (time_t)(d * 86400.); return true; } /* ** Set the expiration fields for all groups that match this pattern. */ static void EXPmatch(char *p, NEWSGROUP *v, char mod) { NEWSGROUP *ngp; int i; bool negate; negate = *p == '!'; if (negate) p++; for (ngp = Groups, i = nGroups; --i >= 0; ngp++) if (negate ? !uwildmat(ngp->Name, p) : uwildmat(ngp->Name, p)) if (mod == 'a' || (mod == 'm' && ngp->Rest[0] == NF_FLAG_MODERATED) || (mod == 'u' && ngp->Rest[0] != NF_FLAG_MODERATED)) { ngp->Keep = v->Keep; ngp->Default = v->Default; ngp->Purge = v->Purge; ngp->Poison = v->Poison; } } /* ** Parse the expiration control file. Return true if okay. */ static bool EXPreadfile(FILE *F) { char *p; int i; int j; int k; char mod; NEWSGROUP v; bool SawDefault; char buff[BUFSIZ]; char *fields[7]; char **patterns; /* Scan all lines. */ SawDefault = false; patterns = xmalloc(nGroups * sizeof(char *)); for (i = 1; fgets(buff, sizeof buff, F) != NULL; i++) { if ((p = strchr(buff, '\n')) == NULL) { fprintf(stderr, "Line %d too long\n", i); free(patterns); return false; } *p = '\0'; p = strchr(buff, '#'); if (p) *p = '\0'; else p = buff + strlen(buff); while (--p >= buff) { if (isspace((unsigned char) *p)) *p = '\0'; else break; } if (buff[0] == '\0') continue; if ((j = EXPsplit(buff, ':', fields, ARRAY_SIZE(fields))) == -1) { fprintf(stderr, "Line %d too many fields\n", i); free(patterns); return false; } /* Expired-article remember line? */ if (strcmp(fields[0], "/remember/") == 0) { continue; } /* Regular expiration line -- right number of fields? */ if (j != 5) { fprintf(stderr, "Line %d bad format\n", i); free(patterns); return false; } /* Parse the fields. */ if (strchr(fields[1], 'M') != NULL) mod = 'm'; else if (strchr(fields[1], 'U') != NULL) mod = 'u'; else if (strchr(fields[1], 'A') != NULL) mod = 'a'; else { fprintf(stderr, "Line %d bad modflag\n", i); free(patterns); return false; } v.Poison = (strchr(fields[1], 'X') != NULL); if (!EXPgetnum(i, fields[2], &v.Keep, "keep") || !EXPgetnum(i, fields[3], &v.Default, "default") || !EXPgetnum(i, fields[4], &v.Purge, "purge")) { free(patterns); return false; } /* These were turned into offsets, so the test is the opposite * of what you think it should be. If Purge isn't forever, * make sure it's greater then the other two fields. */ if (v.Purge) { /* Some value not forever; make sure other values are in range. */ if (v.Keep && v.Keep < v.Purge) { fprintf(stderr, "Line %d keep>purge\n", i); free(patterns); return false; } if (v.Default && v.Default < v.Purge) { fprintf(stderr, "Line %d default>purge\n", i); free(patterns); return false; } } /* Is this the default line? */ if (fields[0][0] == '*' && fields[0][1] == '\0' && mod == 'a') { if (SawDefault) { fprintf(stderr, "Line %d duplicate default\n", i); free(patterns); return false; } EXPdefault.Keep = v.Keep; EXPdefault.Default = v.Default; EXPdefault.Purge = v.Purge; EXPdefault.Poison = v.Poison; SawDefault = true; } /* Assign to all groups that match the pattern and flags. */ if ((j = EXPsplit(fields[0], ',', patterns, nGroups)) == -1) { fprintf(stderr, "Line %d too many patterns\n", i); free(patterns); return false; } for (k = 0; k < j; k++) EXPmatch(patterns[k], &v, mod); } free(patterns); return true; } /* ** Handle a newsgroup that isn't in the active file. */ static NEWSGROUP * EXPnotfound(char *Entry) { static NEWSGROUP Removeit; BADGROUP *bg; /* See if we already know about this group. */ for (bg = EXPbadgroups; bg; bg = bg->Next) if (strcmp(Entry, bg->Name) == 0) break; if (bg == NULL) { bg = xmalloc(sizeof(BADGROUP)); bg->Name = xstrdup(Entry); bg->Next = EXPbadgroups; EXPbadgroups = bg; } /* remove it all now. */ if (Removeit.Keep == 0) { Removeit.Keep = OVnow; Removeit.Default = OVnow; Removeit.Purge = OVnow; } return &Removeit; } /* ** Should we keep the specified article? */ static enum KRP EXPkeepit(char *Entry, time_t when, time_t expires) { NEWSGROUP *ngp; enum KRP retval = Remove; if ((ngp = NGfind(Entry)) == NULL) ngp = EXPnotfound(Entry); /* Bad posting date? */ if (when > OVrealnow + 86400) { /* Yes -- force the article to go right now. */ when = expires ? ngp->Purge : ngp->Default; } /* If no expiration, make sure it wasn't posted before the default. */ if (expires == 0) { if (when >= ngp->Default) retval = Keep; /* Make sure it's not posted before the purge cut-off and * that it's not due to expire. */ } else { if (when >= ngp->Purge && (expires >= OVnow || when >= ngp->Keep)) retval = Keep; } if (retval == Keep) { return Keep; } else { return ngp->Poison ? Poison : Remove; } } /* ** An article can be removed. Either print a note, or actually remove it. ** Takes in the Xref information so that it can pass this to the storage ** API callback used to generate the list of files to remove. */ void OVEXPremove(TOKEN token, bool deletedgroups, char **xref, int ngroups) { EXPunlinked++; if (deletedgroups) { EXPprocessed++; EXPoverindexdrop++; } if (EXPunlinkfile && xref != NULL) { SMprintfiles(EXPunlinkfile, token, xref, ngroups); if (!ferror(EXPunlinkfile)) return; fprintf(stderr, "Can't write to -z file, %s\n", strerror(errno)); fprintf(stderr, "(Will ignore it for rest of run.)\n"); fclose(EXPunlinkfile); EXPunlinkfile = NULL; } if (!SMcancel(token) && SMerrno != SMERR_NOENT && SMerrno != SMERR_UNINIT) fprintf(stderr, "Can't unlink %s: %s\n", TokenToText(token), SMerrorstr); } /* ** Read the overview schema. */ static void ARTreadschema(void) { const struct cvector *standardoverview; struct vector *extraoverview; ARTOVERFIELD *fp; unsigned int i; /* Count the number of overview fields and allocate ARTfields. */ standardoverview = overview_fields(); extraoverview = overview_extra_fields(true); ARTfields = xmalloc((standardoverview->count + extraoverview->count + 1) * sizeof(ARTOVERFIELD)); /* Parse each field. */ for (i = 0, fp = ARTfields; i < standardoverview->count; i++) { fp->NeedsHeader = false; fp->HasHeader = false; fp->Header = xstrdup(standardoverview->strings[i]); fp->Length = strlen(standardoverview->strings[i]); fp++; } for (i = 0; i < extraoverview->count; i++) { fp->NeedsHeader = true; fp->HasHeader = false; fp->Header = xstrdup(extraoverview->strings[i]); fp->Length = strlen(extraoverview->strings[i]); fp++; } ARTfieldsize = fp - ARTfields; vector_free(extraoverview); } /* ** Return a field from the overview line or NULL on error. Return a copy ** since we might be re-using the line later. */ static char * OVERGetHeader(const char *p, int field) { static char *buff; static int buffsize; int i; ARTOVERFIELD *fp; char *next; fp = &ARTfields[field]; /* Skip leading headers. */ for (; field-- >= 0 && *p; p++) if ((p = strchr(p, '\t')) == NULL) return NULL; if (*p == '\0') return NULL; if (fp->HasHeader) p += fp->Length + 2; if (fp->NeedsHeader) { /* find an exact match */ while (strncmp(fp->Header, p, fp->Length) != 0) { if ((p = strchr(p, '\t')) == NULL) return NULL; p++; } p += fp->Length + 2; } /* Figure out length; get space. */ if ((next = strpbrk(p, "\n\r\t")) != NULL) { i = next - p; } else { i = strlen(p); } if (buffsize == 0) { buffsize = i; buff = xmalloc(buffsize + 1); } else if (buffsize < i) { buffsize = i; buff = xrealloc(buff, buffsize + 1); } strncpy(buff, p, i); buff[i] = '\0'; return buff; } /* ** Read overview schema and find index for headers. */ static void OVfindheaderindex(void) { FILE *F; char *active; char *path; int i; if (ReadOverviewfmt) return; if (innconf->groupbaseexpiry) { ACTIVE = concatpath(innconf->pathdb, INN_PATH_ACTIVE); if ((active = ReadInFile(ACTIVE, (struct stat *)NULL)) == NULL) { fprintf(stderr, "Can't read %s, %s\n", ACTIVE, strerror(errno)); exit(1); } BuildGroups(active); arts = xmalloc(nGroups * sizeof(char *)); krps = xmalloc(nGroups * sizeof(enum KRP)); path = concatpath(innconf->pathetc, INN_PATH_EXPIRECTL); F = fopen(path, "r"); free(path); if (!EXPreadfile(F)) { fclose(F); fprintf(stderr, "Format error in expire.ctl\n"); exit(1); } fclose(F); } ARTreadschema(); if (Dateindex == OVFMT_UNINIT) { for (Dateindex = OVFMT_NODATE, i = 0; i < ARTfieldsize; i++) { if (strcasecmp(ARTfields[i].Header, "Date") == 0) { Dateindex = i; } else if (strcasecmp(ARTfields[i].Header, "Xref") == 0) { Xrefindex = i; } else if (strcasecmp(ARTfields[i].Header, "Message-ID") == 0) { Messageidindex = i; } } } ReadOverviewfmt = true; return; } /* ** Do the work of expiring one line. Assumes article still exists in the ** spool. Returns true if article should be purged, or return false. */ bool OVgroupbasedexpire(TOKEN token, const char *group, const char *data, int len UNUSED, time_t arrived, time_t expires) { static char *Group = NULL; char *p; int i; int count; time_t when; bool poisoned; bool keeper; bool delete; bool purge; char *Xref; if (SMprobe(SELFEXPIRE, &token, NULL)) { if (!OVignoreselfexpire) /* this article should be kept */ return false; } if (!ReadOverviewfmt) { OVfindheaderindex(); } if (OVusepost) { if ((p = OVERGetHeader(data, Dateindex)) == NULL) { EXPoverindexdrop++; return true; } when = parsedate_rfc5322_lax(p); if (when == (time_t) -1) { EXPoverindexdrop++; return true; } } else { when = arrived; } if ((Xref = OVERGetHeader(data, Xrefindex)) == NULL) { if (Group != NULL) { free(Group); } Group = concat(group, ":", (char *) 0); Xref = Group; } else { if ((Xref = strchr(Xref, ' ')) == NULL) { EXPoverindexdrop++; return true; } for (Xref++; *Xref == ' '; Xref++) ; } if ((count = EXPsplit(Xref, ' ', arts, nGroups)) == -1) { EXPoverindexdrop++; return true; } /* arts is now an array of strings, each of which is a group name, a colon, and an article number. EXPkeepit wants just pure group names, so replace the colons with nuls (deleting the overview entry if it isn't in the expected form). */ for (i = 0; i < count; i++) { p = strchr(arts[i], ':'); if (p == NULL) { fflush(stdout); fprintf(stderr, "Bad entry, \"%s\"\n", arts[i]); EXPoverindexdrop++; return true; } *p = '\0'; } /* First check all postings */ poisoned = false; keeper = false; delete = false; purge = true; for (i = 0; i < count; ++i) { if ((krps[i] = EXPkeepit(arts[i], when, expires)) == Poison) poisoned = true; if (OVkeep && (krps[i] == Keep)) keeper = true; if ((krps[i] == Remove) && strcmp(group, arts[i]) == 0) delete = true; if (krps[i] == Keep) purge = false; } EXPprocessed++; if (OVearliest) { if (delete || poisoned || token.type == TOKEN_EMPTY) { /* delete article if this is first entry */ if (strcmp(group, arts[0]) == 0) { for (i = 0; i < count; i++) arts[i][strlen(arts[i])] = ':'; OVEXPremove(token, false, arts, count); } EXPoverindexdrop++; return true; } } else { /* not earliest mode */ if ((!keeper && delete) || token.type == TOKEN_EMPTY) { /* delete article if purge is set, indicating that it has expired out of every group to which it was posted */ if (purge) { for (i = 0; i < count; i++) arts[i][strlen(arts[i])] = ':'; OVEXPremove(token, false, arts, count); } EXPoverindexdrop++; return true; } } /* this article should be kept */ return false; } bool OVhisthasmsgid(struct history *h, const char *data) { char *p; if (!ReadOverviewfmt) { OVfindheaderindex(); } if ((p = OVERGetHeader(data, Messageidindex)) == NULL) return false; return HISlookup(h, p, NULL, NULL, NULL, NULL); } void OVEXPcleanup(void) { int i; BADGROUP *bg, *bgnext; ARTOVERFIELD *fp; NGHASH *htp; if (EXPprocessed != 0) { if (!OVquiet) { printf("Article lines processed %8ld\n", EXPprocessed); printf("Articles dropped %8ld\n", EXPunlinked); printf("Overview index dropped %8ld\n", EXPoverindexdrop); } EXPprocessed = EXPunlinked = EXPoverindexdrop = 0; } for (bg = EXPbadgroups; bg; bg = bgnext) { bgnext = bg->Next; free(bg->Name); free(bg); } for (fp = ARTfields, i = 0; i < ARTfieldsize ; i++, fp++) { free(fp->Header); } free(ARTfields); if (ACTIVE != NULL) { free(ACTIVE); ACTIVE = NULL; } if (Groups != NULL) { free(Groups); Groups = NULL; } for (i = 0, htp = NGHtable ; i < NGH_SIZE ; i++, htp++) { if (htp->Groups != NULL) { free(htp->Groups); htp->Groups = NULL; } } } inn-2.6.0/storage/interface.h0000644000175200017520000000446512575023702015506 0ustar iuliusiulius/* $Id: interface.h 8817 2009-11-17 18:57:19Z iulius $ ** ** Storage Manager interface header */ #ifndef __INTERFACE_H__ #define __INTERFACE_H__ #include "config.h" #include "inn/storage.h" #include typedef struct { bool selfexpire; bool expensivestat; } SMATTRIBUTE; typedef struct { const char *name; unsigned char type; bool (*init)(SMATTRIBUTE *attr); TOKEN (*store)(const ARTHANDLE article, const STORAGECLASS storageclass); ARTHANDLE *(*retrieve)(const TOKEN token, const RETRTYPE amount); ARTHANDLE *(*next)(ARTHANDLE *article, const RETRTYPE amount); void (*freearticle)(ARTHANDLE *article); bool (*cancel)(TOKEN token); bool (*ctl)(PROBETYPE type, TOKEN *token, void *value); bool (*flushcacheddata)(FLUSHTYPE type); void (*printfiles)(FILE *, TOKEN, char **xref, int ngroups); char *(*explaintoken)(const TOKEN token); void (*shutdown)(void); } STORAGE_METHOD; typedef struct __S_SUB__ { int type; /* Index into storage_methods of the one to use */ size_t minsize; /* Minimum size to send to this method */ size_t maxsize; /* Maximum size to send to this method */ time_t minexpire; /* Minimum expire offset to send method */ time_t maxexpire; /* Maximum expire offset to send method */ int numpatterns; /* Number of patterns in patterns */ int class; /* Number of the storage class for this subscription */ char *pattern; /* Wildmat pattern to check against the groups to determine if the article should go to this method */ char *options; /* additional options specific to the method */ bool exactmatch; /* all newsgroups to which article belongs should match the patterns */ struct __S_SUB__ *next; } STORAGE_SUB; extern bool SMopenmode; extern bool SMpreopen; char *SMFindBody(char *article, int len); STORAGE_SUB *SMGetConfig(STORAGETYPE type, STORAGE_SUB *sub); STORAGE_SUB *SMgetsub(const ARTHANDLE article); void SMseterror(int errorno, const char *error); #endif /* __INTERFACE_H__ */ inn-2.6.0/storage/trash/0000755000175200017520000000000012575023701014504 5ustar iuliusiuliusinn-2.6.0/storage/trash/method.config0000644000175200017520000000005612575023702017155 0ustar iuliusiuliusname = trash number = 0 sources = trash.c inn-2.6.0/storage/trash/trash.c0000644000175200017520000000351112575023702015772 0ustar iuliusiulius/* $Id: trash.c 8817 2009-11-17 18:57:19Z iulius $ ** ** Storage manager module for trash method. */ #include "config.h" #include "clibrary.h" #include "inn/libinn.h" #include "methods.h" #include "trash.h" bool trash_init(SMATTRIBUTE *attr) { if (attr == NULL) { SMseterror(SMERR_INTERNAL, "attr is NULL"); return false; } attr->selfexpire = true; attr->expensivestat = false; return true; } TOKEN trash_store(const ARTHANDLE article, const STORAGECLASS class) { TOKEN token; if (article.token == (TOKEN *)NULL) memset(&token, '\0', sizeof(token)); else { memcpy(&token, article.token, sizeof(token)); memset(&token.token, '\0', STORAGE_TOKEN_LENGTH); } token.type = TOKEN_TRASH; token.class = class; return token; } ARTHANDLE * trash_retrieve(const TOKEN token, const RETRTYPE amount UNUSED) { if (token.type != TOKEN_TRASH) { SMseterror(SMERR_INTERNAL, NULL); return (ARTHANDLE *)NULL; } SMseterror(SMERR_NOENT, NULL); return (ARTHANDLE *)NULL; } void trash_freearticle(ARTHANDLE *article UNUSED) { } bool trash_cancel(TOKEN token UNUSED) { SMseterror(SMERR_NOENT, NULL); return false; } bool trash_ctl(PROBETYPE type, TOKEN *token UNUSED, void *value UNUSED) { switch (type) { case SMARTNGNUM: default: return false; } } bool trash_flushcacheddata(FLUSHTYPE type UNUSED) { return true; } void trash_printfiles(FILE *file UNUSED, TOKEN token UNUSED, char **xref UNUSED, int ngroups UNUSED) { } char * trash_explaintoken(const TOKEN token UNUSED) { char *text; xasprintf(&text, "method=%s", "trash"); return text; } ARTHANDLE * trash_next(ARTHANDLE *article UNUSED, const RETRTYPE amount UNUSED) { return NULL; } void trash_shutdown(void) { } inn-2.6.0/storage/trash/trash.h0000644000175200017520000000140212575023702015774 0ustar iuliusiulius/* $Id: trash.h 8817 2009-11-17 18:57:19Z iulius $ ** ** Storage manager module header for trash method. */ #ifndef __TRASH_H__ #define __TRASH_H__ #include "config.h" #include "interface.h" bool trash_init(SMATTRIBUTE *attr); TOKEN trash_store(const ARTHANDLE article, const STORAGECLASS class); ARTHANDLE *trash_retrieve(const TOKEN token, const RETRTYPE amount); ARTHANDLE *trash_next(ARTHANDLE *article, const RETRTYPE amount); void trash_freearticle(ARTHANDLE *article); bool trash_cancel(TOKEN token); bool trash_ctl(PROBETYPE type, TOKEN *token, void *value); bool trash_flushcacheddata(FLUSHTYPE type); void trash_printfiles(FILE *file, TOKEN token, char **xref, int ngroups); char *trash_explaintoken(const TOKEN token); void trash_shutdown(void); #endif inn-2.6.0/Makefile0000644000175200017520000002354012575023702013364 0ustar iuliusiulius## $Id: Makefile 9832 2015-05-01 09:11:23Z iulius $ include Makefile.global ## All installation directories except for $(PATHRUN), which has a ## different mode than the rest. INSTDIRS = $(PATHNEWS) $(PATHBIN) $(PATHAUTH) $(PATHAUTHRESOLV) \ $(PATHAUTHPASSWD) $(PATHCONTROL) $(PATHFILTER) \ $(PATHRNEWS) $(PATHDB) $(PATHDOC) $(PATHETC) $(PATHHTTP) $(PATHLIB) \ $(PATHLIBPERL) $(PATHLIBPERL)/INN $(PATHDATASHARE) \ $(PATHMAN) $(MAN1) $(MAN3) $(MAN3PM) $(MAN5) $(MAN8) $(PATHSPOOL) \ $(PATHTMP) $(PATHARCHIVE) $(PATHARTICLES) $(PATHINCOMING) \ $(PATHINBAD) $(PATHTAPE) $(PATHOVERVIEW) $(PATHOUTGOING) \ $(PATHLOG) $(PATHLOGOLD) $(PATHINCLUDE) ## LIBDIRS are built before PROGDIRS, make update runs in all UPDATEDIRS, ## and make install runs in all ALLDIRS. Nothing runs in test except the ## test target itself and the clean targets. Currently, include is built ## before anything else but nothing else runs in it except clean targets. LIBDIRS = include lib storage history perl PROGDIRS = innd nnrpd innfeed control expire frontends backends authprogs \ scripts UPDATEDIRS = $(LIBDIRS) $(PROGDIRS) doc ALLDIRS = $(UPDATEDIRS) samples site CLEANDIRS = $(ALLDIRS) contrib tests ## The directory name and tar file to use when building a release. TARDIR = inn-$(VERSION)$(RELEASENUMBER) TARFILE = $(TARDIR).tar ## The directory to use when building a snapshot. SNAPDIR = inn-$(SNAPSHOT)-$(SNAPDATE) ## DISTDIRS gets all directories from the MANIFEST, SNAPDIRS gets the same ## but for a snapshot, and DISTFILES gets all regular files. Anything not ## listed in the MANIFEST will not be included in a distribution. These are ## arguments to sed. DISTDIRS = -e 1,2d -e '/(Directory)/!d' -e 's/ .*//' -e 's;^;$(TARDIR)/;' SNAPDIRS = -e 1,2d -e '/(Directory)/!d' -e 's/ .*//' -e 's;^;$(SNAPDIR)/;' DISTFILES = -e 1,2d -e '/(Directory)/d' -e 's/ .*//' ## Major target -- build everything. Rather than just looping through ## all the directories, use a set of parallel rules so that make -j can ## work on more than one directory at a time. ## Be careful of a non-GNU make: after a completed command, it does not ## necessarily return the script back to the starting directory. all: all-include all-libraries all-programs cd doc && $(MAKE) all || exit 1 ; cd .. cd samples && $(MAKE) all || exit 1 ; cd .. cd site && $(MAKE) all || exit 1 ; cd .. all-include: ; cd include && $(MAKE) all all-libraries: all-lib all-storage all-history all-perl all-lib: all-include ; cd lib && $(MAKE) all all-storage: all-lib ; cd storage && $(MAKE) library all-history: all-storage ; cd history && $(MAKE) all all-perl: all-history ; cd perl && $(MAKE) all all-programs: all-innd all-nnrpd all-innfeed all-control all-expire \ all-frontends all-backends all-authprogs all-scripts \ all-store-util all-authprogs: all-lib ; cd authprogs && $(MAKE) all all-backends: all-libraries ; cd backends && $(MAKE) all all-control: ; cd control && $(MAKE) all all-expire: all-libraries ; cd expire && $(MAKE) all all-frontends: all-libraries ; cd frontends && $(MAKE) all all-innd: all-libraries ; cd innd && $(MAKE) all all-innfeed: all-libraries ; cd innfeed && $(MAKE) all all-nnrpd: all-libraries ; cd nnrpd && $(MAKE) all all-scripts: ; cd scripts && $(MAKE) all all-store-util: all-libraries ; cd storage && $(MAKE) programs ## If someone tries to run make before running configure, tell them to run ## configure first. Makefile.global: @echo 'Run ./configure before running make. See INSTALL for details.' @exit 1 ## Installation rules. make install installs everything; make update only ## updates the binaries, scripts, and documentation and leaves config ## files alone. make install-root does only the installation that needs ## to be done as root and can be run after doing the regular install as the ## news user if the installation directory is news-writable. install: directories @for D in $(ALLDIRS) ; do \ echo '' ; \ cd $$D && $(MAKE) D="$(DESTDIR)" install || exit 1 ; cd .. ; \ done @echo '' @echo 'If this is a first-time installation, a minimal active file and' @echo 'history database have been installed. Do not forget to update' @echo 'your cron entries and configure INN. See INSTALL for more' @echo 'information.' @echo '' directories: @chmod +x support/install-sh for D in $(INSTDIRS) ; do \ support/install-sh $(OWNER) -m 0755 -d $(DESTDIR)$$D ; \ done support/install-sh $(OWNER) -m 0750 -d $(DESTDIR)$(PATHRUN) update: @chmod +x support/install-sh @for D in $(UPDATEDIRS) ; do \ echo '' ; \ cd $$D && $(MAKE) D="$(DESTDIR)" install || exit 1 ; cd .. ; \ done $(PERL) -Tw $(PATHBIN)/innupgrade $(DESTDIR)$(PATHAUTHPASSWD) $(PERL) -Tw $(PATHBIN)/innupgrade $(DESTDIR)$(PATHBIN) $(PERL) -Tw $(PATHBIN)/innupgrade $(DESTDIR)$(PATHDOC) $(PERL) -Tw $(PATHBIN)/innupgrade $(DESTDIR)$(PATHETC) $(PERL) -Tw $(PATHBIN)/innupgrade $(DESTDIR)${PATHFILTER} $(PERL) -Tw $(PATHBIN)/innupgrade $(DESTDIR)$(MAN1) $(PERL) -Tw $(PATHBIN)/innupgrade $(DESTDIR)$(MAN3) $(PERL) -Tw $(PATHBIN)/innupgrade $(DESTDIR)$(MAN5) $(PERL) -Tw $(PATHBIN)/innupgrade $(DESTDIR)$(MAN8) install-root: @chmod +x support/install-sh support/install-sh $(OWNER) -m 0755 -d $(DESTDIR)$(PATHBIN) cd backends && $(MAKE) D="$(DESTDIR)" install-root || exit 1 ; cd .. ## Install a certificate for TLS/SSL support. cert: umask 077 ; \ $(SSLBIN) req -new -x509 -nodes \ -out $(DESTDIR)$(PATHETC)/cert.pem -days 366 \ -keyout $(DESTDIR)$(PATHETC)/key.pem @ME=`$(WHOAMI)` ; \ if [ x"$$ME" = xroot ] ; then \ chown $(RUNASUSER) $(DESTDIR)$(PATHETC)/cert.pem ; \ chgrp $(RUNASGROUP) $(DESTDIR)$(PATHETC)/cert.pem ; \ chown $(RUNASUSER) $(DESTDIR)$(PATHETC)/key.pem ; \ chgrp $(RUNASGROUP) $(DESTDIR)$(PATHETC)/key.pem ; \ fi chmod 640 $(DESTDIR)$(PATHETC)/cert.pem chmod 600 $(DESTDIR)$(PATHETC)/key.pem ## Cleanup targets. clean deletes all compilation results but leaves the ## configure results. distclean or clobber removes everything not part of ## the distribution tarball. maintclean removes some additional files ## created as part of the release process. clean: @for D in $(CLEANDIRS) ; do \ echo '' ; \ cd $$D && $(MAKE) clean || exit 1 ; cd .. ; \ done clobber realclean distclean: @for D in $(CLEANDIRS) ; do \ echo '' ; \ cd $$D && $(MAKE) $(FLAGS) distclean || exit 1 ; cd .. ; \ done @echo '' rm -f LIST.* Makefile.global config.cache config.log rm -f config.status libtool support/fixconfig support/fixscript rm -f config.status.lineno configure.lineno # The removal list is unfortunately duplicated here, both to avoid doing work # twice and because we can't just depend on distclean since it removes # Makefile.global and then nothing works right. maintclean: @for D in $(CLEANDIRS) ; do \ echo '' ; \ cd $$D && $(MAKE) $(FLAGS) maintclean || exit 1 ; cd .. ; \ done @echo '' rm -f LIST.* Makefile.global config.cache config.log rm -f config.status libtool support/fixconfig support/fixscript rm -f config.status.lineno configure.lineno rm -f CHANGES ChangeLog inn*.tar.gz configure include/config.h.in rm -rf $(TARDIR) ## Other generic targets. bootstrap depend profiled: @for D in $(ALLDIRS) ; do \ echo '' ; \ cd $$D && $(MAKE) $@ || exit 1 ; cd .. ; \ done ## Run the test suite. check test tests: all cd tests && $(MAKE) test || exit 1 ; cd .. ## For maintainers, build the entire source base with warnings enabled. warnings: $(MAKE) COPT="$(WARNINGS) $(COPT)" all ## Make a release. We create a release by recreating the directory ## structure and then copying over all files listed in the MANIFEST. If it ## isn't in the MANIFEST, it doesn't go into the release. We also update ## the version information in Makefile.global.in to remove the prerelease ## designation and update all timestamps to the date the release is made. ## If RELEASENUMBER is set, it is a beta release or a release candidate. release: ChangeLog rm -rf $(TARDIR) rm -f inn*.tar.gz mkdir $(TARDIR) for d in `$(SED) $(DISTDIRS) MANIFEST` ; do $(MKDIR_P) $$d ; done for f in `$(SED) $(DISTFILES) MANIFEST` ; do \ cp $$f $(TARDIR)/$$f || exit 1 ; \ done if [ "x$(RELEASENUMBER)" != "x" ] ; then \ cp README.$(RELEASEEXTENSION) $(TARDIR)/ ; \ $(SED) 's/= prerelease/= $(RELEASENUMBER) version/' \ Makefile.global.in > $(TARDIR)/Makefile.global.in ; \ else \ $(SED) 's/= prerelease/=/' Makefile.global.in \ > $(TARDIR)/Makefile.global.in ; \ fi cp ChangeLog $(TARDIR)/ find $(TARDIR) -type f -print | xargs touch -t `date +%m%d%H%M.%S` tar cf $(TARFILE) $(TARDIR) $(GZIP) -9 $(TARFILE) ## Generate the ChangeLog using support/mkchangelog. This should only be ## run by a maintainer since it depends on svn log working and also ## requires svn2cl be available somewhere. ChangeLog: $(PERL) support/mkchangelog ## Check the MANIFEST against the files present in the current tree, ## building a list with find and running diff between the lists. check-manifest: $(SED) -e 1,2d -e 's/ .*//' MANIFEST > LIST.manifest $(PERL) support/mkmanifest > LIST.real diff -u LIST.manifest LIST.real ## Make a snapshot. This is like making a release, except that we don't do ## the ChangeLog thing and we don't change the version number. We also ## assume that SNAPSHOT has been set to the appropriate current branch. snapshot: rm -rf $(SNAPDIR) rm -f inn*.tar.gz mkdir $(SNAPDIR) set -e ; for d in `$(SED) $(SNAPDIRS) MANIFEST` ; do $(MKDIR_P) $$d ; done set -e ; for f in `$(SED) $(DISTFILES) MANIFEST` ; do \ cp $$f $(SNAPDIR)/$$f ; \ done cp README.snapshot $(SNAPDIR)/ $(SED) 's/= prerelease/= $(SNAPDATE) snapshot/' \ Makefile.global.in > $(SNAPDIR)/Makefile.global.in find $(SNAPDIR) -type f -print | xargs touch -t `date +%m%d%H%M.%S` tar cf $(SNAPDIR).tar $(SNAPDIR) $(GZIP) -9 $(SNAPDIR).tar inn-2.6.0/configure.ac0000644000175200017520000006236112575023702014216 0ustar iuliusiuliusdnl Process this file with Autoconf to produce a configure script. dnl $Id: configure.ac 9839 2015-05-02 15:38:55Z iulius $ dnl dnl Please try to follow GNU conventions and Autoconf manual conventions as dnl much as possible in this file so that any macros we develop can be easily dnl contributed to the macro archive and possibly rolled into future versions dnl of Autoconf so that we can stop supporting them. This means, for example, dnl that code excerpts should probably follow the GNU coding standards rather dnl than INN's. dnl dnl If a check is any way non-trivial, please package it up in a macro with dnl AC_DEFUN and put that macro into a separate file under m4. Please also dnl put any long code blocks into a separate macro rather than in-line in the dnl test macro; this will make quoting issues much easier. See the existing dnl tests for details on how to do this. dnl dnl Try to do as much with AC_DEFINE and as little with AC_SUBST as is dnl reasonable; obviously, makefile things like library paths and so forth and dnl paths to programs have to use AC_SUBST, but any compile-time parameters dnl are easier to handle with AC_DEFINE. (And AC_SUBST is slower.) dnl dnl And remember: If you don't have any alternative available if your check dnl for something fails, and there's no purpose served in aborting configure dnl instead of the compile if what you're checking for is missing, don't dnl bother checking for it. Compile-time errors often produce a lot more dnl useful information for someone debugging a problem than configure-time dnl errors. AC_INIT([INN], [2.6.0], [inn-workers@lists.isc.org], [inn], [https://www.isc.org/downloads/projects/]) AC_PREREQ([2.64]) AC_REVISION([$Revision: 9839 $]) AC_CONFIG_AUX_DIR([support]) dnl Lots of our macros are stored in separate files for ease of maintenance. m4_include([m4/aux-libs.m4]) m4_include([m4/bdb.m4]) m4_include([m4/cc-c-o.m4]) m4_include([m4/cc-flags.m4]) m4_include([m4/compress.m4]) m4_include([m4/inet-ntoa.m4]) m4_include([m4/iov-max.m4]) m4_include([m4/krb5-config.m4]) m4_include([m4/krb5.m4]) m4_include([m4/large-fpos.m4]) m4_include([m4/lib-depends.m4]) m4_include([m4/lib-helper.m4]) m4_include([m4/lib-pathname.m4]) m4_include([m4/libtool.m4]) m4_include([m4/lt~obsolete.m4]) m4_include([m4/ltoptions.m4]) m4_include([m4/ltsugar.m4]) m4_include([m4/ltversion.m4]) m4_include([m4/mmap.m4]) m4_include([m4/modes.m4]) m4_include([m4/openssl.m4]) m4_include([m4/pam-const.m4]) m4_include([m4/paths.m4]) m4_include([m4/perl.m4]) m4_include([m4/prog-ensure.m4]) m4_include([m4/python.m4]) m4_include([m4/sasl.m4]) m4_include([m4/sendfd.m4]) m4_include([m4/sendmail.m4]) m4_include([m4/snprintf.m4]) m4_include([m4/socket-unix.m4]) m4_include([m4/socket.m4]) m4_include([m4/syslog.m4]) m4_include([m4/users.m4]) m4_include([m4/vamacros.m4]) m4_include([m4/zlib.m4]) dnl By default, INN installs into its own independent tree. Support for FHS dnl is not yet implemented; once it is, this will need to become conditional dnl on that support being disabled. AC_PREFIX_DEFAULT([/usr/local/news]) dnl Make sure $prefix is set so that we can use it internally. test x"$prefix" = xNONE && prefix="$ac_default_prefix" dnl A few tests need to happen before any of the libtool tests. We dnl therefore lift them up to the top of the file. (This is probably no dnl longer needed now that we're running LT_INIT unconditionally, but dnl we'll leave it alone until we switch to Automake.) AC_PROG_CC AC_USE_SYSTEM_EXTENSIONS AC_SEARCH_LIBS([strerror], [cposix]) INN_PROG_CC_C_O AC_CANONICAL_HOST dnl Configure libtool and set the variables that we used to use when we dnl supported builds without libtool. LT_INIT EXTLIB='la' EXTOBJ='lo' LIBTOOL='$(top)/libtool' LIBTOOLCC='$(top)/libtool --mode=compile' LIBTOOLINST='$(top)/libtool --mode=install' LIBTOOLLD='$(top)/libtool --mode=link' CCOUTPUT='-c -o $@ $<' AC_SUBST(EXTLIB) AC_SUBST(EXTOBJ) AC_SUBST(LIBTOOLCC) AC_SUBST(LIBTOOLINST) AC_SUBST(LIBTOOLLD) AC_SUBST(CCOUTPUT) dnl Command-line options. Many of these macros just do the command-line dnl component of a set of macros that have additional parts called later. INN_ARG_PATHS INN_ARG_SYSLOG INN_ARG_USERS INN_ARG_MODES INN_ARG_COMPRESS INN_ARG_SENDMAIL dnl innbind by default only allows ports 119, 433 and 563 below 1024; if the user dnl wants to use some other port as well, they must use this option. AC_ARG_WITH([innd-port], [AS_HELP_STRING([--with-innd-port=PORT], [Additional low-numbered port for innbind])], [AC_DEFINE_UNQUOTED([INND_PORT], [$with_innd_port], [Additional permitted low-numbered port for innbind.])]) dnl Whether to use a different (less tested) history algorithm. This will dnl eventually be a runtime option rather than a compile-time option as soon dnl as the #ifdefs can be untangled and put into the history API. The output dnl variable is needed for now to support news.daily. DO_DBZ_TAGGED_HASH=DONT inn_enable_tagged_hash=no AC_ARG_ENABLE([tagged-hash], [AS_HELP_STRING([--enable-tagged-hash], [Use tagged hash table for history])], [if test "x$enableval" = xyes ; then DO_DBZ_TAGGED_HASH=DO inn_enable_tagged_hash=yes AC_DEFINE([DO_TAGGED_HASH], [1], [Define to use tagged hash for the history file.]) fi]) AC_SUBST([DO_DBZ_TAGGED_HASH]) dnl Whether to enable the keyword generation code in innd. Use of this code dnl requires a regular expression library, which is checked for later on. inn_enable_keywords=0 AC_ARG_ENABLE([keywords], [AS_HELP_STRING([--enable-keywords], [Automatic innd keyword generation support])], if test x"$enableval" = xyes ; then inn_enable_keywords=1 fi) AC_DEFINE_UNQUOTED([DO_KEYWORDS], [$inn_enable_keywords], [Define to 1 to compile in support for keyword generation code.]) dnl Whether to use the OS flags to enable large file support. Ideally this dnl should just always be turned on if possible and the various parts of INN dnl that read off_t's from disk should adjust somehow to the size, but INN dnl isn't there yet. Currently tagged hash doesn't work with large file dnl support due to assumptions about the size of off_t. inn_enable_largefiles=no AC_ARG_ENABLE([largefiles], [AS_HELP_STRING([--enable-largefiles], [Support for files larger than 2GB @<:@default=no@:>@])], if test x"$enableval" = xyes ; then inn_enable_largefiles=yes fi) if test x"$inn_enable_tagged_hash" = xyes \ && test x"$inn_enable_largefiles" = xyes ; then AC_MSG_ERROR([--enable-tagged-hash conflicts with --enable-largefiles.]) fi dnl Checks for embedded interpreters. INN_ARG_PERL INN_ARG_PYTHON dnl Set some configuration file defaults from the machine hostname. HOSTNAME=`hostname 2> /dev/null || uname -n` AC_SUBST(HOSTNAME) dnl Checks for programs. AC_PROG_GCC_TRADITIONAL AC_PROG_LEX AC_PROG_LN_S AC_PROG_MAKE_SET AC_PROG_MKDIR_P AC_PROG_RANLIB AC_PROG_YACC dnl On MacOS X Server, -traditional-cpp is needed for gcc for compiling as dnl well as preprocessing according to Miro Jurisic . case "$CPP" in *-traditional-cpp*) CFLAGS="-traditional-cpp $CFLAGS" ;; esac case "$host" in dnl When building on Linux, we need to compile with _GNU_SOURCE to get the dnl right definitions of pread and pwrite and to get some other functions dnl (like asprintf) that we use. *linux*) AC_DEFINE([_GNU_SOURCE], [1], [Define if compiling on Linux to get prototypes for some functions.]) ;; dnl HP-UX's native compiler needs a special flag to turn on ANSI, and needs dnl -g on link as well as compile for debugging to work. *hpux*) if test x"$GCC" != xyes ; then CFLAGS="$CFLAGS -Ae" case "$CFLAGS" in *-g*) LDFLAGS="$LDFLAGS -g" ;; esac fi ;; dnl Mac OS X needed "-multiply_defined suppress" before this linker flag became dnl obsolete (darwin13 is Mavericks, OS X v10.9). *-darwin[[0-9]].*|*-darwin1[[0-2]].*) LDFLAGS="$LDFLAGS -multiply_defined suppress" ;; dnl From Boyd Gerber , needed in some cases to compile dnl the bison-generated parser for innfeed.conf. *UnixWare*|*unixware*|*-sco3*) if test x"$GCC" != xyes ; then CFLAGS="$CFLAGS -Kalloca" fi esac dnl Checks for pathnames. If AC_PROG_* does not manage to find a working dnl program, INN_PATH_PROG_ENSURE will output an error. AC_ARG_VAR([AWK], [Location of awk]) AC_ARG_VAR([EGREP], [Location of egrep]) AC_ARG_VAR([SED], [Location of sed]) AC_ARG_VAR([SORT], [Location of sort]) AC_ARG_VAR([SSLBIN], [Location of openssl]) AC_ARG_VAR([UUSTAT], [Location of uustat]) AC_ARG_VAR([UUX], [Location of uux]) AC_PROG_AWK AC_PROG_EGREP AC_PROG_SED INN_PATH_PROG_ENSURE([AWK], [awk]) INN_PATH_PROG_ENSURE([EGREP], [egrep]) INN_PATH_PROG_ENSURE([SED], [sed]) INN_PATH_PROG_ENSURE([SORT], [sort]) AC_PATH_PROGS([SSLBIN], [openssl], [openssl]) AC_PATH_PROGS([UUSTAT], [uustat]) AC_PATH_PROGS([UUX], [uux], [uux]) INN_PATH_COMPRESS INN_PATH_SENDMAIL dnl Look for PGP 5.0's pgpv, then pgp, then pgpgpg (not sure why anyone would dnl have pgpgpg and not gpgv, but it doesn't hurt). Separately look for dnl GnuPG (which we prefer). DO_PGPVERIFY=true AC_ARG_VAR([GPGV], [Location of GnuPG gpgv program]) AC_ARG_VAR([PGP], [Location of PGP verification program]) AC_PATH_PROGS([GPGV], [gpgv2 gpgv]) AC_PATH_PROGS([PGP], [pgpv pgp pgpgpg]) if test -z "$PGP" && test -z "$GPGV" ; then DO_PGPVERIFY=false fi AC_SUBST([DO_PGPVERIFY]) dnl Look for a program that takes an ftp URL as a command line argument and dnl retrieves the file to the current directory. Shame we can't also use dnl lynx -source; it only writes to stdout. ncftp as of version 3 doesn't dnl support this any more (it comes with ncftpget instead), but if someone dnl has ncftp and not ncftpget they have an earlier version. AC_PATH_PROGS([PATH_GETFTP], [wget ncftpget ncftp], [$bindir/simpleftp]) dnl Checks for libraries. Use AC_SEARCH_LIBS where possible to avoid dnl adding libraries when the function is found in libc, and use dnl INN_SEARCH_AUX_LIBS for libraries that are only used by particular dnl subsections of INN. dnl Check for setproctitle in libc first, then libutil if not found there. dnl We have a replacement function if we can't find it, and then we also need dnl to check for pstat. AC_SEARCH_LIBS([setproctitle], [util], [AC_DEFINE([HAVE_SETPROCTITLE], [1], [Define if you have the setproctitle function.])], [AC_LIBOBJ([setproctitle]) AC_CHECK_FUNCS([pstat])]) dnl The rat's nest of networking libraries. The common cases are not to dnl need any extra libraries, or to need -lsocket -lnsl. We need to avoid dnl linking with libnsl unless we need it, though, since on some OSes where dnl it isn't necessary it will totally break networking. Unisys also dnl includes gethostbyname in libsocket but needs libnsl for socket(). AC_SEARCH_LIBS([gethostbyname], [nsl]) AC_SEARCH_LIBS([socket], [socket], [], [AC_CHECK_LIB([nsl], [socket], [LIBS="$LIBS -lsocket -lnsl"], [], [-lsocket])]) dnl Check for inet_aton. We have our own, but on Solaris the version in dnl libresolv is more lenient in ways that Solaris's internal DNS resolution dnl code requires, so if we use our own *and* link with libresolv (which may dnl be forced by Perl) DNS resolution fails. AC_SEARCH_LIBS([inet_aton], [resolv]) dnl Search for various additional libraries used by portions of INN. INN_SEARCH_AUX_LIBS([crypt], [crypt], [CRYPT_LIBS]) INN_SEARCH_AUX_LIBS([getspnam], [shadow], [SHADOW_LIBS]) dnl IRIX has a PAM library with the right symbols but no header files suitable dnl for use with it, so we have to check the header files first and then only dnl if one is found do we check for the library. inn_check_pam=1 AC_CHECK_HEADERS([security/pam_appl.h], [], [AC_CHECK_HEADERS([pam/pam_appl.h], [], [inn_check_pam=0])]) if test x"$inn_check_pam" = x1; then INN_SEARCH_AUX_LIBS([pam_start], [pam], [PAM_LIBS], [AC_DEFINE([HAVE_PAM], [1], [Define if you have PAM.])]) fi INN_HEADER_PAM_CONST dnl If keyword generation support was requested, check for the appropriate dnl libraries. if test x"$inn_enable_keywords" = x1 ; then INN_SEARCH_AUX_LIBS([regexec], [regex], [REGEX_LIBS], [], [AC_MSG_ERROR([no usable regular expression library found])]) fi dnl Handle optional libraries and probing for their locations and component dnl libraries if needed. Support for zlib is handled later. INN_LIB_BDB_OPTIONAL INN_LIB_KRB5_OPTIONAL INN_LIB_OPENSSL_OPTIONAL INN_LIB_SASL_OPTIONAL dnl If Kerberos is found, define KRB5_AUTH to auth_krb5 so as to build dnl that program. In case neither et/com_err.h nor kerberosv5/com_err.h dnl nor com_err.h can be found and --with-krb5 is not given, do not define dnl KRB5_AUTH and clear the other variables set for Kerberos support. AS_IF([test x"$inn_use_KRB5" = xtrue], [AC_CHECK_HEADERS([et/com_err.h], [KRB5_AUTH=auth_krb5], [AC_CHECK_HEADERS([kerberosv5/com_err.h], [KRB5_AUTH=auth_krb5], [AC_CHECK_HEADERS([com_err.h], [KRB5_AUTH=auth_krb5], [AS_IF([test x"$with_krb5" = x], [KRB5_CPPFLAGS= KRB5_LDFLAGS= KRB5_LIBS=], [AC_MSG_ERROR([cannot find usable com_err header])])])])])]) AC_SUBST([KRB5_AUTH]) dnl If Berkeley DB is found, check the presence of its header. dnl Also, do not build with zlib support unless Berkeley DB is enabled. AS_IF([test x"$inn_use_BDB" = xtrue], [inn_BDB_incroot= inn_BDB_header_found= AS_IF([test x"$inn_BDB_includedir" != x], [inn_BDB_incroot="$inn_BDB_includedir"], [AS_IF([test x"$inn_BDB_root" != x], [inn_BDB_incroot="${inn_BDB_root}/include"])]) AS_IF([test x"$inn_BDB_incroot" = x], [AC_CHECK_HEADERS([db.h], [inn_BDB_header_found=yes], [inn_BDB_header_found=no])], [AS_IF([test -f "${inn_BDB_incroot}/db.h"], [inn_BDB_header_found=yes AC_DEFINE([HAVE_DB_H], [1], [Define if you have the header file.])], [inn_BDB_header_found=no])]) AS_IF([test x"${inn_BDB_header_found}" = xyes], [INN_LIB_BDB_NDBM INN_LIB_ZLIB_OPTIONAL], [AS_IF([test x"$with_bdb" = x], [BDB_CPPFLAGS= BDB_LDFLAGS= BDB_LIBS=], [AC_MSG_ERROR([cannot find usable Berkeley DB header])])])]) dnl The dbm libraries are a special case. If we're building with Berkeley DB, dnl just use the ndbm support provided by it. AS_IF([test x"$inn_cv_lib_bdb_ndbm" != xyes], [INN_SEARCH_AUX_LIBS([dbm_open], [ndbm dbm gdbm "gdbm_compat -lgdbm"], [DBM_LIBS], [AC_DEFINE([HAVE_DBM], [1], [Define if you have a dbm library.])]) DBM_CPPFLAGS=], [DBM_CPPFLAGS="$BDB_CPPFLAGS" DBM_LIBS="$BDB_LDFLAGS $BDB_LIBS" AC_SUBST([DBM_LIBS])]) AC_SUBST([DBM_CPPFLAGS]) dnl If zlib support is found, check the presence of its header. AS_IF([test x"$inn_use_ZLIB" = xtrue], [inn_ZLIB_incroot= inn_ZLIB_header_found= AS_IF([test x"$inn_ZLIB_includedir" != x], [inn_ZLIB_incroot="$inn_ZLIB_includedir"], [AS_IF([test x"$inn_ZLIB_root" != x], [inn_ZLIB_incroot="${inn_ZLIB_root}/include"])]) AS_IF([test x"$inn_ZLIB_incroot" = x], [AC_CHECK_HEADERS([zlib.h], [inn_ZLIB_header_found=yes], [inn_ZLIB_header_found=no])], [AS_IF([test -f "${inn_ZLIB_incroot}/zlib.h"], [inn_ZLIB_header_found=yes AC_DEFINE([HAVE_ZLIB_H], [1], [Define if you have the header file.])], [inn_ZLIB_header_found=no])]) AS_IF([test x"${inn_ZLIB_header_found}" = xyes], [], [AS_IF([test x"$with_zlib" = x], [ZLIB_CPPFLAGS= ZLIB_LDFLAGS= ZLIB_LIBS=], [AC_MSG_ERROR([cannot find usable zlib header])])])]) dnl If OpenSSL support is found, check the presence of its header. AS_IF([test x"$inn_use_OPENSSL" = xtrue], [inn_OPENSSL_incroot= inn_OPENSSL_header_found= AS_IF([test x"$inn_OPENSSL_includedir" != x], [inn_OPENSSL_incroot="$inn_OPENSSL_includedir"], [AS_IF([test x"$inn_OPENSSL_root" != x], [inn_OPENSSL_incroot="${inn_OPENSSL_root}/include"])]) AS_IF([test x"$inn_OPENSSL_incroot" = x], [AC_CHECK_HEADERS([openssl/ssl.h], [inn_OPENSSL_header_found=yes], [inn_OPENSSL_header_found=no])], [AS_IF([test -f "${inn_OPENSSL_incroot}/openssl/ssl.h"], [inn_OPENSSL_header_found=yes AC_DEFINE([HAVE_OPENSSL_SSL_H], [1], [Define if you have the header file.])], [inn_OPENSSL_header_found=no])]) AS_IF([test x"${inn_OPENSSL_header_found}" = xyes], [], [AS_IF([test x"$with_openssl" = x], [OPENSSL_CPPFLAGS= OPENSSL_LDFLAGS= OPENSSL_LIBS=], [AC_MSG_ERROR([cannot find usable OpenSSL header])])])]) dnl If SASL support is found, check the presence of its header. AS_IF([test x"$inn_use_SASL" = xtrue], [inn_SASL_incroot= inn_SASL_header_found= AS_IF([test x"$inn_SASL_includedir" != x], [inn_SASL_incroot="$inn_SASL_includedir"], [AS_IF([test x"$inn_SASL_root" != x], [inn_SASL_incroot="${inn_SASL_root}/include"])]) AS_IF([test x"$inn_SASL_incroot" = x], [AC_CHECK_HEADERS([sasl/sasl.h], [inn_SASL_header_found=yes], [inn_SASL_header_found=no])], [AS_IF([test -f "${inn_SASL_incroot}/sasl/sasl.h"], [inn_SASL_header_found=yes AC_DEFINE([HAVE_SASL_SASL_H], [1], [Define if you have the header file.])], [inn_SASL_header_found=no])]) AS_IF([test x"${inn_SASL_header_found}" = xyes], [], [AS_IF([test x"$with_sasl" = x], [SASL_CPPFLAGS= SASL_LDFLAGS= SASL_LIBS=], [AC_MSG_ERROR([cannot find usable Cyrus SASL header])])])]) dnl If configuring with large file support, determine the right flags to dnl use based on the platform. if test x"$inn_enable_largefiles" = xyes ; then AC_SYS_LARGEFILE AC_DEFINE([DO_LARGEFILES], [1], [Define to use large files.]) fi dnl Special checks for header files. AC_HEADER_STDBOOL dnl Generic checks for header files. AC_CHECK_HEADERS([crypt.h inttypes.h limits.h \ stdint.h sys/bitypes.h sys/filio.h sys/loadavg.h \ sys/select.h sys/time.h sys/uio.h syslog.h unistd.h]) dnl Some Linux systems have db1/ndbm.h instead of ndbm.h. Others have dnl gdbm/ndbm.h or gdbm-ndbm.h. Detecting the last two ones is not dnl straight-forward because Autoconf defines the same preprocessor name dnl and the same shell variable for them; we therefore have to make sure dnl the cache is not populated, and to define HAVE_GDBM_SLASH_NDBM_H and dnl HAVE_GDBM_HYPHEN_NDBM_H separately. AC_CHECK_HEADERS([ndbm.h db1/ndbm.h], [break]) AS_UNSET([ac_cv_header_gdbm_ndbm_h]) AC_CHECK_HEADER([gdbm/ndbm.h]) AS_IF([test x"$ac_cv_header_gdbm_ndbm_h" != xno], [AC_DEFINE([HAVE_GDBM_SLASH_NDBM_H], [1], [Define if you have the header file.])], [AS_UNSET([ac_cv_header_gdbm_ndbm_h]) AC_CHECK_HEADER([gdbm-ndbm.h]) AS_IF([test x"$ac_cv_header_gdbm_ndbm_h" != xno], [AC_DEFINE([HAVE_GDBM_HYPHEN_NDBM_H], [1], [Define if you have the header file.])])]) dnl Check for whether various symbols are declared. AC_CHECK_DECLS([pread, pwrite, snprintf, setproctitle, strlcat, strlcpy, vsnprintf]) AC_CHECK_DECLS([h_errno], [], [], [#include ]) AC_CHECK_DECLS([inet_aton, inet_ntoa], [], [], [#include #include #include ]) AC_CHECK_DECLS([altzone], [], [], [#include ]) dnl Checks for compiler characteristics. AC_C_BIGENDIAN AC_C_CONST INN_C_C99_VAMACROS INN_C_GNU_VAMACROS dnl Checks for structures. AC_STRUCT_TIMEZONE AC_CHECK_MEMBERS([struct stat.st_blksize]) AC_CHECK_MEMBERS([struct tm.tm_gmtoff]) AC_CHECK_MEMBERS([struct sockaddr.sa_len], [], [], [#include #include ]) dnl Checks for types. AC_TYPE_INT8_T AC_TYPE_INT16_T AC_TYPE_INT32_T AC_TYPE_UINT8_T AC_TYPE_UINT16_T AC_TYPE_UINT32_T AC_TYPE_LONG_LONG_INT AC_TYPE_OFF_T AC_TYPE_PID_T AC_TYPE_SIZE_T AC_TYPE_SSIZE_T AC_FUNC_GETGROUPS AC_TYPE_UID_T AC_CHECK_TYPES([sig_atomic_t], [], [], [#include #include ]) AC_CHECK_TYPES([socklen_t], [], [], [#include #include ]) AC_CHECK_TYPES([union semun], [], [], [#include #include #include ]) dnl Enable IPv6 unconditionally if available. AC_CHECK_TYPES([struct sockaddr_in6], [AC_DEFINE([HAVE_INET6], [1], [Define to 1 if IPv6 library interfaces are available.])], [], [#include #include ]) dnl See if struct sockaddr_storage is available, and if so, how its members dnl are named. AC_CHECK_TYPES([struct sockaddr_storage], [AC_CHECK_MEMBERS([struct sockaddr_storage.ss_family], [], [], [#include #include ])], [], [#include #include ]) dnl Checks for macros. INN_MACRO_IN6_ARE_ADDR_EQUAL INN_MACRO_IOV_MAX INN_MACRO_SA_LEN INN_MACRO_SUN_LEN dnl Checks for library functions. AC_FUNC_ALLOCA AC_FUNC_MEMCMP INN_FUNC_GETADDRINFO_ADDRCONFIG INN_FUNC_INET_NTOA INN_FUNC_SNPRINTF dnl Check for various other functions. AC_CHECK_FUNCS(getloadavg getrusage getspnam setbuffer sigaction \ setgroups setrlimit setsid socketpair strncasecmp \ sysconf) dnl Find a way to get the file descriptor limit. AC_CHECK_FUNCS([getrlimit getdtablesize ulimit], [break]) dnl Probe for the appropriate stat functions. AC_CHECK_FUNCS([statvfs], [], [AC_CHECK_FUNCS([statfs]) AC_CHECK_HEADERS([sys/vfs.h sys/param.h]) AC_CHECK_HEADERS(sys/mount.h, [], [], [#ifdef HAVE_SYS_PARAM_H # include #endif ]) ]) dnl If we can't find any of the following, we have replacements for them. AC_REPLACE_FUNCS(asprintf getaddrinfo getnameinfo getpagesize \ inet_aton inet_ntop mkstemp pread pwrite reallocarray \ setenv seteuid strcasecmp \ strlcat strlcpy strspn symlink) dnl Probe for fseeko and ftello, which take off_t instead of int. if test x"$inn_enable_largefiles" = xyes ; then AC_FUNC_FSEEKO if test x"$ac_cv_sys_largefile_source" = xunknown ; then INN_TYPE_FPOS_T_LARGE AC_CHECK_DECLS([fseeko, ftello]) AC_LIBOBJ([fseeko]) AC_LIBOBJ([ftello]) fi else AC_CHECK_DECLS([fseeko]) fi dnl Probe for mmap properties. INN_FUNC_MMAP( [AC_CHECK_FUNCS([madvise]) INN_FUNC_MMAP_SEES_WRITES INN_FUNC_MMAP_NEEDS_MSYNC INN_FUNC_MSYNC_ARGS]) dnl Probes for system characteristics. INN_SYS_STREAMS_SENDFD INN_SYS_UNIX_SOCKETS INN_LOG_FACILITY dnl Clean up our LIBS, just for grins. LIBS=`echo "$LIBS" | sed 's/^ *//' | sed 's/ */ /g' | sed 's/ *$//'` dnl Configure our output files. Try to keep this as limited as possible, dnl since it directly affects how long it takes configure to complete. AC_CONFIG_FILES([Makefile.global include/inn/paths.h]) AC_CONFIG_FILES([scripts/innshellvars scripts/innshellvars.tcl]) AC_CONFIG_FILES([perl/INN/Config.pm scripts/innshellvars.pl]) AC_CONFIG_FILES([support/fixconfig], [chmod +x support/fixconfig]) AC_CONFIG_FILES([support/fixscript], [chmod +x support/fixscript]) dnl Include some additional files at the beginning of config.h. AH_TOP([#include "inn/defines.h" #include "inn/options.h"]) dnl Most configuration information goes here. AC_CONFIG_HEADER([include/config.h]) dnl Generate the configure output. AC_OUTPUT dnl Print out some additional information on what to check. cat < /dev/null ; then : else cat < (to only handle files). ** ** Files that can't be unlinked because they didn't exist are considered ** okay. Any error condition results in exiting with non-zero exit ** status. Input lines in the form @...@ are taken to be storage API ** tokens. Input filenames should be fully qualified. For maximum ** efficiency, input filenames should be sorted; fastrm will cd into each ** directory to avoid additional directory lookups when removing a lot of ** files in a single directory. */ #include "config.h" #include "clibrary.h" #include #include #include #include #include #include "inn/innconf.h" #include "inn/messages.h" #include "inn/qio.h" #include "inn/libinn.h" #include "inn/storage.h" /* We reject any path names longer than this. */ #define MAX_DIR_LEN 2048 /* Data structure for a list of files in a single directory. */ typedef struct filelist { int count; int size; char *dir; char **files; } filelist; /* All relative paths are relative to this directory. */ static char *base_dir = NULL; /* The absolute path of the current working directory. */ static char current_dir[MAX_DIR_LEN]; /* The prefix for the files that we're currently working with. We sometimes also use this as working space for forming file names to remove, so give ourselves a bit of additional leeway just in case. */ static char prefix_dir[MAX_DIR_LEN * 2]; static int prefix_len; /* Some threshold values that govern the optimizations that we are willing to perform. chdir_threshold determines how many files to be removed we want in a directory before we chdir to that directory. sort_threshold determines how many files must be in a directory before we use readdir to remove them in order. relative_threshold determines how many levels of "../" we're willing to try to use to move to the next directory rather than just calling chdir with the new absolute path. */ static int chdir_threshold = 3; static int relative_threshold = 0; static int sort_threshold = 0; /* True if we should only print what we would do, not actually do it. */ static bool debug_only = false; /* A string used for constructing relative paths. */ static const char dotdots[] = "../../../../"; /* The number of errors encountered, used to determine exit status. */ static int error_count = 0; /* Whether the storage manager has been initialized. */ static bool sm_initialized = false; /* True if unlink may be able to remove directories. */ static bool unlink_dangerous = false; /* ** Sorting predicate for qsort and bsearch. */ static int file_compare(const void *a, const void *b) { const char *f1, *f2; f1 = *((const char *const *) a); f2 = *((const char *const *) b); return strcmp(f1, f2); } /* ** Create a new filelist. */ static filelist * filelist_new(char *dir) { filelist *new; new = xmalloc(sizeof(filelist)); new->count = 0; new->size = 0; new->dir = dir; new->files = NULL; return new; } /* ** Insert a file name into a list of files (unsorted). */ static void filelist_insert(filelist *list, char *name) { if (list->count == list->size) { list->size = (list->size == 0) ? 16 : list->size * 2; list->files = xrealloc(list->files, list->size * sizeof(char *)); } list->files[list->count++] = xstrdup(name); } /* ** Find a file name in a sorted list of files. */ static char * filelist_lookup(filelist *list, const char *name) { char **p; p = bsearch(&name, list->files, list->count, sizeof(char *), file_compare); return (p == NULL ? NULL : *p); } /* ** Empty a list of files, freeing all of the names but keeping the ** structure intact. */ static void filelist_empty(filelist *list) { int i; if (list->files == NULL) return; for (i = 0; i < list->count; i++) free(list->files[i]); list->count = 0; } /* ** Free a list of files. */ static void filelist_free(filelist *list) { filelist_empty(list); if (list->files != NULL) free(list->files); if (list->dir != NULL) free(list->dir); free(list); } /* ** Exit handler for die. Shut down the storage manager before exiting. */ static int sm_cleanup(void) { SMshutdown(); return 1; } /* ** Initialize the storage manager. This includes parsing inn.conf, which ** fastrm doesn't need for any other purpose. */ static void sm_initialize(void) { bool value; if (!innconf_read(NULL)) exit(1); value = true; if (!SMsetup(SM_RDWR, &value) || !SMsetup(SM_PREOPEN, &value)) die("can't set up storage manager"); if (!SMinit()) die("can't initialize storage manager: %s", SMerrorstr); sm_initialized = true; message_fatal_cleanup = sm_cleanup; } /* ** Get a line from a given QIO stream, returning a pointer to it. Warn ** about and then skip lines that are too long. Returns NULL at EOF or on ** an error. */ static char * get_line(QIOSTATE *qp) { static int count; char *p; p = QIOread(qp); count++; while (QIOtoolong(qp) || (p != NULL && strlen(p) >= MAX_DIR_LEN)) { warn("line %d too long", count); error_count++; p = QIOread(qp); } if (p == NULL) { if (QIOerror(qp)) { syswarn("read error"); error_count++; } return NULL; } return p; } /* ** Read lines from stdin (including the first that may have been there ** from our last time in) until we reach EOF or until we get a line that ** names a file not in the same directory as the previous lot. Remember ** the file names in the directory we're examining and return the list. */ static filelist * process_line(QIOSTATE *qp, int *queued, int *deleted) { static char *line = NULL; filelist *list = NULL; char *p; char *dir = NULL; int dlen = -1; *queued = 0; *deleted = 0; if (line == NULL) line = get_line(qp); for (; line != NULL; line = get_line(qp)) { if (IsToken(line)) { (*deleted)++; if (debug_only) { printf("Token %s\n", line); continue; } if (!sm_initialized) sm_initialize(); if (!SMcancel(TextToToken(line))) if (SMerrno != SMERR_NOENT && SMerrno != SMERR_UNINIT) { warn("can't cancel %s", line); error_count++; } } else { if (list == NULL) { p = strrchr(line, '/'); if (p != NULL) { *p++ = '\0'; dlen = strlen(line); dir = xstrdup(line); } else { dlen = -1; dir = NULL; } list = filelist_new(dir); } else { if ((dlen < 0 && strchr(line, '/')) || (dlen >= 0 && (line[dlen] != '/' || strchr(line + dlen + 1, '/') || strncmp(dir, line, dlen)))) return list; } filelist_insert(list, line + dlen + 1); (*queued)++; } } return list; } /* ** Copy n leading segments of a path. */ static void copy_segments(char *to, const char *from, int n) { char c; for (c = *from++; c != '\0'; c = *from++) { if (c == '/' && --n <= 0) break; *to++ = c; } *to = '\0'; } /* ** Return the count of path segments in a file name (the number of ** slashes). */ static int slashcount(char *name) { int i; for (i = 0; *name != '\0'; name++) if (*name == '/') i++; return i; } /* ** Unlink a file, reporting errors if the unlink fails for a reason other ** than the file not existing doesn't exist. Be careful to avoid unlinking ** a directory if unlink_dangerous is true. */ static void unlink_file(const char *file) { struct stat st; /* On some systems, unlink will remove directories if used by root. If we're running as root, unlink_dangerous will be set, and we need to make sure that the file isn't a directory first. */ if (unlink_dangerous) { if (stat(file, &st) < 0) { if (errno != ENOENT) { if (*file == '/') syswarn("can't stat %s", file); else syswarn("can't stat %s in %s", file, current_dir); error_count++; } return; } if (S_ISDIR(st.st_mode)) { if (*file == '/') syswarn("%s is a directory", file); else syswarn("%s in %s is a directory", file, current_dir); error_count++; return; } } if (debug_only) { if (*file != '/') printf("%s / ", current_dir); printf("%s\n", file); return; } if (unlink(file) < 0 && errno != ENOENT) { if (*file == '/') syswarn("can't unlink %s", file); else syswarn("can't unlink %s in %s", file, current_dir); } } /* ** A wrapper around chdir that dies if chdir fails for a reason other than ** the directory not existing, returns false if the directory doesn't ** exist (reporting an error), and otherwise returns true. It also checks ** to make sure that filecount is larger than chdir_threshold, and if it ** isn't it instead just sets prefix_dir and prefix_len to point to the new ** directory without changing the working directory. */ static bool chdir_checked(const char *path, int filecount) { if (filecount < chdir_threshold) { strlcpy(prefix_dir, path, sizeof(prefix_dir)); prefix_len = strlen(path); } else { prefix_len = 0; if (chdir(path) < 0) { if (errno != ENOENT) sysdie("can't chdir from %s to %s", current_dir, path); else { syswarn("can't chdir from %s to %s", current_dir, path); return false; } } } return true; } /* ** Set our environment (process working directory, and global vars) to ** reflect a change of directory to dir (relative to base_dir if dir is not ** an absolute path). We're likely to want to do different things ** depending on the amount of work to do in dir, so we also take the number ** of files to remove in dir as the second argument. Return false if the ** directory doesn't exist (and therefore all files in it have already been ** removed; otherwise, return true. */ static bool setup_dir(char *dir, int filecount) { char *p, *q, *absolute; char path[MAX_DIR_LEN]; int base_depth, depth; /* Set absolute to the absolute path to the new directory. */ if (dir == NULL) absolute = base_dir; else if (*dir == '/') absolute = dir; else if (*dir == '\0') { strlcpy(path, "/", sizeof(path)); absolute = path; } else { /* Strip off leading "./". */ while (dir[0] == '.' && dir[1] == '/') for (dir += 2; *dir == '/'; dir++) ; /* Handle any leading "../", but only up to the number of segments in base_dir. */ base_depth = slashcount(base_dir); while (base_depth > 0 && strncmp(dir, "../", 3) == 0) for (base_depth--, dir += 3; *dir == '/'; dir++) ; if (base_depth <= 0) die("too many ../'s in path %s", dir); copy_segments(path, base_dir, base_depth + 1); if (strlen(path) + strlen(dir) + 2 > MAX_DIR_LEN) die("path %s too long", dir); strlcat(path, "/", sizeof(path)); strlcat(path, dir, sizeof(path)); absolute = path; } /* Find the first point of difference between absolute and current_dir. If there is no difference, we're done; we're changing to the same directory we were in (this is probably some sort of error, but can happen with odd relative paths). */ for (p = absolute, q = current_dir; *p == *q; p++, q++) if (*p == '\0') return true; /* If we reached the end of current_dir and there's more left of absolute, we're changing to a subdirectory of where we were. */ if (*q == '\0' && *p == '/') { p++; if (!chdir_checked(p, filecount)) return false; if (prefix_len == 0) strlcpy(current_dir, absolute, sizeof(current_dir)); return true; } /* Otherwise, if we were promised that we have a pure tree (in other words, no symbolic links to directories), see if it's worth going up the tree with ".." and then down again rather than chdir to the absolute path. relative_threshold determines how many levels of ".." we're willing to use; the default of 1 seems fractionally faster than 2 and 0 indicates to always use absolute paths. Values larger than 3 would require extending the dotdots string, but are unlikely to be worth it. FIXME: It's too hard to figure out what this code does. It needs to be rewritten. */ if (*p != '\0' && relative_threshold > 0) { depth = slashcount(q); if (depth <= relative_threshold) { while (p > absolute && *--p != '/') ; p++; strlcpy(prefix_dir, dotdots + 9 - depth * 3, sizeof(prefix_dir)); strlcat(prefix_dir, p, sizeof(prefix_dir)); if (!chdir_checked(prefix_dir, filecount)) return false; /* Now patch up current_dir to reflect where we are. */ if (prefix_len == 0) { while (q > current_dir && *--q != '/') ; q[1] = '\0'; strlcat(current_dir, p, sizeof(current_dir)); } return true; } } /* All else has failed; just use the absolute path. This includes the case where current_dir is a subdirectory of absolute, in which case it may be somewhat faster to use chdir("../..") or the like rather than the absolute path, but this case rarely happens when the user cares about speed (it usually doesn't happen with sorted input). So we don't bother. */ if (!chdir_checked(absolute, filecount)) return false; if (prefix_len == 0) strlcpy(current_dir, absolute, sizeof(current_dir)); return true; } /* ** Process a filelist of files to be deleted, all in the same directory. */ static void unlink_filelist(filelist *list, int filecount) { bool sorted; DIR *dir; struct dirent *entry; char *file; int i; /* If setup_dir returns false, the directory doesn't exist and we're already all done. */ if (!setup_dir(list->dir, filecount)) { filelist_free(list); return; } /* We'll use prefix_dir as a buffer to write each file name into as we go, so get it set up. */ if (prefix_len == 0) file = prefix_dir; else { prefix_dir[prefix_len++] = '/'; file = prefix_dir + prefix_len; *file = '\0'; } /* If we're not sorting directories or if the number of files is under the threshold, just remove the files. */ if (sort_threshold == 0 || filecount < sort_threshold) { for (i = 0; i < list->count; i++) { strlcpy(file, list->files[i], sizeof(prefix_dir) - prefix_len); unlink_file(prefix_dir); } filelist_free(list); return; } /* We have enough files to remove in this directory that it's worth optimizing. First, make sure the list of files is sorted. It's not uncommon for the files to already be sorted, so check first. */ for (sorted = true, i = 1; sorted && i < list->count; i++) sorted = (strcmp(list->files[i - 1], list->files[i]) <= 0); if (!sorted) qsort(list->files, list->count, sizeof(char *), file_compare); /* Now, begin doing our optimized unlinks. The technique we use is to open the directory containing the files and read through it, checking each file in the directory to see if it's one of the files we should be removing. The theory is that we want to minimize the amount of time the operating system spends doing string compares trying to find the file to be removed in the directory. This is often an O(n) operation. Note that this optimization may slightly slow more effecient operating systems. */ dir = opendir(prefix_len == 0 ? "." : prefix_dir); if (dir == NULL) { if (prefix_len > 0 && prefix_dir[0] == '/') warn("can't open directory %s", prefix_dir); else warn("can't open directory %s in %s", (prefix_len == 0) ? "." : prefix_dir, current_dir); error_count++; filelist_free(list); return; } for (i = 0, entry = readdir(dir); entry != NULL; entry = readdir(dir)) if (filelist_lookup(list, entry->d_name) != NULL) { i++; strlcpy(file, entry->d_name, sizeof(prefix_dir) - prefix_len); unlink_file(prefix_dir); if (i == list->count) break; } closedir(dir); filelist_free(list); } /* ** Check a path to see if it's okay (not likely to confuse us). This ** ensures that it doesn't contain elements like "./" or "../" and doesn't ** contain doubled slashes. */ static bool bad_path(const char *p) { if (strlen(p) >= MAX_DIR_LEN) return true; while (*p) { if (p[0] == '.' && (p[1] == '/' || (p[1] == '.' && p[2] == '/'))) return true; while (*p && *p != '/') p++; if (p[0] == '/' && p[1] == '/') return true; if (*p == '/') p++; } return false; } /* ** Main routine. Parse options, initialize the storage manager, and ** initalize various global variables, and then go into a loop calling ** process_line and unlink_filelist as needed. */ int main(int argc, char *argv[]) { const char *name; char *p, **arg; QIOSTATE *qp; filelist *list; int filecount, deleted; bool empty_error = false; /* Establish our identity. Since we use the storage manager, we need to set up syslog as well, although we won't use it ourselves. */ name = argv[0]; if (*name == '\0') name = "fastrm"; else { p = strrchr(name, '/'); if (p != NULL) name = p + 1; } message_program_name = name; openlog(name, LOG_CONS | LOG_PID, LOG_INN_PROG); /* If we're running as root, unlink may remove directories. */ unlink_dangerous = (geteuid() == 0); /* Unfortunately, we can't use getopt, because several of our options take optional arguments. Bleh. */ arg = argv + 1; while (argc >= 2 && **arg == '-') { p = *arg; while (*++p) { switch (*p) { default: die("invalid option -- %c", *p); case 'a': case 'r': continue; case 'c': chdir_threshold = 1; if (!isdigit((unsigned char) p[1])) continue; chdir_threshold = atoi(p + 1); break; case 'd': debug_only = true; continue; case 'e': empty_error = true; continue; case 's': sort_threshold = 5; if (!isdigit((unsigned char) p[1])) continue; sort_threshold = atoi(p + 1); break; case 'u': relative_threshold = 1; if (!isdigit((unsigned char) p[1])) continue; relative_threshold = atoi(p + 1); if (relative_threshold >= (int) strlen(dotdots) / 3) relative_threshold = strlen(dotdots) / 3 - 1; break; } break; } argc--; arg++; } if (argc != 2) die("usage error, wrong number of arguments"); /* The remaining argument is the base path. Make sure it's valid and not excessively large and then change to it. */ base_dir = *arg; if (*base_dir != '/' || bad_path(base_dir)) die("bad base path %s", base_dir); strlcpy(current_dir, base_dir, sizeof(current_dir)); if (chdir(current_dir) < 0) sysdie("can't chdir to base path %s", current_dir); /* Open our input stream and then loop through it, building filelists and processing them until done. */ qp = QIOfdopen(fileno(stdin)); if (qp == NULL) sysdie("can't reopen stdin"); while ((list = process_line(qp, &filecount, &deleted)) != NULL) { empty_error = false; unlink_filelist(list, filecount); } if (deleted > 0) empty_error = false; /* All done. */ SMshutdown(); if (empty_error) die("no files to remove"); exit(error_count > 0 ? 1 : 0); } inn-2.6.0/expire/expireover.c0000644000175200017520000001512112575023702015550 0ustar iuliusiulius/* $Id: expireover.c 8570 2009-08-17 19:05:28Z iulius $ ** ** Expire the overview database. ** ** This program handles the nightly expiration of overview information. If ** groupbaseexpiry is true, this program also handles the removal of ** articles that have expired. It's separate from the process that scans ** and expires the history file. */ #include "config.h" #include "clibrary.h" #include #include #include #include #include "inn/innconf.h" #include "inn/libinn.h" #include "inn/messages.h" #include "inn/newsuser.h" #include "inn/ov.h" #include "inn/paths.h" #include "inn/qio.h" #include "inn/storage.h" static const char usage[] = "\ Usage: expireover [-ekNpqs] [-f file] [-w offset] [-z rmfile] [-Z lowmarkfile]\n"; /* Set to 1 if we've received a signal; expireover then terminates after finishing the newsgroup that it's working on (this prevents corruption of the overview by killing expireover). */ static volatile sig_atomic_t signalled = 0; /* ** Handle a fatal signal and set signalled. Restore the default signal ** behavior after receiving a signal so that repeating the signal will kill ** the program immediately. */ static void fatal_signal(int sig) { signalled = 1; xsignal(sig, SIG_DFL); } int main(int argc, char *argv[]) { int option, low; char *line, *p; QIOSTATE *qp; bool value; OVGE ovge; char *active_path = NULL; char *lowmark_path = NULL; char *path; FILE *lowmark = NULL; bool purge_deleted = false; bool always_stat = false; struct history *history; /* First thing, set up logging and our identity. */ openlog("expireover", L_OPENLOG_FLAGS | LOG_PID, LOG_INN_PROG); message_program_name = "expireover"; /* Set up some default options for group-based expiration, although none of these will be used if groupbaseexpiry isn't true. */ ovge.earliest = false; ovge.keep = false; ovge.ignoreselfexpire = false; ovge.usepost = false; ovge.quiet = false; ovge.timewarp = 0; ovge.filename = NULL; ovge.delayrm = false; /* Parse the command-line options. */ while ((option = getopt(argc, argv, "ef:kNpqsw:z:Z:")) != EOF) { switch (option) { case 'e': ovge.earliest = true; break; case 'f': active_path = xstrdup(optarg); break; case 'k': ovge.keep = true; break; case 'N': ovge.ignoreselfexpire = true; break; case 'p': ovge.usepost = true; break; case 'q': ovge.quiet = true; break; case 's': always_stat = true; break; case 'w': ovge.timewarp = (time_t) (atof(optarg) * 86400.); break; case 'z': ovge.filename = optarg; ovge.delayrm = true; break; case 'Z': lowmark_path = optarg; break; default: fprintf(stderr, "%s", usage); exit(1); } } if (ovge.earliest && ovge.keep) die("-e and -k cannot be specified at the same time"); /* Initialize innconf. */ if (!innconf_read(NULL)) exit(1); /* Change to the runasuser user and runasgroup group if necessary. */ ensure_news_user_grp(true, true); /* Initialize the lowmark file, if one was requested. */ if (lowmark_path != NULL) { if (unlink(lowmark_path) < 0 && errno != ENOENT) syswarn("can't remove %s", lowmark_path); lowmark = fopen(lowmark_path, "a"); if (lowmark == NULL) sysdie("can't open %s", lowmark_path); } /* Set up the path to the list of newsgroups we're going to use and open that file. This could be stdin. */ if (active_path == NULL) { active_path = concatpath(innconf->pathdb, INN_PATH_ACTIVE); purge_deleted = true; } if (strcmp(active_path, "-") == 0) { qp = QIOfdopen(fileno(stdin)); if (qp == NULL) sysdie("can't reopen stdin"); } else { qp = QIOopen(active_path); if (qp == NULL) sysdie("can't open active file (%s)", active_path); } free(active_path); /* open up the history manager */ path = concatpath(innconf->pathdb, INN_PATH_HISTORY); history = HISopen(path, innconf->hismethod, HIS_RDONLY); free(path); /* Initialize the storage manager. We only need to initialize it in read/write mode if we're not going to be writing a separate file for the use of fastrm. */ if (!ovge.delayrm) { value = true; if (!SMsetup(SM_RDWR, &value)) die("can't setup storage manager read/write"); } value = true; if (!SMsetup(SM_PREOPEN, &value)) die("can't setup storage manager"); if (!SMinit()) die("can't initialize storage manager: %s", SMerrorstr); /* Initialize and configure the overview subsystem. */ if (!OVopen(OV_READ | OV_WRITE)) die("can't open overview database"); if (innconf->groupbaseexpiry) { time(&ovge.now); if (!OVctl(OVGROUPBASEDEXPIRE, &ovge)) die("can't configure group-based expire"); } if (!OVctl(OVSTATALL, &always_stat)) die("can't configure overview stat behavior"); /* We want to be careful about being interrupted from this point on, so set up our signal handlers. */ xsignal(SIGTERM, fatal_signal); xsignal(SIGINT, fatal_signal); xsignal(SIGHUP, fatal_signal); /* Loop through each line of the input file and process each group, writing data to the lowmark file if desired. */ line = QIOread(qp); while (line != NULL && !signalled) { p = strchr(line, ' '); if (p != NULL) *p = '\0'; p = strchr(line, '\t'); if (p != NULL) *p = '\0'; if (!OVexpiregroup(line, &low, history)) warn("can't expire %s", line); else if (lowmark != NULL && low != 0) fprintf(lowmark, "%s %u\n", line, low); line = QIOread(qp); } if (signalled) warn("received signal, exiting"); /* If desired, purge all deleted newsgroups. */ if (!signalled && purge_deleted) if (!OVexpiregroup(NULL, NULL, history)) warn("can't expire deleted newsgroups"); /* Close everything down in an orderly fashion. */ QIOclose(qp); OVclose(); SMshutdown(); HISclose(history); if (lowmark != NULL) if (fclose(lowmark) == EOF) syswarn("can't close %s", lowmark_path); return 0; } inn-2.6.0/expire/makehistory.c0000644000175200017520000007726512575023702015740 0ustar iuliusiulius/* $Id: makehistory.c 9896 2015-06-14 10:09:47Z iulius $ ** ** Rebuild history/overview databases. */ #include "config.h" #include "clibrary.h" #include #include #include #include #include #include "inn/buffer.h" #include "inn/history.h" #include "inn/innconf.h" #include "inn/libinn.h" #include "inn/messages.h" #include "inn/newsuser.h" #include "inn/ov.h" #include "inn/paths.h" #include "inn/qio.h" #include "inn/storage.h" #include "inn/vector.h" #include "inn/wire.h" /* ** If we have getloadavg, include the appropriate header file. Otherwise, ** just assume that we always have a load of 0. */ #if HAVE_GETLOADAVG # if HAVE_SYS_LOADAVG_H # include # endif #else static int getloadavg(double loadavg[], int nelem) { int i; for (i = 0; i < nelem && i < 3; i++) loadavg[i] = 0; return i; } #endif static const char usage[] = "\ Usage: makehistory [-abFIOSx] [-f file] [-l count] [-L load] [-s size] [-T tmpdir]\n\ \n\ -a open output history file in append mode\n\ -b delete bad articles from spool\n\ -F fork when writing overview\n\ -f file write history entries to file (default $pathdb/history)\n\ -I do not create overview for articles numbered below lowmark\n\ -l count size of overview updates (default 10000)\n\ -L load pause when load average exceeds threshold\n\ -O create overview entries for articles\n\ -S write overview data to standard output\n\ -s size size new history database for approximately size entries\n\ -T tmpdir use directory tmpdir for temporary files\n\ -x don't write history entries\n"; /* ** Information about the schema of the news overview files. */ typedef struct _ARTOVERFIELD { char *Headername; int HeadernameLength; bool NeedHeadername; const char *Header; int HeaderLength; bool HasHeader; } ARTOVERFIELD; #define DEFAULT_SEGSIZE 10000; bool NukeBadArts; char *ActivePath = NULL; char *HistoryPath = NULL; struct history *History; FILE *Overchan; bool DoOverview; bool Fork; bool Cutofflow = false; char *TmpDir; int OverTmpSegSize, OverTmpSegCount; FILE *OverTmpFile; char *OverTmpPath = NULL; bool NoHistory; OVSORTTYPE sorttype; bool WriteStdout = false; /* Misc variables needed for the overview creation code. */ static char BYTES[] = "Bytes"; static char DATE[] = "Date"; static char EXPIRES[] = "Expires"; static char INJECTIONDATE[] = "Injection-Date"; static char LINES[] = "Lines"; static char MESSAGEID[] = "Message-ID"; static char XREF[] = "Xref"; static ARTOVERFIELD *ARTfields; /* Overview fields for which overview data * is generated. */ static size_t ARTfieldsize; static ARTOVERFIELD *Bytesp = (ARTOVERFIELD *)NULL; static ARTOVERFIELD *Datep = (ARTOVERFIELD *)NULL; static ARTOVERFIELD *Expp = (ARTOVERFIELD *)NULL; static ARTOVERFIELD *InjectionDatep = (ARTOVERFIELD *)NULL; static ARTOVERFIELD *Linesp = (ARTOVERFIELD *)NULL; static ARTOVERFIELD *Msgidp = (ARTOVERFIELD *)NULL; static ARTOVERFIELD *Xrefp = (ARTOVERFIELD *)NULL; static ARTOVERFIELD *Missfields; /* Header fields not used in overview * but that we need (e.g. Expires:). */ static size_t Missfieldsize = 0; static void OverAddAllNewsgroups(void); /* ** Check and parse a Message-ID header line. Return private space. */ static const char * GetMessageID(char *p) { static struct buffer buffer = { 0, 0, 0, NULL }; while (ISWHITE(*p)) p++; if (p[0] != '<' || p[strlen(p) - 1] != '>') return ""; /* Copy into re-used memory space, including NUL. */ buffer_set(&buffer, p, strlen(p)+1); return buffer.data; } /* * The overview temp file is used to accumulate overview lines as articles are * scanned. The format is * (1st) newsgroup name\tToken\toverview data. * When about 10000 lines of this overview data are accumulated, the data * file is sorted and then read back in and the data added to overview. * The sorting/batching helps improve efficiency. */ /* * Flush the unwritten OverTempFile data to disk, sort the file, read it * back in, and add it to overview. */ static void FlushOverTmpFile(void) { char temp[SMBUF]; char *SortedTmpPath; int i, pid, fd; TOKEN token; QIOSTATE *qp; int count; char *line, *p; char *q = NULL; char *r = NULL; time_t arrived, expires; static int first = 1; if (OverTmpFile == NULL) return; if (fflush(OverTmpFile) == EOF || ferror(OverTmpFile) || fclose(OverTmpFile) == EOF) sysdie("cannot close temporary overview file"); if(Fork) { if(!first) { /* if previous one is running, wait for it */ int status; wait(&status); if((WIFEXITED(status) && WEXITSTATUS(status) != 0) || WIFSIGNALED(status)) exit(1); } pid = fork(); if(pid == -1) sysdie("cannot fork"); if(pid > 0) { /* parent */ first = 0; free(OverTmpPath); OverTmpPath = NULL; return; } /* child */ /* init the overview setup. */ if (!OVopen(OV_WRITE)) { warn("cannot open overview"); _exit(1); } if (!OVctl(OVSORT, (void *)&sorttype)) { warn("cannot obtain overview sorting information"); OVclose(); _exit(1); } if (!OVctl(OVCUTOFFLOW, (void *)&Cutofflow)) { warn("cannot obtain overview cutoff information"); OVclose(); _exit(1); } } /* This is a bit odd, but as long as other user's files can't be deleted out of the temporary directory, it should work. We're using mkstemp to create a file and then passing its name to sort, which will then open it again and overwrite it. */ SortedTmpPath = concatpath(TmpDir, "hisTXXXXXX"); fd = mkstemp(SortedTmpPath); if (fd < 0) { syswarn("cannot create temporary file"); OVclose(); Fork ? _exit(1) : exit(1); } close(fd); snprintf(temp, sizeof(temp), "exec %s -T %s -t'%c' -o %s %s", INN_PATH_SORT, TmpDir, '\t', SortedTmpPath, OverTmpPath); i = system(temp) >> 8; if (i != 0) { syswarn("cannot sort temporary overview file (%s exited %d)", INN_PATH_SORT, i); OVclose(); Fork ? _exit(1) : exit(1); } /* don't need old path anymore. */ unlink(OverTmpPath); free(OverTmpPath); OverTmpPath = NULL; /* read sorted lines. */ if ((qp = QIOopen(SortedTmpPath)) == NULL) { syswarn("cannot open sorted overview file %s", SortedTmpPath); OVclose(); Fork ? _exit(1) : exit(1); } for (count = 1; ; ++count) { line = QIOread(qp); if (line == NULL) { if (QIOtoolong(qp)) { warn("overview line %d is too long", count); continue; } else break; } if ((p = strchr(line, '\t')) == NULL || (q = strchr(p+1, '\t')) == NULL || (r = strchr(q+1, '\t')) == NULL) { warn("sorted overview file %s has a bad line at %d", SortedTmpPath, count); continue; } /* p+1 now points to start of token, q+1 points to start of overline. */ if (sorttype == OVNEWSGROUP) { *p++ = '\0'; *q++ = '\0'; *r++ = '\0'; arrived = (time_t)atol(p); expires = (time_t)atol(q); q = r; if ((r = strchr(r, '\t')) == NULL) { warn("sorted overview file %s has a bad line at %d", SortedTmpPath, count); continue; } *r++ = '\0'; } else { *p++ = '\0'; *q++ = '\0'; *r++ = '\0'; arrived = (time_t)atol(line); expires = (time_t)atol(p); } token = TextToToken(q); if (OVadd(token, r, strlen(r), arrived, expires) == OVADDFAILED) { if (OVctl(OVSPACE, (void *)&i) && i == OV_NOSPACE) { warn("no space left for overview"); OVclose(); Fork ? _exit(1) : exit(1); } warn("cannot write overview data \"%.40s\"", q); } } /* Check for errors and close. */ if (QIOerror(qp)) { syswarn("cannot read sorted overview file %s", SortedTmpPath); OVclose(); Fork ? _exit(1) : exit(1); } QIOclose(qp); /* unlink sorted tmp file */ unlink(SortedTmpPath); free(SortedTmpPath); if(Fork) { OVclose(); _exit(0); } } /* ** Write a line to the overview temp file, standard output, our child process ** that's doing the writing, or directly to overview, whichever is ** appropriate. */ static void WriteOverLine(TOKEN *token, const char *xrefs, int xrefslen, char *overdata, int overlen, time_t arrived, time_t expires) { char temp[SMBUF]; const char *p, *q, *r; int i, fd; /* If WriteStdout is set, just print the overview information to standard output and return. */ if (WriteStdout) { printf("%s %ld %ld ", TokenToText(*token), (long) arrived, (long) expires); fwrite(overdata, 1, overlen, stdout); fputc('\n', stdout); return; } /* If the overview method doesn't care about sorted data, don't bother with the temporary file and just write the data out to our child process or to the overview database directly. */ if (sorttype == OVNOSORT) { if (Fork) { fprintf(Overchan, "%s %ld %ld ", TokenToText(*token), (long)arrived, (long)expires); if (fwrite(overdata, 1, overlen, Overchan) != (size_t) overlen) sysdie("writing overview failed"); fputc('\n', Overchan); } else if (OVadd(*token, overdata, overlen, arrived, expires) == OVADDFAILED) { if (OVctl(OVSPACE, (void *)&i) && i == OV_NOSPACE) { warn("no space left for overview"); OVclose(); exit(1); } warn("cannot write overview data for article %s", TokenToText(*token)); } return; } /* Otherwise, we need the temporary file. Create it if it doesn't already exist. */ if (OverTmpPath == NULL) { /* need new temp file, so create it. */ OverTmpPath = concatpath(TmpDir, "histXXXXXX"); fd = mkstemp(OverTmpPath); if (fd < 0) sysdie("cannot create temporary file"); OverTmpFile = fdopen(fd, "w"); if (OverTmpFile == NULL) sysdie("cannot open %s", OverTmpPath); OverTmpSegCount = 0; } /* Print out the data to the teporary file with the appropriate keys for sorting. */ if (sorttype == OVNEWSGROUP) { /* find first ng name in xref. */ for (p = xrefs, q=NULL ; p < xrefs+xrefslen ; ++p) { if (*p == ' ') { q = p+1; /* found space */ break; } } if (!q) { warn("bogus Xref data for %s", TokenToText(*token)); /* XXX do nuke here? */ return; } for (p = q, r=NULL ; p < xrefs+xrefslen ; ++p) { if (*p == ':') { r=p; break; } } if (!r) { warn("bogus Xref data for %s", TokenToText(*token)); /* XXX do nuke here? */ return; } /* q points to start of ng name, r points to its end. */ assert((ptrdiff_t) sizeof(temp) > r - q + 1); memcpy(temp, q, r - q + 1); temp[r - q + 1] = '\0'; fprintf(OverTmpFile, "%s\t%10lu\t%lu\t%s\t", temp, (unsigned long) arrived, (unsigned long) expires, TokenToText(*token)); } else fprintf(OverTmpFile, "%10lu\t%lu\t%s\t", (unsigned long) arrived, (unsigned long) expires, TokenToText(*token)); fwrite(overdata, overlen, 1, OverTmpFile); fprintf(OverTmpFile, "\n"); OverTmpSegCount++; if (OverTmpSegSize != 0 && OverTmpSegCount >= OverTmpSegSize) { FlushOverTmpFile(); } } /* ** Read the overview schema. */ static void ARTreadschema(bool Overview) { const struct cvector *standardoverview; struct vector *extraoverview; ARTOVERFIELD *fp; unsigned int i; if (Overview) { /* Count the number of overview fields and allocate ARTfields. */ standardoverview = overview_fields(); extraoverview = overview_extra_fields(true); ARTfields = xmalloc((standardoverview->count + extraoverview->count + 1) * sizeof(ARTOVERFIELD)); /* Parse each field. */ for (i = 0, fp = ARTfields; i < standardoverview->count; i++) { fp->NeedHeadername = false; fp->Headername = xstrdup(standardoverview->strings[i]); fp->HeadernameLength = strlen(standardoverview->strings[i]); fp->Header = (char *)NULL; fp->HasHeader = false; fp->HeaderLength = 0; /* We need to keep the value of fp for :bytes and :lines * because we will need them afterwards; we will then not * have to compare fp->Headername to both "Bytes" and "Lines" * for each fp. */ if (strncasecmp(standardoverview->strings[i], BYTES, strlen(BYTES)) == 0) Bytesp = fp; if (strncasecmp(standardoverview->strings[i], DATE, strlen(DATE)) == 0) Datep = fp; if (strncasecmp(standardoverview->strings[i], LINES, strlen(LINES)) == 0) Linesp = fp; if (strncasecmp(standardoverview->strings[i], MESSAGEID, strlen(MESSAGEID)) == 0) Msgidp = fp; fp++; } for (i = 0; i < extraoverview->count; i++) { fp->NeedHeadername = true; fp->Headername = xstrdup(extraoverview->strings[i]); fp->HeadernameLength = strlen(extraoverview->strings[i]); fp->Header = (char *)NULL; fp->HasHeader = false; fp->HeaderLength = 0; if (strncasecmp(extraoverview->strings[i], XREF, strlen(XREF)) == 0) Xrefp = fp; if (strncasecmp(extraoverview->strings[i], EXPIRES, strlen(EXPIRES)) == 0) Expp = fp; if (strncasecmp(extraoverview->strings[i], INJECTIONDATE, strlen(INJECTIONDATE)) == 0) InjectionDatep = fp; fp++; } ARTfieldsize = fp - ARTfields; vector_free(extraoverview); } if (Bytesp == (ARTOVERFIELD *)NULL) Missfieldsize++; if (Datep == (ARTOVERFIELD *)NULL) Missfieldsize++; if (Expp == (ARTOVERFIELD *)NULL) Missfieldsize++; if (InjectionDatep == (ARTOVERFIELD *)NULL) Missfieldsize++; if (Linesp == (ARTOVERFIELD *)NULL) Missfieldsize++; if (Msgidp == (ARTOVERFIELD *)NULL) Missfieldsize++; /* The fields in Missfields are not used in the overview (because they were not * set before), so the values do not matter much (especially for NeedHeadername). */ if (Missfieldsize > 0) { Missfields = xmalloc(Missfieldsize * sizeof(ARTOVERFIELD)); fp = Missfields; if (Bytesp == (ARTOVERFIELD *)NULL) { fp->NeedHeadername = false; fp->Headername = xstrdup(BYTES); fp->HeadernameLength = strlen(BYTES); fp->Header = (char *)NULL; fp->HasHeader = false; fp->HeaderLength = 0; Bytesp = fp++; } if (Datep == (ARTOVERFIELD *)NULL) { fp->NeedHeadername = false; fp->Headername = xstrdup(DATE); fp->HeadernameLength = strlen(DATE); fp->Header = (char *)NULL; fp->HasHeader = false; fp->HeaderLength = 0; Datep = fp++; } if (Expp == (ARTOVERFIELD *)NULL) { fp->NeedHeadername = true; fp->Headername = xstrdup(EXPIRES); fp->HeadernameLength = strlen(EXPIRES); fp->Header = (char *)NULL; fp->HasHeader = false; fp->HeaderLength = 0; Expp = fp++; } if (InjectionDatep == (ARTOVERFIELD *)NULL) { fp->NeedHeadername = true; fp->Headername = xstrdup(INJECTIONDATE); fp->HeadernameLength = strlen(INJECTIONDATE); fp->Header = (char *)NULL; fp->HasHeader = false; fp->HeaderLength = 0; InjectionDatep = fp++; } if (Linesp == (ARTOVERFIELD *)NULL) { fp->NeedHeadername = false; fp->Headername = xstrdup(LINES); fp->HeadernameLength = strlen(LINES); fp->Header = (char *)NULL; fp->HasHeader = false; fp->HeaderLength = 0; Linesp = fp++; } if (Msgidp == (ARTOVERFIELD *)NULL) { fp->NeedHeadername = false; fp->Headername = xstrdup(MESSAGEID); fp->HeadernameLength = strlen(MESSAGEID); fp->Header = (char *)NULL; fp->HasHeader = false; fp->HeaderLength = 0; Msgidp = fp++; } if (Overview && Xrefp == (ARTOVERFIELD *)NULL) { fp->NeedHeadername = true; fp->Headername = xstrdup(XREF); fp->HeadernameLength = strlen(XREF); fp->Header = (char *)NULL; fp->HasHeader = false; fp->HeaderLength = 0; Xrefp = fp++; } } } /* * Handle a single article. This routine's fairly complicated. */ static void DoArt(ARTHANDLE *art) { ARTOVERFIELD *fp; const char *p, *end; char *q; static struct buffer buffer = { 0, 0, 0, NULL }; static char SEP[] = "\t"; static char NUL[] = "\0"; static char COLONSPACE[] = ": "; size_t i, j, len; const char *MessageID; time_t Arrived; time_t Expires; time_t Posted; char overdata[BIG_BUFFER]; char Bytes[BIG_BUFFER]; char Lines[BIG_BUFFER]; struct artngnum ann; int DotStuffedLines = 0; int lines = 0; bool hasCounts = false; /* Set up place to store headers. */ for (fp = ARTfields, i = 0; i < ARTfieldsize; i++, fp++) { if (fp->HeaderLength) { fp->Header = 0; } fp->HeaderLength = 0; fp->HasHeader = false; } if (Missfieldsize > 0) { for (fp = Missfields, i = 0; i < Missfieldsize; i++, fp++) { if (fp->HeaderLength) { fp->Header = 0; } fp->HeaderLength = 0; fp->HasHeader = false; } } for (fp = ARTfields, i = 0; i < ARTfieldsize; i++, fp++) { fp->Header = wire_findheader(art->data, art->len, fp->Headername, false); /* Someone managed to break their server so that they were appending multiple Xref headers, and INN had a bug where it wouldn't notice this and reject the article. Just in case, see if there are multiple Xref headers and use the last one. */ if (fp == Xrefp) { const char *next = fp->Header; size_t left; while (next != NULL) { next = wire_endheader(fp->Header, art->data + art->len - 1); if (next == NULL) break; next++; left = art->len - (next - art->data); next = wire_findheader(next, left, fp->Headername, false); if (next != NULL) fp->Header = next; } } /* Work out the real values for :bytes and :lines. */ if (fp == Bytesp || fp == Linesp) { /* Parse the article only once. */ if (!hasCounts && (p = wire_findbody(art->data, art->len)) != NULL) { end = art->data + art->len; /* p is at the beginning of the body, if any. */ for (; *p != '\0';) { /* p is at the beginning of a new line, if any. */ lines++; if (*p == '.') DotStuffedLines++; if ((p = wire_nextline(p, end)) == NULL) { /* p is at the final ".\r\n". */ lines--; DotStuffedLines--; break; } } } if (fp == Bytesp) { /* :bytes does not count the final ".\r\n", so remove 3. */ snprintf(Bytes, sizeof(Bytes), "%lu", (unsigned long) art->len - DotStuffedLines - 3); fp->Header = Bytes; fp->HeaderLength = strlen(Bytes); } else { snprintf(Lines, sizeof(Lines), "%lu", (unsigned long) lines); fp->Header = Lines; fp->HeaderLength = strlen(Lines); } fp->HasHeader = true; hasCounts = true; continue; } /* Now, if we have a header, find and record its length. */ if (fp->Header != NULL) { fp->HasHeader = true; p = wire_endheader(fp->Header, art->data + art->len - 1); if (p == NULL) continue; /* The true length of the header is p - fp->Header + 1, but p points to the \n at the end of the header, so subtract 2 to peel off the \r\n (we're guaranteed we're dealing with wire-format articles. */ fp->HeaderLength = p - fp->Header - 1; } } if (Missfieldsize > 0) { for (fp = Missfields, i = 0; i < Missfieldsize; i++, fp++) { fp->Header = wire_findheader(art->data, art->len, fp->Headername, false); if (fp->Header != NULL) { fp->HasHeader = true; p = wire_endheader(fp->Header, art->data + art->len - 1); if (p == NULL) continue; fp->HeaderLength = p - fp->Header - 1; } } } if (DoOverview && Xrefp->HeaderLength == 0) { if (!SMprobe(SMARTNGNUM, art->token, (void *)&ann)) { Xrefp->Header = NULL; Xrefp->HeaderLength = 0; } else { if (ann.artnum == 0 || ann.groupname == NULL) return; len = strlen(innconf->pathhost) + 1 + strlen(ann.groupname) + 1 + 16 + 1; if (len > BIG_BUFFER) { Xrefp->Header = NULL; Xrefp->HeaderLength = 0; } else { snprintf(overdata, sizeof(overdata), "%s %s:%lu", innconf->pathhost, ann.groupname, ann.artnum); Xrefp->Header = overdata; Xrefp->HeaderLength = strlen(overdata); } if (ann.groupname != NULL) free(ann.groupname); } } Arrived = art->arrived; Expires = 0; if (!Msgidp->HasHeader) { warn("no Message-ID header in %s", TokenToText(*art->token)); if (NukeBadArts) SMcancel(*art->token); return; } buffer_set(&buffer, Msgidp->Header, Msgidp->HeaderLength); buffer_append(&buffer, NUL, 1); for (i = 0, q = buffer.data; i < buffer.left; q++, i++) if (*q == '\t' || *q == '\n' || *q == '\r') *q = ' '; MessageID = GetMessageID(buffer.data); if (*MessageID == '\0') { warn("no Message-ID header in %s", TokenToText(*art->token)); if (NukeBadArts) SMcancel(*art->token); return; } /* * check if msgid is in history if in update mode, or if article is * newer than start time of makehistory. */ if (!InjectionDatep->HasHeader && !Datep->HasHeader) { Posted = Arrived; } else { if (InjectionDatep->HasHeader) { buffer_set(&buffer, InjectionDatep->Header, InjectionDatep->HeaderLength); buffer_append(&buffer, NUL, 1); Posted = parsedate_rfc5322_lax(buffer.data); /* If parsing failed for Injection-Date:, take the arrival time. * Do not look at the Date: header (though we could). */ if (Posted == (time_t) -1) Posted = Arrived; } else { buffer_set(&buffer, Datep->Header, Datep->HeaderLength); buffer_append(&buffer, NUL, 1); Posted = parsedate_rfc5322_lax(buffer.data); if (Posted == (time_t) -1) Posted = Arrived; } } if (Expp->HasHeader) { buffer_set(&buffer, Expp->Header, Expp->HeaderLength); buffer_append(&buffer, NUL, 1); Expires = parsedate_rfc5322_lax(buffer.data); if (Expires == (time_t) -1) Expires = 0; } if (DoOverview && Xrefp->HeaderLength > 0) { for (fp = ARTfields, j = 0; j < ARTfieldsize; j++, fp++) { if (fp == ARTfields) buffer_set(&buffer, "", 0); else buffer_append(&buffer, SEP, strlen(SEP)); if (fp->HeaderLength == 0) continue; if (fp->NeedHeadername) { buffer_append(&buffer, fp->Headername, fp->HeadernameLength); buffer_append(&buffer, COLONSPACE, strlen(COLONSPACE)); } i = buffer.left; buffer_resize(&buffer, buffer.left + fp->HeaderLength); end = fp->Header + fp->HeaderLength - 1; for (p = fp->Header, q = &buffer.data[i]; p <= end; p++) { if (*p == '\r' && p < end && p[1] == '\n') { p++; continue; } if (*p == '\0' || *p == '\t' || *p == '\n' || *p == '\r') *q++ = ' '; else *q++ = *p; buffer.left++; } } WriteOverLine(art->token, Xrefp->Header, Xrefp->HeaderLength, buffer.data, buffer.left, Arrived, Expires); } if (!NoHistory) { bool r; r = HISwrite(History, MessageID, Arrived, Posted, Expires, art->token); if (r == false) sysdie("cannot write history line"); } } /* ** Add all groups to overview group.index. --rmt */ static void OverAddAllNewsgroups(void) { QIOSTATE *qp; int count; char *q,*p; char *line; ARTNUM hi, lo; if ((qp = QIOopen(ActivePath)) == NULL) sysdie("cannot open %s", ActivePath); for (count = 1; (line = QIOread(qp)) != NULL; count++) { if ((p = strchr(line, ' ')) == NULL) { warn("bad active line %d: %.40s", count, line); continue; } *p++ = '\0'; hi = (ARTNUM)atol(p); if ((p = strchr(p, ' ')) == NULL) { warn("bad active line %d: %.40s", count, line); continue; } *p++ = '\0'; lo = (ARTNUM)atol(p); if ((q = strrchr(p, ' ')) == NULL) { warn("bad active line %d: %.40s", count, line); continue; } /* q+1 points to NG flag */ if (!OVgroupadd(line, lo, hi, q+1)) die("cannot add %s to overview group index", line); } /* Test error conditions; QIOtoolong shouldn't happen. */ if (QIOtoolong(qp)) die("active file line %d is too long", count); if (QIOerror(qp)) sysdie("cannot read %s around line %d", ActivePath, count); QIOclose(qp); } int main(int argc, char **argv) { ARTHANDLE *art = NULL; bool AppendMode; int LoadAverage; double load[1]; int i; bool val; char *HistoryDir; char *RebuiltflagPath; char *p; char *buff; size_t npairs = 0; FILE *F; /* First thing, set up logging and our identity. */ openlog("makehistory", L_OPENLOG_FLAGS | LOG_PID, LOG_INN_PROG); message_program_name = "makehistory"; /* Set defaults. */ if (!innconf_read(NULL)) exit(1); HistoryPath = concatpath(innconf->pathdb, INN_PATH_HISTORY); ActivePath = concatpath(innconf->pathdb, INN_PATH_ACTIVE); TmpDir = innconf->pathtmp; RebuiltflagPath = concatpath(innconf->pathrun, INN_PATH_REBUILDOVERVIEW); OverTmpSegSize = DEFAULT_SEGSIZE; OverTmpSegCount = 0; NukeBadArts = false; DoOverview = false; Fork = false; AppendMode = false; LoadAverage = 0; NoHistory = false; while ((i = getopt(argc, argv, "abFf:Il:L:OSs:T:x")) != EOF) { switch(i) { case 'a': AppendMode = true; break; case 'b': NukeBadArts = true; break; case 'F': Fork = true; break; case 'f': HistoryPath = optarg; break; case 'I': Cutofflow = true; break; case 'l': OverTmpSegSize = atoi(optarg); break; case 'L': LoadAverage = atoi(optarg); break; case 'O': DoOverview = true; break; case 'S': WriteStdout = true; OverTmpSegSize = 0; break; case 's': npairs = atoi(optarg); break; case 'T': TmpDir = optarg; break; case 'x': NoHistory = true; break; default: fprintf(stderr, "%s", usage); exit(1); break; } } argc -= optind; if (argc) { fprintf(stderr, "%s", usage); exit(1); } if (!NoHistory) { if ((p = strrchr(HistoryPath, '/')) == NULL) { /* find the default history file directory */ HistoryDir = innconf->pathdb; } else { *p = '\0'; HistoryDir = xstrdup(HistoryPath); *p = '/'; } if (chdir(HistoryDir) < 0) sysdie("cannot chdir to %s", HistoryDir); } /* Change to the runasuser user and runasgroup group if necessary. */ if (!NoHistory || !WriteStdout) { if (getenv("INN_TESTSUITE") == NULL) ensure_news_user_grp(true, true); } /* Read in the overview schema. */ ARTreadschema(DoOverview); if (DoOverview && !WriteStdout) { /* init the overview setup. */ if (!OVopen(OV_WRITE)) sysdie("cannot open overview"); if (!OVctl(OVSORT, (void *)&sorttype)) die("cannot obtain overview sort information"); if (!Fork) { if (!OVctl(OVCUTOFFLOW, (void *)&Cutofflow)) die("cannot obtain overview cutoff information"); OverAddAllNewsgroups(); } else { OverAddAllNewsgroups(); if (sorttype == OVNOSORT) { buff = concat(innconf->pathbin, "/", "overchan", NULL); if ((Overchan = popen(buff, "w")) == NULL) sysdie("cannot fork overchan process"); free(buff); } OVclose(); } } /* Init the Storage Manager */ val = true; if (!SMsetup(SM_RDWR, (void *)&val) || !SMsetup(SM_PREOPEN, (void *)&val)) sysdie("cannot set up storage manager"); if (!SMinit()) sysdie("cannot initialize storage manager: %s", SMerrorstr); /* Initialise the history manager */ if (!NoHistory) { int flags = HIS_RDWR | HIS_INCORE; if (!AppendMode) flags |= HIS_CREAT; History = HISopen(NULL, innconf->hismethod, flags); if (History == NULL) sysdie("cannot create history handle"); HISctl(History, HISCTLS_NPAIRS, &npairs); if (!HISctl(History, HISCTLS_PATH, HistoryPath)) sysdie("cannot open %s", HistoryPath); } /* * Scan the entire spool, nuke any bad arts if needed, and process each * article. We take a break when the load is too high. */ while ((art = SMnext(art, RETR_ALL)) != NULL) { if (art->len == 0) { if (NukeBadArts && art->data == NULL && art->token != NULL) SMcancel(*art->token); continue; } DoArt(art); if (LoadAverage > 0) { while (getloadavg(load, 1) > 0 && (int) (load[0]) >= LoadAverage) { sleep(1); } } } if (!NoHistory) { /* close history file. */ if (!HISclose(History)) sysdie("cannot close history file"); } if (DoOverview) { if (sorttype == OVNOSORT && Fork) if (fflush(Overchan) == EOF || ferror(Overchan) || pclose(Overchan) == EOF) sysdie("cannot flush overview data"); if (sorttype != OVNOSORT && !WriteStdout) { int status; FlushOverTmpFile(); if(Fork) wait(&status); } if (!WriteStdout) { if ((F = fopen(RebuiltflagPath, "w")) == NULL) sysdie("cannot open %s", RebuiltflagPath); if (fprintf(F, "%ld\n", (long) time (NULL)) == EOF || ferror(F)) sysdie("cannot write rebuilt flag file"); fclose(F); } } if (!Fork && !WriteStdout) OVclose(); exit(0); } inn-2.6.0/expire/Makefile0000644000175200017520000001566612575023702014672 0ustar iuliusiulius## $Id: Makefile 9815 2015-03-25 20:26:58Z iulius $ include ../Makefile.global top = .. CFLAGS = $(GCFLAGS) ALL = convdate expire expireover expirerm fastrm grephistory \ makedbz makehistory prunehistory SOURCES = convdate.c expire.c expireover.c fastrm.c grephistory.c \ makedbz.c makehistory.c prunehistory.c all: $(ALL) warnings: $(MAKE) COPT='$(WARNINGS)' all install: all for F in convdate fastrm grephistory ; do \ $(LI_XPUB) $$F $D$(PATHBIN)/$$F ; \ done for F in expire expireover makedbz makehistory prunehistory ; do \ $(LI_XPRI) $$F $D$(PATHBIN)/$$F ; \ done $(CP_XPRI) expirerm $D$(PATHBIN)/expirerm bootstrap: clean clobber distclean maintclean: rm -f *.o $(ALL) rm -f profiled expirep rm -rf .libs ## Compilation rules. BOTH = $(LIBSTORAGE) $(LIBHIST) $(LIBINN) LINK = $(LIBLD) $(LDFLAGS) -o $@ INNLIBS = $(LIBINN) $(LIBS) STORELIBS = $(BOTH) $(STORAGE_LIBS) $(LIBS) FIX = $(FIXSCRIPT) $(FIXSCRIPT): @echo Run configure before running make. See INSTALL for details. @exit 1 convdate: convdate.o $(LIBINN) ; $(LINK) convdate.o $(INNLIBS) expire: expire.o $(BOTH) ; $(LINK) expire.o $(STORELIBS) expireover: expireover.o $(BOTH) ; $(LINK) expireover.o $(STORELIBS) fastrm: fastrm.o $(BOTH) ; $(LINK) fastrm.o $(STORELIBS) grephistory: grephistory.o $(BOTH) ; $(LINK) grephistory.o $(STORELIBS) makedbz: makedbz.o $(LIBINN) ; $(LINK) makedbz.o $(INNLIBS) makehistory: makehistory.o $(BOTH) ; $(LINK) makehistory.o $(STORELIBS) prunehistory: prunehistory.o $(BOTH) ; $(LINK) prunehistory.o $(STORELIBS) expirerm: expirerm.in $(FIX) ; $(FIX) expirerm.in $(LIBINN): ; (cd ../lib ; $(MAKE)) $(LIBSTORAGE): ; (cd ../storage ; $(MAKE)) $(LIBHIST): ; (cd ../history ; $(MAKE)) ## Profiling. These rules have not been checked for a while and may need ## some work. profiled: expirep date >$@ expirep: expire.c rm -f expire.o $(MAKEPROFILING) expire mv expire expirep rm -f expire.o ## Dependencies. Default list, below, is probably good enough. depend: Makefile $(SOURCES) $(MAKEDEPEND) '$(CFLAGS)' $(SOURCES) # DO NOT DELETE THIS LINE -- make depend depends on it. convdate.o: convdate.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/messages.h \ ../include/inn/libinn.h ../include/inn/xmalloc.h ../include/inn/xwrite.h expire.o: expire.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/history.h \ ../include/inn/innconf.h ../include/inn/messages.h \ ../include/inn/inndcomm.h ../include/inn/libinn.h \ ../include/inn/xmalloc.h ../include/inn/xwrite.h \ ../include/inn/newsuser.h ../include/inn/paths.h \ ../include/inn/storage.h ../include/inn/options.h expireover.o: expireover.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/innconf.h \ ../include/inn/libinn.h ../include/inn/xmalloc.h ../include/inn/xwrite.h \ ../include/inn/messages.h ../include/inn/newsuser.h ../include/inn/ov.h \ ../include/inn/history.h ../include/inn/storage.h \ ../include/inn/options.h ../include/inn/paths.h ../include/inn/qio.h \ ../include/inn/storage.h fastrm.o: fastrm.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/innconf.h \ ../include/inn/messages.h ../include/inn/qio.h ../include/inn/libinn.h \ ../include/inn/xmalloc.h ../include/inn/xwrite.h \ ../include/inn/storage.h ../include/inn/options.h grephistory.o: grephistory.c ../include/clibrary.h ../include/config.h \ ../include/inn/defines.h ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h \ ../include/inn/macros.h ../include/portable/stdbool.h \ ../include/inn/history.h ../include/inn/innconf.h \ ../include/inn/messages.h ../include/inn/libinn.h \ ../include/inn/xmalloc.h ../include/inn/xwrite.h ../include/inn/paths.h \ ../include/inn/storage.h ../include/inn/options.h makedbz.o: makedbz.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/dbz.h \ ../include/inn/libinn.h ../include/inn/xmalloc.h ../include/inn/xwrite.h \ ../include/inn/innconf.h ../include/inn/messages.h \ ../include/inn/newsuser.h ../include/inn/paths.h ../include/inn/qio.h \ ../include/inn/storage.h ../include/inn/options.h makehistory.o: makehistory.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/buffer.h \ ../include/inn/history.h ../include/inn/innconf.h \ ../include/inn/libinn.h ../include/inn/xmalloc.h ../include/inn/xwrite.h \ ../include/inn/messages.h ../include/inn/newsuser.h ../include/inn/ov.h \ ../include/inn/storage.h ../include/inn/options.h ../include/inn/paths.h \ ../include/inn/qio.h ../include/inn/storage.h ../include/inn/vector.h \ ../include/inn/wire.h prunehistory.o: prunehistory.c ../include/config.h \ ../include/inn/defines.h ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/history.h \ ../include/inn/innconf.h ../include/inn/messages.h \ ../include/inn/libinn.h ../include/inn/xmalloc.h ../include/inn/xwrite.h \ ../include/inn/paths.h inn-2.6.0/expire/convdate.c0000644000175200017520000001061012575023702015161 0ustar iuliusiulius/* $Id: convdate.c 9019 2010-03-19 21:27:15Z iulius $ ** ** Convert date strings and numbers to numbers and strings. */ #include "config.h" #include "clibrary.h" #include #include #include "inn/messages.h" #include "inn/libinn.h" static const char usage[] = "\ Usage: convdate -n [date ...]\n\ convdate [-dl] -c [time ...]\n\ convdate [-dl] [-s] [date ...]\n\ \n\ convdate -n converts a date in RFC 5322 format to the number of seconds\n\ since epoch. convdate -s does the same, but converts to a date string.\n\ convdate -c converts seconds since epoch to a date string. The default\n\ output is the output of ctime (normally the same format as returned by the\n\ date command). If -d is given, the output is formatted as a valid Usenet\n\ article Date header. If -l is given with -d, format the time in local\n\ time rather than UTC. If no options are given, the -s behavior is the\n\ default; if no dates are given, the current time is used.\n"; /* Whether to format the output as a Date header. */ static bool date_format = false; /* Whether to use local time instead of UTC. */ static bool date_local = false; /* ** Return true if the given string is entirely digits. */ static bool isdigits(const char *p) { for (; *p; p++) if (!isdigit((unsigned char) *p)) return false; return true; } /* ** Print date corresponding to the provided time_t. By default, the output ** of ctime is printed, but if date_format is true, makedate is used ** instead. If date_local is true, format in local time; otherwise, use ** UTC. Returns success. */ static bool print_date(time_t date) { char date_buffer[128]; char *result; if (date_format) { if (!makedate(date, date_local, date_buffer, sizeof(date_buffer))) { warn("can't format %ld", (long) date); return false; } else { printf("%s\n", date_buffer); } } else { result = ctime(&date); if (result == NULL) { warn("can't format %ld", (long) date); return false; } else { printf("%s", result); } } return true; } /* ** The core function. Given a string representing a date (in some format ** given by the mode) and a mode ('s', 'n', or 'c', corresponding to the ** basic three options to the program), convert the date and print the ** output. date may be NULL, in which case the current date is used. ** Returns true if conversion was successful, false otherwise. */ static bool convdate(const char *date, char mode) { time_t seconds; /* Convert the given date to seconds or obtain the current time. */ if (date == NULL) { seconds = time(NULL); } else if (mode == 'c') { if (!isdigits(date)) { warn("\"%s\" doesn't look like a number", date); return false; } else { seconds = (time_t) atol(date); } } else { seconds = parsedate_rfc5322_lax(date); if (seconds == (time_t) -1) { warn("can't convert \"%s\"", date); return false; } } /* Output the resulting date. */ if (mode == 'n') { printf("%ld\n", (long) seconds); return true; } else { return print_date(seconds); } } int main(int argc, char *argv[]) { int option, status; char *date; char mode = '\0'; message_program_name = "convdate"; /* Parse options. */ while ((option = getopt(argc, argv, "cdhlns")) != EOF) { switch (option) { case 'h': printf("%s\n", usage); exit(0); break; case 'd': date_format = true; break; case 'l': date_local = true; break; case 'c': case 'n': case 's': if (mode != 0) die("only one of -c, -n, or -s is allowed"); mode = option; break; default: fprintf(stderr, "%s", usage); exit(1); break; } } if (mode == '\0') mode = 's'; argc -= optind; argv += optind; /* Perform the desired action for each provided argument. */ if (argc == 0) { exit(convdate(NULL, mode) ? 0 : 1); } else { for (date = *argv, status = 0; date != NULL; date = *++argv) status += (convdate(date, mode) ? 0 : 1); exit(status); } } inn-2.6.0/expire/expirerm.in0000644000175200017520000000141512575023702015400 0ustar iuliusiulius#! /bin/sh # fixscript will replace this line with code to load innshellvars ## $Id: expirerm.in 8572 2009-08-18 13:47:40Z iulius $ ## ## Remove articles listed by expire -z. ## Remove all files specified in the input file. MAIL="${MAILCMD} -s 'Problem removing expired files' ${NEWSMASTER}" #RMPROC="xargs rm" RMPROC="fastrm -e ${SPOOL}" if [ -z "$1" ] ; then echo "Expire called with zero list of files on `hostname`" \ | eval ${MAIL} exit 0 fi if [ ! -f $1 ] ; then echo "Expire called with no files to expire on `hostname`" \ | eval ${MAIL} exit 0 fi eval "cd ${SPOOL} \ && ${RMPROC} <$1 \ && mv $1 ${MOST_LOGS}/expire.list" if [ -f $1 ] ; then echo "Expire had problems removing articles on `hostname`" \ | eval ${MAIL} exit 1 fi inn-2.6.0/expire/makedbz.c0000644000175200017520000001547512575023702015011 0ustar iuliusiulius/* $Id: makedbz.c 9623 2014-03-15 22:29:46Z iulius $ ** ** Rebuild dbz file for history db. */ #include "config.h" #include "clibrary.h" #include #include #include "inn/dbz.h" #include "inn/innconf.h" #include "inn/libinn.h" #include "inn/messages.h" #include "inn/newsuser.h" #include "inn/paths.h" #include "inn/qio.h" #include "inn/storage.h" /* FIXME: once we figure out how to integrate this stuff with the * history API this external visibility of internal voodoo should * go */ #define HIS_FIELDSEP '\t' char *TextFile = NULL; char *HistoryDir = NULL; char *HISTORY = NULL; /* ** Remove the DBZ files for the specified base text file. */ static void RemoveDBZFiles(char *p) { char buff[SMBUF]; snprintf(buff, sizeof(buff), "%s.dir", p); if (unlink(buff) && errno != ENOENT) syswarn("cannot unlink %s", buff); #ifdef DO_TAGGED_HASH snprintf(buff, sizeof(buff), "%s.pag", p); if (unlink(buff) && errno != ENOENT) syswarn("cannot unlink %s", buff); #else snprintf(buff, sizeof(buff), "%s.index", p); if (unlink(buff) && errno != ENOENT) syswarn("cannot unlink %s", buff); snprintf(buff, sizeof(buff), "%s.hash", p); if (unlink(buff) && errno != ENOENT) syswarn("cannot unlink %s", buff); #endif } /* ** Count lines in the history text. A long-winded way of saying "wc -l" */ static off_t Countlines(void) { QIOSTATE *qp; off_t count; /* Open the text file. */ qp = QIOopen(TextFile); if (qp == NULL) sysdie("cannot open %s", TextFile); /* Loop through all lines in the text file. */ count = 0; for (; QIOread(qp) != NULL;) count++; if (QIOerror(qp)) sysdie("cannot read %s near line %lu", TextFile, (unsigned long) count); if (QIOtoolong(qp)) sysdie("line %lu of %s is too long", (unsigned long) count, TextFile); QIOclose(qp); return count; } /* ** Rebuild the DBZ file from the text file. */ static void Rebuild(off_t size, bool IgnoreOld, bool Overwrite) { QIOSTATE *qp; char *p; char *save; off_t count; off_t where; HASH key; char temp[SMBUF]; dbzoptions opt; if (chdir(HistoryDir) < 0) sysdie("cannot chdir to %s", HistoryDir); /* If we are ignoring the old database and the user didn't specify a table size, determine one ourselves from the size of the text history file. Note that this will still use the defaults in dbz if the text file is empty, since size will still be left set to 0. */ if (IgnoreOld == true && size == 0) { size = Countlines(); size += (size / 10); if (size > 0) warn("no size specified, using %ld", (unsigned long) size); } /* Open the text file. */ qp = QIOopen(TextFile); if (qp == NULL) sysdie("cannot open %s", TextFile); /* If using the standard history file, force DBZ to use history.n. */ if (strcmp(TextFile, HISTORY) == 0 && !Overwrite) { snprintf(temp, sizeof(temp), "%s.n", HISTORY); if (link(HISTORY, temp) < 0) sysdie("cannot create temporary link to %s", temp); RemoveDBZFiles(temp); p = temp; } else { temp[0] = '\0'; /* ** only do removedbz files if a. we're using something besides ** $pathdb/history, or b. we're ignoring the old db. */ if (strcmp(TextFile, HISTORY) != 0 || IgnoreOld) RemoveDBZFiles(TextFile); p = TextFile; } /* Open the new database, using the old file if desired and possible. */ dbzgetoptions(&opt); opt.pag_incore = INCORE_MEM; #ifndef DO_TAGGED_HASH opt.exists_incore = INCORE_MEM; #endif dbzsetoptions(opt); if (IgnoreOld) { if (!dbzfresh(p, dbzsize(size))) { syswarn("cannot do dbzfresh"); if (temp[0]) unlink(temp); exit(1); } } else { if (!dbzagain(p, HISTORY)) { syswarn("cannot do dbzagain"); if (temp[0]) unlink(temp); exit(1); } } /* Loop through all lines in the text file. */ count = 0; for (where = QIOtell(qp); (p = QIOread(qp)) != NULL; where = QIOtell(qp)) { count++; if ((save = strchr(p, HIS_FIELDSEP)) == NULL) { warn("bad line #%lu: %.40s", (unsigned long) count, p); if (temp[0]) unlink(temp); exit(1); } *save = '\0'; switch (*p) { case '[': if (strlen(p) != ((sizeof(HASH) * 2) + 2)) { warn("invalid length for hash %s, skipping", p); continue; } key = TextToHash(p+1); break; default: warn("invalid message ID %s in history text", p); continue; } switch (dbzstore(key, where)) { case DBZSTORE_EXISTS: warn("duplicate message ID %s in history text", p); break; case DBZSTORE_ERROR: syswarn("cannot store %s", p); if (temp[0]) unlink(temp); exit(1); default: break; } } if (QIOerror(qp)) { syswarn("cannot read %s near line %lu", TextFile, (unsigned long) count); if (temp[0]) unlink(temp); exit(1); } if (QIOtoolong(qp)) { warn("line %lu is too long", (unsigned long) count); if (temp[0]) unlink(temp); exit(1); } /* Close files. */ QIOclose(qp); if (!dbzclose()) { syswarn("cannot close history"); if (temp[0]) unlink(temp); exit(1); } if (temp[0]) unlink(temp); } static void Usage(void) { fprintf(stderr, "Usage: makedbz [-f histfile] [-s numlines] [-i] [-o]\n"); exit(1); } int main(int argc, char **argv) { bool Overwrite; bool IgnoreOld; off_t size = 0; int i; char *p; /* First thing, set up logging and our identity. */ openlog("makedbz", L_OPENLOG_FLAGS | LOG_PID, LOG_INN_PROG); message_program_name = "makedbz"; /* Set defaults. */ if (!innconf_read(NULL)) exit(1); TextFile = concatpath(innconf->pathdb, INN_PATH_HISTORY); HISTORY = concatpath(innconf->pathdb, INN_PATH_HISTORY); HistoryDir = innconf->pathdb; IgnoreOld = false; Overwrite = false; while ((i = getopt(argc, argv, "s:iof:")) != EOF) { switch (i) { default: Usage(); case 'f': TextFile = optarg; break; case 's': size = atol(optarg); IgnoreOld = true; break; case 'o': Overwrite = true; break; case 'i': IgnoreOld = true; break; } } argc -= optind; if (argc) { Usage(); } if ((p = strrchr(TextFile, '/')) == NULL) { /* find the default history file directory */ HistoryDir = innconf->pathdb; } else { *p = '\0'; HistoryDir = xstrdup(TextFile); *p = '/'; } if (chdir(HistoryDir) < 0) sysdie("cannot chdir to %s", HistoryDir); /* Change to the runasuser user and runasgroup group if necessary. */ ensure_news_user_grp(true, true); Rebuild(size, IgnoreOld, Overwrite); closelog(); exit(0); } inn-2.6.0/expire/expire.c0000644000175200017520000004113612575023702014661 0ustar iuliusiulius/* $Id: expire.c 9019 2010-03-19 21:27:15Z iulius $ ** ** Expire news articles. */ #include "config.h" #include "clibrary.h" #include #include #include #include #include #include "inn/history.h" #include "inn/innconf.h" #include "inn/messages.h" #include "inn/inndcomm.h" #include "inn/libinn.h" #include "inn/newsuser.h" #include "inn/paths.h" #include "inn/storage.h" typedef struct _EXPIRECLASS { time_t Keep; time_t Default; time_t Purge; bool Missing; bool ReportedMissing; } EXPIRECLASS; /* ** Expire-specific stuff. */ #define MAGIC_TIME 49710. static bool EXPtracing; static bool EXPusepost; static bool Ignoreselfexpire = false; static FILE *EXPunlinkfile; static EXPIRECLASS EXPclasses[NUM_STORAGE_CLASSES+1]; static char *EXPreason; static time_t EXPremember; static time_t Now; static time_t RealNow; /* Statistics; for -v flag. */ static char *EXPgraph; static int EXPverbose; static long EXPprocessed; static long EXPunlinked; static long EXPallgone; static long EXPstillhere; static struct history *History; static char *NHistory; static void CleanupAndExit(bool Server, bool Paused, int x); static int EXPsplit(char *p, char sep, char **argv, int count); enum KR {Keep, Remove}; /* ** Open a file or give up. */ static FILE * EXPfopen(bool Unlink, const char *Name, const char *Mode, bool Needclean, bool Server, bool Paused) { FILE *F; if (Unlink && unlink(Name) < 0 && errno != ENOENT) syswarn("cannot remove %s", Name); if ((F = fopen(Name, Mode)) == NULL) { syswarn("cannot open %s in %s mode", Name, Mode); if (Needclean) CleanupAndExit(Server, Paused, 1); else exit(1); } return F; } /* ** Split a line at a specified field separator into a vector and return ** the number of fields found, or -1 on error. */ static int EXPsplit(char *p, char sep, char **argv, int count) { int i; if (!p) return 0; while (*p == sep) ++p; if (!*p) return 0; if (!p) return 0; while (*p == sep) ++p; if (!*p) return 0; for (i = 1, *argv++ = p; *p; ) if (*p++ == sep) { p[-1] = '\0'; for (; *p == sep; p++) ; if (!*p) return i; if (++i == count) /* Overflow. */ return -1; *argv++ = p; } return i; } /* ** Parse a number field converting it into a "when did this start?". ** This makes the "keep it" tests fast, but inverts the logic of ** just about everything you expect. Print a message and return false ** on error. */ static bool EXPgetnum(int line, char *word, time_t *v, const char *name) { char *p; bool SawDot; double d; if (strcasecmp(word, "never") == 0) { *v = (time_t)0; return true; } /* Check the number. We don't have strtod yet. */ for (p = word; ISWHITE(*p); p++) ; if (*p == '+' || *p == '-') p++; for (SawDot = false; *p; p++) if (*p == '.') { if (SawDot) break; SawDot = true; } else if (!isdigit((unsigned char) *p)) break; if (*p) { warn("bad '%c' character in %s field on line %d", *p, name, line); return false; } d = atof(word); if (d > MAGIC_TIME) *v = (time_t)0; else *v = Now - (time_t)(d * 86400.); return true; } /* ** Parse the expiration control file. Return true if okay. */ static bool EXPreadfile(FILE *F) { char *p; int i; int j; bool SawDefault; char buff[BUFSIZ]; char *fields[7]; /* Scan all lines. */ EXPremember = -1; SawDefault = false; for (i = 0; i <= NUM_STORAGE_CLASSES; i++) { EXPclasses[i].ReportedMissing = false; EXPclasses[i].Missing = true; } for (i = 1; fgets(buff, sizeof buff, F) != NULL; i++) { if ((p = strchr(buff, '\n')) == NULL) { warn("line %d too long", i); return false; } *p = '\0'; p = strchr(buff, '#'); if (p) *p = '\0'; else p = buff + strlen(buff); while (--p >= buff) { if (isspace((unsigned char) *p)) *p = '\0'; else break; } if (buff[0] == '\0') continue; if ((j = EXPsplit(buff, ':', fields, ARRAY_SIZE(fields))) == -1) { warn("too many fields on line %d", i); return false; } /* Expired-article remember line? */ if (strcmp(fields[0], "/remember/") == 0) { if (j != 2) { warn("invalid format on line %d", i); return false; } if (EXPremember != -1) { warn("duplicate /remember/ on line %d", i); return false; } if (!EXPgetnum(i, fields[1], &EXPremember, "remember")) return false; continue; } /* Storage class line? */ if (j == 4) { /* Is this the default line? */ if (fields[0][0] == '*' && fields[0][1] == '\0') { if (SawDefault) { warn("duplicate default on line %d", i); return false; } j = NUM_STORAGE_CLASSES; SawDefault = true; } else { j = atoi(fields[0]); if ((j < 0) || (j >= NUM_STORAGE_CLASSES)) warn("bad storage class %d on line %d", j, i); } if (!EXPgetnum(i, fields[1], &EXPclasses[j].Keep, "keep") || !EXPgetnum(i, fields[2], &EXPclasses[j].Default, "default") || !EXPgetnum(i, fields[3], &EXPclasses[j].Purge, "purge")) return false; /* These were turned into offsets, so the test is the opposite * of what you think it should be. If Purge isn't forever, * make sure it's greater then the other two fields. */ if (EXPclasses[j].Purge) { /* Some value not forever; make sure other values are in range. */ if (EXPclasses[j].Keep && EXPclasses[j].Keep < EXPclasses[j].Purge) { warn("keep time longer than purge time on line %d", i); return false; } if (EXPclasses[j].Default && EXPclasses[j].Default < EXPclasses[j].Purge) { warn("default time longer than purge time on line %d", i); return false; } } EXPclasses[j].Missing = false; continue; } /* Regular expiration line -- right number of fields? */ if (j != 5) { warn("bad format on line %d", i); return false; } continue; /* don't process this line--per-group expiry is done by expireover */ } return true; } /* ** Should we keep the specified article? */ static enum KR EXPkeepit(const TOKEN *token, time_t when, time_t Expires) { EXPIRECLASS class; class = EXPclasses[token->class]; if (class.Missing) { if (EXPclasses[NUM_STORAGE_CLASSES].Missing) { /* no default */ if (!class.ReportedMissing) { warn("class definition for %d missing from control file," " assuming it should never expire", token->class); EXPclasses[token->class].ReportedMissing = true; } return Keep; } else { /* use the default */ class = EXPclasses[NUM_STORAGE_CLASSES]; EXPclasses[token->class] = class; } } /* Bad posting date? */ if (when > (RealNow + 86400)) { /* Yes -- force the article to go to right now */ when = Expires ? class.Purge : class.Default; } if (EXPverbose > 2) { if (EXPverbose > 3) printf("%s age = %0.2f\n", TokenToText(*token), (Now - when) / 86400.); if (Expires == 0) { if (when <= class.Default) printf("%s too old (no exp)\n", TokenToText(*token)); } else { if (when <= class.Purge) printf("%s later than purge\n", TokenToText(*token)); if (when >= class.Keep) printf("%s earlier than min\n", TokenToText(*token)); if (Now >= Expires) printf("%s later than header\n", TokenToText(*token)); } } /* If no expiration, make sure it wasn't posted before the default. */ if (Expires == 0) { if (when >= class.Default) return Keep; /* Make sure it's not posted before the purge cut-off and * that it's not due to expire. */ } else { if (when >= class.Purge && (Expires >= Now || when >= class.Keep)) return Keep; } return Remove; } /* ** An article can be removed. Either print a note, or actually remove it. ** Also fill in the article size. */ static void EXPremove(const TOKEN *token) { /* Turn into a filename and get the size if we need it. */ if (EXPverbose > 1) printf("\tunlink %s\n", TokenToText(*token)); if (EXPtracing) { EXPunlinked++; printf("%s\n", TokenToText(*token)); return; } EXPunlinked++; if (EXPunlinkfile) { fprintf(EXPunlinkfile, "%s\n", TokenToText(*token)); if (!ferror(EXPunlinkfile)) return; syswarn("cannot write to -z file (will ignore it for rest of run)"); fclose(EXPunlinkfile); EXPunlinkfile = NULL; } if (!SMcancel(*token) && SMerrno != SMERR_NOENT && SMerrno != SMERR_UNINIT) warn("cannot unlink %s", TokenToText(*token)); } /* ** Do the work of expiring one line. */ static bool EXPdoline(void *cookie UNUSED, time_t arrived, time_t posted, time_t expires, TOKEN *token) { time_t when; bool HasSelfexpire = false; bool Selfexpired = false; ARTHANDLE *article; enum KR kr; bool r; if (innconf->groupbaseexpiry || SMprobe(SELFEXPIRE, token, NULL)) { if ((article = SMretrieve(*token, RETR_STAT)) == (ARTHANDLE *)NULL) { HasSelfexpire = true; Selfexpired = true; } else { /* the article is still alive */ SMfreearticle(article); if (innconf->groupbaseexpiry || !Ignoreselfexpire) HasSelfexpire = true; } } if (EXPusepost && posted != 0) when = posted; else when = arrived; EXPprocessed++; if (HasSelfexpire) { if (Selfexpired || token->type == TOKEN_EMPTY) { EXPallgone++; r = false; } else { EXPstillhere++; r = true; } } else { kr = EXPkeepit(token, when, expires); if (kr == Remove) { EXPremove(token); EXPallgone++; r = false; } else { EXPstillhere++; r = true; } } return r; } /* ** Clean up link with the server and exit. */ static void CleanupAndExit(bool Server, bool Paused, int x) { FILE *F; if (Server) { ICCreserve(""); if (Paused && ICCgo(EXPreason) != 0) { syswarn("cannot unpause server"); x = 1; } } if (Server && ICCclose() < 0) { syswarn("cannot close communication link to server"); x = 1; } if (EXPunlinkfile && fclose(EXPunlinkfile) == EOF) { syswarn("cannot close -z file"); x = 1; } /* Report stats. */ if (EXPverbose) { printf("Article lines processed %8ld\n", EXPprocessed); printf("Articles retained %8ld\n", EXPstillhere); printf("Entries expired %8ld\n", EXPallgone); if (!innconf->groupbaseexpiry) printf("Articles dropped %8ld\n", EXPunlinked); } /* Append statistics to a summary file */ if (EXPgraph) { F = EXPfopen(false, EXPgraph, "a", false, false, false); fprintf(F, "%ld %ld %ld %ld %ld\n", (long)Now, EXPprocessed, EXPstillhere, EXPallgone, EXPunlinked); fclose(F); } SMshutdown(); HISclose(History); if (EXPreason != NULL) free(EXPreason); if (NHistory != NULL) free(NHistory); closelog(); exit(x); } /* ** Print a usage message and exit. */ static void Usage(void) { fprintf(stderr, "Usage: expire [flags] [expire.ctl]\n"); exit(1); } int main(int ac, char *av[]) { int i; char *p; FILE *F; char *HistoryText; const char *NHistoryPath = NULL; const char *NHistoryText = NULL; char *EXPhistdir; char buff[SMBUF]; bool Server; bool Bad; bool IgnoreOld; bool Writing; bool UnlinkFile; bool val; time_t TimeWarp; size_t Size = 0; /* First thing, set up logging and our identity. */ openlog("expire", L_OPENLOG_FLAGS | LOG_PID, LOG_INN_PROG); message_program_name = "expire"; /* Set defaults. */ Server = true; IgnoreOld = false; Writing = true; TimeWarp = 0; UnlinkFile = false; if (!innconf_read(NULL)) exit(1); HistoryText = concatpath(innconf->pathdb, INN_PATH_HISTORY); umask(NEWSUMASK); /* find the default history file directory */ EXPhistdir = xstrdup(HistoryText); p = strrchr(EXPhistdir, '/'); if (p != NULL) { *p = '\0'; } /* Parse JCL. */ while ((i = getopt(ac, av, "d:f:g:h:iNnpr:s:tv:w:xz:")) != EOF) switch (i) { default: Usage(); /* NOTREACHED */ case 'd': NHistoryPath = optarg; break; case 'f': NHistoryText = optarg; break; case 'g': EXPgraph = optarg; break; case 'h': HistoryText = optarg; break; case 'i': IgnoreOld = true; break; case 'N': Ignoreselfexpire = true; break; case 'n': Server = false; break; case 'p': EXPusepost = true; break; case 'r': EXPreason = xstrdup(optarg); break; case 's': Size = atoi(optarg); break; case 't': EXPtracing = true; break; case 'v': EXPverbose = atoi(optarg); break; case 'w': TimeWarp = (time_t)(atof(optarg) * 86400.); break; case 'x': Writing = false; break; case 'z': EXPunlinkfile = EXPfopen(true, optarg, "a", false, false, false); UnlinkFile = true; break; } ac -= optind; av += optind; if ((ac != 0 && ac != 1)) Usage(); /* if EXPtracing is set, then pass in a path, this ensures we * don't replace the existing history files */ if (EXPtracing || NHistoryText || NHistoryPath) { if (NHistoryPath == NULL) NHistoryPath = innconf->pathdb; if (NHistoryText == NULL) NHistoryText = INN_PATH_HISTORY; NHistory = concatpath(NHistoryPath, NHistoryText); } else { NHistory = NULL; } time(&Now); RealNow = Now; Now += TimeWarp; /* Change to the runasuser user and runasgroup group if necessary. */ ensure_news_user_grp(true, true); /* Parse the control file. */ if (av[0]) { if (strcmp(av[0], "-") == 0) F = stdin; else F = EXPfopen(false, av[0], "r", false, false, false); } else { char *path; path = concatpath(innconf->pathetc, INN_PATH_EXPIRECTL); F = EXPfopen(false, path, "r", false, false, false); free(path); } if (!EXPreadfile(F)) { fclose(F); die("format error in expire.ctl"); } fclose(F); /* Set up the link, reserve the lock. */ if (Server) { if (EXPreason == NULL) { snprintf(buff, sizeof(buff), "Expiring process %ld", (long) getpid()); EXPreason = xstrdup(buff); } } else { EXPreason = NULL; } if (Server) { /* If we fail, leave evidence behind. */ if (ICCopen() < 0) { syswarn("cannot open channel to server"); CleanupAndExit(false, false, 1); } if (ICCreserve((char *)EXPreason) != 0) { warn("cannot reserve server"); CleanupAndExit(false, false, 1); } } History = HISopen(HistoryText, innconf->hismethod, HIS_RDONLY); if (!History) { warn("cannot open history"); CleanupAndExit(Server, false, 1); } /* Ignore failure on the HISctl()s, if the underlying history * manager doesn't implement them its not a disaster */ HISctl(History, HISCTLS_IGNOREOLD, &IgnoreOld); if (Size != 0) { HISctl(History, HISCTLS_NPAIRS, &Size); } val = true; if (!SMsetup(SM_RDWR, (void *)&val) || !SMsetup(SM_PREOPEN, (void *)&val)) { warn("cannot set up storage manager"); CleanupAndExit(Server, false, 1); } if (!SMinit()) { warn("cannot initialize storage manager: %s", SMerrorstr); CleanupAndExit(Server, false, 1); } if (chdir(EXPhistdir) < 0) { syswarn("cannot chdir to %s", EXPhistdir); CleanupAndExit(Server, false, 1); } Bad = HISexpire(History, NHistory, EXPreason, Writing, NULL, EXPremember, EXPdoline) == false; if (UnlinkFile && EXPunlinkfile == NULL) /* Got -z but file was closed; oops. */ Bad = true; /* If we're done okay, and we're not tracing, slip in the new files. */ if (EXPverbose) { if (Bad) printf("Expire errors: history files not updated.\n"); if (EXPtracing) printf("Expire tracing: history files not updated.\n"); } if (!Bad && NHistory != NULL) { snprintf(buff, sizeof(buff), "%s.n.done", NHistory); fclose(EXPfopen(false, buff, "w", true, Server, false)); CleanupAndExit(Server, false, Bad ? 1 : 0); } CleanupAndExit(Server, !Bad, Bad ? 1 : 0); /* NOTREACHED */ abort(); } inn-2.6.0/expire/grephistory.c0000644000175200017520000001016112575023702015736 0ustar iuliusiulius/* $Id: grephistory.c 8578 2009-08-18 20:11:22Z iulius $ ** ** Get data from history database. */ #include "clibrary.h" #include #include #include "inn/history.h" #include "inn/innconf.h" #include "inn/messages.h" #include "inn/libinn.h" #include "inn/paths.h" #include "inn/storage.h" /* ** Read stdin for list of Message-ID's, output list of ones we ** don't have. Or, output list of files for ones we DO have. */ static void IhaveSendme(struct history *h, char What) { char *p; char *q; char buff[BUFSIZ]; while (fgets(buff, sizeof buff, stdin) != NULL) { time_t arrived, posted, expires; TOKEN token; for (p = buff; ISWHITE(*p); p++) ; if (*p != '<') continue; for (q = p; *q && *q != '>' && !ISWHITE(*q); q++) ; if (*q != '>') continue; *++q = '\0'; if (!HIScheck(h, p)) { if (What == 'i') printf("%s\n", p); continue; } /* Ihave -- say if we want it, and continue. */ if (What == 'i') { continue; } if (HISlookup(h, p, &arrived, &posted, &expires, &token)) printf("%s\n", TokenToText(token)); } } /* ** Print a usage message and exit. */ static void Usage(void) { fprintf(stderr, "Usage: grephistory [flags] MessageID\n"); exit(1); } int main(int ac, char *av[]) { int i; const char *History; char *key; char What; struct history *history; time_t arrived, posted, expires; TOKEN token; unsigned int Verbosity = 0; /* First thing, set up logging and our identity. */ openlog("grephistory", L_OPENLOG_FLAGS | LOG_PID, LOG_INN_PROG); message_program_name = "grephistory"; /* Set defaults. */ if (!innconf_read(NULL)) exit(1); History = concatpath(innconf->pathdb, INN_PATH_HISTORY); What = '?'; /* Parse JCL. */ while ((i = getopt(ac, av, "vf:eilnqs")) != EOF) switch (i) { default: Usage(); /* NOTREACHED */ case 'v': Verbosity++; break; case 'f': History = optarg; break; case 'e': case 'i': case 'l': case 'n': case 'q': case 's': if (What != '?') { die("only one [eilnqs] flag allowed"); } What = (char)i; break; } ac -= optind; av += optind; history = HISopen(History, innconf->hismethod, HIS_RDONLY); if (history == NULL) die("cannot open history"); /* Set operating mode. */ switch (What) { case '?': What = 'n'; break; case 'i': case 's': IhaveSendme(history, What); HISclose(history); exit(0); /* NOTREACHED */ } /* All modes other than -i and -l want a message-ID. */ if (ac != 1) Usage(); key = av[0]; if (*key == '[') { warn("accessing history by hash isn't supported"); HISclose(history); exit(1); } else { /* Add optional braces if not already present. */ if (*key != '<') key = concat("<", key, ">", (char *) 0); } if (!HIScheck(history, key)) { if (What == 'n') { if (Verbosity > 0) die("not found (hash is %s)", HashToText(HashMessageID(key))); else die("not found"); } } else if (What != 'q') { if (HISlookup(history, key, &arrived, &posted, &expires, &token)) { if (What == 'l') { if (expires <= 0) { printf("[%s]\t%ld~-~%ld\t%s\n", HashToText(HashMessageID(key)), (long)arrived, (long)posted, TokenToText(token)); } else { printf("[%s]\t%ld~%ld~%ld\t%s\n", HashToText(HashMessageID(key)), (long)arrived, (long)expires, (long)posted, TokenToText(token)); } } else { if (Verbosity > 0) printf("%s (hash is %s)\n", TokenToText(token), HashToText(HashMessageID(key))); else printf("%s\n", TokenToText(token)); } } else if (What == 'n') { if (Verbosity > 0) printf("/dev/null (hash is %s)\n", HashToText(HashMessageID(key))); else printf("/dev/null\n"); } } HISclose(history); return 0; } inn-2.6.0/expire/prunehistory.c0000644000175200017520000000504712575023702016141 0ustar iuliusiulius/* $Id: prunehistory.c 9623 2014-03-15 22:29:46Z iulius $ ** ** Prune file names from history file. */ #include "config.h" #include "clibrary.h" #include #include #include "inn/history.h" #include "inn/innconf.h" #include "inn/messages.h" #include "inn/libinn.h" #include "inn/paths.h" /* ** Print usage message and exit. */ static void Usage(void) { fprintf(stderr, "Usage: prunehistory [-p] [-f file]\n"); exit(1); } int main(int ac, char *av[]) { char *p; int i; char buff[BUFSIZ]; const char *History; bool Passing; struct history *history = NULL; int rc = 0; /* First thing, set up logging and our identity. */ openlog("prunehistory", L_OPENLOG_FLAGS | LOG_PID, LOG_INN_PROG); message_program_name = "prunehistory"; /* Set defaults. */ if (!innconf_read(NULL)) exit(1); History = concatpath(innconf->pathdb, INN_PATH_HISTORY); Passing = false; /* Parse JCL. */ while ((i = getopt(ac, av, "f:p")) != EOF) switch (i) { default: Usage(); /* NOTREACHED */ case 'f': History = optarg; break; case 'p': Passing = true; break; } ac -= optind; if (ac) { Usage(); rc = 1; goto fail; } history = HISopen(History, innconf->hismethod, HIS_RDWR); if (history == NULL) { syswarn("cannot set up %s database", History); rc = 1; goto fail; } /* Loop over all input. */ while (fgets(buff, sizeof buff, stdin) != NULL) { time_t arrived, posted, expires; if ((p = strchr(buff, '\n')) == NULL) { if (Passing) printf("%s\n", buff); else warn("line too long, ignored: %.40s", buff); continue; } *p = '\0'; /* Ignore blank and comment lines. */ if (buff[0] == '\0' || buff[0] == '#') { if (Passing) printf("%s\n", buff); continue; } if (buff[0] != '<' || (p = strchr(buff, '>')) == NULL) { if (Passing) printf("%s\n", buff); else warn("line doesn't start with a message ID, ignored: %.40s", buff); continue; } *++p = '\0'; if (HISlookup(history, buff, &arrived, &posted, &expires, NULL)) { if (!HISreplace(history, buff, arrived, posted, expires, NULL)) syswarn("cannot write new text for %s", buff); } else { syswarn("no entry for %s", buff); } } fail: /* Close files; we're done. */ if (history != NULL && !HISclose(history)) { syswarn("cannot close %s", History); rc = 1; } return rc; } inn-2.6.0/HACKING0000644000175200017520000011647712575023702012727 0ustar iuliusiuliusHacking INN This file is for people who are interested in making modifications to INN. Normal users can safely skip reading it. It is intended primarily as a guide, resource, and accumulation of tips for maintainers and contributors, and secondarily as documentation of some of INN's internals. First of all, if you plan on working on INN source, please start from the current development tree. There may be significant changes from the previous full release, so starting from development sources will make it considerably easier to integrate your work. You can check out the current development tree using Subversion from: or view it with Trac source browser at: You will need autoconf 2.64 or later to use the development tree. After checking out the tree, run "./autogen" to generate the necessary autoconf files. Nightly snapshots can be found at: Configuring and Portability All INN code should be written expecting ANSI C and POSIX. There is no need to attempt to support pre-ANSI compilers, and ANSI-only features such as , string concatenation, "#elif", and token pasting may be used freely. So far as possible, INN is written to attempt to be portable to any system new enough that someone is likely to want to run a news server on it, but whenever possible this portability should be provided by checking for standard behavior in configure and supplying replacements for standard functions that are missing. When there is a conflict between ANSI C and C99, INN code should be written expecting C99 and autoconf used to patch up the differences. Try to avoid using "#ifdef" and the like in the middle of code as much as possible. Instead, try to isolate the necessary portability bits and include them in libinn or at least in conditional macros separate from the code. Trying to read code littered with conditional compilation directives is much more difficult. The shell script configure at the top level of the source tree is generated by autoconf from configure.ac and the additional macros in the m4 directory, and include/config.h.in is generated by autoheader from configure.ac. At configure time, configure generates include/config.h and several other files based on options it was given and what it discovers about the target system. All modifications to configure should instead be made to configure.ac. The autoconf manual (available using "info autoconf" if you have autoconf and the GNU info utilities installed on your system) is a valuable reference when making any modifications. To regenerate configure, just run "autoconf". To regenerate include/config.h.in, run "autoheader". Alternately, to regenerate both, you can run "./autogen". Please don't include patches to either configure or include/config.h.in when sending patches to INN; instead, note in your patch that those files must be regenerated. These (and all other) generated files should not be checked into Subversion. At the time of this writing, autoconf 2.64 or later is required. The supporting files for autoconf are in the support subdirectory, including the files config.guess and config.sub to determine the system name and ltmain.sh for libtool support. The latter file comes from the Debian package of Libtool, available from (ltmain.sh is in the libltdl/config subdirectory created after building the package with for instance debuild); the canonical version of the former two are available from (which currently redirects to ). In addition, m4/libtool.m4 and a few others m4 files are just a copy of the corresponding files in the libltdl/m4 subdirectory of the Debian package of the libtool distribution. (Using libtool without using automake requires a few odd hacks.) New versions should be checked in periodically when available. There are no INN-specific modifications to those files except for ltmain.sh which recognizes the additional -B flag that INN's install-sh script uses. This script should also be updated at the same time from ; it similarly contains local modifications in order to support the additional -B flag, as well as a few other changes mentioned in a comment at the beginning of the file. Autobuilds Automatic build logs on several platforms are available at . Documentation INN's documentation is currently somewhat in a state of flux. Much of the documentation is still in the form of man pages written directly in nroff. Some parts of the documentation have been rewritten in POD; that documentation can be found in doc/pod. The canonical source for most of the text documentation, including README, INSTALL, NEWS, and this file, is also POD. If you're modifying some part of INN's documentation and see that it has a POD version in doc/pod, you should make the modifications to the POD source and then regenerate the derived files. For a quick introduction to POD, see the perlpod(1) man page on your system (it should be installed if you have Perl installed). When writing new documentation, write in whatever format you care to; if necessary, we can always convert it to POD or whatever else we want to use. Having the documentation exist in *some* form is more important than what language you write it in. If you don't have a preference, however, we prefer new documentation to be in POD. If you use POD or regenerate POD documentation, please install something close to the latest versions of the POD processing utilities to avoid changes to the documentation depending on who generated it last. You can find the latest version on CPAN (ftp.perl.org or another mirror) in modules/by-module/Pod. You'll need PodParser (for versions of Perl before 5.6.1; 5.6.1 and later come with a recent enough version) and the latest version of podlators. For versions of Perl earlier than 5.005, you'll also need "File::Spec" in modules/by-module/File. podlators 1.25 or later will build INN's documentation without significant changes from the versions that are checked into the repository. There are makefile rules in doc/pod/Makefile to build all of the documentation whose master form is POD; if you add additional documentation, please add a rule there as well. Documentation should be generated by cd'ing to doc/pod and typing "make file" where "file" is the relative path to the documentation file. This will get all of the various flags right for pod2text or pod2man. Error Handling INN has a set of generic error handling routines that should be used as much as possible so that the same syntax can be used for reporting errors everywhere in INN. The six basic functions are notice, sysnotice, warn, syswarn, die, and sysdie; notice prints or logs an informative message, warn prints or logs a warning, and die does the same and then exits the current program. The sys* versions add a colon, a space, and the value of strerror(errno) to the end of the message, and should be used to report failing system calls. All of the actual error reporting is done via error handlers, and a program can register its own handlers in addition to or instead of the default one. The default error handler (message_log_stderr) prints to stderr, prepending the value of message_program_name if it's set to something other than NULL. Several other error handlers are available, particularly including ones that use syslog. See include/inn/messages.h for all of the available options. There is a different set of error handlers for notice/sysnotice, warn/syswarn, and die/sysdie. To set them, make calls like: message_handlers_warn(1, message_log_stderr); message_handlers_die(2, message_log_stderr, message_log_syslog_err); The first argument is the number of handlers, and the remaining arguments are pointers to functions taking an int (the length of the formatted message), a const char * (the format), a va_list (the arguments), and an int that's 0 if warn or die was called and equal to the value of errno if syswarn or sysdie was called. The length of the formatted message is obtained by calling vsnprintf with the provided format and arguments, and therefore is reliable to use as the size of a buffer to malloc to hold the result of formatting the message provided that vsnprintf is used to format it (warning: the system vsprintf may produce more output under some circumstances, so always use vsnprintf). The error handler can do anything it wishes; each error handler is called in the sequence given. Error handlers shouldn't call warn or die unless great caution is taken to prevent infinite recursion. Also be aware that sysdie is called if malloc fails in xmalloc, so if the error handler needs to allocate memory, it must not use xmalloc or a related function to do so and it must not call die to report failure. The default syslog handlers report memory allocation failure to stderr and exit. Finally, die and sysdie support an additional handler (the external variable message_fatal_cleanup) that's called immediate before exiting, takes no arguments, and returns an int which is used as the argument for exit. It can do any necessary global cleanup, call abort instead to generate a core dump or the like. The advantage of using this system everywhere in INN is that library code can use notice, warn, and die to report messages and errors, and each calling program can set up the handlers as appropriate to make sure the errors go to the right place. The default handler is fine for interactive programs; for programs that run from interactive scripts, adding something like: message_program_name = "program"; to the beginning of main (where program is the name of the program) will make it easier to figure out which program the script calls is failing. For programs that may also be called non-interactively, like rnews, one may want to set up handlers like: message_handlers_notice(2, message_log_stdout, message_log_syslog_info); message_handlers_warn(2, message_log_stderr, message_log_syslog_warning); message_handlers_die(2, message_log_stderr, message_log_syslog_err); Finally, for daemons and other non-interactive programs, one may want to do: message_handlers_notice(1, message_log_syslog_info); message_handlers_warn(1, message_log_syslog_warning); message_handlers_die(1, message_log_syslog_err); to report errors only via syslog. (Note that if you use syslog error handlers, the program should call openlog first thing to make sure they are logged with the right facility.) For historical reasons, error messages that are fatal to the news subsystem are logged at the LOG_CRIT priority, and therefore die in innd should use message_log_syslog_crit. This seems like priority inflation and may change in the future to message_log_syslog_err. Test Suite The test suite for INN is located in the tests directory and is just getting started. The test suite consists of a set of programs listed in tests/TESTS and the scaffolding in the runtests program. Adding new tests is very straightforward and very flexible. Just write a program that tests some part of INN, put it in a directory under tests named after the part of INN it's testing, and have it output first a line containing the count of test cases in that file, and then for each test a line saying "ok n" or "not ok n" where "n" is the test case number. (If a test is skipped for some reason, such as a test of an optional feature that wasn't compiled into INN, the test program should output "ok n # skip".) Add any rules necessary to build the test to tests/Makefile (note that for simplicity it doesn't recurse into subdirectories) and make sure it creates an executable ending in .t. Then add the name of the test to tests/TESTS, without the .t ending. For C tests, you probably want to use the functions in tests/tap/basic.c (prototypes in tests/tap/basic.h) to handle all of the output. For shell script tests, tests/tap/libtap.sh contains helpful shell functions. See the existing tests for some hints about how to write new tests, as well as the tests/README guide. One naming convention: to distinguish more easily between for example lib/error.c (the implementation) and tests/lib/error-t.c (the test suite), we add -t to the end of the test file names. So tests/lib/error-t.c is the source that compiles into an executable tests/lib/error.t which is run by putting a line in tests/TESTS of just "lib/error". Note that tests don't have to be written in C; in fact, lib/xmalloc.t is just a shell script (that calls a supporting C program). Tests can be written in shell or Perl (but other languages should be avoided because someone who wants to run the test suite may not have it) and just have to follow the above output conventions. Additions to the test suite, no matter how simple, are very welcome. Running the test suite is done with: make check A single test can be run with verbose ouput via: tests/runtests -o Using runtests ensures that necessary environment variables are set up. Makefiles All INN makefiles include Makefile.global at the top level, and only that makefile is a configure substitution target. This has the disadvantage that configure's normal support for building in a tree outside of the source tree doesn't work, but it has the significant advantage of making configure run much faster and allowing one to run "make" in any subdirectory and pick up all the definitions and settings from the top level configuration. All INN makefiles should also set "$(top)" to be the path to the top of the build directory (usually relative). This path is used to find various programs like fixscript and libtool so that the same macros (set in Makefile.global) can be used all over INN. The format of INN's makefiles is mostly standardized; the best examples of the format are probably frontends/Makefile and backends/Makefile, at least for directories with lots of separate programs. The "ALL" variable holds all the files that should be generated, "EXTRA" those additional files that were generated by configure, and "SOURCES" the C source files for generating tag information. There are a set of standard installation commands defined in make variables by Makefile.global, and these should be used for all file installations. See the comment blocks in Makefile.global.in for information on what commands are available and when they should be used. There are also variables set for each of the installation directories that INN uses, for use in building the list of installed paths to files. Each subdirectory makefile should have the targets "all" (the default), "clean", "clobber"/"distclean", "install", and "profiled". The "profiled" target generates a profiling version of the programs (although this hasn't been tested much). These rules should be present and empty in those directories where they don't apply. Be sure to test compiling with both static and dynamic libraries and make sure that all the libtool support works correctly. All linking steps, and the compile steps for all library source, should be done through "$(LIBCC)" and "$(LIBLD)" (which will be set as appropriate in Makefile.global). Scripts INN comes with and installs a large number of different scripts, both Bourne shell and Perl, and also comes with support for Tcl scripts (although it doesn't come with any). Shell variables containing both configure-time information and configuration information from inn.conf are set by the innshellvars support libraries, so the only system-specific configuration that should have to be done is fixing the right path to the interpreter and adding a line to load the appropriate innshellvars. support/fixscript, built by configure, does this. It takes a .in file and generates the final script (removing the .in) by fixing the path to the interpreter on the first line and replacing the second line, whatever it is, with code to load the innshellvars appropriate for that interpreter. (If invoked with -i, it just fixes the interpreter path.) Scripts should use innshellvars (via fixscript) to get the right path and the right variables whenever possible, rather than having configure substitute values in them. Any values needed at run-time should instead be available from all of the different innshellvars. As for Perl, the "INN::Config" module has the same features as innshellvars.pl (only kept for compatibility reasons with old scripts not shipped with INN); however, it can be safely used with warnings on in Perl scripts. See the existing scripts for examples of how this is done. Include Files Include files relevant to all of INN, or relevant to the two libraries built as part of INN (the utility libinn library and the libstorage library that contains all storage and overview functions) are found in the include directory; other include files relevant only to a portion of INN are found in the relevant directory. Practically all INN source files will start with: #include "config.h" #include "clibrary.h" The first picks up all defines generated by autoconf and is necessary for types that may not be present on all systems (*uid_t*, *pid_t*, *size_t*, *uint32_t*, and the like). It therefore should be included before any other headers that use those types, as well as to get general configuration information. It also includes inn/defines.h and inn/options.h, which pick up additional support macros and compile-time configuration. The second is portably equivalent to: #include #include #include #include #include #include #include #include except that it doesn't include headers that are missing on a given system, replaces functions not found on the system with the INN equivalents, provides macros that INN assumes are available but which weren't found, and defines some additional portability things. Even if this is more headers than the source file actually needs, it's generally better to just include clibrary.h rather than trying to duplicate the autoconf-driven hackery that it does to do things portably. The primary exception is for source files in lib that only define a single function and are used for portability; those may want to include only config.h so that they can be easily used in other projects that use autoconf. config.h is a fairly standard header name for this purpose. clibrary.h does also include config.h, but it's somewhat poor form to rely on this; it's better to explicitly list the header dependencies for the benefit of someone else reading the code. There are portable wrappers around several header files that have known portability traps or that need some fixing up on some platforms. Look in include/portable and familiarize yourself with them and use them where appropriate. Another frequently included header file is inn/libinn.h, which among other things defines xmalloc(), xrealloc(), xstrdup(), and xcalloc(), which are checked versions of the standard memory allocation routines that terminate the program if the memory allocation fails. These should generally always be used instead of the regular C versions. inn/libinn.h also provides various other utility functions that are frequently used. inn/paths.h includes a wide variety of paths determined at configure time, both default paths to various parts of INN and paths to programs. Don't just use the default paths, though, if they're also configurable in inn.conf; instead, call innconf_read() and use the global *innconf* structure. Other files in include are interfaces to particular bits of INN library functionality or are used for other purposes; see the comments in each file. Eventually, the header files will be separated into installed header files and uninstalled header files; the latter are those headers that are used only for compiling INN and aren't useful for users of INN's libraries (such as clibrary.h). All of the installed headers will live in include/inn and be installed in a subdirectory named inn in the configured include directory. This conversion is still in progress. When writing header files, remember that C reserves all identifiers beginning with two underscores and all identifiers beginning with an underscore and a capital letter for the use of the implementation; don't use any identifiers with names like that. Additionally, any identifier beginning with an underscore and a lower-case letter is reserved in file scope, which means that such identifiers can only be used by INN for the name of structure members or function arguments in function prototypes. Try to pay attention to the impact of a header file on the program namespace, particularly for installed header files in include/inn. All symbols defined by a header file should ideally begin with "INN_", "inn_", or some other unique prefix indicating the subsystem that symbol is part of, to avoid accidental conflicts with symbols defined by the program that uses that header file. Libraries INN includes three shared libraries, in lib, storage, and history. Whenever any of the source to those libraries is modified, the library version information must be updated at the top of the Makefile in that directory. Follow the instructions in the Libtool manual under Versioning / Updating version info, with the exception that, contrary to point 2, it's useful to those running snapshots to update the version information more frequently than for each public release. Remember that the version information has to be updated to allow recovery from a "make update" command. Coding Style INN has quite a variety of coding styles intermixed. As with all programs, it's preferrable when making minor modifications to keep the coding style of the code you're modifying. In INN, that will vary by file. (Over time we're trying to standardize on one coding style, so changing the region you worked on to fit the general coding style is also acceptable). If you're writing a substantial new piece of code, the prevailing "standard" INN coding style is the following: * Write in regular ANSI C whenever possible. Use the normal ANSI and POSIX constructs and use autoconf or portability wrappers to fix things up beforehand so that the code itself can read like regular ANSI or POSIX code. Code should be written so that it works as expected on a modern platform and is fixed up with portability tricks for older platforms, not the other way around. You may assume an ANSI C compiler. Try to use const wherever appropriate. Don't use register; modern compilers will do as good of a job as you will in choosing what to put into a register. Don't bother with restrict (at least yet). * Use string handling functions that take counts for the size of the buffer whenever possible. This means using snprintf in preference to sprintf and using strlcpy and strlcat in preference to strcpy and strcat. Also, use strlcpy and strlcat instead of strncpy and strncat, as it is much easier to audit uses of the former than the latter. strlcpy is like strncpy except that it always nul-terminates and doesn't fill the rest of the buffer with nuls, making it more efficient. strlcat is like strncat except that it always nul-terminates and it takes the total size of the buffer as its third argument rather than just the amount of space left. All of these functions are guaranteed to be available; there are replacements in lib for systems that don't have them. If you have to use a string copying routine that doesn't nul-terminate, use memcpy instead. Avoid introducing any uses of strcpy, strcat, strncpy, or strncat so that we can use grep to find dangerous usages and switch them to better functions. * Avoid "#ifdef" and friends whenever possible. Particularly avoid using them in the middle of code blocks. Try to hide all portability preprocessor magic in header files or in portability code in lib. When something just has to be done two completely different ways depending on the platform or compile options or the like, try to abstract that functionality out into a generic function and provide two separate implementations using "#ifdef"; then the main code can just call that function. If you do have to use preprocessor defines, note that if you always define them to either 0 or 1 (never use "#define" without a second argument), you can use the preprocessor define in a regular if statement rather than using "#if" or "#ifdef". Make use of this instead of "#ifdef" when possible, since that way the compiler will still syntax-check the other branch for you and it makes it far easier to convert the code to use a run-time check if necessary. (Unfortunately, this trick can't be used if one branch may call functions unavailable on a particular platform.) * Avoid uses of fixed-width buffers except in performance-critical code, as it's harder to be sure that such code is correct and it tends to be less flexible later on. If you need a reusable, resizable memory buffer, one is provided in lib/buffer.c. * Avoid uses of static variables whenever possible, particularly in libraries, because it interferes with making the code re-entrant down the road and makes it harder to follow what's going on. Similarly, avoid using global variables whenever possible, and if they are required, try to wrap them into structures that could later be changed into arguments to the affected functions. * Use a roughly BSD indentation style but with four-space indents. This means no space before the parenthesis around function arguments, open brace on the same line as if/while/for, and close and open brace on the same line as else. * Introductory comments for functions or files are generally written as: /* ** Introductory comment. */ Other multiline comments in the source are generally written as: /* This is a multiline comment. */ Comments before functions saying what they do are nice to have. In general, the RCS/SVN "Id" tag is on the first line of each source file since it's useful to know when a file was last modified. * Checks for NULL pointers are preferrably written out explicitly; in other words, use: if (p != NULL) rather than: if (p) to make it clearer what the code is assuming. * It's better to always put the body of an "if" statement on a separate line, even if it's only a single line. In other words, write: if (p != NULL) return p; and not: if (p != NULL) return p; This is in part for a practical reason: some code coverage analysis tools like purecov will count the second example above as a single line and won't notice if the condition always evaluates the same way. * Plain structs make perfectly reasonable abstract data types; it's not necessary to typedef the struct to something else. Structs are actually very useful for opaque data structures, since you can predeclare them and then manipulate pointers to them without ever having to know what the contents look like. Please try to avoid typedefs except for function pointers or other extremely confusing data types, or for data types where we really gain some significant data abstraction from hiding the underlying data type. Also avoid using the "_t" suffix for any type; all types ending in "_t" are reserved by POSIX. For typedefs of function pointer types, a suffix of "_func" usually works. This style point is currently widely violated inside of INN itself; INN originally made extensive use of typedefs. * When noting something that should be improved later, add a comment containing "FIXME:" so that one can easily grep for such comments. INN's indentation style roughly corresponds to that produced by GNU indent 2.2.6 with the following options: -bad -bap -nsob -fca -lc78 -cd41 -cp1 -br -ce -cdw -cli0 -ss -npcs -ncs -di1 -nbc -psl -brs -i4 -ci4 -lp -ts8 -nut -ip5 -lps -l78 -bbo -hnl Unfortunately, indent currently doesn't get everything right (it has problems with spacing around struct pointer arguments in functions, wants to put in a space between a dereference of a function pointer and the arguments to the called function, misidentifies some macro calls as being type declarations, and fouls up long but simple case statements). It would be excellent if someday we could just run all of INN's code through indent routinely to enforce a consistant coding style, but indent isn't quite ready for that. For users of emacs cc-mode, use the "bsd" style but with: (setq c-basic-offset 4) Finally, if possible, please don't use tabs in source files, since they can expand differently in different environments. In particular, please try not to use the mix of tabs and spaces that is the default in emacs. If you use emacs to edit INN code, you may want to put: ; Use only spaces when indenting or centering, no tabs. (setq-default indent-tabs-mode nil) in your ~/.emacs file. Note that this is only a rough guideline and the maintainers aren't style nazis; we're more interested in your code contribution than in how you write it. Making a Release This is a checklist that INN maintainers should go through when preparing a new release of INN. 1. Update the files shipped with INN, and that are maintained by external projects. * Make sure that support/config.guess, support/config.sub, support/install-sh, support/ltmain.sh and libtool m4 files (libtool.m4, ltoptions.m4, ltsugar.m4, ltversion.m4 and lt~obsolete.m4) are the latest versions. See the instructions in "Configuring and Portability" for details on how to update these files. * Make sure that the latest upstream version of the C TAP Harness package is used for the test suite driver. It is available at , and can be easily synchronized with the script support/getc-tap-harness; just run it, have a look at the resulting changes in INN source code and, if everything seems all right, commit these changes. Parts specific to INN should be kept during an update (especially sections relative to LIBTEST_NEW_FORMAT because the test suite has not yet been fully updated to use the new format of C TAP Harness). * Make sure that the latest upstream version of the files maintained in the rra-c-util package that INN uses are the ones shipped with the release. These files are available at and can be easily synchronized with the script support/getrra-c-util; just run it, have a look at the resulting changes in INN source code and, if everything seems all right, commit these changes. * Make sure that samples/control.ctl and samples/nocem.ctl are in sync with the master version at and . 2. Update copyright years in LICENSE. 3. Update doc/pod/news.pod and regenerate NEWS. Be more detailed for a minor release than for a major release. For a major release, also add information on how to upgrade from the last major release, including anything special to be aware of. (Minor releases shouldn't require any special care when upgrading.) 4. Bump the revision number in doc/FAQ (subject 1.2) so that it could be included in a final release. It should not be changed for a beta or a release candidate. If making a major release, the revision numbers of the STABLE series should also be bumped at the end of subject 1.2. 5. Double-check that the version information in lib/Makefile, storage/Makefile, and history/Makefile has been updated if there has been any change in the library sources since the previous release. 6. If making a major release, branch the source tree by creating a new directory under branches in Subversion named after the major release. This branch will be used for minor releases based on that major release and can be done a little while before the .0 release of that major release. svn copy -r ZZZZ -m "Branch Y.Y.0 release." \ file:///srv/svn/inn/trunk file:///srv/svn/inn/branches/Y.Y Then, in that newly created branch, remove the first paragraph in doc/pod/readme.pod which deals with development versions. 7. Check out a copy of the release branch. It's currently necessary to run "autogen" and "configure" to generate Makefile.global. Then, run "make warnings" to generate all necessary files. Afterwards, run "make check-manifest". There shouldn't be any differences; otherwise, fix the MANIFEST file. 8. Run "make release" for a final release, "support/mksnapshot BETA b1" for the first beta version of a new release, or "support/mksnapshot RC rc1" for the first release candidate version of a new release. Note that you need to have a copy of svn2cl from to do this; at least version 0.7 is required. Start the ChangeLog at the time of the previous release. (Eventually, the script will be smart enough to do this for you.) Check that the ChangeLog file is correct; otherwise, regenerate it or manually edit it. Then run again "make release" or any other command you used. 9. Generate an MD5 checksum of the release tarball. md5sum inn-Y.Y.Y.tar.gz > inn-Y.Y.Y.tar.gz.md5 10. Generate a diff between this release and the previous release if feasible (always for minor releases, possibly not a good idea due to the length of the diff for major releases). You will need the tar file of the previous release for the comparison. diff -Nurp inn-X.X.X inn-Y.Y.Y > inn-X.X.X-Y.Y.Y.diff gzip inn-X.X.X-Y.Y.Y.diff 11. Make the resulting tar file, along with its MD5 checksum and the possible diff from the previous release, available for testing in the testing directory on ftp.isc.org and announce its availability on inn-workers. Install it on at least one system and make sure that system runs fine for at least a few days. This is also a good time to send out a draft of the release announcement to inn-workers for proof-reading. 12. Move the release into the public area of the ftp site and update the inn.tar.gz link. Also put the diff and the MD5 checksum on the ftp site and update the inn.tar.gz.md5 link. Sign the release (ASC, SHA-1, SHA-256 and SHA-512) and update the inn.tar.gz.asc link. Possibly move older releases off into the OLD directory. Contact the ISC folks to push the release to ftp.isc.org and to update the ISC web site (the relevant contact is "web-request" instead of "webmaster"). 13. After the ISC web site has been updated with links towards the new release, send an announce on inn-announce and in news.software.nntp (with a possible crosspost to news.admin.announce). 14. Tag the checked-out tree that was used for generating the release with a release tag by copying it to tags in Subversion. svn copy -r ZZZZ -m "Tag Y.Y.Y release." \ file:///srv/svn/inn/branches/Y.Y file:///srv/svn/inn/tags/Y.Y.Y 15. Bump revision numbers to reflect the one of the following release, especially in doc/pod/install.pod, doc/pod/readme.pod and TODO for major releases, configure.ac and Makefile.global.in for both minor and major releases. The release versions in the Trac wiki should also be updated. 16. For major releases, update the branch used for the generation of STABLE snapshots. References Some additional references that may be hard to find and may be of use to people working on INN: The home page for the IETF NNTP standardization effort, including links to the IETF NNTP working group archives and copies of the latest drafts of the new NNTP standard. The old archived mailing list traffic contains a lot of interesting discussion of why NNTP is the way it is. A collection of documents about the Usenet article format, including most of the relevant RFCs and Internet-Drafts. The archives for the USEFOR IETF working group, the working group for the RFC 1036 replacement (the format of Usenet articles), now published as RFC 5536 and RFC 5537. Forrest Cavalier provides several tools for following INN development at this page and elsewhere in the Usenet RKT. Under here is a web-accessible checked-out copy of the current INN source tree and pointers to how to use CVSup. However, please note that INN development now uses Subversion. A primer on IPv6 with pointers to the appropriate places for more technical details as needed, useful when working on IPv6 support in INN. $Id: hacking.pod 9942 2015-09-08 19:49:55Z iulius $ inn-2.6.0/Makefile.global.in0000644000175200017520000003203512575023702015227 0ustar iuliusiulius## $Id: Makefile.global.in 9913 2015-07-07 16:31:52Z iulius $ ## ## This file is meant to be the central Makefile that configure works with ## and that all other Makefiles include. No Makefile other than this one ## should have to be a configure substitution target. ## ## For installation paths, see the bottom of this file. ## This version information is used to generate include/inn/version.h and is ## used by INN for banner and local version identification. The version ## identification string will be "$VERSION ($VERSION_EXTRA)", with the ## parentheses omitted if $VERSION_EXTRA is empty (as it is for major ## releases). If you make extensive local modifications to INN, you can ## put your own version information in $VERSION_EXTRA. If it's set to ## "prerelease", the build time will be automatically included. ## If you modify these two strings, you must encode them in UTF-8 ## (using only US-ASCII characters is consequently also fine) and ## keep their length reasonable; otherwise, your news server will not ## be complying with the NNTP protocol. VERSION = 2.6.0 VERSION_EXTRA = ## The absolute path to the top of the build directory, used to find the ## libraries built as part of INN. Using relative paths confuses libtool ## when linking the test suite. abs_builddir = @abs_builddir@ ## Basic compiler settings. COPT is the variable to override on the make ## command line to change the optimization or add warning flags (such as ## -Wall). LFS_* is for large file support. All of INN is built with the ## large file support flags if provided. CC = @CC@ COPT = @CFLAGS@ GCFLAGS = $(COPT) -I$(top)/include @CPPFLAGS@ LDFLAGS = @LDFLAGS@ LIBS = @LIBS@ PROF = -pg PROFSUFFIX = _p MAKEPROFILING = $(MAKE) COPT="$(COPT) $(PROF)" \ LDFLAGS="$(LDFLAGS) $(PROF)" \ LIBSUFFIX=$(PROFSUFFIX) ## Used to support non-recursive make. This variable is set to the necessary ## options to the compiler to create an object file in a subdirectory. It ## should be used instead of -c -o $@ $< and may be replaced with code that ## calls mv, if the compiler doesn't support -c with -o. CCOUTPUT = @CCOUTPUT@ ## Warnings to use with gcc. Default to including all of the generally ## useful warnings unless there's something that makes them unsuitable. In ## particular, the following warnings are *not* included: ## ## -ansi Requires messing with feature test macros. ## -pedantic Too much noise from embedded Perl. ## -Wdouble-promotion Too much noise from using printf with floats. ## -Wswitch-default Quite annoying to enforce it. ## -Wstrict-overflow=2 Gives false positives. ## -Wtraditional We assume ANSI C, so these aren't interesting. ## -Wtraditional-conversion ## Warns about problems when upgrading from K&R to ## ANSI C, so these aren't interesting. ## -Wundef We need portability code. ## -Wshadow Names like log or index are too convenient. ## -Wunsafe-loop-optimizations ## Too much noise. ## -Wcast-qual Used for a while, but some casts are unavoidable. ## -Wconversion Too much unsigned to signed noise. ## -Waggregate-return Syntax allowed in C. ## -Wmissing-noreturn Not necessary (unless for optimization). ## -Wpacked Used by INN. ## -Wpadded Used by INN. ## -Wredundant-decls Too much noise from system headers. ## -Wlong-long Too much noise from system headers. ## -Woverlength-strings Useless noise. ## ## Some may be worth looking at again once a released version of gcc doesn't ## warn on system headers. The warnings below are in the same order as ## they're listed in the gcc manual. ## ## Last checked against gcc 4.7.4 (2014-06-12). ## Add -g because when building with warnings, one generally also wants the ## debugging information, and add -O2 because gcc won't find some warnings ## without optimization turned on. Add -DDEBUG=1 so that we'll also ## compile all debugging code and check it as well. ## It would be worthwhile trying to enforce the following checks: ## -D_FORTIFY_SOURCE=2 enables warn_unused_result attribute markings ## on glibc functions on Linux, as well as more object size checking, ## which catches a few more issues. ## -Wswitch-enum, -Wfloat-equal, -Wlogical-op, ## "-Wconversion -Wno-sign-conversion", ## -Wunreachable-code, -Wstack-protector, -Wdeclaration-after-statement WARNINGS = -g -O2 -DDEBUG=1 -Werror -Wall -Wextra -Wformat=2 \ -Winit-self -Wmissing-include-dirs \ -Wsync-nand -Wendif-labels -Wtrampolines -Wpointer-arith \ -Wbad-function-cast -Wcast-align -Wwrite-strings \ -Wjump-misses-init -Wstrict-prototypes \ -Wold-style-definition \ -Wmissing-prototypes -Wmissing-declarations \ -Wmissing-format-attribute \ -Wnormalized=nfc -Wnested-externs -Winline \ -Winvalid-pch -Wvla ## Some warnings have to be suppressed for Perl, since there's no way to work ## around the Perl header problems in INN code. PERL_WARNINGS = @PERL_WARNINGS@ ## libtool support. Note that INN does not use Automake (and that ## retrofitting Automake is likely more work than it's worth), so ## libtool-aware rules have to be written by hand. LIBTOOL = @LIBTOOL@ LIBTOOLCC = @LIBTOOLCC@ LIBTOOLINST = @LIBTOOLINST@ LIBTOOLLD = @LIBTOOLLD@ EXTOBJ = @EXTOBJ@ EXTLIB = @EXTLIB@ LIBCC = $(LIBTOOLCC) $(CC) LIBLD = $(LIBTOOLLD) $(CC) ## Berkeley DB support. If this support is configured, anything linking ## against libstorage also needs to link against BDB_LDFLAGS and BDB_LIBS. BDB_CPPFLAGS = @BDB_CPPFLAGS@ @ZLIB_CPPFLAGS@ BDB_LDFLAGS = @BDB_LDFLAGS@ @ZLIB_LDFLAGS@ BDB_LIBS = @BDB_LIBS@ @ZLIB_LIBS@ ## INN libraries. Nearly all INN programs are linked with libinn, and any ## INN program that reads from or writes to article storage or overview is ## linked against libstorage. STORAGE_LIBS is for external libraries ## needed by libstorage. LIBINN = $(abs_builddir)/lib/libinn$(LIBSUFFIX).$(EXTLIB) LIBHIST = $(abs_builddir)/history/libinnhist$(LIBSUFFIX).$(EXTLIB) LIBSTORAGE = $(abs_builddir)/storage/libstorage$(LIBSUFFIX).$(EXTLIB) STORAGE_LIBS = $(BDB_LDFLAGS) $(BDB_LIBS) DBM_CPPFLAGS = @DBM_CPPFLAGS@ DBM_LIBS = @DBM_LIBS@ CRYPT_LIBS = @CRYPT_LIBS@ PAM_LIBS = @PAM_LIBS@ REGEX_LIBS = @REGEX_LIBS@ SHADOW_LIBS = @SHADOW_LIBS@ ## Embedding support. Additional flags and libraries used when compiling ## or linking portions of INN that support embedded interpreters, set by ## configure based on what interpreter embeddings are selected. PERL_CPPFLAGS = @PERL_CPPFLAGS@ $(PERL_WARNINGS) PERL_LIBS = $(abs_builddir)/lib/perl$(LIBSUFFIX).o @PERL_LIBS@ PYTHON_CPPFLAGS = @PYTHON_CPPFLAGS@ PYTHON_LIBS = @PYTHON_LIBS@ ## OpenSSL support. Additional flags and libraries used when compiling or ## linking code that contains OpenSSL support, and the path to the OpenSSL ## binaries. SSL_CPPFLAGS = @OPENSSL_CPPFLAGS@ SSL_LDFLAGS = @OPENSSL_LDFLAGS@ SSL_LIBS = @OPENSSL_LIBS@ CRYPTO_LIBS = @CRYPTO_LIBS@ SSLBIN = @SSLBIN@ ## SASL support. Additional flags and libraries used when compiling or ## linking code that contains SASL support. SASL_CPPFLAGS = @SASL_CPPFLAGS@ SASL_LDFLAGS = @SASL_LDFLAGS@ SASL_LIBS = @SASL_LIBS@ ## Kerberos support. Additional flags and libraries used when compiling or ## linking code that contains Kerberos support. If Kerberos libraries were ## compiled, KRB5_AUTH is also set to the name of the Kerberos v5 ## authenticator that should be compiled and installed. KRB5_AUTH = @KRB5_AUTH@ KRB5_CPPFLAGS = @KRB5_CPPFLAGS@ KRB5_LDFLAGS = @KRB5_LDFLAGS@ KRB5_LIBS = @KRB5_LIBS@ ## Missing functions. If non-empty, configure detected that your system ## was missing some standard functions, and INN will be providing its own ## replacements from the lib directory. ALLOCA = @ALLOCA@ LIBOBJS = @LIBOBJS@ ## Paths to various standard programs used during the build process. ## Changes to this file will *not* be reflected in the paths compiled into ## programs; these paths are only used during the build process and for ## some autogenerated scripts. To change the compiled paths, see ## include/inn/paths.h. You may also need to modify scripts/innshellvars*. AWK = @AWK@ BZIP2 = @BZIP2@ COMPRESS = @COMPRESS@ GZIP = @GZIP@ LEX = @LEX@ LN_S = @LN_S@ MKDIR_P = @MKDIR_P@ PERL = @PERL@ RANLIB = @RANLIB@ SED = @SED@ UNCOMPRESS = @UNCOMPRESS@ YACC = @YACC@ FIXCONFIG = $(top)/support/fixconfig FIXSCRIPT = $(top)/support/fixscript PERLWHOAMI = $(PERL) -e 'print scalar getpwuid($$>), "\n"' WHOAMI = (whoami || /usr/ucb/whoami || $(PERLWHOAMI)) 2> /dev/null ## Paths and command lines for programs used only by the maintainers to ## regenerate dependencies, documentation, and the like. MAKEDEPEND = $(top)/support/makedepend POD2MAN = pod2man -c 'InterNetNews Documentation' -r 'INN $(VERSION)' POD2TEXT = pod2text -s -l ## Installation directories. If any of the below are incorrect, don't just ## edit this file; these directories are substituted in all over the source ## tree by configure. Instead, re-run configure with the correct ## command-line flags to set the directories. Run configure --help for a ## list of supported flags. prefix = @prefix@ exec_prefix = @exec_prefix@ datarootdir = @datarootdir@ bindir = @bindir@ docdir = @docdir@ includedir = @includedir@ libdir = @libdir@ mandir = @mandir@ sysconfdir = @sysconfdir@ tmpdir = @tmpdir@ PATHNEWS = $(prefix) PATHBIN = $(bindir) PATHDATASHARE = $(datarootdir) PATHDOC = $(docdir) PATHETC = $(sysconfdir) PATHMAN = $(mandir) PATHINCLUDE = $(includedir) PATHLIB = $(libdir) PATHLIBPERL = @LIBPERLDIR@ PATHCONTROL = @CONTROLDIR@ PATHFILTER = @FILTERDIR@ PATHRUN = @RUNDIR@ PATHHTTP = @HTTPDIR@ PATHLOG = @LOGDIR@ PATHLOGOLD = $(PATHLOG)/OLD PATHDB = @DBDIR@ PATHSPOOL = @SPOOLDIR@ PATHTMP = $(tmpdir) PATHAUTH = $(PATHBIN)/auth PATHAUTHRESOLV = $(PATHAUTH)/resolv PATHAUTHPASSWD = $(PATHAUTH)/passwd PATHRNEWS = $(PATHBIN)/rnews.libexec PATHARCHIVE = $(PATHSPOOL)/archive PATHARTICLES = $(PATHSPOOL)/articles PATHINCOMING = $(PATHSPOOL)/incoming PATHTAPE = $(PATHSPOOL)/innfeed PATHINBAD = $(PATHINCOMING)/bad PATHOVERVIEW = $(PATHSPOOL)/overview PATHOUTGOING = $(PATHSPOOL)/outgoing MAN1 = $(mandir)/man1 MAN3 = $(mandir)/man3 MAN3PM = $(mandir)/man3 MAN5 = $(mandir)/man5 MAN8 = $(mandir)/man8 ## Extension used for Perl modules man pages (for instance "INN::Config.3pm" ## if MAN3PM_EXT is set to "3pm"). MAN3PM_EXT = 3pm ## Installation settings. The file installation modes are determined by ## configure; inews and rnews are special and have configure flags to ## control how they're installed. See INSTALL for more information. RUNASUSER = @RUNASUSER@ RUNASGROUP = @RUNASGROUP@ RNEWSGROUP = @RNEWSGRP@ INEWSMODE = @INEWSMODE@ RNEWSMODE = @RNEWSMODE@ FILEMODE = @FILEMODE@ OWNER = -o $(RUNASUSER) -g $(RUNASGROUP) ROWNER = -o $(RUNASUSER) -g $(RNEWSGROUP) INSTALL = $(top)/support/install-sh -c ## Installation commands. These commands are used by the installation ## rules of each separate subdirectory. The naming scheme is as follows: ## the first two characters are CP (indicating a plain copy) or LI ## (indicating an installation that goes through libtool). After an ## underscore is a one-character indicator of the file type (R for a ## regular file, X for an executable, S for a setuid root executable, L for ## a shared library) and then PUB for a world-readable/world-executable ## file or PRI for a group-readable/ group-executable file (only the news ## group). ## ## Man pages should not be backed up with an .OLD extension since it causes ## man to complain about bogus filenames in MANPATH. Shared libraries ## should not be backed up with an .OLD extension since ldconfig will ## select the .OLD version in preference to the regular version. We use ## shared library versioning to allow recovery from make update for shared ## libraries. ## ## inews and rnews have their own special installation rules, as do database ## files like active and newsgroups that should have the same permissions as ## article files. BACKUP_OPTION = -B .OLD LI_SPRI = $(LIBTOOLINST) $(INSTALL) -o root -g $(RUNASGROUP) -m 4550 $(BACKUP_OPTION) LI_XPRI = $(LIBTOOLINST) $(INSTALL) $(OWNER) -m 0550 $(BACKUP_OPTION) LI_XPUB = $(LIBTOOLINST) $(INSTALL) $(OWNER) -m 0555 $(BACKUP_OPTION) LI_LPUB = $(LIBTOOLINST) $(INSTALL) $(OWNER) -m 0555 LI_INEWS = $(LIBTOOLINST) $(INSTALL) $(OWNER) -m $(INEWSMODE) $(BACKUP_OPTION) LI_RNEWS = $(LIBTOOLINST) $(INSTALL) $(ROWNER) -m $(RNEWSMODE) $(BACKUP_OPTION) CP_RPRI = $(INSTALL) $(OWNER) -m 0640 $(BACKUP_OPTION) CP_RPUB = $(INSTALL) $(OWNER) -m 0644 $(BACKUP_OPTION) CP_XPRI = $(INSTALL) $(OWNER) -m 0550 $(BACKUP_OPTION) CP_XPUB = $(INSTALL) $(OWNER) -m 0555 $(BACKUP_OPTION) CP_DATA = $(INSTALL) $(OWNER) -m $(FILEMODE) $(BACKUP_OPTION) CP_MAN = $(INSTALL) $(OWNER) -m 0444 ## Some additional definitions needed by some versions of make, to ensure a ## consistant set of variables are available. SHELL = @SHELL@ @SET_MAKE@ ## Local variables: ## mode: makefile ## End: inn-2.6.0/frontends/0000755000175200017520000000000012575023700013720 5ustar iuliusiuliusinn-2.6.0/frontends/rnews.c0000644000175200017520000005747112575023702015242 0ustar iuliusiulius/* $Id: rnews.c 9911 2015-07-04 21:32:56Z iulius $ ** ** A front-end for InterNetNews. ** ** Read UUCP batches and offer them up NNTP-style. Because we may end ** up sending our input down a pipe to uncompress, we have to be careful ** to do unbuffered reads. */ #include "config.h" #include "clibrary.h" #include #include #include #include #include #include #include #include "inn/fdflag.h" #include "inn/innconf.h" #include "inn/libinn.h" #include "inn/messages.h" #include "inn/newsuser.h" #include "inn/nntp.h" #include "inn/paths.h" #include "inn/storage.h" #include "inn/wire.h" typedef struct _HEADER { const char *Name; int size; } HEADER; static bool Verbose; static const char *InputFile = "stdin"; static char *UUCPHost; static char *PathBadNews = NULL; static char *remoteServer; static FILE *FromServer; static FILE *ToServer; static char UNPACK[] = "gzip"; static HEADER RequiredHeaders[] = { { "Message-ID", 10 }, #define _messageid 0 { "Newsgroups", 10 }, #define _newsgroups 1 { "From", 4 }, #define _from 2 { "Date", 4 }, #define _date 3 { "Subject", 7 }, #define _subject 4 { "Path", 4 }, #define _path 5 }; #define IS_MESGID(hp) ((hp) == &RequiredHeaders[_messageid]) #define IS_PATH(hp) ((hp) == &RequiredHeaders[_path]) /* ** Open up a pipe to a process with fd tied to its stdin. Return a ** descriptor tied to its stdout or -1 on error. */ static int StartChild(int fd, const char *path, const char *argv[]) { int pan[2]; int i; pid_t pid; /* Create a pipe. */ if (pipe(pan) < 0) sysdie("cannot pipe for %s", path); /* Get a child. */ for (i = 0; (pid = fork()) < 0; i++) { if (i == (long) innconf->maxforks) { syswarn("cannot fork %s, spooling", path); return -1; } notice("cannot fork %s, waiting", path); sleep(60); } /* Run the child, with redirection. */ if (pid == 0) { close(pan[PIPE_READ]); /* Stdin comes from our old input. */ if (fd != STDIN_FILENO) { if ((i = dup2(fd, STDIN_FILENO)) != STDIN_FILENO) { syswarn("cannot dup2 %d to 0, got %d", fd, i); _exit(1); } close(fd); } /* Stdout goes down the pipe. */ if (pan[PIPE_WRITE] != STDOUT_FILENO) { if ((i = dup2(pan[PIPE_WRITE], STDOUT_FILENO)) != STDOUT_FILENO) { syswarn("cannot dup2 %d to 1, got %d", pan[PIPE_WRITE], i); _exit(1); } close(pan[PIPE_WRITE]); } execv(path, (char * const *)argv); syswarn("cannot execv %s", path); _exit(1); } close(pan[PIPE_WRITE]); close(fd); return pan[PIPE_READ]; } /* ** Wait for the specified number of children. */ static void WaitForChildren(int n) { pid_t pid; while (--n >= 0) { pid = waitpid(-1, NULL, WNOHANG); if (pid == (pid_t) -1 && errno != EINTR) { if (errno != ECHILD) syswarn("cannot wait"); break; } } } /* ** Clean up the NNTP escapes from a line. */ static char *REMclean(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; } /* ** Write an article to the rejected directory. */ static void Reject(const char *article, size_t length UNUSED, const char *reason, const char *arg) { #if defined(DO_RNEWS_SAVE_BAD) char *filename; FILE *F; int fd; #endif /* defined(DO_RNEWS_SAVE_BAD) */ #pragma GCC diagnostic ignored "-Wformat-nonliteral" notice(reason, arg); #pragma GCC diagnostic warning "-Wformat-nonliteral" if (Verbose) { fprintf(stderr, "%s: ", InputFile); #pragma GCC diagnostic ignored "-Wformat-nonliteral" fprintf(stderr, reason, arg); #pragma GCC diagnostic warning "-Wformat-nonliteral" fprintf(stderr, " [%.40s...]\n", article); } #if defined(DO_RNEWS_SAVE_BAD) filename = concat(PathBadNews, "/XXXXXX", (char *) 0); fd = mkstemp(filename); if (fd < 0) { warn("cannot create temporary file"); return; } F = fdopen(fd, "w"); if (F == NULL) { warn("cannot fdopen %s", filename); return; } if (fwrite(article, 1, length, F) != length) warn("cannot fwrite to %s", filename); if (fclose(F) == EOF) warn("cannot close %s", filename); free(filename); #endif /* defined(DO_RNEWS_SAVE_BAD) */ } /* ** Process one article. Return true if the article was okay; false if the ** whole batch needs to be saved (such as when the server goes down or if ** the file is corrupted). */ static bool Process(char *article, size_t artlen) { HEADER *hp; const char *p; size_t length; char *wirefmt, *q; const char *id = NULL; char *msgid; char buff[SMBUF]; #if defined(FILE_RNEWS_LOG_DUPS) FILE *F; #endif /* defined(FILE_RNEWS_LOG_DUPS) */ #if !defined(DONT_RNEWS_LOG_DUPS) char path[40]; #endif /* !defined(DONT_RNEWS_LOG_DUPS) */ /* Empty article? */ if (*article == '\0') return true; /* Convert the article to wire format. */ wirefmt = wire_from_native(article, artlen, &length); /* Make sure that all the headers are there, note the ID. */ for (hp = RequiredHeaders; hp < ARRAY_END(RequiredHeaders); hp++) { p = wire_findheader(wirefmt, length, hp->Name, true); if (p == NULL) { free(wirefmt); Reject(article, artlen, "bad_article missing %s", hp->Name); return true; } if (IS_MESGID(hp)) { id = p; continue; } #if !defined(DONT_RNEWS_LOG_DUPS) if (IS_PATH(hp)) { strlcpy(path, p, sizeof(path)); if ((q = strchr(path, '\r')) != NULL) *q = '\0'; } #endif /* !defined(DONT_RNEWS_LOG_DUPS) */ } /* Send the NNTP "ihave" message. */ if ((p = strchr(id, '\r')) == NULL) { free(wirefmt); Reject(article, artlen, "bad_article unterminated %s header", "Message-ID"); return true; } msgid = xstrndup(id, p - id); fprintf(ToServer, "ihave %s\r\n", msgid); fflush(ToServer); if (UUCPHost) notice("offered %s %s", msgid, UUCPHost); free(msgid); /* Get a reply, see if they want the article. */ if (fgets(buff, sizeof buff, FromServer) == NULL) { free(wirefmt); if (ferror(FromServer)) syswarn("cannot fgets after ihave"); else warn("unexpected EOF from server after ihave"); return false; } REMclean(buff); if (!isdigit((unsigned char) buff[0])) { free(wirefmt); notice("bad_reply after ihave %s", buff); return false; } switch (atoi(buff)) { default: free(wirefmt); notice("unknown_reply after ihave %s", buff); return false; case NNTP_FAIL_IHAVE_DEFER: free(wirefmt); return false; case NNTP_CONT_IHAVE: break; case NNTP_FAIL_IHAVE_REFUSE: #if defined(SYSLOG_RNEWS_LOG_DUPS) *p = '\0'; notice("duplicate %s %s", id, path); #endif /* defined(SYSLOG_RNEWS_LOG_DUPS) */ #if defined(FILE_RNEWS_LOG_DUPS) if ((F = fopen(INN_PATH_RNEWS_DUP_LOG, "a")) != NULL) { *p = '\0'; fprintf(F, "duplicate %s %s\n", id, path); fclose(F); } #endif /* defined(FILE_RNEWS_LOG_DUPS) */ free(wirefmt); return true; } /* Send the article to the server. */ if (fwrite(wirefmt, length, 1, ToServer) != 1) { free(wirefmt); sysnotice("cant sendarticle"); return false; } free(wirefmt); /* Flush the server buffer. */ if (fflush(ToServer) == EOF) { syswarn("cant fflush after article"); return false; } /* Process server reply code. */ if (fgets(buff, sizeof buff, FromServer) == NULL) { if (ferror(FromServer)) syswarn("cannot fgets after article"); else warn("unexpected EOF from server after article"); return false; } REMclean(buff); if (!isdigit((unsigned char) buff[0])) { notice("bad_reply after article %s", buff); return false; } switch (atoi(buff)) { default: notice("unknown_reply after article %s", buff); /* FALLTHROUGH */ case NNTP_FAIL_IHAVE_DEFER: return false; case NNTP_OK_IHAVE: break; case NNTP_FAIL_IHAVE_REJECT: Reject(article, artlen, "rejected %s", buff); break; } return true; } /* ** Read the rest of the input as an article. */ static bool ReadRemainder(int fd, char first, char second) { char *article; char *p; char buf[BUFSIZ]; int size; int used; int left; int skipnl; int i, n; bool ok; /* Get an initial allocation, leaving space for the \0. */ size = BUFSIZ + 1; article = xmalloc(size + 2); article[0] = first; article[1] = second; used = second ? 2 : 1; left = size - used; skipnl = 0; /* Read the input, coverting line ends as we go if necessary. */ while ((n = read(fd, buf, sizeof(buf))) > 0) { p = article + used; for (i = 0; i < n; i++) { if (skipnl) { skipnl = 0; if (buf[i] == '\n') continue; } if (buf[i] == '\r') { buf[i] = '\n'; skipnl = 1; } *p++ = buf[i]; used++; left--; if (left < SMBUF) { size += BUFSIZ; left += BUFSIZ; article = xrealloc(article, size); p = article + used; } } } if (n < 0) sysdie("cannot read after %d bytes", used); if (article[used - 1] != '\n') article[used++] = '\n'; article[used] = '\0'; ok = Process(article, used); free(article); return ok; } /* ** Read an article from the input stream that is artsize bytes long. */ static bool ReadBytecount(int fd, int artsize) { static char *article; static int oldsize; char *p; int left; int i; /* If we haven't gotten any memory before, or we didn't get enough, * then get some. */ if (article == NULL) { oldsize = artsize; article = xmalloc(oldsize + 1 + 1); } else if (artsize > oldsize) { oldsize = artsize; article = xrealloc(article, oldsize + 1 + 1); } /* Read in the article. If we don't get as many bytes as we should, return true without doing anything, throwing away the article. This seems like the best of a set of bad options; Reject would save the article into bad and then someone might reprocess it, leaving us with accepting the truncated version. */ for (p = article, left = artsize; left; p += i, left -= i) { i = read(fd, p, left); if (i <= 0) { warn("cannot read, wanted %d got %d", artsize, artsize - left); return true; } } if (p[-1] != '\n') { *p++ = '\n'; artsize++; } *p = '\0'; return Process(article, artsize); } /* ** Read a single text line; not unlike fgets(). Just more inefficient. */ static bool ReadLine(char *p, int size, int fd) { char *save; /* Fill the buffer, a byte at a time. */ for (save = p; size > 0; p++, size--) { if (read(fd, p, 1) != 1) { *p = '\0'; sysdie("cannot read first line, got %s", save); } if (*p == '\n') { *p = '\0'; return true; } } *p = '\0'; warn("bad_line too long %s", save); return false; } /* ** Unpack a single batch. */ static bool UnpackOne(int *fdp, size_t *countp) { #if defined(DO_RNEWSPROGS) char path[(SMBUF * 2) + 1]; char *p; #endif /* defined(DO_RNEWSPROGS) */ char buff[SMBUF]; const char *cargv[4]; int artsize; int i; int gzip = 0; bool HadCount; bool SawCunbatch; int len; *countp = 0; for (SawCunbatch = false, HadCount = false; ; ) { /* Get the first character. */ if ((i = read(*fdp, &buff[0], 1)) < 0) { syswarn("cannot read first character"); return false; } if (i == 0) break; if (buff[0] == 0x1f) gzip = 1; else if (buff[0] != '#') /* Not a batch file. If we already got one count, the batch * is corrupted, else read rest of input as an article. */ return HadCount ? false : ReadRemainder(*fdp, buff[0], '\0'); /* Get the second character. */ if ((i = read(*fdp, &buff[1], 1)) < 0) { syswarn("cannot read second character"); return false; } if (i == 0) /* A one-byte batch? */ return false; /* Check second magic character. */ /* gzipped ($1f$8b) or compressed ($1f$9d) */ if (gzip && ((buff[1] == (char)0x8b) || (buff[1] == (char)0x9d))) { cargv[0] = "gzip"; cargv[1] = "-d"; cargv[2] = NULL; lseek(*fdp, 0, 0); /* Back to the beginning */ *fdp = StartChild(*fdp, INN_PATH_GZIP, cargv); if (*fdp < 0) return false; (*countp)++; SawCunbatch = true; continue; } if (buff[1] != '!') return HadCount ? false : ReadRemainder(*fdp, buff[0], buff[1]); /* Some kind of batch -- get the command. */ if (!ReadLine(&buff[2], (int)(sizeof buff - 3), *fdp)) return false; if (strncmp(buff, "#! rnews ", 9) == 0) { artsize = atoi(&buff[9]); if (artsize <= 0) { syswarn("bad_line bad count %s", buff); return false; } HadCount = true; if (ReadBytecount(*fdp, artsize)) continue; return false; } if (HadCount) /* Already saw a bytecount -- probably corrupted. */ return false; if (strcmp(buff, "#! cunbatch") == 0) { if (SawCunbatch) { syswarn("nested_cunbatch"); return false; } cargv[0] = UNPACK; cargv[1] = "-d"; cargv[2] = NULL; *fdp = StartChild(*fdp, INN_PATH_GZIP, cargv); if (*fdp < 0) return false; (*countp)++; SawCunbatch = true; continue; } #if defined(DO_RNEWSPROGS) cargv[0] = UNPACK; cargv[1] = NULL; /* Ignore any possible leading pathnames, to avoid trouble. */ if ((p = strrchr(&buff[3], '/')) != NULL) p++; else p = &buff[3]; if (strchr(INN_PATH_RNEWSPROGS, '/') == NULL) { snprintf(path, sizeof(path), "%s/%s/%s", innconf->pathbin, INN_PATH_RNEWSPROGS, p); len = strlen(innconf->pathbin) + 1 + sizeof INN_PATH_RNEWSPROGS; } else { snprintf(path, sizeof(path), "%s/%s", INN_PATH_RNEWSPROGS, p); len = sizeof INN_PATH_RNEWSPROGS; } for (p = &path[len]; *p; p++) if (ISWHITE(*p)) { *p = '\0'; break; } *fdp = StartChild(*fdp, path, cargv); if (*fdp < 0) return false; (*countp)++; continue; #else warn("bad_format unknown command %s", buff); return false; #endif /* defined(DO_RNEWSPROGS) */ } return true; } /* ** Read all articles in the spool directory and unpack them. Print all ** errors with xperror as well as syslog, since we're probably being run ** interactively. */ static void Unspool(void) { DIR *dp; struct dirent *ep; bool ok; struct stat Sb; char hostname[SMBUF]; int fd; size_t i; char *uuhost; message_handlers_die(2, message_log_stderr, message_log_syslog_err); message_handlers_warn(2, message_log_stderr, message_log_syslog_err); /* Go to the spool directory, get ready to scan it. */ if (chdir(innconf->pathincoming) < 0) sysdie("cannot chdir to %s", innconf->pathincoming); if ((dp = opendir(".")) == NULL) sysdie("cannot open spool directory"); /* Loop over all files, and parse them. */ while ((ep = readdir(dp)) != NULL) { InputFile = ep->d_name; if (InputFile[0] == '.') continue; if (stat(InputFile, &Sb) < 0 && errno != ENOENT) { syswarn("cannot stat %s", InputFile); continue; } if (!S_ISREG(Sb.st_mode)) continue; if ((fd = open(InputFile, O_RDWR)) < 0) { if (errno != ENOENT) syswarn("cannot open %s", InputFile); continue; } /* Make sure multiple Unspools don't stomp on eachother. */ if (!inn_lock_file(fd, INN_LOCK_WRITE, 0)) { close(fd); continue; } /* Get UUCP host from spool file, deleting the mktemp XXXXXX suffix. */ uuhost = UUCPHost; hostname[0] = 0; if ((i = strlen(InputFile)) > 6) { i -= 6; if (i > sizeof hostname - 1) /* Just in case someone wrote their own spooled file. */ i = sizeof hostname - 1; strlcpy(hostname, InputFile, i + 1); UUCPHost = hostname; } ok = UnpackOne(&fd, &i); WaitForChildren(i); UUCPHost = uuhost; /* If UnpackOne returned true, the article has been dealt with one way or the other, so remove it. Otherwise, leave it in place; either we got an unknown error from the server or we got a deferral, and for both we want to try later. */ if (ok) { if (unlink(InputFile) < 0) syswarn("cannot remove %s", InputFile); } close(fd); } closedir(dp); message_handlers_die(1, message_log_syslog_err); message_handlers_warn(1, message_log_syslog_err); } /* ** Can't connect to the server, so spool our input. There isn't much ** we can do if this routine fails, unfortunately. Perhaps try to use ** an alternate filesystem? */ static void Spool(int fd, int mode) { int spfd; int i; int j; char *tmpspool, *spoolfile, *p; char buff[BUFSIZ]; int count; int status; if (mode == 'N') exit(9); tmpspool = concat(innconf->pathincoming, "/.", UUCPHost ? UUCPHost : "", "XXXXXX", (char *)0); spfd = mkstemp(tmpspool); if (spfd < 0) sysdie("cannot create temporary batch file %s", tmpspool); if (fchmod(spfd, BATCHFILE_MODE) < 0) sysdie("cannot chmod temporary batch file %s", tmpspool); /* Read until we there is nothing left. */ for (status = 0, count = 0; (i = read(fd, buff, sizeof buff)) != 0; ) { /* Break out on error. */ if (i < 0) { syswarn("cannot read after %d", count); status++; break; } /* Write out what we read. */ for (count += i, p = buff; i; p += j, i -= j) if ((j = write(spfd, p, i)) <= 0) { syswarn("cannot write around %d", count); status++; break; } } /* Close the file. */ if (close(spfd) < 0) { syswarn("cannot close spooled article %s", tmpspool); status++; } /* Move temp file into the spool area, and exit appropriately. */ spoolfile = concat(innconf->pathincoming, "/", UUCPHost ? UUCPHost : "", "XXXXXX", (char *)0); spfd = mkstemp(spoolfile); if (spfd < 0) { syswarn("cannot create spool file %s", spoolfile); status++; } else { close(spfd); if (rename(tmpspool, spoolfile) < 0) { syswarn("cannot rename %s to %s", tmpspool, spoolfile); status++; } } free(tmpspool); free(spoolfile); exit(status); /* NOTREACHED */ } /* ** Try to read the password file and open a connection to a remote ** NNTP server. */ static bool OpenRemote(char *server, int port, char *buff, size_t len) { int i; /* Open the remote connection. */ if (server) i = NNTPconnect(server, port, &FromServer, &ToServer, buff, len); else i = NNTPremoteopen(port, &FromServer, &ToServer, buff, len); if (i < 0) return false; *buff = '\0'; if (NNTPsendpassword(server, FromServer, ToServer) < 0) { int oerrno = errno; fclose(FromServer); fclose(ToServer); errno = oerrno; return false; } return true; } /* ** Can't connect to server; print message and spool if necessary. */ static void CantConnect(char *buff, int mode, int fd) { if (buff[0]) notice("rejected connection %s", REMclean(buff)); else syswarn("cant open_remote"); if (mode != 'U') Spool(fd, mode); exit(1); } int main(int ac, char *av[]) { int fd; int i; size_t count; int mode; char buff[SMBUF]; int port = NNTP_PORT; /* First thing, set up logging and our identity. */ openlog("rnews", L_OPENLOG_FLAGS, LOG_INN_PROG); message_program_name = "rnews"; message_handlers_notice(1, message_log_syslog_notice); message_handlers_warn(1, message_log_syslog_err); message_handlers_die(1, message_log_syslog_err); /* The reason for the following is somewhat obscure and is done only because rnews is sometimes installed setuid. The stderr stream used by message_log_syslog_err is associated with file descriptor 2, generally even if that file descriptor is closed. Someone running rnews may close all of the standard file descriptors before running it, in which case, later in its operations, one of the article files or network connections it has open could be file descriptor 2. If an error occurs at that point, the error message may be written to that file or network connection instead of to stderr, with unpredictable results. We avoid this by burning three file descriptors if the real and effective user IDs don't match, or if we're running as root. (If they do match, there is no escalation of privileges and at worst the user is just managing to produce a strange bug.) */ if (getuid() != geteuid() || geteuid() == 0) { if (open("/dev/null", O_RDONLY) < 0) sysdie("cannot open /dev/null"); if (open("/dev/null", O_RDONLY) < 0) sysdie("cannot open /dev/null"); if (open("/dev/null", O_RDONLY) < 0) sysdie("cannot open /dev/null"); } /* Make sure that we switch to the news user if we're running as root, since we may spool files and don't want those files owned by root. Don't require that we be running as the news user, though; there are other setups where rnews might be setuid news or be run by other processes in the news group. */ if (getuid() == 0 || geteuid() == 0) { ensure_news_user(true); } if (!innconf_read(NULL)) exit(1); UUCPHost = getenv(INN_ENV_UUCPHOST); PathBadNews = concatpath(innconf->pathincoming, INN_PATH_BADNEWS); port = innconf->nnrpdpostport; umask(NEWSUMASK); /* Parse JCL. */ fd = STDIN_FILENO; mode = '\0'; while ((i = getopt(ac, av, "h:P:NUvr:S:")) != EOF) switch (i) { default: die("usage error"); /* NOTRTEACHED */ case 'h': UUCPHost = *optarg ? optarg : NULL; break; case 'N': case 'U': mode = i; break; case 'P': port = atoi(optarg); break; case 'v': Verbose = true; break; case 'r': case 'S': remoteServer = optarg; break; } ac -= optind; av += optind; /* Parse arguments. At most one, the input file. */ switch (ac) { default: die("usage error"); /* NOTREACHED */ case 0: break; case 1: if (mode == 'U') die("usage error"); if (freopen(av[0], "r", stdin) == NULL) sysdie("cannot freopen %s", av[0]); fd = fileno(stdin); InputFile = av[0]; break; } /* Open the link to the server. */ if (remoteServer != NULL) { if (!OpenRemote(remoteServer, port, buff, sizeof(buff))) CantConnect(buff,mode,fd); } else if (innconf->nnrpdposthost != NULL) { if (!OpenRemote(innconf->nnrpdposthost, (port != NNTP_PORT) ? (unsigned) port : innconf->nnrpdpostport, buff, sizeof(buff))) CantConnect(buff, mode, fd); } else { if (NNTPlocalopen(&FromServer, &ToServer, buff, sizeof(buff)) < 0) { /* If server rejected us, no point in continuing. */ if (buff[0]) CantConnect(buff, mode, fd); if (!OpenRemote(NULL, (port != NNTP_PORT) ? (unsigned) port : innconf->port, buff, sizeof(buff))) CantConnect(buff, mode, fd); } } fdflag_close_exec(fileno(FromServer), true); fdflag_close_exec(fileno(ToServer), true); /* Execute the command. */ if (mode == 'U') Unspool(); else { if (!UnpackOne(&fd, &count)) { lseek(fd, 0, 0); Spool(fd, mode); } close(fd); WaitForChildren(count); } /* Tell the server we're quitting, get his okay message. */ fprintf(ToServer, "quit\r\n"); fflush(ToServer); fgets(buff, sizeof buff, FromServer); /* Return the appropriate status. */ exit(0); /* NOTREACHED */ } inn-2.6.0/frontends/innconfval.c0000644000175200017520000000466712575023702016240 0ustar iuliusiulius/* $Id: innconfval.c 7585 2006-11-21 09:37:51Z eagle $ ** ** Get a config value from INN. */ #include "config.h" #include "clibrary.h" #include "inn/innconf.h" #include "inn/messages.h" #include "inn/version.h" #include "inn/libinn.h" /* ** Print the INN version string with appropriate quoting. */ static void print_version(FILE *file, enum innconf_quoting quoting) { switch (quoting) { case INNCONF_QUOTE_NONE: fprintf(file, "%s\n", INN_VERSION_STRING); break; case INNCONF_QUOTE_SHELL: fprintf(file, "VERSION='%s'; export VERSION\n", INN_VERSION_STRING); break; case INNCONF_QUOTE_PERL: fprintf(file, "$version = '%s';\n", INN_VERSION_STRING); break; case INNCONF_QUOTE_TCL: fprintf(file, "set inn_version \"%s\"\n", INN_VERSION_STRING); break; } } /* ** Main routine. Most of the real work is done by the innconf library ** routines. */ int main(int argc, char *argv[]) { int option, i; char *file = NULL; enum innconf_quoting quoting = INNCONF_QUOTE_NONE; bool okay = true; bool version = false; bool checking = false; message_program_name = "innconfval"; while ((option = getopt(argc, argv, "Ci:pstv")) != EOF) switch (option) { default: die("usage error"); break; case 'C': checking = true; break; case 'i': file = optarg; break; case 'p': quoting = INNCONF_QUOTE_PERL; break; case 's': quoting = INNCONF_QUOTE_SHELL; break; case 't': quoting = INNCONF_QUOTE_TCL; break; case 'v': version = true; break; } argc -= optind; argv += optind; if (version) { print_version(stdout, quoting); exit(0); } if (checking) exit(innconf_check(file) ? 0 : 1); /* Read in the inn.conf file specified. */ if (!innconf_read(file)) exit(1); /* Perform the specified action. */ if (argv[0] == NULL) { innconf_dump(stdout, quoting); print_version(stdout, quoting); } else { for (i = 0; i < argc; i++) if (strcmp(argv[i], "version") == 0) print_version(stdout, quoting); else if (!innconf_print_value(stdout, argv[i], quoting)) okay = false; } exit(okay ? 0 : 1); } inn-2.6.0/frontends/decode.c0000644000175200017520000000626512575023702015322 0ustar iuliusiulius/* ** Decode seven-bit input into full binary output. ** From @(#)decode.c 1.3 5/15/85, distributed with B2.11 News. ** ** Collect runs of 12 seven-bit characters. Combine them in pairs to ** make six 13-bit characters. Extract the top bit of each pair to make ** a 13th six-bit character, and split the remaining six 12-bit ** characters to form 12 six-bit characters. Collect four six-bit ** characters and convert it to three eight-bit characters. ** ** Got that? All the remaining work in this program is to get the ** ending conditions right. */ #include "config.h" #include "clibrary.h" #include "inn/messages.h" /* ** These characters can't appear in normal output, so we use them to ** mark that the data that follows is the terminator. The character ** immediately following this pair is the length of the terminator (which ** otherwise might be indeterminable) */ #define ENDMARK1 ((90 * 91 + 90) / 91) #define ENDMARK2 ((90 * 91 + 90) % 91) static char Buffer[4]; static int count; static void pack6(int n, int last) { char *q; int i; char b3[3]; i = 3; if (last && (i = Buffer[n - 1]) >= 3) { /* Do the best we can. */ warn("badly-terminated file"); i = 3; } b3[0] = (Buffer[0] << 2) | ((Buffer[1] >> 4) & 0x03); b3[1] = (Buffer[1] << 4) | ((Buffer[2] >> 2) & 0x0F); b3[2] = (Buffer[2] << 6) | ( Buffer[3] & 0x3F); for (q = b3; --i >= 0; ) putchar(*q++); } static void pack12(char *p, int n, int last) { char *q; int c13; int c; int i; char b13[13]; char b3[3]; for (q = b13, c13 = 0, i = 0; i < n; i += 2) { c = *p++ * 91; c += *p++; c13 <<= 1; if (c & (1 << 12)) c13 |= 1; *q++ = (c >> 6) & 0x3F; *q++ = c & 0x3F; } *q++ = (char)c13; if (last) q = &b13[last]; for (p = b13, n = q - p, i = count, q = &Buffer[count]; --n > 0; ) { *q++ = *p++; if (++i == 4) { /* Inline expansion of pack6. */ b3[0] = (Buffer[0] << 2) | ((Buffer[1] >> 4) & 0x03); b3[1] = (Buffer[1] << 4) | ((Buffer[2] >> 2) & 0x0F); b3[2] = (Buffer[2] << 6) | ( Buffer[3] & 0x3F); putchar(b3[0]); putchar(b3[1]); putchar(b3[2]); i = 0; q = Buffer; } } /* The last octet. */ *q++ = *p++; i++; if (last || i == 4) { pack6(i, last); i = 0; } count = i; } int main(void) { int c; char *p; int i; int first; int cnt; char *base; char b12[12]; char c12[12]; message_program_name = "decode"; base = p = b12; for (i = 12, cnt = 0, first = 1; (c = getchar()) != EOF; ) { if (c < ' ' || c >= ' ' + 91) die("bad data"); if (i == 10 && p[-1] == ENDMARK1 && p[-2] == ENDMARK2) { cnt = c - ' '; i = 12; p -= 2; continue; } *p++ = c - ' '; if (--i == 0) { if (p == &b12[12]) { if (!first) pack12(c12, 12, 0); else first = 0; base = p = c12; } else { pack12(b12, 12, 0); base = p = b12; } i = 12; } } if (base == b12) { if (!first) pack12(c12, 12, i == 12 ? cnt : 0); } else pack12(b12, 12, i == 12 ? cnt : 0); if (i != 12) pack12(base, 12 - i, cnt); exit(0); /* NOTREACHED */ } inn-2.6.0/frontends/encode.c0000644000175200017520000000472212575023702015330 0ustar iuliusiulius/* $Id: encode.c 6119 2003-01-13 07:59:39Z rra $ ** ** Produce a seven-bit printable encoding of stdin on stdout. ** From @(#)encode.c 1.3 5/15/85, distributed with B2.11 News. ** ** The encoding uses characters from 0x20 (' ') through 0x7A ('z'). ** (That fits nicely into the UUCP 'f' protocol by Piet Beertema.) First, ** expand three eight-bit charcters into four six-bit ones. Collect ** until we have 13, and spread the last one over the first 12, so that ** we have 12 6.5-bit characters. Since there are very few half-bit ** machines, collect them into pairs, making six 13-bit characters. We ** can do this as A * 91 + B where A and B are less then 91 after we add ** 0x20 to make it printable. ** ** And if you thought that was unclear, then we won't even get into the ** terminating conditions! */ #include "config.h" #include "clibrary.h" /* ** These characters can't appear in normal output, so we use them to ** mark that the data that follows is the terminator. The character ** immediately following this pair is the length of the terminator (which ** otherwise might be indeterminable) */ #define ENDMARK1 ((90 * 91 + 90) / 91 + ' ') #define ENDMARK2 ((90 * 91 + 90) % 91 + ' ') static char Buffer[13]; static int Count; static void dumpcode(char *p, int n) { int last; int c; if (n == 13) { n--; last = p[12]; } else if (n & 1) last = 1 << (6 - 1); else last = 0; for (; n > 0; n -= 2) { c = *p++ << 6; c |= *p++; if (last & (1 << (6 - 1))) c |= (1 << 12); last <<= 1; putchar((c / 91) + ' '); putchar((c % 91) + ' '); } } static void flushout(void) { putchar(ENDMARK1); putchar(ENDMARK2); putchar(Count + ' '); dumpcode(Buffer, Count); } static void encode(char *dest, int n) { char *p; int i; int j; char b4[4]; b4[0] = (dest[0] >> 2) & 0x3F; b4[1] = ((dest[0] & 0x03) << 4) | ((dest[1] >> 4) & 0x0F); b4[2] = ((dest[1] & 0x0F) << 2) | ((dest[2] >> 6) & 0x03); b4[3] = (char)(n == 3 ? dest[2] & 0x3F : n); for (p = b4, i = Count, dest = &Buffer[i], j = 4; --j >= 0; i++) { if (i == 13) { dumpcode(Buffer, 13); dest = Buffer; i = 0; } *dest++ = *p++; } Count = i; } int main(void) { char *p; int c; char b3[3]; for (p = b3; (c = getchar()) != EOF; ) { *p++ = (char)c; if (p == &b3[3]) { encode(b3, 3); p = b3; } } encode(b3, (int)(p - b3)); flushout(); exit(0); /* NOTREACHED */ } inn-2.6.0/frontends/inews.c0000644000175200017520000006750412575023702015227 0ustar iuliusiulius/* $Id: inews.c 9911 2015-07-04 21:32:56Z iulius $ ** ** Send an article (prepared by someone on the local site) to the ** master news server. */ #include "config.h" #include "clibrary.h" #include #include #include #include #include #ifdef HAVE_SYS_TIME_H # include #endif #include #include "inn/innconf.h" #include "inn/libinn.h" #include "inn/messages.h" #include "inn/newsuser.h" #include "inn/nntp.h" #include "inn/paths.h" /* Signature handling. The separator will be appended before the signature, and at most SIG_MAXLINES will be appended. */ #define SIG_MAXLINES 4 #define SIG_SEPARATOR "-- \n" #define FLUSH_ERROR(F) (fflush((F)) == EOF || ferror((F))) #define LPAREN '(' /* For vi :-) */ #define HEADER_DELTA 20 #define GECOSTERM(c) \ ((c) == ',' || (c) == ';' || (c) == ':' || (c) == LPAREN) #define HEADER_STRLEN 998 typedef enum _HEADERTYPE { HTobs, HTreq, HTstd } HEADERTYPE; typedef struct _HEADER { const char *Name; bool CanSet; HEADERTYPE Type; int Size; char *Value; } HEADER; static bool Dump; static bool Revoked; static bool Spooling; static char **OtherHeaders; static char SIGSEP[] = SIG_SEPARATOR; static FILE *FromServer; static FILE *ToServer; static int OtherCount; static int OtherSize; static const char *Exclusions = ""; static const char * const BadDistribs[] = { BAD_DISTRIBS }; static HEADER Table[] = { /* Name Canset Type */ { "Path", true, HTstd, 0, NULL }, #define _path 0 { "From", true, HTstd, 0, NULL }, #define _from 1 { "Newsgroups", true, HTreq, 0, NULL }, #define _newsgroups 2 { "Subject", true, HTreq, 0, NULL }, #define _subject 3 { "Control", true, HTstd, 0, NULL }, #define _control 4 { "Supersedes", true, HTstd, 0, NULL }, #define _supersedes 5 { "Followup-To", true, HTstd, 0, NULL }, #define _followupto 6 { "Date", true, HTstd, 0, NULL }, #define _date 7 { "Organization", true, HTstd, 0, NULL }, #define _organization 8 { "Lines", true, HTstd, 0, NULL }, #define _lines 9 { "Sender", true, HTstd, 0, NULL }, #define _sender 10 { "Approved", true, HTstd, 0, NULL }, #define _approved 11 { "Distribution", true, HTstd, 0, NULL }, #define _distribution 12 { "Expires", true, HTstd, 0, NULL }, #define _expires 13 { "Message-ID", true, HTstd, 0, NULL }, #define _messageid 14 { "References", true, HTstd, 0, NULL }, #define _references 15 { "Reply-To", true, HTstd, 0, NULL }, #define _replyto 16 { "Also-Control", true, HTstd, 0, NULL }, #define _alsocontrol 17 { "Xref", false, HTstd, 0, NULL }, { "Summary", true, HTstd, 0, NULL }, { "Keywords", true, HTstd, 0, NULL }, { "Date-Received", false, HTobs, 0, NULL }, { "Received", false, HTobs, 0, NULL }, { "Posted", false, HTobs, 0, NULL }, { "Posting-Version", false, HTobs, 0, NULL }, { "Relay-Version", false, HTobs, 0, NULL }, }; #define HDR(_x) (Table[(_x)].Value) /* ** Send the server a quit message, wait for a reply. */ static void QuitServer(int x) { char buff[HEADER_STRLEN]; char *p; if (Spooling) exit(x); if (x) warn("article not posted"); fprintf(ToServer, "quit\r\n"); if (FLUSH_ERROR(ToServer)) sysdie("cannot send quit to server"); if (fgets(buff, sizeof buff, FromServer) == NULL) sysdie("warning: server did not reply to quit"); if ((p = strchr(buff, '\r')) != NULL) *p = '\0'; if ((p = strchr(buff, '\n')) != NULL) *p = '\0'; if (atoi(buff) != NNTP_OK_QUIT) die("server did not reply to quit properly: %s", buff); fclose(FromServer); fclose(ToServer); exit(x); } /* ** Failure handler, called by die. Calls QuitServer to cleanly shut down the ** connection with the remote server before exiting. */ static int fatal_cleanup(void) { /* Don't recurse. */ message_fatal_cleanup = NULL; /* QuitServer does all the work. */ QuitServer(1); return 1; } /* ** Flush a stdio FILE; exit if there are any errors. */ static void SafeFlush(FILE *F) { if (FLUSH_ERROR(F)) sysdie("cannot send text to server"); } /* ** Trim trailing spaces, return pointer to first non-space char. */ static char * TrimSpaces(char *p) { char *start; for (start = p; ISWHITE(*start); start++) continue; for (p = start + strlen(start); p > start && isspace((unsigned char) p[-1]); ) *--p = '\0'; return start; } /* ** Mark the end of the header starting at p, and return a pointer ** to the start of the next one. Handles continuations. */ static char * NextHeader(char *p) { for ( ; ; p++) { if ((p = strchr(p, '\n')) == NULL) die("article is all headers"); if (!ISWHITE(p[1])) { *p = '\0'; return p + 1; } } } /* ** Strip any headers off the article and dump them into the table. */ static char * StripOffHeaders(char *article) { char *p; char *q; HEADER *hp; char c; /* Set up the other headers list. */ OtherSize = HEADER_DELTA; OtherHeaders = xmalloc(OtherSize * sizeof(char *)); OtherCount = 0; /* Scan through buffer, a header at a time. */ for (p = article; ; ) { if ((q = strchr(p, ':')) == NULL) die("no colon in header line \"%.30s...\"", p); if (q[1] == '\n' && !ISWHITE(q[2])) { /* Empty header; ignore this one, get next line. */ p = NextHeader(p); if (*p == '\n') break; } if (q[1] != '\0' && !ISWHITE(q[1])) { if ((q = strchr(q, '\n')) != NULL) *q = '\0'; die("no space after colon in \"%.30s...\"", p); } /* See if it's a known header. */ c = islower((unsigned char) *p) ? toupper((unsigned char) *p) : *p; for (hp = Table; hp < ARRAY_END(Table); hp++) if (c == hp->Name[0] && p[hp->Size] == ':' && ISWHITE(p[hp->Size + 1]) && strncasecmp(p, hp->Name, hp->Size) == 0) { if (hp->Type == HTobs) die("obsolete header: %s", hp->Name); if (hp->Value) die("duplicate header: %s", hp->Name); for (q = &p[hp->Size + 1]; ISWHITE(*q); q++) continue; hp->Value = q; break; } /* No; add it to the set of other headers. */ if (hp == ARRAY_END(Table)) { if (OtherCount >= OtherSize - 1) { OtherSize += HEADER_DELTA; OtherHeaders = xrealloc(OtherHeaders, OtherSize * sizeof(char *)); } OtherHeaders[OtherCount++] = p; } /* Get start of next header; if it's a blank line, we hit the end. */ p = NextHeader(p); if (*p == '\n') break; } return p + 1; } /* ** See if the user is allowed to cancel the indicated message. Assumes ** that the Sender or From line has already been filled in. */ static void CheckCancel(char *msgid, bool JustReturn) { char localfrom[SMBUF]; char *p; char buff[BUFSIZ]; char remotefrom[SMBUF]; /* Ask the server for the article. */ fprintf(ToServer, "head %s\r\n", msgid); SafeFlush(ToServer); if (fgets(buff, sizeof buff, FromServer) == NULL || atoi(buff) != NNTP_OK_HEAD) { if (JustReturn) return; die("server has no such article"); } /* Read the headers, looking for the From or Sender. */ remotefrom[0] = '\0'; while (fgets(buff, sizeof buff, FromServer) != NULL) { if ((p = strchr(buff, '\r')) != NULL) *p = '\0'; if ((p = strchr(buff, '\n')) != NULL) *p = '\0'; if (buff[0] == '.' && buff[1] == '\0') break; if (strncasecmp(buff, "Sender:", 7) == 0) strlcpy(remotefrom, TrimSpaces(&buff[7]), SMBUF); else if (remotefrom[0] == '\0' && strncasecmp(buff, "From:", 5) == 0) strlcpy(remotefrom, TrimSpaces(&buff[5]), SMBUF); } if (remotefrom[0] == '\0') { if (JustReturn) return; die("article is garbled"); } HeaderCleanFrom(remotefrom); /* Get the local user. */ strlcpy(localfrom, HDR(_sender) ? HDR(_sender) : HDR(_from), SMBUF); HeaderCleanFrom(localfrom); /* Is the right person cancelling? */ if (strcasecmp(localfrom, remotefrom) != 0) die("article was posted by \"%s\" and you are \"%s\"", remotefrom, localfrom); } /* ** See if the user is the news administrator. */ static bool AnAdministrator(void) { uid_t news_uid; gid_t news_gid; if (Revoked) return false; /* Find out who we are. */ if (get_news_uid_gid(&news_uid, &news_gid, false) != 0) { /* Silent failure; clients might not have the group. */ return false; } if (getuid() == news_uid) return true; /* See if we are in the right group and examine process * supplementary groups, rather than the group(5) file entry. */ #ifdef HAVE_GETGROUPS { int ngroups = getgroups(0, 0); GETGROUPS_T *groups, *gp; int rv; int rest; groups = (GETGROUPS_T *) xmalloc(ngroups * sizeof(GETGROUPS_T)); if ((rv = getgroups(ngroups, groups)) < 0) { /* Silent failure; client doesn't have the group. */ return false; } for (rest = ngroups, gp = groups; rest > 0; rest--, gp++) { if (*gp == (GETGROUPS_T) news_gid) return true; } } #endif return false; } /* ** Check the control message, and see if it's legit. */ static void CheckControl(char *ctrl) { char *p; char *q; char save; /* Snip off the first word. */ for (p = ctrl; ISWHITE(*p); p++) continue; for (ctrl = p; *p && !ISWHITE(*p); p++) continue; if (p == ctrl) die("emtpy control message"); save = *p; *p = '\0'; if (strcasecmp(ctrl, "cancel") == 0) { for (q = p + 1; ISWHITE(*q); q++) continue; if (*q == '\0') die("message ID missing in cancel"); if (!Spooling) CheckCancel(q, false); } else if (strcasecmp(ctrl, "checkgroups") == 0 || strcasecmp(ctrl, "ihave") == 0 || strcasecmp(ctrl, "sendme") == 0 || strcasecmp(ctrl, "newgroup") == 0 || strcasecmp(ctrl, "rmgroup") == 0) { if (!AnAdministrator()) die("ask your news administrator to do the %s for you", ctrl); } else { die("%s is not a valid control message", ctrl); } *p = save; } /* ** Parse the GECOS field to get the user's full name. This comes Sendmail's ** buildfname routine. Ignore leading stuff like "23-" "stuff]-" or ** "stuff -" as well as trailing whitespace, or anything that comes after ** a comma, semicolon, or in parentheses. This seems to strip off most of ** the UCB or ATT stuff people fill out the entries with. Also, turn & ** into the login name, with perhaps an initial capital. (Everyone seems ** to hate that, but everyone also supports it.) */ static char * FormatUserName(struct passwd *pwp, char *node) { char outbuff[SMBUF]; char *buff; char *out; char *p; int left; #if !defined(DONT_MUNGE_GETENV) memset(outbuff, 0, SMBUF); if ((p = getenv("NAME")) != NULL) strlcpy(outbuff, p, SMBUF); if (strlen(outbuff) == 0) { #endif /* !defined(DONT_MUNGE_GETENV) */ #ifndef DO_MUNGE_GECOS strlcpy(outbuff, pwp->pw_gecos, SMBUF); #else /* Be very careful here. If we're not, we can potentially overflow our * buffer. Remember that on some Unix systems, the content of the GECOS * field is under (untrusted) user control and we could be setgid. */ p = pwp->pw_gecos; left = SMBUF - 1; if (*p == '*') p++; for (out = outbuff; *p && !GECOSTERM(*p) && left; p++) { if (*p == '&') { strncpy(out, pwp->pw_name, left); if (islower((unsigned char) *out) && (out == outbuff || !isalpha((unsigned char) out[-1]))) *out = toupper((unsigned char) *out); while (*out) { out++; left--; } } else if (*p == '-' && p > pwp->pw_gecos && (isdigit((unsigned char) p[-1]) || isspace((unsigned char) p[-1]) || p[-1] == ']')) { out = outbuff; left = SMBUF - 1; } else { *out++ = *p; left--; } } *out = '\0'; #endif /* DO_MUNGE_GECOS */ #if !defined(DONT_MUNGE_GETENV) } #endif /* !defined(DONT_MUNGE_GETENV) */ out = TrimSpaces(outbuff); if (out[0]) buff = concat(pwp->pw_name, "@", node, " (", out, ")", (char *) 0); else buff = concat(pwp->pw_name, "@", node, (char *) 0); return buff; } /* ** Check the Distribution header, and exit on error. */ static void CheckDistribution(char *p) { static char SEPS[] = " \t,"; const char * const *dp; if ((p = strtok(p, SEPS)) == NULL) die("cannot parse Distribution header"); do { for (dp = BadDistribs; *dp; dp++) if (uwildmat(p, *dp)) die("illegal distribution %s", p); } while ((p = strtok((char *)NULL, SEPS)) != NULL); } /* ** Process all the headers. FYI, they're done in RFC-order. */ static void ProcessHeaders(bool AddOrg, int linecount, struct passwd *pwp) { static char PATHFLUFF[] = PATHMASTER; HEADER *hp; char *p; char buff[SMBUF]; char from[SMBUF]; /* Do some preliminary fix-ups. */ for (hp = Table; hp < ARRAY_END(Table); hp++) { if (!hp->CanSet && hp->Value) die("cannot set system header %s", hp->Name); if (hp->Value) { hp->Value = TrimSpaces(hp->Value); if (*hp->Value == '\0') hp->Value = NULL; } } /* Set From or Sender. */ if ((p = innconf->fromhost) == NULL) sysdie("cannot get hostname"); if (HDR(_from) == NULL) HDR(_from) = FormatUserName(pwp, p); else { if (strlen(pwp->pw_name) + strlen(p) + 2 > sizeof(buff)) die("username and host are too long"); sprintf(buff, "%s@%s", pwp->pw_name, p); strlcpy(from, HDR(_from), SMBUF); HeaderCleanFrom(from); if (strcmp(from, buff) != 0) HDR(_sender) = xstrdup(buff); } if (HDR(_date) == NULL) { /* Set Date. */ if (!makedate(-1, true, buff, sizeof(buff))) die("cannot generate Date header"); HDR(_date) = xstrdup(buff); } /* Newsgroups are checked later. */ /* Set Subject; Control overrides the subject. */ if (HDR(_control)) { CheckControl(HDR(_control)); } else { p = HDR(_subject); if (p == NULL) die("required Subject header is missing or empty"); else if (HDR(_alsocontrol)) CheckControl(HDR(_alsocontrol)); #if 0 if (strncmp(p, "Re: ", 4) == 0 && HDR(_references) == NULL) die("article subject begins with \"Re: \" but has no references"); #endif /* 0 */ } /* Set Message-ID */ if (HDR(_messageid) == NULL) { if ((p = GenerateMessageID(innconf->domain)) == NULL) die("cannot generate Message-ID header"); HDR(_messageid) = xstrdup(p); } else if ((p = strchr(HDR(_messageid), '@')) == NULL || strchr(++p, '@') != NULL) { die("message ID must have exactly one @"); } /* Set Path */ if (HDR(_path) == NULL) { #if defined(DO_INEWS_PATH) if ((p = innconf->pathhost) != NULL) { if (*p) HDR(_path) = concat(Exclusions, p, "!", PATHFLUFF, (char *) 0); else HDR(_path) = concat(Exclusions, PATHFLUFF, (char *) 0); } else if (innconf->server != NULL) { if ((p = GetFQDN(innconf->domain)) == NULL) sysdie("cannot get hostname"); HDR(_path) = concat(Exclusions, p, "!", PATHFLUFF, (char *) 0); } else { HDR(_path) = concat(Exclusions, PATHFLUFF, (char *) 0); } #else HDR(_path) = concat(Exclusions, PATHFLUFF, (char *) 0); #endif /* defined(DO_INEWS_PATH) */ } /* Reply-To; left alone. */ /* Sender; set above. */ /* Followup-To; checked with Newsgroups. */ /* Check Expires. */ if (HDR(_expires) && parsedate_rfc5322_lax(HDR(_expires)) == -1) die("cannot parse \"%s\" as an expiration date", HDR(_expires)); /* References; left alone. */ /* Control; checked above. */ /* Distribution. */ if ((p = HDR(_distribution)) != NULL) { p = xstrdup(p); CheckDistribution(p); free(p); } /* Set Organization. */ if (AddOrg && HDR(_organization) == NULL && (p = innconf->organization) != NULL) { HDR(_organization) = xstrdup(p); } /* Keywords; left alone. */ /* Summary; left alone. */ /* Approved; left alone. */ /* Set Lines */ sprintf(buff, "%d", linecount); HDR(_lines) = xstrdup(buff); /* Check Supersedes. */ if (HDR(_supersedes)) CheckCancel(HDR(_supersedes), true); /* Now make sure everything is there. */ for (hp = Table; hp < ARRAY_END(Table); hp++) if (hp->Type == HTreq && hp->Value == NULL) die("required header %s is missing or empty", hp->Name); } /* ** Try to append $HOME/.signature to the article. When in doubt, exit ** out in order to avoid postings like "Sorry, I forgot my .signature ** -- here's the article again." */ static char * AppendSignature(bool UseMalloc, char *article, char *homedir, int *linesp) { int i; int length; size_t artsize; char *p; char buff[BUFSIZ]; FILE *F; /* Open the file. */ *linesp = 0; if (strlen(homedir) > sizeof(buff) - 14) die("home directory path too long"); sprintf(buff, "%s/.signature", homedir); if ((F = fopen(buff, "r")) == NULL) { if (errno == ENOENT) return article; fprintf(stderr, "Can't add your .signature (%s), article not posted", strerror(errno)); QuitServer(1); } /* Read it in. */ length = fread(buff, 1, sizeof buff - 2, F); i = feof(F); fclose(F); if (length == 0) die("signature file is empty"); if (length < 0) sysdie("cannot read signature file"); if (length == sizeof buff - 2 && !i) die("signature is too large"); /* Make sure the buffer ends with \n\0. */ if (buff[length - 1] != '\n') buff[length++] = '\n'; buff[length] = '\0'; /* Count the lines. */ for (i = 0, p = buff; (p = strchr(p, '\n')) != NULL; p++) if (++i > SIG_MAXLINES) die("signature has too many lines"); *linesp = 1 + i; /* Grow the article to have the signature. */ i = strlen(article); artsize = i + sizeof(SIGSEP) - 1 + length + 1; if (UseMalloc) { p = xmalloc(artsize); strlcpy(p, article, artsize); article = p; } else article = xrealloc(article, artsize); strlcat(article, SIGSEP, artsize); strlcat(article, buff, artsize); return article; } /* ** See if the user has more included text than new text. Simple-minded, but ** reasonably effective for catching neophyte's mistakes. A line starting ** with > is included text. Decrement the count on lines starting with < ** so that we don't reject diff(1) output. */ static void CheckIncludedText(char *p, int lines) { int i; for (i = 0; ; p++) { switch (*p) { case '>': i++; break; case '|': i++; break; case ':': i++; break; case '<': i--; break; } if ((p = strchr(p, '\n')) == NULL) break; } if ((i * 2 > lines) && (lines > 40)) die("more included text than new text"); } /* ** Read stdin into a string and return it. Can't use ReadInDescriptor ** since that will fail if stdin is a tty. */ static char * ReadStdin(void) { int size; char *p; char *article; char *end; int i; size = BUFSIZ; article = xmalloc(size); end = &article[size - 3]; for (p = article; (i = getchar()) != EOF; *p++ = (char)i) if (p == end) { article = xrealloc(article, size + BUFSIZ); p = &article[size - 3]; size += BUFSIZ; end = &article[size - 3]; } /* Force a \n terminator. */ if (p > article && p[-1] != '\n') *p++ = '\n'; *p = '\0'; return article; } /* ** Offer the article to the server, return its reply. */ static int OfferArticle(char *buff, bool Authorized) { fprintf(ToServer, "post\r\n"); SafeFlush(ToServer); if (fgets(buff, HEADER_STRLEN, FromServer) == NULL) sysdie(Authorized ? "Can't offer article to server (authorized)" : "Can't offer article to server"); return atoi(buff); } /* ** Spool article to temp file. */ static void Spoolit(char *article, size_t Length, char *deadfile) { HEADER *hp; FILE *F; int i; /* Try to write to the deadfile. */ if (deadfile == NULL) return; F = xfopena(deadfile); if (F == NULL) sysdie("cannot create spool file"); /* Write the headers and a blank line. */ for (hp = Table; hp < ARRAY_END(Table); hp++) if (hp->Value) fprintf(F, "%s: %s\n", hp->Name, hp->Value); for (i = 0; i < OtherCount; i++) fprintf(F, "%s\n", OtherHeaders[i]); fprintf(F, "\n"); if (FLUSH_ERROR(F)) sysdie("cannot write headers"); /* Write the article and exit. */ if (fwrite(article, 1, Length, F) != Length) sysdie("cannot write article"); if (FLUSH_ERROR(F)) sysdie("cannot write article"); if (fclose(F) == EOF) sysdie("cannot close spool file"); } /* ** Print usage message and exit. */ static void Usage(void) { fprintf(stderr, "Usage: inews [-D] [-h] [header_flags] [article]\n"); /* Don't call QuitServer here -- connection isn't open yet. */ exit(1); } int main(int ac, char *av[]) { static char NOCONNECT[] = "cannot connect to server"; int i; char *p; HEADER *hp; int j; int port; int Mode; int SigLines; struct passwd *pwp; char *article; char *deadfile; char buff[HEADER_STRLEN]; char SpoolMessage[HEADER_STRLEN]; bool DoSignature; bool AddOrg; size_t Length; uid_t uid; /* First thing, set up logging and our identity. */ message_program_name = "inews"; /* Find out who we are. */ uid = geteuid(); if (uid == (uid_t) -1) sysdie("cannot get your user ID"); if ((pwp = getpwuid(uid)) == NULL) sysdie("cannot get your passwd entry"); /* Set defaults. */ Mode = '\0'; Dump = false; DoSignature = true; AddOrg = true; port = 0; if (!innconf_read(NULL)) exit(1); umask(NEWSUMASK); /* Parse JCL. */ while ((i = getopt(ac, av, "DNAVWORShx:a:c:d:e:f:n:p:r:t:F:o:w:")) != EOF) switch (i) { default: Usage(); /* NOTREACHED */ case 'D': case 'N': Dump = true; break; case 'A': case 'V': case 'W': /* Ignore C News options. */ break; case 'O': AddOrg = false; break; case 'R': Revoked = true; break; case 'S': DoSignature = false; break; case 'h': Mode = i; break; case 'x': Exclusions = concat(optarg, "!", (char *) 0); break; case 'p': port = atoi(optarg); break; /* Header lines that can be specified on the command line. */ case 'a': HDR(_approved) = optarg; break; case 'c': HDR(_control) = optarg; break; case 'd': HDR(_distribution) = optarg; break; case 'e': HDR(_expires) = optarg; break; case 'f': HDR(_from) = optarg; break; case 'n': HDR(_newsgroups) = optarg; break; case 'r': HDR(_replyto) = optarg; break; case 't': HDR(_subject) = optarg; break; case 'F': HDR(_references) = optarg; break; case 'o': HDR(_organization) = optarg; break; case 'w': HDR(_followupto) = optarg; break; } ac -= optind; av += optind; /* Parse positional arguments; at most one, the input file. */ switch (ac) { default: Usage(); /* NOTREACHED */ case 0: /* Read stdin. */ article = ReadStdin(); break; case 1: /* Read named file. */ article = ReadInFile(av[0], (struct stat *)NULL); if (article == NULL) sysdie("cannot read input file"); break; } if (port == 0) port = NNTP_PORT; /* Try to open a connection to the server. */ if (NNTPremoteopen(port, &FromServer, &ToServer, buff, sizeof(buff)) < 0) { Spooling = true; if ((p = strchr(buff, '\n')) != NULL) *p = '\0'; if ((p = strchr(buff, '\r')) != NULL) *p = '\0'; strlcpy(SpoolMessage, buff[0] ? buff : NOCONNECT, sizeof(SpoolMessage)); deadfile = concatpath(pwp->pw_dir, "dead.article"); } else { /* We now have an open server connection, so close it on failure. */ message_fatal_cleanup = fatal_cleanup; /* See if we can post. */ i = atoi(buff); /* Tell the server we're posting. */ setbuf(FromServer, xmalloc(BUFSIZ)); setbuf(ToServer, xmalloc(BUFSIZ)); fprintf(ToServer, "mode reader\r\n"); SafeFlush(ToServer); if (fgets(buff, HEADER_STRLEN, FromServer) == NULL) sysdie("cannot tell server we're reading"); if ((j = atoi(buff)) != NNTP_ERR_COMMAND) i = j; if (i != NNTP_OK_BANNER_POST) { /* We try to authenticate in case it is all the same possible * to post. */ if (NNTPsendpassword((char *)NULL, FromServer, ToServer) < 0) die("you do not have permission to post"); } deadfile = NULL; } /* Basic processing. */ for (hp = Table; hp < ARRAY_END(Table); hp++) hp->Size = strlen(hp->Name); if (Mode == 'h') article = StripOffHeaders(article); for (i = 0, p = article; (p = strchr(p, '\n')) != NULL; i++, p++) continue; if (innconf->checkincludedtext) CheckIncludedText(article, i); if (DoSignature) article = AppendSignature(Mode == 'h', article, pwp->pw_dir, &SigLines); else SigLines = 0; ProcessHeaders(AddOrg, i + SigLines, pwp); Length = strlen(article); if ((innconf->localmaxartsize != 0) && (Length > innconf->localmaxartsize)) die("article is larger than local limit of %lu bytes", innconf->localmaxartsize); /* Do final checks. */ if (i == 0 && HDR(_control) == NULL) die("article is empty"); for (hp = Table; hp < ARRAY_END(Table); hp++) if (hp->Value && (int)strlen(hp->Value) + hp->Size > HEADER_STRLEN) die("%s header is too long", hp->Name); for (i = 0; i < OtherCount; i++) if ((int)strlen(OtherHeaders[i]) > HEADER_STRLEN) die("header too long (maximum length is %d): %.40s...", HEADER_STRLEN, OtherHeaders[i]); if (Dump) { /* Write the headers and a blank line. */ for (hp = Table; hp < ARRAY_END(Table); hp++) if (hp->Value) printf("%s: %s\n", hp->Name, hp->Value); for (i = 0; i < OtherCount; i++) printf("%s\n", OtherHeaders[i]); printf("\n"); if (FLUSH_ERROR(stdout)) sysdie("cannot write headers"); /* Write the article and exit. */ if (fwrite(article, 1, Length, stdout) != Length) sysdie("cannot write article"); SafeFlush(stdout); QuitServer(0); } if (Spooling) { warn("warning: %s", SpoolMessage); warn("article will be spooled"); Spoolit(article, Length, deadfile); exit(0); } /* Article is prepared, offer it to the server. */ i = OfferArticle(buff, false); if (i == NNTP_FAIL_AUTH_NEEDED) { /* Posting not allowed, try to authorize. */ if (NNTPsendpassword((char *)NULL, FromServer, ToServer) < 0) sysdie("authorization error"); i = OfferArticle(buff, true); } if (i != NNTP_CONT_POST) die("server doesn't want the article: %s", buff); /* Write the headers, a blank line, then the article. */ for (hp = Table; hp < ARRAY_END(Table); hp++) if (hp->Value) fprintf(ToServer, "%s: %s\r\n", hp->Name, hp->Value); for (i = 0; i < OtherCount; i++) fprintf(ToServer, "%s\r\n", OtherHeaders[i]); fprintf(ToServer, "\r\n"); if (NNTPsendarticle(article, ToServer, true) < 0) sysdie("cannot send article to server"); SafeFlush(ToServer); if (fgets(buff, sizeof buff, FromServer) == NULL) sysdie("no reply from server after sending the article"); if ((p = strchr(buff, '\r')) != NULL) *p = '\0'; if ((p = strchr(buff, '\n')) != NULL) *p = '\0'; if (atoi(buff) != NNTP_OK_POST) die("cannot send article to server: %s", buff); /* Close up. */ QuitServer(0); /* NOTREACHED */ return 1; } inn-2.6.0/frontends/ovdb_init.c0000644000175200017520000002460512575023702016052 0ustar iuliusiulius/* * ovdb_init * Performs recovery on OV database, if needed * Performs upgrade of OV database, if needed and if '-u' used * Starts ovdb_monitor, if needed */ #include "config.h" #include "clibrary.h" #include "inn/libinn.h" #include #include #include "inn/innconf.h" #include "inn/messages.h" #include "inn/ov.h" #include "../storage/ovdb/ovdb.h" #include "../storage/ovdb/ovdb-private.h" #ifndef HAVE_BDB int main(int argc UNUSED, char **argv UNUSED) { die("Berkeley DB support not compiled"); } #else /* HAVE_BDB */ static int open_db(DB **db, const char *name, int type) { int ret; ret = db_create(db, OVDBenv, 0); if (ret != 0) { warn("db_create failed: %s\n", db_strerror(ret)); return ret; } ret = (*db)->open(*db, NULL, name, NULL, type, DB_CREATE, 0666); if (ret != 0) { (*db)->close(*db, 0); warn("%s->open failed: %s", name, db_strerror(ret)); return ret; } return 0; } /* Upgrade Berkeley DB version */ static int upgrade_database(const char *name UNUSED) { int ret; DB *db; ret = db_create(&db, OVDBenv, 0); if (ret != 0) return ret; notice("upgrading %s...", name); ret = db->upgrade(db, name, 0); if (ret != 0) warn("db->upgrade(%s) failed: %s", name, db_strerror(ret)); db->close(db, 0); return ret; } struct groupstats { ARTNUM low; ARTNUM high; int count; int flag; time_t expired; }; static int v1_which_db(char *group) { HASH grouphash; unsigned int i; grouphash = Hash(group, strlen(group)); memcpy(&i, &grouphash, sizeof(i)); return i % ovdb_conf.numdbfiles; } /* Upgrade ovdb data format version 1 to 2 */ /* groupstats and groupsbyname are replaced by groupinfo */ static int upgrade_v1_to_v2(void) { DB *groupstats, *groupsbyname, *groupinfo, *vdb; DBT key, val, ikey, ival; DBC *cursor; group_id_t gid, higid = 0, higidbang = 0; struct groupinfo gi; struct groupstats gs; char group[MED_BUFFER]; u_int32_t v2 = 2; int ret; notice("upgrading data to version 2"); ret = open_db(&groupstats, "groupstats", DB_BTREE); if (ret != 0) return ret; ret = open_db(&groupsbyname, "groupsbyname", DB_HASH); if (ret != 0) return ret; ret = open_db(&groupinfo, "groupinfo", DB_BTREE); if (ret != 0) return ret; memset(&key, 0, sizeof key); memset(&val, 0, sizeof val); memset(&ikey, 0, sizeof ikey); memset(&ival, 0, sizeof ival); ret = groupsbyname->cursor(groupsbyname, NULL, &cursor, 0); if (ret != 0) return ret; while((ret = cursor->c_get(cursor, &key, &val, DB_NEXT)) == 0) { if(key.size == 1 && *((char *)(key.data)) == '!') { if(val.size == sizeof(group_id_t)) memcpy(&higidbang, val.data, sizeof(group_id_t)); continue; } if(key.size >= MED_BUFFER) continue; memcpy(group, key.data, key.size); group[key.size] = 0; if(val.size != sizeof(group_id_t)) continue; memcpy(&gid, val.data, sizeof(group_id_t)); if(gid > higid) higid = gid; ikey.data = &gid; ikey.size = sizeof(group_id_t); ret = groupstats->get(groupstats, NULL, &ikey, &ival, 0); if (ret != 0) continue; if(ival.size != sizeof(struct groupstats)) continue; memcpy(&gs, ival.data, sizeof(struct groupstats)); gi.low = gs.low; gi.high = gs.high; gi.count = gs.count; gi.flag = gs.flag; gi.expired = gs.expired; gi.current_gid = gi.new_gid = gid; gi.current_db = gi.new_db = v1_which_db(group); gi.expiregrouppid = gi.status = 0; val.data = &gi; val.size = sizeof(gi); ret = groupinfo->put(groupinfo, NULL, &key, &val, 0); if (ret != 0) { warn("groupinfo->put failed: %s", db_strerror(ret)); cursor->c_close(cursor); return ret; } } cursor->c_close(cursor); if(ret != DB_NOTFOUND) { warn("cursor->get failed: %s", db_strerror(ret)); return ret; } higid++; if(higidbang > higid) higid = higidbang; key.data = (char *) "!groupid_freelist"; key.size = sizeof("!groupid_freelist"); val.data = &higid; val.size = sizeof(group_id_t); ret = groupinfo->put(groupinfo, NULL, &key, &val, 0); if (ret != 0) { warn("groupinfo->put failed: %s", db_strerror(ret)); return ret; } ret = open_db(&vdb, "version", DB_BTREE); if (ret != 0) return ret; key.data = (char *) "dataversion"; key.size = sizeof("dataversion"); val.data = &v2; val.size = sizeof v2; ret = vdb->put(vdb, NULL, &key, &val, 0); if (ret != 0) { warn("version->put failed: %s", db_strerror(ret)); return ret; } groupstats->close(groupstats, 0); groupsbyname->close(groupsbyname, 0); groupinfo->close(groupinfo, 0); vdb->close(vdb, 0); ret = db_create(&groupstats, OVDBenv, 0); if (ret != 0) return ret; groupstats->remove(groupstats, "groupstats", NULL, 0); ret = db_create(&groupsbyname, OVDBenv, 0); if (ret != 0) return ret; groupsbyname->remove(groupsbyname, "groupsbyname", NULL, 0); return 0; } static int check_upgrade(int do_upgrade) { int ret, i; DB *db; DBT key, val; u_int32_t dv; char name[50]; if(do_upgrade && (ret = upgrade_database("version"))) return ret; ret = open_db(&db, "version", DB_BTREE); if (ret != 0) return ret; memset(&key, 0, sizeof key); memset(&val, 0, sizeof val); key.data = (char *) "dataversion"; key.size = sizeof("dataversion"); ret = db->get(db, NULL, &key, &val, 0); if (ret != 0) { if(ret != DB_NOTFOUND) { warn("cannot retrieve version: %s", db_strerror(ret)); db->close(db, 0); return ret; } } if(ret == DB_NOTFOUND || val.size != sizeof dv) { dv = DATA_VERSION; val.data = &dv; val.size = sizeof dv; ret = db->put(db, NULL, &key, &val, 0); if (ret != 0) { warn("cannot store version: %s", db_strerror(ret)); db->close(db, 0); return ret; } } else memcpy(&dv, val.data, sizeof dv); key.data = (char *) "numdbfiles"; key.size = sizeof("numdbfiles"); if ((ret = db->get(db, NULL, &key, &val, 0)) == 0) if(val.size == sizeof(ovdb_conf.numdbfiles)) memcpy(&(ovdb_conf.numdbfiles), val.data, sizeof(ovdb_conf.numdbfiles)); db->close(db, 0); if(do_upgrade) { if(dv == 1) { ret = upgrade_database("groupstats"); if (ret != 0) return ret; ret = upgrade_database("groupsbyname"); if (ret != 0) return ret; } else { ret = upgrade_database("groupinfo"); if (ret != 0) return ret; } ret = upgrade_database("groupaliases"); if (ret != 0) return ret; for(i = 0; i < ovdb_conf.numdbfiles; i++) { snprintf(name, sizeof(name), "ov%05d", i); ret = upgrade_database(name); if (ret != 0) return ret; } } if(dv > DATA_VERSION_COMPRESS) { warn("cannot open database: unknown version %d", dv); return EINVAL; } if(dv < DATA_VERSION) { if(do_upgrade) return upgrade_v1_to_v2(); warn("database needs to be upgraded"); return EINVAL; } return 0; } static int upgrade_environment(void) { int ret; ovdb_close_berkeleydb(); ret = ovdb_open_berkeleydb(OV_WRITE, OVDB_UPGRADE); if (ret != 0) return ret; ret = OVDBenv->remove(OVDBenv, ovdb_conf.home, 0); if (ret != 0) return ret; OVDBenv = NULL; ret = ovdb_open_berkeleydb(OV_WRITE, 0); return ret; } int main(int argc, char **argv) { int ret, c, do_upgrade = 0, recover_only = 0, err = 0; bool locked; int flags; openlog("ovdb_init", L_OPENLOG_FLAGS | LOG_PID, LOG_INN_PROG); message_program_name = "ovdb_init"; if (!innconf_read(NULL)) exit(1); if(strcmp(innconf->ovmethod, "ovdb")) die("ovmethod not set to ovdb in inn.conf"); if(!ovdb_check_user()) die("command must be run as runasuser user"); chdir(innconf->pathtmp); while((c = getopt(argc, argv, "ru")) != -1) { switch(c) { case 'r': recover_only = 1; break; case 'u': do_upgrade = 1; break; case '?': warn("unrecognized option -%c", optopt); err++; break; } } if(recover_only && do_upgrade) { warn("cannot use both -r and -u at the same time"); err++; } if(err) { fprintf(stderr, "Usage: ovdb_init [-r|-u]\n"); exit(1); } locked = ovdb_getlock(OVDB_LOCK_EXCLUSIVE); if(locked) { if(do_upgrade) { notice("database is quiescent, upgrading"); flags = OVDB_RECOVER | OVDB_UPGRADE; } else { notice("database is quiescent, running normal recovery"); flags = OVDB_RECOVER; } } else { warn("database is active"); if(do_upgrade) { warn("upgrade will not be attempted"); do_upgrade = 0; } if(recover_only) die("recovery will not be attempted"); ovdb_getlock(OVDB_LOCK_ADMIN); flags = 0; } ret = ovdb_open_berkeleydb(OV_WRITE, flags); if(ret == DB_RUNRECOVERY) { if(locked) die("database could not be recovered"); else { warn("database needs recovery but cannot be locked"); die("other processes accessing the database must exit to start" " recovery"); } } if(ret != 0) die("cannot open Berkeley DB: %s", db_strerror(ret)); if(recover_only) exit(0); if(do_upgrade) { ret = upgrade_environment(); if(ret != 0) die("cannot upgrade Berkeley DB environment: %s", db_strerror(ret)); } if(check_upgrade(do_upgrade)) { ovdb_close_berkeleydb(); exit(1); } ovdb_close_berkeleydb(); ovdb_releaselock(); if(ovdb_check_pidfile(OVDB_MONITOR_PIDFILE) == false) { notice("starting ovdb monitor"); switch(fork()) { case -1: sysdie("cannot fork"); case 0: setsid(); execl(concatpath(innconf->pathbin, "ovdb_monitor"), "ovdb_monitor", SPACES, NULL); syswarn("cannot exec ovdb_monitor"); _exit(1); } sleep(2); /* give the monitor a chance to start */ } else warn("ovdb_monitor already running"); if(ovdb_conf.readserver) { if(ovdb_check_pidfile(OVDB_SERVER_PIDFILE) == false) { notice("starting ovdb server"); daemonize(innconf->pathtmp); execl(concatpath(innconf->pathbin, "ovdb_server"), "ovdb_server", SPACES, NULL); syswarn("cannot exec ovdb_server"); _exit(1); } else warn("ovdb_server already running"); } exit(0); } #endif /* HAVE_BDB */ inn-2.6.0/frontends/ctlinnd.c0000644000175200017520000002071212575023702015523 0ustar iuliusiulius/* $Id: ctlinnd.c 9623 2014-03-15 22:29:46Z iulius $ ** ** Send control messages to the InterNetNews daemon. */ #include "config.h" #include "clibrary.h" #include #include #include #include "inn/innconf.h" #include "inn/messages.h" #include "inn/inndcomm.h" #include "inn/libinn.h" #include "inn/paths.h" /* ** Datatype for an entry in the command table. */ typedef struct _COMMAND { const char *Command; const char *Text; int argc; char Letter; bool Glue; } COMMAND; static COMMAND Commands[] = { { "addhist", "id arr exp post token...\tAdd history line", 5, SC_ADDHIST, true }, { "allow", "reason...\t\t\tAllow remote connections", 1, SC_ALLOW, true }, { "begin", "site\t\t\tStart newly-added site", 1, SC_BEGIN, false }, { "cancel", "id\t\t\tCancel message locally", 1, SC_CANCEL, false }, { "changegroup", "group rest\tChange mode of group", 2, SC_CHANGEGROUP, false }, { "checkfile", "\t\t\tCheck syntax of newsfeeds file", 0, SC_CHECKFILE, false }, { "drop", "site\t\t\tStop feeding site", 1, SC_DROP, false }, { "feedinfo", "site\t\t\tPrint state of feed to site*", 1, SC_FEEDINFO, false }, { "flush", "site\t\t\tFlush feed for site*", 1, SC_FLUSH, false }, { "flushlogs", "\t\t\tFlush log files", 0, SC_FLUSHLOGS, false }, { "go", "reason...\t\t\tRestart after pause or throttle", 1, SC_GO, true }, { "hangup", "channel\t\tHangup specified incoming channel", 1, SC_HANGUP, false }, { "logmode", "\t\t\t\tSend server mode to syslog", 0, SC_LOGMODE, false }, { "mode", "\t\t\t\tPrint operating mode", 0, SC_MODE, false }, { "name", "nnn\t\t\tPrint name of specified channel*", 1, SC_NAME, false }, { "newgroup", "group rest creator\tCreate new group", 3, SC_NEWGROUP, false }, { "param", "letter value\t\tChange command-line parameters", 2, SC_PARAM, false }, { "pause", "reason...\t\tShort-term pause in accepting articles", 1, SC_PAUSE, true }, #ifdef DO_PERL { "perl", "flag\t\t\tEnable or disable Perl filtering", 1, SC_PERL, false }, #endif #ifdef DO_PYTHON { "python", "flag\t\t\tEnable or disable Python filtering", 1, SC_PYTHON, false }, #endif { "readers", "flag text...\t\tEnable or disable newsreading", 2, SC_READERS, true }, { "reject", "reason...\t\t\tReject remote connections", 1, SC_REJECT, true }, { "reload", "what reason...\t\tRe-read config files*", 2, SC_RELOAD, true }, { "renumber", "group\t\tRenumber the active file*", 1, SC_RENUMBER, false }, { "reserve", "reason...\t\tReserve the next pause or throttle", 1, SC_RESERVE, true }, { "rmgroup", "group\t\t\tRemove named group", 1, SC_RMGROUP, false }, { "send", "feed text...\t\tSend text to exploder feed", 2, SC_SEND, true }, { "shutdown", "reason...\t\tShut down server", 1, SC_SHUTDOWN, true }, { "stathist", "filename|off\t\tLog into filename some history stats", 1, SC_STATHIST, false }, { "status", "interval|off\t\tTurn innd status generation on or off", 1, SC_STATUS, false }, { "kill", "signal site\t\tSend signal to site's process", 2, SC_SIGNAL, false }, { "throttle", "reason...\t\tStop accepting articles", 1, SC_THROTTLE, true }, { "timer", "interval|off\t\tTurn performance monitoring on or off", 1, SC_TIMER, false }, { "trace", "innd|#|nnrpd flag\tTurn tracing on or off", 2, SC_TRACE, false }, { "xabort", "text...\t\tAbort the server", 1, SC_XABORT, true }, { "lowmark", "filename\t\tReset active file low article marks", 1, SC_LOWMARK, false }, { "renumberlow", "filename\t\tReset active file low article marks", 1, SC_LOWMARK, false }, { "xexec", "path\t\t\tExec new server", 1, SC_XEXEC, false } }; /* ** Print a help summary. */ static void Help(char *p) { COMMAND *cp; if (p == NULL) { printf("Command summary:\n"); for (cp = Commands; cp < ARRAY_END(Commands); cp++) printf(" %s %s\n", cp->Command, cp->Text); printf("* Empty string means all sites/groups/etc.\n"); printf("... All trailing words are glued together.\n"); exit(0); } for (cp = Commands; cp < ARRAY_END(Commands); cp++) if (strcmp(p, cp->Command) == 0) { printf("Command usage:\n"); printf(" %s %s\n", cp->Command, cp->Text); exit(0); } printf("No such command.\n"); exit(0); } /* ** Print a command-usage message and exit. */ static void WrongArgs(COMMAND *cp) { printf("Wrong number of arguments -- usage:\n"); printf(" %s %s\n", cp->Command, cp->Text); exit(1); } /* ** Print an error message and exit. */ static void Failed(const char *p) { if (ICCfailure) syswarn("cannot %s (%s failure)", p, ICCfailure); else syswarn("cannot %s", p); ICCclose(); exit(1); } /* ** Print an error reporting incorrect usage. */ static void Usage(const char *what) { fprintf(stderr, "Usage error (%s) -- try -h for help.\n", what); exit(1); } int main(int ac, char *av[]) { static char Y[] = NF_FLAG_OK_STRING; static char EMPTY[] = ""; COMMAND *cp; char *p; int i; bool Silent; bool NeedHelp; char *reply; char *new; int length; char *nv[4]; struct stat Sb; char buff[SMBUF]; /* First thing, set up our identity. */ message_program_name = "ctlinnd"; /* Set defaults. */ if (!innconf_read(NULL)) exit(1); Silent = false; NeedHelp = false; ICCsettimeout(CTLINND_TIMEOUT); /* Parse JCL. */ while ((i = getopt(ac, av, "hst:")) != EOF) switch (i) { default: Usage("bad flags"); /* NOTREACHED */ case 'h': /* Get help */ NeedHelp = true; break; case 's': /* Silent -- no output */ Silent = true; break; case 't': /* Time to wait for reply */ ICCsettimeout(atoi(optarg)); break; } ac -= optind; av += optind; if (NeedHelp) Help(av[0]); if (ac == 0) Usage("missing command"); /* Look up the command word and move to the arguments. */ if (strcmp(av[0], "help") == 0) Help(av[1]); for (cp = Commands; cp < ARRAY_END(Commands); cp++) if (strcmp(av[0], cp->Command) == 0) break; if (cp == ARRAY_END(Commands)) Usage("unknown command"); ac--; av++; /* Check argument count. */ if (cp->Letter == SC_NEWGROUP) { /* Newgroup command has defaults. */ switch (ac) { default: WrongArgs(cp); /* NOTREACHED */ case 1: nv[0] = av[0]; nv[1] = Y; nv[2] = EMPTY; nv[3] = NULL; av = nv; break; case 2: nv[0] = av[0]; nv[1] = av[1]; nv[2] = EMPTY; nv[3] = NULL; av = nv; break; case 3: break; } } else if (ac > cp->argc && cp->Glue) { /* Glue any extra words together. */ for (length = 0, i = cp->argc - 1; (p = av[i++]) != NULL; ) length += strlen(p) + 1; new = xmalloc(length); *new = '\0'; for (i = cp->argc - 1; av[i]; i++) { if (i >= cp->argc) strlcat(new, " ", length); strlcat(new, av[i], length); } av[cp->argc - 1] = new; av[cp->argc] = NULL; } else if (ac != cp->argc) /* All other commands must have the right number of arguments. */ WrongArgs(cp); /* For newgroup and changegroup, make sure the mode is valid. */ if (cp->Letter == SC_NEWGROUP || cp->Letter == SC_CHANGEGROUP) { switch (av[1][0]) { default: Usage("Bad group mode"); /* NOTREACHED */ case NF_FLAG_ALIAS: case NF_FLAG_JUNK: case NF_FLAG_MODERATED: case NF_FLAG_OK: case NF_FLAG_NOLOCAL: case NF_FLAG_IGNORE: break; } } /* Make sure there are no separators in the parameters. */ for (i = 0; (p = av[i++]) != NULL; ) if (strchr(p, SC_SEP) != NULL) die("illegal character \\%03o in %s", SC_SEP, p); /* Do the real work. */ if (ICCopen() < 0) Failed("setup communication"); i = ICCcommand(cp->Letter, (const char **) av, &reply); if (i < 0) { i = errno; p = concatpath(innconf->pathrun, INN_PATH_SERVERPID); if (stat(p, &Sb) < 0) warn("no innd.pid file; did server die?"); free(p); snprintf(buff, sizeof(buff), "send \"%s\" command", cp->Command); errno = i; Failed(buff); } if (reply) { /* Skip "" part of reply. */ for (p = reply; *p && isdigit((unsigned char) *p); p++) continue; while (*p && ISWHITE(*p)) p++; if (i != 0) warn("%s", p); else if (!Silent) printf("%s\n", p); } if (ICCclose() < 0) Failed("end communication"); exit(i); /* NOTREACHED */ } inn-2.6.0/frontends/mailpost.in0000644000175200017520000005203112575023702016103 0ustar iuliusiulius#! /usr/bin/perl -w # fixscript will replace this line with code to load INN::Config # mailpost - Yet another mail-to-news filter # # $Id: mailpost.in 9830 2015-04-23 18:56:28Z iulius $ # # 21feb00 [added "lc" to duplicate header fixer stmt to make it case-insensitive] # doka 11may99 [fixed duplicate headers problem] # brister 19oct98 [cleaned up somewhat for Perl v. 5. and made a little more robust] # vixie 29jan95 [RCS'd] # vixie 15jun93 [added -m] # vixie 30jun92 [added -a and -d] # vixie 17jun92 [attempt simple-minded fixup to $path] # vixie 14jun92 [original] use Getopt::Std; use IPC::Open3; use IO::Select; use POSIX qw(setsid); use Sys::Hostname; use INN::Utils::Shlock; use strict; my $debugging = 0 ; my $tmpfile ; my $tmpfile2 ; my $msg ; END { unlink ($tmpfile) if $tmpfile; # in case we die() unlink ($tmpfile2) if $tmpfile2; # in case we die() # In case we bail out, typically by calling mailArtAndDie(), while # holding a lock. INN::Utils::Shlock::releaselocks(); } my $usage = $0 ; $usage =~ s!.*/!! ; my $prog = $usage ; my $use_syslog = 0; eval { require Sys::Syslog; import Sys::Syslog; $use_syslog = 1; }; if ($use_syslog) { if ($Sys::Syslog::VERSION < 0.15) { eval "sub Sys::Syslog::_PATH_LOG { '/dev/log' }" if $^O eq 'dec_osf'; Sys::Syslog::setlogsock('unix') if $^O =~ /linux|dec_osf|freebsd|darwin/; } openlog($prog, 'pid', $INN::Config::syslog_facility); } $usage .= "[ -h ][ -n ][ -r addr ][ -f addr ][ -a approved ][ -d distribution ]" . "[ -m mailing-list ][ -b database ][ -o output-path ][ -c wait-time ]" . "[ -x header[:header...] ][ -p port ][ -t tempdir ] newsgroups" ; use vars qw($opt_r $opt_f $opt_a $opt_d $opt_m $opt_b $opt_n $opt_o $opt_h $opt_c $opt_x $opt_p $opt_t) ; getopts("hr:f:a:d:m:b:no:c:x:p:t:") || die "usage: $usage\n" ; die "usage: $usage\n" if $opt_h ; # # $Submit is a program which takes no arguments and whose stdin is supposed # to be a news article (without the #!rnews header but with the news hdr). # my $Sendmail = $INN::Config::mta ; my $Submit = $INN::Config::inews . " -S -h" . ($opt_p ? " -p $opt_p" : ''); my $Maintainer = $INN::Config::newsmaster || "usenet" ; my $WhereTo = $opt_o || $Submit ; my $Mailname = $INN::Config::fromhost || hostname ; my $Databasedir = $opt_b || $INN::Config::pathdb; die "Database path $Databasedir is not a directory\n" unless -d $Databasedir; die "Database directory $Databasedir is not writable\n" unless -w $Databasedir; my $Database = $Databasedir . "/mailpost-msgid" ; # Can't always use $INN::Config::pathtmp as we're usually not running as news. my $Tmpdir = $opt_t || (-w $INN::Config::pathtmp ? $INN::Config::pathtmp : "/var/tmp"); die "Path $Tmpdir is not a directory\n" unless -d $Tmpdir; die "Directory $Tmpdir is not writable\n" unless -w $Tmpdir; if ($debugging || $opt_n) { $Sendmail = "cat" ; $WhereTo = "cat" ; } # # Our command-line argument(s) are the list of newsgroups to post to. # # There may be a "-r sender" or "-f sender" which becomes the $path # (which is in turn overridden below by various optional headers). # # -d (distribution) and -a (approved) are also supported to supply # or override the mail headers by those names. # my $path = 'nobody'; my $newsgroups = undef; my $approved = undef; my $distribution = undef; my $mailing_list = undef; my $references = undef; my @errorText = (); if ($opt_r || $opt_f) { $path = $opt_r || $opt_f ; push @errorText, "((path: $path))\n" ; } if ($opt_a) { $approved = &fix_sender_addr($opt_a); push @errorText, "((approved: $approved))\n"; } if ($opt_d) { $distribution = $opt_d ; push @errorText, "((distribution: $distribution))\n"; } if ($opt_m) { $mailing_list = "<" . $opt_m . "> /dev/null"; push @errorText, "((mailing_list: $mailing_list))\n"; } my $exclude = 'Organization|Distribution'; if ($opt_x) { $exclude .= '|' . join('|', split(/:/, $opt_x)); } $newsgroups = join ",", @ARGV ; die "usage: $0 newsgroup [newsgroup ...]\n" unless $newsgroups; # # Do the header. Our input is a mail message, with or without the From. # #$message_id = sprintf("", time, $$, $Hostname); my $real_news_hdrs = ''; my $weird_mail_hdrs = ''; my $fromHdr = "MAILPOST-UNKNOWN-FROM" ; my $dateHdr= "MAILPOST-UNKNOWN-DATE" ; my $msgIdHdr = "MAILPOST-UNKNOWN-MESSAGE-ID" ; my $from = undef; my $date = undef; my $hdr = undef; my $txt = undef; my $message_id ; my $subject = "(NONE)"; $_ = ; if (!$_) { if ( $debugging || -t STDERR ) { die "empty input" ; } else { syslog("err", "empty input") if ($use_syslog); exit (0) ; } } # Remove (CR)LF at the end of each line. s/\r?\n$//; my $line = undef; if (/^From\s+([^\s]+)\s+/) { $path = $1; push @errorText, "((path: $path))\n"; $_ = $'; if (/ remote from /) { $path = $' . '!' . $path; $_ = $`; } } else { $line = $_; } for (;;) { last if defined($line) && ($line =~ /^$/) ; $_ = ; last unless defined $_ ; # Remove (CR)LF at the end of each line. s/\r?\n$//; # Gather up a single header with possible continuation lines into $line. if (/^\s+/) { if (! $line) { $msg = "First line with leading whitespace!" ; if ($use_syslog) { syslog("err", $msg) unless -t STDERR; } die "$msg\n" ; } $line .= "\n" . $_ ; next ; } # Add a space after the colon following a header name, if not present. s/:/: / if ($_ !~ /^[^:]+: /); # On the first header, $line will be undefined. ($_, $line) = ($line, $_) ; # Swap $line and $_. last if defined($_) && /^$/; next unless defined($_); # Only on first header will this happen. push @errorText, "($_)\n"; next if /^Approved:\s/sio && defined($approved); next if /^Distribution:\s/sio && defined($distribution); if (/^($exclude):\s*/sio) { $real_news_hdrs .= "$_\n"; next; } if (/^Subject:\s*/sio) { $subject = $'; next; } if (/^Message-ID:\s*/sio) { $message_id = $'; next; } if (/^Mailing-List:\s*/sio) { $mailing_list = $'; next; } if (/^(Sender|Approved):\s*/sio) { $real_news_hdrs .= "$&" . fix_sender_addr($') . "\n"; next; } if (/^Return-Path:\s*/sio) { $path = $'; $path = $1 if ($path =~ /\<([^\>]*)\>/); push @errorText, "((path: $path))\n"; next; } if (/^Date:\s*/sio) { $date = $'; next; } if (/^From:\s*/sio) { $from = &fix_sender_addr($'); next; } if (/^References:\s*/sio) { $references = $'; # 986 = 998 (maximum per RFC 5536) - length("References: ") if (length($references) > 985) { my @refarray = ( $references =~ /(<.*?>)/g ); # Keep only the first and the last two message-IDs, per RFC 5537. # # Remove the header in case we do not have at least 3 message-IDs # because it then probably means that the header is broken, or # contains CFWS that we do not deal with. if (scalar(@refarray) > 2) { my $last_mid = pop(@refarray); $references = shift(@refarray) . ' ' . pop(@refarray) . ' ' . $last_mid; } else { $references = undef; } } next; } if (!defined($references) && /^In-Reply-To:[^\<]*\<([^\>]+)\>/sio) { $references = "<$1>"; # FALLTHROUGH } if (/^(MIME|Content)-[^:]+:\s*/sio) { $real_news_hdrs .= $_ . "\n" ; next ; } # Strip out news X-Trace: and X-Complaints-To: headers since otherwise posting # may fail. Other trace headers will be renamed to add 'X-' so we don't have # to worry about them. if (/^X-(Trace|Complaints-To):\s*/sio) { next ; } # Random unknown header. Prepend 'X-' if it is not already there. $_ = "X-$_" unless /^X-/sio ; $weird_mail_hdrs .= "$_\n"; } $msgIdHdr = $message_id if $message_id ; $fromHdr = $from if $from ; $dateHdr = $date if $date ; if ($path !~ /\!/) { $path = "$'!$`" if ($path =~ /\@/); } $real_news_hdrs .= "Subject: ${subject}\n"; $real_news_hdrs .= "Message-ID: ${msgIdHdr}\n" if defined($message_id); $real_news_hdrs .= "Mailing-List: ${mailing_list}\n" if defined($mailing_list); $real_news_hdrs .= "Distribution: ${distribution}\n" if defined($distribution); $real_news_hdrs .= "Approved: ${approved}\n" if defined($approved); $real_news_hdrs .= "References: ${references}\n" if defined($references); # Remove duplicate headers. my %headers = (); $real_news_hdrs =~ s/((.*?:) .*?($|\n)([ \t]+.*?($|\n))*)/$headers{lc$2}++?"":"$1"/ges; # inews writes error messages to stdout. We want to capture those and mail # them back to the newsmaster. Trying to write and read from a subprocess is # ugly and prone to deadlock, so we use a temp file. $tmpfile = sprintf "%s/mailpost.%d.%d", $Tmpdir, time, $$ ; if (!open TMPFILE,">$tmpfile") { $msg = "can't open temp file ($tmpfile): $!" ; $tmpfile = undef ; if ($use_syslog) { syslog("err", "$msg") unless $debugging || -t STDERR; } open(TMPFILE, "|" . sprintf ($Sendmail, $Maintainer)) || die "die(no tmpfile): sendmail: $!\n" ; print TMPFILE <<"EOF"; To: $Maintainer Subject: mailpost failure ($newsgroups): $msg -------- Article Contents EOF } print TMPFILE <<"EOF"; Path: ${path} From: ${fromHdr} Newsgroups: ${newsgroups} ${real_news_hdrs}Date: ${dateHdr} ${weird_mail_hdrs} EOF my $rest = ''; $rest .= $_ while (); $rest =~ s/\n*$/\n/g; # Remove trailing \n except very last. print TMPFILE $rest; close TMPFILE ; if ( ! $tmpfile ) { # We had to bail and mail the article to the admin. print STDERR "The creation of the temporary file $tmpfile failed.\n" if -t STDERR; exit(1); } ## ## We've got the article in a temp file and now we validate some of the ## data we found and update our Message-ID database. ## mailArtAndDie ("no From: found") unless $from; mailArtAndDie ("no Message-ID: found") unless $message_id; mailArtAndDie ("Malformed Message-ID ($message_id)") if ($message_id !~ /\<(\S+)\@(\S+)\>/); # Update (with locking) our Message-ID database. This is used to make sure we # don't loop our own gatewayed articles back through the mailing list. my ($lhs, $rhs) = ($1, $2); # Of message_id matched above. $rhs =~ tr/A-Z/a-z/; $message_id = "${lhs}\@${rhs}"; push @errorText, "(TAS message-ID database for $message_id)\n"; my $lockfile = sprintf("%s.lock", $Database); # Acquire a lock. INN::Utils::Shlock::lock($lockfile, 5) or mailArtAndDie ("cannot create lockfile $lockfile"); my %DATABASE ; dbmopen(%DATABASE, $Database, 0666) || mailArtAndDie ("can't dbmopen $Database: $!"); if (defined $DATABASE{$message_id}) { if (!$opt_c) { syslog("err", "Duplicate article <$message_id>.") if $use_syslog; print STDERR "Duplicate article <$message_id>.\n" if -t STDERR; exit(1); } ## crosspost -c $newsgroups = &append_newsgroups($DATABASE{$message_id}, $newsgroups) ; syslog("err", "crosspost $newsgroups") if $debugging && $use_syslog; } #$DATABASE{$message_id} = sprintf "%d.%s", time, 'mailpost' ; $DATABASE{$message_id} = $newsgroups ; mailArtAndDie ("TAS didn't set $message_id") unless defined $DATABASE{$message_id}; dbmclose(%DATABASE) || mailArtAndDie ("can't dbmclose $Database: $!") ; # Unlock. INN::Utils::Shlock::unlock($lockfile) or mailArtAndDie ("cannot unlock $lockfile"); ## For crosspost. if ($opt_c) { my $pid = fork(); if (!defined($pid)) { undef $tmpfile; # Don't unlink $tmpfile. print STDERR "An error occurred during the fork.\n" if -t STDERR; exit(1); } if ($pid != 0) { # Parent. undef $tmpfile; # Don't unlink $tmpfile. exit(0); } close STDIN; close STDOUT; close STDERR; setsid(); open (STDIN, "/dev/null"); open (STDERR, ">&STDOUT"); sleep $opt_c ; # Acquire a lock. INN::Utils::Shlock::lock($lockfile, 5) or mailArtAndDie ("cannot create lockfile $lockfile"); my $umask_bak = umask(); umask(000); dbmopen(%DATABASE, $Database, 0666) || mailArtAndDie ("can't dbmopen $Database: $!"); umask($umask_bak); my $dup = undef ; syslog("err", "check " . $DATABASE{$message_id} . " : $newsgroups") if $debugging && $use_syslog; $dup = 1 if ($DATABASE{$message_id} ne $newsgroups) ; dbmclose(%DATABASE) || mailArtAndDie ("can't dbmclose $Database: $!") ; # Unlock. INN::Utils::Shlock::unlock($lockfile) or mailArtAndDie ("cannot unlock $lockfile"); if (defined($dup)) { syslog("err", "mismatch $newsgroups") if $debugging && $use_syslog; exit(1); } # Replace Newsgroups:. open(TMPFILE, "$tmpfile") || mailArtAndDie ("can't open temp file ($tmpfile): $!") ; $tmpfile2 = sprintf "%s/mailpost-crosspost.%d.%d", $Tmpdir, time, $$ ; if ( !open TMPFILE2, ">$tmpfile2") { $msg = "can't open temp file ($tmpfile2): $!" ; $tmpfile2 = undef ; die $msg ; } for (;;) { $_ = ; # Remove (CR)LF at the end of each line. s/\r?\n$//; last if defined($_) && /^$/ ; if (/^Newsgroups:\s*/sio) { printf TMPFILE2 "Newsgroups: %s\n", $newsgroups ; next ; } print TMPFILE2 "$_\n" ; } printf TMPFILE2 "\n" ; my $rest = ''; $rest .= $_ while (); $rest =~ s/\n*$/\n/g; # Remove trailing \n except very last. print TMPFILE2 $rest; close TMPFILE2 ; close TMPFILE ; rename($tmpfile2, $tmpfile) || mailArtAndDie ("can't rename $tmpfile2 $tmpfile: $!") ; $tmpfile2 = undef ; } if (!open INEWS, "$WhereTo < $tmpfile 2>&1 |") { mailArtAndDie ("can't start $WhereTo: $!") ; } my @inews = ; close INEWS ; my $status = $? ; if (@inews) { chomp @inews ; mailArtAndDie ("inews failed: @inews") ; } unlink $tmpfile ; exit $status; sub mailArtAndDie { my ($msg) = @_ ; syslog("err", "$msg") if $use_syslog; print STDERR $msg,"\n" if -t STDERR ; open(SENDMAIL, "|" . sprintf ($Sendmail,$Maintainer)) || die "die($msg): sendmail: $!\n" ; print SENDMAIL <<"EOF" ; To: $Maintainer Subject: mailpost failure ($newsgroups) $msg EOF if ($tmpfile && -f $tmpfile) { print SENDMAIL "\n-------- Article Contents\n\n" ; open(FILE, "<$tmpfile") || die "open($tmpfile): $!\n" ; print SENDMAIL while ; close FILE ; } else { print STDERR "No article left to send back.\n" if -t STDERR; } close SENDMAIL ; # unlink $tmpfile ; # We use here a non-zero exit. It should normally not cause problems. exit(1); } # # Take RFC-5322-format name (either "comment comment" or "addr (comment)") # and return in always-qualified RFC-5321-format ("addr (comment)"). # sub fix_sender_addr { my ($address) = @_; my ($lcomment, $addr, $rcomment, $comment); local ($',$`,$_) ; if ($address =~ /\<([^\>]*)\>/) { ($lcomment, $addr, $rcomment) = (&dltb($`), &dltb($1), &dltb($')); } elsif ($address =~ /\(([^\)]*)\)/) { ($lcomment, $addr, $rcomment) = ('', &dltb($`.$'), &dltb($1)); } else { ($lcomment, $addr, $rcomment) = ('', &dltb($address), ''); } #print STDERR "fix_sender_addr($address) == ($lcomment, $addr, $rcomment)\n"; $addr .= "\@$Mailname" unless ($addr =~ /\@/); if ($lcomment && $rcomment) { $comment = $lcomment . ' ' . $rcomment; } else { $comment = $lcomment . $rcomment; } $_ = $addr; $_ .= " ($comment)" if $comment; #print STDERR "\t-> $_\n"; return $_; } # # Delete leading and trailing blanks. # sub dltb { my ($str) = @_; $str =~ s/^\s+//o; $str =~ s/\s+$//o; return $str; } sub append_newsgroups ($$) { my (@orig) = split(/,/,$_[0]) ; my (@new) = split(/,/,$_[1]) ; my $newsgroup ; foreach $newsgroup (@new) { if ( !grep($_ eq $newsgroup,@orig)) { push @orig, $newsgroup ; } else { # mailArtAndDie ("Duplicate Newsgroups: $newsgroup") ; } } return join ",", @orig ; } =head1 NAME mailpost - Feed an e-mail message into a newsgroup =head1 SYNOPSIS B [B<-hn>] [B<-a> I] [B<-b> I] [B<-c> I] [B<-d> I] [B<-f> I] [B<-m> I] [B<-o> I] [B<-p> I] [B<-r> I] [B<-t> I] [B<-x> I
[B<:>I
...]] I =head1 DESCRIPTION The B program reads a properly formatted e-mail message from stdin and feeds it to B for posting to a news server. I is a whitespace-separated list of group names to which to post the article (at least one newsgroup must be specified). Before feeding the article to B, it checks that the article has not been seen before, and it changes some headers (cleans up some address headers, removes X-Trace: and X-Complaints-To:, and puts C in front of unknown headers). If the article has been seen before (B records the Message-ID of each article it handles), then the article will be dropped with a non-zero error status. Other errors will cause the article to be mailed to the newsmaster (selected at configure time and defaulting to C). Normally, B is run by sendmail(8) via an alias entry: local-mail-wreck-bikes: "|/mailpost -b /var/tmp -t /var/tmp -d local local.mail.rec.bicycles.racing" The B<-b> and B<-t> flags are useful to change the directories used by B by default. As a matter of fact, though it is recommended to run B as the news user, it is as often as not run as another user, for instance the mail user. Therefore, you should make sure to create and set to be writable by the user that B runs as the directories where to put the database and the temporary files. Instead of F, the mail spool directory can be specified, or any other directory where the B process has write access. =head1 OPTIONS =over 4 =item B<-a> I If the B<-a> flag is used, the value given is added to the article as an Approved: header. =item B<-b> I If the B<-b> flag is used, then it defines the location of the persistent database used to store the Message-IDs of articles sent on. This is to prevent articles looping around if a news-to-mail gateway sends them back here. This option may be required if the B process does not have write access to the news database directory. The default value is I as set in F. =item B<-c> I The B<-c> flag indicates a length of time to sleep before posting. If duplicate messages are received in this interval (by any instance of B using the same database), the article is only posted once, but with Newsgroups: header modified to crosspost the article to all indicated groups. The units for I are seconds; a reasonable value may be anywhere from tens to hundreds of seconds, or even higher, depending on how long mail can be delayed on its way to your system. =item B<-d> I If the B<-d> flag is used, the value given is added to the article as a Distribution: header. =item B<-f> I The B<-f> flag is a synonym for the B<-r> flag. =item B<-h> Print usage information and exit. =item B<-m> I If the B<-m> flag is used, the value given is added to the article in a Mailing-List: header, if such a header doesn't already exist. =item B<-n> If the B<-n> flag is used, neither an article is posted nor a mail is sent in case an error occurs. Everything is written to the standard output. =item B<-o> I Specifies the program to which the resulting article processed by B should be sent. For debugging purpose, C<-o cat> can be used. The default value is C. =item B<-p> I Specifies the port on which B is listening, used for article posting. If given, B<-p> is passed along to B. =item B<-r> I A heuristic is used to determine a reasonable value for the Path: header. The B<-r> flag indicates what to use if no other value can be determined. =item B<-t> I If the B<-t> flag is used, then it defines the location of the directory to use to temporarily store error messages that are sent to the newsmaster. This option may be required if the default value refers to a path that does not exist or the B process does not have write access to. Two paths are tried by default: I as set in F, and then F if I is not writable. =item B<-x> I
[B<:>I
...] A colon-separated list of additional headers which should be treated as known headers; these headers will be passed through to B without having C prepended. Known headers are: Approved Content-* Date Distribution From Mailing-List Message-ID MIME-* References Return-Path Sender Subject =back =head1 FILES =over 4 =item I/mailpost The Perl script itself used to feed an e-mail message to a newsgroup. =item I/mailpost-msgid.dir and I/mailpost-msgid.pag The default database files which record previously seen Message-IDs. =back =head1 HISTORY Written by Paul Vixie long ago and then hacked up by James Brister for INN integration. $Id: mailpost.in 9830 2015-04-23 18:56:28Z iulius $ =head1 SEE ALSO active(5), inews(1), inn.conf(5), nnrpd(8), uwildmat(3). =cut inn-2.6.0/frontends/getlist.c0000644000175200017520000002126612575023702015550 0ustar iuliusiulius/* $Id: getlist.c 9691 2014-09-17 16:31:35Z iulius $ ** ** Send a LIST command to an NNTP server and print the results. */ #include "config.h" #include "clibrary.h" #include #include "inn/innconf.h" #include "inn/messages.h" #include "inn/nntp.h" #include "inn/qio.h" #include "inn/vector.h" #include "inn/libinn.h" #include "inn/paths.h" static const char usage[] = "\ Usage: getlist [-AR] [-h host] [-p port] [list [pattern [types]]]\n\ \n\ getlist obtains a list from an NNTP server and prints it out. By default,\n\ the active file is retrieved, but any list that the NNTP server supports\n\ can be requested. The pattern is passed to the remote server as a filter\n\ on the returned values. If the list parameter is \"active\", a third\n\ parameter can be given, listing the acceptable group types. Only groups of\n\ that type (y and m being the most common) are returned.\n"; /* ** Print out an appropriate error message for a bad NNTP response code and ** exit. */ static void die_nntp_code(enum nntp_code code, const char *line) { if (code == 0) die("unexpected server response: %s", line); else die("unexpected server response: %03d %s", code, line); } /* ** Print out an appropriate error message for a bad NNTP status and exit. */ static void die_nntp_status(enum nntp_status status) { switch (status) { case NNTP_READ_LONG: die("line from server too long for 128KB buffer"); case NNTP_READ_EOF: die("server closed connection unexpectedly"); case NNTP_READ_TIMEOUT: die("read from server timed out"); default: sysdie("cannot read response from server"); } } /* ** Send a command and pattern to the remote server and make sure we get an ** appropriate response. */ static void send_list(struct nntp *nntp, const char *command, const char *pattern) { bool okay; enum nntp_status status; enum nntp_code code; char *line; if (pattern != NULL) okay = nntp_send_line(nntp, "LIST %s %s", command, pattern); else okay = nntp_send_line(nntp, "LIST %s", command); if (!okay) sysdie("cannot send LIST command to server"); status = nntp_read_response(nntp, &code, &line); if (status != NNTP_READ_OK) die_nntp_status(status); if (code != NNTP_OK_LIST) die_nntp_code(code, line); } /* ** Print out the results of a LIST command. Used for generic list commands ** or active commands without a type parameter. */ static void print_list(struct nntp *nntp) { enum nntp_status status; char *line; status = nntp_read_line(nntp, &line); while (status == NNTP_READ_OK) { if (strcmp(line, ".") == 0) break; printf("%s\n", line); status = nntp_read_line(nntp, &line); } if (status != NNTP_READ_OK) die_nntp_status(status); } /* ** Print out the results of a LIST ACTIVE command, limited to particular ** group types. */ static void print_active(struct nntp *nntp, const char *types) { enum nntp_status status; char *line; struct cvector *group = NULL; status = nntp_read_line(nntp, &line); while (status == NNTP_READ_OK) { if (strcmp(line, ".") == 0) break; group = cvector_split_space(line, group); if (group->count != 4) { warn("malformed line from server: %s", line); continue; } if (strchr(types, group->strings[3][0]) != NULL) printf("%s %s %s %s\n", group->strings[0], group->strings[1], group->strings[2], group->strings[3]); status = nntp_read_line(nntp, &line); } if (status != NNTP_READ_OK) die_nntp_status(status); } /* ** Get the username and password for a remote site from the local password ** file. username and password will be set to newly allocated strings on ** success. */ static bool get_authinfo(const char *server, char **username, char **password) { char *path, *line; QIOSTATE *passwords; struct cvector *info = NULL; path = concatpath(innconf->pathetc, INN_PATH_NNTPPASS); passwords = QIOopen(path); if (passwords == NULL) { if (errno != ENOENT) warn("cannot open %s", path); return false; } free(path); while ((line = QIOread(passwords)) != NULL) { if (line[0] == '\0' || line[0] == '#') continue; info = cvector_split(line, ':', info); if (info->count > 4 || info->count < 3) continue; if (info->count == 4 && strcmp(info->strings[3], "authinfo") != 0) continue; if (strcasecmp(info->strings[0], server) != 0) continue; *username = xstrdup(info->strings[1]); *password = xstrdup(info->strings[2]); return true; } return false; } /* ** Send AUTHINFO information to a remote site. Returns true if successful, ** false on failure. Problems sending to the remote site (as opposed to just ** having the remote site reject the authentication) are fatal. */ static bool send_authinfo(struct nntp *nntp, const char *username, const char *password) { enum nntp_status status; enum nntp_code code; char *line; if (!nntp_send_line(nntp, "AUTHINFO USER %s", username)) sysdie("cannot send AUTHINFO USER to remote server"); status = nntp_read_response(nntp, &code, &line); if (status != NNTP_READ_OK) die_nntp_status(status); if (code == NNTP_OK_AUTHINFO) return true; if (code != NNTP_CONT_AUTHINFO) return false; if (!nntp_send_line(nntp, "AUTHINFO PASS %s", password)) sysdie("cannot send AUTHINFO PASS to remote server"); status = nntp_read_response(nntp, &code, &line); if (status != NNTP_READ_OK) die_nntp_status(status); return (code == NNTP_OK_AUTHINFO); } int main(int argc, char *argv[]) { struct nntp *nntp; const char *host = NULL; const char *list = "active"; const char *pattern = NULL; const char *types = NULL; enum nntp_status status; enum nntp_code response; char *line; unsigned short port = NNTP_PORT; bool authinfo = false; bool reader = false; int option; message_program_name = "getlist"; if (!innconf_read(NULL)) exit(1); host = innconf->server; /* Parse options. */ while ((option = getopt(argc, argv, "Ah:p:R")) != EOF) { switch (option) { case 'A': authinfo = true; break; case 'h': host = optarg; break; case 'p': port = atoi(optarg); if (port <= 0) die("%s is not a valid port number", optarg); break; case 'R': reader = true; break; default: die("%s", usage); } } argc -= optind; argv += optind; /* Read optional arguments. */ if (argc > 3) die("too many arguments"); if (argc >= 1) list = argv[0]; if (argc >= 2) pattern = argv[1]; if (argc == 3) types = argv[2]; if (strcasecmp(list, "active") != 0 && types != NULL) die("group types can only be specified with a list type of active"); /* Connect to the server. */ if (host == NULL) sysdie("cannot get server name"); nntp = nntp_connect(host, port, 128 * 1024, DEFAULT_TIMEOUT); if (nntp == NULL) sysdie("cannot connect to server %s:%hu", host, port); status = nntp_read_response(nntp, &response, &line); if (status != NNTP_READ_OK) die_nntp_status(status); if (response < 200 || response > 201) die_nntp_code(response, line); /* Switch to nnrpd if desired. */ if (reader) { if (!nntp_send_line(nntp, "MODE READER")) sysdie("cannot send MODE READER command to server %s", host); status = nntp_read_response(nntp, &response, &line); if (status != NNTP_READ_OK) die_nntp_status(status); } /* Authenticate if desired. */ if (authinfo) { char *username, *password; if (get_authinfo(host, &username, &password)) { if (!send_authinfo(nntp, username, password)) warn("server %s did not accept authentication", host); free(username); free(password); } else warn("no authentication information found for %s", host); } /* Get and display the data. */ send_list(nntp, list, pattern); if (types != NULL) print_active(nntp, types); else print_list(nntp); /* Be polite and say goodbye; it gives the server a chance to shut the connection down cleanly. */ if (nntp_send_line(nntp, "QUIT")) nntp_read_response(nntp, &response, &line); nntp_free(nntp); exit(0); } inn-2.6.0/frontends/Makefile0000644000175200017520000002614712575023702015374 0ustar iuliusiulius## $Id: Makefile 9794 2015-03-17 20:49:15Z iulius $ include ../Makefile.global top = .. CFLAGS = $(GCFLAGS) ALL = bunbatch c7unbatch cnfsheadconf cnfsstat ctlinnd decode encode \ getlist gunbatch inews innconfval mailpost pullnews \ ovdb_init ovdb_monitor ovdb_server ovdb_stat rnews \ scanspool sm MAN = ../doc/man/mailpost.8 SOURCES = ctlinnd.c decode.c encode.c getlist.c inews.c innconfval.c \ ovdb_init.c ovdb_monitor.c ovdb_server.c ovdb_stat.c rnews.c \ sm.c PATHRNEWS = $(PATHBIN)/rnews.libexec all: $(ALL) $(MAN) warnings: $(MAKE) COPT='$(WARNINGS)' all install: all $(LI_INEWS) inews $D$(PATHBIN)/inews $(LI_RNEWS) rnews $D$(PATHBIN)/rnews $(CP_XPRI) cnfsheadconf $D$(PATHBIN)/cnfsheadconf for F in cnfsstat mailpost pullnews scanspool ; do \ $(CP_XPUB) $$F $D$(PATHBIN)/$$F ; \ done for F in ctlinnd ovdb_init ovdb_monitor ovdb_server ovdb_stat ; do \ $(LI_XPRI) $$F $D$(PATHBIN)/$$F ; \ done for F in getlist innconfval sm ; do \ $(LI_XPUB) $$F $D$(PATHBIN)/$$F ; \ done $(CP_XPUB) bunbatch $D$(PATHBIN)/rnews.libexec/bunbatch $(CP_XPUB) c7unbatch $D$(PATHBIN)/rnews.libexec/c7unbatch $(LI_XPUB) decode $D$(PATHBIN)/rnews.libexec/decode $(LI_XPUB) encode $D$(PATHBIN)/rnews.libexec/encode $(CP_XPUB) gunbatch $D$(PATHBIN)/rnews.libexec/gunbatch bootstrap: $(MAN) clean clobber distclean: rm -f *.o $(ALL) rm -rf .libs maintclean: distclean rm -f $(MAN) profiled: $(MAKEPROFILING) all $(FIXSCRIPT): @echo Run configure before running make. See INSTALL for details. @exit 1 ## Compilation rules. BOTH = $(LIBSTORAGE) $(LIBHIST) $(LIBINN) LINK = $(LIBLD) $(LDFLAGS) -o $@ INNLIBS = $(LIBINN) $(LIBS) STORELIBS = $(BOTH) $(STORAGE_LIBS) $(LIBS) FIX = $(FIXSCRIPT) ctlinnd: ctlinnd.o $(LIBINN) ; $(LINK) ctlinnd.o $(INNLIBS) decode: decode.o $(LIBINN) ; $(LINK) decode.o $(INNLIBS) encode: encode.o ; $(LINK) encode.o getlist: getlist.o $(LIBINN) ; $(LINK) getlist.o $(INNLIBS) inews: inews.o $(LIBINN) ; $(LINK) inews.o $(INNLIBS) innconfval: innconfval.o $(LIBINN) ; $(LINK) innconfval.o $(INNLIBS) ovdb_init: ovdb_init.o $(BOTH) ; $(LINK) ovdb_init.o $(STORELIBS) ovdb_monitor: ovdb_monitor.o $(BOTH) ; $(LINK) ovdb_monitor.o $(STORELIBS) ovdb_server: ovdb_server.o $(BOTH) ; $(LINK) ovdb_server.o $(STORELIBS) ovdb_stat: ovdb_stat.o $(BOTH) ; $(LINK) ovdb_stat.o $(STORELIBS) rnews: rnews.o $(BOTH) ; $(LINK) rnews.o $(STORELIBS) sm: sm.o $(BOTH) ; $(LINK) sm.o $(STORELIBS) ovdb_init.o: ovdb_init.c $(CC) $(CFLAGS) $(BDB_CPPFLAGS) -c $< ovdb_monitor.o: ovdb_monitor.c $(CC) $(CFLAGS) $(BDB_CPPFLAGS) -c $< ovdb_server.o: ovdb_server.c $(CC) $(CFLAGS) $(BDB_CPPFLAGS) -c $< ovdb_stat.o: ovdb_stat.c $(CC) $(CFLAGS) $(BDB_CPPFLAGS) -c $< cnfsheadconf: cnfsheadconf.in $(FIX) ; $(FIX) cnfsheadconf.in cnfsstat: cnfsstat.in $(FIX) ; $(FIX) cnfsstat.in mailpost: mailpost.in $(FIX) ; $(FIX) mailpost.in pullnews: pullnews.in $(FIX) ; $(FIX) pullnews.in scanspool: scanspool.in $(FIX) ; $(FIX) scanspool.in bunbatch: Makefile ../Makefile.global ( echo '#! $(SHELL)' ; echo 'exec $(BZIP2) -d -c' ) > $@ chmod 755 bunbatch c7unbatch: Makefile ../Makefile.global ( echo '#! $(SHELL)' ; echo 'decode | $(UNCOMPRESS)' ) > $@ chmod 755 c7unbatch gunbatch: Makefile ../Makefile.global ( echo '#! $(SHELL)' ; echo 'exec $(GZIP) -d -c' ) > $@ chmod 755 gunbatch ## Not normally built. feedone: feedone.o $(LIBINN) ; $(LINK) feedone.o $(INNLIBS) sys2nf: sys2nf.o $(LIBINN) ; $(LINK) sys2nf.o $(INNLIBS) $(LIBINN): ; (cd ../lib ; $(MAKE)) $(LIBSTORAGE): ; (cd ../storage ; $(MAKE)) $(LIBHIST): ; (cd ../history ; $(MAKE)) ../doc/man/mailpost.8: mailpost.in $(POD2MAN) -s 8 -n "MAILPOST" $? > $@ ## Dependencies. Default list, below, is probably good enough. depend: Makefile $(SOURCES) $(MAKEDEPEND) '$(CFLAGS)' $(SOURCES) # DO NOT DELETE THIS LINE -- make depend depends on it. ctlinnd.o: ctlinnd.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/innconf.h \ ../include/inn/messages.h ../include/inn/inndcomm.h \ ../include/inn/libinn.h ../include/inn/xmalloc.h ../include/inn/xwrite.h \ ../include/inn/paths.h decode.o: decode.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/messages.h encode.o: encode.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h getlist.o: getlist.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/innconf.h \ ../include/inn/messages.h ../include/inn/nntp.h ../include/inn/qio.h \ ../include/inn/vector.h ../include/inn/libinn.h ../include/inn/xmalloc.h \ ../include/inn/xwrite.h ../include/inn/paths.h inews.o: inews.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/innconf.h \ ../include/inn/libinn.h ../include/inn/xmalloc.h ../include/inn/xwrite.h \ ../include/inn/messages.h ../include/inn/newsuser.h \ ../include/inn/nntp.h ../include/inn/paths.h innconfval.o: innconfval.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/innconf.h \ ../include/inn/messages.h ../include/inn/version.h \ ../include/inn/libinn.h ../include/inn/xmalloc.h ../include/inn/xwrite.h ovdb_init.o: ovdb_init.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/libinn.h \ ../include/inn/xmalloc.h ../include/inn/xwrite.h \ ../include/inn/innconf.h ../include/inn/messages.h ../include/inn/ov.h \ ../include/inn/history.h ../include/inn/storage.h \ ../include/inn/options.h ../storage/ovdb/ovdb.h \ ../storage/ovdb/ovdb-private.h ovdb_monitor.o: ovdb_monitor.c ../include/config.h \ ../include/inn/defines.h ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/portable/setproctitle.h \ ../include/portable/macros.h ../include/inn/innconf.h \ ../include/inn/messages.h ../include/inn/libinn.h \ ../include/inn/xmalloc.h ../include/inn/xwrite.h ../include/inn/ov.h \ ../include/inn/history.h ../include/inn/storage.h \ ../include/inn/options.h ../storage/ovdb/ovdb.h \ ../storage/ovdb/ovdb-private.h ovdb_server.o: ovdb_server.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/portable/mmap.h \ ../include/portable/setproctitle.h ../include/portable/macros.h \ ../include/portable/socket.h ../include/portable/getaddrinfo.h \ ../include/portable/getnameinfo.h ../include/portable/socket-unix.h \ ../include/inn/fdflag.h ../include/inn/portable-socket.h \ ../include/inn/innconf.h ../include/inn/messages.h \ ../include/inn/libinn.h ../include/inn/xmalloc.h ../include/inn/xwrite.h \ ../include/inn/paths.h ../include/inn/storage.h ../include/inn/options.h \ ../include/inn/ov.h ../include/inn/history.h ../include/inn/storage.h \ ../storage/ovdb/ovdb.h ../storage/ovdb/ovdb-private.h ovdb_stat.o: ovdb_stat.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/innconf.h \ ../include/inn/messages.h ../include/inn/libinn.h \ ../include/inn/xmalloc.h ../include/inn/xwrite.h ../include/inn/ov.h \ ../include/inn/history.h ../include/inn/storage.h \ ../include/inn/options.h ../include/inn/paths.h ../include/inn/storage.h \ ../storage/ovdb/ovdb.h ../storage/ovdb/ovdb-private.h rnews.o: rnews.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/fdflag.h \ ../include/inn/portable-socket.h ../include/inn/portable-getaddrinfo.h \ ../include/inn/portable-getnameinfo.h ../include/inn/innconf.h \ ../include/inn/libinn.h ../include/inn/xmalloc.h ../include/inn/xwrite.h \ ../include/inn/messages.h ../include/inn/newsuser.h \ ../include/inn/nntp.h ../include/inn/paths.h ../include/inn/storage.h \ ../include/inn/options.h ../include/inn/wire.h sm.o: sm.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/buffer.h \ ../include/inn/innconf.h ../include/inn/messages.h ../include/inn/qio.h \ ../include/inn/wire.h ../include/inn/libinn.h ../include/inn/xmalloc.h \ ../include/inn/xwrite.h ../include/inn/storage.h \ ../include/inn/options.h inn-2.6.0/frontends/sm.c0000644000175200017520000002314312575023702014510 0ustar iuliusiulius/* $Id: sm.c 9877 2015-05-24 13:59:15Z iulius $ ** ** Provide a command line interface to the storage manager. */ #include "config.h" #include "clibrary.h" #include #include "inn/buffer.h" #include "inn/innconf.h" #include "inn/messages.h" #include "inn/qio.h" #include "inn/wire.h" #include "inn/libinn.h" #include "inn/storage.h" static const char usage[] = "\ Usage: sm [-cdHiqrRSs] [token ...]\n\ \n\ Command-line interface to the INN storage manager. The default action is\n\ to display the complete article associated with each token given. If no\n\ tokens are specified on the command line, they're read from stdin, one per\n\ line.\n\ \n\ -c Show clear information about a token\n\ -d, -r Delete the articles associated with the given tokens\n\ -H Display the headers of articles only\n\ -i Translate tokens into newsgroup names and article numbers\n\ -q Suppress all error messages except usage\n\ -R Display the raw article rather than undoing wire format\n\ -S Output articles in rnews batch file format\n\ -s Store the article provided on stdin.\n"; /* The options that can be set on the command line, used to determine what to do with each token. */ struct options { bool artinfo; /* Show newsgroup and article number. */ bool clearinfo; /* Show clear information about a token. */ bool delete; /* Delete articles instead of showing them. */ bool header; /* Display article headers only. */ bool raw; /* Show the raw wire-format articles. */ bool rnews; /* Output articles as rnews batch files. */ }; /* ** Given a file descriptor, read a post from that file descriptor and then ** store it. This basically duplicates what INN does, except that INN has to ** do more complex things to add the Path header. ** ** Note that we make no attempt to add history or overview information, at ** least right now. */ static bool store_article(int fd) { struct buffer *article; size_t size; char *text, *start, *end; ARTHANDLE handle = ARTHANDLE_INITIALIZER; TOKEN token; /* Build the basic article handle. */ article = buffer_new(); if (!buffer_read_file(article, fd)) sysdie("cannot read article"); text = wire_from_native(article->data, article->left, &size); handle.type = TOKEN_EMPTY; handle.data = text; handle.iov = xmalloc(sizeof(struct iovec)); handle.iov->iov_base = text; handle.iov->iov_len = size; handle.iovcnt = 1; handle.len = size; handle.arrived = 0; handle.expires = 0; buffer_free(article); /* Find the expiration time, if any. */ start = wire_findheader(text, size, "Expires", true); if (start != NULL) { char *expires; end = wire_endheader(start, text + size - 1); if (end == NULL) die("cannot find end of Expires header"); expires = xstrndup(start, end - start); handle.expires = parsedate_rfc5322_lax(expires); free(expires); if (handle.expires == (time_t) -1) handle.expires = 0; } /* Find the appropriate newsgroups header. */ if (innconf->storeonxref) { start = wire_findheader(text, size, "Xref", true); if (start == NULL) die("no Xref header found in message"); end = wire_endheader(start, text + size - 1); if (end == NULL) die("cannot find end of Xref header"); for (; *start != ' ' && start < end; start++) ; if (start >= end) die("malformed Xref header"); start++; } else { start = wire_findheader(text, size, "Newsgroups", true); if (start == NULL) die("no Newsgroups header found in message"); end = wire_endheader(start, text + size - 1); if (end == NULL) die("cannot find end of Newsgroups header"); } handle.groups = start; handle.groupslen = end - start; /* Store the article. */ token = SMstore(handle); free(text); free(handle.iov); if (token.type == TOKEN_EMPTY) { warn("failed to store article: %s", SMerrorstr); return false; } else { printf("%s\n", TokenToText(token)); return true; } } /* ** Process a single token, performing the operations specified in the given ** options struct. Calls warn and die to display error messages; -q is ** implemented by removing all the warn and die error handlers. */ static bool process_token(const char *id, const struct options *options) { TOKEN token; struct artngnum artinfo; ARTHANDLE *article; size_t length; char *text; if (!IsToken(id)) { warn("%s is not a storage token", id); return false; } token = TextToToken(id); if (options->artinfo) { if (!SMprobe(SMARTNGNUM, &token, &artinfo)) { warn("could not get article information for %s", id); return false; } else { printf("%s: %lu\n", artinfo.groupname, artinfo.artnum); free(artinfo.groupname); } } else if (options->clearinfo) { text = SMexplaintoken(token); printf("%s %s\n", id, text); free(text); } else if (options->delete) { if (!SMcancel(token)) { warn("could not remove %s: %s", id, SMerrorstr); return false; } } else { article = SMretrieve(token, options->header ? RETR_HEAD : RETR_ALL); if (article == NULL) { warn("could not retrieve %s", id); return false; } if (options->raw) { if (fwrite(article->data, article->len, 1, stdout) != 1) die("output failed"); } else { text = wire_to_native(article->data, article->len, &length); if (options->rnews) printf("#! rnews %lu\n", (unsigned long) length); if (fwrite(text, length, 1, stdout) != 1) die("output failed"); free(text); } SMfreearticle(article); } return true; } int main(int argc, char *argv[]) { int option; bool okay, status; struct options options = { false, false, false, false, false, false }; bool store = false; /* Suppress notice messages like tradspool rebuilding its map. */ message_handlers_notice(0); message_program_name = "sm"; if (!innconf_read(NULL)) exit(1); while ((option = getopt(argc, argv, "cdHiqrRSs")) != EOF) { switch (option) { case 'c': options.clearinfo = true; break; case 'd': case 'r': options.delete = true; break; case 'H': options.header = true; break; case 'i': options.artinfo = true; break; case 'q': message_handlers_warn(0); message_handlers_die(0); break; case 'R': options.raw = true; break; case 'S': options.rnews = true; break; case 's': store = true; break; default: fprintf(stderr, usage); exit(1); } } /* Check options for consistency. */ if (options.artinfo && options.delete) die("-i cannot be used with -r or -d"); if (options.artinfo && (options.header || options.raw || options.rnews)) die("-i cannot be used with -H, -R, or -S"); if (options.delete && (options.header || options.rnews)) die("-r or -d cannot be used with -H or -S"); if (options.raw && options.rnews) die("-R cannot be used with -S"); if (options.header && options.rnews) die("-H cannot be used with -S"); if (store && (options.artinfo || options.delete || options.header)) die("-s cannot be used with -i, -r, -d, -H, -R, or -S"); if (store && (options.raw || options.rnews)) die("-s cannot be used with -i, -r, -d, -H, -R, or -S"); if (options.clearinfo && (options.artinfo || options.delete || options.header || options.raw || options.rnews || store)) die("-c cannot be used with -i, -r, -d, -H, -R, -S, or -s"); /* Initialize the storage manager. If we're doing article deletions, we need to open it read/write. */ if (store || options.delete) { bool value = true; if (!SMsetup(SM_RDWR, &value)) die("cannot set up storage manager"); } if (!SMinit()) die("cannot initialize storage manager: %s", SMerrorstr); /* If we're storing an article, do that and then exit. */ if (store) { status = store_article(fileno(stdin)); exit(status ? 0 : 1); } /* Process tokens. If no arguments were given on the command line, process tokens from stdin. Otherwise, walk through the remaining command line arguments. */ okay = true; if (optind == argc) { QIOSTATE *qp; char *line; qp = QIOfdopen(fileno(stdin)); for (line = QIOread(qp); line != NULL; line = QIOread(qp)) { status = process_token(line, &options); okay = okay && status; } if (QIOerror(qp)) { if (QIOtoolong(qp)) die("input line too long"); sysdie("error reading stdin"); } QIOclose(qp); } else { int i; for (i = optind; i < argc; i++) { status = process_token(argv[i], &options); okay = okay && status; } } SMshutdown(); exit(okay ? 0 : 1); } inn-2.6.0/frontends/scanspool.in0000644000175200017520000003105112575023702016253 0ustar iuliusiulius#! /usr/bin/perl -w # fixscript will replace this line with code to load INN::Config use strict; # @(#)scanspool.pl 1.20 4/6/92 00:47:35 # # Written by: Landon Curt Noll (chongo was here /\../\) # # This code is placed in the public domain. # # scanspool - Perform a big scan over all articles in # # usage: # scanspool [-cnv] [-a active-file] [-s spool-dir] # # -a active-file active file to use (default /active) # -s spool-dir spool tree (default ) # -v verbose mode # verbose messages begin with a tab # show articles found in non-active directories # -c check article filenames, don't scan the articles # -n don't throttle innd # # NOTE: This take a while, -v is a good thing if you want to know # how far this program has progressed. # # This program will scan first the active file, noting problems such as: # # malformed line # group aliased to a non-existent group # group aliased to a group that is also aliased # # Then it will examine all articles under your news spool directory, # looking for articles that: # # basename that starts with a leading 0 # basename that is out of range with the active file # does not contain a Newsgroups: line # article that is all header and no text # is in a directory for which there is no active group # article that is in a group to which it does not belong # # Scanspool understands aliased groups. Thus, if an article is posted # to foo.old.name that is aliased to foo.bar, it will be expected to # be found under foo.bar and not foo.old.name. # # Any group that is of type 'j' or 'x' (4th field of the active file) # will be allowed to show up under the junk group. # # Scanspool assumes that the path of a valid newsgroup's directory # from the top of the spool tree will not contain any "." character. # Thus, directories such as out.going, tmp.dir, in.coming and # news.archive will not be searched. This program also assumes that # article basenames contain only decimal digits. Last, files under # the top level directory "lost+found" are not scanned. # # The output of scanspool will start with one of 4 forms: # # FATAL: fatal or internal error (to stderr) # # WARN: active or article format problem, (to stderr) # group alias problem, find error, # article open error # # path/123: basename starts with 0, (to stdout) # article number out of range, # article in the wrong directory, # article in directory not related to # an active non-aliases newsgroup # # \t ... verbose message starting with a tab (to stdout) # Data structures # my %gname2type; # $gname2type{$name} # $name - newsgroup name in foo.dot.form # produces => 4th active field (y, n, x, ...) # alias type is "=", not "=foo.bar" # my %realgname; # $realgname{$name} # $name - newsgroup name in foo.dot.form # produces => newsgroup name in foo.dot.form # if type is =, this will be a.b, not $name # my %lowart; # $lowart{$name} # $name - newsgroup name in foo.dot.form # produces => lowest article allowed in the group # if type is =, this is not valid # my %highart; # $highart{$name} # $name - newsgroup name in foo.dot.form # produces => highest article allowed in the group # if type is =, this is not valid # If $highart{$name} < $lowart{$name}, # then the group should be empty # perl requirements # use Getopt::Std; # global constants # my $prog = $0; # our name my $spool = "$INN::Config::patharticles"; my $active = "$INN::Config::active"; my $ctlinnd = "$INN::Config::pathbin/ctlinnd"; my $reason = "running scanspool"; # throttle reason # parse args # my %opt; getopts("a:s:vcn", \%opt); $active = $opt{'a'} if defined $opt{'a'}; $spool = $opt{'s'} if defined $opt{'s'}; # throttle innd unless -n # if (! defined $opt{'n'}) { system("$ctlinnd throttle '$reason' >/dev/null 2>&1"); } # process the active file # parse_active($active); # check the spool directory # check_spool($spool); # unthrottle innd unless -n # if (! defined $opt{'n'}) { system("$ctlinnd go '$reason' >/dev/null 2>&1"); } # all done exit(0); # parse_active - parse the active file # # From the active file, fill out the %gname2type (type of newsgroup) # and %realgname (real/non-aliased name of group), %lowart & %highart # (low and high article numbers). This routine will also check for # aliases to missing groups or groups that are also aliases. # sub parse_active { my ($active) = @_; # the name of the active file to use my $ACTIVE; # active file handle my $line; # active file line my $name; # name of newsgroup my $low; # low article number my $high; # high article number my $type; # type of newsgroup (4th active field) my $alias; # realname of an aliased group my $linenum; # active file line number # if verbose (-v), say what we are doing print "\tscanning $active\n" if defined $opt{'v'}; # open the active file open ($ACTIVE, '<', $active) || fatal(1, "cannot open $active"); # parse each line $linenum = 0; while ($line = <$ACTIVE>) { # count the line ++$linenum; # verify that we have a correct number of tokens if ($line !~ /^\S+ 0*(\d+) 0*(\d+) \S+$/o) { problem("WARNING: active line is mal-formed at line $linenum"); next; } ($name, $high, $low, $type) = $line =~ /^(\S+) 0*(\d+) 0*(\d+) (\S+)$/o; # watch for duplicate entries if (defined $realgname{$name}) { problem("WARNING: ignoring duplicate group: $name, at active line $linenum"); next; } # record which type it is $gname2type{$name} = $type; # record the low and high article numbers $lowart{$name} = $low; $highart{$name} = $high; # determine the directory and real group name if ($type eq "j" || $type eq "x") { $alias = $name; } elsif ($type =~ /^=(.+)/o) { $alias = $1; $gname2type{$name} = "="; # rename type to be just = } else { $alias = $name; } $realgname{$name} = $alias; } # close the active file close $ACTIVE; # be sure that any alias type is aliased to a real group foreach my $name (keys %realgname) { # skip if not an alias type next if $gname2type{$name} ne "="; # be sure that the alias exists $alias = $realgname{$name}; if (! defined $realgname{$alias}) { problem("WARNING: alias for $name: $alias, is not a group"); next; } # be sure that the alias is not an alias of something else if ($gname2type{$alias} eq "=") { problem("WARNING: alias for $name: $alias, is also an alias"); next; } } return; } # problem - report a problem to stdout # # Print a message to stdout. Parameters are space separated. # A final newline is appended to it. # # usage: # problem(arg, arg2, ...) # sub problem { # print the line with the header and newline my $line = join(" ", @_); print STDERR $line, "\n"; return; } # fatal - report a fatal error to stderr and exit # # Print a message to stderr. The message has the program name prepended # to it. Parameters are space separated. A final newline is appended # to it. This function exits with the code of exitval. # # usage: # fatal(exitval, arg, arg2, ...) # sub fatal { my ($exitval, @args) = @_; # firewall, in case we're called with less than two arguments if ($#_ < 1) { print STDERR "FATAL: fatal() called with only ", $#_+1, " arguments\n"; if ($#_ < 0) { $exitval = -1; } } # print the error message my $line = join(" ", @args); print STDERR "$prog: $line\n"; # unthrottle innd unless -n # if (! defined $opt{'n'}) { system("$ctlinnd go '$reason' >/dev/null 2>&1"); } # exit exit $exitval; } # check_spool - check the articles found in the spool directory # # This subroutine will check all articles found under the $spool directory. # It will examine only file path that do not contain any "." or whitespace # character, and whose basename is completely numeric. Files under # lost+found will also be ignored. # # given: # $spooldir - top of tree # sub check_spool { my ($spooldir) = @_; # top of article tree my $filename; # article pathname under $spool my $artgrp; # group of an article my $artnum; # article number in a group my $prevgrp = ''; # previous different value of $artgrp my $preverrgrp = ''; # previous non-active $artgrp my $ARTICLE; # article handle my $aline; # header line from an article my @group; # array of groups from the Newsgroups: header my $FINDFILE; # find command pipe handle # if verbose, say what we are doing print "\tfinding articles under $spooldir\n" if defined $opt{'v'}; # move to the $spool directory chdir $spooldir or fatal(2, "cannot chdir to $spool"); # start finding files # if (!open $FINDFILE, '-|', "find . \\( -type f -o -type l \\) -name '[0-9]*' -print 2>&1") { fatal(3, "cannot start find in $spool"); } # process each history line # while ($filename = <$FINDFILE>) { # if the line contains find:, assume it is a find error and print it chomp($filename); if ($filename =~ /find:\s/o) { problem("WARNING:", $filename); next; } # remove the ./ that find put in our path $filename =~ s#^\./##o; # skip if this path has a . in it (beyond a leading ./) next if ($filename =~ /\./o); # skip if lost+found next if ($filename =~ m:^lost+found/:o); # skip if not a numeric basename next if ($filename !~ m:/\d+$:o); # get the article's newsgroup name (based on its path from $spool) $artgrp = $filename; $artgrp =~ s#/\d+$##o; $artgrp =~ s#/#.#go; # if verbose (-v), then note if our group changed if (defined $opt{'v'} && $artgrp ne $prevgrp) { print "\t$artgrp\n"; $prevgrp = $artgrp; } # note if the article is not in a directory that is used by # a real (non-aliased) group in the active file # # If we complained about this group before, don't complain again. # If verbose, note files that could be removed. # if (!defined $gname2type{$artgrp} || $gname2type{$artgrp} =~ /[=jx]/o){ if ($preverrgrp ne $artgrp) { problem("$artgrp: not an active group directory"); $preverrgrp = $artgrp; } if (defined $opt{'v'}) { problem("$filename: article found in non-active directory"); } next; } # check on the article number $artnum = $filename; $artnum =~ s#^.+/##o; if ($artnum =~ m/^0/o) { problem("$filename: article basename starts with a 0"); } if (defined $gname2type{$artgrp}) { if ($lowart{$artgrp} > $highart{$artgrp}) { problem("$filename: active indicates group should be empty"); } elsif ($artnum < $lowart{$artgrp}) { problem("$filename: article number is too low"); } elsif ($artnum > $highart{$artgrp}) { problem("$filename: article number is too high"); } } # if check filenames only (-c), then do nothing else with the file next if (defined $opt{'c'}); # don't open a control or junk, they can be from anywhere next if ($artgrp eq "control" || $artgrp eq "junk"); # try open the file if (!open $ARTICLE, '<', $filename) { # the find is now gone (expired?), give up on it problem("WARNING: cannot open $filename"); next; } # read until the Newsgroups: header line is found AREADLINE: while ($aline = <$ARTICLE>) { # catch the Newsgroups: header if ($aline =~ /^Newsgroups:\w*\W/io) { # convert $aline into a comma separated list of groups # remove both CR and LF from the header value (because the article may be # stored in wire-format). $aline =~ s/^Newsgroups://io; $aline =~ tr/ \t\r\n//d; # form an array of news groups @group = split(",", $aline); # see if any groups in the Newsgroups: header are our group for (my $j=0; $j <= $#group; ++$j) { # look at the group if (exists $realgname{$group[$j]} and $realgname{$group[$j]} eq $artgrp) { # this article was posted to this group last AREADLINE; } } # no group or group alias was found problem("$filename: does not belong in $artgrp according to its Newsgroups: header"); last; # else watch for the end of the header } elsif ($aline =~ /^\s*$/o) { # no Newsgroups: header found problem("WARNING: $filename: no Newsgroups: header"); last; } if (eof $ARTICLE) { problem("WARNING: $filename: EOF found while reading header"); } } # close the article close $ARTICLE; } # all done with the find close $FINDFILE; return; } inn-2.6.0/frontends/feedone.c0000644000175200017520000001003512575023702015472 0ustar iuliusiulius/* $Id: feedone.c 8903 2010-01-17 18:21:56Z iulius $ ** ** Connect to the NNTP server and feed one article. */ #include "config.h" #include "clibrary.h" #include #include "inn/libinn.h" #include "inn/messages.h" #include "inn/nntp.h" static FILE *FromServer; static FILE *ToServer; static int Tracing; /* ** Read a line from the server or die trying. */ static void GetFromServer(buff, size, text) char *buff; int size; char *text; { if (fgets(buff, size, FromServer) == NULL) sysdie("s", text); if (Tracing) printf("S: %s", buff); } /* ** Flush a stdio FILE; exit if there are any errors. */ static void SafeFlush(F) FILE *F; { if (fflush(F) == EOF || ferror(F)) sysdie("cannot send text to server"); } static void SendQuit(x) int x; { char buff[BUFSIZ]; /* Close up. */ fprintf(ToServer, "quit\r\n"); SafeFlush(ToServer); fclose(ToServer); GetFromServer(buff, sizeof buff, "cannot get reply to quit"); exit(x); } static void Usage() { fprintf(stderr, "Usage: feedone [-r|-m msgid] [-p] [-t] articlefile\n"); exit(1); } int main(ac, av) int ac; char *av[]; { static char MESGIDHDR[] = "Message-ID:"; int i; FILE *F; char buff[BUFSIZ]; char *mesgid = NULL; char *p; char *q; bool PostMode; /* Set defaults. */ mesgid[0] = '\0'; PostMode = false; message_program_name = "feedone"; /* Parse JCL. */ while ((i = getopt(ac, av, "m:prt")) != EOF) switch (i) { default: Usage(); /* NOTREACHED */ case 'm': /* Specified Message-ID */ if (*optarg == '<') mesgid = optarg; else mesgid = concat("<", optarg, ">", (char *) 0); break; case 'p': /* Use Post, not ihave */ PostMode = true; break; case 'r': /* Random Message-ID */ xasprintf(&mesgid, "<%ld@%ld>", (long) getpid(), (long) time(NULL)); break; case 't': Tracing = true; break; } ac -= optind; av += optind; /* One argument; the input filename. */ if (ac != 1) Usage(); if ((F = fopen(av[0], "r")) == NULL) sysdie("cannot open input"); /* Scan for the message-id. */ if (mesgid == NULL) { while (fgets(buff, sizeof buff, F) != NULL) if (strncmp(buff, MESGIDHDR, strlen(MESGIDHDR)) == 0) { if ((p = strchr(buff, '<')) == NULL || (q = strchr(p, '>')) == NULL) die("bad message ID line"); q[1] = '\0'; mesgid = xstrdup(p); break; } if (mesgid == NULL) die("no message ID"); } /* Connect to the server. */ if (NNTPremoteopen(NNTP_PORT, &FromServer, &ToServer, buff, sizeof(buff)) < 0 || FromServer == NULL || ToServer == NULL) { if (buff[0]) warn("server says: %s", buff); sysdie("cannot connect to server"); } /* Does the server want this article? */ if (PostMode) { fprintf(ToServer, "post\r\n"); i = NNTP_CONT_POST; } else { fprintf(ToServer, "ihave %s\r\n", mesgid); i = NNTP_CONT_IHAVE; } SafeFlush(ToServer); GetFromServer(buff, sizeof buff, "cannot offer article to server"); if (atoi(buff) != i) { warn("server doesn't want the article: %s", buff); SendQuit(1); } /* Send the file over. */ fseeko(F, 0, SEEK_SET); while (fgets(buff, sizeof buff, F) != NULL) { if (strncmp(buff, MESGIDHDR, strlen(MESGIDHDR)) == 0) { fprintf(ToServer, "%s %s\r\n", MESGIDHDR, mesgid); continue; } if ((p = strchr(buff, '\n')) != NULL) *p = '\0'; fprintf(ToServer, buff[0] == '.' ? ".%s\r\n" : "%s\r\n", buff); SafeFlush(ToServer); } fprintf(ToServer, ".\r\n"); SafeFlush(ToServer); fclose(F); /* How did the server respond? */ GetFromServer(buff, sizeof buff, "no reply from server after sending the article"); i = PostMode ? NNTP_OK_POST : NNTP_OK_IHAVE; if (atoi(buff) != i) sysdie("cannot send article to the server: %s", buff); SendQuit(0); /* NOTREACHED */ } inn-2.6.0/frontends/cnfsstat.in0000644000175200017520000004066612575023702016113 0ustar iuliusiulius#! /usr/bin/perl -w # fixscript will replace this line with code to load INN::Config # $Id: cnfsstat.in 9927 2015-08-08 17:18:23Z iulius $ # # Copyright Andreas Lamrecht 1998 # # # Modified by Kjetil T. Homme 1998, 2000 # # # Modified by Robert R. Collier 1998 # # # bigint support added by Duane Currie (sandman@hub.org) 1998 use strict; use Getopt::Long; use Math::BigInt; use Math::BigFloat; use English; my $conffile = "$INN::Config::pathetc/cycbuff.conf"; my $storageconf = "$INN::Config::pathetc/storage.conf"; my $lastconftime = 0; sub usage { print <<"_end_"; Summary tool for CNFS Usage: $0 [-ahpPsv] [-c class] [-i seconds] [-l [seconds]] [-m buffer] If called without args, does a one-time status of all CNFS buffers -a: print the age of the oldest article in the cycbuff -c class: print out status of CNFS buffers in that class -h: this information -i seconds: initial sleep of that many seconds at startup -l [seconds]: loop like vmstat, default seconds = 600 -m buffer: print out information suitable for MRTG -p: print out an MRTG config file -P: write PID into $INN::Config::pathrun/cnfsstat.pid -s: log through syslog -v: do consistency checks and print the result _end_ exit(1); } my (%class, %buff, %stor, @storsort); my (%prevclass, %prevbuff, %prevstor, @prevstorsort); my @buffers; my ($oclass, $obuffer); my %opt = (c=>\$oclass, m=>\$obuffer); Getopt::Long::config('no_ignore_case'); GetOptions(\%opt, "-a", "-c=s", "-h", "-i=i", "-l:i", "-m=s", "-p", "-P", "-s", "-v"); usage() if $opt{'h'}; my $use_syslog = 0; if ($opt{'s'}) { eval { require Sys::Syslog; import Sys::Syslog; $use_syslog = 1 }; if ($use_syslog) { if ($Sys::Syslog::VERSION < 0.15) { eval "sub Sys::Syslog::_PATH_LOG { '/dev/log' }" if $^O eq 'dec_osf'; Sys::Syslog::setlogsock('unix') if $^O =~ /linux|dec_osf|freebsd|darwin/; } openlog('cnfsstat', 'pid', $INN::Config::syslog_facility); } else { print STDERR "Syslog is not available. -s option is ignored.\n"; } } if ($opt{'P'}) { if (open my $FILE, '>', "$INN::Config::pathrun/cnfsstat.pid") { print $FILE "$$\n"; close $FILE; }; } my $sleeptime = (defined($opt{'l'}) && $opt{'l'} > 0) ? $opt{'l'} : 600; unless (read_cycbuffconf()) { print STDERR "Invalid $conffile file.\n"; exit (1); } unless (read_storageconf()) { print STDERR "Invalid $storageconf file.\n"; exit (1); } mrtg($obuffer) if $obuffer; mrtg_config() if $opt{'p'}; # Initial sleep, before starting the work. if(defined($opt{'i'}) && $opt{'i'} > 0) { sleep($opt{'i'}); if (!$use_syslog) { print STDOUT "$opt{'i'} seconds later:\n"; } } START: # Check whether the configuration files need reloading. my $cycbufftime = (stat($conffile))[9] if (-r $conffile); my $storagetime = (stat($storageconf))[9] if (-r $storageconf); my $maxtime = ($cycbufftime < $storagetime) ? $storagetime : $cycbufftime; # Set $lastconftime for the first run of the comparison. $lastconftime = $maxtime if not $lastconftime; if ($lastconftime < $maxtime) { my $reloadok = 1; $lastconftime = $maxtime; # Save the previous configuration, in case reloading it fails. # Direct copies of the arrays and hashes works fine here. %prevclass = %class; undef %class; %prevbuff = %buff; undef %buff; %prevstor = %stor; undef %stor; @prevstorsort = @storsort; undef @storsort; unless (read_cycbuffconf()) { print STDERR "Invalid $conffile file.\n"; $reloadok = 0; } unless (read_storageconf()) { print STDERR "Invalid $storageconf file.\n"; $reloadok = 0; } # In case reloading configuration files fails, restore the # previous known configuration for this run of cnfsstat. if (!$reloadok) { %class = %prevclass; %buff = %prevbuff; %stor = %prevstor; @storsort = @prevstorsort; } } my $logline; my $header_printed = 0; my ($gr, $cl, $min, $max); if ($oclass) { if ($class{$oclass}) { if (!$header_printed) { if ($stor{$oclass}) { ($gr, $cl, $min, $max) = split(/:/, $stor{$oclass}); } else { ($gr, $cl, $min, $max) = ('', $oclass, 0, 0); } # Remove leading and trailing double quotes, if present. $gr =~ s/"?([^"]*)"?/$1/g; if ($use_syslog) { if ($min || $max) { $logline = sprintf("Class %s for groups matching \"%s\" article size min/max: %d/%d", $oclass, $gr, $min, $max); } else { $logline = sprintf("Class %s for groups matching \"%s\"", $oclass, $gr); } } else { print STDOUT "Class $oclass"; print STDOUT " for groups matching \"$gr\""; if ($min || $max) { print STDOUT ", article size min/max: $min/$max"; } print STDOUT "\n"; } $header_printed = 1; } @buffers = split(/,/, $class{$oclass}); if (! @buffers) { print STDERR "No buffers in Class $oclass ...\n"; next; } foreach my $b (@buffers) { if (! $buff{$b} ) { print STDERR "No buffer definition for buffer $b ...\n"; next; } print_cycbuff_head($buff{$b}); } } else { print STDERR "Class $oclass not found ...\n"; } } else { # Print all Classes foreach my $c (@storsort) { ($gr, $cl, $min, $max) = split(/:/, $stor{$c}); # Remove leading and trailing double quotes, if present. $gr =~ s/"?([^"]*)"?/$1/g; if ($use_syslog) { if ($min || $max) { $logline = sprintf("Class %s for groups matching \"%s\" article size min/max: %d/%d", $c, $gr, $min, $max); } else { $logline = sprintf("Class %s for groups matching \"%s\"", $c, $gr); } } else { print STDOUT "Class $c"; print STDOUT " for groups matching \"$gr\""; if($min || $max) { print STDOUT ", article size min/max: $min/$max"; } print STDOUT "\n"; } @buffers = split(/,/, $class{$c}); if(! @buffers) { print STDERR "No buffers in Class $c ...\n"; next; } foreach my $b (@buffers) { if(! $buff{$b} ) { print STDERR "No buffer definition for buffer $b ...\n"; next; } print_cycbuff_head($buff{$b}); } if (!$use_syslog) { print STDOUT "\n"; } } } if(defined($opt{'l'})) { sleep($sleeptime); if (!$use_syslog) { print STDOUT "$sleeptime seconds later:\n"; } goto START; } sub read_cycbuffconf { my @line; return 0 unless open my $CONFFILE, '<', $conffile; while(<$CONFFILE>) { $_ =~ s/^\s*(.*?)\s*$/$1/; # Here we handle continuation lines while (m/\\$/) { my $contline = <$CONFFILE>; $contline =~ s/^\s*(.*?)\s*$/$1/; chop; $_ .= $contline; } # \x23 below is #. Emacs perl-mode gets confused by the "comment" next if ($_ =~ /^\s*$/ || $_ =~ /^\x23/); next if ($_ =~ /^cycbuffupdate:/ || $_ =~ /^refreshinterval:/); if($_ =~ /^metacycbuff:/) { @line = split(/:/, $_); if ($class{$line[1]}) { print STDERR "Class $line[1] more than one time in CycBuff Conffile $conffile ...\n"; return 0; } $class{$line[1]} = $line[2]; next; } if ($_ =~ /^cycbuff/) { @line = split(/:/, $_); if ($buff{$line[1]}) { print STDERR "Buff $line[1] more than one time in CycBuff Conffile $conffile ...\n"; return 0; } $buff{$line[1]} = $line[2]; next; } print STDERR "Unknown config line \"$_\" in CycBuff Conffile $conffile ...\n"; return 0; } close $CONFFILE; return 1; } sub read_storageconf { my $line = 0; return 0 unless open my $STOR, '<', $storageconf; while (<$STOR>) { ++$line; next if /^\s*#/; # defaults my %key = ("NEWSGROUPS" => "*", "SIZE" => "0,0"); if (/method\s+cnfs\s+\{/) { while (<$STOR>) { ++$line; next if /^\s*#/; last if /\}/; if (/(\w+):\s+(\S+)/i) { $key{uc($1)} = $2; } } unless (defined $key{'CLASS'} && defined $key{'OPTIONS'}) { print STDERR "storage.conf:$line: ". "Missing 'class' or 'options'\n"; return 0; } $key{'SIZE'} .= ",0" unless $key{'SIZE'} =~ /,/; $key{'SIZE'} =~ s/,/:/; if (!defined $stor{$key{'OPTIONS'}}) { $stor{$key{'OPTIONS'}} = "$key{'NEWSGROUPS'}:$key{'CLASS'}:" . "$key{'SIZE'}:$key{'OPTIONS'}"; push(@storsort, $key{'OPTIONS'}); } } } return 1; } sub print_cycbuff_head { my ($buffpath) = @_; my ($name, $len, $free, $update, $cyclenum, $oldart) = get_cycbuff_info($buffpath); if ($use_syslog) { ($name) = split(/\s/, $name); $name =~ s/\0//g; # Log only if the buffer is initialized (cyclenum is not -1). syslog ('notice', '%s Buffer %s, len: %.2f Mbytes, used: %.2f Mbytes (%4.1f%%) %3d cycles', $logline, $name, Math::BigFloat->new($len) / (1024 * 1024), Math::BigFloat->new($free) / (1024 * 1024), 100 * Math::BigFloat->new($free) / Math::BigFloat->new($len), $cyclenum) if $cyclenum >= 0; return 0; } $name =~ s/\0//g; print " Buffer $name, size: ", human_readable($len, 4); print ", position: ", human_readable($free, 4); printf " %.2f cycles\n", $cyclenum + Math::BigFloat->new($free) / Math::BigFloat->new($len); # The CNFS buffer may not have been initialized yet or received an article. # Take it into account because $oldart may be undefined. my ($when, $ago) = make_time($update); if (defined $oldart or not $opt{'a'}) { print " Newest: $when, $ago ago\n"; } else { print " Created: $when, $ago ago\n"; } if ($opt{'a'}) { if (defined $oldart) { my ($when, $ago) = make_time($oldart); print " Oldest: $when, $ago ago\n"; } else { print " No oldest article\n"; } } return; } sub make_time { my ($t) = @_; my (@ret); my ($sec,$min,$hour,$mday,$mon,$year) = (localtime($t))[0..5]; push (@ret, sprintf("%04d-%02d-%02d %2d:%02d:%02d", $year + 1900, $mon + 1, $mday, $hour, $min, $sec)); $t = time - $t; $mday = int($t/86400); $t = $t % 86400; $hour = int($t/3600); $t = $t % 3600; $min = int($t/60); $t = $t % 60; push (@ret, sprintf("%4d days, %2d:%02d:%02d", $mday, $hour, $min, $t)); return @ret; } sub human_readable { my ($val, $digits) = @_; $val =~ s/\+//; my @name = ("kBytes", "MBytes", "GBytes", "TBytes"); my $base = 1024; my $factor = 1024; my $unit = -1; my $oldscaled = Math::BigFloat->new($val) / $base; my $scaled = $oldscaled; while ( ( int($scaled) > 0 ) && ( $unit < $#name ) ) { $oldscaled = $scaled; $scaled /= $factor; $unit++; } $scaled = $oldscaled; my $predigits = length (int($scaled)); my $postdigits = $digits - $predigits - 1; $postdigits = 0 if $postdigits < 0; ++$digits; return sprintf ("%${digits}.${postdigits}f %s", $scaled, $name[$unit]); } sub mrtg { my $buffer = shift; # print "Buffer = $buff{$buffer}\n"; my @info = get_cycbuff_info($buff{$buffer}); print "$info[1]\n"; print "$info[2]\n"; print "$info[4]\n"; print "$info[0]\n"; exit(0); } sub mrtg_config { print "Sub MRTG-CONFIG\n"; foreach my $class (sort(keys(%class))) { print "##\n## Class : $class\n## Wildmat: $stor{$class}\n##\n\n"; foreach my $buffer (split /\,/, $class{$class}) { mrtg_buffer($class, $buffer); } } exit(0); } sub mrtg_buffer { my ($class,$buffer) = @_; #my ($name, $num, $buff, $size) = @_; my $tag = 'cnfs-' . $buffer; print 'Target[', $tag, ']: `', "$INN::Config::pathbin/cnfsstat -m ", $buffer, '`', "\n"; print 'MaxBytes[', $tag, ']: ', (get_cycbuff_info($buff{$buffer}))[1], "\n"; print 'Title[', $tag, ']: ', "${buffer} Usage\n"; print 'Options[', $tag, ']: growright gauge', "\n"; print 'YLegend[', $tag, ']: ', "${buffer}\n"; print 'ShortLegend[', $tag, ']: MB', "\n"; print 'PageTop[', $tag, ']: ', "

Usage of ${buffer}

\n"; print "
$stor{$class}\n"; print "\n"; return 1; } sub bigsysseek { my ($handle, $offset) = @_; # $offset may be a bigint; and have a value that doesn't fit in a signed long. # Even with largefiles enabled, perl will still truncate the argument to lseek64 # to 32 bits. So we seek multiple times, <2G at a time. if ($offset > 2147483647) { # Since perl truncates the return value of lseek64 to 32 bits, it might # see a successful return value as negative, and return FALSE (undef). # So we must ignore the return value of sysseek and assume that it worked. seek($handle, 0, 0); while ($offset > 2000000000) { sysseek($handle, 2000000000, 1) || return 0; $offset -= 2000000000; } sysseek($handle, $offset, 1) || return 0; return 1; } else { return sysseek($handle, $offset, 0); } } sub check_read_return { my $result = shift; die "read: $!\n" unless defined($result); die "read reached eof\n" unless $result; return $result; } sub get_cycbuff_info { my ($buffpath) = @_; my $oldart; my $CNFSMASIZ = 8; my $CNFSNASIZ = 16; my $CNFSPASIZ = 64; my $CNFSLASIZ = 16; my $headerlength = 2 * $CNFSMASIZ + 2 * $CNFSNASIZ + $CNFSPASIZ + (6 * $CNFSLASIZ); my ($BUFF, $buff); if ( !open $BUFF, '<', $buffpath ) { print STDERR "Cannot open Cycbuff $buffpath ...\n"; exit(1); } $buff = ""; if ( !read $BUFF, $buff, $headerlength ) { print STDERR "Cannot read $headerlength bytes from file $buffpath...\n"; exit(1); } my ($magic, $name, $path, $lena, $freea, $updatea, $cyclenuma, $metaname, $orderinmeta, $currentbuff, $blksza) = unpack("a8 a16 a64 a16 a16 a16 a16 a16 a16 a8 a16", $buff); if (!$magic) { print STDERR "Error while unpacking header ...\n"; exit(1); } my $len = bhex($lena); my $free = bhex($freea); my $update = hex($updatea); my $cyclenum = hex($cyclenuma) - 1; my $blksz = ($magic =~ m/^CBuf4/) ? hex($blksza) : 512; if ($opt{'a'}) { my $pagesize = 16384; my $minartoffset = int($len / ($blksz * 8)) + 512; # Align upwards: $minartoffset = ($minartoffset + $pagesize) & ~($pagesize - 1); if ($cyclenum == 0 && $free == $minartoffset) { # The cycbuff has no articles yet. goto done; } # Don't loop endlessly, set rough upper bound my $sentinel = $cyclenum == 0 ? $free : $len; my $offset = $cyclenum == 0 ? $minartoffset : $free + $pagesize; bigsysseek($BUFF, $offset) || die "sysseek: $!\n"; check_read_return (sysread ($BUFF, $buff, $pagesize)); do { my $chunk; check_read_return (sysread ($BUFF, $chunk, $pagesize)); $buff .= $chunk; while ($buff =~ /^message-id:\s+(<.*?>)/mi) { $buff = $POSTMATCH; $oldart = lookup_age($1); next unless $oldart; # Is the article newer than the last update of the cycbuff? if ($oldart >= $update) { $update = $oldart; } elsif ($oldart < $update - 60) { goto done; } } # Just in case we chopped Message-ID in two, use the end # at the front in next iteration. $buff = substr ($buff, -$blksz); } while ($sentinel -= $pagesize > 0); } done: close $BUFF; return($name,$len,$free,$update,$cyclenum,$oldart); } sub lookup_age { my ($msgid) = @_; my $history = safe_run("$INN::Config::newsbin/grephistory", "-l", $msgid); if ($history =~ /\t(\d+)~/) { return $1; } if ($opt{'v'}) { print " (Missing $msgid)\n"; } return 0; } sub safe_run { my $output = ""; my $pid = open my $KID_TO_READ, "-|"; die "fork: $!\n" unless defined $pid; if ($pid) { while (<$KID_TO_READ>) { $output .= $_; } close $KID_TO_READ; } else { exec(@_) || die "can't exec $_[0]: $!"; # NOTREACHED } return $output; } # Hex to bigint conversion routine. # bhex(HEXSTRING) returns BIGINT (with leading + chopped off). # # In most languages, unlimited size integers are done using string math # libraries usually called bigint. (Java, Perl, etc.) # # Bigint's are really just strings. sub bhex { my $hexValue = shift; $hexValue =~ s/^0x//; my $integerValue = Math::BigInt->new('0'); for (my $i = 0; $i < length($hexValue); $i += 2) { # Could be more efficient going at larger increments, but byte # by byte is safer for the case of 9 byte values, 11 bytes, etc. my $byte = substr($hexValue, $i, 2); my $byteIntValue = hex($byte); # bmuladd() is only in Perl >= 5.10.0. $integerValue->bmul('256'); $integerValue->badd("$byteIntValue"); } my $result = $integerValue->bstr(); $result =~ s/^\+//; return $result; } inn-2.6.0/frontends/ovdb_server.c0000644000175200017520000003761612575023702016423 0ustar iuliusiulius/* * ovdb_server.c * ovdb read server */ #include "config.h" #include "clibrary.h" #include "portable/mmap.h" #include "portable/setproctitle.h" #include "portable/socket.h" #include #include #include #ifdef HAVE_SYS_SELECT_H # include #endif #include #ifdef HAVE_SYS_TIME_H # include #endif #include #ifdef HAVE_UNIX_DOMAIN_SOCKETS # include "portable/socket-unix.h" #endif #include #include "inn/fdflag.h" #include "inn/innconf.h" #include "inn/messages.h" #include "inn/libinn.h" #include "inn/paths.h" #include "inn/storage.h" #include "inn/ov.h" #include "../storage/ovdb/ovdb.h" #include "../storage/ovdb/ovdb-private.h" #ifndef HAVE_BDB int main(int argc UNUSED, char **argv UNUSED) { die("Berkeley DB support not compiled"); } #else /* HAVE_BDB */ #define SELECT_TIMEOUT 15 /* This will work unless user sets a larger clienttimeout in readers.conf */ #define CLIENT_TIMEOUT (innconf->clienttimeout + 60) /*#define CLIENT_TIMEOUT 3600*/ static int listensock; #define MODE_READ 0 #define MODE_WRITE 1 #define MODE_CLOSED 2 #define STATE_READCMD 0 #define STATE_READGROUP 1 struct reader { int fd; int mode; int state; int buflen; int bufpos; void *buf; time_t lastactive; void *currentsearch; }; static struct reader *readertab; static int readertablen; static int numreaders; static time_t now; static pid_t parent; struct child { pid_t pid; int num; time_t started; }; static struct child *children; #define wholistens (children[ovdb_conf.numrsprocs].num) static int signalled = 0; static void sigfunc(int sig UNUSED) { signalled = 1; } static int updated = 0; static void childsig(int sig UNUSED) { updated = 1; } static void parentsig(int sig UNUSED) { int i, which, smallest; if(wholistens < 0) { which = smallest = -1; for(i = 0; i < ovdb_conf.numrsprocs; i++) { if(children[i].pid == -1) continue; if(!ovdb_conf.maxrsconn || children[i].num <= ovdb_conf.maxrsconn) { if(smallest == -1 || children[i].num < smallest) { smallest = children[i].num; which = i; } } } if(which != -1) { wholistens = which; kill(children[which].pid, SIGUSR1); } else { wholistens = -2; } updated = 1; } } static int putpid(const char *path) { char buf[30]; int fd = open(path, O_WRONLY|O_TRUNC|O_CREAT, 0664); if(fd == -1) { syswarn("cannot open %s", path); return -1; } snprintf(buf, sizeof(buf), "%d\n", getpid()); if(write(fd, buf, strlen(buf)) < 0) { syswarn("cannot write to %s", path); close(fd); return -1; } close(fd); return 0; } static void do_groupstats(struct reader *r) { struct rs_groupstats *reply; char *group = (char *)(r->buf) + sizeof(struct rs_cmd); reply = xmalloc(sizeof(struct rs_groupstats)); debug("OVDB: rs: do_groupstats '%s'", group); if(ovdb_groupstats(group, &reply->lo, &reply->hi, &reply->count, &reply->flag)) { reply->status = CMD_GROUPSTATS; reply->aliaslen = 0; } else { reply->status = CMD_GROUPSTATS | RPLY_ERROR; } free(r->buf); r->buf = reply; r->buflen = sizeof(struct rs_groupstats); r->bufpos = 0; r->mode = MODE_WRITE; } static void do_opensrch(struct reader *r) { struct rs_cmd *cmd = r->buf; struct rs_opensrch *reply; char *group = (char *)(r->buf) + sizeof(struct rs_cmd); reply = xmalloc(sizeof(struct rs_opensrch)); debug("OVDB: rs: do_opensrch '%s' %d %d", group, cmd->artlo, cmd->arthi); if(r->currentsearch != NULL) { /* can only open one search at a time */ reply->status = CMD_OPENSRCH | RPLY_ERROR; } else { reply->handle = ovdb_opensearch(group, cmd->artlo, cmd->arthi); if(reply->handle == NULL) { reply->status = CMD_OPENSRCH | RPLY_ERROR; } else { reply->status = CMD_OPENSRCH; } r->currentsearch = reply->handle; } free(r->buf); r->buf = reply; r->buflen = sizeof(struct rs_opensrch); r->bufpos = 0; r->mode = MODE_WRITE; } static void do_srch(struct reader *r) { struct rs_cmd *cmd = r->buf; struct rs_srch *reply; ARTNUM artnum; TOKEN token; time_t arrived; int len; char *data; if(ovdb_search(cmd->handle, &artnum, &data, &len, &token, &arrived)) { reply = xmalloc(sizeof(struct rs_srch) + len); reply->status = CMD_SRCH; reply->artnum = artnum; reply->token = token; reply->arrived = arrived; reply->len = len; memcpy((char *)reply + sizeof(struct rs_srch), data, len); r->buflen = sizeof(struct rs_srch) + len; } else { reply = xmalloc(sizeof(struct rs_srch)); reply->status = CMD_SRCH | RPLY_ERROR; r->buflen = sizeof(struct rs_srch); } free(r->buf); r->buf = reply; r->bufpos = 0; r->mode = MODE_WRITE; } static void do_closesrch(struct reader *r) { struct rs_cmd *cmd = r->buf; ovdb_closesearch(cmd->handle); free(r->buf); r->buf = NULL; r->bufpos = r->buflen = 0; r->mode = MODE_READ; r->currentsearch = NULL; } static void do_artinfo(struct reader *r) { struct rs_cmd *cmd = r->buf; struct rs_artinfo *reply; char *group = (char *)(r->buf) + sizeof(struct rs_cmd); TOKEN token; debug("OVDB: rs: do_artinfo: '%s' %d", group, cmd->artlo); if(ovdb_getartinfo(group, cmd->artlo, &token)) { reply = xmalloc(sizeof(struct rs_artinfo)); reply->status = CMD_ARTINFO; reply->token = token; r->buflen = sizeof(struct rs_artinfo); } else { reply = xmalloc(sizeof(struct rs_artinfo)); reply->status = CMD_ARTINFO | RPLY_ERROR; r->buflen = sizeof(struct rs_artinfo); } free(r->buf); r->buf = reply; r->bufpos = 0; r->mode = MODE_WRITE; } static int process_cmd(struct reader *r) { struct rs_cmd *cmd = r->buf; if(r->state == STATE_READCMD) { switch(cmd->what) { case CMD_GROUPSTATS: case CMD_OPENSRCH: case CMD_ARTINFO: r->state = STATE_READGROUP; if(cmd->grouplen == 0) { /* shoudn't happen... */ r->mode = MODE_CLOSED; close(r->fd); free(r->buf); r->buf = NULL; return 0; } r->buflen += cmd->grouplen; r->buf = xrealloc(r->buf, r->buflen); return 1; } } switch(cmd->what) { case CMD_GROUPSTATS: ((char *)r->buf)[r->buflen - 1] = 0; /* make sure group is null-terminated */ do_groupstats(r); break; case CMD_OPENSRCH: ((char *)r->buf)[r->buflen - 1] = 0; do_opensrch(r); break; case CMD_SRCH: do_srch(r); break; case CMD_CLOSESRCH: do_closesrch(r); break; case CMD_ARTINFO: ((char *)r->buf)[r->buflen - 1] = 0; do_artinfo(r); break; default: r->mode = MODE_CLOSED; close(r->fd); free(r->buf); r->buf = NULL; break; } return 0; } static void handle_read(struct reader *r) { int n; r->lastactive = now; if(r->buf == NULL) { r->state = STATE_READCMD; r->buf = xmalloc(sizeof(struct rs_cmd)); r->buflen = sizeof(struct rs_cmd); r->bufpos = 0; } again: n = read(r->fd, (char *)(r->buf) + r->bufpos, r->buflen - r->bufpos); if(n <= 0) { if(n < 0 && (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK)) return; r->mode = MODE_CLOSED; close(r->fd); free(r->buf); r->buf = NULL; } r->bufpos += n; if(r->bufpos >= r->buflen) if(process_cmd(r)) goto again; } static void handle_write(struct reader *r) { int n; r->lastactive = now; if(r->buf == NULL) /* shouldn't happen */ return; n = write(r->fd, (char *)(r->buf) + r->bufpos, r->buflen - r->bufpos); if(n <= 0) { if(n < 0 && (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK)) return; r->mode = MODE_CLOSED; close(r->fd); free(r->buf); r->buf = NULL; } r->bufpos += n; if(r->bufpos >= r->buflen) { free(r->buf); r->buf = NULL; r->bufpos = r->buflen = 0; r->mode = MODE_READ; } } static void newclient(int fd) { struct reader *r; int i; fdflag_nonblocking(fd, 1); if(numreaders >= readertablen) { readertablen += 50; readertab = xrealloc(readertab, readertablen * sizeof(struct reader)); for(i = numreaders; i < readertablen; i++) { readertab[i].mode = MODE_CLOSED; readertab[i].buf = NULL; } } r = &(readertab[numreaders]); numreaders++; r->fd = fd; r->mode = MODE_WRITE; r->buflen = sizeof(OVDB_SERVER_BANNER); r->bufpos = 0; r->buf = xstrdup(OVDB_SERVER_BANNER); r->lastactive = now; r->currentsearch = NULL; handle_write(r); } static void delclient(int which) { int i; struct reader *r = &(readertab[which]); if(r->mode != MODE_CLOSED) close(r->fd); if(r->buf != NULL) { free(r->buf); } if(r->currentsearch != NULL) { ovdb_closesearch(r->currentsearch); r->currentsearch = NULL; } /* numreaders will get decremented by the calling function */ for(i = which; i < numreaders-1; i++) readertab[i] = readertab[i+1]; readertab[i].mode = MODE_CLOSED; readertab[i].buf = NULL; } static pid_t serverproc(int me) { fd_set rdset, wrset; int i, ret, count, lastfd, lastnumreaders; socklen_t salen; struct sockaddr_in sa; struct timeval tv; pid_t pid; pid = fork(); if (pid != 0) return pid; if (!ovdb_open(OV_READ|OVDB_SERVER)) die("cannot open overview"); xsignal_norestart(SIGINT, sigfunc); xsignal_norestart(SIGTERM, sigfunc); xsignal_norestart(SIGHUP, sigfunc); xsignal_norestart(SIGUSR1, childsig); xsignal(SIGPIPE, SIG_IGN); numreaders = lastnumreaders = 0; if(ovdb_conf.maxrsconn) { readertablen = ovdb_conf.maxrsconn; } else { readertablen = 50; } readertab = xmalloc(readertablen * sizeof(struct reader)); for(i = 0; i < readertablen; i++) { readertab[i].mode = MODE_CLOSED; readertab[i].buf = NULL; } setproctitle("0 clients"); /* main loop */ while(!signalled) { FD_ZERO(&rdset); FD_ZERO(&wrset); lastfd = 0; if(wholistens == me) { if(!ovdb_conf.maxrsconn || numreaders < ovdb_conf.maxrsconn) { FD_SET(listensock, &rdset); lastfd = listensock; setproctitle("%d client%s *", numreaders, numreaders == 1 ? "" : "s"); } else { wholistens = -1; kill(parent, SIGUSR1); } } for(i = 0; i < numreaders; i++) { switch(readertab[i].mode) { case MODE_READ: FD_SET(readertab[i].fd, &rdset); break; case MODE_WRITE: FD_SET(readertab[i].fd, &wrset); break; default: continue; } if(readertab[i].fd > lastfd) lastfd = readertab[i].fd; } tv.tv_usec = 0; tv.tv_sec = SELECT_TIMEOUT; count = select(lastfd + 1, &rdset, &wrset, NULL, &tv); if(signalled) break; if(count <= 0) continue; now = time(NULL); if(FD_ISSET(listensock, &rdset)) { if(!ovdb_conf.maxrsconn || numreaders < ovdb_conf.maxrsconn) { salen = sizeof(sa); ret = accept(listensock, (struct sockaddr *)&sa, &salen); if(ret >= 0) { newclient(ret); wholistens = -1; children[me].num = numreaders; kill(parent, SIGUSR1); } } } for(i = 0; i < numreaders; i++) { switch(readertab[i].mode) { case MODE_READ: if(FD_ISSET(readertab[i].fd, &rdset)) handle_read(&(readertab[i])); break; case MODE_WRITE: if(FD_ISSET(readertab[i].fd, &wrset)) handle_write(&(readertab[i])); break; } } for(i = 0; i < numreaders; i++) { if(readertab[i].mode == MODE_CLOSED || (time_t) (readertab[i].lastactive + CLIENT_TIMEOUT) < now) { delclient(i); numreaders--; i--; } } if(children[me].num != numreaders) { children[me].num = numreaders; kill(parent, SIGUSR1); } if(numreaders != lastnumreaders) { lastnumreaders = numreaders; setproctitle("%d client%s", numreaders, numreaders == 1 ? "" : "s"); } } ovdb_close(); exit(0); } static int reap(void) { int i, cs; pid_t c; while((c = waitpid(-1, &cs, WNOHANG)) > 0) { for(i = 0; i < ovdb_conf.numrsprocs; i++) { if(c == children[i].pid) { if(children[i].started + 30 > time(NULL)) return 1; children[i].num = 0; if(wholistens == i) wholistens = -1; if((children[i].pid = serverproc(i)) == -1) return 1; children[i].started = time(NULL); break; } } } if(wholistens == -1) parentsig(SIGUSR1); return 0; } #ifndef MAP_ANON #ifdef MAP_ANONYMOUS #define MAP_ANON MAP_ANONYMOUS #endif #endif static void * sharemem(size_t len) { #ifdef MAP_ANON return mmap(0, len, PROT_READ|PROT_WRITE, MAP_ANON|MAP_SHARED, -1, 0); #else int fd = open("/dev/zero", O_RDWR, 0); char *ptr = mmap(0, len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); close(fd); return ptr; #endif } int main(int argc, char *argv[]) { int i, ret; socklen_t salen; char *path, *pidfile; #ifdef HAVE_UNIX_DOMAIN_SOCKETS struct sockaddr_un sa; #else struct sockaddr_in sa; #endif struct timeval tv; fd_set rdset; setproctitle_init(argc, argv); openlog("ovdb_server", L_OPENLOG_FLAGS | LOG_PID, LOG_INN_PROG); message_program_name = "ovdb_server"; if(argc != 2 || strcmp(argv[1], SPACES)) die("should be started by ovdb_init"); message_handlers_warn(1, message_log_syslog_err); message_handlers_die(1, message_log_syslog_err); if (!innconf_read(NULL)) exit(1); if(strcmp(innconf->ovmethod, "ovdb")) die("ovmethod not set to ovdb in inn.conf"); read_ovdb_conf(); #ifdef HAVE_UNIX_DOMAIN_SOCKETS listensock = socket(AF_UNIX, SOCK_STREAM, 0); #else listensock = socket(AF_INET, SOCK_STREAM, 0); #endif if(listensock < 0) sysdie("cannot create socket"); fdflag_nonblocking(listensock, 1); memset(&sa, 0, sizeof sa); #ifdef HAVE_UNIX_DOMAIN_SOCKETS sa.sun_family = AF_UNIX; path = concatpath(innconf->pathrun, OVDB_SERVER_SOCKET); strlcpy(sa.sun_path, path, sizeof(sa.sun_path)); unlink(sa.sun_path); free(path); ret = bind(listensock, (struct sockaddr *)&sa, SUN_LEN(&sa)); #else sa.sin_family = AF_INET; sa.sin_port = htons(OVDB_SERVER_PORT); sa.sin_addr.s_addr = htonl(0x7f000001UL); ret = bind(listensock, (struct sockaddr *)&sa, sizeof sa); if(ret != 0 && errno == EADDRNOTAVAIL) { sa.sin_family = AF_INET; sa.sin_port = htons(OVDB_SERVER_PORT); sa.sin_addr.s_addr = INADDR_ANY; ret = bind(listensock, (struct sockaddr *)&sa, sizeof sa); } #endif if(ret != 0) sysdie("cannot bind socket"); if(listen(listensock, MAXLISTEN) < 0) sysdie("cannot listen on socket"); pidfile = concatpath(innconf->pathrun, OVDB_SERVER_PIDFILE); if(putpid(pidfile)) exit(1); xsignal_norestart(SIGINT, sigfunc); xsignal_norestart(SIGTERM, sigfunc); xsignal_norestart(SIGHUP, sigfunc); xsignal_norestart(SIGUSR1, parentsig); xsignal_norestart(SIGCHLD, childsig); parent = getpid(); children = sharemem(sizeof(struct child) * (ovdb_conf.numrsprocs+1)); if(children == NULL) sysdie("cannot mmap shared memory"); for(i = 0; i < ovdb_conf.numrsprocs+1; i++) { children[i].pid = -1; children[i].num = 0; } for(i = 0; i < ovdb_conf.numrsprocs; i++) { if((children[i].pid = serverproc(i)) == -1) { for(i--; i >= 0; i--) kill(children[i].pid, SIGTERM); exit(1); } children[i].started = time(NULL); sleep(1); } while(!signalled) { if(reap()) break; if(wholistens == -2) { FD_ZERO(&rdset); FD_SET(listensock, &rdset); tv.tv_usec = 0; tv.tv_sec = SELECT_TIMEOUT; ret = select(listensock+1, &rdset, NULL, NULL, &tv); if(ret == 1 && wholistens == -2) { salen = sizeof(sa); ret = accept(listensock, (struct sockaddr *)&sa, &salen); if(ret >= 0) close(ret); } } else { pause(); } } for(i = 0; i < ovdb_conf.numrsprocs; i++) if(children[i].pid != -1) kill(children[i].pid, SIGTERM); while(wait(&ret) > 0) ; unlink(pidfile); exit(0); } #endif /* HAVE_BDB */ inn-2.6.0/frontends/ovdb_monitor.c0000644000175200017520000001304612575023702016573 0ustar iuliusiulius/* * ovdb_monitor * Performs database maintenance tasks * + Transaction checkpoints * + Deadlock detection * + Transaction log removal */ #include "config.h" #include "clibrary.h" #include "portable/setproctitle.h" #include #include #include #include #include "inn/innconf.h" #include "inn/messages.h" #include "inn/libinn.h" #include "inn/ov.h" #include "../storage/ovdb/ovdb.h" #include "../storage/ovdb/ovdb-private.h" #ifndef HAVE_BDB int main(int argc UNUSED, char **argv UNUSED) { exit(0); } #else /* HAVE_BDB */ static int signalled = 0; static void sigfunc(int sig UNUSED) { signalled = 1; } static pid_t deadlockpid = 0; static pid_t checkpointpid = 0; static pid_t logremoverpid = 0; static int putpid(const char *path) { char buf[30]; int fd = open(path, O_WRONLY|O_TRUNC|O_CREAT, 0664); if(!fd) { syswarn("cannot open %s", path); return -1; } snprintf(buf, sizeof(buf), "%d\n", getpid()); if(write(fd, buf, strlen(buf)) < 0) { syswarn("cannot write to %s", path); close(fd); return -1; } close(fd); return 0; } static void deadlock(void) { int ret, status = 0; u_int32_t atype = DB_LOCK_YOUNGEST; if(ovdb_open_berkeleydb(OV_WRITE, 0)) _exit(1); setproctitle("deadlock"); while(!signalled) { ret = OVDBenv->lock_detect(OVDBenv, 0, atype, NULL); if(ret != 0) { warn("OVDB: lock_detect: %s", db_strerror(ret)); status = 1; break; } sleep(30); } ovdb_close_berkeleydb(); _exit(status); } static void checkpoint(void) { int ret, status = 0; DB *db; if(ovdb_open_berkeleydb(OV_WRITE, 0)) _exit(1); setproctitle("checkpoint"); /* Open a database and close it. This is so a necessary initialization gets performed (by the db->open function). */ ret = db_create(&db, OVDBenv, 0); if (ret != 0) { warn("OVDB: checkpoint: db_create: %s", db_strerror(ret)); _exit(1); } ret = db->open(db, NULL, "version", NULL, DB_BTREE, DB_CREATE, 0666); if (ret != 0) { db->close(db, 0); warn("OVDB: checkpoint: version open: %s", db_strerror(ret)); _exit(1); } db->close(db, 0); while(!signalled) { OVDBenv->txn_checkpoint(OVDBenv, 2048, 1, 0); sleep(30); } ovdb_close_berkeleydb(); _exit(status); } static void logremover(void) { int ret, status = 0; char **listp, **p; if(ovdb_open_berkeleydb(OV_WRITE, 0)) _exit(1); setproctitle("logremover"); while(!signalled) { ret = OVDBenv->log_archive(OVDBenv, &listp, DB_ARCH_ABS); if(ret != 0) { warn("OVDB: log_archive: %s", db_strerror(ret)); status = 1; break; } if(listp != NULL) { for(p = listp; *p; p++) unlink(*p); free(listp); } sleep(45); } ovdb_close_berkeleydb(); _exit(status); } static int start_process(pid_t *pid, void (*func)(void)) { pid_t child; switch(child = fork()) { case 0: (*func)(); _exit(0); case -1: syswarn("cannot fork"); return -1; default: *pid = child; return 0; } /*NOTREACHED*/ } static void cleanup(int status) { int cs; if(deadlockpid) kill(deadlockpid, SIGTERM); if(checkpointpid) kill(checkpointpid, SIGTERM); if(logremoverpid) kill(logremoverpid, SIGTERM); xsignal(SIGINT, SIG_DFL); xsignal(SIGTERM, SIG_DFL); xsignal(SIGHUP, SIG_DFL); if(deadlockpid) waitpid(deadlockpid, &cs, 0); if(checkpointpid) waitpid(checkpointpid, &cs, 0); if(logremoverpid) waitpid(logremoverpid, &cs, 0); unlink(concatpath(innconf->pathrun, OVDB_MONITOR_PIDFILE)); exit(status); } static void monitorloop(void) { int cs, restartit; pid_t child; while(!signalled) { child = waitpid(-1, &cs, WNOHANG); if(child > 0) { if(WIFSIGNALED(cs)) { restartit = 0; } else { if(WEXITSTATUS(cs) == 0) restartit = 1; else restartit = 0; } if(child == deadlockpid) { deadlockpid = 0; if(restartit && start_process(&deadlockpid, deadlock)) cleanup(1); } else if(child == checkpointpid) { checkpointpid = 0; if(restartit && start_process(&checkpointpid, checkpoint)) cleanup(1); } else if(child == logremoverpid) { logremoverpid = 0; if(restartit && start_process(&logremoverpid, logremover)) cleanup(1); } if(!restartit) cleanup(1); } sleep(20); } cleanup(0); } int main(int argc, char **argv) { char *pidfile; setproctitle_init(argc, argv); openlog("ovdb_monitor", L_OPENLOG_FLAGS | LOG_PID, LOG_INN_PROG); message_program_name = "ovdb_monitor"; if(argc != 2 || strcmp(argv[1], SPACES)) die("should be started by ovdb_init"); message_handlers_warn(1, message_log_syslog_err); message_handlers_die(1, message_log_syslog_err); if (!innconf_read(NULL)) exit(1); if(strcmp(innconf->ovmethod, "ovdb")) die("ovmethod not set to ovdb in inn.conf"); if(!ovdb_check_user()) die("command must be run as runasuser user"); if(!ovdb_getlock(OVDB_LOCK_ADMIN)) die("cannot lock database"); xsignal(SIGINT, sigfunc); xsignal(SIGTERM, sigfunc); xsignal(SIGHUP, sigfunc); pidfile = concatpath(innconf->pathrun, OVDB_MONITOR_PIDFILE); if(putpid(pidfile)) exit(1); if(start_process(&deadlockpid, deadlock)) cleanup(1); if(start_process(&checkpointpid, checkpoint)) cleanup(1); if(start_process(&logremoverpid, logremover)) cleanup(1); monitorloop(); /* Never reached. */ return 1; } #endif /* HAVE_BDB */ inn-2.6.0/frontends/cnfsheadconf.in0000644000175200017520000002166712575023702016707 0ustar iuliusiulius#! /usr/bin/perl -w # fixscript will replace this line with code to load INN::Config # $Id: cnfsheadconf.in 9216 2011-07-05 18:30:57Z iulius $ # # Copyright Andreas Lamrecht 1998 # # # Modified by Kjetil T. Homme 1998 # # # Modified by Robert R. Collier 1998 # # # bigint support added by Duane Currie (sandman@hub.org) 1998 # # cnfsheadconf is originally from cnfsstat 1999 # use strict; use Getopt::Long; # Required for >32bit integers. use Math::BigInt; use Math::BigFloat; my $conffile = "$INN::Config::pathetc/cycbuff.conf"; my $storageconf = "$INN::Config::pathetc/storage.conf"; # Hex to bigint conversion routine. # bhex(HEXSTRING) returns BIGINT (with leading + chopped off). # # In most languages, unlimited size integers are done using string math # libraries usually called bigint. (Java, Perl, etc.) # # Bigint's are really just strings. sub bhex { my $hexValue = shift; $hexValue =~ s/^0x//; my $integerValue = Math::BigInt->new('0'); for (my $i = 0; $i < length($hexValue); $i+=2) { # Could be more efficient going at larger increments, but byte # by byte is safer for the case of 9 byte values, 11 bytes, etc. my $byte = substr($hexValue, $i, 2); my $byteIntValue = hex($byte); # bmuladd() is only in Perl >= 5.10.0. $integerValue->bmul('256'); $integerValue->badd("$byteIntValue"); } my $result = $integerValue->bstr(); $result =~ s/^\+//; return $result; } sub bint2hex { my $d = shift; my $o = "0"; my $integerValue = Math::BigInt->new("$d"); while ($integerValue->is_pos() and not $integerValue->is_zero()) { my $h = $integerValue->copy()->bmod('16')->bstr(); $integerValue->bdiv('16'); $h =~ s/^\+//; $h='a' if $h eq '10'; $h='b' if $h eq '11'; $h='c' if $h eq '12'; $h='d' if $h eq '13'; $h='e' if $h eq '14'; $h='f' if $h eq '15'; $o="$h$o"; } # The result ends with a "0". return "$o"; } sub usage { print <<"_end_"; Summary tool for cycbuff header manipulation Usage: $0 [-c CYCBUFF] [-h] [-w] If called without args, does a one-time status of all CNFS buffers. -c : print out status of cycbuff -h: this information -w: change header _end_ exit(1); } my (%buff, $cycbuff, $opt_w); GetOptions( 'c=s' => \$cycbuff, 'w' => \$opt_w, 'h' => sub { usage() }, ); unless (read_cycbuffconf()) { print STDERR "Cannot open CycBuff Conffile $conffile ...\n"; exit (1); } unless (read_storageconf()) { print STDERR "No valid $storageconf.\n"; exit (1); } sub read_cycbuffconf { my (@line, %class, %metamode); return 0 unless open my $CONFFILE, '<', $conffile; while (<$CONFFILE>) { $_ =~ s/^\s*(.*?)\s*$/$1/; # Read continuation lines. while(/\\$/) { chop; chop (my $next = <$CONFFILE>); $next =~ s/^\s*(.*?)\s*$/$1/; $_ .= $next; } # \x23 below is #. Emacs perl-mode gets confused by the "comment". next if ($_ =~ /^\s*$/ || $_ =~ /^\x23/); next if ($_ =~ /^cycbuffupdate:/ || $_ =~ /^refreshinterval:/); if($_ =~ /^metacycbuff:/) { @line = split(/:/, $_); if ($class{$line[1]}) { print STDERR "Class $line[1] more than one time in CycBuff Conffile $conffile ...\n"; return 0; } $class{$line[1]} = $line[2]; if (scalar @line > 3 && $line[3] ne "") { $metamode{$line[1]} = $line[3]; } else { $metamode{$line[1]} = "INTERLEAVE"; } next; } if ($_ =~ /^cycbuff/) { @line = split(/:/, $_); if ($buff{$line[1]}) { print STDERR "Buff $line[1] more than one time in CycBuff Conffile $conffile ...\n"; return 1; } $buff{$line[1]} = $line[2]; next; } print STDERR "Unknown config line \"$_\" in CycBuff Conffile $conffile ...\n"; } close $CONFFILE; return 1; } sub read_storageconf { my $line = 0; my %stor; return 0 unless open my $STOR, '<', $storageconf; while (<$STOR>) { ++$line; next if /^\s*#/; # defaults my %key = ("NEWSGROUPS" => "*", "SIZE" => "0,0"); if (/method\s+cnfs\s+\{/) { while (<$STOR>) { ++$line; next if /^\s*#/; last if /\}/; if (/(\w+):\s+(\S+)/i) { $key{uc($1)} = $2; } } unless (defined $key{'CLASS'} && defined $key{'OPTIONS'}) { print STDERR "storage.conf:$line: ". "Missing 'class' or 'options'\n"; return 0; } $key{'SIZE'} .= ",0" unless $key{'SIZE'} =~ /,/; $key{'SIZE'} =~ s/,/:/; if (defined $stor{$key{'OPTIONS'}}) { print STDERR "storage.conf:$line: ". "Class $key{'CLASS'} has several criteria\n"; } else { $stor{$key{'OPTIONS'}} = "$key{'NEWSGROUPS'}:$key{'CLASS'}:" . "$key{'SIZE'}:$key{'OPTIONS'}"; } } } close $STOR; return 1; } START: # If no cycbuff is specified, we check all of them and exit. if (not defined $cycbuff) { foreach (sort keys %buff) { print_cycbuff_head($buff{$_}); } exit(0); } if (not defined $buff{$cycbuff}) { print STDERR "No buffer definition for buffer $cycbuff...\n"; exit(1); } print_cycbuff_head($buff{$cycbuff}); sub make_time { my ($t) = @_; my (@ret); my ($sec,$min,$hour,$mday,$mon,$year) = (localtime($t))[0..5]; push (@ret, sprintf("%04d-%02d-%02d %2d:%02d:%02d", $year + 1900, $mon + 1, $mday, $hour, $min, $sec)); $t = time - $t; $mday = int($t/86400); $t = $t % 86400; $hour = int($t/3600); $t = $t % 3600; $min = int($t/60); $t = $t % 60; push (@ret, sprintf("%4d days, %2d:%02d:%02d", $mday, $hour, $min, $t)); return @ret; } sub print_cycbuff_head { my ($buffpath) = @_; my $CNFSMASIZ = 8; my $CNFSNASIZ = 16; my $CNFSPASIZ = 64; my $CNFSLASIZ = 16; my $headerlength = 2 * $CNFSMASIZ + 2 * $CNFSNASIZ + $CNFSPASIZ + (6 * $CNFSLASIZ); my ($BUFF, $buff); if ($opt_w) { if ( !open $BUFF, '+<', $buffpath ) { print STDERR "Cannot open Cycbuff $buffpath ...\n"; exit(1); } } else { if ( !open $BUFF, '<', $buffpath ) { print STDERR "Cannot open Cycbuff $buffpath ...\n"; exit(1); } } $buff = ""; if ( !read $BUFF, $buff, $headerlength ) { print STDERR "Cannot read $headerlength bytes from file $buffpath...\n"; exit(1); } my ($magic, $name, $path, $lena, $freea, $updatea, $cyclenuma, $metaname, $orderinmetaa, $currentbuff, $blksza) = unpack("a8 a16 a64 a16 a16 a16 a16 a16 a16 a8 a16", $buff); if (!$magic) { print STDERR "Error while unpacking header ...\n"; exit(1); } my $len = bhex($lena); my $free = bhex($freea); my $update = hex($updatea); my $cyclenum = hex($cyclenuma) - 1; my $orderinmeta = hex($orderinmetaa); my $blksz = ($magic =~ m/^CBuf4/) ? hex($blksza) : 512; my ($nupdate_str, $nago_str) = make_time($update); $name =~ s/\0//g; print " Buffer $name, len: "; printf "%.2f", Math::BigFloat->new($len) / (1024 * 1024); print " Mbytes, used: "; printf "%.2f Mbytes", Math::BigFloat->new($free) / (1024 * 1024); printf " (%4.1f%%) %3d cycles\n", 100 * Math::BigFloat->new($free) / Math::BigFloat->new($len), $cyclenum; print " Meta $metaname, order: "; printf "%d", $orderinmeta; print ", current: $currentbuff"; print ", blocksize: $blksz"; print "\n Newest: $nupdate_str, $nago_str ago\n"; if ($opt_w) { print "\nBuffer [$name] => "; my $in = <>; chop $in; if ($in ne "") { $name = sprintf("%0.9s\0", $in); } print "Path [$path] => "; $in = <>; chop $in; if ($in ne "") { $path = sprintf("%0.65s\0", $in); } print "Length [$len ($lena)] => "; $in = <>; chop $in; if ($in ne "") { $in = bint2hex($in); $lena = sprintf("%017.17s\0", $in); } print "Free [$free ($freea)] => "; $in = <>; chop $in; if ($in ne "") { $in = bint2hex($in); $freea = sprintf("%017.17s\0", $in); } print "Meta [$metaname] => "; $in = <>; chop $in; if ($in ne "") { $metaname = sprintf("%0.17s\0", $in); } print "Order [$orderinmeta ($orderinmetaa)] => "; $in = <>; chop $in; if ($in ne "") { $in = bint2hex($in); $orderinmetaa = sprintf("%017.17s\0", $in); } print "Currentbuff [$currentbuff] => "; $in = <>; chop $in; if ($in eq "TRUE" || $in eq "FALSE") { $currentbuff = sprintf("%0.8s", $in); } $buff = pack("a8 a16 a64 a16 a16 a16 a16 a16 a16 a8", $magic, $name, $path, $lena, $freea, $updatea, $cyclenuma, $metaname, $orderinmetaa, $currentbuff); $buff .= pack("a16", $blksza) if ($magic =~ m/^CBuf4/); seek $BUFF, 0, 0; if(! syswrite $BUFF, $buff, $headerlength ) { print STDERR "Cannot write $headerlength bytes to file $buffpath...\n"; exit(1); } } close $BUFF; return; } inn-2.6.0/frontends/sys2nf.c0000644000175200017520000001602112575023702015312 0ustar iuliusiulius/* $Id: sys2nf.c 9019 2010-03-19 21:27:15Z iulius $ ** ** Read a C news "sys" file and split it up into a set of INN ** newsfeeds entries. Also works with B news. ** ** Once done, edit all files that have HELP or all in them. ** Review all files, anyway. */ #include "config.h" #include "clibrary.h" #include #include #include #include "inn/innconf.h" #include "inn/libinn.h" #include "inn/nntp.h" #include "inn/paths.h" #define TEMPFILE ":tmp" static char **Groups; /* ** Fill in the Groups array with the names of all active newsgroups. */ static void ReadActive(act) char *act; { FILE *F; int i; char buff[BUFSIZ]; char *p; /* Open file, count lines. */ if ((F = fopen(act, "r")) == NULL) { perror(act); exit(1); } for (i = 0; fgets(buff, sizeof buff, F) != NULL; i++) continue; Groups = xmalloc((i + 2) * sizeof(char *)); /* Fill in each word. */ rewind(F); for (i = 0; fgets(buff, sizeof buff, F) != NULL; i++) { if ((p = strchr(buff, ' ')) != NULL) *p = '\0'; Groups[i] = xstrdup(buff); } Groups[i] = NULL; fclose(F); } /* ** Read in the sys file and turn it into an array of strings, one ** per continued line. */ char ** ReadSys(sys) char *sys; { char *p; char *to; char *site; int i; char *data; char **strings; /* Read in the file, get rough count. */ if ((data = ReadInFile(sys, (struct stat *)NULL)) == NULL) { perror(sys); exit(1); } for (p = data, i = 0; (p = strchr(p, '\n')) != NULL; p++, i++) continue; /* Scan the file, glue all multi-line entries. */ for (strings = xmalloc((i + 1) * sizeof(char *)), i = 0, to = p = data; *p; ) { for (site = to; *p; ) { if (*p == '\n') { p++; *to = '\0'; break; } if (*p == '\\' && p[1] == '\n') while (*++p && isspace((unsigned char) *p)) continue; else *to++ = *p++; } *to++ = '\0'; if (*site == '\0') continue; strings[i++] = xstrdup(site); } strings[i] = NULL; free(data); return strings; } /* ** Is this the name of a top-level group? We want a simple name, "foo", ** and should find a "foo." in the group list. */ static bool Toplevel(p) char *p; { char **gp; char *g; int i; if (strchr(p, '.') != NULL) return false; for (i = strlen(p) - 1, gp = Groups; (g = *gp++) != NULL; ) if (strncmp(p, g, i) == 0 && g[i + 1] == '.') return true; return false; } /* ** Do we have a name that's a prefix for more then one newsgroup? ** For "foo.bar", we must find more then one "foo.bar" or "foo.bar." */ static bool GroupPrefix(p) char *p; { char **gp; char *g; int count; int i; if (strchr(p, '.') == NULL) return false; for (i = strlen(p), count = 0, gp = Groups; (g = *gp++) != NULL; ) if (strcmp(p, g) == 0 || (strncmp(p, g, i) == 0 && g[i] == '.')) count++; return count > 1; } /* ** Step through the old subscription list, try to update each one in ** turn. */ static void DoSub(F, p) FILE *F; char *p; { char *s; int len, i; bool matched; bool SawBang; bool SawAll; /* Distributions, not newsgroups. */ static const char * const distributions[] = { "world", "na", "usa", "inet", "mod", "net", "local" }; /* Newsgroup hierarchies. */ static const char * const hierarchies[] = { "comp", "misc", "news", "rec", "sci", "soc", "talk", "alt", "bionet", "bit", "biz", "clari", "ddn", "gnu", "ieee", "k12", "pubnet", "trial", "u3b", "vmsnet", "ba", "ca", "dc", "ne", "ny", "tx", "info", "mail", "opinions", "uunet" }; if ((s = strtok(p, ",")) == NULL) return; fprintf(F, "!*"); len = 8 + 1 + 2; do { for (matched = false, i = 0; i < ARRAY_SIZE(distributions); i++) if (strcasecmp(s, distributions[i]) == 0) { matched = true; break; } if (matched) continue; if (innconf->mergetogroups) if (strcmp(s, "!to") == 0 || strncmp(s, "to.", 3) == 0) continue; putc(',', F); len++; if (len + strlen(s) + 3 > 72) { fprintf(F,"\\\n\t "); len = 12; } SawBang = *s == '!'; if (SawBang) { putc('!', F); len++; s++; } SawAll = (strcasecmp(s, "all") == 0); if (SawAll) s = SawBang ? "*" : "*,!control,!control.*"; len += strlen(s); fprintf(F, "%s", s); if (SawAll) ; else { for (matched = false, i = 0; i < ARRAY_SIZE(distributions); i++) if (strcmp(s, hierarchies[i]) == 0) { matched = true; break; } if (matched) { fprintf(F, ".*"); len += 2; } else if (GroupPrefix(s)) { putc('*', F); len++; } } } while ((s = strtok((char *)NULL, ",")) != NULL); } int main(ac, av) int ac; char *av[]; { FILE *F; FILE *out; char **sites; char *f2; char *f3; char *f4; char *p; char *q; char *site; char buff[256]; char *act; char *dir; char *sys; int i; if (!innconf_read(NULL)) exit(1); /* Set defaults. */ act = concatpath(innconf->pathdb, INN_PATH_ACTIVE); sys = "sys"; dir = "feeds"; while ((i = getopt(ac, av, "a:s:d:")) != EOF) switch (i) { default: exit(1); /* NOTREACHED */ case 'a': act = optarg; break; case 'd': dir = optarg; break; case 's': sys = optarg; break; } sites = ReadSys(sys); ReadActive(act); if (mkdir(dir, 0777) < 0 && errno != EEXIST) perror(dir), exit(1); if (chdir(dir) < 0) perror("chdir"), exit(1); for ( ; ; ) { /* Get next non-comment ilne. */ if ((p = *sites++) == NULL) break; for (F = fopen(TEMPFILE, "w"); p && *p == '#'; p = *sites++) fprintf(F, "%s\n", p); if (p == NULL) { fclose(F); break; } site = xstrdup(p); if ((f2 = strchr(site, ':')) == NULL) f2 = "HELP"; else *f2++ = '\0'; if ((f3 = strchr(f2, ':')) == NULL) f3 = "HELP"; else *f3++ = '\0'; if ((f4 = strchr(f3, ':')) == NULL) f4 = "HELP"; else *f4++ = '\0'; /* Write the fields. */ fprintf(F, "%s\\\n", site); fprintf(F, "\t:"); DoSub(F, f2); fprintf(F, "\\\n"); if (strcmp(f3, "n") == 0) fprintf(F, "\t:Tf,Wnm\\\n", f3); else fprintf(F, "\t:HELP%s\\\n", f3); fprintf(F, "\t:%s\n", f4); if (ferror(F) || fclose(F) == EOF) perror(TEMPFILE), exit(1); free(site); /* Find the sitename. */ for (q = p; *q && *q != '/' && *q != ':'; q++) continue; *q = '\0'; /* Append temp file to site file. */ if ((F = fopen(TEMPFILE, "r")) == NULL) perror(TEMPFILE), exit(1); if ((out = xfopena(p)) == NULL) perror(p), exit(1); while ((i = fread(buff, 1, sizeof buff, F)) > 0) if (fwrite(buff, 1, i, out) != i) perror(p), exit(1); fclose(F); if (fclose(out) == EOF) perror(p), exit(1); if (unlink(TEMPFILE) < 0) perror("can't unlink temp file"); } exit(0); /* NOTREACHED */ } inn-2.6.0/frontends/pullnews.in0000644000175200017520000012233412575023702016130 0ustar iuliusiulius#! /usr/bin/perl -w # use lib '@LIBPERLDIR@'; use INN::Config; # If running inside INN, uncomment the above and point to INN::Config. # # Author: James Brister -- berkeley-unix -- # Start Date: Sat, 10 Oct 1998 21:40:11 +0200 # Project: INN # File: pullnews # RCSId: $Id: pullnews.in 9796 2015-03-18 20:46:20Z iulius $ # # History: # Full changelog can be found in the Subversion repository of the # INN project. Major changes are: # # January 2010: Geraint A. Edwards added header-only feeding (-B); # added ability to hashfeed (-a) - uses MD5 - Diablo-compatible; # enabled -m to remove headers matching (or not) a given regexp; # minor bug fix to rnews when -O; improved rnews reporting. # # December 2008: Matija Nalis added -O (optimized mode, checking # whether the downstream server already has the article to download). # Bug fixes too. # # May 2008: Geraint A. Edwards greatly improved pullnews, adding # -b, -C, -d, -G, -H, -k, -l, -m, -M, -n, -P, -Q, -R, -t, -T, -w and # improving -s as well as fixing some bugs. # He also integrated the backupfeed contrib script by Kai Henningsen, # adding -f, -F, -N, -S, -z and -Z to pullnews. # # Description: A simple pull feeder. Connects to multiple upstream # machines (in the guise of a reader), and pulls over articles # and feeds them to a downstream server (in the guise of a feeder). # # Uses a simple configuration file: /pullnews.marks when # run as the news user, or otherwise ~/pullnews.marks, to define # which machines to pull articles from and which groups at each # machine to pull over. There is also support for more specific # configurations like cross-posted newsgroups to kill, thanks to # the -m flag which allows articles with headers matching regexp # to be dropped. # # A configuration file looks like: # # data.pa.vix.com # news.software.nntp 0 0 # comp.lang.c 0 0 # news.uu.net username password # uunet.announce 0 0 # uunet.help 0 0 # # Hostname lines have no leading space and may have an optional # username and password after the hostname; all the # subsequent group lines for that host must have leading # spaces. The two integers on the group line will be updated by # the program when it runs. They are the Unix time the group was # accessed, and the highest numbered article that was pulled # over. # # If you have INN and the script is able to successfully include your # INN::Config module, the value of $INN::Config::pathdb will override it # if pullnews is run as the news user. my $pathdb = $ENV{HOME}; require 5.004; $0 =~ s!.*/!!; my $rcsID =<<'EOM'; $Id: pullnews.in 9796 2015-03-18 20:46:20Z iulius $ EOM $SIG{INT} = \&outtaHere; $SIG{QUIT} = \&bail; use Net::NNTP 2.18; # With libnet 1.0606 (10-Dec-1998) because older versions # issued MODE READER with Net::NNTP::new(). use Getopt::Std; use IO::Handle; use POSIX qw(ceil floor); use strict; my $usage = $0; $pathdb = $INN::Config::pathdb if $INN::Config::pathdb && $INN::Config::runasuser && $INN::Config::runasuser eq getpwuid($<); my $defaultConfig = "${pathdb}/pullnews.marks"; my $defaultPort = 119; my $defaultHost = "localhost"; my $defaultCheckPoint = 0; my $defaultRetries = 0; my $defaultDebug = 0; my $defaultRetryTime = 1; my $defaultProgressWidth = 50; my $defaultMaxArts; my $lockfile; # Check whether pullnews is run inside INN. my $use_inn_shlock = 0; eval { require INN::Utils::Shlock; import INN::Utils::Shlock; $use_inn_shlock = 1; }; # In case pullnews is run outside INN, fall back to call flock(2) # and its corresponding Perl function instead of shlock (a program # shipped with INN). # Note that this Perl function does not work as expected on all # existing systems (for instance on Solaris). if (not $use_inn_shlock) { use Fcntl; use Fcntl qw(:flock); } END { # In case we bail out, while holding a lock. if ($use_inn_shlock) { INN::Utils::Shlock::releaselocks(); } elsif (defined $lockfile) { flock (LOCK, LOCK_UN); unlink $lockfile; } } $usage =~ s!.*/!!; $usage .= " [ -BhnOqRx -a hashfeed -b fraction -c config -C width -d level -f fraction -F fakehop -g groups -G newsgroups -H headers -k checkpt -l logfile -m header_pats -M num -N num -p port -P hop_limit -Q level -r file -s host[:port] -S num -t retries -T seconds -w num -z num -Z num ] [ upstream_host ... ] -a hashfeed only feed article if the MD5 hash of the Message-ID matches hashfeed (where hashfeed is of the form value/mod, value/mod:offset, start-end/mod, or start-end/mod:offset). The algorithm used is compatible with the one used by Diablo; see the pullnews man page for more details. -b fraction backtrack on server numbering reset. The proportion (0.0 to 1.0) of a group's articles to pull when the server's article number is less than our high for that group. When fraction is 1.0, pull all the articles on the server. The default is to do nothing. -B feed is header-only (headers plus one blank line). Add the Bytes: header field if needed. Keep body if control article. -c config specify the configuration file instead of the default file located in ${pathdb}/pullnews.marks when run as the news user, or otherwise in ~/pullnews.marks (the running user's home directory). -C width use width characters for progress (default is $defaultProgressWidth). -d level set debugging level to this integer (default is $defaultDebug). -f fraction proportion of articles to get in each group (0.0 to 1.0). -F fakehop prepend fakehop as a host to the Path: header. -g groups specify a collection of groups to get. The value must be a single argument with commas between group names: -g comp.lang.c,comp.lang.lisp,comp.lang.python The groups must be defined in the config file somewhere. Only the hosts that carry those groups will be contacted. -G newsgroups add these groups to the configuration (see -g and -w). -h print this message. -H headers remove these named headers (colon-separated list). -k checkpt checkpoint the config file every checkpt articles (default is $defaultCheckPoint). A value of 0 means normally (at end). -l logfile log progress/stats to logfile (default is stdout). -m 'Hdr1:regexp1 !Hdr2:regexp2 #Hdr3:regexp3 !#Hdr4:regexp4 ...' feed article only if: the Hdr1: header matches regexp1; and the Hdr2: header does not match regexp2; also, process the message thus: if the Hdr3: header matches regexp3, remove that header; if the Hdr4: header does not match regexp4, remove it. -M num maximum number of articles (per group) to process before bailing out. -n do nothing -- just fake it. -N num timeout length when establishing NNTP connection. -O optimized mode (may help for big articles/slow link to upstream hosts). Check whether an article exists before downloading it. -p port specify the port to connect to in order to feed articles (default is $defaultPort). -P hop_limit count hops ('!') in the Path: header, feed article only if: hop_limit is '+num' and hop_count is more than num; or hop_limit is '-num' and hop_count is less than num. -q $0 will normally be verbose about what it is doing. This option will make it quiet. -Q level set the quietness level (-Q 2 is equivalent to -q). -r file rather than feeding to a server, $0 will instead create an rnews-compatible file. -R be a reader (use MODE READER and POST) -s host[:port] specify the downstream hostname (and optional port) (default is $defaultHost). -S num specify the maximum time (in seconds) to run. -t retries number of attempts to connect to a server (default is $defaultRetries, see also -T). -T secs time (in seconds) to pause between retries (default is $defaultRetryTime, see also -t). -w num set highwater mark to num (if num is negative, use Current+num instead); a num of 0 will re-get all articles on the server; but a num of -0 will get no old articles, set mark to Current. -x insert an Xref: header in any article that lacks one. -z num time (in seconds) to sleep between articles. -Z num time (in seconds) to sleep between groups. "; use vars qw($opt_a $opt_b $opt_B $opt_c $opt_C $opt_d $opt_f $opt_F $opt_g $opt_G $opt_h $opt_H $opt_k $opt_l $opt_m $opt_M $opt_n $opt_N $opt_O $opt_p $opt_P $opt_q $opt_Q $opt_r $opt_R $opt_s $opt_S $opt_t $opt_T $opt_w $opt_x $opt_z $opt_Z); getopts("a:b:Bc:C:d:f:F:g:G:hH:k:l:m:M:nN:Op:P:qQ:r:Rs:S:t:T:w:xz:Z:") || die $usage; die $usage if $opt_h; my @groupsToGet = (); # Empty list means all groups in config file. my @groupsToAdd = (); my $rnews = $opt_r; my $groupFile = $opt_c || $defaultConfig; my $localServer = $opt_s || $defaultHost; my $localPort = $opt_p || $defaultPort; my $quiet = $opt_q; my $hashfeed = $opt_a || ''; my $header_only = $opt_B; my $watermark = $opt_w; my $retries = $opt_t || $defaultRetries; my $retryTime = $opt_T || $defaultRetryTime; my $checkPoint = $opt_k || $defaultCheckPoint; my $debug = $opt_d || $defaultDebug; my $progressWidth = $opt_C || $defaultProgressWidth; my $maxArts = $opt_M || $defaultMaxArts; my $no_op = $opt_n || 0; my $reader = $opt_R || 0; my $quietness = $opt_Q || 0; my $skip_headers = lc($opt_H || ''); my $logFile = '>&STDOUT'; $logFile = ">>$opt_l" if $opt_l; my @hdr_to_match = split(/\s+/, $opt_m) if defined $opt_m; my $pathSteps = $opt_P if defined $opt_P; my $path_limit; # Find a possible port at the end of the news server name. # Count the number of ":" to check that it is not an IPv6 address. if (not defined $opt_p) { my @colons = split(/:/, $localServer); $localPort = $1 if ((scalar(@colons) == 2) and ($localServer =~ s/:(\d+)$//)); } die "can\'t have both ``-s'' and ``-r''\n" if $opt_s && $opt_r; die "``-b'' value not 0.0-1.0: $opt_b\n" if defined $opt_b and $opt_b !~ /^([01](\.0*)?|0?\.\d+)$/; die "``-C'' value not an integer: $opt_C\n" if $progressWidth !~ m!^\d+$!; die "``-d'' value not an integer: $opt_d\n" if $debug !~ m!^\d+$!; die "``-f'' value not 0.0-1.0: $opt_f\n" if defined $opt_f and $opt_f !~ /^([01](\.0*)?|0?\.\d+)$/; die "``-F'' value not a hostname: $opt_F\n" if defined $opt_F and $opt_F !~ m!^[\w\-\.]+$!; die "``-k'' value not an integer: $opt_k\n" if $checkPoint !~ m!^\d+$!; die "``-M'' value not an integer: $opt_M\n" if defined $maxArts and $maxArts !~ m!^\d+$!; die "``-N'' value not an integer: $opt_N\n" if defined $opt_N and $opt_N !~ /^\d+$/; die "``-p'' value not an integer: $opt_p\n" if $localPort !~ m!^\d+$!; if (defined $pathSteps) { die "``-P'' value not a signed integer: $opt_P\n" if $pathSteps !~ /^[-+](\d+)$/; $path_limit = $1; } die "option ``-r -'' needs ``-l'' option\n" if defined $opt_r and $opt_r eq '-' and not $opt_l; die "``-S'' value not an integer: $opt_S\n" if defined $opt_S and $opt_S !~ /^\d+$/; die "``-t'' value not an integer: $opt_t\n" if $retries !~ m!^\d+$!; die "``-w'' value not an integer: $opt_w\n" if defined $watermark and $watermark !~ /^-?\d+$/; die "``-z'' value not an integer: $opt_z\n" if defined $opt_z and $opt_z !~ /^\d+$/; die "``-Z'' value not an integer: $opt_Z\n" if defined $opt_Z and $opt_Z !~ /^\d+$/; if ($hashfeed ne '') { my $a_err = "``-a'' value not in format ``start[-end]/mod[:offset]'': $opt_a\n"; die $a_err if $opt_a !~ m!^(\d+)(?:-(\d+))?/(\d+)(?:[:_](\d+))?$!; $hashfeed = { 'low' => $1, 'high' => $2 || $1, 'modulus' => $3, 'offset' => $4 || 0, }; die $a_err if $hashfeed->{'low'} > $hashfeed->{'high'} or $hashfeed->{'modulus'} == 0 or $hashfeed->{'offset'} > 12; if ($hashfeed->{'low'} == 1 and $hashfeed->{'high'} == $hashfeed->{'modulus'}) { $hashfeed = ''; } else { require Digest::MD5; Digest::MD5->import(qw/md5/); } } $quiet = 1 if $quietness > 1; my %NNTP_Args = (); $NNTP_Args{'Timeout'} = $opt_N if defined $opt_N; @groupsToGet = map { s!^\s*(\S+)\s*!$1!; $_ } split (",", $opt_g) if $opt_g; @groupsToAdd = map { s!^\s*(\S+)\s*!$1!; $_ } split (",", $opt_G) if $opt_G; $| = 1; my $servers = {}; my $sname = undef; my %fed = (); my %refused = (); my %rejected = (); my $pulled = {}; my %passwd = (); my %info = ( fed => 0, refused => 0, rejected => 0, bytes => 0, ); if ($rnews) { if ($no_op) { print "Would write to rnews file $rnews\n"; } else { open(RNEWS, ">$rnews") || die "can't open rnews-format output: $rnews: $!\n"; } $info{'rnews'}->{bytes} = 0; $info{'rnews'}->{fed} = 0; } open(LOG, $logFile) || die "can't open logfile ($logFile)!: $!\n"; # Forces a flush after each write or print. my $oldfh = select; $| = 1; select LOG; $| = 1; select $oldfh; $lockfile = $groupFile . '.pid'; # Acquire a lock. if ($use_inn_shlock) { INN::Utils::Shlock::lock($lockfile) or die "cannot create lockfile $lockfile\n"; } else { sysopen (LOCK, "$lockfile", O_RDWR | O_CREAT, 0700) or die "cannot create lockfile $lockfile: $!\n"; $oldfh = select; select LOCK; $| = 1; select $oldfh; if (!flock (LOCK, LOCK_EX | LOCK_NB)) { seek LOCK, 0, 0; my $otherpid = ; chomp $otherpid; die "Another pullnews (pid: $otherpid) seems to be running.\n"; } print LOCK "$$\n"; } print LOG scalar(localtime(time)), " start\n\n" unless $quiet; if (@groupsToGet && ! $quiet) { print LOG "Checking for specific groups:\n"; map { printf LOG "\t%s\n", $_ } @groupsToGet; print LOG "\n"; } open(FILE, "<$groupFile") || die "can't open group file $groupFile\n"; my $array_order = 0; while () { next if m!^\s*\#! || m!^\s*$!; if (m!^(\S+)(\s+(\S+)\s+(\S+))?\s*$!) { $sname = $1; $servers->{$sname} = {}; $servers->{$sname}->{_order} = $array_order++; $passwd{$sname} = [ $3, $4 ] if defined $3 and $3 ne ""; } elsif (m!^\s+(\S+)\s+(\d+)\s+(\d+)!) { my ($group,$date,$high) = ($1,$2,$3); $servers->{$sname}->{$group} = [ $date, $high ]; } elsif (m!^\s+(\S+)\s*$!) { # Assume this is a new group. my ($group,$date,$high) = ($1,0,0); print LOG "Looking for new group $group on $sname\n" unless $quiet; $servers->{$sname}->{$group} = [ $date, $high ]; } else { die "Fatal error in $groupFile: $.: $_\n"; } } close FILE; my @servers = @ARGV ? @ARGV : sort {$servers->{$a}->{_order} <=> $servers->{$b}->{_order}} keys %$servers; die "No servers!\n" if ! @servers; my $localcxn; my $art_total_count = 0; # The number of articles transferred during the session. if ( not $rnews ) { print LOG "Connecting to downstream host: $localServer " . "port: $localPort ..." unless $quiet; my %localopts = ("Port" => "$localPort", "Reader" => $reader, %NNTP_Args); $localcxn = Net::NNTP->new($localServer, %localopts) || die "Can't connect to server $localServer\n"; if (exists $passwd{$localServer} && !$localcxn->authinfo(@{$passwd{$localServer}})) { warn sprintf ("failed to authorize: %s %s\n", $localcxn->code(), $localcxn->message()); } } if ( not $quiet and not $quietness ) { print LOG "done.\n\n"; print LOG "Legend: ``.'' is an article the downstream server refused\n"; print LOG " ``*'' is an article the downstream server rejected\n"; print LOG " ``+'' is an article the downstream server accepted\n"; print LOG " ``x'' is an article the upstream server couldn't "; print LOG "give out\n"; print LOG " ``m'' is an article skipped due to headers (-a, -m or -P)\n"; print LOG "\n"; print LOG "Writing to rnews-format output: $rnews\n\n" if $rnews; } foreach my $server (@servers) { my ($username, $passwd); foreach my $addGroup (@groupsToAdd) { next if defined $servers->{$server}->{$addGroup}; $servers->{$server}->{$addGroup} = [ 0, 0 ]; } if (@groupsToGet > 0) { my $ok; foreach my $sgroup (keys %{$servers->{$server}}) { $ok = 1 if grep($_ eq $sgroup, @groupsToGet); } if (! $ok) { # User gave -g and the server doesn't have those groups. warn "Skipping server $server. Doesn't have specified groups.\n"; next; } } if (exists $passwd{$server}) { ($username, $passwd) = @{$passwd{$server}}; } if (!exists($servers->{$server})) { warn "No such upstream host $server configured.\n"; next; } my $shash = $servers->{$server}; # No need to connect to the upstream server when there is no newsgroup # to fetch. (The value 1 is for the "_order" key in the hash.) next if keys(%{$shash}) == 1; my $connectionAttempts = 0; my $upstream; {{ print LOG "connecting to upstream server $server..." unless $quiet; $upstream = Net::NNTP->new($server, %NNTP_Args); $connectionAttempts++; if (!$upstream && $connectionAttempts <= $retries) { sleep $retryTime; next; } }} if (!$upstream) { print LOG " failed.\n" unless $quiet; warn "can't connect to upstream server $server: $!\n"; next; } else { print LOG " done.\n" unless $quiet; } if ($username && !$upstream->authinfo($username, $passwd)) { warn sprintf ("failed to authorize: %s %s\n", $upstream->code(), $upstream->message()); next; } $info{server}->{$server}->{bytes} = 0; $info{server}->{$server}->{fed} = 0; $info{server}->{$server}->{refused} = 0; $info{server}->{$server}->{rejected} = 0; foreach my $group (sort keys %{$servers->{$server}}) { next if $group eq '_order'; next if (@groupsToGet && !grep ($_ eq $group, @groupsToGet)); # crossFeedGroup returns 1 when the newsgroup is successfully # pulled. No need to check the return value because we do not # want to abort on the first error. crossFeedGroup ($upstream, $localcxn, $server, $group, $shash); last if defined $opt_S and time >= $^T+$opt_S; sleep $opt_Z if defined $opt_Z; } $upstream->quit(); last if defined $opt_S and time >= $^T+$opt_S; } saveConfig (); stats() unless $quiet; if ($rnews) { if (not $no_op and not close RNEWS) { print LOG "\nRNEWS close failure: $!"; } unlink $rnews if -f $rnews and not -s $rnews; } print LOG "\nDone ", scalar(localtime(time)), "\n" unless $quiet; cleanLock(); exit (0); ############################################################################### sub stats { my $ltotal = 0; my $reftotal = 0; my $rejtotal = 0; my $sum; map { $reftotal += $refused{$_} } keys %refused; map { $rejtotal += $rejected{$_} } keys %rejected; map { $ltotal += $fed{$_} } keys %fed; $sum = $reftotal + $rejtotal + $ltotal; if ($quiet) { printf LOG localtime() . " [$$] %d article%s to $localServer\n", $sum, ($sum != 1 ? "s" : ""); } elsif ($rnews) { printf LOG "\n%d article%s written to $rnews\n", $sum, ($sum != 1 ? "s were" : " was"); } else { printf LOG "\n%d article%s offered to server on $localServer\n", $sum, ($sum != 1 ? "s were" : " was"); } return if ($sum == 0); if ($quiet) { print LOG localtime() . " [$$] $ltotal ok, $reftotal ref, $rejtotal rej\n"; } elsif (not $rnews) { printf LOG "%d article%s accepted\n", $ltotal, ($ltotal != 1 ? "s were" : " was") if ($ltotal != 0); printf LOG "%d article%s refused\n", $reftotal, ($reftotal != 1 ? "s were" : " was") if ($reftotal != 0); printf LOG "%d article%s rejected\n", $rejtotal, ($rejtotal != 1 ? "s were" : " was") if ($rejtotal != 0); } map { print LOG "\nUpstream server $_:\n" if not $quiet; my $server = $_; my $width = 0; map { $width = length if length > $width; } sort keys %{$pulled->{$server}} if not $quiet; map { if ($quiet) { printf LOG "%s [$$] from $server $_ %s\n", localtime(), $pulled->{$server}->{$_}; } else { printf LOG "\t%${width}s %d\n", $_, $pulled->{$server}->{$_}; } } sort keys %{$pulled->{$server}}; } sort keys %{$pulled}; } sub saveConfig { return if $no_op; $SIG{INT} = $SIG{QUIT} = 'IGNORE'; open(FILE,">$groupFile") || die "can't open $groupFile: $!\n"; my $server; my $group; print LOG "\nSaving config\n" unless $quiet; print FILE "# Format: (date is epoch seconds)\n"; print FILE "# hostname [username password]\n"; print FILE "# group date high\n"; foreach $server (sort {$servers->{$a}->{_order} <=> $servers->{$b}->{_order}} keys %$servers) { print FILE "$server"; if (defined $passwd{$server}) { printf FILE " %s %s", $passwd{$server}->[0], $passwd{$server}->[1]; } print FILE "\n"; foreach $group (sort keys %{$servers->{$server}}) { next if $group eq '_order'; my ($date,$high) = @{$servers->{$server}->{$group}}; printf FILE "\t%s %d %d\n",$group,$date,$high; } } close FILE; } sub outtaHere { saveConfig(); cleanLock(); exit (0); } sub cleanLock { # Unlock. if ($use_inn_shlock) { INN::Utils::Shlock::unlock($lockfile) if defined $lockfile; } else { flock (LOCK, LOCK_UN); unlink $lockfile if defined $lockfile; } } sub bail { warn "received QUIT signal. Not saving config.\n"; cleanLock(); exit (0); } sub crossFeedGroup { my ($fromServer,$toServer,$server,$group,$shash) = @_; my ($date,$high) = @{$shash->{$group}}; my ($prevDate,$prevHigh) = @{$shash->{$group}}; my ($narticles,$first,$last,$name) = $fromServer->group($group); my $count = 0; my $len = 0; # Received article length (bytes) (for stats). my $code; my $startTime = time; my ($prevRefused, $prevRejected) = ($info{refused}, $info{rejected}); if (!defined($narticles)) { # Group command failed. warn sprintf ("Group command failed for $group: %s %s\n", $fromServer->code() || 'NO_CODE', $fromServer->message()); return undef; } if (not $quiet) { printf LOG "\n%s:\n", $name; printf LOG "\tLast checked: %s\n", $prevDate ? scalar(localtime($prevDate)) : "never"; printf LOG "\t%d article%s available (first %d, last %d)\n", $narticles, $narticles != 1 ? "s" : "", $first, $last; } if (defined $watermark) { printf LOG "\tOur previous highest: %d\n", $prevHigh if not $quiet; $high = $watermark; $high = $last+$watermark if substr($watermark, 0, 1) eq '-'; $high = 0 if $high < 0; $shash->{$group} = [ time, $high ]; } printf LOG "\tOur current highest: %d", $high if not $quiet; return 0 if ! $name; if ($narticles == 0) { print LOG " (nothing to get)\n" unless $quiet; # Just update the time; keep the last known high watermark. $shash->{$group} = [ time, $high ]; return 1; } my $toget = (($last - $high) < $narticles ? $last - $high : $narticles); $toget = ceil($toget * $opt_f) if defined $opt_f; if ($last < $high and $opt_b) { $high = $first+floor(($last-$first+1)*(1-$opt_b)); $toget = $last - $high; print LOG " (reset highwater mark to $high)" unless $quiet; } elsif ($prevHigh == -1 || $last <= $prevHigh) { # We connected OK but there's nothing there, or we just want # to reset our highwater mark. $shash->{$group} = [ time, $high ]; print LOG " (nothing to get)\n" unless $quiet; return 1; } print LOG " ($toget to get)\n" unless $quiet; my $i; my @warns; my $skip_article; for ($i = ($first > $high ? $first : $high + 1) ; $i <= $last ; $i++) { $skip_article = 0; last if defined $maxArts and $count >= $maxArts; last if defined $opt_f and $count >= $toget; $count++; $art_total_count++; sleep $opt_z if defined $opt_z and $count > 1; # "Optimized mode" -- check if the article is wanted *before* downloading it. if (defined $opt_O) { # 223 n article retrieved -- request text separately (after STAT) # 423 no such article number in this group # 430 no such article found my $org_msgid = $fromServer->nntpstat($i); my $org_code = $fromServer->code(); # Continue if the article exists on the upstream server. if ($org_code == 223 and not $rnews) { my $new_msgid = $toServer->nntpstat($org_msgid); my $new_code = $toServer->code(); print LOG "\tDEBUGGING $i\t$org_msgid ($org_code) => $new_code\n" if $debug >= 3; # Skip the article if it already exists on the downstream server. if ($new_code == 223) { print LOG "\tDEBUGGING $i\t-- not downloading already existing message $org_msgid code=$new_code\n" if $debug >= 2; $skip_article = 1; } } } my $add_bytes_header = 0; my $is_control_art = 0; my $article; if (not $skip_article and $header_only) { $article = $fromServer->head($i); if ($fromServer->code() == 221) { my $has_bytes_header = 0; for my $hdr (@$article) { if (lc(substr($hdr, 0, 6)) eq 'bytes:') { $has_bytes_header = 1; } elsif (lc(substr($hdr, 0, 8)) eq 'control:') { $is_control_art = 1; last; } } $add_bytes_header = 1 if not $has_bytes_header; push @{$article}, "\n" if not $is_control_art; } } if (not $skip_article and (not $header_only or $is_control_art or $add_bytes_header)) { $article = $fromServer->article($i); } if ($article) { my $msgid; my $xref = 0; my $headers = 1; my $line_len = 0; my $idx_blank_pre_body; # Index of the blank line between headers/body. my $tx_len = 0; # Transmitted article length (bytes) (for rnews, Bytes:). my @header_nums_to_go = (); my $match_all_hdrs = 1; # Assume no headers to match. my $skip_due_to_hdrs = 0; # Set to 1 if triggered by -P, 2 if by -m, 3 if by -a. my %m_found_hdrs = (); my $curr_hdr = ''; for (my $idx = 0 ; $idx < @{$article} ; $idx++) { $line_len = length($article->[$idx]); $len += $line_len; $tx_len += $line_len; $info{server}->{$server}->{bytes} += $line_len; $info{bytes} += $line_len; next if not $headers; $idx_blank_pre_body = $idx; $curr_hdr = lc($1) if $article->[$idx] =~ /^([^:[:blank:]]+):/; if ($article->[$idx] eq "\n") { $headers = 0; next; } if ($match_all_hdrs and @hdr_to_match and $article->[$idx] =~ /^[^[:blank:]]/) { # Check header matches -m flag if new header. # Unfold this header (with following lines). my $unfolded_art_hdr = $article->[$idx]; for (my $idx_step = $idx+1; $idx_step < @$article and $article->[$idx_step] =~ /^[[:space:]](.+)/; $idx_step++) { # While next line is continuation... my $more_line = $1; chomp $unfolded_art_hdr; $unfolded_art_hdr .= $more_line; } my ($hdr_un, $val_un) = split(':', $unfolded_art_hdr, 2); $val_un = '' if not defined $val_un; $val_un =~ s/^\s*//; my $remove_hdr = 0; for my $tuple_match (@hdr_to_match) { my ($hdr_m, $val_m) = split(':', $tuple_match, 2); my $negate_h = ($hdr_m =~ s/^!//); my $remove_h = ($hdr_m =~ s/^#//); next if lc($hdr_un) ne lc($hdr_m); $m_found_hdrs{lc($hdr_m)} = 1 if not $remove_h; if ($negate_h) { if ($val_un =~ /$val_m/i) { print LOG "\tDEBUGGING $i\t-- $hdr_un [$val_un]\n" if $debug >= 2; if (not $remove_h) { $match_all_hdrs = 0; } } elsif ($remove_h) { $remove_hdr = 1; } } elsif (not $val_un =~ /$val_m/i) { print LOG "\tDEBUGGING $i\t++ $hdr_un [$val_un]\n" if $debug >= 2; if (not $remove_h) { $match_all_hdrs = 0; } } elsif ($remove_h) { $remove_hdr = 1; } last if not $match_all_hdrs; } push @header_nums_to_go, $idx if $remove_hdr; } if (grep { $curr_hdr eq $_ } split(':', $skip_headers)) { print LOG "\tDEBUGGING $i\tskip_hdr $idx\t$curr_hdr\n" if $debug >= 2; push @header_nums_to_go, $idx; } if ($article->[$idx] =~ m!^message-id:\s*(\S+)!i) { $msgid = $1; } if ($article->[$idx] =~ m!^Path:\s*!i) { if (defined $pathSteps) { my $path_count = $article->[$idx]; $path_count = ($path_count =~ s@!@@g) || 0; if (substr($pathSteps, 0, 1) eq '-') { $skip_due_to_hdrs = 1 if $path_count >= $path_limit; } elsif (substr($pathSteps, 0, 1) eq '+') { $skip_due_to_hdrs = 1 if $path_count <= $path_limit; } } if ($skip_due_to_hdrs) { print LOG "\tDEBUGGING $i\tNpath_skip_art $i\n" if $debug >= 2; } else { if (defined $opt_F) { $tx_len += length($opt_F)+1; $article->[$idx] =~ s/^Path:\s*/$&$opt_F!/i; } } } if ($opt_x && $article->[$idx] =~ m!^xref:!i) { $xref = 1; } } if (@hdr_to_match and (not $match_all_hdrs or scalar(grep { ! /^!?#/ } @hdr_to_match) != keys %m_found_hdrs)) { $skip_due_to_hdrs = 2; } while (@header_nums_to_go) { my $idx = pop @header_nums_to_go; # Start from last, so numbers are not affected. my $cut = join("\n\t", splice(@{$article}, $idx, 1)); $tx_len -= length($cut); $idx_blank_pre_body--; print LOG "\tDEBUGGING $i\tcut1 $cut" if $debug >= 2; while ($article->[$idx] =~ /^[[:space:]](.+)/) { # Folded lines. my $cut = join("\n\t", splice(@{$article}, $idx, 1)); $tx_len -= length($cut); $idx_blank_pre_body--; print LOG "\tDEBUGGING $i\tcut_ $cut" if $debug >= 2; } } if (!$msgid) { warn "No Message-ID: header found in article\n"; next; } else { print LOG "\tDEBUGGING $i\tMessage-ID: $msgid\n" if $debug >= 2; } # Some old servers lack Xref:, which bothers a downstream INN if # it has xrefslave set, so add one just before the blank line. if ($opt_x && !$xref) { warn "No Xref: header found in article, adding\n"; my $xref_h = "Xref: $server $group: $i\n"; splice(@{$article}, $idx_blank_pre_body, 0, $xref_h); $tx_len += length($xref_h); $idx_blank_pre_body++; } if ($add_bytes_header) { # Compute the number of bytes the same way the :bytes # metadata item would do. The additional Bytes: header # field is not counted, as well as headers removed by # pullnews. my $bytes_real_count = $tx_len + scalar(@{$article}); my $bytes_h = "Bytes: $bytes_real_count\n"; splice(@{$article}, $idx_blank_pre_body, 0, $bytes_h); $tx_len += length($bytes_h); $idx_blank_pre_body++; } if ($header_only and not $is_control_art and @{$article} > $idx_blank_pre_body+1) { splice(@{$article}, $idx_blank_pre_body+1); $tx_len = 0; for my $line (@{$article}) { $tx_len += length($line); } } if (not $skip_due_to_hdrs and ref $hashfeed) { my $hash_val = unpack('N', substr(md5($msgid), 12-$hashfeed->{'offset'}, 4)) % $hashfeed->{'modulus'} + 1; $skip_due_to_hdrs = 3 if $hash_val < $hashfeed->{'low'} or $hash_val > $hashfeed->{'high'}; } $pulled->{$server}->{$group}++; if ($skip_due_to_hdrs) { if ($debug >= 2) { print LOG "\tDEBUGGING $i\tskip_art: " . ($skip_due_to_hdrs == 1 ? 'hopsPath' : ($skip_due_to_hdrs == 2 ? 'hdr' : ($skip_due_to_hdrs == 3 ? 'hashfeed' : 'unknown'))) . "\n"; } print LOG "m" unless $quiet; } elsif ($rnews) { printf RNEWS "#! rnews %d\n", $tx_len; map { print RNEWS $_ } @{$article}; print LOG "+" unless $quiet; $fed{$group}++; $info{'rnews'}->{fed}++; $info{fed}++; } else { if ($no_op) { print "Would offer $msgid\n"; } elsif ($reader and not $toServer->post($article)) { # 240 article posted ok # 340 send article to be posted. End with . # 440 posting not allowed # 441 posting failed my $code = $toServer->code(); my $msg = $toServer->message(); print LOG "\tDEBUGGING $i\tPost $code: Msg: <" . join('//', split(/\r?\n/, $msg)) . ">\n" if $debug >= 2; $msg =~ s/^340 .*?\n(?=.)//o; if ($msg =~ /^240 /) { print LOG "+" unless $quiet; push @warns, "Post $i ok ($code): $msg"; $fed{$group}++; $info{server}->{$server}->{fed}++; $info{fed}++; } elsif ($msg =~ /^435 / or $msg =~ /duplicate message-id/io) { print LOG "." unless $quiet; push @warns, "Post $i to server declined ($code): $msg" if $msg !~ /^435 $msgid$/ and $msg !~ /duplicate message-id/io; $refused{$group}++; $info{server}->{$server}->{refused}++; $info{refused}++; } else { warn "Post $i to server failed ($code): $msg\n"; $toServer->quit(); } } elsif (not $reader and not $toServer->ihave($msgid,$article)) { # 235 article transferred ok # 335 send article to be transferred. End with . # 435 article not wanted -- do not send it # 436 transfer failed -- try again later # 437 article rejected -- do not try again my $code = $toServer->code(); my $msg = $toServer->message(); print LOG "\tDEBUGGING $i\tPost $code: Msg: <" . join('//', split(/\r?\n/, $msg)) . ">\n" if $debug >= 2; if ($code == 435) { print LOG "." unless $quiet; $refused{$group}++; $info{server}->{$server}->{refused}++; $info{refused}++; } elsif ($code == 437) { print LOG "*" unless $quiet; $rejected{$group}++; $info{server}->{$server}->{rejected}++; $info{rejected}++; } else { warn "Transfer to server failed ($code): $msg\n"; $toServer->quit(); saveConfig(); exit (1); } } else { my $code = $toServer->code(); my $msg = $toServer->message(); print LOG "\tDEBUGGING $i\tPost $code: Msg: <" . join('//', split(/\r?\n/, $msg)) . ">\n" if $debug >= 2; print LOG "+" unless $quiet; $fed{$group}++; $info{server}->{$server}->{fed}++; $info{fed}++; } } $shash->{$group} = [ time, $high = $i ]; # Optimized mode (-O) decided to skip this article... } elsif ($skip_article) { print LOG "." unless $quiet; $refused{$group}++; $info{server}->{$server}->{refused}++; $info{refused}++; $shash->{$group} = [ time, $high = $i ]; } else { $shash->{$group} = [ time, $high = $i ] if $fromServer->code() == 430 # No such article, do not retry. or $fromServer->code() == 423; print LOG "x" unless $quiet; printf LOG ("\nDEBUGGING $i %s %s\n", $fromServer->code(), $fromServer->message()) if $debug >= 2; } saveConfig() if $checkPoint and ($art_total_count % $checkPoint) == 0; print LOG "\n" if (!$quiet && (($count % $progressWidth) == 0)); last if defined $opt_S and time >= $^T+$opt_S; } print LOG "\n" unless $quiet; print LOG join("\n\t", '', @warns) . "\n\n" if @warns; my $elapsed_time = time - $startTime + 1; if ($quiet) { my $rejectedDiff = $info{rejected}-$prevRejected; my $refusedDiff = $info{refused}-$prevRefused; my $destServer = ($localServer ne $defaultHost ? " to $localServer" : ''); print LOG localtime() . "[$$] $server$destServer $name $narticles $first-$last : $count $prevHigh-" . ($high == $last ? '' : $high) . " $refusedDiff $rejectedDiff\n" unless $prevHigh == $high and $count == 0; } else { printf LOG "%s article%s retrieved in %d seconds (%d bytes, %d cps)\n", $count, ($count == 1 ? "" : "s"), $elapsed_time, $len, int($len*100/$elapsed_time)/100; } return 1; } inn-2.6.0/frontends/ovdb_stat.c0000644000175200017520000006327212575023702016065 0ustar iuliusiulius/* * ovdb_stat.c * print information about ovdb database */ #include "config.h" #include "clibrary.h" #include #include #include #include #include "inn/innconf.h" #include "inn/messages.h" #include "inn/libinn.h" #include "inn/ov.h" #include "inn/paths.h" #include "inn/storage.h" #include "../storage/ovdb/ovdb.h" #include "../storage/ovdb/ovdb-private.h" #ifndef HAVE_BDB int main(int argc UNUSED, char **argv UNUSED) { die("Berkeley DB support not compiled"); } #else /* HAVE_BDB */ static int signalled = 0; static void sigfunc(int signum UNUSED) { signalled = 1; } static int html = 0; typedef enum { END, INT32, /* 'a' points to u_int32_t */ HEX32, /* 'a' printed in hex */ DIFF32, /* 'a' - 'b' - 'c' */ PCT32, /* 100 * 'a' / ('a' + 'b') */ FF, /* 'a' = freebytes, 'b' = npages, 'c' = pagesize */ BYTES, /* 'a' = bytes, 'b' = mbytes, 'c' = gbytes */ MODE, /* 'a' points to int, printed as octal mode */ TIME, /* 'a' points to time_t, printed as date/time */ LSN, /* 'a' points to DB_LSN */ STR, /* 'a' points to char* */ SIZE /* 'a' points to size_t */ } DATATYPE; struct datatab { DATATYPE type; ssize_t a; ssize_t b; ssize_t c; const char *desc; }; static void display_heading(const char *str) { if(html) printf("

%s

\n", str); else printf("%s\n", str); } static void print_value(int i, void *p, struct datatab *tab, const char *sep) { int mode = 0; u_int32_t a = 0, b = 0, c = 0, bytes = 0, mbytes = 0, gbytes = 0; double percent; char *cp = p; char *tmp = NULL; time_t tm = 0; size_t sz = 0; DB_LSN *dl = NULL; char buf[SMBUF]; switch(tab[i].type) { case INT32: /* 'a' points to u_int32_t */ memcpy(&a, cp + tab[i].a, sizeof(a)); printf("%16u%s", a, sep); break; case HEX32: /* 'a' printed in hex */ memcpy(&a, cp + tab[i].a, sizeof(a)); printf("%16x%s", a, sep); break; case DIFF32: /* 'a' - 'b' - 'c' */ memcpy(&a, cp + tab[i].a, sizeof(a)); memcpy(&b, cp + tab[i].b, sizeof(b)); if (tab[i].c != -1) { memcpy(&c, cp + tab[i].c, sizeof(c)); printf("%16d%s", a - b - c, sep); } else { printf("%16d%s", a - b, sep); } break; case PCT32: /* 100 * 'a' / ('a' + 'b') */ memcpy(&a, cp + tab[i].a, sizeof(a)); memcpy(&b, cp + tab[i].b, sizeof(b)); printf("%16.0f%s%% ", (double) a / (a + b) * 100.0, sep); break; case FF: /* 'a' = freebytes, 'b' = npages, 'c' = pagesize */ memcpy(&a, cp + tab[i].a, sizeof(a)); memcpy(&b, cp + tab[i].b, sizeof(b)); memcpy(&c, cp + tab[i].c, sizeof(c)); if (b == 0) { printf("%16.0f%s%% ", 0.0, sep); } else { percent = (double) ((b * c) - a) / (b * c) * 100; printf("%16.0f%s%% ", percent, sep); } break; case BYTES: /* 'a' = bytes, 'b' = mbytes, 'c' = gbytes */ if (tab[i].a != -1) memcpy(&bytes, cp + tab[i].a, sizeof(bytes)); else bytes = 0; if (tab[i].b != -1) memcpy(&mbytes, cp + tab[i].b, sizeof(mbytes)); else mbytes = 0; if (tab[i].c != -1) memcpy(&gbytes, cp + tab[i].c, sizeof(gbytes)); else gbytes = 0; if (gbytes > 0 || mbytes > 0) { mbytes += gbytes * 1024; if (bytes > (1024*1024)) mbytes += bytes / (1024*1024); printf("%16u%sMB", mbytes, sep); } else { printf("%16u%s", bytes, sep); } break; case MODE: /* 'a' points to int, printed as octal mode */ memcpy(&mode, cp + tab[i].a, sizeof(mode)); printf(" %04o%s", mode, sep); break; case TIME: /* 'a' points to time_t, printed as date/time */ memcpy(&tm, cp + tab[i].a, sizeof(tm)); if (tm == 0) { printf("%16s%s", "none", sep); } else { strftime(buf, SMBUF, "%Y-%m-%d %T %Z", localtime(&tm)); printf("%16s%s", buf, sep); } break; case LSN: /* 'a' points to DB_LSN */ dl = (DB_LSN *)(cp + tab[i].a); if (dl->file == 0) { printf("%16s%s", "none", sep); } else { snprintf(buf, sizeof(buf), "%u/%u", dl->file, dl->offset); printf("%16s%s", buf, sep); } break; case STR: /* 'a' points to char* */ memcpy(&tmp, cp + tab[i].a, sizeof(tmp)); printf("%16s%s", tmp, sep); break; case SIZE: /* 'a' points to size_t */ memcpy(&sz, cp + tab[i].a, sizeof(sz)); printf("%16lu%s", (unsigned long) sz, sep); break; case END: break; } } static char *myctime(time_t *tm) { static char val[SMBUF]; strftime(val, SMBUF, "%Y-%m-%d %T %Z", localtime(tm)); return val; } static void display_data(void *p, struct datatab *tab) { int i; if (html) puts(""); for (i = 0; tab[i].type != END; i++) { if (html) { printf("
"); print_value(i, p, tab, ""); printf("%s\n", tab[i].desc); } else { print_value(i, p, tab, ""); printf(" %s\n", tab[i].desc); } } if (html) puts("

"); } static void start_table(const char *label, struct datatab *tab) { int i; if(html) { printf("

%s

\n", label); puts("\n"); for(i = 0; tab[i].type != END; i++) printf(""); for (i = 0; tab[i].type != END; i++) { printf("
%s\n", tab[i].desc); } } static void display_row(void *p, struct datatab *tab) { int i; if (html) { puts("
"); print_value(i, p, tab, ""); printf("\n"); } } else { puts("---------------------------------------------"); display_data(p, tab); } } static void end_table(void) { if(html) puts("

"); } #define OFFSETOF(type, f) ((char *)&(((type *)0)->f) - (char *)0) #define F(f) OFFSETOF(DB_LOCK_STAT, f) static struct datatab LOCK_tab[] = { { INT32, F(st_id), -1, -1, "Last allocated locker ID" }, { INT32, F(st_maxlocks), -1, -1, "Maximum number of locks possible" }, { INT32, F(st_maxlockers), -1, -1, "Maximum number of lockers possible" }, { INT32, F(st_maxobjects), -1, -1, "Maximum number of objects possible" }, { INT32, F(st_nmodes), -1, -1, "Lock modes" }, { INT32, F(st_nlocks), -1, -1, "Current locks" }, { INT32, F(st_maxnlocks), -1, -1, "Maximum locks" }, { INT32, F(st_nlockers), -1, -1, "Current lockers" }, { INT32, F(st_maxnlockers), -1, -1, "Maximum lockers" }, { INT32, F(st_nobjects), -1, -1, "Current objects" }, { INT32, F(st_maxnobjects), -1, -1, "Maximum objects" }, { INT32, F(st_lock_wait), -1, -1, "Lock conflicts" }, { INT32, F(st_nrequests), -1, -1, "Lock requests" }, { INT32, F(st_nreleases), -1, -1, "Lock releases" }, { DIFF32, F(st_nrequests), F(st_nreleases), F(st_ndeadlocks), "Outstanding locks" }, { INT32, F(st_lock_nowait), -1, -1, "Lock conflicts w/o subsequent wait" }, { INT32, F(st_ndeadlocks), -1, -1, "Deadlocks" }, { INT32, F(st_nlocktimeouts), -1, -1, "Lock timeouts" }, { INT32, F(st_ntxntimeouts), -1, -1, "Transaction timeouts" }, { INT32, F(st_region_nowait), -1, -1, "Region locks granted without waiting" }, { INT32, F(st_region_wait), -1, -1, "Region locks granted after waiting" }, { BYTES, F(st_regsize), -1, -1, "Lock region size" }, { END, -1, -1, -1, NULL } }; static int display_lock(void) { DB_LOCK_STAT *sp; if(OVDBenv->lock_stat(OVDBenv, &sp, 0) != 0) return 1; display_heading("Lock Region Statistics"); display_data(sp, LOCK_tab); free(sp); return 0; } #undef F #define F(f) OFFSETOF(DB_LOG_STAT, f) static struct datatab LOG_tab[] = { { HEX32, F(st_magic), -1, -1, "Log magic number" }, { INT32, F(st_version), -1, -1, "Log version number" }, { MODE, F(st_mode), -1, -1, "Log file mode" }, { BYTES, F(st_lg_bsize), -1, -1, "Log record cache size" }, { BYTES, F(st_lg_size), -1, -1, "The current log file size" }, { BYTES, F(st_w_bytes), F(st_w_mbytes), -1, "Log bytes written" }, { BYTES, F(st_wc_bytes), F(st_wc_mbytes), -1, "Log bytes written since last checkpoint" }, { INT32, F(st_wcount), -1, -1, "Total log writes" }, { INT32, F(st_wcount_fill), -1, -1, "Total log writes due to overflow" }, { INT32, F(st_scount), -1, -1, "Total log flushes" }, { INT32, F(st_region_nowait), -1, -1, "Region locks granted without waiting" }, { INT32, F(st_region_wait), -1, -1, "Region locks granted after waiting" }, { INT32, F(st_cur_file), -1, -1, "Current log file number" }, { INT32, F(st_cur_offset), -1, -1, "Current log file offset" }, { INT32, F(st_disk_file), -1, -1, "Known on disk log file number" }, { INT32, F(st_disk_offset), -1, -1, "Known on disk log file offset" }, { BYTES, F(st_regsize), -1, -1, "Log region size" }, { INT32, F(st_maxcommitperflush), -1, -1, "Max number of commits in a flush"}, { INT32, F(st_mincommitperflush), -1, -1, "Min number of commits in a flush"}, { END, -1, -1, -1, NULL } }; static int display_log(void) { DB_LOG_STAT *sp; if(OVDBenv->log_stat(OVDBenv, &sp, 0) != 0) return 1; display_heading("Log Region Statistics"); display_data(sp, LOG_tab); free(sp); return 0; } #undef F #define F(f) OFFSETOF(DB_MPOOL_STAT, f) static struct datatab MEM_tab[] = { { INT32, F(st_cache_hit), -1, -1, "Cache hits"}, { INT32, F(st_cache_miss), -1, -1, "Cache misses"}, { PCT32, F(st_cache_hit), F(st_cache_miss), -1, "Cache hit percentage"}, { BYTES, F(st_bytes), -1, F(st_gbytes), "Total cache size"}, { INT32, F(st_ncache), -1, -1, "Number of caches"}, { INT32, F(st_regsize), -1, -1, "Pool individual cache size"}, { INT32, F(st_map), -1, -1, "Memory mapped pages"}, { INT32, F(st_page_create), -1, -1, "Pages created in the cache"}, { INT32, F(st_page_in), -1, -1, "Pages read into the cache"}, { INT32, F(st_page_out), -1, -1, "Pages written from the cache to the backing file"}, { INT32, F(st_ro_evict), -1, -1, "Clean pages forced from the cache"}, { INT32, F(st_rw_evict), -1, -1, "Dirty pages forced from the cache"}, { INT32, F(st_hash_buckets), -1, -1, "Hash buckets used for page location"}, { INT32, F(st_hash_searches), -1, -1, "Total hash chain searches"}, { INT32, F(st_hash_longest), -1, -1, "Longest hash chain searched"}, { INT32, F(st_hash_examined), -1, -1, "Total hash entries searched"}, { INT32, F(st_page_trickle), -1, -1, "Dirty buffers written by trickle-sync thread"}, { INT32, F(st_page_clean), -1, -1, "Current clean buffer count"}, { INT32, F(st_page_dirty), -1, -1, "Current dirty buffer count"}, { INT32, F(st_region_nowait), -1, -1, "Region locks granted without waiting"}, { INT32, F(st_region_wait), -1, -1, "Region locks granted after waiting"}, { END, -1, -1, -1, NULL } }; #undef F #define F(f) OFFSETOF(DB_MPOOL_FSTAT, f) static struct datatab MEMF_tab[] = { { STR, F(file_name), -1, -1, "Database"}, { SIZE, F(st_pagesize), -1, -1, "Page size"}, { INT32, F(st_cache_hit), -1, -1, "Cache hits"}, { INT32, F(st_cache_miss), -1, -1, "Cache misses"}, { PCT32, F(st_cache_hit), F(st_cache_miss), -1, "Cache hit percentage"}, { INT32, F(st_map), -1, -1, "Memory mapped pages"}, { INT32, F(st_page_create), -1, -1, "Pages created in the cache"}, { INT32, F(st_page_in), -1, -1, "Pages read into the cache"}, { INT32, F(st_page_out), -1, -1, "Pages written from the cache to the backing file"}, { END, -1, -1, -1, NULL } }; static int display_mem(int all) { DB_MPOOL_FSTAT **fsp; DB_MPOOL_STAT *gsp; if(OVDBenv->memp_stat(OVDBenv, &gsp, &fsp, 0) != 0) return 1; display_heading("Memory Pool Statistics"); display_data(gsp, MEM_tab); if(all) { DB_MPOOL_FSTAT **p = fsp; start_table("Per-database Memory Pool Statistics", MEMF_tab); for(; p != NULL && *p != NULL; ++p) { display_row(*p, MEMF_tab); } end_table(); } free(fsp); free(gsp); return 0; } static int txn_compare(const void *a, const void *b) { if (((const DB_TXN_ACTIVE *)a)->txnid > ((const DB_TXN_ACTIVE *)b)->txnid) return 1; if (((const DB_TXN_ACTIVE *)a)->txnid < ((const DB_TXN_ACTIVE *)b)->txnid) return -1; return 0; } #undef F #define F(f) OFFSETOF(DB_TXN_STAT, f) static struct datatab TXN_tab[] = { { LSN, F(st_last_ckp), -1, -1, "File/offset for last checkpoint LSN" }, { TIME, F(st_time_ckp), -1, -1, "Checkpoint timestamp" }, { HEX32, F(st_last_txnid), -1, -1, "Last transaction ID allocated" }, { INT32, F(st_maxtxns), -1, -1, "Maximum active transactions possible" }, { INT32, F(st_nactive), -1, -1, "Active transactions" }, { INT32, F(st_nrestores), -1, -1, "Restored transactions after recovery" }, { INT32, F(st_maxnactive), -1, -1, "Maximum active transactions" }, { INT32, F(st_nbegins), -1, -1, "Transactions started" }, { INT32, F(st_ncommits), -1, -1, "Transactions committed" }, { INT32, F(st_naborts), -1, -1, "Transactions aborted" }, { INT32, F(st_region_nowait), -1, -1, "Region locks granted without waiting"}, { INT32, F(st_region_wait), -1, -1, "Region locks granted after waiting"}, { BYTES, F(st_regsize), -1, -1, "Transaction region size" }, { END, -1, -1, -1, NULL } }; #undef F #define F(f) OFFSETOF(DB_TXN_ACTIVE, f) static struct datatab TXNA_tab[] = { { INT32, F(txnid), -1, -1, "Transaction ID" }, { INT32, F(parentid), -1, -1, "Parent Transaction ID" }, { LSN, F(lsn), -1, -1, "Initial LSN file/offset" }, { END, -1, -1, -1, NULL } }; static int display_txn(void) { DB_TXN_STAT *sp; u_int32_t i; if(OVDBenv->txn_stat(OVDBenv, &sp, 0) != 0) return 1; display_heading("Transaction Region Statistics"); display_data(sp, TXN_tab); if(sp->st_nactive) { qsort(sp->st_txnarray, sp->st_nactive, sizeof(sp->st_txnarray[0]), txn_compare); start_table("Active Transactions", TXNA_tab); for (i = 0; i < sp->st_nactive; ++i) display_row(&(sp->st_txnarray[i]), TXNA_tab); end_table(); } free(sp); return 0; } static int display_ver(void) { if(html) puts("

"); printf("ovdb data version: %d\n", ovdb_data_ver); if(html) puts("
"); printf("Berkeley DB version: %s\n", db_version(NULL,NULL,NULL)); if(html) puts("

"); return 0; } #undef F #define F(f) OFFSETOF(DB_BTREE_STAT, f) static struct datatab BTREE_tab[] = { { HEX32, F(bt_magic), -1, -1, "Btree magic number" }, { INT32, F(bt_version), -1, -1, "Btree version number" }, { INT32, F(bt_minkey), -1, -1, "Minimum keys per page (minkey)" }, { INT32, F(bt_pagesize), -1, -1, "Database page size" }, { INT32, F(bt_levels), -1, -1, "Levels in the tree" }, { INT32, F(bt_nkeys), -1, -1, "Unique keys in the tree" }, { INT32, F(bt_ndata), -1, -1, "Data items in the tree" }, { INT32, F(bt_int_pg), -1, -1, "Tree internal pages" }, { BYTES, F(bt_int_pgfree), -1, -1, "Bytes free in internal pages" }, { FF, F(bt_int_pgfree), F(bt_int_pg), F(bt_pagesize), "Internal page fill factor" }, { INT32, F(bt_leaf_pg), -1, -1, "Tree leaf pages" }, { BYTES, F(bt_leaf_pgfree), -1, -1, "Bytes free in leaf pages" }, { FF, F(bt_leaf_pgfree), F(bt_leaf_pg), F(bt_pagesize), "Leaf page fill factor" }, { INT32, F(bt_dup_pg), -1, -1, "Tree duplicate pages" }, { BYTES, F(bt_dup_pgfree), -1, -1, "Bytes free in duplicate pages" }, { FF, F(bt_dup_pgfree), F(bt_dup_pg), F(bt_pagesize), "Duplicate page fill factor" }, { INT32, F(bt_over_pg), -1, -1, "Tree overflow pages" }, { BYTES, F(bt_over_pgfree), -1, -1, "Bytes free overflow pages" }, { FF, F(bt_over_pgfree), F(bt_over_pg), F(bt_pagesize), "Overflow page fill factor" }, { INT32, F(bt_free), -1, -1, "Pages on the free list" }, { END, -1, -1, -1, NULL } }; static int display_btree(DB *db) { DB_BTREE_STAT *sp; if(db->stat(db, NULL, &sp, 0)) return 1; display_heading("Btree Statistics"); display_data(sp, BTREE_tab); free(sp); return 0; } #undef F #define F(f) OFFSETOF(DB_HASH_STAT, f) static struct datatab HASH_tab[] = { { HEX32, F(hash_magic), -1, -1, "Hash magic number" }, { INT32, F(hash_version), -1, -1, "Hash version number" }, { INT32, F(hash_pagesize), -1, -1, "Database page size" }, { INT32, F(hash_nkeys), -1, -1, "Keys in the database" }, { INT32, F(hash_ndata), -1, -1, "Data items in the database" }, { INT32, F(hash_buckets), -1, -1, "Hash buckets" }, { BYTES, F(hash_bfree), -1, -1, "Bytes free on bucket pages" }, { FF, F(hash_buckets), F(hash_bfree), F(hash_pagesize), "Bucket page fill factor" }, { INT32, F(hash_bigpages), -1, -1, "Overflow pages" }, { BYTES, F(hash_big_bfree), -1, -1, "Bytes free on Overflow pages" }, { FF, F(hash_bigpages), F(hash_big_bfree), F(hash_pagesize), "Overflow page fill factor" }, { INT32, F(hash_overflows), -1, -1, "Bucket overflow pages" }, { BYTES, F(hash_ovfl_free), -1, -1, "Bytes free on bucket overflow pages" }, { FF, F(hash_overflows), F(hash_ovfl_free), F(hash_pagesize), "Bucket overflow page fill factor" }, { INT32, F(hash_dup), -1, -1, "Duplicate pages" }, { BYTES, F(hash_dup_free), -1, -1, "Bytes free in duplicate pages" }, { FF, F(hash_dup), F(hash_dup_free), F(hash_pagesize), "Duplicate page fill factor" }, { INT32, F(hash_free), -1, -1, "Pages on the free list"}, { END, -1, -1, -1, NULL } }; static int display_hash(DB *db UNUSED) { DB_HASH_STAT *sp; if(db->stat(db, NULL, &sp, 0)) return 1; display_heading("Hash Information"); display_data(sp, HASH_tab); return 0; } static int display_db(char *dbfile) { int ret; DB *db; if(db_create(&db, OVDBenv, 0)) return 1; if(db->open(db, NULL, dbfile, NULL, DB_UNKNOWN, DB_RDONLY, 0)) return 1; switch(db->type) { case DB_BTREE: case DB_RECNO: ret = display_btree(db); break; case DB_HASH: ret = display_hash(db); break; default: ret = 1; break; } db->close(db, 0); return ret; } static int parse_artrange(char *str, ARTNUM *start, ARTNUM *stop) { char *c; int i; c = strchr(str, '-'); if(c == NULL) { i = atoi(str); if(i == 0) { return 1; } *start = *stop = i; return 0; } if(c == str) { *start = 0; *stop = atoi(str+1); return (*stop == 0); } if (strlen(str) == (size_t)(c - str + 1)) { *start = atoi(str); *stop = 0xffffffff; return (*start == 0); } *start = atoi(str); *stop = atoi(c+1); if(*start == 0 || *stop == 0 || *start > *stop) return 1; return 0; } static void htwrite(char *data, int len) { int i; for(i = 0; i < len; i++) { switch(data[i]) { case '<': case '>': case '&': printf("&#%d;", (int)data[i]); break; default: putchar(data[i]); } } } int main(int argc, char *argv[]) { void *s; ARTNUM a, start=0, stop=0, low, high; char *data, *disp_db = NULL; int len, c, count, flag, lowi, highi; int getgs=0, getcount=0, getinfo=0, err=0, gotone=0; int disp_lock=0, disp_log=0, disp_mem=0, disp_mem_all=0, disp_txn=0, disp_ver=0; int needng=0, o; openlog("ovdb_stat", L_OPENLOG_FLAGS | LOG_PID, LOG_INN_PROG); message_program_name = "ovdb_stat"; if (!innconf_read(NULL)) exit(1); if(!ovdb_check_user()) die("command must be run as runasuser user"); if(!ovdb_getlock(OVDB_LOCK_ADMIN)) sysdie("cannot lock database"); if(!ovdb_open(OV_READ|OVDB_SERVER)) sysdie("cannot open overview; check syslog for OVDB messages"); xsignal(SIGINT, sigfunc); xsignal(SIGTERM, sigfunc); xsignal(SIGHUP, sigfunc); while((c = getopt(argc, argv, ":Hgcir:klmMtvd:")) != -1) { switch(c) { case 'H': html = 1; break; case 'g': getgs = 1; needng = 1; gotone++; break; case 'c': getcount = 1; needng = 1; gotone++; break; case 'i': getinfo = 1; needng = 1; gotone++; break; case 'r': if(parse_artrange(optarg, &start, &stop)) err++; needng = 1; gotone++; break; case 'k': disp_lock = 1; gotone++; break; case 'l': disp_log = 1; gotone++; break; case 'm': disp_mem = 1; gotone++; break; case 'M': disp_mem = 1; disp_mem_all = 1; gotone++; break; case 't': disp_txn = 1; gotone++; break; case 'v': disp_ver = 1; gotone++; break; case 'd': disp_db = optarg; gotone++; break; case ':': warn("option -%c requires an argument", optopt); err++; break; case '?': warn("unrecognized option -%c", optopt); err++; break; } } if(!gotone) { err++; } else if(optind == argc && needng) { warn("missing newsgroup argument(s)"); err++; } if(err) { fprintf(stderr, "\ Usage:\n\ ovdb_stat -Hgci [-r artnum] newsgroup [newsgroup ...]\n\ -H : output in HTML\n\ -g : show groupstats info\n\ -c : show groupstats info by counting actual records\n\ -i : show additional group info\n\ -r artnum-range : retrieve OV records for article number range\n\ \n\ ovdb_stat -Hklmtv [-d ]\n\ -H : output in HTML\n\ -k : Display lock region statistics\n\ -l : Display log region statistics\n\ -m : Display global memory cache statistics\n\ -M : Display all memory cache statistics\n\ -t : Display transaction statistics\n\ -v : Display version information\n\ -d database : Display statistics of specified database\n"); goto out; } if(html) puts("ovdb_stat

"); if(disp_lock) display_lock(); if(disp_log) display_log(); if(disp_mem) display_mem(disp_mem_all); if(disp_txn) display_txn(); if(disp_ver) display_ver(); if(disp_db) display_db(disp_db); if(getgs || getcount || getinfo) { if(html) { puts("\n"); puts(""); if(getgs) puts("
Group"); if(getgs) puts("Groupstats"); if(getcount) puts("Counted"); if(getinfo) puts("StatusCurrentPending"); puts("ExpiredExpire PID
LowHighCountFlag"); if(getcount) puts("LowHighCount"); if(getinfo) puts("FlagsGroupIDDBGroupIDDB"); } for(o = optind ; o < argc; o++) { if(html) printf("
%s", argv[o]); if(getgs) { if(ovdb_groupstats(argv[o], &lowi, &highi, &count, &flag)) { if(html) printf("%d%d%d%c", lowi, highi, count, flag); else printf("%s: groupstats: low: %d, high: %d, count: %d, flag: %c\n", argv[o], lowi, highi, count, flag); } } if(getcount) { low = high = count = 0; s = ovdb_opensearch(argv[o], 1, 0xffffffff); if (s != NULL) { while(ovdb_search(s, &a, NULL, NULL, NULL, NULL)) { if(low == 0 || a < low) low = a; if(a > high) high = a; count++; if(signalled) break; } ovdb_closesearch(s); if(signalled) goto out; if(html) printf("%ld%ld%d", low, high, count); else printf("%s: counted: low: %ld, high: %ld, count: %d\n", argv[o], low, high, count); } } if(getinfo) { int ret; struct groupinfo gi; ret = ovdb_getgroupinfo(argv[o], &gi, false, NULL, 0); if (ret != 0) { warn("%s: ovdb_getgroupinfo error: %s", argv[o], db_strerror(ret)); continue; } if(html) { printf("%s%s%s%s", (gi.status & GROUPINFO_DELETED) ? "D ":"", (gi.status & GROUPINFO_EXPIRING) ? "E ":"", (gi.status & GROUPINFO_MOVING) ? "M":"", (gi.status == 0) ? " ":""); printf("%dov%05d", gi.current_gid, gi.current_db); if(gi.status & GROUPINFO_MOVING) printf("%dov%05d", gi.new_gid, gi.new_db); else printf("  "); if(gi.expired) printf("%s%lu", myctime(&gi.expired), (unsigned long) gi.expiregrouppid); else printf("  "); putchar('\n'); } else { printf("%s: flags: %s%s%s%s\n", argv[o], (gi.status & GROUPINFO_DELETED) ? "DELETED ":"", (gi.status & GROUPINFO_EXPIRING) ? "EXPIRING ":"", (gi.status & GROUPINFO_MOVING) ? "MOVING":"", (gi.status == 0) ? "none":""); printf("%s: gid: %d; Stored in: ov%05d\n", argv[o], gi.current_gid, gi.current_db); if(gi.status & GROUPINFO_MOVING) printf("%s: pending gid: %d; pending db: ov%05d\n", argv[o], gi.new_gid, gi.new_db); if(gi.expired) { printf("%s: last expired: %s\n", argv[o], myctime(&gi.expired)); printf("%s: by process id: %lu\n", argv[o], (unsigned long) gi.expiregrouppid); } } } if(signalled) goto out; } if(html) puts("

"); } if(start || stop) { if(html) puts("

");
	for(o = optind ; o < argc; o++) {
            s = ovdb_opensearch(argv[o], start, stop);
	    if (s != NULL) {
		while(ovdb_search(s, &a, &data, &len, NULL, NULL)) {
		    if(html)
			htwrite(data, len);
		    else
			fwrite(data, len, 1, stdout);
		    if(signalled)
			break;
		}
		ovdb_closesearch(s);
		if(signalled)
		    goto out;
	    }
	    if(signalled)
		goto out;
	}
	if(html)
	    puts("
"); } out: if(html) puts("

"); ovdb_close(); return 0; } #endif /* HAVE_BDB */ inn-2.6.0/history/0000755000175200017520000000000012575023700013417 5ustar iuliusiuliusinn-2.6.0/history/his.c0000644000175200017520000002605512575023702014360 0ustar iuliusiulius/* $Id: his.c 9748 2014-11-23 21:37:46Z iulius $ ** ** API to history routines ** ** Copyright (c) 2001, Thus plc ** ** Redistribution and use of the source code in source and binary ** forms, with or without modification, are permitted provided that ** the following 3 conditions are met: ** ** 1. Redistributions of the source code must retain the above ** copyright notice, this list of conditions and the disclaimer ** set out below. ** ** 2. Redistributions of the source code in binary form must ** reproduce the above copyright notice, this list of conditions ** and the disclaimer set out below in the documentation and/or ** other materials provided with the distribution. ** ** 3. Neither the name of the Thus plc nor the names of its ** contributors may be used to endorse or promote products ** derived from this software without specific prior written ** permission from Thus plc. ** ** Disclaimer: ** ** "THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE DIRECTORS ** OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." */ #include "config.h" #include "clibrary.h" #include #include #ifdef HAVE_SYS_TIME_H # include #endif #include #include "inn/history.h" #include "inn/messages.h" #include "inn/timer.h" #include "inn/libinn.h" #include "inn/storage.h" #include "hisinterface.h" #include "hismethods.h" struct hiscache { HASH Hash; /* Hash value of the message-id using Hash() */ bool Found; /* Whether this entry is in the dbz file yet */ }; struct history { struct hismethod *methods; void *sub; struct hiscache *cache; size_t cachesize; const char *error; struct histstats stats; }; enum HISRESULT {HIScachehit, HIScachemiss, HIScachedne}; static const struct histstats nullhist = { 0, 0, 0, 0 }; /* ** Put an entry into the history cache */ static void his_cacheadd(struct history *h, HASH MessageID, bool Found) { unsigned int i, loc; his_logger("HIScacheadd begin", S_HIScacheadd); if (h->cache != NULL) { memcpy(&loc, ((char *)&MessageID) + (sizeof(HASH) - sizeof(loc)), sizeof(loc)); i = loc % h->cachesize; memcpy((char *)&h->cache[i].Hash, (char *)&MessageID, sizeof(HASH)); h->cache[i].Found = Found; } his_logger("HIScacheadd end", S_HIScacheadd); } /* ** Lookup an entry in the history cache */ static enum HISRESULT his_cachelookup(struct history *h, HASH MessageID) { unsigned int i, loc; if (h->cache == NULL) return HIScachedne; his_logger("HIScachelookup begin", S_HIScachelookup); memcpy(&loc, ((char *)&MessageID) + (sizeof(HASH) - sizeof(loc)), sizeof(loc)); i = loc % h->cachesize; if (memcmp((char *)&h->cache[i].Hash, (char *)&MessageID, sizeof(HASH)) == 0) { his_logger("HIScachelookup end", S_HIScachelookup); if (h->cache[i].Found) { return HIScachehit; } else { return HIScachemiss; } } else { his_logger("HIScachelookup end", S_HIScachelookup); return HIScachedne; } } /* ** set error status to that indicated by s; doesn't copy the string, ** assumes the caller did that for us */ void his_seterror(struct history *h, const char *s) { if (h != NULL) { if (h->error) free((void *)h->error); h->error = s; } if (s != NULL) warn("%s", s); } struct history * HISopen(const char *path, const char *method, int flags) { struct history *h; int i; for (i = 0; i < NUM_HIS_METHODS; ++i) { if (strcmp(method, his_methods[i].name) == 0) break; } if (i == NUM_HIS_METHODS) { warn("`%s' isn't a valid history method", method); return NULL; } /* allocate up our structure and start subordinate history * manager */ h = xmalloc(sizeof *h); h->methods = &his_methods[i]; h->cache = NULL; h->error = NULL; h->cachesize = 0; h->stats = nullhist; h->sub = (*h->methods->open)(path, flags, h); if (h->sub == NULL) { free(h); h = NULL; } return h; } static bool his_checknull(struct history *h) { if (h != NULL) return false; errno = EBADF; return true; } bool HISclose(struct history *h) { bool r; if (his_checknull(h)) return false; r = (*h->methods->close)(h->sub); if (h->cache) { free(h->cache); h->cache = NULL; } if (h->error) { free((void *)h->error); h->error = NULL; } free(h); return r; } bool HISsync(struct history *h) { bool r = false; if (his_checknull(h)) return false; TMRstart(TMR_HISSYNC); r = (*h->methods->sync)(h->sub); TMRstop(TMR_HISSYNC); return r; } bool HISlookup(struct history *h, const char *key, time_t *arrived, time_t *posted, time_t *expires, TOKEN *token) { bool r; if (his_checknull(h)) return false; TMRstart(TMR_HISGREP); r = (*h->methods->lookup)(h->sub, key, arrived, posted, expires, token); TMRstop(TMR_HISGREP); return r; } bool HIScheck(struct history *h, const char *key) { bool r = false; HASH hash; if (his_checknull(h)) return false; TMRstart(TMR_HISHAVE); hash = HashMessageID(key); switch (his_cachelookup(h, hash)) { case HIScachehit: h->stats.hitpos++; r = true; break; case HIScachemiss: h->stats.hitneg++; r = false; break; case HIScachedne: r = (*h->methods->check)(h->sub, key); his_cacheadd(h, hash, r); if (r) h->stats.misses++; else h->stats.dne++; break; } TMRstop(TMR_HISHAVE); return r; } bool HISwrite(struct history *h, const char *key, time_t arrived, time_t posted, time_t expires, const TOKEN *token) { bool r; if (his_checknull(h)) return false; TMRstart(TMR_HISWRITE); r = (*h->methods->write)(h->sub, key, arrived, posted, expires, token); if (r == true) { HASH hash; /* if we successfully wrote it, add it to the cache */ hash = HashMessageID(key); his_cacheadd(h, hash, true); } TMRstop(TMR_HISWRITE); return r; } bool HISremember(struct history *h, const char *key, time_t arrived, time_t posted) { bool r; if (his_checknull(h)) return false; TMRstart(TMR_HISWRITE); r = (*h->methods->remember)(h->sub, key, arrived, posted); if (r == true) { HASH hash; /* if we successfully wrote it, add it to the cache */ hash = HashMessageID(key); his_cacheadd(h, hash, true); } TMRstop(TMR_HISWRITE); return r; } bool HISreplace(struct history *h, const char *key, time_t arrived, time_t posted, time_t expires, const TOKEN *token) { bool r; if (his_checknull(h)) return false; r = (*h->methods->replace)(h->sub, key, arrived, posted, expires, token); if (r == true) { HASH hash; /* if we successfully wrote it, add it to the cache */ hash = HashMessageID(key); his_cacheadd(h, hash, true); } return r; } bool HISwalk(struct history *h, const char *reason, void *cookie, bool (*callback)(void *, time_t, time_t, time_t, const TOKEN *)) { bool r; if (his_checknull(h)) return false; r = (*h->methods->walk)(h->sub, reason, cookie, callback); return r; } bool HISexpire(struct history *h, const char *path, const char *reason, bool writing, void *cookie, time_t threshold, bool (*exists)(void *, time_t, time_t, time_t, TOKEN *)) { bool r; if (his_checknull(h)) return false; r = (*h->methods->expire)(h->sub, path, reason, writing, cookie, threshold, exists); return r; } void HISsetcache(struct history *h, size_t size) { if (h == NULL) return; if (h->cache) { free(h->cache); h->cache = NULL; } h->cachesize = size / sizeof(struct hiscache); if (h->cachesize != 0) h->cache = xcalloc(h->cachesize, sizeof(struct hiscache)); h->stats = nullhist; } /* ** return current history cache stats and zero the counters */ struct histstats HISstats(struct history *h) { struct histstats r; if (h == NULL) return nullhist; r = h->stats; h->stats = nullhist; return r; } /* ** return current error status to caller */ const char * HISerror(struct history *h) { if (h == NULL) return NULL; return h->error; } /* ** control interface to underlying method */ bool HISctl(struct history *h, int selector, void *val) { bool r; if (his_checknull(h)) return false; r = (*h->methods->ctl)(h->sub, selector, val); return r; } /* ** This code doesn't fit well with the generic history API, it really ** needs migrating to use the new nested timers */ FILE *HISfdlog = NULL; /* filehandle for history logging purpose */ static struct timeval HISstat_start[S_HIS_MAX]; static struct timeval HISstat_total[S_HIS_MAX]; static unsigned long HISstat_count[S_HIS_MAX]; void HISlogclose(void) { if (HISfdlog != NULL) Fclose(HISfdlog); HISfdlog = NULL; } void HISlogto(const char *s) { int i; HISlogclose(); if ((HISfdlog = Fopen(s, "a", INND_HISLOG)) == NULL) syswarn("cant open %s", s); /* initialize our counters */ for (i = 0; i < S_HIS_MAX; i++) { HISstat_start[i].tv_sec = 0; HISstat_start[i].tv_usec = 0; HISstat_total[i].tv_sec = 0; HISstat_total[i].tv_usec = 0; HISstat_count[i] = 0; } } void his_logger(const char *s, int code) { struct timeval tv; struct tm *tm; time_t t; if (HISfdlog == NULL) /* do nothing unless HISlogto() has been called */ return; gettimeofday(&tv, NULL); t = tv.tv_sec; tm = localtime(&t); if (HISstat_start[code].tv_sec != 0) { fprintf(HISfdlog, "%d/%d/%d %02d:%02d:%02d.%06d: [%d] %s (%.6f)\n", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, (int)tv.tv_usec, code, s, (float) tv.tv_sec + (float) tv.tv_usec / 1000000 - (float) HISstat_start[code].tv_sec - (float) HISstat_start[code].tv_usec / 1000000); if (tv.tv_usec < HISstat_start[code].tv_usec) { HISstat_total[code].tv_sec++; HISstat_total[code].tv_usec += tv.tv_usec - HISstat_start[code].tv_usec + 1000000; } else HISstat_total[code].tv_usec += tv.tv_usec - HISstat_start[code].tv_usec; HISstat_total[code].tv_sec += tv.tv_sec - HISstat_start[code].tv_sec; HISstat_count[code]++; HISstat_start[code].tv_sec = 0; HISstat_start[code].tv_usec = 0; } else { fprintf(HISfdlog, "%d/%d/%d %02d:%02d:%02d.%06d: [%d] %s\n", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, (int)tv.tv_usec, code, s); HISstat_start[code].tv_sec = tv.tv_sec; HISstat_start[code].tv_usec = tv.tv_usec; } } inn-2.6.0/history/Make.methods0000644000175200017520000000017312575023702015664 0ustar iuliusiulius# This file is automatically generated by buildconfig METHOD_SOURCES = hisv6/hisv6.c EXTRA_SOURCES = PROGRAMS = inn-2.6.0/history/hisinterface.h0000644000175200017520000000270112575023702016236 0ustar iuliusiulius/* $Id: hisinterface.h 8571 2009-08-17 19:10:07Z iulius $ ** ** Interface to history API modules */ #ifndef HISINTERFACE_H #define HISINTERFACE_H #include "config.h" #include struct token; struct histopts; struct history; typedef struct hismethod { const char *name; void *(*open)(const char *path, int flags, struct history *); bool (*close)(void *); bool (*sync)(void *); bool (*lookup)(void *, const char *, time_t *, time_t *, time_t *, struct token *); bool (*check)(void *, const char *); bool (*write)(void *, const char *, time_t, time_t, time_t, const struct token *); bool (*replace)(void *, const char *, time_t, time_t, time_t, const struct token *); bool (*expire)(void *, const char *, const char *, bool, void *, time_t, bool (*)(void *, time_t, time_t, time_t, struct token *)); bool (*walk)(void *, const char *, void *, bool (*)(void *, time_t, time_t, time_t, const struct token *)); bool (*remember)(void *, const char *, time_t, time_t); bool (*ctl)(void *, int, void *); } HIS_METHOD; /* subordinate history manager private methods */ void his_seterror(struct history *, const char *); enum { S_HIScacheadd, S_HIScachelookup, S_HISsetup, S_HISsync, S_HISlogstats, S_HISclose, S_HISfilesfor, S_HIShavearticle, S_HISwrite, S_HISremember, S_HIS_MAX }; /* fine grained history logging */ void his_logger(const char *s, int code); #endif inn-2.6.0/history/hismethods.h0000644000175200017520000000035112575023702015740 0ustar iuliusiulius/* This file is automatically generated by buildconfig */ #ifndef HISMETHODS_H #define HISMETHODS_H 1 #include "hisinterface.h" #define NUM_HIS_METHODS 1 extern HIS_METHOD his_methods[NUM_HIS_METHODS]; #endif /* HISMETHODS_H */ inn-2.6.0/history/hisv6/0000755000175200017520000000000012575023700014456 5ustar iuliusiuliusinn-2.6.0/history/hisv6/hisv6.c0000644000175200017520000010575312575023702015676 0ustar iuliusiulius/* $Id: hisv6.c 9659 2014-08-30 08:08:11Z iulius $ ** ** History v6 implementation against the history API. ** ** Copyright (c) 2001, Thus plc ** ** Redistribution and use of the source code in source and binary ** forms, with or without modification, are permitted provided that ** the following 3 conditions are met: ** ** 1. Redistributions of the source code must retain the above ** copyright notice, this list of conditions and the disclaimer ** set out below. ** ** 2. Redistributions of the source code in binary form must ** reproduce the above copyright notice, this list of conditions ** and the disclaimer set out below in the documentation and/or ** other materials provided with the distribution. ** ** 3. Neither the name of the Thus plc nor the names of its ** contributors may be used to endorse or promote products ** derived from this software without specific prior written ** permission from Thus plc. ** ** Disclaimer: ** ** "THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE DIRECTORS ** OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." */ #include "config.h" #include "clibrary.h" #include #include #include #include "hisinterface.h" #include "hisv6.h" #include "hisv6-private.h" #include "inn/dbz.h" #include "inn/fdflag.h" #include "inn/innconf.h" #include "inn/timer.h" #include "inn/qio.h" #include "inn/sequence.h" #include "inn/inndcomm.h" /* ** because we can only have one open dbz per process, we keep a ** pointer to which of the current history structures owns it */ static struct hisv6 *hisv6_dbzowner; /* ** set error status to that indicated by s; doesn't copy the string, ** assumes the caller did that for us */ static void hisv6_seterror(struct hisv6 *h, const char *s) { his_seterror(h->history, s); } /* ** format line or offset into a string for error reporting */ static void hisv6_errloc(char *s, size_t line, off_t offset) { if (offset != -1) { /* really we want an autoconf test for %ll/%L/%I64, sigh */ sprintf(s, "@%.0f", (double)offset); } else { sprintf(s, ":%lu", (unsigned long)line); } } /* ** split a history line into its constituent components; return a ** bitmap indicating which components we're returning are valid (or ** would be valid if a NULL pointer is passed for that component) or ** -1 for error. *error is set to a string which describes the ** failure. */ static int hisv6_splitline(const char *line, const char **error, HASH *hash, time_t *arrived, time_t *posted, time_t *expires, TOKEN *token) { const char *p = line; char *end; unsigned long l; int r = 0; /* parse the [...] hash field */ if (*p != '[') { *error = "`[' missing from history line"; return -1; } ++p; if (hash) *hash = TextToHash(p); p += 32; if (*p != ']') { *error = "`]' missing from history line"; return -1; } ++p; r |= HISV6_HAVE_HASH; if (*p != HISV6_FIELDSEP) { *error = "field separator missing from history line"; return -1; } /* parse the arrived field */ l = strtoul(p + 1, &end, 10); p = end; if (l == ULONG_MAX) { *error = "arrived timestamp out of range"; return -1; } r |= HISV6_HAVE_ARRIVED; if (arrived) *arrived = (time_t)l; if (*p != HISV6_SUBFIELDSEP) { /* no expires or posted time */ if (posted) *posted = 0; if (expires) *expires = 0; } else { /* parse out the expires field */ ++p; if (*p == HISV6_NOEXP) { ++p; if (expires) *expires = 0; } else { l = strtoul(p, &end, 10); p = end; if (l == ULONG_MAX) { *error = "expires timestamp out of range"; return -1; } r |= HISV6_HAVE_EXPIRES; if (expires) *expires = (time_t)l; } /* parse out the posted field */ if (*p != HISV6_SUBFIELDSEP) { /* no posted time */ if (posted) *posted = 0; } else { ++p; l = strtoul(p, &end, 10); p = end; if (l == ULONG_MAX) { *error = "posted timestamp out of range"; return -1; } r |= HISV6_HAVE_POSTED; if (posted) *posted = (time_t)l; } } /* parse the token */ if (*p == HISV6_FIELDSEP) ++p; else if (*p != '\0') { *error = "field separator missing from history line"; return -1; } /* IsToken false would imply a remembered line, or where someone's * used prunehistory */ if (IsToken(p)) { r |= HISV6_HAVE_TOKEN; if (token) *token = TextToToken(p); } return r; } /* ** Given the time, now, return the time at which we should next check ** the history file */ static unsigned long hisv6_nextcheck(struct hisv6 *h, unsigned long now) { return now + h->statinterval; } /* ** close any dbz structures associated with h; we also manage the ** single dbz instance voodoo */ static bool hisv6_dbzclose(struct hisv6 *h) { bool r = true; if (h == hisv6_dbzowner) { if (!hisv6_sync(h)) r = false; if (!dbzclose()) { hisv6_seterror(h, concat("can't dbzclose ", h->histpath, " ", strerror(errno), NULL)); r = false; } hisv6_dbzowner = NULL; } return r; } /* ** close an existing history structure, cleaning it to the point ** where we can reopon without leaking resources */ static bool hisv6_closefiles(struct hisv6 *h) { bool r = true; if (!hisv6_dbzclose(h)) r = false; if (h->readfd != -1) { if (close(h->readfd) != 0 && errno != EINTR) { hisv6_seterror(h, concat("can't close history ", h->histpath, " ", strerror(errno),NULL)); r = false; } h->readfd = -1; } if (h->writefp != NULL) { if (ferror(h->writefp) || fflush(h->writefp) == EOF) { hisv6_seterror(h, concat("error on history ", h->histpath, " ", strerror(errno), NULL)); r = false; } if (Fclose(h->writefp) == EOF) { hisv6_seterror(h, concat("can't fclose history ", h->histpath, " ", strerror(errno), NULL)); r = false; } h->writefp = NULL; h->offset = 0; } h->nextcheck = 0; h->st.st_ino = (ino_t)-1; h->st.st_dev = (dev_t)-1; return r; } /* ** Reopen (or open from fresh) a history structure; assumes the flags ** & path are all set up, ready to roll. If we don't own the dbz, we ** suppress the dbz code; this is needed during expiry (since the dbz ** code doesn't yet understand multiple open contexts... yes its a ** hack) */ static bool hisv6_reopen(struct hisv6 *h) { bool r = false; if (h->flags & HIS_RDWR) { const char *mode; if (h->flags & HIS_CREAT) mode = "w"; else mode = "r+"; if ((h->writefp = Fopen(h->histpath, mode, INND_HISTORY)) == NULL) { hisv6_seterror(h, concat("can't fopen history ", h->histpath, " ", strerror(errno), NULL)); hisv6_closefiles(h); goto fail; } if (fseeko(h->writefp, 0, SEEK_END) == -1) { hisv6_seterror(h, concat("can't fseek to end of ", h->histpath, " ", strerror(errno), NULL)); hisv6_closefiles(h); goto fail; } h->offset = ftello(h->writefp); if (h->offset == -1) { hisv6_seterror(h, concat("can't ftello ", h->histpath, " ", strerror(errno), NULL)); hisv6_closefiles(h); goto fail; } fdflag_close_exec(fileno(h->writefp), true); } /* Open the history file for reading. */ if ((h->readfd = open(h->histpath, O_RDONLY)) < 0) { hisv6_seterror(h, concat("can't open ", h->histpath, " ", strerror(errno), NULL)); hisv6_closefiles(h); goto fail; } fdflag_close_exec(h->readfd, true); /* if there's no current dbz owner, claim it here */ if (hisv6_dbzowner == NULL) { hisv6_dbzowner = h; } /* During expiry we need two history structures in place, so we have to select which one gets the dbz file */ if (h == hisv6_dbzowner) { dbzoptions opt; /* Open the DBZ file. */ dbzgetoptions(&opt); /* HIS_INCORE usually means we're rebuilding from scratch, so keep the whole lot in core until we flush */ if (h->flags & HIS_INCORE) { opt.writethrough = false; opt.pag_incore = INCORE_MEM; #ifndef DO_TAGGED_HASH opt.exists_incore = INCORE_MEM; #endif } else { opt.writethrough = true; #ifdef DO_TAGGED_HASH opt.pag_incore = INCORE_MMAP; #else /*opt.pag_incore = INCORE_NO;*/ opt.pag_incore = (h->flags & HIS_MMAP) ? INCORE_MMAP : INCORE_NO; opt.exists_incore = (h->flags & HIS_MMAP) ? INCORE_MMAP : INCORE_NO; # if defined(MMAP_NEEDS_MSYNC) && INND_DBZINCORE == 1 /* Systems that have MMAP_NEEDS_MSYNC defined will have their on-disk copies out of sync with the mmap'ed copies most of the time. So if innd is using INCORE_MMAP, then we force everything else to use it, too (unless we're on NFS) */ if(!innconf->nfsreader) { opt.pag_incore = INCORE_MMAP; opt.exists_incore = INCORE_MMAP; } # endif #endif } dbzsetoptions(opt); if (h->flags & HIS_CREAT) { size_t npairs; /* must only do this once! */ h->flags &= ~HIS_CREAT; npairs = (h->npairs == -1) ? 0 : h->npairs; if (!dbzfresh(h->histpath, dbzsize(npairs))) { hisv6_seterror(h, concat("can't dbzfresh ", h->histpath, " ", strerror(errno), NULL)); hisv6_closefiles(h); goto fail; } } else if (!dbzinit(h->histpath)) { hisv6_seterror(h, concat("can't dbzinit ", h->histpath, " ", strerror(errno), NULL)); hisv6_closefiles(h); goto fail; } } h->nextcheck = hisv6_nextcheck(h, TMRnow()); r = true; fail: return r; } /* ** check if the history file has changed, if so rotate to the new ** history file. Returns false on failure (which is probably fatal as ** we'll have closed the files) */ static bool hisv6_checkfiles(struct hisv6 *h) { unsigned long t = TMRnow(); if (h->statinterval == 0) return true; if (h->readfd == -1) { /* this can happen if a previous checkfiles() has failed to * reopen the handles, but our caller hasn't realised... */ hisv6_closefiles(h); if (!hisv6_reopen(h)) { hisv6_closefiles(h); return false; } } if (seq_lcompare(t, h->nextcheck) == 1) { struct stat st; if (stat(h->histpath, &st) == 0 && (st.st_ino != h->st.st_ino || st.st_dev != h->st.st_dev)) { /* there's a possible race on the history file here... */ hisv6_closefiles(h); if (!hisv6_reopen(h)) { hisv6_closefiles(h); return false; } h->st = st; } h->nextcheck = hisv6_nextcheck(h, t); } return true; } /* ** dispose (and clean up) an existing history structure */ static bool hisv6_dispose(struct hisv6 *h) { bool r; r = hisv6_closefiles(h); if (h->histpath) { free(h->histpath); h->histpath = NULL; } free(h); return r; } /* ** return a newly constructed, but empty, history structure */ static struct hisv6 * hisv6_new(const char *path, int flags, struct history *history) { struct hisv6 *h; h = xmalloc(sizeof *h); h->histpath = path ? xstrdup(path) : NULL; h->flags = flags; h->writefp = NULL; h->offset = 0; h->history = history; h->readfd = -1; h->nextcheck = 0; h->statinterval = 0; h->npairs = 0; h->dirty = 0; h->synccount = 0; h->st.st_ino = (ino_t)-1; h->st.st_dev = (dev_t)-1; return h; } /* ** open the history database identified by path in mode flags */ void * hisv6_open(const char *path, int flags, struct history *history) { struct hisv6 *h; his_logger("HISsetup begin", S_HISsetup); h = hisv6_new(path, flags, history); if (path) { if (!hisv6_reopen(h)) { hisv6_dispose(h); h = NULL; } } his_logger("HISsetup end", S_HISsetup); return h; } /* ** close and free a history handle */ bool hisv6_close(void *history) { struct hisv6 *h = history; bool r; his_logger("HISclose begin", S_HISclose); r = hisv6_dispose(h); his_logger("HISclose end", S_HISclose); return r; } /* ** synchronise any outstanding history changes to disk */ bool hisv6_sync(void *history) { struct hisv6 *h = history; bool r = true; if (h->writefp != NULL) { his_logger("HISsync begin", S_HISsync); if (fflush(h->writefp) == EOF) { hisv6_seterror(h, concat("error on history ", h->histpath, " ", strerror(errno), NULL)); r = false; } if (h->dirty && h == hisv6_dbzowner) { if (!dbzsync()) { hisv6_seterror(h, concat("can't dbzsync ", h->histpath, " ", strerror(errno), NULL)); r = false; } else { h->dirty = 0; } } his_logger("HISsync end", S_HISsync); } return r; } /* ** fetch the line associated with `hash' in the history database into ** buf; buf must be at least HISV6_MAXLINE+1 bytes. `poff' is filled ** with the offset of the line in the history file. */ static bool hisv6_fetchline(struct hisv6 *h, const HASH *hash, char *buf, off_t *poff) { off_t offset; bool r; if (h != hisv6_dbzowner) { hisv6_seterror(h, concat("dbz not open for this history file ", h->histpath, NULL)); return false; } if ((h->flags & (HIS_RDWR | HIS_INCORE)) == (HIS_RDWR | HIS_INCORE)) { /* need to fflush as we may be reading uncommitted data written via writefp */ if (fflush(h->writefp) == EOF) { hisv6_seterror(h, concat("error on history ", h->histpath, " ", strerror(errno), NULL)); r = false; goto fail; } } /* Get the seek value into the history file. */ errno = 0; r = dbzfetch(*hash, &offset); #ifdef ESTALE /* If your history is on NFS need to deal with stale NFS * handles */ if (!r && errno == ESTALE) { hisv6_closefiles(h); if (!hisv6_reopen(h)) { hisv6_closefiles(h); r = false; goto fail; } } #endif if (r) { ssize_t n; do { n = pread(h->readfd, buf, HISV6_MAXLINE, offset); #ifdef ESTALE if (n == -1 && errno == ESTALE) { hisv6_closefiles(h); if (!hisv6_reopen(h)) { hisv6_closefiles(h); r = false; goto fail; } } #endif } while (n == -1 && errno == EINTR); if (n >= HISV6_MINLINE) { char *p; buf[n] = '\0'; p = strchr(buf, '\n'); if (!p) { char location[HISV6_MAX_LOCATION]; hisv6_errloc(location, (size_t)-1, offset); hisv6_seterror(h, concat("can't locate end of line in history ", h->histpath, location, NULL)); r = false; } else { *p = '\0'; *poff = offset; r = true; } } else { char location[HISV6_MAX_LOCATION]; hisv6_errloc(location, (size_t)-1, offset); hisv6_seterror(h, concat("line too short in history ", h->histpath, location, NULL)); r = false; } } else { /* not found */ r = false; } fail: return r; } /* ** lookup up the entry `key' in the history database, returning ** arrived, posted and expires (for those which aren't NULL ** pointers), and any storage token associated with the entry. ** ** If any of arrived, posted or expires aren't available, return zero ** for that component. */ bool hisv6_lookup(void *history, const char *key, time_t *arrived, time_t *posted, time_t *expires, TOKEN *token) { struct hisv6 *h = history; HASH messageid; bool r; off_t offset; char buf[HISV6_MAXLINE + 1]; his_logger("HISfilesfor begin", S_HISfilesfor); hisv6_checkfiles(h); messageid = HashMessageID(key); r = hisv6_fetchline(h, &messageid, buf, &offset); if (r == true) { int status; const char *error; status = hisv6_splitline(buf, &error, NULL, arrived, posted, expires, token); if (status < 0) { char location[HISV6_MAX_LOCATION]; hisv6_errloc(location, (size_t)-1, offset); hisv6_seterror(h, concat(error, " ", h->histpath, location, NULL)); r = false; } else { /* if we have a token then we have the article */ r = !!(status & HISV6_HAVE_TOKEN); } } his_logger("HISfilesfor end", S_HISfilesfor); return r; } /* ** check `key' has been seen in this history database */ bool hisv6_check(void *history, const char *key) { struct hisv6 *h = history; bool r; HASH hash; if (h != hisv6_dbzowner) { hisv6_seterror(h, concat("dbz not open for this history file ", h->histpath, NULL)); return false; } his_logger("HIShavearticle begin", S_HIShavearticle); hisv6_checkfiles(h); hash = HashMessageID(key); r = dbzexists(hash); his_logger("HIShavearticle end", S_HIShavearticle); return r; } /* ** Format a history line. s should hold at least HISV6_MAXLINE + 1 ** characters (to allow for the nul). Returns the length of the data ** written, 0 if there was some error or if the data was too long to write. */ static int hisv6_formatline(char *s, const HASH *hash, time_t arrived, time_t posted, time_t expires, const TOKEN *token) { int i; const char *hashtext = HashToText(*hash); if (token == NULL) { /* Only a line to remember an article. We keep its arrival * and posting time. */ if (posted <= 0) { i = snprintf(s, HISV6_MAXLINE, "[%s]%c%lu%c%c\n", hashtext, HISV6_FIELDSEP, (unsigned long)arrived, HISV6_SUBFIELDSEP, HISV6_NOEXP); } else { i = snprintf(s, HISV6_MAXLINE, "[%s]%c%lu%c%c%c%lu\n", hashtext, HISV6_FIELDSEP, (unsigned long)arrived, HISV6_SUBFIELDSEP, HISV6_NOEXP, HISV6_SUBFIELDSEP, (unsigned long)posted); } } else { const char *texttok; texttok = TokenToText(*token); if (expires <= 0) { i = snprintf(s, HISV6_MAXLINE, "[%s]%c%lu%c%c%c%lu%c%s\n", hashtext, HISV6_FIELDSEP, (unsigned long)arrived, HISV6_SUBFIELDSEP, HISV6_NOEXP, HISV6_SUBFIELDSEP, (unsigned long)posted, HISV6_FIELDSEP, texttok); } else { i = snprintf(s, HISV6_MAXLINE, "[%s]%c%lu%c%lu%c%lu%c%s\n", hashtext, HISV6_FIELDSEP, (unsigned long)arrived, HISV6_SUBFIELDSEP, (unsigned long)expires, HISV6_SUBFIELDSEP, (unsigned long)posted, HISV6_FIELDSEP, texttok); } } if (i < 0 || i >= HISV6_MAXLINE) return 0; return i; } /* ** write the hash and offset to the dbz */ static bool hisv6_writedbz(struct hisv6 *h, const HASH *hash, off_t offset) { bool r; char location[HISV6_MAX_LOCATION]; const char *error; /* store the offset in the database */ switch (dbzstore(*hash, offset)) { case DBZSTORE_EXISTS: error = "dbzstore duplicate message-id "; /* not `false' so that we duplicate the pre-existing behaviour */ r = true; break; case DBZSTORE_ERROR: error = "dbzstore error "; r = false; break; default: error = NULL; r = true; break; } if (error) { hisv6_errloc(location, (size_t)-1, offset); hisv6_seterror(h, concat(error, h->histpath, ":[", HashToText(*hash), "]", location, " ", strerror(errno), NULL)); } if (r && h->synccount != 0 && ++h->dirty >= h->synccount) r = hisv6_sync(h); return r; } /* ** write a history entry, hash, with times arrived, posted and ** expires, and storage token. */ static bool hisv6_writeline(struct hisv6 *h, const HASH *hash, time_t arrived, time_t posted, time_t expires, const TOKEN *token) { bool r; size_t i, length; char hisline[HISV6_MAXLINE + 1]; char location[HISV6_MAX_LOCATION]; if (h != hisv6_dbzowner) { hisv6_seterror(h, concat("dbz not open for this history file ", h->histpath, NULL)); return false; } if (!(h->flags & HIS_RDWR)) { hisv6_seterror(h, concat("history not open for writing ", h->histpath, NULL)); return false; } length = hisv6_formatline(hisline, hash, arrived, posted, expires, token); if (length == 0) { hisv6_seterror(h, concat("error formatting history line ", h->histpath, NULL)); return false; } i = fwrite(hisline, 1, length, h->writefp); /* If the write failed, the history line is now an orphan. Attempt to rewind the write pointer to our offset to avoid leaving behind a partial write and desyncing the offset from our file position. */ if (i < length || (!(h->flags & HIS_INCORE) && fflush(h->writefp) == EOF)) { hisv6_errloc(location, (size_t)-1, h->offset); hisv6_seterror(h, concat("can't write history ", h->histpath, location, " ", strerror(errno), NULL)); if (fseeko(h->writefp, h->offset, SEEK_SET) == -1) h->offset += i; r = false; goto fail; } r = hisv6_writedbz(h, hash, h->offset); h->offset += length; /* increment regardless of error from writedbz */ fail: return r; } /* ** write a history entry, key, with times arrived, posted and ** expires, and storage token. */ bool hisv6_write(void *history, const char *key, time_t arrived, time_t posted, time_t expires, const TOKEN *token) { struct hisv6 *h = history; HASH hash; bool r; his_logger("HISwrite begin", S_HISwrite); hash = HashMessageID(key); r = hisv6_writeline(h, &hash, arrived, posted, expires, token); his_logger("HISwrite end", S_HISwrite); return r; } /* ** Remember a history entry, key, with arrival time, and also ** posting time if known. */ bool hisv6_remember(void *history, const char *key, time_t arrived, time_t posted) { struct hisv6 *h = history; HASH hash; bool r; his_logger("HISwrite begin", S_HISwrite); hash = HashMessageID(key); r = hisv6_writeline(h, &hash, arrived, posted, 0, NULL); his_logger("HISwrite end", S_HISwrite); return r; } /* ** replace an existing history entry, `key', with times arrived, ** posted and expires, and (optionally) storage token `token'. The ** new history line must fit in the space allocated for the old one - ** if it had previously just been HISremember()ed you'll almost ** certainly lose. */ bool hisv6_replace(void *history, const char *key, time_t arrived, time_t posted, time_t expires, const TOKEN *token) { struct hisv6 *h = history; HASH hash; bool r; off_t offset; char old[HISV6_MAXLINE + 1]; if (!(h->flags & HIS_RDWR)) { hisv6_seterror(h, concat("history not open for writing ", h->histpath, NULL)); return false; } hash = HashMessageID(key); r = hisv6_fetchline(h, &hash, old, &offset); if (r == true) { char new[HISV6_MAXLINE + 1]; if (hisv6_formatline(new, &hash, arrived, posted, expires, token) == 0) { hisv6_seterror(h, concat("error formatting history line ", h->histpath, NULL)); r = false; } else { size_t oldlen, newlen; oldlen = strlen(old); newlen = strlen(new); if (new[newlen - 1] == '\n') newlen--; if (newlen > oldlen) { hisv6_seterror(h, concat("new history line too long ", h->histpath, NULL)); r = false; } else { ssize_t n; /* space fill any excess in the tail of new */ memset(new + newlen, ' ', oldlen - newlen); do { n = pwrite(fileno(h->writefp), new, oldlen, offset); } while (n == -1 && errno == EINTR); if ((size_t) n != oldlen) { char location[HISV6_MAX_LOCATION]; hisv6_errloc(location, (size_t)-1, offset); hisv6_seterror(h, concat("can't write history ", h->histpath, location, " ", strerror(errno), NULL)); r = false; } } } } return r; } /* ** traverse a history database, passing the pieces through a ** callback; note that we have more parameters in the callback than ** the public interface, we add the internal history struct and the ** message hash so we can use those if we need them. If the callback ** returns false we abort the traversal. **/ static bool hisv6_traverse(struct hisv6 *h, struct hisv6_walkstate *cookie, const char *reason, bool (*callback)(struct hisv6 *, void *, const HASH *hash, time_t, time_t, time_t, const TOKEN *)) { bool r = false; QIOSTATE *qp; void *p; size_t line; char location[HISV6_MAX_LOCATION]; if ((qp = QIOopen(h->histpath)) == NULL) { hisv6_seterror(h, concat("can't QIOopen history file ", h->histpath, strerror(errno), NULL)); return false; } line = 1; /* we come back to again after we hit EOF for the first time, when we pause the server & clean up any lines which sneak through in the interim */ again: while ((p = QIOread(qp)) != NULL) { time_t arrived, posted, expires; int status; TOKEN token; HASH hash; const char *error; status = hisv6_splitline(p, &error, &hash, &arrived, &posted, &expires, &token); if (status > 0) { r = (*callback)(h, cookie, &hash, arrived, posted, expires, (status & HISV6_HAVE_TOKEN) ? &token : NULL); if (r == false) hisv6_seterror(h, concat("callback failed ", h->histpath, NULL)); } else { hisv6_errloc(location, line, (off_t)-1); hisv6_seterror(h, concat(error, " ", h->histpath, location, NULL)); /* if we're not ignoring errors set the status */ if (!cookie->ignore) r = false; } if (r == false) goto fail; ++line; } if (p == NULL) { /* read or line-format error? */ if (QIOerror(qp) || QIOtoolong(qp)) { hisv6_errloc(location, line, (off_t)-1); if (QIOtoolong(qp)) { hisv6_seterror(h, concat("line too long ", h->histpath, location, NULL)); /* if we're not ignoring errors set the status */ if (!cookie->ignore) r = false; } else { hisv6_seterror(h, concat("can't read line ", h->histpath, location, " ", strerror(errno), NULL)); r = false; } if (r == false) goto fail; } /* must have been EOF, pause the server & clean up any * stragglers */ if (reason && !cookie->paused) { if (ICCpause(reason) != 0) { hisv6_seterror(h, concat("can't pause server ", h->histpath, strerror(errno), NULL)); r = false; goto fail; } cookie->paused = true; goto again; } } fail: QIOclose(qp); return r; } /* ** internal callback used during hisv6_traverse; we just pass on the ** parameters the user callback expects **/ static bool hisv6_traversecb(struct hisv6 *h UNUSED, void *cookie, const HASH *hash UNUSED, time_t arrived, time_t posted, time_t expires, const TOKEN *token) { struct hisv6_walkstate *hiscookie = cookie; return (*hiscookie->cb.walk)(hiscookie->cookie, arrived, posted, expires, token); } /* ** history API interface to the database traversal routine */ bool hisv6_walk(void *history, const char *reason, void *cookie, bool (*callback)(void *, time_t, time_t, time_t, const TOKEN *)) { struct hisv6 *h = history; struct hisv6_walkstate hiscookie; bool r; /* our internal walk routine passes too many parameters, so add a wrapper */ hiscookie.cb.walk = callback; hiscookie.cookie = cookie; hiscookie.new = NULL; hiscookie.paused = false; hiscookie.ignore = false; r = hisv6_traverse(h, &hiscookie, reason, hisv6_traversecb); return r; } /* ** internal callback used during expire **/ static bool hisv6_expirecb(struct hisv6 *h, void *cookie, const HASH *hash, time_t arrived, time_t posted, time_t expires, const TOKEN *token) { struct hisv6_walkstate *hiscookie = cookie; bool r = true; /* check if we've seen this message id already */ if (hiscookie->new && dbzexists(*hash)) { /* continue after duplicates, it's serious, but not fatal */ hisv6_seterror(h, concat("duplicate message-id [", HashToText(*hash), "] in history ", hiscookie->new->histpath, NULL)); } else { struct hisv6_walkstate *hiscookie = cookie; TOKEN ltoken, *t; /* if we have a token pass it to the discrimination function */ if (token) { bool keep; /* make a local copy of the token so the callback can * modify it */ ltoken = *token; t = <oken; keep = (*hiscookie->cb.expire)(hiscookie->cookie, arrived, posted, expires, t); /* If the callback returns true, we should keep the * token for the time being, else we just remember * it. */ if (keep == false) { t = NULL; expires = 0; } } else { t = NULL; } /* When t is NULL (no token), the message-ID is removed from * history when the posting time of the article is older than * threshold, as set by the /remember/ line in expire.ctl. * We keep the check for the arrival time because some entries * might not have one. */ if (hiscookie->new && (t != NULL || posted >= hiscookie->threshold || (posted <= 0 && arrived >= hiscookie->threshold))) { r = hisv6_writeline(hiscookie->new, hash, arrived, posted, expires, t); } } return r; } /* ** unlink files associated with the history structure h */ static bool hisv6_unlink(struct hisv6 *h) { bool r = true; char *p; #ifdef DO_TAGGED_HASH p = concat(h->histpath, ".pag", NULL); r = (unlink(p) == 0) && r; free(p); #else p = concat(h->histpath, ".index", NULL); r = (unlink(p) == 0) && r; free(p); p = concat(h->histpath, ".hash", NULL); r = (unlink(p) == 0) && r; free(p); #endif p = concat(h->histpath, ".dir", NULL); r = (unlink(p) == 0) && r; free(p); r = (unlink(h->histpath) == 0) && r; return r; } /* ** rename files associated with hold to hnew */ static bool hisv6_rename(struct hisv6 *hold, struct hisv6 *hnew) { bool r = true; char *old, *new; #ifdef DO_TAGGED_HASH old = concat(hold->histpath, ".pag", NULL); new = concat(hnew->histpath, ".pag", NULL); r = (rename(old, new) == 0) && r; free(old); free(new); #else old = concat(hold->histpath, ".index", NULL); new = concat(hnew->histpath, ".index", NULL); r = (rename(old, new) == 0) && r; free(old); free(new); old = concat(hold->histpath, ".hash", NULL); new = concat(hnew->histpath, ".hash", NULL); r = (rename(old, new) == 0) && r; free(old); free(new); #endif old = concat(hold->histpath, ".dir", NULL); new = concat(hnew->histpath, ".dir", NULL); r = (rename(old, new) == 0) && r; free(old); free(new); r = (rename(hold->histpath, hnew->histpath) == 0) && r; return r; } /* ** expire the history database, history. */ bool hisv6_expire(void *history, const char *path, const char *reason, bool writing, void *cookie, time_t threshold, bool (*exists)(void *, time_t, time_t, time_t, TOKEN *)) { struct hisv6 *h = history, *hnew = NULL; char *nhistory = NULL; dbzoptions opt; bool r; struct hisv6_walkstate hiscookie; /* this flag is always tested in the fail clause, so initialise it now */ hiscookie.paused = false; /* during expire we ignore errors whilst reading the history file * so any errors in it get fixed automagically */ hiscookie.ignore = true; if (writing && (h->flags & HIS_RDWR)) { hisv6_seterror(h, concat("can't expire from read/write history ", h->histpath, NULL)); r = false; goto fail; } if (writing) { /* form base name for new history file */ if (path != NULL) { nhistory = concat(path, ".n", NULL); } else { nhistory = concat(h->histpath, ".n", NULL); } hnew = hisv6_new(nhistory, HIS_CREAT | HIS_RDWR | HIS_INCORE, h->history); if (!hisv6_reopen(hnew)) { hisv6_dispose(hnew); hnew = NULL; r = false; goto fail; } /* this is icky... we can only have one dbz open at a time; we really want to make dbz take a state structure. For now we'll just close the existing one and create our new one they way we need it */ if (!hisv6_dbzclose(h)) { r = false; goto fail; } dbzgetoptions(&opt); opt.writethrough = false; opt.pag_incore = INCORE_MEM; #ifndef DO_TAGGED_HASH opt.exists_incore = INCORE_MEM; #endif dbzsetoptions(opt); if (h->npairs == 0) { if (!dbzagain(hnew->histpath, h->histpath)) { hisv6_seterror(h, concat("can't dbzagain ", hnew->histpath, ":", h->histpath, strerror(errno), NULL)); r = false; goto fail; } } else { size_t npairs; npairs = (h->npairs == -1) ? 0 : h->npairs; if (!dbzfresh(hnew->histpath, dbzsize(npairs))) { hisv6_seterror(h, concat("can't dbzfresh ", hnew->histpath, ":", h->histpath, strerror(errno), NULL)); r = false; goto fail; } } hisv6_dbzowner = hnew; } /* set up the callback handler */ hiscookie.cb.expire = exists; hiscookie.cookie = cookie; hiscookie.new = hnew; hiscookie.threshold = threshold; r = hisv6_traverse(h, &hiscookie, reason, hisv6_expirecb); fail: if (writing) { if (hnew && !hisv6_closefiles(hnew)) { /* error will already have been set */ r = false; } /* reopen will synchronise the dbz stuff for us */ if (!hisv6_closefiles(h)) { /* error will already have been set */ r = false; } if (r) { /* if the new path was explicitly specified don't move the files around, our caller is planning to do it out of band */ if (path == NULL) { /* unlink the old files */ r = hisv6_unlink(h); if (r) { r = hisv6_rename(hnew, h); } } } else if (hnew) { /* something went pear shaped, unlink the new files */ hisv6_unlink(hnew); } /* re-enable dbz on the old history file */ if (!hisv6_reopen(h)) { hisv6_closefiles(h); } } if (hnew && !hisv6_dispose(hnew)) r = false; if (nhistory && nhistory != path) free(nhistory); if (r == false && hiscookie.paused) ICCgo(reason); return r; } /* ** control interface */ bool hisv6_ctl(void *history, int selector, void *val) { struct hisv6 *h = history; bool r = true; switch (selector) { case HISCTLG_PATH: *(char **)val = h->histpath; break; case HISCTLS_PATH: if (h->histpath) { hisv6_seterror(h, concat("path already set in handle", NULL)); r = false; } else { h->histpath = xstrdup((char *)val); if (!hisv6_reopen(h)) { free(h->histpath); h->histpath = NULL; r = false; } } break; case HISCTLS_STATINTERVAL: h->statinterval = *(time_t *)val * 1000; break; case HISCTLS_SYNCCOUNT: h->synccount = *(size_t *)val; break; case HISCTLS_NPAIRS: h->npairs = (ssize_t)*(size_t *)val; break; case HISCTLS_IGNOREOLD: if (h->npairs == 0 && *(bool *)val) { h->npairs = -1; } else if (h->npairs == -1 && !*(bool *)val) { h->npairs = 0; } break; default: /* deliberately doesn't call hisv6_seterror as we don't want * to spam the error log if someone's passing in stuff which * would be relevant to a different history manager */ r = false; break; } return r; } inn-2.6.0/history/hisv6/hisv6.h0000644000175200017520000000220612575023702015670 0ustar iuliusiulius/* $Id: hisv6.h 8571 2009-08-17 19:10:07Z iulius $ ** ** Internal history API interface exposed to HISxxx */ #ifndef HISV6_H #define HISV6_H struct token; struct histopts; struct history; void *hisv6_open(const char *path, int flags, struct history *); bool hisv6_close(void *); bool hisv6_sync(void *); bool hisv6_lookup(void *, const char *key, time_t *arrived, time_t *posted, time_t *expires, struct token *token); bool hisv6_check(void *, const char *key); bool hisv6_write(void *, const char *key, time_t arrived, time_t posted, time_t expires, const struct token *token); bool hisv6_replace(void *, const char *key, time_t arrived, time_t posted, time_t expires, const struct token *token); bool hisv6_expire(void *, const char *, const char *, bool, void *, time_t threshold, bool (*exists)(void *, time_t, time_t, time_t, struct token *)); bool hisv6_walk(void *, const char *, void *, bool (*)(void *, time_t, time_t, time_t, const struct token *)); const char *hisv6_error(void *); bool hisv6_remember(void *, const char *key, time_t arrived, time_t posted); bool hisv6_ctl(void *, int, void *); #endif inn-2.6.0/history/hisv6/hismethod.config0000644000175200017520000000005612575023702017634 0ustar iuliusiuliusname = hisv6 number = 0 sources = hisv6.c inn-2.6.0/history/hisv6/hisv6-private.h0000644000175200017520000000366212575023702017347 0ustar iuliusiulius#ifndef HISV6_PRIVATE_H #define HISV6_PRIVATE_H #include "config.h" #include #include #include #include #include "inn/history.h" #include "inn/storage.h" #include "inn/libinn.h" /* Used by lots of stuff that parses history file entries. Should be moved into a header specifically for history parsing. */ #define HISV6_BADCHAR '_' #define HISV6_FIELDSEP '\t' #define HISV6_NOEXP '-' #define HISV6_SUBFIELDSEP '~' /* maximum length of a history line: 34 - hash 1 - \t 20 - arrived 1 - ~ 20 - expires 1 - ~ 20 - posted 1 - tab 38 - token 1 - \n */ #define HISV6_MAXLINE 137 /* minimum length of a history line: 34 - hash 1 - \t 1 - arrived 1 - \n */ #define HISV6_MINLINE 37 struct hisv6 { char *histpath; FILE *writefp; off_t offset; /* Offset into writefp. */ unsigned long nextcheck; struct history *history; time_t statinterval; size_t synccount; size_t dirty; ssize_t npairs; int readfd; int flags; struct stat st; }; /* values in the bitmap returned from hisv6_splitline */ #define HISV6_HAVE_HASH (1<<0) #define HISV6_HAVE_ARRIVED (1<<1) #define HISV6_HAVE_POSTED (1<<2) #define HISV6_HAVE_EXPIRES (1<<3) #define HISV6_HAVE_TOKEN (1<<4) /* structure used to hold the callback and cookie so we don't try * passing too many parameters into the callers callback */ struct hisv6_walkstate { union { bool (*expire)(void *, time_t, time_t, time_t, TOKEN *); bool (*walk)(void *, time_t, time_t, time_t, const TOKEN *); } cb; void *cookie; bool paused; bool ignore; /* the next two fields are only used during expire... they should * probably be linked off of cookie, but I've been lazy */ struct hisv6 *new; time_t threshold; }; /* maximum length of the string from hisv6_errloc */ #define HISV6_MAX_LOCATION 22 #endif inn-2.6.0/history/Makefile0000644000175200017520000001117512575023702015066 0ustar iuliusiulius## $Id: Makefile 9794 2015-03-17 20:49:15Z iulius $ include ../Makefile.global # This version number should be increased with every change to the library # source following the rules laid out in the libtool manual. This will also # force the file name of the shared library to change so that one can # recover from make update. We can't use .OLD extensions for the shared # library since ldconfig will think .OLD sorts after the regular library and # will point the binaries at the old library. LTVERSION = 3:0:0 top = .. CFLAGS = $(GCFLAGS) -I. SOURCES = his.c hismethods.c $(METHOD_SOURCES) OBJECTS = $(SOURCES:.c=.o) LOBJECTS = $(OBJECTS:.o=.lo) .SUFFIXES: .lo all: library programs # Included here after the all target, since additional rules are defined in # Make.methods to be sure that we recurse properly to build the methods. include Make.methods warnings: $(MAKE) COPT='$(WARNINGS)' all install: all $(LI_LPUB) libinnhist.$(EXTLIB) $D$(PATHLIB)/libinnhist.$(EXTLIB) bootstrap: Make.methods library: libinnhist.$(EXTLIB) programs: $(PROGRAMS) clean clobber distclean: rm -f *.o *.lo */*.o */*.lo libinnhist.la libinnhist.a rm -f libinnhist_pure_*.a .pure $(PROGRAMS) rm -f buildconfig profiled libinnhist$(PROFSUFFIX).a rm -rf .libs */.libs maintclean: distclean rm -f Make.methods hismethods.c hismethods.h $(FIXSCRIPT): @echo Run configure before running make. See INSTALL for details. @exit 1 libinnhist.la: $(OBJECTS) $(LIBSTORAGE) $(LIBINN) $(LIBLD) $(LDFLAGS) -o $@ $(LOBJECTS) \ $(LIBSTORAGE) $(LIBINN) $(STORAGE_LIBS) $(LIBS) \ -rpath $(PATHLIB) -version-info $(LTVERSION) libinnhist.a: $(OBJECTS) ar r $@ $(OBJECTS) $(RANLIB) libinnhist.a # Try to set up these rules so that buildconfig is only run once. # Make.methods is included in the distribution tarball since some non-GNU # makes can't deal with including a non-existent file, so don't depend on # it. The dependencies aren't entirely accurate; you really want to re-run # buildconfig each time a new subdirectory is added to the directory. But # adding a dependency on . is a bit too non-portable for my taste and causes # too many rebuilds. Make.methods hismethods.h hismethods.c: buildconfig ./buildconfig buildconfig: buildconfig.in $(FIXSCRIPT) $(FIXSCRIPT) -i buildconfig.in .c.o .c.lo: $(LIBCC) $(CFLAGS) $(CCOUTPUT) $(LIBINN): ; (cd ../lib ; $(MAKE)) $(LIBSTORAGE): ; (cd ../storage ; $(MAKE) library) ## Profiling. The rules are a bit brute-force, but good enough. profiled: libinnhist$(PROFSUFFIX).a date >$@ libinnhist$(PROFSUFFIX).a: $(SOURCES) rm -f $(OBJECTS) $(MAKEPROFILING) libinnhist.a mv libinnhist.a libinnhist$(PROFSUFFIX).a $(RANLIB) libinnhist$(PROFSUFFIX).a rm -f $(OBJECTS) ## Dependencies. Default list, below, is probably good enough. depend: $(SOURCES) $(EXTRA_SOURCES) $(MAKEDEPEND) '$(CFLAGS)' $(SOURCES) $(EXTRA_SOURCES) # DO NOT DELETE THIS LINE -- make depend depends on it. his.o: his.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/history.h \ ../include/inn/messages.h ../include/inn/timer.h ../include/inn/libinn.h \ ../include/inn/xmalloc.h ../include/inn/xwrite.h \ ../include/inn/storage.h ../include/inn/options.h hisinterface.h \ hismethods.h hismethods.o: hismethods.c hisinterface.h ../include/config.h \ ../include/inn/defines.h ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h hismethods.h \ hisv6/hisv6.h hisv6/hisv6.o: hisv6/hisv6.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h hisinterface.h hisv6/hisv6.h \ hisv6/hisv6-private.h ../include/inn/history.h ../include/inn/storage.h \ ../include/inn/options.h ../include/inn/libinn.h \ ../include/inn/xmalloc.h ../include/inn/xwrite.h ../include/inn/dbz.h \ ../include/inn/fdflag.h ../include/inn/portable-socket.h \ ../include/inn/portable-getaddrinfo.h \ ../include/inn/portable-getnameinfo.h ../include/inn/innconf.h \ ../include/inn/timer.h ../include/inn/qio.h ../include/inn/sequence.h \ ../include/inn/inndcomm.h inn-2.6.0/history/buildconfig.in0000644000175200017520000001442412575023702016243 0ustar iuliusiulius#! /usr/bin/perl ## $Id: buildconfig.in 6803 2004-05-18 00:48:58Z rra $ ## ## Generate linkage code and makefiles for storage and overview methods. ## ## Goes through all subdirectories of the current directory and finds ## directories that history methods or overview methods. Builds ## hismethods.[ch] as well as makefile stubs. require 5.003; use strict; use vars qw(@HISTORY); # History API functions. @HISTORY = qw(open close sync lookup check write replace expire walk remember ctl); # Used to make heredocs more readable. sub unquote { my ($string) = @_; $string =~ s/^:( {0,7}|\t)//gm; $string } # Parse a hismethod.config file for a history method, putting information # about that history method into the given hash ref. sub parse_config { my ($dir, $file, $config) = @_; local $_; $$config{sources} ||= []; $$config{extra} ||= []; $$config{programs} ||= []; $$config{makefiles} ||= []; open (CONFIG, "$dir/$file") or die "Can't open $dir/$file: $!\n"; while () { s/^\s+//; s/\s+$//; if (/^name\s*=\s*(\S+)$/) { my $method = $1; die "$dir/$file: $method has already been defined\n" if (defined $$config{method}{$method}); $$config{method}{$method} = $dir; } elsif (/^number\s*=\s*(\d+)$/) { my $number = $1; if (defined $$config{number}{$number}) { die "$dir/$file: method number $number was already " . "allocated in $$config{number}{$number}\n"; } $$config{number}{$dir} = $number; } elsif (/^sources\s*=\s*(.*)/) { my $sources = $1; my @sources = split (' ', $sources); push (@{ $$config{sources} }, map { "$dir/$_" } @sources); } elsif (/^extra-sources\s*=\s*(.*)/) { my $extra = $1; my @extra = split (' ', $extra); push (@{ $$config{extra} }, map { "$dir/$_" } @extra); } elsif (/^programs\s*=\s*(.*)/) { my $programs = $1; my @programs = split (' ', $programs); push (@{ $$config{programs} }, map { "$dir/$_" } @programs); } else { warn "$dir/$file: ignoring unknown line: $_\n"; } } # If there is a makefile fragment in the directory, note it. if (-f "$dir/hismethod.mk") { push (@{ $$config{makefiles} }, "$dir/hismethod.mk"); } } # Write out include directives for a list of files. sub write_includes { my ($fh, $config) = @_; my $method; for $method (sort keys %{ $$config{method} }) { my $path = $$config{method}{$method}; print $fh qq(\#include "$path/$method.h"\n); } } # Write out the method struct. sub write_methods { my ($fh, $config, $prefix, @funcs) = @_; my ($notfirst, $method); for $method (sort keys %{ $$config{method} }) { print $fh "\n},\n" if $notfirst; print $fh qq(\{\n "$method"); print $fh ', ', $prefix, '_', uc ($method) if $prefix; for (@funcs) { print $fh ",\n ${method}_$_"; } $notfirst++; } print $fh "\n}\n};\n\n"; } # Write out hismethods.c and hismethods.h for the interface to the history # methods. sub write_history { my $history = shift; open (DEF, '> hismethods.c.new') or die "Can't create hismethods.c.new: $!\n"; print DEF unquote (<<'EOE'); : /* This file is automatically generated by buildconfig. */ : : #include "hisinterface.h" : #include "hismethods.h" EOE my $method; write_includes (\*DEF, $history); print DEF "\nHIS_METHOD his_methods[", scalar (keys %{ $$history{method} }), "] = {\n"; write_methods (\*DEF, $history, undef, @HISTORY); close DEF; rename ('hismethods.c.new', 'hismethods.c'); open (H, '> hismethods.h.new') or die "Can't open hismethods.h.new: $!\n"; print H unquote (<<'EOE'); : /* This file is automatically generated by buildconfig */ : : #ifndef HISMETHODS_H : #define HISMETHODS_H 1 : : #include "hisinterface.h" : EOE print H '#define NUM_HIS_METHODS ', scalar (keys %{ $$history{method} }), "\n"; print H unquote (<<'EOE'); : : extern HIS_METHOD his_methods[NUM_HIS_METHODS]; : : #endif /* HISMETHODS_H */ EOE close H; rename ('hismethods.h.new', 'hismethods.h'); } # Return a string setting a makefile variable. Tab over the = properly and # wrap to fit our coding standards. sub makefile_var { my ($variable, @values) = @_; my $output; $output = sprintf ("%-15s =", $variable); my $column = 17; for (@values) { if ($column > 17 && 77 - $column < length ($_)) { $output .= " \\\n" . ' ' x 17; $column = 17; } $output .= " $_"; $column += 1 + length ($_); } $output .= "\n"; return $output; } # Write out the makefile fragment for history methods. sub write_makefile { my ($dirs, $sources, $extra, $programs, $makefiles) = @_; open (MAKE, '> Make.methods.new') or die "Can't create Make.methods.new: $!\n"; print MAKE "# This file is automatically generated by buildconfig\n\n"; print MAKE makefile_var ('METHOD_SOURCES', @$sources); print MAKE makefile_var ('EXTRA_SOURCES', @$extra); print MAKE makefile_var ('PROGRAMS', @$programs); for (@$makefiles) { print MAKE "\n\n## Included from $_\n\n"; open (FRAG, $_) or die "Can't open $_: $!\n"; print MAKE ; close FRAG; } rename ('Make.methods.new', 'Make.methods'); } my ($dir, %history); if (!-d 'hisv6') { if (-d 'history/cnfs') { chdir 'history' or die "Can't chdir to history: $!\n"; } else { die "Can't find history directory (looking for history/hisv6)\n"; } } opendir (D, ".") or die "Can't open current directory: $!\n"; my @dirs = sort readdir D; for $dir (@dirs) { if (-e "$dir/hismethod.config") { parse_config ($dir, 'hismethod.config', \%history); } } write_history (\%history); @dirs = sort values %{ $history{method} }; my @sources = sort @{ $history{sources} }; my @extra = sort @{ $history{extra} }; my @programs = sort @{ $history{programs} }; my @makefiles = sort @{ $history{makefiles} }; write_makefile (\@dirs, \@sources, \@extra, \@programs, \@makefiles); inn-2.6.0/history/hismethods.c0000644000175200017520000000056712575023702015744 0ustar iuliusiulius/* This file is automatically generated by buildconfig. */ #include "hisinterface.h" #include "hismethods.h" #include "hisv6/hisv6.h" HIS_METHOD his_methods[1] = { { "hisv6", hisv6_open, hisv6_close, hisv6_sync, hisv6_lookup, hisv6_check, hisv6_write, hisv6_replace, hisv6_expire, hisv6_walk, hisv6_remember, hisv6_ctl } }; inn-2.6.0/lib/0000755000175200017520000000000012575023701012465 5ustar iuliusiuliusinn-2.6.0/lib/conffile.c0000644000175200017520000000663712575023702014433 0ustar iuliusiulius/* $Id: conffile.c 9582 2013-12-09 21:30:42Z iulius $ ** ** Routines for reading in incoming.conf-style config files. */ #include "config.h" #include "clibrary.h" #include "conffile.h" #include "inn/libinn.h" static int getconfline(CONFFILE *F, char *buffer, int length) { if (F->f) { fgets(buffer, length, F->f); if (ferror(F->f)) { return 1; } } else if (F->array) { strlcpy(buffer, F->array[F->lineno], F->sbuf); } F->lineno++; if (strlen (F->buf) >= F->sbuf - 1) { return 1; /* Line too long */ } else { return 0; } } static int cfeof(CONFFILE *F) { if (F->f) { return feof(F->f); } else if (F->array) { return (F->lineno == F->array_len); } else { return 1; } } static char *CONFgetword(CONFFILE *F) { char *p; char *s; char *t; char *word; bool comment; if (!F) return (NULL); /* No conf file */ if (!F->buf || !F->buf[0]) { if (cfeof (F)) return (NULL); if (!F->buf) { F->sbuf = BIG_BUFFER; F->buf = xmalloc(F->sbuf); } if (getconfline(F, F->buf, F->sbuf) != 0) return (NULL); /* Line too long */ } do { /* Ignore blank and comment lines. */ if ((p = strchr(F->buf, '\n')) != NULL) *p = '\0'; for (p = F->buf; *p == ' ' || *p == '\t' ; p++); if ((*p == '\0' || *p == '#') && !cfeof(F)) { if (getconfline(F, F->buf, F->sbuf)) return (NULL); /* Line too long */ continue; } break; } while (!cfeof(F)); comment = false; if (*p == '"') { /* double quoted string ? */ p++; do { for (t = p; (*t != '"' || (*t == '"' && *(t - 1) == '\\')) && *t != '\0'; t++); if (*t == '\0') { if (strlen(F->buf) >= F->sbuf - 2) return (NULL); /* Line too long */ *t++ = '\n'; *t = '\0'; if (getconfline(F, t, F->sbuf - strlen(F->buf))) return (NULL); /* Line too long */ if ((s = strchr(t, '\n')) != NULL) *s = '\0'; } else break; } while (!cfeof(F)); if (*t != '"') return (NULL); *t++ = '\0'; } else { for (t = p; *t != ' ' && *t != '\t' && *t != '\0'; t++) if (*t == '#' && (t == p || *(t - 1) != '\\')) { comment = true; break; } if (*t != '\0') *t++ = '\0'; } if (*p == '\0' && cfeof(F)) return (NULL); word = xstrdup (p); p = F->buf; if (!comment) for (; *t != '\0'; t++) *p++ = *t; *p = '\0'; return (word); } CONFFILE *CONFfopen(const char *filename) { FILE *f; CONFFILE *ret; f = fopen(filename, "r"); if (!f) return(0); ret = xmalloc(sizeof(CONFFILE)); if (!ret) { fclose(f); return(0); } ret->filename = xstrdup(filename); ret->buf = 0; ret->sbuf = 0; ret->lineno = 0; ret->f = f; ret->array = NULL; return(ret); } void CONFfclose(CONFFILE *f) { if (!f) return; /* No conf file */ fclose(f->f); if (f->buf) free(f->buf); if (f->filename) free(f->filename); free(f); } CONFTOKEN *CONFgettoken(CONFTOKEN *toklist, CONFFILE *file) { char *word; static CONFTOKEN ret = {CONFstring, 0}; int i; if (ret.name) { free(ret.name); ret.name = 0; } word = CONFgetword(file); if (!word) return(0); if (toklist) { for (i = 0; toklist[i].type; i++) { if (strcmp(word, toklist[i].name) == 0) { free(word); return(&toklist[i]); } } } ret.name = word; return(&ret); } inn-2.6.0/lib/fdflag.c0000644000175200017520000001254712575023702014066 0ustar iuliusiulius/* $Id: fdflag.c 9680 2014-09-06 08:07:27Z iulius $ * * Set or clear file descriptor flags. * * Simple functions (wrappers around fcntl) to set or clear file descriptor * flags like close-on-exec or nonblocking I/O. * * The canonical version of this file is maintained in the rra-c-util package, * which can be found at . * * Copyright 2008, 2011, 2013 * The Board of Trustees of the Leland Stanford Junior University * Copyright (c) 2004, 2005, 2006 * by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, * 2002, 2003 by The Internet Software Consortium and Rich Salz * * This code is derived from software contributed to the Internet Software * Consortium by Rich Salz. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #include "config.h" #include "clibrary.h" #ifdef _WIN32 # include #else # include # ifndef O_NONBLOCK # include # if HAVE_SYS_FILIO_H # include # endif # endif #endif #include "inn/fdflag.h" #include "inn/macros.h" /* * Set a file to close-on-exec (or clear that setting if the flag is false), * returning true on success and false on failure. * * One is supposed to retrieve the flags, add FD_CLOEXEC, and then set them, * although I've never seen a system with any flags other than close-on-exec. * Do it right anyway; it's not that expensive. * * Stub this out on Windows, where it's not supported (at least currently by * this utility library). Do not use socket_type for the first parameter, * since it's meaningful to set file descriptors for regular files to * close-on-exec (even though this is currently irrelevant since the function * isn't supported on Windows). */ #ifdef _WIN32 bool fdflag_close_exec(int fd UNUSED, bool flag UNUSED) { return false; } #else bool fdflag_close_exec(int fd, bool flag) { int oflag, mode; oflag = fcntl(fd, F_GETFD, 0); if (oflag < 0) return false; mode = flag ? (oflag | FD_CLOEXEC) : (oflag & ~FD_CLOEXEC); return (fcntl(fd, F_SETFD, mode) == 0); } #endif /* * Set a file descriptor to nonblocking (or clear the nonblocking flag if flag * is false), returning true on success and false on failure. * * For Windows, be aware that this will only work for sockets. For UNIX, you * can pass a non-socket in and it will do the right thing, since UNIX doesn't * distinguish, but Windows will not allow that. Thankfully, there's rarely * any need to set non-sockets non-blocking. * * For UNIX, always use O_NONBLOCK; O_NDELAY is not the same thing * historically. The semantics of O_NDELAY are that if the read would block, * it returns 0 instead. This is indistinguishable from an end of file * condition. POSIX added O_NONBLOCK, which requires read to return -1 and * set errno to EAGAIN, which is what we want. * * FNDELAY (4.3BSD) originally did the correct thing, although it has a * different incompatibility (affecting all users of a socket rather than just * a file descriptor and returning EWOULDBLOCK instead of EAGAIN) that we * probably don't care about. Using it is probably safe, but BSD should also * have the ioctl, and at least on Solaris FNDELAY does the same thing as * O_NDELAY, not O_NONBLOCK. So if we don't have O_NONBLOCK, fall back to the * ioctl instead. * * Reference: Stevens, Advanced Unix Programming, pg. 364. * * Note that O_NONBLOCK is known not to work on earlier versions of ULTRIX, * SunOS, and AIX, possibly not setting the socket nonblocking at all, despite * the fact that they do define it. It works in later SunOS and, current AIX, * however, and a 1999-10-25 survey of current operating systems failed to * turn up any that didn't handle it correctly (as required by POSIX), while * HP-UX 11.00 did use the broken return-zero semantics of O_NDELAY (most * other operating systems surveyed treated O_NDELAY as synonymous with * O_NONBLOCK). Accordingly, we currently unconditionally use O_NONBLOCK. If * this causes too many problems, an autoconf test may be required. */ #if defined(_WIN32) bool fdflag_nonblocking(socket_type fd, bool flag) { u_long mode; mode = flag ? 1 : 0; return (ioctlsocket(fd, FIONBIO, &mode) == 0); } #elif defined(O_NONBLOCK) bool fdflag_nonblocking(socket_type fd, bool flag) { int mode; mode = fcntl(fd, F_GETFL, 0); if (mode < 0) return false; mode = (flag ? (mode | O_NONBLOCK) : (mode & ~O_NONBLOCK)); return (fcntl(fd, F_SETFL, mode) == 0); } #else /* !O_NONBLOCK */ bool fdflag_nonblocking(socket_type fd, bool flag) { int state; state = flag ? 1 : 0; return (ioctl(fd, FIONBIO, &state) == 0); } #endif /* !O_NONBLOCK */ inn-2.6.0/lib/hashtab.c0000644000175200017520000003221112575023702014243 0ustar iuliusiulius/* $Id: hashtab.c 9907 2015-06-25 19:44:59Z iulius $ ** ** Generic hash table implementation. ** ** Written by Russ Allbery ** This work is hereby placed in the public domain by its author. ** ** This is a generic hash table implementation with linear probing. It ** takes a comparison function and a hashing function and stores void *. ** ** Included for the use of callers is the hash function LOOKUP2 by Bob ** Jenkins, taken from ; see that web ** page for analysis and performance comparisons. The performance of this ** hash is slightly worse than the standard sum and modulus hash function ** seen in many places but it produces fewer collisions. */ #include "config.h" #include "clibrary.h" #include "inn/hashtab.h" #include "inn/libinn.h" /* Magic values for empty and deleted hash table slots. */ #define HASH_EMPTY ((void *) 0) #define HASH_DELETED ((void *) 1) struct hash { size_t size; /* Allocated size. */ size_t mask; /* Used to resolve a hash to an index. */ size_t nelements; /* Total elements, including deleted. */ size_t ndeleted; /* Number of deleted elements. */ unsigned long searches; /* Count of lookups (for debugging). */ unsigned long collisions; /* Count of collisions (for debugging). */ unsigned long expansions; /* Count of hash resizes needed. */ hash_func hash; /* Return hash of a key. */ hash_key_func key; /* Given an element, returns its key. */ hash_equal_func equal; /* Whether a key matches an element. */ hash_delete_func delete; /* Called when a hash element is deleted. */ void **table; /* The actual elements. */ }; /* ** Given a target table size, return the nearest power of two that's ** greater than or equal to that size, with a minimum size of four. The ** minimum must be at least four to ensure that there is always at least ** one empty slot in the table given hash_find_slot's resizing of the table ** if it as least 75% full. Otherwise, it would be possible for ** hash_find_slot to go into an infinite loop. */ static size_t hash_size(size_t target) { int n; size_t size; if (target == 0) { return 4; } size = target - 1; for (n = 0; size > 0; n++) size >>= 1; size = 1 << n; return (size < 4) ? 4 : size; } /* ** Create a new hash table. The given size is rounded up to the nearest ** power of two for speed reasons (it greatly simplifies the use of the ** hash function). */ struct hash * hash_create(size_t size, hash_func hash_f, hash_key_func key_f, hash_equal_func equal_f, hash_delete_func delete_f) { struct hash *hash; hash = xcalloc(1, sizeof(struct hash)); hash->hash = hash_f; hash->key = key_f; hash->equal = equal_f; hash->delete = delete_f; hash->size = hash_size(size); hash->mask = hash->size - 1; hash->table = xcalloc(hash->size, sizeof(void *)); return hash; } /* ** Free a hash and all resources used by it, and call the delete function ** on every element. */ void hash_free(struct hash *hash) { size_t i; void *entry; for (i = 0; i < hash->size; i++) { entry = hash->table[i]; if (entry != HASH_EMPTY && entry != HASH_DELETED) (*hash->delete)(entry); } free(hash->table); free(hash); } /* ** Internal search function used by hash_expand. This is an optimized ** version of hash_find_slot that returns a pointer to the first empty ** slot, not trying to call the equality function on non-empty slots and ** assuming there are no HASH_DELETED slots. */ static void ** hash_find_empty(struct hash *hash, const void *key) { size_t slot; slot = (*hash->hash)(key) & hash->mask; while (1) { if (hash->table[slot] == HASH_EMPTY) return &hash->table[slot]; slot++; if (slot >= hash->size) slot -= hash->size; } } /* ** Expand the hash table to be approximately 50% empty based on the number ** of elements in the hash. This is done by allocating a new table and ** then calling hash_find_empty for each element in the previous table, ** recovering the key by calling hash->key on the element. */ static void hash_expand(struct hash *hash) { void **old, **slot; size_t i, size; old = hash->table; size = hash->size; hash->size = hash_size((hash->nelements - hash->ndeleted) * 2); hash->mask = hash->size - 1; hash->table = xcalloc(hash->size, sizeof(void *)); hash->nelements = 0; hash->ndeleted = 0; for (i = 0; i < size; i++) if (old[i] != HASH_EMPTY && old[i] != HASH_DELETED) { slot = hash_find_empty(hash, (*hash->key)(old[i])); *slot = old[i]; hash->nelements++; } hash->expansions++; free(old); } /* ** Find a slot in the hash for a given key. This is used both for ** inserting and deleting elements from the hash, as well as looking up ** entries. Returns a pointer to the slot. If insert is true, return the ** first empty or deleted slot. If insert is false, return NULL if the ** element could not be found. ** ** This function assumes that there is at least one empty slot in the ** hash; otherwise, it can loop infinitely. It attempts to ensure this by ** always expanding the hash if it is at least 75% full; this will ensure ** that property for any hash size of 4 or higher. */ static void ** hash_find_slot(struct hash *hash, const void *key, bool insert) { void **deleted_slot = NULL; void *entry; size_t slot; if (insert && hash->nelements * 4 >= hash->size * 3) hash_expand(hash); hash->searches++; slot = (*hash->hash)(key) & hash->mask; while (1) { entry = hash->table[slot]; if (entry == HASH_EMPTY) { if (!insert) return NULL; if (deleted_slot != NULL) { *deleted_slot = HASH_EMPTY; hash->ndeleted--; return deleted_slot; } hash->nelements++; return &hash->table[slot]; } else if (entry == HASH_DELETED) { if (insert) deleted_slot = &hash->table[slot]; } else if ((*hash->equal)(key, entry)) { return &hash->table[slot]; } hash->collisions++; slot++; if (slot >= hash->size) slot -= hash->size; } } /* ** Given a key, return the entry corresponding to that key or NULL if that ** key isn't present in the hash table. */ void * hash_lookup(struct hash *hash, const void *key) { void **slot; slot = hash_find_slot(hash, key, false); return (slot == NULL) ? NULL : *slot; } /* ** Insert a new key/value pair into the hash, returning true if the ** insertion was successful and false if there is already a value in the ** hash with that key. */ bool hash_insert(struct hash *hash, const void *key, void *datum) { void **slot; slot = hash_find_slot(hash, key, true); if (*slot != HASH_EMPTY) return false; *slot = datum; return true; } /* ** Replace an existing hash value with a new data value, calling the delete ** function for the old data. Returns true if the replacement was ** successful or false (without changing the hash) if the key whose value ** should be replaced was not found in the hash. */ bool hash_replace(struct hash *hash, const void *key, void *datum) { void **slot; slot = hash_find_slot(hash, key, false); if (slot == NULL) return false; (*hash->delete)(*slot); *slot = datum; return true; } /* ** Delete a key out of the hash. Returns true if the deletion was ** successful, false if the key could not be found in the hash. */ bool hash_delete(struct hash *hash, const void *key) { bool result; result = hash_replace(hash, key, HASH_DELETED); if (result) hash->ndeleted++; return result; } /* ** For each element in the hash table, call the provided function, passing ** it the element and the opaque token that's passed to this function. */ void hash_traverse(struct hash *hash, hash_traverse_func callback, void *data) { size_t i; void *entry; for (i = 0; i < hash->size; i++) { entry = hash->table[i]; if (entry != HASH_EMPTY && entry != HASH_DELETED) (*callback)(entry, data); } } /* ** Returns a count of undeleted elements in the hash. */ unsigned long hash_count(struct hash *hash) { return hash->nelements - hash->ndeleted; } /* ** Accessor functions for the debugging statistics. */ unsigned long hash_searches(struct hash *hash) { return hash->searches; } unsigned long hash_collisions(struct hash *hash) { return hash->collisions; } unsigned long hash_expansions(struct hash *hash) { return hash->expansions; } /* ** Mix three 32-bit values reversibly. This is the internal mixing ** function for the hash function. ** ** For every delta with one or two bit set, and the deltas of all three ** high bits or all three low bits, whether the original value of a,b,c ** is almost all zero or is uniformly distributed, ** ** * If mix() is run forward or backward, at least 32 bits in a,b,c ** have at least 1/4 probability of changing. ** ** * If mix() is run forward, every bit of c will change between 1/3 and ** 2/3 of the time. (Well, 22/100 and 78/100 for some 2-bit deltas.) ** ** mix() takes 36 machine instructions, but only 18 cycles on a superscalar ** machine (like a Pentium or a Sparc). No faster mixer seems to work, ** that's the result of my brute-force search. There were about 2^68 ** hashes to choose from. I (Bob Jenkins) only tested about a billion of ** those. */ #define MIX(a, b, c) \ { \ (a) -= (b); (a) -= (c); (a) ^= ((c) >> 13); \ (b) -= (c); (b) -= (a); (b) ^= ((a) << 8); \ (c) -= (a); (c) -= (b); (c) ^= ((b) >> 13); \ (a) -= (b); (a) -= (c); (a) ^= ((c) >> 12); \ (b) -= (c); (b) -= (a); (b) ^= ((a) << 16); \ (c) -= (a); (c) -= (b); (c) ^= ((b) >> 5); \ (a) -= (b); (a) -= (c); (a) ^= ((c) >> 3); \ (b) -= (c); (b) -= (a); (b) ^= ((a) << 10); \ (c) -= (a); (c) -= (b); (c) ^= ((b) >> 15); \ } /* ** Hash a variable-length key into a 32-bit value. ** ** Takes byte sequence to hash and returns a 32-bit value. A partial ** result can be passed as the third parameter so that large amounts of ** data can be hashed by subsequent calls, passing in the result of the ** previous call each time. Every bit of the key affects every bit of the ** return value. Every 1-bit and 2-bit delta achieves avalanche. About ** (36 + 6n) instructions. ** ** The best hash table sizes are powers of 2. There is no need to mod with ** a prime (mod is sooo slow!). If you need less than 32 bits, use a ** bitmask. For example, if you need only 10 bits, do: ** ** h = h & ((1 << 10) - 1); ** ** In which case, the hash table should have 2^10 elements. ** ** Based on code by Bob Jenkins , originally ** written in 1996. The original license was: ** ** By Bob Jenkins, 1996. bob_jenkins@burtleburtle.net. You may use ** this code any way you wish, private, educational, or commercial. ** It's free. ** ** See for discussion of ** this hash function. Use for hash table lookup, or anything where one ** collision in 2^32 is acceptable. Do NOT use for cryptographic purposes. */ unsigned long hash_lookup2(const char *key, size_t length, unsigned long partial) { uint32_t a, b, c, len; /* Set up the internal state. a and b are initialized to a golden ratio, an arbitrary value intended to avoid mapping all zeroes to all zeroes. */ len = length; a = b = 0x9e3779b9; c = partial; #define S0(c) ((uint32_t)(c)) #define S1(c) ((uint32_t)(c) << 8) #define S2(c) ((uint32_t)(c) << 16) #define S3(c) ((uint32_t)(c) << 24) /* Handle most of the key. */ while (len >= 12) { a += S0(key[0]) + S1(key[1]) + S2(key[2]) + S3(key[3]); b += S0(key[4]) + S1(key[5]) + S2(key[6]) + S3(key[7]); c += S0(key[8]) + S1(key[9]) + S2(key[10]) + S3(key[11]); MIX(a, b, c); key += 12; len -= 12; } /* Handle the last 11 bytes. All of the cases fall through. */ c += length; switch (len) { case 11: c += S3(key[10]); case 10: c += S2(key[9]); case 9: c += S1(key[8]); /* The first byte of c is reserved for the length. */ case 8: b += S3(key[7]); case 7: b += S2(key[6]); case 6: b += S1(key[5]); case 5: b += S0(key[4]); case 4: a += S3(key[3]); case 3: a += S2(key[2]); case 2: a += S1(key[1]); case 1: a += S0(key[0]); /* case 0: nothing left to add. */ } MIX(a, b, c); return c; } /* ** A hash function for nul-terminated strings using hash_lookup2, suitable ** for passing to hash_create. */ unsigned long hash_string(const void *key) { return hash_lookup2(key, strlen(key), 0); } inn-2.6.0/lib/getmodaddr.c0000644000175200017520000001257612575023702014757 0ustar iuliusiulius/* $Id: getmodaddr.c 9911 2015-07-04 21:32:56Z iulius $ ** */ #include "config.h" #include "clibrary.h" #include #include "inn/innconf.h" #include "inn/libinn.h" #include "inn/nntp.h" #include "inn/paths.h" static char *GMApathname = NULL; static FILE *GMAfp = NULL; /* ** Close the file opened by GMAlistopen. */ static void GMAclose(void) { if (GMAfp) { fclose(GMAfp); GMAfp = NULL; } if (GMApathname != NULL) { unlink(GMApathname); free(GMApathname); GMApathname = NULL; } } /* ** Internal library routine. */ static FILE * GMA_listopen(int fd, FILE *FromServer, FILE *ToServer, const char *request) { char buff[BUFSIZ]; char expectedanswer[BUFSIZ]; char *p; int oerrno; FILE *F; F = fdopen(fd, "r+"); if (F == NULL) return NULL; /* Send a LIST command to and capture the output. */ if (request == NULL) fprintf(ToServer, "LIST MODERATORS\r\n"); else fprintf(ToServer, "LIST %s\r\n", request); fflush(ToServer); snprintf(expectedanswer, sizeof(expectedanswer), "%d", NNTP_OK_LIST); /* Get the server's reply to our command. */ if (fgets(buff, sizeof buff, FromServer) == NULL || strncmp(buff, expectedanswer, strlen(expectedanswer)) != 0) { oerrno = errno; fclose(F); GMAclose(); errno = oerrno; return NULL; } /* Slurp up the rest of the response. */ while (fgets(buff, sizeof buff, FromServer) != NULL) { if ((p = strchr(buff, '\r')) != NULL) *p = '\0'; if ((p = strchr(buff, '\n')) != NULL) *p = '\0'; if (buff[0] == '.' && buff[1] == '\0') { if (ferror(F) || fflush(F) == EOF || fseeko(F, 0, SEEK_SET) != 0) break; return F; } fprintf(F, "%s\n", buff); } /* Ran out of input before finding the terminator; quit. */ oerrno = errno; fclose(F); GMAclose(); errno = oerrno; return NULL; } /* ** Check if the argument is a valid submission template according to RFC 6048. ** At least, make sure it does not contain "%" except as part of "%s" or "%%", ** and that only one occurrence of "%s" exists, if any. */ static bool IsValidSubmissionTemplate(const char *string) { bool found = false; const char *p; /* Not NULL. */ if (string == NULL) return false; p = string; while ((p = strchr(p, '%')) != NULL) { /* Look at the next character. */ p++; /* Skip "%%". */ if (*p == '%') { p++; continue; } /* Invalid template if not "%s". */ if (*p != 's') return false; /* Invalid template if another "%s". */ if (found) return false; found = true; } return true; } /* ** Read the moderators file, looking for a moderator. */ char * GetModeratorAddress(FILE *FromServer, FILE *ToServer, char *group, char *moderatormailer) { static char address[SMBUF]; char *p; char *save; char *path; char buff[BUFSIZ]; char name[SMBUF]; int fd; strlcpy(name, group, sizeof(name)); address[0] = '\0'; if (FromServer==NULL || ToServer==NULL){ /* * This should be part of nnrpd or the like running on the server. * Open the server copy of the moderators file. */ path = concatpath(innconf->pathetc, INN_PATH_MODERATORS); GMAfp = fopen(path, "r"); free(path); }else{ /* * Get a local copy of the moderators file from the server. */ GMApathname = concatpath(innconf->pathtmp, INN_PATH_TEMPMODERATORS); fd = mkstemp(GMApathname); if (fd >= 0) GMAfp = GMA_listopen(fd, FromServer, ToServer, "MODERATORS"); else GMAfp = NULL; /* Fallback to the local copy if the server doesn't have it */ if (GMAfp == NULL) { path = concatpath(innconf->pathetc, INN_PATH_MODERATORS); GMAfp = fopen(path, "r"); free(path); } } if (GMAfp != NULL) { while (fgets(buff, sizeof buff, GMAfp) != NULL) { /* Skip blank and comment lines. */ if ((p = strchr(buff, '\n')) != NULL) *p = '\0'; if (buff[0] == '\0' || buff[0] == '#') continue; /* Snip off the first word. */ if ((p = strchr(buff, ':')) == NULL) /* Malformed line... */ continue; *p++ = '\0'; /* If it pattern-matches the newsgroup, the second field is a * format for mailing, with periods changed to dashes. */ if (uwildmat(name, buff)) { for (save = p; ISWHITE(*save); save++) continue; for (p = name; *p; p++) if (*p == '.') *p = '-'; if (IsValidSubmissionTemplate(save)) { #pragma GCC diagnostic ignored "-Wformat-nonliteral" snprintf(address, sizeof(address), save, name); #pragma GCC diagnostic warning "-Wformat-nonliteral" break; } } } GMAclose(); if (address[0]) return address; } /* If we don't have an address, see if the config file has a default. */ if ((save = moderatormailer) == NULL) return NULL; for (p = name; *p; p++) if (*p == '.') *p = '-'; if (IsValidSubmissionTemplate(save)) { #pragma GCC diagnostic ignored "-Wformat-nonliteral" snprintf(address, sizeof(address), save, name); #pragma GCC diagnostic warning "-Wformat-nonliteral" } else { return NULL; } return address; } inn-2.6.0/lib/date.c0000644000175200017520000006652212575023702013562 0ustar iuliusiulius/* $Id: date.c 9082 2010-07-11 09:27:43Z iulius $ ** ** Date parsing and conversion routines. ** ** Provides various date parsing and conversion routines, including ** generating Date headers for posted articles. */ #include "config.h" #include "clibrary.h" #include #include #include "inn/libinn.h" /* ** Time constants. ** ** Do not translate these names. RFC 5322 by way of RFC 5536 requires that ** weekday and month names *not* be translated. This is why we use static ** tables rather than strftime for building dates, to avoid locale ** interference. */ static const char WEEKDAY[7][4] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; static const char MONTH[12][4] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; /* Complete month names, used only for lax date parsing. */ static const char OBS_MONTH[12][10] = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" }; /* Number of days in a month. */ static const int MONTHDAYS[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; /* Non-numeric time zones. Supporting these is required to support the obsolete date format of RFC 5322. The military time zones are handled separately. */ static const struct { const char name[4]; long offset; } ZONE_OFFSET[] = { { "UT", 0 }, { "GMT", 0 }, { "EDT", -4 * 60 * 60 }, { "EST", -5 * 60 * 60 }, { "CDT", -5 * 60 * 60 }, { "CST", -6 * 60 * 60 }, { "MDT", -6 * 60 * 60 }, { "MST", -7 * 60 * 60 }, { "PDT", -7 * 60 * 60 }, { "PST", -8 * 60 * 60 }, }; /* Additional non-numeric time zones supported because the old parsedate parser supported them. These aren't legal in RFC 5322, but are supported in lax mode. */ static const struct { const char name[5]; long offset; } OBS_ZONE_OFFSET[] = { { "UTC", 0 }, /* Universal Coordinated */ { "CUT", 0 }, /* Coordinated Universal */ { "WET", 0 }, /* Western European */ { "BST", 1 * 60 * 60 }, /* British Summer */ { "NDT", (-2 * 60 + 30) * 60 }, /* Newfoundland Daylight */ { "NST", (-3 * 60 + 30) * 60 }, /* Newfoundland Standard */ { "ADT", -3 * 60 * 60 }, /* Atlantic Daylight */ { "AST", -4 * 60 * 60 }, /* Atlantic Standard */ { "YDT", -8 * 60 * 60 }, /* Yukon Daylight */ { "YST", -9 * 60 * 60 }, /* Yukon Standard */ { "AKDT", -8 * 60 * 60 }, /* Alaska Daylight */ { "AKST", -9 * 60 * 60 }, /* Alaska Standard */ { "HADT", -9 * 60 * 60 }, /* Hawaii-Aleutian Daylight */ { "HAST", -10 * 60 * 60 }, /* Hawaii-Aleutian Standard */ { "HST", -10 * 60 * 60 }, /* Hawaii Standard */ { "CES", 2 * 60 * 60 }, /* Central European Summer */ { "CEST", 2 * 60 * 60 }, /* Central European Summer */ { "MEZ", 1 * 60 * 60 }, /* Middle European */ { "MEZT", 2 * 60 * 60 }, /* Middle European Summer */ { "CET", 1 * 60 * 60 }, /* Central European */ { "MET", 1 * 60 * 60 }, /* Middle European */ { "EET", 2 * 60 * 60 }, /* Eastern European */ { "MSK", 3 * 60 * 60 }, /* Moscow Winter */ { "MSD", 4 * 60 * 60 }, /* Moscow Summer */ { "WAST", 8 * 60 * 60 }, /* Western Australian Standard */ { "WADT", 9 * 60 * 60 }, /* Western Australian Daylight */ { "HKT", 8 * 60 * 60 }, /* Hong Kong */ { "CCT", 8 * 60 * 60 }, /* China Coast */ { "JST", 9 * 60 * 60 }, /* Japan Standard */ { "KST", 9 * 60 * 60 }, /* Korean Standard */ { "KDT", 9 * 60 * 60 }, /* Korean Daylight (no change?) */ { "CAST", (9 * 60 + 30) * 60 }, /* Central Australian Standard */ { "CADT", (10 * 60 + 30) * 60 }, /* Central Australian Daylight */ { "EAST", 10 * 60 * 60 }, /* Eastern Australian Standard */ { "EADT", 11 * 60 * 60 }, /* Eastern Australian Daylight */ { "NZST", 12 * 60 * 60 }, /* New Zealand Standard */ { "NZST", 13 * 60 * 60 }, /* New Zealand Daylight */ }; /* ** Time parsing macros. */ /* Whether a given year is a leap year. */ #define ISLEAP(year) \ (((year) % 4) == 0 && (((year) % 100) != 0 || ((year) % 400) == 0)) /* ** RFC 5322 date parsing rules. */ /* The data structure to store a rule. The interpretation of the other fields is based on the value of type. For NUMBER, read between min and max characters and convert to a number. For LOOKUP, look for max characters and find that string in the provided table (with size elements). For DELIM, just make sure that we see the character stored in delimiter. */ struct rule { enum { TYPE_NUMBER, TYPE_LOOKUP, TYPE_OBS_MONTH, TYPE_DELIM } type; char delimiter; const char (*table)[4]; size_t size; int min; int max; }; /* ** Given a time as a time_t, return the offset in seconds of the local time ** zone from UTC at that time (adding the offset to UTC time yields local ** time). If the second argument is true, the time represents the current ** time and in that circumstance we can assume that timezone/altzone are ** correct. (We can't for arbitrary times in the past.) */ static long local_tz_offset(time_t date, bool current UNUSED) { struct tm *tm; #if !HAVE_STRUCT_TM_TM_GMTOFF struct tm local, gmt; long offset; #endif tm = localtime(&date); #if !HAVE_STRUCT_TM_TM_GMTOFF && HAVE_DECL_ALTZONE if (current) return (tm->tm_isdst > 0) ? -altzone : -timezone; #endif #if HAVE_STRUCT_TM_TM_GMTOFF return tm->tm_gmtoff; #else /* We don't have any easy returnable value, so we call both localtime and gmtime and calculate the difference. Assume that local time is never more than 24 hours away from UTC and ignore seconds. */ local = *tm; tm = gmtime(&date); gmt = *tm; offset = local.tm_yday - gmt.tm_yday; if (offset < -1) { /* Local time is in the next year. */ offset = 24; } else if (offset > 1) { /* Local time is in the previous year. */ offset = -24; } else { offset *= 24; } offset += local.tm_hour - gmt.tm_hour; offset *= 60; offset += local.tm_min - gmt.tm_min; return offset * 60; #endif /* !HAVE_TM_GMTOFF */ } /* ** Given a time_t, a flag saying whether to use local time, a buffer, and ** the length of the buffer, write the contents of a valid RFC 5322 / RFC ** 5536 Date header into the buffer (provided it's long enough). Returns ** true on success, false if the buffer is too long. Use snprintf rather ** than strftime to be absolutely certain that locales don't result in the ** wrong output. If the time is -1, obtain and use the current time. */ bool makedate(time_t date, bool local, char *buff, size_t buflen) { time_t realdate; struct tm *tmp_tm; struct tm tm; long tz_offset; int tz_hour_offset, tz_min_offset, tz_sign; size_t date_length; const char *tz_name; /* Make sure the buffer is large enough. A complete RFC 5322 date with spaces wherever FWS is required and the optional weekday takes: 1 2 3 1234567890123456789012345678901 Sat, 31 Aug 2002 23:45:18 +0000 31 characters, plus another character for the trailing nul. The buffer will need to have at least another six characters of space to get the optional trailing time zone comment. */ if (buflen < 32) return false; /* Get the current time if the provided time is -1. */ realdate = (date == (time_t) -1) ? time(NULL) : date; /* RFC 5322 says the timezone offset is given as [+-]HHMM, so we have to separate the offset into a sign, hours, and minutes. Dividing the offset by 36 looks like it works, but will fail for any offset that isn't an even number of hours, and there are half-hour timezones. */ if (local) { tmp_tm = localtime(&realdate); tm = *tmp_tm; tz_offset = local_tz_offset(realdate, date == (time_t) -1); tz_sign = (tz_offset < 0) ? -1 : 1; tz_offset *= tz_sign; tz_hour_offset = tz_offset / 3600; tz_min_offset = (tz_offset % 3600) / 60; } else { tmp_tm = gmtime(&realdate); tm = *tmp_tm; tz_sign = 1; tz_hour_offset = 0; tz_min_offset = 0; } /* tz_min_offset cannot be larger than 60 (by basic mathematics). If through some insane circumtances, tz_hour_offset would be larger, reject the time as invalid rather than generate an invalid date. */ if (tz_hour_offset > 24) return false; /* Generate the actual date string, sans the trailing time zone comment but with the day of the week and the seconds (both of which are optional in the standard). */ snprintf(buff, buflen, "%3.3s, %d %3.3s %d %02d:%02d:%02d %c%02d%02d", &WEEKDAY[tm.tm_wday][0], tm.tm_mday, &MONTH[tm.tm_mon][0], 1900 + tm.tm_year, tm.tm_hour, tm.tm_min, tm.tm_sec, (tz_sign > 0) ? '+' : '-', tz_hour_offset, tz_min_offset); date_length = strlen(buff); /* Now, get a pointer to the time zone abbreviation, and if there is enough room in the buffer, add it to the end of the date string as a comment. */ if (!local) { tz_name = "UTC"; } else { #if HAVE_STRUCT_TM_TM_ZONE tz_name = tm.tm_zone; #elif HAVE_TZNAME tz_name = tzname[(tm.tm_isdst > 0) ? 1 : 0]; #else tz_name = NULL; #endif } if (tz_name != NULL && date_length + 4 + strlen(tz_name) <= buflen) { snprintf(buff + date_length, buflen - date_length, " (%s)", tz_name); } return true; } /* ** Given a struct tm representing a calendar time in UTC, convert it to ** seconds since epoch. Returns (time_t) -1 if the time is not ** convertable. Note that this function does not canonicalize the provided ** struct tm, nor does it allow out of range values or years before 1970. */ static time_t mktime_utc(const struct tm *tm) { time_t result = 0; int i; /* We do allow some ill-formed dates, but we don't do anything special with them and our callers really shouldn't pass them to us. Do explicitly disallow the ones that would cause invalid array accesses or other algorithm problems. */ if (tm->tm_mon < 0 || tm->tm_mon > 11 || tm->tm_year < 70) return (time_t) -1; /* Convert to a time_t. */ for (i = 1970; i < tm->tm_year + 1900; i++) result += 365 + ISLEAP(i); for (i = 0; i < tm->tm_mon; i++) result += MONTHDAYS[i]; if (tm->tm_mon > 1 && ISLEAP(tm->tm_year + 1900)) result++; result = 24 * (result + tm->tm_mday - 1) + tm->tm_hour; result = 60 * result + tm->tm_min; result = 60 * result + tm->tm_sec; return result; } /* ** Check the ranges of values in a struct tm to make sure that the date was ** well-formed. Assumes that the year has already been correctly set to ** something (but may be before 1970). */ static bool valid_tm(const struct tm *tm) { if (tm->tm_sec > 60 || tm->tm_min > 59 || tm->tm_hour > 23) return false; if (tm->tm_mday < 1 || tm->tm_mon < 0 || tm->tm_mon > 11) return false; /* Make sure that the day isn't past the end of the month, allowing for leap years. */ if (tm->tm_mday > MONTHDAYS[tm->tm_mon] && (tm->tm_mon != 1 || tm->tm_mday > 29 || !ISLEAP(tm->tm_year + 1900))) return false; /* We can't handle years before 1970. */ if (tm->tm_year < 70) return false; return true; } /* ** Parse a date in the format used in NNTP commands such as NEWGROUPS and ** NEWNEWS. The first argument is a string of the form YYYYMMDD and the ** second a string of the form HHMMSS. The third argument is a boolean ** flag saying whether the date is specified in local time; if false, the ** date is assumed to be in UTC. Returns the time_t corresponding to the ** given date and time or (time_t) -1 in the event of an error. */ time_t parsedate_nntp(const char *date, const char *hour, bool local) { const char *p; size_t datelen; time_t now, result; struct tm tm; struct tm *current; int century; /* Accept YYMMDD and YYYYMMDD. */ datelen = strlen(date); if ((datelen != 6 && datelen != 8) || strlen(hour) != 6) return (time_t) -1; for (p = date; *p; p++) if (!isdigit((unsigned char) *p)) return (time_t) -1; for (p = hour; *p; p++) if (!isdigit((unsigned char) *p)) return (time_t) -1; /* Parse the date into a struct tm, skipping over the century part of the year, if any. We'll deal with it in a moment. */ tm.tm_isdst = -1; p = date + datelen - 6; tm.tm_year = (p[0] - '0') * 10 + p[1] - '0'; tm.tm_mon = (p[2] - '0') * 10 + p[3] - '0' - 1; tm.tm_mday = (p[4] - '0') * 10 + p[5] - '0'; p = hour; tm.tm_hour = (p[0] - '0') * 10 + p[1] - '0'; tm.tm_min = (p[2] - '0') * 10 + p[3] - '0'; tm.tm_sec = (p[4] - '0') * 10 + p[5] - '0'; /* Four-digit years are the easy case. For two-digit years, RFC 3977 says: If the first two digits of the year are not specified, the year is to be taken from the current century if YY is smaller than or equal to the current year, and the previous century otherwise. This implementation assumes "current year" means the last two digits of the current year. Note that this algorithm interacts poorly with clients with a slightly fast clock around the turn of a century, as it may send 00 for the year when the year on the server is still xx99 and have it taken to be 99 years in the past. But 2000 has come and gone, and by 2100 news clients *really* should have started using UTC for everything like the new draft recommends. */ if (datelen == 8) { tm.tm_year += (date[0] - '0') * 1000 + (date[1] - '0') * 100; tm.tm_year -= 1900; } else { now = time(NULL); current = local ? localtime(&now) : gmtime(&now); century = current->tm_year / 100; if (tm.tm_year > current->tm_year % 100) century--; tm.tm_year += century * 100; } /* Ensure that all of the date components are within valid ranges. */ if (!valid_tm(&tm)) return (time_t) -1; /* tm contains the broken-down date; convert it to a time_t. mktime assumes the supplied struct tm is in the local time zone; if given a time in UTC, use our own routine instead. */ result = local ? mktime(&tm) : mktime_utc(&tm); return result; } /* ** Parse a single number. Takes the parsing rule that we're applying and ** returns a pointer to the new position of the parse stream. If there ** aren't enough digits, return NULL. */ static const char * parse_number(const char *p, const struct rule *rule, int *value) { int count; *value = 0; for (count = 0; *p != '\0' && count < rule->max; p++, count++) { if (*p < '0' || *p > '9') break; *value = *value * 10 + (*p - '0'); } if (count < rule->min || count > rule->max) return NULL; return p; } /* ** Parse a single string value that has to be done via table lookup. Takes ** the parsing rule that we're applying. Puts the index number of the string ** if found into the value pointerand returns the new position of the string, ** or NULL if the string could not be found in the table. */ static const char * parse_lookup(const char *p, const struct rule *rule, int *value) { size_t i; for (i = 0; i < rule->size; i++) if (strncasecmp(rule->table[i], p, rule->max) == 0) { p += rule->max; *value = i; return p; } return NULL; } /* ** Parse a single string value that should be an obsolete month name. If we ** have three characters available, check against the abbreviation. Likewise ** if we have four characters and the fourth is a period. Otherwise, check ** against the full English month name. Puts the month number into the value ** pointer and and returns the new position of the string, or NULL if the ** string is not a valid month. */ static const char * parse_legacy_month(const char *p, const struct rule *rule UNUSED, int *value) { size_t i, size; const char *end; for (end = p; *end != '\0' && isalpha((unsigned char) *end); end++) ; if (*end == '.') end++; if (end == p) return NULL; size = end - p; if (size == 3 || (size == 4 && p[3] == '.')) { for (i = 0; i < ARRAY_SIZE(MONTH); i++) if (strncasecmp(MONTH[i], p, 3) == 0) { p += size; *value = i; return p; } } else { for (i = 0; i < ARRAY_SIZE(OBS_MONTH); i++) { if (size != strlen(OBS_MONTH[i])) continue; if (strncasecmp(OBS_MONTH[i], p, size) == 0) { p += size; *value = i; return p; } } } return NULL; } /* ** Apply a set of date parsing rules to a string. Returns the new position ** in the parse string if this succeeds and NULL if it fails. As part of the ** parse, stores values into the value pointer in the array of rules that was ** passed in. Takes an array of rules and a count of rules in that array. */ static const char * parse_by_rule(const char *p, const struct rule rules[], size_t count, int *values) { size_t i; const struct rule *rule; for (i = 0; i < count; i++) { rule = &rules[i]; switch (rule->type) { case TYPE_DELIM: if (*p != rule->delimiter) return NULL; p++; break; case TYPE_LOOKUP: p = parse_lookup(p, rule, &values[i]); break; case TYPE_OBS_MONTH: p = parse_legacy_month(p, rule, &values[i]); break; case TYPE_NUMBER: p = parse_number(p, rule, &values[i]); break; } if (p == NULL) return NULL; p = skip_cfws(p); } return p; } /* ** Parse a legacy time zone. This uses the parsing rules in RFC 5322, ** including assigning an offset of 0 to all single-character military time ** zones due to their ambiguity in practice. Returns the new position in the ** parse stream or NULL if we failed to parse the zone. If the obsolete flag ** is set, also check against obsolete time zones. */ static const char * parse_legacy_timezone(const char *p, long *offset, bool obsolete) { const char *end; size_t max, i; for (end = p; *end != '\0' && isalpha((unsigned char) *end); end++) ; if (end == p) return NULL; max = end - p; for (i = 0; i < ARRAY_SIZE(ZONE_OFFSET); i++) if (strncasecmp(ZONE_OFFSET[i].name, p, max) == 0) { p += strlen(ZONE_OFFSET[i].name); *offset = ZONE_OFFSET[i].offset; return p; } if (max == 1 && isalpha((unsigned char) *p) && *p != 'J' && *p != 'j') { *offset = 0; return p + 1; } if (obsolete) for (i = 0; i < ARRAY_SIZE(OBS_ZONE_OFFSET); i++) { if (strlen(OBS_ZONE_OFFSET[i].name) > max) continue; if (strncasecmp(OBS_ZONE_OFFSET[i].name, p, max) == 0) { p += strlen(OBS_ZONE_OFFSET[i].name); *offset = OBS_ZONE_OFFSET[i].offset; return p; } } return NULL; } /* ** Parse an RFC 5322 date, accepting the normal and obsolete syntax. Takes a ** pointer to the beginning of the date. Returns the translated time in ** seconds since epoch, or (time_t) -1 on error. */ time_t parsedate_rfc5322(const char *date) { const char *p; int zone_sign; long zone_offset; struct tm tm; int values[8]; time_t result; /* The basic rules. Note that we don't bother to check whether the day of the week is accurate or not. */ static const struct rule base_rule[] = { { TYPE_LOOKUP, 0, WEEKDAY, 7, 3, 3 }, { TYPE_DELIM, ',', NULL, 0, 1, 1 }, { TYPE_NUMBER, 0, NULL, 0, 1, 2 }, { TYPE_LOOKUP, 0, MONTH, 12, 3, 3 }, { TYPE_NUMBER, 0, NULL, 0, 2, 4 }, { TYPE_NUMBER, 0, NULL, 0, 2, 2 }, { TYPE_DELIM, ':', NULL, 0, 1, 1 }, { TYPE_NUMBER, 0, NULL, 0, 2, 2 } }; /* Optional seconds at the end of the time. */ static const struct rule seconds_rule[] = { { TYPE_DELIM, ':', NULL, 0, 1, 1 }, { TYPE_NUMBER, 0, NULL, 0, 2, 2 } }; /* Numeric time zone. Keep the hours and minutes separate. */ static const struct rule zone_rule[] = { { TYPE_NUMBER, 0, NULL, 0, 2, 2 }, { TYPE_NUMBER, 0, NULL, 0, 2, 2 } }; /* Start with a clean slate. */ memset(&tm, 0, sizeof(struct tm)); memset(values, 0, sizeof(values)); /* Parse the base part of the date. The initial day of the week is optional. */ p = skip_cfws(date); if (isalpha((unsigned char) *p)) p = parse_by_rule(p, base_rule, ARRAY_SIZE(base_rule), values); else p = parse_by_rule(p, base_rule + 2, ARRAY_SIZE(base_rule) - 2, values + 2); if (p == NULL) return (time_t) -1; /* Stash the results into a struct tm. Values are associated with the rule number of the same index. */ tm.tm_mday = values[2]; tm.tm_mon = values[3]; tm.tm_year = values[4]; tm.tm_hour = values[5]; tm.tm_min = values[7]; /* Parse seconds if they're present. */ if (*p == ':') { p = parse_by_rule(p, seconds_rule, ARRAY_SIZE(seconds_rule), values); if (p == NULL) return (time_t) -1; tm.tm_sec = values[1]; } /* Time zone. Unfortunately this is weird enough that we can't use nice parsing rules for it. */ if (*p == '-' || *p == '+') { zone_sign = (*p == '+') ? 1 : -1; p = parse_by_rule(p + 1, zone_rule, ARRAY_SIZE(zone_rule), values); if (p == NULL) return (time_t) -1; zone_offset = (values[0] * 60 + values[1]) * 60; zone_offset *= zone_sign; } else { p = parse_legacy_timezone(p, &zone_offset, false); if (p == NULL) return (time_t) -1; } /* Fix up the year, using the RFC 5322 rules. Remember that tm_year stores the year - 1900. */ if (tm.tm_year < 50) tm.tm_year += 100; else if (tm.tm_year >= 1000) tm.tm_year -= 1900; /* Done parsing. Make sure there's nothing left but CFWS and range-check our results and then convert the struct tm to seconds since epoch and then apply the time zone offset. */ p = skip_cfws(p); if (*p != '\0') return (time_t) -1; if (!valid_tm(&tm)) return (time_t) -1; result = mktime_utc(&tm); return (result == (time_t) -1) ? result : result - zone_offset; } /* ** Parse a date, accepting a lax syntax that tries to allow for any vaguely ** RFC-5322-like date that the old parsedate code would accept, allowing for ** a lot of variation that's seen in Usenet articles. Takes a pointer to the ** date and returns the translated time in seconds since epoch, or (time_t) ** -1 on error. */ time_t parsedate_rfc5322_lax(const char *date) { const char *p, *start; struct tm tm; int values[6]; bool have_zone; int zone_sign; long zone_offset; time_t result; /* The basic rules. Allow one or two digits in time components, since some broken software omits the leading zero and parsedate didn't care. */ static const struct rule base_rule[] = { { TYPE_NUMBER, 0, NULL, 0, 1, 2 }, { TYPE_OBS_MONTH, 0, NULL, 12, 3, 3 }, { TYPE_NUMBER, 0, NULL, 0, 2, 4 }, { TYPE_NUMBER, 0, NULL, 0, 1, 2 }, { TYPE_DELIM, ':', NULL, 0, 1, 1 }, { TYPE_NUMBER, 0, NULL, 0, 1, 2 } }; /* Optional seconds at the end of the time. Similarly, don't require the leading zero. */ static const struct rule seconds_rule[] = { { TYPE_DELIM, ':', NULL, 0, 1, 1 }, { TYPE_NUMBER, 0, NULL, 0, 1, 2 } }; /* Numeric time zone. Allow the hour portion to omit the leading zero. Unfortunately, our parser is greedy, so we have to parse this as one number and then patch it up later. */ static const struct rule zone_rule[] = { { TYPE_NUMBER, 0, NULL, 0, 1, 5 } }; /* Start with a clean slate. */ memset(&tm, 0, sizeof(struct tm)); memset(values, 0, sizeof(values)); /* Parse the base part of the date. The initial day of the week is optional. Allow for anything that looks vaguely day-like. */ p = skip_cfws(date); while (*p != '\0' && !isdigit((unsigned char) *p) && *p != ',') p++; if (*p == ',') p = skip_cfws(p + 1); p = parse_by_rule(p, base_rule, ARRAY_SIZE(base_rule), values); if (p == NULL) return (time_t) -1; /* Stash the results into a struct tm. Values are associated with the rule number of the same index. */ tm.tm_mday = values[0]; tm.tm_mon = values[1]; tm.tm_year = values[2]; tm.tm_hour = values[3]; tm.tm_min = values[5]; /* Parse seconds if they're present. */ if (*p == ':') { p = parse_by_rule(p, seconds_rule, ARRAY_SIZE(seconds_rule), values); if (p == NULL) return (time_t) -1; tm.tm_sec = values[1]; } /* Time zone. Unfortunately this is weird enough that we can't use nice parsing rules for it. If we don't recognize the time zone at all, just bail and assume GMT. parsedate used the final time zone found, when multiple ones were supplied, so emulate that behavior. */ have_zone = false; zone_offset = 0; while (p != NULL && *p != '\0') { if (*p == '-' || *p == '+') { zone_sign = (*p == '+') ? 1 : -1; start = p + 1; p = parse_by_rule(start, zone_rule, ARRAY_SIZE(zone_rule), values); if (p == NULL) return (time_t) -1; if (p - start < 3) zone_offset = values[0] * 60 * 60; else zone_offset = ((values[0] / 100) * 60 + values[0] % 100) * 60; zone_offset *= zone_sign; } else { p = parse_legacy_timezone(p, &zone_offset, true); if (p == NULL) zone_offset = 0; } have_zone = true; if (p != NULL) p = skip_cfws(p); } /* Fix up the year, using the RFC 5322 rules. Remember that tm_year stores the year - 1900. */ if (tm.tm_year < 50) tm.tm_year += 100; else if (tm.tm_year >= 1000) tm.tm_year -= 1900; /* Done parsing. We don't check to see if this is the end of the string; we allow for any trailing garbage. Convert the struct tm to seconds since epoch and then apply the time zone offset. */ if (!valid_tm(&tm)) return (time_t) -1; if (have_zone) result = mktime_utc(&tm); else { tm.tm_isdst = -1; result = mktime(&tm); } return (result == (time_t) -1) ? result : result - zone_offset; } inn-2.6.0/lib/localopen.c0000644000175200017520000000420212575023702014604 0ustar iuliusiulius/* $Id: localopen.c 9761 2014-12-04 20:47:19Z iulius $ ** */ #include "config.h" #include "clibrary.h" #include #include #include "inn/innconf.h" #include "inn/libinn.h" #include "inn/nntp.h" #include "inn/paths.h" #ifdef HAVE_UNIX_DOMAIN_SOCKETS # include "portable/socket-unix.h" #endif /* ** Open a connection to the local InterNetNews NNTP server and optionally ** create stdio FILE's for talking to it. Return -1 on error. */ int NNTPlocalopen(FILE **FromServerp, FILE **ToServerp, char *errbuff, size_t len) { #if defined(HAVE_UNIX_DOMAIN_SOCKETS) int i; int j; int oerrno; struct sockaddr_un server; FILE *F; char mybuff[NNTP_MAXLEN_COMMAND + 2]; char *buff; if (errbuff) buff = errbuff; else { buff = mybuff; len = sizeof(mybuff); } *buff = '\0'; /* Create a socket. */ if ((i = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) return -1; /* Connect to the server. */ memset(&server, 0, sizeof server); server.sun_family = AF_UNIX; strlcpy(server.sun_path, innconf->pathrun, sizeof(server.sun_path)); strlcat(server.sun_path, "/", sizeof(server.sun_path)); strlcat(server.sun_path, INN_PATH_NNTPCONNECT, sizeof(server.sun_path)); if (connect(i, (struct sockaddr *)&server, SUN_LEN(&server)) < 0) { oerrno = errno; close(i); errno = oerrno; return -1; } /* Connected -- now make sure we can post. */ if ((F = fdopen(i, "r")) == NULL) { oerrno = errno; close(i); errno = oerrno; return -1; } if (fgets(buff, len, F) == NULL) { oerrno = errno; fclose(F); errno = oerrno; return -1; } j = atoi(buff); if (j != NNTP_OK_BANNER_POST && j != NNTP_OK_BANNER_NOPOST) { fclose(F); /* This seems like a reasonable error code to use... */ errno = EPERM; return -1; } *FromServerp = F; if ((*ToServerp = fdopen(dup(i), "w")) == NULL) { oerrno = errno; fclose(F); errno = oerrno; return -1; } return 0; #else return NNTPconnect("127.0.0.1", innconf->port, FromServerp, ToServerp, errbuff, len); #endif /* defined(HAVE_UNIX_DOMAIN_SOCKETS) */ } inn-2.6.0/lib/headers.c0000644000175200017520000000517212575023702014252 0ustar iuliusiulius/* $Id: headers.c 9019 2010-03-19 21:27:15Z iulius $ ** ** Routines for headers: manipulation and checks. */ #include "config.h" #include "clibrary.h" #include #include "inn/libinn.h" /* ** We currently only check the requirements for RFC 3977: ** ** o The name [of a header] consists of one or more printable ** US-ASCII characters other than colon. */ bool IsValidHeaderName(const char *string) { const unsigned char *p; /* Not NULL. */ if (string == NULL) return false; p = (const unsigned char *) string; /* Not empty. */ if (*p == '\0') return false; for (; *p != '\0'; p++) { /* Contains only printable US-ASCII characters other * than colon. */ if (!isgraph((unsigned char) *p) || *p == ':') return false; } return true; } /* ** Skip any amount of CFWS (comments and folding whitespace), the RFC 5322 ** grammar term for whitespace, CRLF pairs, and possibly nested comments that ** may contain escaped parens. We also allow simple newlines since we don't ** always deal with wire-format messages. Note that we do not attempt to ** ensure that CRLF or a newline is followed by whitespace. Returns the new ** position of the pointer. */ const char * skip_cfws(const char *p) { int nesting = 0; for (; *p != '\0'; p++) { switch (*p) { case ' ': case '\t': case '\n': break; case '\r': if (p[1] != '\n' && nesting == 0) return p; break; case '(': nesting++; break; case ')': if (nesting == 0) return p; nesting--; break; case '\\': if (nesting == 0 || p[1] == '\0') return p; p++; break; default: if (nesting == 0) return p; break; } } return p; } /* ** Skip any amount of FWS (folding whitespace), the RFC 5322 grammar term ** for whitespace and CRLF pairs. We also allow simple newlines since we don't ** always deal with wire-format messages. Note that we do not attempt to ** ensure that CRLF or a newline is followed by whitespace. Returns the new ** position of the pointer. */ const char * skip_fws(const char *p) { for (; *p != '\0'; p++) { switch (*p) { case ' ': case '\t': case '\n': break; case '\r': if (p[1] != '\n') return p; break; default: return p; } } return p; } inn-2.6.0/lib/tst.c0000644000175200017520000003244112575023702013450 0ustar iuliusiulius/* $Id: tst.c 7585 2006-11-21 09:37:51Z eagle $ ** ** Ternary search trie implementation. ** ** A ternary search trie stores key/value pairs where the key is a ** nul-terminated string and the value is an arbitrary pointer. It uses a ** data structure designed for fast lookups, where each level of the trie ** represents a character in the string being searched for. ** ** This implementation is based on the implementation by Peter A. Friend ** (version 1.3), but has been assimilated into INN and modified to use INN ** formatting conventions. If new versions are released, examine the ** differences between that version and version 1.3 (which was checked into ** INN as individual files in case it's no longer available) and then apply ** the changes to this file. ** ** Copyright (c) 2002, Peter A. Friend ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** ** Redistributions of source code must retain the above copyright notice, ** this list of conditions and the following disclaimer. ** ** Redistributions in binary form must reproduce the above copyright notice, ** this list of conditions and the following disclaimer in the documentation ** and/or other materials provided with the distribution. ** ** Neither the name of Peter A. Friend nor the names of his contributors may ** be used to endorse or promote products derived from this software without ** specific prior written permission. ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ** IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ** THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ** PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ** LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ** NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include "clibrary.h" #include "inn/tst.h" #include "inn/libinn.h" /* A single node in the ternary search trie. Stores a character, which is part of the string formed by walking the tree from its root down to the node, and left, right, and middle pointers to child nodes. If value is non-zero (not a nul), middle is the pointer to follow if the desired string's character matches value. left is used if it's less than value and right is used if it's greater than value. If value is zero, this is a terminal node, and middle holds a pointer to the data associated with the string that ends at this point. */ struct node { unsigned char value; struct node *left; struct node *middle; struct node *right; }; /* The search trie structure. node_line_width is the number of nodes that are allocated at a time, and node_lines holds a linked list of groups of nodes. The free_list is a linked list (through the middle pointers) of available nodes to use, and head holds pointers to the first nodes for each possible first letter of the string. */ struct tst { int node_line_width; struct node_lines *node_lines; struct node *free_list; struct node *head[256]; }; /* A simple linked list structure used to hold all the groups of nodes. */ struct node_lines { struct node *node_line; struct node_lines *next; }; /* ** Given a node and a character, decide whether a new node for that character ** should be placed as the left child of that node. (If false, it should be ** placed as the right child.) */ #define LEFTP(n, c) (((n)->value == 0) ? ((c) < 64) : ((c) < (n)->value)) /* ** Allocate new nodes for the free list, called when the free list is empty. */ static void tst_grow_node_free_list(struct tst *tst) { struct node *current_node; struct node_lines *new_line; int i; new_line = xmalloc(sizeof(struct node_lines)); new_line->node_line = xcalloc(tst->node_line_width, sizeof(struct node)); new_line->next = tst->node_lines; tst->node_lines = new_line; current_node = tst->node_lines->node_line; tst->free_list = current_node; for (i = 1; i < tst->node_line_width; i++) { current_node->middle = &(tst->node_lines->node_line[i]); current_node = current_node->middle; } current_node->middle = NULL; } /* ** Grab a node from the free list and initialize it with the given value. */ static struct node * tst_get_free_node(struct tst *tst, unsigned char value) { struct node *free_node; if (tst->free_list == NULL) tst_grow_node_free_list(tst); free_node = tst->free_list; tst->free_list = tst->free_list->middle; free_node->middle = NULL; free_node->value = value; return free_node; } /* ** tst_init allocates memory for members of struct tst, and allocates the ** first node_line_width nodes. The value for width must be chosen very ** carefully. One node is required for every character in the tree. If you ** choose a value that is too small, your application will spend too much ** time calling malloc and your node space will be too spread out. Too large ** a value is just a waste of space. */ struct tst * tst_init(int width) { struct tst *tst; tst = xcalloc(1, sizeof(struct tst)); tst->node_lines = NULL; tst->node_line_width = width; tst_grow_node_free_list(tst); return tst; } /* ** tst_insert inserts the string key into the tree. Behavior when a ** duplicate key is inserted is controlled by option. If key is already in ** the tree then TST_DUPLICATE_KEY is returned, and the data pointer for the ** existing key is placed in exist_ptr. If option is set to TST_REPLACE then ** the existing data pointer for the existing key is replaced by data. The ** old data pointer will still be placed in exist_ptr. ** ** If a duplicate key is encountered and option is not set to TST_REPLACE ** then TST_DUPLICATE_KEY is returned. If key is zero-length, then ** TST_NULL_KEY is returned. A successful insert or replace returns TST_OK. ** ** The data argument may not be NULL; if it is, TST_NULL_DATA is returned. ** If you just want a simple existence tree, use the tst pointer as the data ** pointer. */ int tst_insert(struct tst *tst, const unsigned char *key, void *data, int option, void **exist_ptr) { struct node *current_node = NULL; struct node **root_node = NULL; int key_index; if (data == NULL) return TST_NULL_DATA; if (key == NULL || *key == '\0') return TST_NULL_KEY; key_index = 1; if (tst->head[*key] == NULL) root_node = &tst->head[*key]; else current_node = tst->head[*key]; while (root_node == NULL) { if (key[key_index] == current_node->value) { if (key[key_index] == '\0') { if (exist_ptr != NULL) *exist_ptr = current_node->middle; if (option == TST_REPLACE) { current_node->middle = data; return TST_OK; } else return TST_DUPLICATE_KEY; } if (current_node->middle == NULL) root_node = ¤t_node->middle; else { current_node = current_node->middle; key_index++; } } else if (LEFTP(current_node, key[key_index])) { if (current_node->left == NULL) root_node = ¤t_node->left; else current_node = current_node->left; } else { if (current_node->right == NULL) root_node = ¤t_node->right; else current_node = current_node->right; } } *root_node = tst_get_free_node(tst, key[key_index]); current_node = *root_node; while (key[key_index] != '\0') { key_index++; current_node->middle = tst_get_free_node(tst, key[key_index]); current_node = current_node->middle; } current_node->middle = data; return TST_OK; } /* ** tst_search finds the string key in the tree if it exists and returns the ** data pointer associated with that key or NULL if it's not found. */ void * tst_search(struct tst *tst, const unsigned char *key) { struct node *current_node; int key_index; if (key == NULL || *key == '\0') return NULL; if (tst->head[*key] == NULL) return NULL; current_node = tst->head[*key]; key_index = 1; while (current_node != NULL) { if (key[key_index] == current_node->value) { if (current_node->value == '\0') return current_node->middle; else { current_node = current_node->middle; key_index++; continue; } } else if (LEFTP(current_node, key[key_index])) current_node = current_node->left; else current_node = current_node->right; } return NULL; } /* ** tst_delete deletes the string key from the tree if it exists and returns ** the data pointer assocaited with that key, or NULL if it wasn't found. */ void * tst_delete(struct tst *tst, const unsigned char *key) { struct node *current_node; struct node *current_node_parent; struct node *last_branch; struct node *last_branch_parent; struct node *next_node; struct node *last_branch_replacement; struct node *last_branch_dangling_child; int key_index; if (key == NULL || *key == '\0') return NULL; if (tst->head[*key] == NULL) return NULL; last_branch = NULL; last_branch_parent = NULL; current_node = tst->head[*key]; current_node_parent = NULL; key_index = 1; while (current_node != NULL) { if (key[key_index] == current_node->value) { if (current_node->left != NULL || current_node->right != NULL) { last_branch = current_node; last_branch_parent = current_node_parent; } if (key[key_index] == '\0') break; else { current_node_parent = current_node; current_node = current_node->middle; key_index++; } } else if (LEFTP(current_node, key[key_index])) { last_branch_parent = current_node; current_node_parent = current_node; current_node = current_node->left; last_branch = current_node; } else { last_branch_parent = current_node; current_node_parent = current_node; current_node = current_node->right; last_branch = current_node; } } if (current_node == NULL) return NULL; if (last_branch == NULL) { next_node = tst->head[*key]; tst->head[*key] = NULL; } else if (last_branch->left == NULL && last_branch->right == NULL) { if (last_branch_parent->left == last_branch) last_branch_parent->left = NULL; else last_branch_parent->right = NULL; next_node = last_branch; } else { if (last_branch->left != NULL && last_branch->right != NULL) { last_branch_replacement = last_branch->right; last_branch_dangling_child = last_branch->left; } else if (last_branch->right != NULL) { last_branch_replacement = last_branch->right; last_branch_dangling_child = NULL; } else { last_branch_replacement = last_branch->left; last_branch_dangling_child = NULL; } if (last_branch_parent == NULL) tst->head[*key] = last_branch_replacement; else { if (last_branch_parent->left == last_branch) last_branch_parent->left = last_branch_replacement; else if (last_branch_parent->right == last_branch) last_branch_parent->right = last_branch_replacement; else last_branch_parent->middle = last_branch_replacement; } if (last_branch_dangling_child != NULL) { current_node = last_branch_replacement; while (current_node->left != NULL) current_node = current_node->left; current_node->left = last_branch_dangling_child; } next_node = last_branch; } do { current_node = next_node; next_node = current_node->middle; current_node->left = NULL; current_node->right = NULL; current_node->middle = tst->free_list; tst->free_list = current_node; } while (current_node->value != 0); return next_node; } /* ** tst_cleanup frees all memory allocated to nodes, internal structures, ** as well as tst itself. */ void tst_cleanup(struct tst *tst) { struct node_lines *current_line; struct node_lines *next_line; next_line = tst->node_lines; do { current_line = next_line; next_line = current_line->next; free(current_line->node_line); free(current_line); } while (next_line != NULL); free(tst); } inn-2.6.0/lib/argparse.c0000644000175200017520000000625012575023702014441 0ustar iuliusiulius/* $Id: argparse.c 8674 2009-10-21 17:38:42Z iulius $ ** ** Routines for parsing arguments. */ #include "config.h" #include "clibrary.h" #include #include #include "inn/innconf.h" #include "inn/libinn.h" /* ** Parse a string into a NULL-terminated array of words; return number ** of words. If argvp isn't NULL, it and what it points to will be freed. */ int Argify(char *line, char ***argvp) { return nArgify(line, argvp, -1); } /* ** Parse a string into a NULL-terminated array of at most n words; ** return number of words. If there are more than n words, stop ** processing at the beginning of the (n+1)th word, store everything ** from the beginning of word (n+1) in argv[n] and return (n+1). ** If n is negative, parses all words. If argvp isn't NULL, it and ** what it points to will be freed. */ int nArgify(char *line, char ***argvp, int n) { char *p; if (*argvp != NULL) { free(*argvp[0]); free(*argvp); } /* Copy the line, which we will split up. */ while (ISWHITE(*line)) line++; p = xstrdup(line); /* Allocate worst-case amount of space. */ *argvp = xmalloc((strlen(p) + 2) * sizeof(char *)); return reArgify(p, *argvp, n, true); } /* ** Destructively parse a string into a NULL-terminated array of at most ** n words; return number of words. Behaviour on negative n and strings ** of more than n words matches that of nArgify (see above). Caller ** must supply an array of sufficient size (such as created by nArgify). ** ** Note that the sequence ** ac = nArgify(line, &argv, n1); ** ac--; ** ac += reArgify(argv[ac], &argv[ac], n2, true); ** is equivalent to ** ac = nArgify(line, &argv, n1 + n2); ** ** It is sometimes useful not to strip spaces. For instance ** "AUTHINFO PASS test" should be understood as giving the ** password " test", beginning with a space. */ int reArgify(char *p, char **argv, int n, bool stripspaces) { char **save = argv; /* Should never happen unless caller modifies argv between calls * or stripspaces has previously been set to false. */ if (stripspaces) { while (ISWHITE(*p)) p++; } for ( ; *p; ) { if (n == 0) { *argv++ = p; break; } /* Decrement limit, mark start of this word, find its end. */ for (n--, *argv++ = p; *p && !ISWHITE(*p); ) p++; if (*p == '\0') break; /* Nip off word. */ *p++ = '\0'; /* Skip whitespace. */ if (stripspaces) { while (ISWHITE(*p)) p++; } } *argv = NULL; return argv - save; } /* ** Take a vector which Argify made and glue it back together with ** spaces between each element. Returns a pointer to dynamic space. */ char * Glom(char **av) { char **v; int i; char *save; /* Get space. */ for (i = 0, v = av; *v; v++) i += strlen(*v) + 1; i++; save = xmalloc(i); save[0] = '\0'; for (v = av; *v; v++) { if (v > av) strlcat(save, " ", i); strlcat(save, *v, i); } return save; } inn-2.6.0/lib/fseeko.c0000644000175200017520000000235712575023702014115 0ustar iuliusiulius/* $Id: fseeko.c 3680 2000-07-29 22:54:52Z rra $ ** ** Replacement for a missing fseeko. ** ** fseeko is a version of fseek that takes an off_t instead of a long. For ** large file support (and because it's a more logical interface), INN uses ** fseeko unconditionally; if fseeko isn't provided by a platform but ** fpos_t is compatible with off_t (as in BSDI), define it in terms of ** fsetpos. Otherwise, just call fseek (which won't work for files over ** 2GB). */ #include "config.h" #include "clibrary.h" #include #if HAVE_LARGE_FPOS_T int fseeko(FILE *stream, off_t pos, int whence) { fpos_t fpos; switch (whence) { case SEEK_SET: fpos = pos; return fsetpos(stream, &fpos); case SEEK_END: if (fseek(stream, 0, SEEK_END) < 0) return -1; if (pos == 0) return 0; /* Fall through. */ case SEEK_CUR: if (fgetpos(stream, &fpos) < 0) return -1; fpos += pos; return fsetpos(stream, &fpos); default: errno = EINVAL; return -1; } } #else /* !HAVE_LARGE_FPOS_T */ int fseeko(FILE *stream, off_t pos, int whence) { return fseek(stream, (long) pos, whence); } #endif /* !HAVE_LARGE_FPOS_T */ inn-2.6.0/lib/strlcat.c0000644000175200017520000000351712575023702014314 0ustar iuliusiulius/* $Id: strlcat.c 9759 2014-12-04 20:11:38Z iulius $ * * Replacement for a missing strlcat. * * Provides the same functionality as the *BSD function strlcat, originally * developed by Todd Miller and Theo de Raadt. strlcat works similarly to * strncat, except simpler. The result is always nul-terminated even if the * source string is longer than the space remaining in the destination string, * and the total space required is returned. The third argument is the total * space available in the destination buffer, not just the amount of space * remaining. * * The canonical version of this file is maintained in the rra-c-util package, * which can be found at . * * Written by Russ Allbery * * The authors hereby relinquish any claim to any copyright that they may have * in this work, whether granted under contract or by operation of law or * international treaty, and hereby commit to the public, at large, that they * shall not, at any time in the future, seek to enforce any copyright in this * work against any person or entity, or prevent any person or entity from * copying, publishing, distributing or creating derivative works of this * work. */ #include "config.h" #include "clibrary.h" /* * If we're running the test suite, rename strlcat to avoid conflicts with * the system version. */ #if TESTING # undef strlcat # define strlcat test_strlcat size_t test_strlcat(char *, const char *, size_t); #endif size_t strlcat(char *dst, const char *src, size_t size) { size_t used, length, copy; used = strlen(dst); length = strlen(src); if (size > 0 && used < size - 1) { copy = (length >= size - used) ? size - used - 1 : length; memcpy(dst + used, src, copy); dst[used + copy] = '\0'; } return used + length; } inn-2.6.0/lib/radix32.c0000644000175200017520000000221012575023702014101 0ustar iuliusiulius/* $Id: radix32.c 7585 2006-11-21 09:37:51Z eagle $ ** ** Radix-32 strings divide a number into five-bit nibbles and use the ** alphabet 0..9a..v to represent 0..32. */ #include "config.h" #include "clibrary.h" #include #include "inn/libinn.h" static char ALPHABET[] = "0123456789abcdefghijklmnopqrstuv"; /* ** Turn a number into a Radix-32 string. Assume the number fits into ** 32 bits. */ void Radix32(unsigned long l, char *buff) { char *p; int i; char temp[10]; /* Simple sanity checks. */ if ((l &= 0xFFFFFFFFL) == 0) { *buff++ = ALPHABET[0]; *buff = '\0'; return; } /* Format the string, in reverse. */ for (p = temp; l; l >>= 5) *p++ = ALPHABET[(int)(l & 037)]; /* Reverse it. */ for (i = p - temp; --i >= 0; ) *buff++ = *--p; *buff = '\0'; } #if 0 /* ** Return a Radix-32 string as a number, or ~0 on error. */ unsigned long Decode32(p) char *p; { unsigned long l; char *cp; for (l = 0; *p; p++) { if ((cp = strchr(ALPHABET, *p)) == NULL) return ~0; l = (l << 6) + cp - ALPHABET; } return l; } #endif /* 0 */ inn-2.6.0/lib/qio.c0000644000175200017520000001205012575023702013420 0ustar iuliusiulius/* $Id: qio.c 9889 2015-06-03 19:05:15Z iulius $ ** ** Quick I/O package. ** ** A set of routines optimized for reading through files line by line. ** This package uses internal buffering like stdio, but is even more ** aggressive about its buffering. The basic read call reads a single line ** and returns the whole line, provided that it can fit in the buffer. */ #include "config.h" #include "clibrary.h" #include #include #include #include "inn/qio.h" #include "inn/libinn.h" /* A reasonable default buffer size to use. */ #define QIO_BUFFERSIZE 8192 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE #define NO_STRUCT_STAT_ST_BLKSIZE_UNUSED #else #define NO_STRUCT_STAT_ST_BLKSIZE_UNUSED UNUSED #endif /* ** Given a file descriptor, return a reasonable buffer size to use for that ** file. Uses st_blksize if available and reasonable, QIO_BUFFERSIZE ** otherwise. */ static size_t buffer_size(int fd NO_STRUCT_STAT_ST_BLKSIZE_UNUSED) { size_t size = QIO_BUFFERSIZE; #if HAVE_STRUCT_STAT_ST_BLKSIZE struct stat st; /* The Solaris 2.6 man page says that st_blksize is not defined for block or character special devices (and could contain garbage), so only use this value for regular files. */ if (fstat(fd, &st) == 0 && S_ISREG(st.st_mode)) { size = st.st_blksize; if (size > (4 * QIO_BUFFERSIZE) || size == 0) size = QIO_BUFFERSIZE; else while(size < QIO_BUFFERSIZE) size += st.st_blksize; } #endif /* HAVE_STRUCT_STAT_ST_BLKSIZE */ return size; } /* ** Open a quick file from a descriptor. */ QIOSTATE * QIOfdopen(const int fd) { QIOSTATE *qp; qp = xmalloc(sizeof(*qp)); qp->_fd = fd; qp->_length = 0; qp->_size = buffer_size(fd); qp->_buffer = xmalloc(qp->_size); qp->_start = qp->_buffer; qp->_end = qp->_buffer; qp->_count = 0; qp->_flag = QIO_ok; return qp; } /* ** Open a quick file from a file name. */ QIOSTATE * QIOopen(const char *name) { int fd; fd = open(name, O_RDONLY); if (fd < 0) return NULL; return QIOfdopen(fd); } /* ** Close an open quick file. */ void QIOclose(QIOSTATE *qp) { close(qp->_fd); free(qp->_buffer); free(qp); } /* ** Rewind a quick file. Reads the first buffer full of data automatically, ** anticipating the first read from the file. Returns -1 on error, 0 on ** success. */ int QIOrewind(QIOSTATE *qp) { ssize_t nread; if (lseek(qp->_fd, 0, SEEK_SET) < 0) return -1; nread = read(qp->_fd, qp->_buffer, qp->_size); if (nread < 0) return nread; qp->_count = nread; qp->_start = qp->_buffer; qp->_end = qp->_buffer + nread; return 0; } /* ** Get the next newline-terminated line from a quick file, replacing the ** newline with a nul. Returns a pointer to that line on success and NULL ** on failure or end of file, with _flag set appropriately. */ char * QIOread(QIOSTATE *qp) { char *p, *line; ssize_t nread; size_t nleft; /* Loop until we get a result or fill the buffer. */ qp->_flag = QIO_ok; while (1) { nleft = qp->_end - qp->_start; /* If nleft <= 0, the buffer currently contains no data that hasn't previously been returned by QIOread, so we can overwrite the buffer with new data. Otherwise, first check the existing data to see if we have a full line. */ if (nleft <= 0) { qp->_start = qp->_buffer; qp->_end = qp->_buffer; } else { p = memchr(qp->_start, '\n', nleft); if (p != NULL) { *p = '\0'; qp->_length = p - qp->_start; line = qp->_start; qp->_start = p + 1; return (qp->_flag == QIO_long) ? NULL : line; } /* Not there. See if our buffer is full. If so, tag as having seen too long of a line. This will cause us to keep reading as normal until we finally see the end of a line and then return NULL. */ if (nleft >= qp->_size) { qp->_flag = QIO_long; qp->_start = qp->_end; nleft = 0; } /* We need to read more data. If there's read data in buffer, then move the unread data down to the beginning of the buffer first. */ if (qp->_start > qp->_buffer) { if (nleft > 0) memmove(qp->_buffer, qp->_start, nleft); qp->_start = qp->_buffer; qp->_end = qp->_buffer + nleft; } } /* Read in some more data, and then let the loop try to find the newline again or discover that the line is too long. */ do { nread = read(qp->_fd, qp->_end, qp->_size - nleft); } while (nread == -1 && errno == EINTR); if (nread <= 0) { if (nread < 0) qp->_flag = QIO_error; return NULL; } qp->_count += nread; qp->_end += nread; } } inn-2.6.0/lib/network-innbind.c0000644000175200017520000003136612575023702015753 0ustar iuliusiulius/* $Id: network-innbind.c 9914 2015-07-07 16:34:36Z iulius $ * * Utility functions for network connections using innbind. * * This is a collection of utility functions for network connections and * socket creation, encapsulating some of the complexities of IPv4 and IPv6 * support and abstracting operations common to most network code. * * All of the portability difficulties with supporting IPv4 and IPv6 should be * encapsulated in the combination of this code and replacement * implementations for functions that aren't found on some pre-IPv6 systems. * No other part of the source tree should have to care about IPv4 vs. IPv6. * * This file is heavily based on lib/network.c. */ #include "config.h" #include "clibrary.h" #include "portable/socket.h" #include #ifdef HAVE_STREAMS_SENDFD # include #endif #include #include "inn/innconf.h" #include "inn/libinn.h" #include "inn/macros.h" #include "inn/messages.h" #include "inn/network.h" #include "inn/network-innbind.h" #include "inn/xmalloc.h" /* If SO_REUSEADDR isn't available, make calls to set_reuseaddr go away. */ #ifndef SO_REUSEADDR # define network_set_reuseaddr(fd) /* empty */ #endif /* If IPV6_V6ONLY isn't available, make calls to set_v6only go away. */ #ifndef IPV6_V6ONLY # define network_set_v6only(fd) /* empty */ #endif /* If IP_FREEBIND isn't available, make calls to set_freebind go away. */ #ifndef IP_FREEBIND # define network_set_freebind(fd) /* empty */ #endif /* * Set SO_REUSEADDR on a socket if possible (so that something new can listen * on the same port immediately if the daemon dies unexpectedly). */ #ifdef SO_REUSEADDR static void network_set_reuseaddr(socket_type fd) { int flag = 1; const void *flagaddr = &flag; if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, flagaddr, sizeof(flag)) < 0) syswarn("cannot mark bind address reusable"); } #endif /* * Set IPV6_V6ONLY on a socket if possible, since the IPv6 behavior is more * consistent and easier to understand. */ #ifdef IPV6_V6ONLY static void network_set_v6only(socket_type fd) { int flag = 1; if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &flag, sizeof(flag)) < 0) syswarn("cannot set IPv6 socket to v6only"); } #endif /* * Set IP_FREEBIND on a socket if possible, which allows binding servers to * IPv6 addresses that may not have been set up yet. */ #ifdef IP_FREEBIND static void network_set_freebind(socket_type fd) { int flag = 1; if (setsockopt(fd, IPPROTO_IP, IP_FREEBIND, &flag, sizeof(flag)) < 0) syswarn("cannot set IPv6 socket to free binding"); } #endif /* * Function used as a die handler in child processes to prevent any atexit * functions from being run and any buffers from being flushed twice. */ static int network_child_fatal(void) { _exit(1); return 1; } /* * Receive a file descriptor from a STREAMS pipe if supported and return the * file descriptor. If not supported, return INVALID_SOCKET for failure. */ #ifdef HAVE_STREAMS_SENDFD static socket_type network_recvfd(int pipe) { struct strrecvfd fdrec; if (ioctl(pipe, I_RECVFD, &fdrec) < 0) { syswarn("cannot receive file descriptor from innbind"); return INVALID_SOCKET; } else return fdrec.fd; } #else /* !HAVE_STREAMS_SENDFD */ static socket_type network_recvfd(int pipe UNUSED) { return INVALID_SOCKET; } #endif /* * Call innbind to bind a socket to a privileged port. Takes the file * descriptor, the family, the bind address (as a string), and the port * number. Returns the bound file descriptor, which may be different than * the provided file descriptor if the system didn't support binding in a * subprocess, or INVALID_SOCKET on error. */ static socket_type network_innbind(int fd, int family, const char *address, unsigned short port) { char *path; char buff[128]; int pipefds[2]; pid_t child, result; int status; /* We need innconf in order to find innbind. */ if (innconf == NULL || innconf->pathbin == NULL) return INVALID_SOCKET; /* Open a pipe to innbind and run it to bind the socket. */ if (pipe(pipefds) < 0) { syswarn("cannot create pipe"); return INVALID_SOCKET; } path = concatpath(innconf->pathbin, "innbind"); snprintf(buff, sizeof(buff), "%d,%d,%s,%hu", fd, family, address, port); child = fork(); if (child < 0) { syswarn("cannot fork innbind for %s, port %hu", address, port); return INVALID_SOCKET; } else if (child == 0) { /* Restore signal disposition and mask */ xsignal_forked(); message_fatal_cleanup = network_child_fatal; socket_close(1); if (dup2(pipefds[1], 1) < 0) sysdie("cannot dup pipe to stdout"); socket_close(pipefds[0]); if (execl(path, path, buff, (char *) 0) < 0) sysdie("cannot exec innbind for %s, port %hu", address, port); } socket_close(pipefds[1]); free(path); /* * Read the results from innbind. This will either be "ok\n" or "no\n" * followed by an attempt to pass a new file descriptor back. */ status = socket_read(pipefds[0], buff, 3); buff[3] = '\0'; if (status == 0) { warn("innbind returned no output, assuming failure"); fd = INVALID_SOCKET; } else if (status < 0) { syswarn("cannot read from innbind"); fd = INVALID_SOCKET; } else if (strcmp(buff, "no\n") == 0) { fd = network_recvfd(pipefds[0]); } else if (strcmp(buff, "ok\n") != 0) { fd = INVALID_SOCKET; } /* Wait for the results of the child process. */ do { result = waitpid(child, &status, 0); } while (result == -1 && errno == EINTR); if (result != child) { syswarn("cannot wait for innbind for %s, port %hu", address, port); return INVALID_SOCKET; } if (WIFEXITED(status) && WEXITSTATUS(status) == 0) return fd; else { warn("innbind failed for %s, port %hu", address, port); return INVALID_SOCKET; } } /* * Create an IPv4 socket and bind it, returning the resulting file descriptor * (or INVALID_SOCKET on a failure). */ socket_type network_innbind_ipv4(int type, const char *address, unsigned short port) { socket_type fd, bindfd; /* Use the generic network function when innbind is not necessary. */ if (innconf->port >= 1024 || geteuid() == 0) { return network_bind_ipv4(type, address, port); } /* Create the socket. */ fd = socket(PF_INET, type, IPPROTO_IP); if (fd == INVALID_SOCKET) { syswarn("cannot create IPv4 socket for %s, port %hu", address, port); return INVALID_SOCKET; } network_set_reuseaddr(fd); /* Accept "any" or "all" in the bind address to mean 0.0.0.0. */ if (!strcmp(address, "any") || !strcmp(address, "all")) address = "0.0.0.0"; /* Do the bind. */ bindfd = network_innbind(fd, AF_INET, address, port); if (bindfd != fd) socket_close(fd); return bindfd; } /* * Create an IPv6 socket and bind it, returning the resulting file descriptor * (or INVALID_SOCKET on a failure). This socket will be restricted to IPv6 * only if possible (as opposed to the standard behavior of binding IPv6 * sockets to both IPv6 and IPv4). * * Note that we don't warn (but still return failure) if the reason for the * socket creation failure is that IPv6 isn't supported; this is to handle * systems like many Linux hosts where IPv6 is available in userland but the * kernel doesn't support it. */ #if HAVE_INET6 socket_type network_innbind_ipv6(int type, const char *address, unsigned short port) { socket_type fd, bindfd; /* Use the generic network function when innbind is not necessary. */ if (innconf->port >= 1024 || geteuid() == 0) { return network_bind_ipv6(type, address, port); } /* Create the socket. */ fd = socket(PF_INET6, type, IPPROTO_IP); if (fd == INVALID_SOCKET) { if (socket_errno != EAFNOSUPPORT && socket_errno != EPROTONOSUPPORT) syswarn("cannot create IPv6 socket for %s, port %hu", address, port); return INVALID_SOCKET; } network_set_reuseaddr(fd); /* * Restrict the socket to IPv6 only if possible. The default behavior is * to bind IPv6 sockets to both IPv6 and IPv4 for backward compatibility, * but this causes various other problems (such as with reusing sockets * and requiring handling of mapped addresses). Continue on if this * fails, however. */ network_set_v6only(fd); /* Accept "any" or "all" in the bind address to mean ::. */ if (!strcmp(address, "any") || !strcmp(address, "all")) address = "::"; /* * If the address is not ::, use IP_FREEBIND if it's available. This * allows the network stack to bind to an address that isn't configured. * We lose diagnosis of errors from specifying bind addresses that don't * exist on the system, but we gain the ability to bind to IPv6 addresses * that aren't yet configured. Since IPv6 address configuration can take * unpredictable amounts of time during system setup, this is more robust. * * Ensure there is always a block here to avoid compiler warnings, since * network_set_freebind() may expand into nothing. */ if (strcmp(address, "::") != 0) { network_set_freebind(fd); } /* Do the bind. */ bindfd = network_innbind(fd, AF_INET6, address, port); if (bindfd != fd) socket_close(fd); return bindfd; } #else /* HAVE_INET6 */ socket_type network_innbind_ipv6(int type UNUSED, const char *address, unsigned short port) { warn("cannot bind %s, port %hu: IPv6 not supported", address, port); socket_set_errno(EPROTONOSUPPORT); return INVALID_SOCKET; } #endif /* HAVE_INET6 */ /* * Create and bind sockets for every local address, as determined by * getaddrinfo if IPv6 is available (otherwise, just use the IPv4 loopback * address). Takes the socket type and port number, and then a pointer to an * array of integers and a pointer to a count of them. Allocates a new array * to hold the file descriptors and stores the count in the fourth argument. */ #if HAVE_INET6 bool network_innbind_all(int type, unsigned short port, socket_type **fds, unsigned int *count) { struct addrinfo hints, *addrs, *addr; unsigned int size; int status; socket_type fd; char service[16], name[INET6_ADDRSTRLEN]; /* Use the generic network function when innbind is not necessary. */ if (innconf->port >= 1024 || geteuid() == 0) { return network_bind_all(type, port, fds, count); } *count = 0; /* Do the query to find all the available addresses. */ memset(&hints, 0, sizeof(hints)); hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG; hints.ai_family = AF_UNSPEC; hints.ai_socktype = type; status = snprintf(service, sizeof(service), "%hu", port); if (status < 0 || (size_t) status > sizeof(service)) { warn("cannot convert port %hu to string", port); socket_set_errno_einval(); return false; } status = getaddrinfo(NULL, service, &hints, &addrs); if (status < 0) { warn("getaddrinfo for %s failed: %s", service, gai_strerror(status)); socket_set_errno_einval(); return false; } /* * Now, try to bind each of them. Start the fds array at two entries, * assuming an IPv6 and IPv4 socket, and grow it by two when necessary. */ size = 2; *fds = xcalloc(size, sizeof(socket_type)); for (addr = addrs; addr != NULL; addr = addr->ai_next) { network_sockaddr_sprint(name, sizeof(name), addr->ai_addr); if (addr->ai_family == AF_INET) fd = network_innbind_ipv4(type, name, port); else if (addr->ai_family == AF_INET6) fd = network_innbind_ipv6(type, name, port); else continue; if (fd != INVALID_SOCKET) { if (*count >= size) { size += 2; *fds = xreallocarray(*fds, size, sizeof(socket_type)); } (*fds)[*count] = fd; (*count)++; } } freeaddrinfo(addrs); return (*count > 0); } #else /* HAVE_INET6 */ bool network_innbind_all(int type, unsigned short port, socket_type **fds, unsigned int *count) { socket_type fd; /* Use the generic network function when innbind is not necessary. */ if (innconf->port >= 1024 || geteuid() == 0) { return network_bind_all(type, port, fds, count); } fd = network_innbind_ipv4(type, "0.0.0.0", port); if (fd == INVALID_SOCKET) { *fds = NULL; *count = 0; return false; } *fds = xmalloc(sizeof(socket_type)); *fds[0] = fd; *count = 1; return true; } #endif /* HAVE_INET6 */ inn-2.6.0/lib/reallocarray.c0000644000175200017520000000403312575023702015312 0ustar iuliusiulius/* $Id: reallocarray.c 9759 2014-12-04 20:11:38Z iulius $ * * Replacement for a missing reallocarray. * * Provides the same functionality as the OpenBSD library function * reallocarray for those systems that don't have it. This function is the * same as realloc, but takes the size arguments in the same form as calloc * and checks for overflow so that the caller doesn't need to. * * The canonical version of this file is maintained in the rra-c-util package, * which can be found at . * * Written by Russ Allbery * * The authors hereby relinquish any claim to any copyright that they may have * in this work, whether granted under contract or by operation of law or * international treaty, and hereby commit to the public, at large, that they * shall not, at any time in the future, seek to enforce any copyright in this * work against any person or entity, or prevent any person or entity from * copying, publishing, distributing or creating derivative works of this * work. */ #include "config.h" #include "clibrary.h" #include /* * If we're running the test suite, rename reallocarray to avoid conflicts * with the system version. #undef it first because some systems may define * it to another name. */ #if TESTING # undef reallocarray # define reallocarray test_reallocarray void *test_reallocarray(void *, size_t, size_t); #endif /* * nmemb * size cannot overflow if both are smaller than sqrt(SIZE_MAX). We * can calculate that value statically by using 2^(sizeof(size_t) * 8) as the * value of SIZE_MAX and then taking the square root, which gives * 2^(sizeof(size_t) * 4). Compute the exponentiation with shift. */ #define CHECK_THRESHOLD (1UL << (sizeof(size_t) * 4)) void * reallocarray(void *ptr, size_t nmemb, size_t size) { if (nmemb >= CHECK_THRESHOLD || size >= CHECK_THRESHOLD) if (nmemb > 0 && SIZE_MAX / nmemb <= size) { errno = ENOMEM; return NULL; } return realloc(ptr, nmemb * size); } inn-2.6.0/lib/getnameinfo.c0000644000175200017520000001367112575023702015136 0ustar iuliusiulius/* $Id: getnameinfo.c 9759 2014-12-04 20:11:38Z iulius $ * * Replacement for a missing getnameinfo. * * This is an implementation of getnameinfo for systems that don't have one so * that networking code can use a consistant interface without #ifdef. It is * a fairly minimal implementation, with the following limitations: * * - IPv4 support only. IPv6 is not supported. * - NI_NOFQDN is ignored. * - Not thread-safe due to gethostbyaddr, getservbyport, and inet_ntoa. * * The last two issues could probably be easily remedied, but haven't been * needed so far. Adding IPv6 support isn't worth it; systems with IPv6 * support should already support getnameinfo natively. * * The canonical version of this file is maintained in the rra-c-util package, * which can be found at . * * Written by Russ Allbery * * The authors hereby relinquish any claim to any copyright that they may have * in this work, whether granted under contract or by operation of law or * international treaty, and hereby commit to the public, at large, that they * shall not, at any time in the future, seek to enforce any copyright in this * work against any person or entity, or prevent any person or entity from * copying, publishing, distributing or creating derivative works of this * work. */ #include "config.h" #include "portable/macros.h" #include "portable/socket.h" #include "clibrary.h" #include /* * If we're running the test suite, rename inet_ntoa to avoid conflicts with * the system version. Note that we don't rename the structures and * constants, but that should be okay (except possibly for gai_strerror). */ #if TESTING # undef getnameinfo # define getnameinfo test_getnameinfo int test_getnameinfo(const struct sockaddr *, socklen_t, char *, socklen_t, char *, socklen_t, int); /* Linux doesn't provide EAI_OVERFLOW, so make up our own for testing. */ # ifndef EAI_OVERFLOW # define EAI_OVERFLOW 10 # endif #endif /* Used for unused parameters to silence gcc warnings. */ #define UNUSED __attribute__((__unused__)) /* * Check to see if a name is fully qualified by seeing if it contains a * period. If it does, try to copy it into the provided node buffer and set * status accordingly, returning true. If not, return false. */ static bool try_name(const char *name, char *node, socklen_t nodelen, int *status) { size_t namelen; if (strchr(name, '.') == NULL) return false; namelen = strlen(name); if (namelen + 1 > (size_t) nodelen) *status = EAI_OVERFLOW; else { memcpy(node, name, namelen + 1); *status = 0; } return true; } /* * Look up an address (or convert it to ASCII form) and put it in the provided * buffer, depending on what is requested by flags. */ static int lookup_name(const struct in_addr *addr, char *node, socklen_t nodelen, int flags) { struct hostent *host; char **alias; int status; char *name; size_t namelen; /* Do the name lookup first unless told not to. */ if (!(flags & NI_NUMERICHOST)) { host = gethostbyaddr((const void *) addr, sizeof(struct in_addr), AF_INET); if (host == NULL) { if (flags & NI_NAMEREQD) return EAI_NONAME; } else { if (try_name(host->h_name, node, nodelen, &status)) return status; for (alias = host->h_aliases; *alias != NULL; alias++) if (try_name(*alias, node, nodelen, &status)) return status; } /* * We found some results, but none of them were fully-qualified, so * act as if we found nothing and either fail or fall through. */ if (flags & NI_NAMEREQD) return EAI_NONAME; } /* Just convert the address to ASCII. */ name = inet_ntoa(*addr); namelen = strlen(name); if (namelen + 1 > (size_t) nodelen) return EAI_OVERFLOW; memcpy(node, name, namelen + 1); return 0; } /* * Look up a service (or convert it to ASCII form) and put it in the provided * buffer, depending on what is requested by flags. */ static int lookup_service(unsigned short port, char *service, socklen_t servicelen, int flags) { struct servent *srv; const char *protocol; int status; size_t namelen; /* Do the name lookup first unless told not to. */ if (!(flags & NI_NUMERICSERV)) { protocol = (flags & NI_DGRAM) ? "udp" : "tcp"; srv = getservbyport(htons(port), protocol); if (srv != NULL) { namelen = strlen(srv->s_name); if (namelen + 1 > (size_t) servicelen) return EAI_OVERFLOW; memcpy(service, srv->s_name, namelen + 1); return 0; } } /* Just convert the port number to ASCII. */ status = snprintf(service, servicelen, "%hu", port); if (status < 0 || (socklen_t) status > servicelen) return EAI_OVERFLOW; return 0; } /* * The getnameinfo implementation. */ int getnameinfo(const struct sockaddr *sa, socklen_t salen UNUSED, char *node, socklen_t nodelen, char *service, socklen_t servicelen, int flags) { const struct sockaddr_in *sin; int status; unsigned short port; if ((node == NULL || nodelen <= 0) && (service == NULL || servicelen <= 0)) return EAI_NONAME; /* We only support AF_INET. */ if (sa->sa_family != AF_INET) return EAI_FAMILY; sin = (const struct sockaddr_in *) (const void *) sa; /* Name lookup. */ if (node != NULL && nodelen > 0) { status = lookup_name(&sin->sin_addr, node, nodelen, flags); if (status != 0) return status; } /* Service lookup. */ if (service != NULL && servicelen > 0) { port = ntohs(sin->sin_port); return lookup_service(port, service, servicelen, flags); } else return 0; } inn-2.6.0/lib/buffer.c0000644000175200017520000002176312575023702014114 0ustar iuliusiulius/* $Id: buffer.c 9915 2015-07-07 16:36:20Z iulius $ * * Counted, reusable memory buffer. * * A buffer is an allocated block of memory with a known size and a separate * data length. It's intended to store strings and can be reused repeatedly * to minimize the number of memory allocations. Buffers increase in * increments of 1K, or double for some operations. * * A buffer contains a record of what data has been used and what data is as * yet unprocessed, used when the buffer is an I/O buffer where lots of data * is buffered and then slowly processed out of the buffer. The total length * of the data is used + left. If a buffer is just used to store some data, * used can be set to 0 and left stores the length of the data. * * The canonical version of this file is maintained in the rra-c-util package, * which can be found at . * * Written by Russ Allbery * Copyright 2011, 2012, 2014 * The Board of Trustees of the Leland Stanford Junior University * Copyright (c) 2004, 2005, 2006 * by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, * 2002, 2003 by The Internet Software Consortium and Rich Salz * * This code is derived from software contributed to the Internet Software * Consortium by Rich Salz. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #include "config.h" #include "clibrary.h" #include #include #include "inn/buffer.h" #include "inn/xmalloc.h" /* * Allocate a new struct buffer and initialize it. */ struct buffer * buffer_new(void) { return xcalloc(1, sizeof(struct buffer)); } /* * Free a buffer. */ void buffer_free(struct buffer *buffer) { if (buffer == NULL) return; free(buffer->data); free(buffer); } /* * Resize a buffer to be at least as large as the provided second argument. * Resize buffers to multiples of 1KB to keep the number of reallocations to a * minimum. Refuse to resize a buffer to make it smaller. */ void buffer_resize(struct buffer *buffer, size_t size) { if (size < buffer->size) return; buffer->size = (size + 1023) & ~1023UL; buffer->data = xrealloc(buffer->data, buffer->size); } /* * Compact a buffer by moving the data between buffer->used and buffer->left * to the beginning of the buffer, overwriting the already-consumed data. */ void buffer_compact(struct buffer *buffer) { if (buffer->used == 0) return; if (buffer->left != 0) memmove(buffer->data, buffer->data + buffer->used, buffer->left); buffer->used = 0; } /* * Replace whatever data is currently in the buffer with the provided data. * Resize the buffer if needed. */ void buffer_set(struct buffer *buffer, const char *data, size_t length) { if (length > 0) { buffer_resize(buffer, length); memmove(buffer->data, data, length); } buffer->left = length; buffer->used = 0; } /* * Append data to a buffer. The new data shows up as additional unused data * at the end of the buffer. Resize the buffer if needed. */ void buffer_append(struct buffer *buffer, const char *data, size_t length) { size_t total; if (length == 0) return; total = buffer->used + buffer->left; buffer_resize(buffer, total + length); buffer->left += length; memcpy(buffer->data + total, data, length); } /* * Print data into a buffer from the supplied va_list, appending to the end. * The new data shows up as unused data at the end of the buffer. The * trailing nul is not added to the buffer. */ void buffer_append_vsprintf(struct buffer *buffer, const char *format, va_list args) { size_t total, avail; ssize_t status; va_list args_copy; total = buffer->used + buffer->left; avail = buffer->size - total; va_copy(args_copy, args); status = vsnprintf(buffer->data + total, avail, format, args_copy); va_end(args_copy); if (status < 0) return; if ((size_t) status + 1 <= avail) { buffer->left += status; } else { buffer_resize(buffer, total + status + 1); avail = buffer->size - total; status = vsnprintf(buffer->data + total, avail, format, args); if (status < 0 || (size_t) status + 1 > avail) return; buffer->left += status; } } /* * Print data into a buffer, appending to the end. The new data shows up as * unused data at the end of the buffer. Resize the buffer if needed. The * trailing nul is not added to the buffer. */ void buffer_append_sprintf(struct buffer *buffer, const char *format, ...) { va_list args; va_start(args, format); buffer_append_vsprintf(buffer, format, args); va_end(args); } /* * Replace the current buffer contents with data printed from the supplied * va_list. The new data shows up as unused data at the end of the buffer. * The trailing nul is not added to the buffer. */ void buffer_vsprintf(struct buffer *buffer, const char *format, va_list args) { buffer_set(buffer, NULL, 0); buffer_append_vsprintf(buffer, format, args); } /* * Replace the current buffer contents with data printed from the supplied * format string and arguments. The new data shows up as unused data at the * end of the buffer. The trailing nul is not added to the buffer. */ void buffer_sprintf(struct buffer *buffer, const char *format, ...) { va_list args; va_start(args, format); buffer_vsprintf(buffer, format, args); va_end(args); } /* * Swap the contents of two buffers. */ void buffer_swap(struct buffer *one, struct buffer *two) { struct buffer tmp; tmp = *one; *one = *two; *two = tmp; } /* * Find a given string in the unconsumed data in buffer. We know that all the * data prior to start (an offset into the space between buffer->used and * buffer->left) has already been searched. Returns the offset of the string * (with the same meaning as start) in offset if found, and returns true if * the terminator is found and false otherwise. */ bool buffer_find_string(struct buffer *buffer, const char *string, size_t start, size_t *offset) { char *terminator, *data; size_t length; if (buffer->data == NULL) return false; length = strlen(string); do { data = buffer->data + buffer->used + start; terminator = memchr(data, string[0], buffer->left - start); if (terminator == NULL) return false; start = (terminator - buffer->data) - buffer->used; if (buffer->left - start < length) return false; start++; } while (memcmp(terminator, string, length) != 0); *offset = start - 1; return true; } /* * Read from a file descriptor into a buffer, up to the available space in the * buffer, and return the number of characters read. */ ssize_t buffer_read(struct buffer *buffer, int fd) { ssize_t count; do { size_t used = buffer->used + buffer->left; count = read(fd, buffer->data + used, buffer->size - used); } while (count == -1 && (errno == EAGAIN || errno == EINTR)); if (count > 0) buffer->left += count; return count; } /* * Read from a file descriptor until end of file is reached, doubling the * buffer size as necessary to hold all of the data. Returns true on success, * false on failure (in which case errno will be set). */ bool buffer_read_all(struct buffer *buffer, int fd) { ssize_t count; if (buffer->size == 0) buffer_resize(buffer, 1024); do { size_t used = buffer->used + buffer->left; if (buffer->size <= used) buffer_resize(buffer, buffer->size * 2); count = buffer_read(buffer, fd); } while (count > 0); return (count == 0); } /* * Read the entire contents of a file into a buffer. This is a slight * optimization over buffer_read_all because it can stat the file descriptor * first and size the buffer appropriately. buffer_read_all will still handle * the case where the file size changes while it's being read. Returns true * on success, false on failure (in which case errno will be set). */ bool buffer_read_file(struct buffer *buffer, int fd) { struct stat st; size_t used = buffer->used + buffer->left; if (fstat(fd, &st) < 0) return false; buffer_resize(buffer, st.st_size + used); return buffer_read_all(buffer, fd); } inn-2.6.0/lib/seteuid.c0000644000175200017520000000307312575023702014277 0ustar iuliusiulius/* $Id: seteuid.c 9697 2014-09-20 06:22:24Z iulius $ * * Replacement for a missing seteuid. * * Some systems don't have seteuid but do have setreuid. setreuid with -1 * given for the real UID is equivalent to seteuid on systems with POSIX saved * UIDs. On systems without POSIX saved UIDs, we'd lose our ability to regain * privileges if we just set the effective UID, so instead fake a saved UID by * setting the real UID to the current effective UID, using the real UID as * the saved UID. * * Note that swapping UIDs doesn't work on AIX, but AIX has saved UIDs. Note * also that systems without setreuid lose, and that we assume that any system * with seteuid has saved UIDs. * * The canonical version of this file is maintained in the rra-c-util package, * which can be found at . * * Written by Russ Allbery * * The authors hereby relinquish any claim to any copyright that they may have * in this work, whether granted under contract or by operation of law or * international treaty, and hereby commit to the public, at large, that they * shall not, at any time in the future, seek to enforce any copyright in this * work against any person or entity, or prevent any person or entity from * copying, publishing, distributing or creating derivative works of this * work. */ #include "config.h" #if HAVE_UNISTD_H # include #endif int seteuid(uid_t euid) { int ruid; #ifdef _POSIX_SAVED_IDS ruid = -1; #else ruid = geteuid(); #endif return setreuid(ruid, euid); } inn-2.6.0/lib/confparse.c0000644000175200017520000015257012575023702014624 0ustar iuliusiulius/* $Id: confparse.c 9677 2014-09-05 19:32:31Z iulius $ ** ** Parse a standard block-structured configuration file syntax. ** ** Herein are all the parsing and access functions for the configuration ** syntax used by INN. See doc/config-* for additional documentation. ** ** All entry point functions begin with config_*. config_parse_file is ** the entry point for most of the work done in this file; all other ** functions access the parse tree that config_parse_file generates. ** ** Functions are named by the structure or basic task they work on: ** ** parameter_* config_parameter structs. ** group_* config_group structs. ** file_* config_file structs (including all I/O). ** token_* The guts of the lexer. ** parse_* The guts of the parser. ** error_* Error reporting functions. ** convert_* Converting raw parameter values. ** ** Each currently open file is represented by a config_file struct, which ** contains the current parse state for that file, including the internal ** buffer, a pointer to where in the buffer the next token starts, and the ** current token. Inclusion of additional files is handled by maintaining a ** stack of config_file structs, so when one file is finished, the top struct ** popped off the stack and parsing continues where it left off. ** ** Since config_file structs contain the parse state, they're passed as an ** argument to most functions. ** ** A config_file struct contains a token struct, representing the current ** token. The configuration file syntax is specifically designed to never ** require lookahead to parse; all parse decisions can be made on the basis ** of the current state and a single token. A token consists of a type and ** an optional attached string. Note that strings are allocated by the lexer ** but are never freed by the lexer! Any token with an associated string ** should have that string copied into permanent storage (like the params ** hash of a config_group) or freed. error_unexpected_token will do the ** latter. ** ** Errors in the lexer are indicated by setting the token to TOKEN_ERROR. ** All parsing errors are indicated by setting the error flag in the current ** config_file struct. Error recovery is *not* implemented by the current ** algorithm; it would add a lot of complexity to the parsing algorithm and ** the results still probably shouldn't be used by the calling program, so it ** would only be useful to catch more than one syntax error per invocation ** and it isn't expected that syntax errors will be that common. Instead, if ** something fails to parse, the whole parser unwinds and returns failure. ** ** The config_param_* functions are used to retrieve the values of ** parameters; each use a convert_* function to convert the raw parameter ** value to the type specified by the user. group_parameter_get can ** therefore be the same for all parameter types, with all of the variations ** encapsulated in the convert_* functions. */ #include "config.h" #include "clibrary.h" #include #include #include "inn/confparse.h" #include "inn/hashtab.h" #include "inn/messages.h" #include "inn/vector.h" #include "inn/libinn.h" /* The types of tokens seen in configuration files. */ enum token_type { TOKEN_CRLF, TOKEN_STRING, TOKEN_QSTRING, TOKEN_PARAM, TOKEN_LBRACE, TOKEN_RBRACE, TOKEN_LANGLE, TOKEN_RANGLE, TOKEN_LBRACKET, TOKEN_RBRACKET, TOKEN_SEMICOLON, TOKEN_EOF, TOKEN_ERROR }; /* The parse status of a file. Variables marked internal are only used by file_* functions; other functions don't need to look at them. Other variables are marked by what functions are responsible for maintaining them. */ struct config_file { int fd; /* Internal */ char *buffer; /* Internal */ size_t bufsize; /* Internal */ const char *filename; /* file_open */ unsigned int line; /* token_newline and token_quoted_string */ bool error; /* Everyone */ /* Set by file_* and token_*. current == NULL indicates we've not yet read from the file. */ char *current; /* Normally set by token_*, but file_read and file_read_more may set token to TOKEN_ERROR or TOKEN_EOF when those conditions are encountered. In that situation, they also return false. */ struct { enum token_type type; char *string; } token; }; /* The types of parameters, used to distinguish the values of the union in the config_parameter_s struct. */ enum value_type { VALUE_UNKNOWN, VALUE_BOOL, VALUE_NUMBER, VALUE_UNUMBER, VALUE_REAL, VALUE_STRING, VALUE_LIST, VALUE_INVALID }; /* Each setting is represented by one of these structs, stored in the params hash of a config group. Since all of a config_group must be in the same file (either group->file for regular groups or group->included for groups whose definition is in an included file), we don't have to stash a file name here for error reporting but can instead get that from the enclosing group. */ struct config_parameter { char *key; char *raw_value; unsigned int line; /* For error reporting. */ enum value_type type; union { bool boolean; long signed_number; unsigned long unsigned_number; double real; char *string; struct vector *list; } value; }; /* The type of a function that converts a raw parameter value to some other data type, storing the result in its second argument and returning true on success or false on failure. */ typedef bool (*convert_func)(struct config_parameter *, const char *, void *); /* The basic element of configuration data, a group of parameters. This is the only struct that is exposed to callers, and then only as an opaque data structure. */ struct config_group { char *type; char *tag; char *file; /* File in which the group starts. */ unsigned int line; /* Line number where the group starts. */ char *included; /* For group , the included file. */ struct hash *params; struct config_group *parent; struct config_group *child; struct config_group *next; }; /* Parameter handling, used by the hash table stored in a config_group. */ static const void *parameter_key(const void *p); static bool parameter_equal(const void *k, const void *p); static void parameter_free(void *p); /* Hash traversal function to collect parameters into a vector. */ static void parameter_collect(void *, void *); /* Group handling. */ static struct config_group *group_new(const char *file, unsigned int line, char *type, char *tag); static void group_free(struct config_group *); static bool group_parameter_get(struct config_group *group, const char *key, void *result, convert_func convert); /* Parameter type conversion functions. All take the parameter, the file, and a pointer to where the result can be placed. */ static bool convert_boolean(struct config_parameter *, const char *, void *); static bool convert_signed_number(struct config_parameter *, const char *, void *); static bool convert_unsigned_number(struct config_parameter *, const char *, void *); static bool convert_real(struct config_parameter *, const char *, void *); static bool convert_string(struct config_parameter *, const char *, void *); static bool convert_list(struct config_parameter *, const char *, void *); /* File I/O. Many other functions also manipulate config_file structs; see the struct definition for notes on who's responsible for what. */ static struct config_file *file_open(const char *filename); static bool file_read(struct config_file *); static bool file_read_more(struct config_file *, ptrdiff_t offset); static void file_close(struct config_file *); /* The basic lexer function. The token is stashed in file; the return value is just for convenience and duplicates that information. */ static enum token_type token_next(struct config_file *); /* Handler functions for specific types of tokens. These should only be called by token_next. */ static void token_simple(struct config_file *, enum token_type type); static void token_newline(struct config_file *); static void token_string(struct config_file *); static void token_quoted_string(struct config_file *); /* Handles whitespace for the rest of the lexer. */ static bool token_skip_whitespace(struct config_file *); /* Handles comments for the rest of the lexer. */ static bool token_skip_comment(struct config_file *); /* Convert a quoted string to the string value it represents. */ static char *token_unquote_string(const char *, const char *file, unsigned int line); /* Parser functions to parse the named syntactic element. */ static enum token_type parse_list(struct config_group *, struct config_file *, char *key); static enum token_type parse_parameter(struct config_group *, struct config_file *, char *key); static bool parse_group_contents(struct config_group *, struct config_file *); static bool parse_include(struct config_group *, struct config_file *); static bool parse_group(struct config_file *, struct config_group *parent); /* Error reporting functions. */ static void error_bad_unquoted_char(struct config_file *, char bad); static void error_unexpected_token(struct config_file *, const char *expecting); /* ** Return the key from a parameter struct, used by the hash table. */ static const void * parameter_key(const void *p) { const struct config_parameter *param = p; return param->key; } /* ** Check to see if a provided key matches the key of a parameter struct, ** used by the hash table. */ static bool parameter_equal(const void *k, const void *p) { const char *key = k; const struct config_parameter *param = p; return strcmp(key, param->key) == 0; } /* ** Free a parameter, used by the hash table. */ static void parameter_free(void *p) { struct config_parameter *param = p; free(param->key); if (param->raw_value != NULL) free(param->raw_value); if (param->type == VALUE_STRING) free(param->value.string); else if (param->type == VALUE_LIST) vector_free(param->value.list); free(param); } /* ** Report an unexpected character while parsing a regular string and set the ** current token type to TOKEN_ERROR. */ static void error_bad_unquoted_char(struct config_file *file, char bad) { warn("%s:%u: invalid character '%c' in unquoted string", file->filename, file->line, bad); file->token.type = TOKEN_ERROR; file->error = true; } /* ** Report an unexpected token. If the token is TOKEN_ERROR, don't print an ** additional error message. Takes a string saying what token was expected. ** Sets the token to TOKEN_ERROR and frees the associated string if the ** current token type is TOKEN_STRING, TOKEN_QSTRING, or TOKEN_PARAM. */ static void error_unexpected_token(struct config_file *file, const char *expecting) { const char *name; bool string = false; /* If the bad token type is a string, param, or quoted string, free the string associated with the token to avoid a memory leak. */ if (file->token.type != TOKEN_ERROR) { switch (file->token.type) { case TOKEN_STRING: name = "string"; string = true; break; case TOKEN_QSTRING: name = "quoted string"; string = true; break; case TOKEN_PARAM: name = "parameter"; string = true; break; case TOKEN_CRLF: name = "end of line"; break; case TOKEN_LBRACE: name = "'{'"; break; case TOKEN_RBRACE: name = "'}'"; break; case TOKEN_LANGLE: name = "'<'"; break; case TOKEN_RANGLE: name = "'>'"; break; case TOKEN_LBRACKET: name = "'['"; break; case TOKEN_RBRACKET: name = "']'"; break; case TOKEN_SEMICOLON: name = "';'"; break; case TOKEN_EOF: name = "end of file"; break; default: name = "unknown token"; break; } warn("%s:%u: parse error: saw %s, expecting %s", file->filename, file->line, name, expecting); } if (string) { free(file->token.string); file->token.string = NULL; } file->token.type = TOKEN_ERROR; file->error = true; } /* ** Handle a simple token (a single character), advancing the file->current ** pointer past it and setting file->token as appropriate. */ static void token_simple(struct config_file *file, enum token_type type) { file->current++; file->token.type = type; file->token.string = NULL; } /* ** Handle a newline. Skip any number of comments after the newline, ** including reading more data from the file if necessary, and update ** file->line as needed. */ static void token_newline(struct config_file *file) { /* If we're actually positioned on a newline, update file->line and skip over it. Try to handle CRLF correctly, as a single line terminator that only increments the line count once, while still treating either CR or LF alone as line terminators in their own regard. */ if (*file->current == '\n') { file->current++; file->line++; } else if (*file->current == '\r') { if (file->current[1] == '\n') file->current += 2; else if (file->current[1] != '\0') file->current++; else { if (!file_read(file)) { file->current++; return; } if (*file->current == '\n') file->current++; } file->line++; } if (!token_skip_whitespace(file)) return; while (*file->current == '#') { if (!token_skip_comment(file)) return; if (!token_skip_whitespace(file)) return; } file->token.type = TOKEN_CRLF; file->token.string = NULL; } /* ** Handle a string. Only some characters are allowed in an unquoted string; ** check that, since otherwise it could hide syntax errors. Any whitespace ** ends the token. We have to distinguish between TOKEN_PARAM and ** TOKEN_STRING; the former ends in a colon, unlike the latter. */ static void token_string(struct config_file *file) { int i; bool status; ptrdiff_t offset; bool done = false; bool colon = false; /* Use an offset from file->current rather than a pointer that moves through the buffer, since the base of file->current can change during a file_read_more() call and we don't want to have to readjust a pointer. If we have to read more, adjust our counter back one character, since the nul was replaced by a new, valid character. */ i = 0; while (!done) { switch (file->current[i]) { case '\t': case '\r': case '\n': case ' ': case ';': case '>': case '}': done = true; break; case '"': case '<': case '[': case '\\': case ']': case '{': error_bad_unquoted_char(file, file->current[i]); return; case ':': if (colon) { error_bad_unquoted_char(file, file->current[i]); return; } colon = true; break; case '\0': offset = file->current - file->buffer; status = file_read_more(file, offset); if (status) i--; else done = true; break; default: if (colon) { error_bad_unquoted_char(file, ':'); return; } } if (!done) i++; } file->token.type = colon ? TOKEN_PARAM : TOKEN_STRING; file->token.string = xstrndup(file->current, i - colon); file->current += i; } /* ** Handle a quoted string. This token is unique as the only token that can ** contain whitespace, even newlines if they're escaped, so we also have to ** update file->line as we go. Note that the quotes *are* included in the ** string we stash in file->token, since they should be part of the raw_value ** of a parameter. */ static void token_quoted_string(struct config_file *file) { int i; ptrdiff_t offset; bool status; bool done = false; /* Use an offset from file->current rather than a pointer that moves through the buffer, since the base of file->current can change during a file_read_more() call and we don't want to have to readjust a pointer. If we have to read more, adjust our counter back one character, since the nul was replaced by a new, valid character. */ for (i = 1; !done; i++) { switch (file->current[i]) { case '"': done = true; break; case '\r': case '\n': warn("%s:%u: no close quote seen for quoted string", file->filename, file->line); file->token.type = TOKEN_ERROR; file->error = true; return; case '\\': i++; if (file->current[i] == '\n') file->line++; /* CRLF should count as one line terminator. Handle most cases of that here, but the case where CR is at the end of one buffer and LF at the beginning of the next has to be handled in the \0 case below. */ if (file->current[i] == '\r') { file->line++; if (file->current[i + 1] == '\n') i++; } break; case '\0': offset = file->current - file->buffer; status = file_read_more(file, offset); if (status) i--; else { warn("%s:%u: end of file encountered while parsing quoted" " string", file->filename, file->line); file->token.type = TOKEN_ERROR; file->error = true; return; } /* If the last character of the previous buffer was CR and the first character that we just read was LF, the CR must have been escaped which means that the LF is part of it, forming a CRLF line terminator. Skip over the LF. */ if (file->current[i] == '\r' && file->current[i + 1] == '\n') i++; break; default: break; } } file->token.type = TOKEN_QSTRING; file->token.string = xstrndup(file->current, i); file->current += i; } /* ** Convert a quoted string to a string and returns the newly allocated string ** or NULL on failure. Takes the quoted string and the file and line number ** for error reporting. */ static char * token_unquote_string(const char *raw, const char *file, unsigned int line) { size_t length; char *string, *dest; const char *src; length = strlen(raw) - 2; string = xmalloc(length + 1); src = raw + 1; dest = string; for (; *src != '"' && *src != '\0'; src++) { if (*src != '\\') { *dest++ = *src; } else { src++; /* This should implement precisely the semantics of backslash escapes in quoted strings in C. */ switch (*src) { case 'a': *dest++ = '\a'; break; case 'b': *dest++ = '\b'; break; case 'f': *dest++ = '\f'; break; case 'n': *dest++ = '\n'; break; case 'r': *dest++ = '\r'; break; case 't': *dest++ = '\t'; break; case 'v': *dest++ = '\v'; break; case '\n': break; /* Escaped newlines disappear. */ case '\\': case '\'': case '"': case '?': *dest++ = *src; break; case '\0': /* Should never happen; the tokenizer should catch this. */ warn("%s:%u: unterminated string", file, line); goto fail; default: /* FIXME: \, \x, \u, and \U not yet implemented; the last three could use the same basic code. Think about whether the escape should generate a single 8-bit character or a UTF-8 encoded character; maybe the first two generate the former and \u and \U generate the latter? */ warn("%s:%u: unrecognized escape '\\%c'", file, line, *src); goto fail; } } } *dest = '\0'; /* Should never happen; the tokenizer should catch this. */ if (*src != '"') { warn("%s:%u: unterminated string (no closing quote)", file, line); goto fail; } return string; fail: free(string); return NULL; } /* ** Skip over a comment line at file->current, reading more data as necessary. ** Stop when an end of line is encountered, positioning file->current ** directly after the end of line. Returns false on end of file or a read ** error, true otherwise. */ static bool token_skip_comment(struct config_file *file) { char *p = file->current; while (*p != '\0' && *p != '\n' && *p != '\r') p++; while (*p == '\0') { if (!file_read(file)) return false; p = file->current; while (*p != '\0' && *p != '\n' && *p != '\r') p++; } /* CRLF should count as a single line terminator, but it may be split across a read boundary. Try to handle that case correctly. */ if (*p == '\n') p++; else if (*p == '\r') { p++; if (*p == '\n') p++; else if (*p == '\0') { if (!file_read(file)) return false; p = file->current; if (*p == '\n') p++; } } file->current = p; file->line++; return true; } /* ** Skip over all whitespace at file->current, reading more data as ** necessary. Stop when the first non-whitespace character is encountered or ** at end of file, leaving file->current pointing appropriately. Returns ** true if non-whitespace is found and false on end of file or a read error. */ static bool token_skip_whitespace(struct config_file *file) { char *p = file->current; while (*p == ' ' || *p == '\t') p++; while (*p == '\0') { if (!file_read(file)) return false; p = file->current; while (*p == ' ' || *p == '\t') p++; } file->current = p; return true; } /* ** The basic lexer function. Read the next token from a configuration file. ** Returns the token, which is also stored in file. Lexer failures set the ** token to TOKEN_ERROR. */ static enum token_type token_next(struct config_file *file) { /* If file->current is NULL, we've never read from the file. There is special handling for a comment at the very beginning of a file, since normally we only look for comments after newline tokens. If we do see a # at the beginning of the first line, let token_newline deal with it. That function can cope with file->current not pointing at a newline. We then return the newline token as the first token in the file. */ if (file->current == NULL) { if (!file_read(file)) return file->token.type; if (!token_skip_whitespace(file)) return file->token.type; if (*file->current == '#') { token_newline(file); return file->token.type; } } else { if (!token_skip_whitespace(file)) return file->token.type; } /* Almost all of our tokens can be recognized by the first character; the only exception is telling strings from parameters. token_string handles both of those and sets file->token.type appropriately. Comments are handled by token_newline. */ switch (*file->current) { case '{': token_simple(file, TOKEN_LBRACE); break; case '}': token_simple(file, TOKEN_RBRACE); break; case '<': token_simple(file, TOKEN_LANGLE); break; case '>': token_simple(file, TOKEN_RANGLE); break; case '[': token_simple(file, TOKEN_LBRACKET); break; case ']': token_simple(file, TOKEN_RBRACKET); break; case ';': token_simple(file, TOKEN_SEMICOLON); break; case '\r': token_newline(file); break; case '\n': token_newline(file); break; case '"': token_quoted_string(file); break; default: token_string(file); break; } return file->token.type; } /* ** Open a new configuration file and return config_file representing the ** parse state of that file. We assume that we don't have to make a copy of ** the filename argument. Default to stdio BUFSIZ for our buffer size, since ** it's generally reasonably chosen with respect to disk block sizes, memory ** consumption, and the like. */ static struct config_file * file_open(const char *filename) { struct config_file *file; int oerrno; file = xmalloc(sizeof(*file)); file->filename = filename; file->fd = open(filename, O_RDONLY); if (file->fd < 0) { oerrno = errno; free(file); errno = oerrno; return NULL; } file->buffer = xmalloc(BUFSIZ); file->bufsize = BUFSIZ; file->current = NULL; file->line = 1; file->token.type = TOKEN_ERROR; file->error = false; return file; } /* ** Read some data from a configuration file, handling errors (by reporting ** them with warn) and returning true if there's data left and false on EOF ** or a read error. */ static bool file_read(struct config_file *file) { ssize_t status; status = read(file->fd, file->buffer, file->bufsize - 1); if (status < 0) { syswarn("%s: read error", file->filename); file->token.type = TOKEN_ERROR; file->error = true; } else if (status == 0) { file->token.type = TOKEN_EOF; } if (status <= 0) return false; file->buffer[status] = '\0'; file->current = file->buffer; /* Reject nuls, since otherwise they would cause strange problems. */ if (strlen(file->buffer) != (size_t) status) { warn("%s: invalid NUL character found in file", file->filename); return false; } return true; } /* ** Read additional data from a configuration file when there's some partial ** data in the buffer already that we want to save. Takes the config_file ** struct and an offset from file->buffer specifying the start of the data ** that we want to preserve. Resizes the buffer if offset is 0. Returns ** false on EOF or a read error, true otherwise. */ static bool file_read_more(struct config_file *file, ptrdiff_t offset) { char *start; size_t amount; ssize_t status; if (offset > 0) { size_t left; left = file->bufsize - offset - 1; memmove(file->buffer, file->buffer + offset, left); file->current -= offset; start = file->buffer + left; amount = offset; } else { file->buffer = xrealloc(file->buffer, file->bufsize + BUFSIZ); file->current = file->buffer; start = file->buffer + file->bufsize - 1; amount = BUFSIZ; file->bufsize += BUFSIZ; } status = read(file->fd, start, amount); if (status < 0) syswarn("%s: read error", file->filename); if (status <= 0) return false; start[status] = '\0'; /* Reject nuls, since otherwise they would cause strange problems. */ if (strlen(start) != (size_t) status) { warn("%s: invalid NUL character found in file", file->filename); return false; } return true; } /* ** Close a file and free the resources associated with it. */ static void file_close(struct config_file *file) { close(file->fd); free(file->buffer); free(file); } /* ** Parse a vector. Takes the group that we're currently inside, the ** config_file parse state, and the key of the parameter. Returns the next ** token after the vector. */ static enum token_type parse_list(struct config_group *group, struct config_file *file, char *key) { enum token_type token; struct vector *vector; struct config_parameter *param; char *string; vector = vector_new(); token = token_next(file); while (token != TOKEN_RBRACKET) { if (token == TOKEN_CRLF) { token = token_next(file); continue; } if (token != TOKEN_STRING && token != TOKEN_QSTRING) break; vector_resize(vector, vector->allocated + 1); if (token == TOKEN_QSTRING) { string = token_unquote_string(file->token.string, file->filename, file->line); free(file->token.string); if (string == NULL) { vector_free(vector); free(key); return TOKEN_ERROR; } vector->strings[vector->count] = string; } else { vector->strings[vector->count] = file->token.string; } vector->count++; token = token_next(file); } if (token == TOKEN_RBRACKET) { param = xmalloc(sizeof(*param)); param->key = key; param->raw_value = NULL; param->type = VALUE_LIST; param->value.list = vector; param->line = file->line; if (!hash_insert(group->params, key, param)) { warn("%s:%u: duplicate parameter %s", file->filename, file->line, key); vector_free(vector); free(param->key); free(param); } token = token_next(file); return token; } else { error_unexpected_token(file, "string or ']'"); vector_free(vector); free(key); return TOKEN_ERROR; } } /* ** Parse a parameter. Takes the group we're currently inside, the ** config_file parse state, and the key of the parameter. Returns the next ** token after the parameter, and also checks to make sure that it's ** something legal (end of line, end of file, or a semicolon). */ static enum token_type parse_parameter(struct config_group *group, struct config_file *file, char *key) { enum token_type token; token = token_next(file); if (token == TOKEN_LBRACKET) { return parse_list(group, file, key); } else if (token == TOKEN_STRING || token == TOKEN_QSTRING) { struct config_parameter *param; unsigned int line; char *value; /* Before storing the parameter, check to make sure that the next token is valid. If it isn't, chances are high that the user has tried to set a parameter to a value containing spaces without quoting the value. */ value = file->token.string; line = file->line; token = token_next(file); switch (token) { default: error_unexpected_token(file, "semicolon or newline"); free(value); break; case TOKEN_CRLF: case TOKEN_SEMICOLON: case TOKEN_EOF: case TOKEN_RBRACE: param = xmalloc(sizeof(*param)); param->key = key; param->raw_value = value; param->type = VALUE_UNKNOWN; param->line = line; if (!hash_insert(group->params, key, param)) { warn("%s:%u: duplicate parameter %s", file->filename, line, key); free(param->raw_value); free(param->key); free(param); } return token; } } else { error_unexpected_token(file, "parameter value"); } /* If we fell through, we encountered some sort of error. Free allocated memory and return an error token. */ free(key); return TOKEN_ERROR; } /* ** Given a config_group with the type and tag already filled in and a ** config_file with the buffer positioned after the opening brace of the ** group, read and add parameters to the group until encountering a close ** brace. Returns true on a successful parse, false on an error that ** indicates the group should be discarded. */ static bool parse_group_contents(struct config_group *group, struct config_file *file) { enum token_type token; bool semicolon = false; token = token_next(file); while (!file->error) { switch (token) { case TOKEN_PARAM: if (group->child != NULL) { warn("%s:%u: parameter specified after nested group", file->filename, file->line); free(file->token.string); return false; } token = parse_parameter(group, file, file->token.string); while (token == TOKEN_CRLF || token == TOKEN_SEMICOLON) { semicolon = (token == TOKEN_SEMICOLON); token = token_next(file); } break; case TOKEN_CRLF: token = token_next(file); break; case TOKEN_STRING: if (semicolon) { error_unexpected_token(file, "parameter"); break; } if (!parse_group(file, group)) return false; token = token_next(file); break; case TOKEN_RBRACE: case TOKEN_EOF: return true; case TOKEN_ERROR: return false; default: error_unexpected_token(file, "parameter"); break; } } return false; } /* ** Given a newly allocated group and with config_file positioned on the left ** angle bracket, parse an included group. Return true on a successful ** parse, false on an error that indicates the group should be discarded. */ static bool parse_include(struct config_group *group, struct config_file *file) { char *filename, *path, *slash; enum token_type token; bool status; struct config_group *parent; int levels; struct config_file *included; /* Make sure that the syntax is fine. */ token = token_next(file); if (token != TOKEN_STRING) { error_unexpected_token(file, "file name"); return false; } filename = file->token.string; token = token_next(file); if (token != TOKEN_RANGLE) { error_unexpected_token(file, "'>'"); return false; } /* Build the file name that we want to include. If the file name is relative, it's relative to the file of its enclosing group line. */ if (*filename == '/') path = filename; else { slash = strrchr(group->file, '/'); if (slash == NULL) path = filename; else { *slash = '\0'; path = concat(group->file, "/", filename, (char *) 0); *slash = '/'; free(filename); } } /* Make sure we're not including ourselves recursively and put an arbitrary limit of 20 levels in case of something like a recursive symlink. */ levels = 1; for (parent = group; parent != NULL; parent = parent->parent) { if (strcmp(path, parent->file) == 0) { warn("%s:%u: file %s recursively included", group->file, group->line, path); goto fail; } if (levels > 20) { warn("%s:%u: file inclusions limited to 20 deep", group->file, group->line); goto fail; } levels++; } /* Now, attempt to include the file. */ included = file_open(path); if (included == NULL) { syswarn("%s:%u: open of %s failed", group->file, group->line, path); goto fail; } group->included = path; status = parse_group_contents(group, included); file_close(included); return status; fail: free(path); return false; } /* ** With config_file positioned at the beginning of the group declaration, ** Parse a group declaration and its nested contents. Return true on a ** successful parse, false on an error that indicates the group should be ** discarded. */ static bool parse_group(struct config_file *file, struct config_group *parent) { char *type, *tag; const char *expected; struct config_group *group, *last; enum token_type token; bool status; tag = NULL; type = file->token.string; token = token_next(file); if (token == TOKEN_STRING) { tag = file->token.string; token = token_next(file); } if (token != TOKEN_LBRACE && token != TOKEN_LANGLE) { free(type); if (tag != NULL) free(tag); expected = tag != NULL ? "'{' or '<'" : "group tag, '{', or '<'"; error_unexpected_token(file, expected); return false; } group = group_new(file->filename, file->line, type, tag); group->parent = parent; if (parent->child == NULL) parent->child = group; else { for (last = parent->child; last->next != NULL; last = last->next) ; last->next = group; } if (token == TOKEN_LANGLE) status = parse_include(group, file); else status = parse_group_contents(group, file); if (!status) return false; if (token != TOKEN_LANGLE && file->token.type != TOKEN_RBRACE) { error_unexpected_token(file, "'}'"); return false; } return true; } /* ** Allocate a new config_group and set the initial values of all of the ** struct members. The group type and tag should be allocated memory that ** can later be freed, although the tag may be NULL. */ static struct config_group * group_new(const char *file, unsigned int line, char *type, char *tag) { struct config_group *group; group = xmalloc(sizeof(*group)); group->type = type; group->tag = tag; group->file = xstrdup(file); group->included = NULL; group->line = line; group->params = hash_create(4, hash_string, parameter_key, parameter_equal, parameter_free); group->parent = NULL; group->child = NULL; group->next = NULL; return group; } /* ** Free a config_group and all associated storage. */ static void group_free(struct config_group *group) { free(group->type); if (group->tag != NULL) free(group->tag); free(group->file); if (group->included != NULL) free(group->included); hash_free(group->params); free(group); } /* ** Accessor function for the group type. */ const char * config_group_type(struct config_group *group) { return group->type; } /* ** Accessor function for the group tag. */ const char * config_group_tag(struct config_group *group) { return group->tag; } /* ** Parse a configuration file, returning the config_group that's the root of ** the tree represented by that file (and any other files that it includes). ** Returns NULL on a parse failure. */ struct config_group * config_parse_file(const char *filename, ...) { struct config_group *group; struct config_file *file; bool success; file = file_open(filename); if (file == NULL) { syswarn("open of %s failed", filename); return NULL; } group = group_new(filename, 1, xstrdup("GLOBAL"), NULL); success = parse_group_contents(group, file); file_close(file); if (success) return group; else { config_free(group); return NULL; } } /* ** Given a config_group representing the root of a configuration structure, ** recursively free the entire structure. */ void config_free(struct config_group *group) { struct config_group *child, *last; child = group->child; while (child != NULL) { last = child; child = child->next; config_free(last); } group_free(group); } /* ** Convert a given parameter value to a boolean, returning true if successful ** and false otherwise. */ static bool convert_boolean(struct config_parameter *param, const char *file, void *result) { static const char *const truevals[] = { "yes", "on", "true", NULL }; static const char *const falsevals[] = { "no", "off", "false", NULL }; bool *value = result; int i; if (param->type == VALUE_BOOL) { *value = param->value.boolean; return true; } else if (param->type != VALUE_UNKNOWN) { warn("%s:%u: %s is not a boolean", file, param->line, param->key); return false; } param->type = VALUE_BOOL; for (i = 0; truevals[i] != NULL; i++) if (strcmp(param->raw_value, truevals[i]) == 0) { param->value.boolean = true; *value = true; return true; } for (i = 0; falsevals[i] != NULL; i++) if (strcmp(param->raw_value, falsevals[i]) == 0) { param->value.boolean = false; *value = false; return true; } param->type = VALUE_INVALID; warn("%s:%u: %s is not a boolean", file, param->line, param->key); return false; } /* ** Convert a given parameter value to an integer, returning true if ** successful and false otherwise. */ static bool convert_signed_number(struct config_parameter *param, const char *file, void *result) { long *value = result; char *p; if (param->type == VALUE_NUMBER) { *value = param->value.signed_number; return true; } else if (param->type != VALUE_UNKNOWN) { warn("%s:%u: %s is not an integer", file, param->line, param->key); return false; } /* Do a syntax check even though strtol would do some of this for us, since otherwise some syntax errors may go silently undetected. */ p = param->raw_value; if (*p == '-') p++; for (; *p != '\0'; p++) if (*p < '0' || *p > '9') break; if (*p != '\0') { warn("%s:%u: %s is not an integer", file, param->line, param->key); return false; } /* Do the actual conversion with strtol. */ errno = 0; param->value.signed_number = strtol(param->raw_value, NULL, 10); if (errno != 0) { warn("%s:%u: %s doesn't convert to an integer", file, param->line, param->key); return false; } *value = param->value.signed_number; param->type = VALUE_NUMBER; return true; } /* ** Convert a given parameter value to an unsigned integer, returning true ** if successful and false otherwise. */ static bool convert_unsigned_number(struct config_parameter *param, const char *file, void *result) { unsigned long *value = result; char *p; if (param->type == VALUE_UNUMBER) { *value = param->value.unsigned_number; return true; } else if (param->type != VALUE_UNKNOWN) { warn("%s:%u: %s is not an integer", file, param->line, param->key); return false; } /* Do a syntax check even though strtoul would do some of this for us, * since otherwise some syntax errors may go silently undetected. */ p = param->raw_value; if (*p == '-') { warn("%s:%u: %s is not a positive integer", file, param->line, param->key); return false; } for (; *p != '\0'; p++) if (*p < '0' || *p > '9') break; if (*p != '\0') { warn("%s:%u: %s is not an integer", file, param->line, param->key); return false; } /* Do the actual conversion with strtoul. */ errno = 0; param->value.unsigned_number = strtoul(param->raw_value, NULL, 10); if (errno != 0) { warn("%s:%u: %s doesn't convert to a positive integer", file, param->line, param->key); return false; } *value = param->value.unsigned_number; param->type = VALUE_UNUMBER; return true; } /* ** Convert a given parameter value to a real number, returning true if ** successful and false otherwise. */ static bool convert_real(struct config_parameter *param, const char *file, void *result) { double *value = result; char *p; if (param->type == VALUE_REAL) { *value = param->value.real; return true; } else if (param->type != VALUE_UNKNOWN) { warn("%s:%u: %s is not a real number", file, param->line, param->key); return false; } /* Do a syntax check even though strtod would do some of this for us, since otherwise some syntax errors may go silently undetected. We have a somewhat stricter syntax. */ p = param->raw_value; if (*p == '-') p++; if (*p < '0' || *p > '9') goto fail; while (*p != '\0' && *p >= '0' && *p <= '9') p++; if (*p == '.') { p++; if (*p < '0' || *p > '9') goto fail; while (*p != '\0' && *p >= '0' && *p <= '9') p++; } if (*p == 'e') { p++; if (*p == '-') p++; if (*p < '0' || *p > '9') goto fail; while (*p != '\0' && *p >= '0' && *p <= '9') p++; } if (*p != '\0') goto fail; /* Do the actual conversion with strtod. */ errno = 0; param->value.real = strtod(param->raw_value, NULL); if (errno != 0) { warn("%s:%u: %s doesn't convert to a real number", file, param->line, param->key); return false; } *value = param->value.real; param->type = VALUE_REAL; return true; fail: warn("%s:%u: %s is not a real number", file, param->line, param->key); return false; } /* ** Convert a given parameter value to a string, returning true if successful ** and false otherwise. */ static bool convert_string(struct config_parameter *param, const char *file, void *result) { const char **value = result; char *string; if (param->type == VALUE_STRING) { *value = param->value.string; return true; } else if (param->type != VALUE_UNKNOWN) { warn("%s:%u: %s is not a string", file, param->line, param->key); return false; } if (*param->raw_value == '"') string = token_unquote_string(param->raw_value, file, param->line); else string = xstrdup(param->raw_value); if (string == NULL) return false; param->value.string = string; param->type = VALUE_STRING; *value = param->value.string; return true; } /* ** Convert a given parameter value to a list, returning true if succcessful ** and false otherwise. */ static bool convert_list(struct config_parameter *param, const char *file, void *result) { const struct vector **value = result; struct vector *vector; char *string; if (param->type == VALUE_LIST) { *value = param->value.list; return true; } else if (param->type != VALUE_UNKNOWN) { warn("%s:%u: %s is not a list", file, param->line, param->key); return false; } /* If the value type is unknown, the value was actually a string. We support returning string values as lists with one element, since that way config_param_list can be used to read any value. */ if (*param->raw_value == '"') { string = token_unquote_string(param->raw_value, file, param->line); if (string == NULL) return false; vector = vector_new(); vector_resize(vector, 1); vector->strings[0] = string; vector->count++; } else { vector = vector_new(); vector_add(vector, param->raw_value); } param->type = VALUE_LIST; param->value.list = vector; *value = vector; return true; } /* ** Given a group, query it for the given parameter and then when the ** parameter is found, check to see if it's already marked invalid. If so, ** fail quietly; otherwise, hand it off to the conversion function to do ** type-specific work, returning the result. Returns true if the parameter ** is found in the group or one of its parents and convert can successfully ** convert the raw value and put it in result, false otherwise (either for ** the parameter not being found or for it being the wrong type). */ static bool group_parameter_get(struct config_group *group, const char *key, void *result, convert_func convert) { struct config_group *current = group; while (current != NULL) { struct config_parameter *param; param = hash_lookup(current->params, key); if (param != NULL) { if (param->type == VALUE_INVALID) return false; else return (*convert)(param, current->file, result); } current = current->parent; } return false; } /* ** All of the config_param_* functions do the following: ** ** Given a group, query it for the given parameter, interpreting its value as ** the appropriate type and returning it in the third argument. Returns true ** on success, false on failure (such as the parameter not being set or an ** error), and report errors via warn. */ bool config_param_boolean(struct config_group *group, const char *key, bool *result) { return group_parameter_get(group, key, result, convert_boolean); } bool config_param_signed_number(struct config_group *group, const char *key, long *result) { return group_parameter_get(group, key, result, convert_signed_number); } bool config_param_unsigned_number(struct config_group *group, const char *key, unsigned long *result) { return group_parameter_get(group, key, result, convert_unsigned_number); } bool config_param_real(struct config_group *group, const char *key, double *result) { return group_parameter_get(group, key, result, convert_real); } bool config_param_string(struct config_group *group, const char *key, const char **result) { return group_parameter_get(group, key, result, convert_string); } bool config_param_list(struct config_group *group, const char *key, const struct vector **result) { return group_parameter_get(group, key, result, convert_list); } /* ** A hash traversal function to add all parameter keys to the vector provided ** as the second argument. Currently this does a simple linear search to see ** if this parameter was already set, which makes the config_params function ** overall O(n^2). So far, this isn't a problem. */ static void parameter_collect(void *element, void *cookie) { struct config_parameter *param = element; struct vector *params = cookie; size_t i; for (i = 0; i < params->count; i++) if (strcmp(params->strings[i], param->key) == 0) return; vector_add(params, param->key); } /* ** Returns a newly allocated vector of all of the config parameters in a ** group, including the inherited ones (not implemented yet). */ struct vector * config_params(struct config_group *group) { struct vector *params; size_t size; params = vector_new(); for (; group != NULL; group = group->parent) { size = hash_count(group->params); vector_resize(params, params->allocated + size); hash_traverse(group->params, parameter_collect, params); } return params; } /* ** Given a config_group and a group type, find the next group in a ** depth-first traversal of the configuration tree of the given type and ** return it. Returns NULL if no further groups of that type are found. */ struct config_group * config_find_group(struct config_group *group, const char *type) { struct config_group *sib; if (group->child != NULL) { if (strcmp(group->child->type, type) == 0) return group->child; else return config_find_group(group->child, type); } for (; group != NULL; group = group->parent) for (sib = group->next; sib != NULL; sib = sib->next) { if (strcmp(sib->type, type) == 0) return sib; if (sib->child != NULL) { if (strcmp(sib->child->type, type) == 0) return sib->child; else return config_find_group(sib->child, type); } } return NULL; } /* ** Find the next group with the same type as the current group. This is just ** a simple wrapper around config_find_group for convenience. */ struct config_group * config_next_group(struct config_group *group) { return config_find_group(group, group->type); } /* ** Report an error in a given parameter. Used so that the file and line ** number can be included in the error message. */ void config_error_param(struct config_group *group, const char *key, const char *fmt, ...) { va_list args; char *message, *file; struct config_parameter *param; va_start(args, fmt); xvasprintf(&message, fmt, args); va_end(args); param = hash_lookup(group->params, key); if (param == NULL) warn("%s", message); else { file = (group->included != NULL ? group->included : group->file); warn("%s:%u: %s", file, param->line, message); } free(message); } /* ** Report an error in a given group. Used so that the file and line number ** can be included in the error message. Largely duplicates ** config_error_param (which we could fix if we were sure we had va_copy). */ void config_error_group(struct config_group *group, const char *fmt, ...) { va_list args; char *message; va_start(args, fmt); xvasprintf(&message, fmt, args); va_end(args); warn("%s:%u: %s", group->file, group->line, message); free(message); } inn-2.6.0/lib/mkstemp.c0000644000175200017520000000557112575023702014322 0ustar iuliusiulius/* $Id: mkstemp.c 9759 2014-12-04 20:11:38Z iulius $ * * Replacement for a missing mkstemp. * * Provides the same functionality as the library function mkstemp for those * systems that don't have it. * * The canonical version of this file is maintained in the rra-c-util package, * which can be found at . * * Written by Russ Allbery * * The authors hereby relinquish any claim to any copyright that they may have * in this work, whether granted under contract or by operation of law or * international treaty, and hereby commit to the public, at large, that they * shall not, at any time in the future, seek to enforce any copyright in this * work against any person or entity, or prevent any person or entity from * copying, publishing, distributing or creating derivative works of this * work. */ #include "config.h" #include "clibrary.h" #include #include #ifdef HAVE_SYS_TIME_H # include #endif #include /* * If we're running the test suite, rename mkstemp to avoid conflicts with the * system version. #undef it first because some systems may define it to * another name. */ #if TESTING # undef mkstemp # define mkstemp test_mkstemp int test_mkstemp(char *); #endif /* Pick the longest available integer type. */ #if HAVE_LONG_LONG_INT typedef unsigned long long long_int_type; #else typedef unsigned long long_int_type; #endif int mkstemp(char *template) { static const char letters[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; size_t length; char *XXXXXX; struct timeval tv; long_int_type randnum, working; int i, tries, fd; /* * Make sure we have a valid template and initialize p to point at the * beginning of the template portion of the string. */ length = strlen(template); if (length < 6) { errno = EINVAL; return -1; } XXXXXX = template + length - 6; if (strcmp(XXXXXX, "XXXXXX") != 0) { errno = EINVAL; return -1; } /* Get some more-or-less random information. */ gettimeofday(&tv, NULL); randnum = ((long_int_type) tv.tv_usec << 16) ^ tv.tv_sec ^ getpid(); /* * Now, try to find a working file name. We try no more than TMP_MAX file * names. */ for (tries = 0; tries < TMP_MAX; tries++) { for (working = randnum, i = 0; i < 6; i++) { XXXXXX[i] = letters[working % 62]; working /= 62; } fd = open(template, O_RDWR | O_CREAT | O_EXCL, 0600); if (fd >= 0 || (errno != EEXIST && errno != EISDIR)) return fd; /* * This is a relatively random increment. Cut off the tail end of * tv_usec since it's often predictable. */ randnum += (tv.tv_usec >> 10) & 0xfff; } errno = EEXIST; return -1; } inn-2.6.0/lib/sendarticle.c0000644000175200017520000000144312575023702015131 0ustar iuliusiulius/* $Id: sendarticle.c 8903 2010-01-17 18:21:56Z iulius $ ** */ #include "config.h" #include "clibrary.h" #include "inn/libinn.h" #include "inn/nntp.h" /* ** Send a string of one or more lines down a stdio FILE using RFC 3977 ** conventions. Return -1 on error. */ int NNTPsendarticle(char *p, FILE *F, bool Terminate) { char *next; for (; p && *p; next[-1] = '\n', p = next) { /* Get pointer to next line. Truncate long lines. */ if ((next = strchr(p, '\n')) != NULL) *next++ = '\0'; /* Write line. */ if (*p == '.' && putc('.', F) == EOF) return -1; if (fprintf(F, "%s\r\n", p) == EOF) return -1; /* Done? */ if (next == NULL) break; } if (Terminate && fprintf(F, ".\r\n") == EOF) return -1; return fflush(F) == EOF || ferror(F) ? -1 : 0; } inn-2.6.0/lib/clientlib.c0000644000175200017520000000647112575023702014607 0ustar iuliusiulius/* $Id: clientlib.c 8903 2010-01-17 18:21:56Z iulius $ ** ** Routines compatible with the NNTP "clientlib" routines. */ #include "config.h" #include "clibrary.h" #include "inn/innconf.h" #include "inn/libinn.h" #include "inn/nntp.h" FILE *ser_rd_fp = NULL; FILE *ser_wr_fp = NULL; char ser_line[NNTP_MAXLEN_COMMAND + 2]; /* ** Get the name of the NNTP server. Ignore the filename; we use ** our own configuration stuff. Return pointer to static data. */ char * getserverbyfile(char *file UNUSED) { static char buff[256]; strlcpy(buff, innconf->server, sizeof(buff)); return buff; } /* ** Get a connection to the remote news server. Return server's reply ** code or -1 on error. */ int server_init(char *host, int port) { char line2[NNTP_MAXLEN_COMMAND]; /* This interface may be used by clients that assume C News behavior and won't read inn.conf themselves. */ if (innconf == NULL) if (!innconf_read(NULL)) return -1; if (NNTPconnect(host, port, &ser_rd_fp, &ser_wr_fp, ser_line, sizeof(ser_line)) < 0) { if (ser_line[0] == '\0') /* I/O problem. */ return -1; /* Server rejected connection; return it's reply code. */ return atoi(ser_line); } /* Send the INN command; if understood, use that reply. */ put_server("MODE READER"); if (get_server(line2, (int)sizeof line2) < 0) return -1; if (atoi(line2) != NNTP_ERR_COMMAND) strlcpy(ser_line, line2, sizeof(ser_line)); /* Connected; return server's reply code. */ return atoi(ser_line); } #define CANTPOST \ "NOTE: This machine does not have permission to post articles" #define CANTUSE \ "This machine does not have permission to use the %s news server.\n" /* ** Print a message based on the the server's initial response. ** Return -1 if server wants us to go away. */ int handle_server_response(int response, char *host) { char *p; switch (response) { default: printf("Unknown response code %d from %s.\n", response, host); return -1; case NNTP_FAIL_TERMINATING: if (atoi(ser_line) == response) { p = &ser_line[strlen(ser_line) - 1]; if (*p == '\n' && *--p == '\r') *p = '\0'; if (p > &ser_line[3]) { printf("News server %s unavailable: %s\n", host, &ser_line[4]); return -1; } } printf("News server %s unavailable, try later.\n", host); return -1; case NNTP_ERR_ACCESS: printf(CANTUSE, host); return -1; case NNTP_OK_BANNER_NOPOST: printf("%s.\n", CANTPOST); /* FALLTHROUGH */ case NNTP_OK_BANNER_POST: break; } return 0; } /* ** Send a line of text to the server. */ void put_server(const char *buff) { fprintf(ser_wr_fp, "%s\r\n", buff); fflush(ser_wr_fp); } /* ** Get a line of text from the server, strip trailing \r\n. ** Return -1 on error. */ int get_server(char *buff, int buffsize) { char *p; if (fgets(buff, buffsize, ser_rd_fp) == NULL) return -1; p = &buff[strlen(buff)]; if (p >= &buff[2] && p[-2] == '\r' && p[-1] == '\n') p[-2] = '\0'; return 0; } /* ** Send QUIT and close the server. */ void close_server(void) { char buff[NNTP_MAXLEN_COMMAND]; if (ser_wr_fp != NULL && ser_rd_fp != NULL) { put_server("QUIT"); fclose(ser_wr_fp); ser_wr_fp = NULL; get_server(buff, (int)sizeof buff); fclose(ser_rd_fp); ser_rd_fp = NULL; } } inn-2.6.0/lib/hash.c0000644000175200017520000000776112575023702013570 0ustar iuliusiulius/* This provides a generic hash function for use w/INN. Currently is implemented using MD5, but should probably have a mechanism for choosing the hash algorithm and tagging the hash with the algorithm used */ #include "config.h" #include "clibrary.h" #include #include "inn/md5.h" #include "inn/utility.h" #include "inn/libinn.h" static HASH empty= { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }}; /* cipoint - where in this message-ID does it become case-insensitive? * * The RFC822 code is not quite complete. Absolute, total, full RFC822 * compliance requires a horrible parsing job, because of the arcane * quoting conventions -- abc"def"ghi is not equivalent to abc"DEF"ghi, * for example. There are three or four things that might occur in the * domain part of a message-id that are case-sensitive. They don't seem * to ever occur in real news, thank Cthulhu. (What? You were expecting * a merciful and forgiving deity to be invoked in connection with RFC822? * Forget it; none of them would come near it.) * * Returns: pointer into s, or NULL for "nowhere" */ static const char * cipoint(const char *s, size_t size) { char *p; static const char post[] = "postmaster"; static int plen = sizeof(post) - 1; if ((p = memchr(s, '@', size))== NULL) /* no local/domain split */ return NULL; /* assume all local */ if ((p - (s + 1) == plen) && !strncasecmp(post, s+1, plen)) { /* crazy -- "postmaster" is case-insensitive */ return s; } return p; } HASH Hash(const void *value, const size_t len) { struct md5_context context; HASH hash; md5_init(&context); md5_update(&context, value, len); md5_final(&context); memcpy(&hash, &context.digest, (sizeof(hash) < sizeof(context.digest)) ? sizeof(hash) : sizeof(context.digest)); return hash; } HASH HashMessageID(const char *MessageID) { char *new = NULL; const char *cip, *p = NULL; char *q; int len; HASH hash; len = strlen(MessageID); cip = cipoint(MessageID, len); if (cip != NULL) { for (p = cip + 1; *p != '\0'; p++) { if (!islower((unsigned char) *p)) { new = xstrdup(MessageID); break; } } } if (new != NULL) for (q = new + (p - MessageID); *q != '\0'; q++) *q = tolower((unsigned char) *q); hash = Hash(new ? new : MessageID, len); if (new != NULL) free(new); return hash; } /* ** Check if the hash is all zeros, and subseqently empty, see HashClear ** for more info on this. */ bool HashEmpty(const HASH h) { return (memcmp(&empty, &h, sizeof(HASH)) == 0); } /* ** Set the hash to all zeros. Using all zeros as the value for empty ** introduces the possibility of colliding w/a value that actually hashes ** to all zeros, but that's fairly unlikely. */ void HashClear(HASH *hash) { memset(hash, '\0', sizeof(HASH)); } /* ** Convert the binary form of the hash to a form that we can use in error ** messages and logs. Just a wrapper around inn_encode_hex. */ char * HashToText(const HASH hash) { static char hashstr[(sizeof(HASH) * 2) + 1]; inn_encode_hex((unsigned char *) hash.hash, sizeof(HASH), hashstr, sizeof(hashstr)); return hashstr; } /* ** Convert the ASCII representation of the hash back to the canonical form. ** Just a wrapper around inn_decode_hex. */ HASH TextToHash(const char *text) { HASH hash; inn_decode_hex(text, (unsigned char *) hash.hash, sizeof(HASH)); return hash; } /* This is rather gross, we compare like the last 4 bytes of the hash are at the beginning because dbz considers them to be the most significant bytes */ int HashCompare(const HASH *h1, const HASH *h2) { int i; int tocomp = sizeof(HASH) - sizeof(unsigned long); if ((i = memcmp(&h1->hash[tocomp], &h1->hash[tocomp], sizeof(unsigned long)))) return i; return memcmp(h1, h2, sizeof(HASH)); } inn-2.6.0/lib/perl.c0000644000175200017520000002720512575023702013602 0ustar iuliusiulius/* $Id: perl.c 9930 2015-08-10 11:51:03Z iulius $ ** ** Embedded Perl support for INN. ** ** Originally written by Christophe Wolfhugel (although ** he wouldn't recognize it any more, so don't blame him) and modified, ** expanded, and tweaked by James Brister, Dave Hayes, and Russ Allbery ** among others. ** ** This file contains the Perl linkage shared by both nnrpd and innd. It ** assumes Perl 5.004 or later. */ #include "config.h" /* Skip this entire file if DO_PERL (./configure --with-perl) isn't set. */ #if DO_PERL #include "clibrary.h" #include #include #include "inn/libinn.h" #include #include #include #include "ppport.h" #include "innperl.h" /* Provided by DynaLoader but not declared in Perl's header files. */ XS(boot_DynaLoader); /* Forward declarations. */ void PerlSilence(void); void PerlUnSilence(void); void xs_init(pTHX); /* Forward declarations of XS subs. */ XS(XS_INN_syslog); /* Whether Perl filtering is currently active. */ bool PerlFilterActive = false; /* The filter sub called (filter_art or filter_post). */ CV *perl_filter_cv; /* The embedded Perl interpreter. */ static PerlInterpreter *PerlCode = NULL; static void LogPerl(void) { syslog(L_NOTICE, "SERVER perl filtering %s", PerlFilterActive ? "enabled" : "disabled"); } /* ** Enable or disable the Perl filter. Takes the desired state of the filter ** as an argument and returns success or failure. Failure to enable ** indicates that the filter is not defined. */ bool PerlFilter(bool value) { dSP; char *argv[] = { NULL }; SV *errsv; if (value == PerlFilterActive) return true; if (!value) { /* Execute an end function, if one is defined. */ if (perl_get_cv("filter_end", false) != NULL) { ENTER; SAVETMPS; /* No need for PUSHMARK(SP) with call_argv(). */ perl_call_argv("filter_end", G_EVAL | G_DISCARD | G_NOARGS, argv); SPAGAIN; errsv = ERRSV; if (SvTRUE(errsv)) { syslog (L_ERROR, "SERVER perl function filter_end died: %s", SvPV(errsv, PL_na)); (void) POPs; } PUTBACK; FREETMPS; LEAVE; } PerlFilterActive = value; LogPerl(); return true; } else { if (perl_filter_cv == NULL) { syslog (L_ERROR, "SERVER perl filter not defined"); return false; } else { PerlFilterActive = value; LogPerl(); return true; } } } /* ** Loads a setup Perl module. startupfile is the name of the file loaded ** one-time at startup. filterfile is the file containing the filter ** functions which is loaded at startup and at each reload. function is a ** function name that must be defined after the filterfile file is loaded for ** filtering to be turned on to start with. */ void PERLsetup (char *startupfile, char *filterfile, const char *function) { if (PerlCode == NULL) { /* Perl waits on standard input if not called with '-e'. */ int argc = 3; const char *argv_innd[] = { "innd", "-e", "0", NULL }; char **argv = (char **)argv_innd; /* Cast required by Perl 5.10. */ char **env = { NULL }; #ifdef PERL_SYS_INIT3 PERL_SYS_INIT3(&argc, &argv, &env); #endif PerlCode = perl_alloc(); perl_construct(PerlCode); #ifdef PERL_EXIT_DESTRUCT_END PL_exit_flags |= PERL_EXIT_DESTRUCT_END; #endif perl_parse(PerlCode, xs_init, argc, argv, env) ; } if (startupfile != NULL && filterfile != NULL) { char *evalfile = NULL; bool failure; SV *errsv; dSP; ENTER; SAVETMPS; PUSHMARK(SP); PUTBACK; /* The Perl expression which will be evaluated. */ xasprintf(&evalfile, "do '%s'", startupfile); PerlSilence(); perl_eval_pv(evalfile, TRUE); PerlUnSilence(); SPAGAIN; free(evalfile); evalfile = NULL; /* Check $@. */ errsv = ERRSV; if (SvTRUE(errsv)) { failure = true; syslog(L_ERROR,"SERVER perl loading %s failed: %s", startupfile, SvPV(errsv, PL_na)); } else { failure = false; } PUTBACK; FREETMPS; LEAVE; if (failure) { PerlFilter (false); } else { PERLreadfilter (filterfile, function); } } else { PERLreadfilter (filterfile, function); } } /* Load the perl file FILTERFILE. After it is load check that the given function is defined. If yes filtering is turned on. If not it is turned off. We remember whether the filter function was defined properly so that we can catch when the user tries to turn filtering on without the function there. */ int PERLreadfilter(char *filterfile, const char *function) { dSP; char *argv[] = { NULL }; char *evalfile = NULL; bool failure; SV *errsv; if (perl_get_cv("filter_before_reload", false) != NULL) { ENTER; SAVETMPS; /* No need for PUSHMARK(SP) with call_argv(). */ perl_call_argv("filter_before_reload", G_EVAL|G_DISCARD|G_NOARGS, argv); SPAGAIN; /* Check $@. */ errsv = ERRSV; if (SvTRUE(errsv)) { failure = true; syslog (L_ERROR,"SERVER perl function filter_before_reload died: %s", SvPV(errsv, PL_na)); (void)POPs; } else { failure = false; } PUTBACK; FREETMPS; LEAVE; if (failure) PerlFilter (false); } ENTER; SAVETMPS; PUSHMARK(SP); PUTBACK; /* The Perl expression which will be evaluated. */ xasprintf(&evalfile, "do '%s'", filterfile); PerlSilence(); perl_eval_pv(evalfile, TRUE); PerlUnSilence(); SPAGAIN; free(evalfile); evalfile = NULL; /* Check $@. */ errsv = ERRSV; if (SvTRUE(errsv)) { failure = true; syslog (L_ERROR,"SERVER perl loading %s failed: %s", filterfile, SvPV(errsv, PL_na)); } else { failure = false; } PUTBACK; FREETMPS; LEAVE; if (failure) { PerlFilter (false); /* If the reload failed we don't want the old definition hanging around. */ ENTER; SAVETMPS; PUSHMARK(SP); PUTBACK; xasprintf(&evalfile, "undef &%s", function); perl_eval_pv(evalfile, TRUE); SPAGAIN; free(evalfile); evalfile = NULL; /* Check $@. */ errsv = ERRSV; if (SvTRUE(errsv)) { syslog (L_ERROR,"SERVER perl undef &%s failed: %s", function, SvPV(errsv, PL_na)) ; } PUTBACK; FREETMPS; LEAVE; } else if ((perl_filter_cv = perl_get_cv(function, false)) == NULL) { PerlFilter (false); } if (perl_get_cv("filter_after_reload", false) != NULL) { ENTER; SAVETMPS; /* No need for PUSHMARK(SP) with call_argv(). */ perl_call_argv("filter_after_reload", G_EVAL|G_DISCARD|G_NOARGS, argv); SPAGAIN; /* Check $@. */ errsv = ERRSV; if (SvTRUE(errsv)) { failure = true; syslog (L_ERROR,"SERVER perl function filter_after_reload died: %s", SvPV(errsv, PL_na)); (void)POPs; } else { failure = false; } PUTBACK; FREETMPS; LEAVE; if (failure) { PerlFilter (false); } } /* We try to find an inversion between filter_innd.pl * and filter_nnrpd.pl. */ if (function != NULL) { if ((strncmp(function, "filter_art", 10) == 0) && (perl_get_cv("filter_post", false) != NULL)) { syslog(L_NOTICE, "filter_innd.pl defines a filter_post function" " -- maybe a confusion with filter_nnrpd.pl?"); } if ((strncmp(function, "filter_post", 11) == 0) && (perl_get_cv("filter_art", false) != NULL)) { syslog(L_NOTICE, "filter_nnrpd.pl defines a filter_art function" " -- maybe a confusion with filter_innd.pl?"); } } return (perl_filter_cv != NULL) ; } /* ** Stops using the Perl filter. */ void PerlClose(void) { if (PerlCode != NULL) { perl_destruct(PerlCode); perl_free(PerlCode); #ifdef PERL_SYS_TERM PERL_SYS_TERM(); #endif } PerlFilterActive = false; } /* ** Redirects STDOUT/STDERR briefly (otherwise PERL complains to the net ** connection for NNRPD and that just won't do) -- dave@jetcafe.org */ static int savestdout = 0; static int savestderr = 0; void PerlSilence(void) { int newfd; /* Save the descriptors */ if ( (savestdout = dup(1)) < 0) { syslog(L_ERROR,"SERVER perl silence cant redirect stdout: %m"); savestdout = 0; return; } if ( (savestderr = dup(2)) < 0) { syslog(L_ERROR,"SERVER perl silence cant redirect stderr: %m"); savestdout = 0; savestderr = 0; return; } /* Open /dev/null */ if ((newfd = open("/dev/null",O_WRONLY)) < 0) { syslog(L_ERROR,"SERVER perl silence cant open /dev/null: %m"); savestdout = 0; savestderr = 0; return; } /* Redirect descriptors */ if (dup2(newfd,1) < 0) { syslog(L_ERROR,"SERVER perl silence cant redirect stdout: %m"); savestdout = 0; return; } if (dup2(newfd,2) < 0) { syslog(L_ERROR,"SERVER perl silence cant redirect stderr: %m"); savestderr = 0; return; } close(newfd); } void PerlUnSilence(void) { if (savestdout != 0) { if (dup2(savestdout,1) < 0) { syslog(L_ERROR,"SERVER perl silence cant restore stdout: %m"); } close(savestdout); savestdout = 0; } if (savestderr != 0) { if (dup2(savestderr,2) < 0) { syslog(L_ERROR,"SERVER perl silence cant restore stderr: %m"); } close(savestderr); savestderr = 0; } } /* ** The remainder of this file consists of XS callbacks usable by either ** innd or nnrpd and initialized automatically when the Perl filter is ** initialized, as well as the function that initializes them. */ /* ** Log a message via syslog. Only the first letter of the priority ** matters, and this function assumes that the controlling program has ** already done an openlog(). The argument must be a complete message, not ** a printf-style format. */ XS(XS_INN_syslog) { dXSARGS; const char *loglevel; const char *logmsg; int priority; /* Suppress warnings for the mandatory XS argument. */ cv = cv; if (items != 2) croak("Usage: INN::syslog(level, message)"); loglevel = (const char *) SvPV(ST(0), PL_na); logmsg = (const char *) SvPV(ST(1), PL_na); switch (*loglevel) { case 'a': case 'A': priority = LOG_ALERT; break; case 'c': case 'C': priority = LOG_CRIT; break; case 'e': case 'E': priority = LOG_ERR; break; case 'w': case 'W': priority = LOG_WARNING; break; case 'n': case 'N': priority = LOG_NOTICE; break; case 'i': case 'I': priority = LOG_INFO; break; case 'd': case 'D': priority = LOG_DEBUG; break; default: priority = LOG_NOTICE; } syslog(priority, "filter: %s", logmsg); XSRETURN_UNDEF; } void xs_init(pTHX) { dXSUB_SYS; inn_newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, "perl.c"); inn_newXS("INN::syslog", XS_INN_syslog, "perl.c"); } #endif /* defined(DO_PERL) */ inn-2.6.0/lib/getaddrinfo.c0000644000175200017520000003137612575023702015132 0ustar iuliusiulius/* $Id: getaddrinfo.c 9883 2015-05-25 10:03:20Z iulius $ * * Replacement for a missing getaddrinfo. * * This is an implementation of getaddrinfo for systems that don't have one so * that networking code can use a consistant interface without #ifdef. It is * a fairly minimal implementation, with the following limitations: * * - IPv4 support only. IPv6 is not supported. * - AI_ADDRCONFIG is ignored. * - Not thread-safe due to gethostbyname and getservbyname. * - SOCK_DGRAM and SOCK_STREAM only. * - Multiple possible socket types only generate one addrinfo struct. * - Protocol hints aren't used correctly. * * The last four issues could probably be easily remedied, but haven't been * needed to date. Adding IPv6 support isn't worth it; systems with IPv6 * support should already support getaddrinfo natively. * * The canonical version of this file is maintained in the rra-c-util package, * which can be found at . * * Written by Russ Allbery * * The authors hereby relinquish any claim to any copyright that they may have * in this work, whether granted under contract or by operation of law or * international treaty, and hereby commit to the public, at large, that they * shall not, at any time in the future, seek to enforce any copyright in this * work against any person or entity, or prevent any person or entity from * copying, publishing, distributing or creating derivative works of this * work. */ #include "config.h" #include "clibrary.h" #include "portable/socket.h" #include /* We need access to h_errno to map errors from gethostbyname. */ #if !HAVE_DECL_H_ERRNO extern int h_errno; #endif /* * The netdb constants, which aren't always defined (particularly if h_errno * isn't declared). We also make sure that a few of the less-used ones are * defined so that we can deal with them in case statements. */ #ifndef HOST_NOT_FOUND # define HOST_NOT_FOUND 1 # define TRY_AGAIN 2 # define NO_RECOVERY 3 # define NO_DATA 4 #endif #ifndef NETDB_INTERNAL # define NETDB_INTERNAL -1 #endif /* * If we're running the test suite, rename the functions to avoid conflicts * with the system version. Note that we don't rename the structures and * constants, but that should be okay (except possibly for gai_strerror). */ #if TESTING # undef gai_strerror # undef freeaddrinfo # undef getaddrinfo # define gai_strerror test_gai_strerror # define freeaddrinfo test_freeaddrinfo # define getaddrinfo test_getaddrinfo const char *test_gai_strerror(int); void test_freeaddrinfo(struct addrinfo *); int test_getaddrinfo(const char *, const char *, const struct addrinfo *, struct addrinfo **); #endif /* * If the native platform doesn't support AI_NUMERICSERV or AI_NUMERICHOST, * pick some other values for them. */ #if TESTING # if AI_NUMERICSERV == 0 # undef AI_NUMERICSERV # define AI_NUMERICSERV 0x0080 # endif # if AI_NUMERICHOST == 0 # undef AI_NUMERICHOST # define AI_NUMERICHOST 0x0100 # endif #endif /* * Value representing all of the hint flags set. Linux uses flags up to * 0x0400, and Mac OS X up to 0x1000, so be sure not to break when testing * on these platforms. */ #if TESTING # ifdef HAVE_GETADDRINFO # define AI_INTERNAL_ALL 0x1fff # else # define AI_INTERNAL_ALL 0x01ff # endif #else # define AI_INTERNAL_ALL 0x007f #endif /* Table of strings corresponding to the EAI_* error codes. */ static const char * const gai_errors[] = { "Host name lookup failure", /* 1 EAI_AGAIN */ "Invalid flag value", /* 2 EAI_BADFLAGS */ "Unknown server error", /* 3 EAI_FAIL */ "Unsupported address family", /* 4 EAI_FAMILY */ "Memory allocation failure", /* 5 EAI_MEMORY */ "Host unknown or not given", /* 6 EAI_NONAME */ "Service not supported for socket", /* 7 EAI_SERVICE */ "Unsupported socket type", /* 8 EAI_SOCKTYPE */ "System error", /* 9 EAI_SYSTEM */ "Supplied buffer too small", /* 10 EAI_OVERFLOW */ }; /* Macro to set the len attribute of sockaddr_in. */ #if HAVE_STRUCT_SOCKADDR_SA_LEN # define sin_set_length(s) ((s)->sin_len = sizeof(struct sockaddr_in)) #else # define sin_set_length(s) /* empty */ #endif /* * Used for iterating through arrays. ARRAY_SIZE returns the number of * elements in the array (useful for a < upper bound in a for loop). */ #define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0])) /* * Return a constant string for a given EAI_* error code or a string * indicating an unknown error. */ const char * gai_strerror(int ecode) { if (ecode < 1 || (size_t) ecode > ARRAY_SIZE(gai_errors)) return "Unknown error"; else return gai_errors[ecode - 1]; } /* * Free a linked list of addrinfo structs. */ void freeaddrinfo(struct addrinfo *ai) { struct addrinfo *next; while (ai != NULL) { next = ai->ai_next; free(ai->ai_addr); free(ai->ai_canonname); free(ai); ai = next; } } /* * Convert a numeric service string to a number with error checking, returning * true if the number was parsed correctly and false otherwise. Stores the * converted number in the second argument. Equivalent to calling strtol, but * with the base always fixed at 10, with checking of errno, ensuring that all * of the string is consumed, and checking that the resulting number is * positive. */ static bool convert_service(const char *string, long *result) { char *end; if (*string == '\0') return false; errno = 0; *result = strtol(string, &end, 10); if (errno != 0 || *end != '\0' || *result < 0) return false; return true; } /* * Allocate a new addrinfo struct, setting some defaults given that this * implementation is IPv4 only. Also allocates an attached sockaddr_in and * zeroes it, per the requirement for getaddrinfo. Takes the socktype, * canonical name (which is copied if not NULL), address, and port. Returns * NULL on a memory allocation failure. */ static struct addrinfo * gai_addrinfo_new(int socktype, const char *canonical, struct in_addr addr, unsigned short port) { struct addrinfo *ai; struct sockaddr_in *sin; ai = malloc(sizeof(*ai)); if (ai == NULL) return NULL; sin = calloc(1, sizeof(struct sockaddr_in)); if (sin == NULL) { free(ai); return NULL; } ai->ai_addr = (struct sockaddr *) sin; ai->ai_next = NULL; if (canonical == NULL) ai->ai_canonname = NULL; else { ai->ai_canonname = strdup(canonical); if (ai->ai_canonname == NULL) { freeaddrinfo(ai); return NULL; } } ai->ai_flags = 0; ai->ai_family = AF_INET; ai->ai_socktype = socktype; ai->ai_protocol = (socktype == SOCK_DGRAM) ? IPPROTO_UDP : IPPROTO_TCP; ai->ai_addrlen = sizeof(struct sockaddr_in); sin->sin_family = AF_INET; sin->sin_addr = addr; sin->sin_port = htons(port); sin_set_length(sin); return ai; } /* * Look up a service. Takes the service name (which may be numeric), the hint * flags, a pointer to the socket type (used to determine whether TCP or UDP * services are of interest and, if 0, is filled in with the result of * getservbyname if the service was not numeric), and a pointer to the * addrinfo struct to fill in. Returns 0 on success or an EAI_* error on * failure. */ static int gai_service(const char *servname, int flags, int *type, unsigned short *port) { struct servent *servent; const char *protocol; long value; if (convert_service(servname, &value)) { if (value > (1L << 16) - 1) return EAI_SERVICE; *port = value; } else { if (flags & AI_NUMERICSERV) return EAI_NONAME; if (*type != 0) protocol = (*type == SOCK_DGRAM) ? "udp" : "tcp"; else protocol = NULL; /* * We really technically should be generating an addrinfo struct for * each possible protocol unless type is set, but this works well * enough for what I need this for. */ servent = getservbyname(servname, protocol); if (servent == NULL) return EAI_NONAME; if (strcmp(servent->s_proto, "udp") == 0) *type = SOCK_DGRAM; else if (strcmp(servent->s_proto, "tcp") == 0) *type = SOCK_STREAM; else return EAI_SERVICE; *port = htons(servent->s_port); } return 0; } /* * Look up a host and fill in a linked list of addrinfo structs with the * results, one per IP address of the returned host. Takes the name or IP * address of the host as a string, the lookup flags, the type of socket (to * fill into the addrinfo structs), the port (likewise), and a pointer to * where the head of the linked list should be put. Returns 0 on success or * the appropriate EAI_* error. */ static int gai_lookup(const char *nodename, int flags, int socktype, unsigned short port, struct addrinfo **res) { struct addrinfo *ai, *first, *prev; struct in_addr addr; struct hostent *host; const char *canonical; int i; if (inet_aton(nodename, &addr)) { canonical = (flags & AI_CANONNAME) ? nodename : NULL; ai = gai_addrinfo_new(socktype, canonical, addr, port); if (ai == NULL) return EAI_MEMORY; *res = ai; return 0; } else { if (flags & AI_NUMERICHOST) return EAI_NONAME; host = gethostbyname(nodename); if (host == NULL) switch (h_errno) { case HOST_NOT_FOUND: return EAI_NONAME; case TRY_AGAIN: case NO_DATA: return EAI_AGAIN; case NO_RECOVERY: return EAI_FAIL; case NETDB_INTERNAL: default: return EAI_SYSTEM; } if (host->h_addr_list[0] == NULL) return EAI_FAIL; canonical = (flags & AI_CANONNAME) ? ((host->h_name != NULL) ? host->h_name : nodename) : NULL; first = NULL; prev = NULL; for (i = 0; host->h_addr_list[i] != NULL; i++) { if (host->h_length != sizeof(addr)) { freeaddrinfo(first); return EAI_FAIL; } memcpy(&addr, host->h_addr_list[i], sizeof(addr)); ai = gai_addrinfo_new(socktype, canonical, addr, port); if (ai == NULL) { freeaddrinfo(first); return EAI_MEMORY; } if (first == NULL) { first = ai; prev = ai; } else { prev->ai_next = ai; prev = ai; } } *res = first; return 0; } } /* * The actual getaddrinfo implementation. */ int getaddrinfo(const char *nodename, const char *servname, const struct addrinfo *hints, struct addrinfo **res) { struct addrinfo *ai; struct in_addr addr; int flags, socktype, status; unsigned short port; /* Take the hints into account and check them for validity. */ if (hints != NULL) { flags = hints->ai_flags; socktype = hints->ai_socktype; if ((flags & AI_INTERNAL_ALL) != flags) return EAI_BADFLAGS; if (hints->ai_family != AF_UNSPEC && hints->ai_family != AF_INET) return EAI_FAMILY; if (socktype != 0 && socktype != SOCK_STREAM && socktype != SOCK_DGRAM) return EAI_SOCKTYPE; /* EAI_SOCKTYPE isn't quite right, but there isn't anything better. */ if (hints->ai_protocol != 0) { int protocol = hints->ai_protocol; if (protocol != IPPROTO_TCP && protocol != IPPROTO_UDP) return EAI_SOCKTYPE; } } else { flags = 0; socktype = 0; } /* * See what we're doing. If nodename is null, either AI_PASSIVE is set or * we're getting information for connecting to a service on the loopback * address. Otherwise, we're getting information for connecting to a * remote system. */ if (servname == NULL) port = 0; else { status = gai_service(servname, flags, &socktype, &port); if (status != 0) return status; } if (nodename != NULL) return gai_lookup(nodename, flags, socktype, port, res); else { if (servname == NULL) return EAI_NONAME; if ((flags & AI_PASSIVE) == AI_PASSIVE) addr.s_addr = INADDR_ANY; else addr.s_addr = htonl(0x7f000001UL); ai = gai_addrinfo_new(socktype, NULL, addr, port); if (ai == NULL) return EAI_MEMORY; *res = ai; return 0; } } inn-2.6.0/lib/xmalloc.c0000644000175200017520000002115212575023702014272 0ustar iuliusiulius/* $Id: xmalloc.c 9882 2015-05-25 10:02:28Z iulius $ * * malloc routines with failure handling. * * Usage: * * extern xmalloc_handler_t memory_error; * extern const char *string; * char *buffer; * va_list args; * * xmalloc_error_handler = memory_error; * buffer = xmalloc(1024); * xrealloc(buffer, 2048); * free(buffer); * buffer = xcalloc(1024); * free(buffer); * buffer = xstrdup(string); * free(buffer); * buffer = xstrndup(string, 25); * free(buffer); * xasprintf(&buffer, "%s", "some string"); * free(buffer); * xvasprintf(&buffer, "%s", args); * * xmalloc, xcalloc, xrealloc, and xstrdup behave exactly like their C library * counterparts without the leading x except that they will never return NULL. * Instead, on error, they call xmalloc_error_handler, passing it the name of * the function whose memory allocation failed, the amount of the allocation, * and the file and line number where the allocation function was invoked * (from __FILE__ and __LINE__). This function may do whatever it wishes, * such as some action to free up memory or a call to sleep to hope that * system resources return. If the handler returns, the interrupted memory * allocation function will try its allocation again (calling the handler * again if it still fails). * * xreallocarray behaves the same as the OpenBSD reallocarray function but for * the same error checking, which in turn is the same as realloc but with * calloc-style arguments and size overflow checking. * * xstrndup behaves like xstrdup but only copies the given number of * characters. It allocates an additional byte over its second argument and * always nul-terminates the string. * * xasprintf and xvasprintf behave just like their GNU glibc library * implementations except that they do the same checking as described above. * xasprintf will only be able to provide accurate file and line information * on systems that support variadic macros. * * The default error handler, if none is set by the caller, prints an error * message to stderr and exits with exit status 1. An error handler must take * a const char * (function name), size_t (bytes allocated), const char * * (file), and int (line). * * xmalloc will return a pointer to a valid memory region on an xmalloc of 0 * bytes, ensuring this by allocating space for one character instead of 0 * bytes. * * The functions defined here are actually x_malloc, x_realloc, etc. The * header file defines macros named xmalloc, etc. that pass the file name and * line number to these functions. * * The canonical version of this file is maintained in the rra-c-util package, * which can be found at . * * Copyright 2012, 2013, 2014 * The Board of Trustees of the Leland Stanford Junior University * Copyright (c) 2004, 2005, 2006 * by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, * 2002, 2003 by The Internet Software Consortium and Rich Salz * * This code is derived from software contributed to the Internet Software * Consortium by Rich Salz. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #include "config.h" #include "clibrary.h" #include "inn/messages.h" #include "inn/xmalloc.h" /* * The default error handler. */ void xmalloc_fail(const char *function, size_t size, const char *file, int line) { if (size == 0) sysdie("failed to format output with %s at %s line %d", function, file, line); else sysdie("failed to %s %lu bytes at %s line %d", function, (unsigned long) size, file, line); } /* Assign to this variable to choose a handler other than the default. */ xmalloc_handler_type xmalloc_error_handler = xmalloc_fail; void * x_malloc(size_t size, const char *file, int line) { void *p; size_t real_size; real_size = (size > 0) ? size : 1; p = malloc(real_size); while (p == NULL) { (*xmalloc_error_handler)("malloc", size, file, line); p = malloc(real_size); } return p; } void * x_calloc(size_t n, size_t size, const char *file, int line) { void *p; n = (n > 0) ? n : 1; size = (size > 0) ? size : 1; p = calloc(n, size); while (p == NULL) { (*xmalloc_error_handler)("calloc", n * size, file, line); p = calloc(n, size); } return p; } void * x_realloc(void *p, size_t size, const char *file, int line) { void *newp; newp = realloc(p, size); while (newp == NULL && size > 0) { (*xmalloc_error_handler)("realloc", size, file, line); newp = realloc(p, size); } return newp; } void * x_reallocarray(void *p, size_t n, size_t size, const char *file, int line) { void *newp; newp = reallocarray(p, n, size); while (newp == NULL && size > 0 && n > 0) { (*xmalloc_error_handler)("reallocarray", n * size, file, line); newp = reallocarray(p, n, size); } return newp; } char * x_strdup(const char *s, const char *file, int line) { char *p; size_t len; len = strlen(s) + 1; p = malloc(len); while (p == NULL) { (*xmalloc_error_handler)("strdup", len, file, line); p = malloc(len); } memcpy(p, s, len); return p; } /* * Avoid using the system strndup function since it may not exist (on Mac OS * X, for example), and there's no need to introduce another portability * requirement. */ char * x_strndup(const char *s, size_t size, const char *file, int line) { const char *p; size_t length; char *copy; /* Don't assume that the source string is nul-terminated. */ for (p = s; (size_t) (p - s) < size && *p != '\0'; p++) ; length = p - s; copy = malloc(length + 1); while (copy == NULL) { (*xmalloc_error_handler)("strndup", length + 1, file, line); copy = malloc(length + 1); } memcpy(copy, s, length); copy[length] = '\0'; return copy; } void x_vasprintf(char **strp, const char *fmt, va_list args, const char *file, int line) { va_list args_copy; int status; va_copy(args_copy, args); status = vasprintf(strp, fmt, args_copy); va_end(args_copy); while (status < 0) { va_copy(args_copy, args); status = vsnprintf(NULL, 0, fmt, args_copy); va_end(args_copy); status = (status < 0) ? 0 : status + 1; (*xmalloc_error_handler)("vasprintf", status, file, line); va_copy(args_copy, args); status = vasprintf(strp, fmt, args_copy); va_end(args_copy); } } #if HAVE_C99_VAMACROS || HAVE_GNU_VAMACROS void x_asprintf(char **strp, const char *file, int line, const char *fmt, ...) { va_list args, args_copy; int status; va_start(args, fmt); va_copy(args_copy, args); status = vasprintf(strp, fmt, args_copy); va_end(args_copy); while (status < 0) { va_copy(args_copy, args); status = vsnprintf(NULL, 0, fmt, args_copy); va_end(args_copy); status = (status < 0) ? 0 : status + 1; (*xmalloc_error_handler)("asprintf", status, file, line); va_copy(args_copy, args); status = vasprintf(strp, fmt, args_copy); va_end(args_copy); } va_end(args); } #else /* !(HAVE_C99_VAMACROS || HAVE_GNU_VAMACROS) */ void x_asprintf(char **strp, const char *fmt, ...) { va_list args, args_copy; int status; va_start(args, fmt); va_copy(args_copy, args); status = vasprintf(strp, fmt, args_copy); va_end(args_copy); while (status < 0) { va_copy(args_copy, args); status = vsnprintf(NULL, 0, fmt, args_copy); va_end(args_copy); status = (status < 0) ? 0 : status + 1; (*xmalloc_error_handler)("asprintf", status, __FILE__, __LINE__); va_copy(args_copy, args); status = vasprintf(strp, fmt, args_copy); va_end(args_copy); } va_end(args); } #endif /* !(HAVE_C99_VAMACROS || HAVE_GNU_VAMACROS) */ inn-2.6.0/lib/dbz.c0000644000175200017520000013247312575023702013423 0ustar iuliusiulius/* $Id: dbz.c 9935 2015-09-02 12:23:29Z iulius $ ** ** dbz database implementation V6.1.1 ** ** Copyright 1988 Jon Zeeff (zeeff@b-tech.ann-arbor.mi.us) ** You can use this code in any manner, as long as you leave my name on it ** and don't hold me responsible for any problems with it. ** ** Hacked on by gdb@ninja.UUCP (David Butler); Sun Jun 5 00:27:08 CDT 1988 ** Various improvments + INCORE by moraes@ai.toronto.edu (Mark Moraes) ** Major reworking by Henry Spencer as part of the C News project. ** ** Minor lint and CodeCenter (Saber) fluff removal by Rich $alz ** (March, 1991). ** Non-portable CloseOnExec() calls added by Rich $alz (September, 1991). ** Added "writethrough" and tagmask calculation code from ** and by Rich $alz ** (December, 1992). ** Merged in MMAP code by David Robinson, formerly ** now (January, 1993). ** ** Major reworking by Clayton O'Neill (coneill@oneill.net). Removed all the ** C News and backwards compatible cruft. Ripped out all the tagmask stuff ** and replaced it with hashed .pag entries. This removes the need for base ** file access. Primary bottleneck now appears to be the hash algorithm and ** search(). You can change DBZ_INTERNAL_HASH_SIZE in dbz.h to increase the ** size of the stored hash. ** ** These routines replace dbm as used by the usenet news software (it's not a ** full dbm replacement by any means). It's fast and simple. It contains no ** AT&T code. ** ** The dbz database exploits the fact that when news stores a ** tuple, the "value" part is a seek offset into a text file, pointing to a ** copy of the "key" part. This avoids the need to store a copy of the key ** in the dbz files. ** ** The basic format of the database is two hash tables, each in it's own ** file. One contains the offsets into the history text file, and the other ** contains a hash of the message id. A value is stored by indexing into the ** tables using a hash value computed from the key; collisions are resolved ** by linear probing (just search forward for an empty slot, wrapping around ** to the beginning of the table if necessary). Linear probing is a ** performance disaster when the table starts to get full, so a complication ** is introduced. Each file actually contains one *or more* tables, stored ** sequentially in the files, and the length of the linear-probe sequences is ** limited. The search (for an existing item or an empy slot always starts ** in the first table of the hash file, and whenever MAXRUN probes have been ** done in table N, probing continues in table N+1. It is best not to ** overflow into more than 1-2 tables or else massive performance degradation ** may occur. Choosing the size of the database is extremely important ** because of this. ** ** The table size is fixed for any particular database, but is determined ** dynamically when a database is rebuilt. The strategy is to try to pick ** the size so the first table will be no more than 2/3 full, that being ** slightly before the point where performance starts to degrade. (It is ** desirable to be a bit conservative because the overflow strategy tends to ** produce files with holes in them, which is a nuisance.) ** ** Tagged hash + offset fuzzy technique merged by Sang-yong Suh (Nov, 1997) ** ** Fixed a bug handling larger than 1Gb history offset by Sang-yong Suh ** (1998) Similar fix was suggested by Mike Hucka (Jan, ** 1998) for dbz-3.3.2. ** ** Limited can't tag warnings once per dbzinit() by Sang-yong Suh (May, 1998) */ #include "config.h" #include "clibrary.h" #include "portable/mmap.h" #include #include #include #include #include #ifdef HAVE_SYS_TIME_H # include #endif #include #include "inn/dbz.h" #include "inn/fdflag.h" #include "inn/messages.h" #include "inn/innconf.h" #include "inn/mmap.h" #include "inn/libinn.h" /* Needed on AIX 4.1 to get fd_set and friends. */ #ifdef HAVE_SYS_SELECT_H # include #endif /* * "LIA" = "leave it alone unless you know what you're doing". * * DBZTEST Generate a standalone program for testing and benchmarking * DEFSIZE default table size (not as critical as in old dbz) * NMEMORY number of days of memory for use in sizing new table (LIA) * MAXRUN length of run which shifts to next table (see below) (LIA) */ static int dbzversion = 6; /* for validating .dir file format */ #ifdef DO_TAGGED_HASH /* assume that for tagged hash, we don't want more than 4byte of_t even if * off_t is 8 bytes -- people who use tagged-hash are usually short on * RAM. */ #define of_t long #else #define of_t off_t #endif #ifdef DO_TAGGED_HASH #define SOF (sizeof(of_t)) #define NOTFOUND ((of_t)-1) #include /* MAXDROPBITS is the maximum number of bits dropped from the offset value. The least significant bits are dropped. The space is used to store hash additional bits, thereby increasing the possibility of the hash detection */ #define MAXDROPBITS 4 /* max # of bits to drop from the offset */ /* MAXFUZZYLENGTH is the maximum in the offset value due to the MAXDROPBITS */ #define MAXFUZZYLENGTH ((1 << MAXDROPBITS) - 1) /* * We assume that unused areas of a binary file are zeros, and that the * bit pattern of `(of_t)0' is all zeros. The alternative is rather * painful file initialization. Note that okayvalue(), if DO_TAGGED_HASH is * defined, knows what value of an offset would cause overflow. */ #define VACANT ((of_t)0) #define BIAS(o) ((o)+1) /* make any valid of_t non-VACANT */ #define UNBIAS(o) ((o)-1) /* reverse BIAS() effect */ #define HASTAG(o) ((o)&taghere) #define TAG(o) ((o)&tagbits) #define NOTAG(o) ((o)&~tagboth) #define CANTAG(o) (((o)&tagboth) == 0) #define MKTAG(v) (((v)<tagmask | c->tagenb) == 0) return; /* Use 10 % larger base file size. Sometimes the offset overflows */ basesize += basesize / 10; /* calculate tagging from old file */ for (m = 1, i = 0; m < (unsigned long)basesize; i++, m <<= 1) continue; /* if we had more tags than the default, use the new data */ c->dropbits = 0; while (m > (1 << TAGSHIFT)) { if (c->dropbits >= MAXDROPBITS) break; c->dropbits++; m >>= 1; i--; } c->tagenb = TAGENB; c->tagmask = TAGMASK; c->tagshift = TAGSHIFT; if ((c->tagmask | c->tagenb) && m > (1 << TAGSHIFT)) { c->tagshift = i; c->tagmask = (~(unsigned long)0) >> (i + 1); c->tagenb = (c->tagmask << 1) & ~c->tagmask; } c->lenfuzzy = (int)(1 << c->dropbits) - 1; m = (c->tagmask | c->tagenb) << c->tagshift; if (m & (basesize >> c->dropbits)) { fprintf(stderr, "m 0x%lx size 0x%lx\n", m, basesize); exit(1); } } #endif /* DO_TAGGED_HASH */ /* - create and truncate .pag, .idx, or .hash files - return false on error */ static bool create_truncate(const char *name, const char *pag1) { char *fn; FILE *f; fn = concat(name, pag1, (char *) 0); f = Fopen(fn, "w", TEMPORARYOPEN); free(fn); if (f == NULL) { syswarn("unable to create/truncate %s", pag1); return false; } else Fclose(f); return true; } /* dbzfresh - set up a new database, no historical info * Return true for success, false for failure * name - base name; .dir and .pag must exist * size - table size (0 means default) */ bool dbzfresh(const char *name, off_t size) { char *fn; dbzconfig c; FILE *f; #ifdef DO_TAGGED_HASH struct stat sb; of_t m; #endif if (opendb) { warn("dbzfresh: database already open"); return false; } if (size != 0 && size < 2) { warn("dbzfresh: preposterous size (%ld)", (long) size); return false; } /* get default configuration */ if (!getconf(NULL, &c)) return false; /* "can't happen" */ #ifdef DO_TAGGED_HASH /* and mess with it as specified */ if (size != 0) c.tsize = size; m = c.tagmask; c.tagshift = 0; while (!(m & 1)) { m >>= 1; c.tagshift++; } c.tagmask = m; c.tagenb = (m << 1) & ~m; c.dropbits = 0; c.lenfuzzy = 0; /* if big enough basb file exists, update config */ if (stat(name, &sb) != -1) config_by_text_size(&c, sb.st_size); #else /* set the size as specified, make sure we get at least 2 bytes of implicit hash */ if (size != 0) c.tsize = size > (64 * 1024) ? size : 64 * 1024; #endif /* write it out */ fn = concat(name, dir, (char *) 0); f = Fopen(fn, "w", TEMPORARYOPEN); free(fn); if (f == NULL) { syswarn("dbzfresh: unable to write config"); return false; } if (putconf(f, &c) < 0) { Fclose(f); return false; } if (Fclose(f) == EOF) { syswarn("dbzfresh: fclose failure"); return false; } /* create and truncate .pag, or .index/.hash files */ #ifdef DO_TAGGED_HASH if (!create_truncate(name, pag)) return false; #else if (!create_truncate(name, idx)) return false; if (!create_truncate(name, exists)) return false; #endif /* DO_TAGGED_HASH */ /* and punt to dbzinit for the hard work */ return dbzinit(name); } #ifdef DO_TAGGED_HASH /* - isprime - is a number prime? */ static bool isprime(long x) { static int quick[] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 0 }; int *ip; long div1, stop; /* hit the first few primes quickly to eliminate easy ones */ /* this incidentally prevents ridiculously small tables */ for (ip = quick; (div1 = *ip) != 0; ip++) if (x % div1 == 0) { debug("isprime: quick result on %ld", x); return false; } /* approximate square root of x */ for (stop = x; x/stop < stop; stop >>= 1) continue; stop <<= 1; /* try odd numbers up to stop */ for (div1 = *--ip; div1 < stop; div1 += 2) if (x%div1 == 0) return false; return true; } #endif /* * dbzsize - what's a good table size to hold this many entries? * contents - size of table (0 means return the default) */ long dbzsize(off_t contents) { of_t n; if (contents <= 0) { /* foulup or default inquiry */ debug("dbzsize: preposterous input (%ld)", (long) contents); return DEFSIZE; } if ((conf.fillpercent > 0) && (conf.fillpercent < 100)) n = (contents / conf.fillpercent) * 100; else n = (contents * 3) / 2; /* try to keep table at most 2/3's full */ /* Make sure that we get at least 2 bytes of implicit hash */ if (n < (64 * 1024)) n = 64 * 1024; #ifdef DO_TAGGED_HASH if (!(n & 1)) n += 1; /* make it odd */ debug("dbzsize: tentative size %ld", n); while (!isprime(n)) /* look for a prime */ n += 2; #endif debug("dbzsize: final size %ld", (long) n); return n; } /* dbzagain - set up a new database to be a rebuild of an old one * Returns true on success, false on failure * name - base name; .dir and .pag must exist * oldname - basename, all must exist */ bool dbzagain(const char *name, const char *oldname) { char *fn; dbzconfig c; bool result; int i; long top; FILE *f; int newtable; of_t newsize; #ifdef DO_TAGGED_HASH long vtop; struct stat sb; #endif if (opendb) { warn("dbzagain: database already open"); return false; } /* pick up the old configuration */ fn = concat(oldname, dir, (char *) 0); f = Fopen(fn, "r", TEMPORARYOPEN); free(fn); if (f == NULL) { syswarn("dbzagain: cannot open old .dir file"); return false; } result = getconf(f, &c); Fclose(f); if (!result) { syswarn("dbzagain: getconf failed"); return false; } /* tinker with it */ top = 0; newtable = 0; for (i = 0; i < NUSEDS; i++) { if (top < c.used[i]) top = c.used[i]; if (c.used[i] == 0) newtable = 1; /* hasn't got full usage history yet */ } if (top == 0) { debug("dbzagain: old table has no contents!"); newtable = 1; } for (i = NUSEDS-1; i > 0; i--) c.used[i] = c.used[i-1]; c.used[0] = 0; #ifdef DO_TAGGED_HASH vtop = 0; for (i = 0; i < NUSEDS; i++) { if (vtop < c.vused[i]) vtop = c.vused[i]; if (c.vused[i] == 0) newtable = 1; /* hasn't got full usage history yet */ } if (top != 0 && vtop == 0) { debug("dbzagain: old table has no contents!"); newtable = 1; } for (i = NUSEDS-1; i > 0; i--) c.vused[i] = c.vused[i-1]; c.vused[0] = 0; /* calculate tagging from old file */ if (stat(oldname, &sb) != -1 && vtop < sb.st_size) vtop = sb.st_size; config_by_text_size(&c, vtop); #endif newsize = dbzsize(top); if (!newtable || newsize > c.tsize) /* don't shrink new table */ c.tsize = newsize; /* write it out */ fn = concat(name, dir, (char *) 0); f = Fopen(fn, "w", TEMPORARYOPEN); free(fn); if (f == NULL) { syswarn("dbzagain: unable to write new .dir"); return false; } i = putconf(f, &c); Fclose(f); if (i < 0) { warn("dbzagain: putconf failed"); return false; } /* create and truncate .pag, or .index/.hash files */ #ifdef DO_TAGGED_HASH if (!create_truncate(name, pag)) return false; #else if (!create_truncate(name, idx)) return false; if (!create_truncate(name, exists)) return false; #endif /* and let dbzinit do the work */ return dbzinit(name); } static bool openhashtable(const char *base, const char *ext, hash_table *tab, const size_t reclen, const dbz_incore_val incore) { char *name; int oerrno; name = concat(base, ext, (char *) 0); if ((tab->fd = open(name, readonly ? O_RDONLY : O_RDWR)) < 0) { syswarn("openhashtable: could not open raw"); oerrno = errno; free(name); errno = oerrno; return false; } free(name); tab->reclen = reclen; fdflag_close_exec(tab->fd, true); tab->pos = -1; /* get first table into core, if it looks desirable and feasible */ tab->incore = incore; if (tab->incore != INCORE_NO) { if (!getcore(tab)) { syswarn("openhashtable: getcore failure"); oerrno = errno; close(tab->fd); errno = oerrno; return false; } } if (options.nonblock && !fdflag_nonblocking(tab->fd, true)) { syswarn("fcntl: could not set nonblock"); oerrno = errno; close(tab->fd); errno = oerrno; return false; } return true; } static void closehashtable(hash_table *tab) { close(tab->fd); if (tab->incore == INCORE_MEM) free(tab->core); if (tab->incore == INCORE_MMAP) { #if defined(HAVE_MMAP) if (munmap(tab->core, conf.tsize * tab->reclen) == -1) { syswarn("closehashtable: munmap failed"); } #else warn("closehashtable: can't mmap files"); #endif } } #ifdef DO_TAGGED_HASH static bool openbasefile(const char *name) { basef = Fopen(name, "r", DBZ_BASE); if (basef == NULL) { syswarn("dbzinit: basefile open failed"); basefname = xstrdup(name); } else basefname = NULL; if (basef != NULL) fdflag_close_exec(fileno(basef), true); if (basef != NULL) setvbuf(basef, NULL, _IOFBF, 64); return true; } #endif /* DO_TAGGED_HASH */ /* * dbzinit - open a database, creating it (using defaults) if necessary * * We try to leave errno set plausibly, to the extent that underlying * functions permit this, since many people consult it if dbzinit() fails. * return true for success, false for failure */ bool dbzinit(const char *name) { char *fname; if (opendb) { warn("dbzinit: dbzinit already called once"); errno = 0; return false; } /* open the .dir file */ fname = concat(name, dir, (char *) 0); if ((dirf = Fopen(fname, "r+", DBZ_DIR)) == NULL) { dirf = Fopen(fname, "r", DBZ_DIR); readonly = true; } else readonly = false; free(fname); if (dirf == NULL) { syswarn("dbzinit: can't open .dir file"); return false; } fdflag_close_exec(fileno(dirf), true); /* pick up configuration */ if (!getconf(dirf, &conf)) { warn("dbzinit: getconf failure"); Fclose(dirf); errno = EDOM; /* kind of a kludge, but very portable */ return false; } /* open pag or idx/exists file */ #ifdef DO_TAGGED_HASH if (!openhashtable(name, pag, &pagtab, SOF, options.pag_incore)) { Fclose(dirf); return false; } if (!openbasefile(name)) { close(pagtab.fd); Fclose(dirf); return false; } tagbits = conf.tagmask << conf.tagshift; taghere = conf.tagenb << conf.tagshift; tagboth = tagbits | taghere; canttag_warned = 0; #else if (!openhashtable(name, idx, &idxtab, sizeof(of_t), options.pag_incore)) { Fclose(dirf); return false; } if (!openhashtable(name, exists, &etab, sizeof(erec), options.exists_incore)) { Fclose(dirf); return false; } #endif /* misc. setup */ dirty = false; opendb = true; prevp = FRESH; memset(&empty_rec, '\0', sizeof(empty_rec)); debug("dbzinit: succeeded"); return true; } /* dbzclose - close a database */ bool dbzclose(void) { bool ret = true; if (!opendb) { warn("dbzclose: not opened!"); return false; } if (!dbzsync()) ret = false; #ifdef DO_TAGGED_HASH closehashtable(&pagtab); if (Fclose(basef) == EOF) { syswarn("dbzclose: fclose(basef) failed"); ret = false; } if (basefname != NULL) free(basefname); basef = NULL; #else closehashtable(&idxtab); closehashtable(&etab); #endif if (Fclose(dirf) == EOF) { syswarn("dbzclose: fclose(dirf) failed"); ret = false; } debug("dbzclose: %s", (ret == true) ? "succeeded" : "failed"); if (ret) opendb = false; return ret; } /* dbzsync - push all in-core data out to disk */ bool dbzsync(void) { bool ret = true; if (!opendb) { warn("dbzsync: not opened!"); return false; } if (!dirty) return true;; #ifdef DO_TAGGED_HASH if (!putcore(&pagtab)) { #else if (!putcore(&idxtab) || !putcore(&etab)) { #endif warn("dbzsync: putcore failed"); ret = false;; } if (putconf(dirf, &conf) < 0) ret = false; debug("dbzsync: %s", ret ? "succeeded" : "failed"); return ret; } #ifdef DO_TAGGED_HASH /* - okayvalue - check that a value can be stored */ static int okayvalue(of_t value) { if (HASTAG(value)) return(0); if (value == LONG_MAX) /* BIAS() and UNBIAS() will overflow */ return(0); return(1); } #endif /* dbzexists - check if the given message-id is in the database */ bool dbzexists(const HASH key) { #ifdef DO_TAGGED_HASH off_t value; return (dbzfetch(key, &value) != 0); #else if (!opendb) { warn("dbzexists: database not open!"); return false; } prevp = FRESH; start(&srch, key, FRESH); return search(&srch); #endif } /* * dbzfetch - get offset of an entry from the database * * Returns the offset of the text file for input key, * or -1 if NOTFOUND or error occurs. */ bool dbzfetch(const HASH key, off_t *value) { #ifdef DO_TAGGED_HASH #define MAX_NB2RD (DBZMAXKEY + MAXFUZZYLENGTH + 2) #define MIN_KEY_LENGTH 6 /* strlen("<1@a>") + strlen("\t") */ char *bp, buffer[MAX_NB2RD]; int keylen, j, nb2r; HASH hishash; char *keytext = NULL; of_t offset = NOTFOUND; #endif prevp = FRESH; if (!opendb) { warn("dbzfetch: database not open!"); return false; } start(&srch, key, FRESH); #ifdef DO_TAGGED_HASH /* * nb2r: number of bytes to read from history file. * It can be reduced if history text is written as hashes only. */ nb2r = sizeof(buffer) - 1; while ((offset = search(&srch)) != NOTFOUND) { debug("got 0x%lx", offset); /* fetch the key */ offset <<= conf.dropbits; if (offset) /* backspace 1 character to read '\n' */ offset--; if (fseeko(basef, offset, SEEK_SET) != 0) { syswarn("dbzfetch: seek failed"); return false; } keylen = fread(buffer, 1, nb2r, basef); if (keylen < MIN_KEY_LENGTH) { syswarn("dbzfetch: read failed"); return false; } buffer[keylen] = '\0'; /* terminate the string */ if (offset) { /* find the '\n', the previous EOL */ if (keylen > conf.lenfuzzy) keylen = conf.lenfuzzy; /* keylen is fuzzy distance now */ for (j=0,bp=buffer; j conf.vused[0]) conf.vused[0] = value; /* now value is in fuzzy format */ value >>= conf.dropbits; debug("dbzstore: (%ld)", (long) value); if (!okayvalue(value)) { warn("dbzstore: reserved bit or overflow in 0x%lx", value); return DBZSTORE_ERROR; } /* find the place, exploiting previous search if possible */ start(&srch, key, prevp); while (search(&srch) != NOTFOUND) continue; prevp = FRESH; conf.used[0]++; debug("store: used count %ld", conf.used[0]); dirty = 1; if (!set_pag(&srch, value)) return DBZSTORE_ERROR; return DBZSTORE_OK; #else /* DO_TAGGED_HASH */ /* find the place, exploiting previous search if possible */ start(&srch, key, prevp); if (search(&srch) == true) return DBZSTORE_EXISTS; prevp = FRESH; conf.used[0]++; debug("store: used count %ld", conf.used[0]); dirty = true; memcpy(&evalue.hash, &srch.hash, sizeof(evalue.hash) < sizeof(srch.hash) ? sizeof(evalue.hash) : sizeof(srch.hash)); /* Set the value in the index first since we don't care if it's out of date */ if (!set(&srch, &idxtab, (void *)&data)) return DBZSTORE_ERROR; if (!set(&srch, &etab, &evalue)) return DBZSTORE_ERROR; return DBZSTORE_OK; #endif /* DO_TAGGED_HASH */ } /* * getconf - get configuration from .dir file * df - NULL means just give me the default * pf - NULL means don't care about .pag * returns true for success, false for failure */ static bool getconf(FILE *df, dbzconfig *cp) { int i; /* empty file, no configuration known */ #ifdef DO_TAGGED_HASH if (df == NULL) { cp->tsize = DEFSIZE; for (i = 0; i < NUSEDS; i++) cp->used[i] = 0; for (i = 0; i < NUSEDS; i++) cp->vused[i] = 0; cp->valuesize = sizeof(of_t); cp->fillpercent = 50; cp->tagenb = TAGENB; cp->tagmask = TAGMASK; cp->tagshift = TAGSHIFT; cp->dropbits = 0; cp->lenfuzzy = 0; debug("getconf: defaults (%ld, (0x%lx/0x%lx<<%d %d))", cp->tsize, cp->tagenb, cp->tagmask, cp->tagshift, cp->dropbits); return true; } i = fscanf(df, "dbz 6 %ld %d %d %ld %ld %d %d\n", &cp->tsize, &cp->valuesize, &cp->fillpercent, &cp->tagenb, &cp->tagmask, &cp->tagshift, &cp->dropbits); if (i != 7) { warn("dbz: bad first line in .dir history file"); warn("dbz: you should consider running makedbz manually"); return false; } if (cp->valuesize != sizeof(of_t)) { warn("dbz: wrong of_t size (%d)", cp->valuesize); return false; } cp->lenfuzzy = (int)(1 << cp->dropbits) - 1; #else /* DO_TAGGED_HASH */ if (df == NULL) { cp->tsize = DEFSIZE; for (i = 0; i < NUSEDS; i++) cp->used[i] = 0; cp->valuesize = sizeof(of_t) + sizeof(erec); cp->fillpercent = 66; debug("getconf: defaults (%ld)", cp->tsize); return true; } i = fscanf(df, "dbz 6 %ld %d %d\n", &cp->tsize, &cp->valuesize, &cp->fillpercent); if (i != 3) { warn("dbz: bad first line in .dir history file"); return false; } if (cp->valuesize != (sizeof(of_t) + sizeof(erec))) { warn("dbz: wrong of_t size (%d)", cp->valuesize); return false; } #endif /* DO_TAGGED_HASH */ debug("size %ld", cp->tsize); /* second line, the usages */ for (i = 0; i < NUSEDS; i++) if (!fscanf(df, "%ld", &cp->used[i])) { warn("dbz: bad usage value in .dir history file"); return false; } debug("used %ld %ld %ld...", cp->used[0], cp->used[1], cp->used[2]); #ifdef DO_TAGGED_HASH /* third line, the text usages */ for (i = 0; i < NUSEDS; i++) if (!fscanf(df, "%ld", &cp->vused[i])) { warn("dbz: bad text usage value in .dir history file"); return false; } debug("vused %ld %ld %ld...", cp->vused[0], cp->vused[1], cp->vused[2]); #endif /* DO_TAGGED_HASH */ return true; } /* putconf - write configuration to .dir file * Returns: 0 for success, -1 for failure */ static int putconf(FILE *f, dbzconfig *cp) { int i; int ret = 0; if (fseeko(f, 0, SEEK_SET) != 0) { syswarn("dbz: fseeko failure in putconf"); ret = -1; } #ifdef DO_TAGGED_HASH fprintf(f, "dbz %d %ld %d %d %ld %ld %d %d\n", dbzversion, cp->tsize, cp->valuesize, cp->fillpercent, cp->tagenb, cp->tagmask, cp->tagshift, cp->dropbits); #else /* DO_TAGGED_HASH */ fprintf(f, "dbz %d %ld %d %d\n", dbzversion, cp->tsize, cp->valuesize, cp->fillpercent); #endif /* DO_TAGGED_HASH */ for (i = 0; i < NUSEDS; i++) fprintf(f, "%ld%c", cp->used[i], (i < NUSEDS-1) ? ' ' : '\n'); #ifdef DO_TAGGED_HASH for (i = 0; i < NUSEDS; i++) fprintf(f, "%ld%c", cp->vused[i], (i < NUSEDS-1) ? ' ' : '\n'); #endif fflush(f); if (ferror(f)) ret = -1; debug("putconf status %d", ret); return ret; } /* getcore - try to set up an in-core copy of file * * Returns: pointer to copy of file or NULL on errror */ static bool getcore(hash_table *tab) { char *it; ssize_t nread; size_t i; size_t length = conf.tsize * tab->reclen; #ifdef HAVE_MMAP struct stat st; #endif if (tab->incore == INCORE_MMAP) { #if defined(HAVE_MMAP) if (fstat(tab->fd, &st) == -1) { syswarn("dbz: getcore: fstat failed"); return false; } if ((off_t) length > st.st_size) { /* file too small; extend it */ if (ftruncate(tab->fd, length) == -1) { syswarn("dbz: getcore: ftruncate failed"); return false; } } it = mmap(NULL, length, readonly ? PROT_READ : PROT_WRITE | PROT_READ, MAP_SHARED, tab->fd, 0); if (it == (char *)-1) { syswarn("dbz: getcore: mmap failed"); return false; } #ifdef MADV_RANDOM /* not present in all versions of mmap() */ madvise(it, length, MADV_RANDOM); #endif #else warn("dbz: getcore: can't mmap files"); return false; #endif } else { it = xmalloc(length); nread = read(tab->fd, it, length); if (nread < 0) { syswarn("dbz: getcore: read failed"); free(it); return false; } i = length - nread; memset(it + nread, '\0', i); } tab->core = it; return true; } /* putcore - try to rewrite an in-core table * * Returns true on success, false on failure */ static bool putcore(hash_table *tab) { size_t size; ssize_t result; if (tab->incore == INCORE_MEM) { if(options.writethrough) return true; fdflag_nonblocking(tab->fd, false); size = tab->reclen * conf.tsize; result = xpwrite(tab->fd, tab->core, size, 0); if (result < 0 || (size_t) result != size) { fdflag_nonblocking(tab->fd, options.nonblock); return false; } fdflag_nonblocking(tab->fd, options.nonblock); } #ifdef HAVE_MMAP if(tab->incore == INCORE_MMAP) { msync(tab->core, conf.tsize * tab->reclen, MS_ASYNC); } #endif return true; } #ifdef DO_TAGGED_HASH /* - makehash31 : make 31-bit hash from HASH */ static unsigned int makehash31(const HASH *hash) { unsigned int h; memcpy(&h, hash, sizeof(h)); return (h >> 1); } #endif /* start - set up to start or restart a search * osp == NULL is acceptable */ static void start(searcher *sp, const HASH hash, searcher *osp) { #ifdef DO_TAGGED_HASH unsigned int h; h = makehash31(&hash); if (osp != FRESH && osp->shorthash == h) { if (sp != osp) *sp = *osp; sp->run--; debug("search restarted"); } else { sp->shorthash = h; sp->tag = MKTAG(h / conf.tsize); sp->place = h % conf.tsize; debug("hash %8.8lx tag %8.8lx place %ld", sp->shorthash, sp->tag, sp->place); sp->tabno = 0; sp->run = -1; sp->aborted = 0; } #else /* DO_TAGGED_HASH */ int tocopy; if (osp != FRESH && !memcmp(&osp->hash, &hash, sizeof(hash))) { if (sp != osp) *sp = *osp; sp->run--; debug("search restarted"); } else { sp->hash = hash; tocopy = sizeof(hash) < sizeof(sp->shorthash) ? sizeof(hash) : sizeof(sp->shorthash); /* Copy the bottom half of thhe hash into sp->shorthash */ memcpy(&sp->shorthash, (const char *)&hash + (sizeof(hash) - tocopy), tocopy); sp->shorthash >>= 1; sp->tabno = 0; sp->run = -1; sp->aborted = 0; } #endif /* DO_TAGGED_HASH */ } #ifdef DO_TAGGED_HASH /* - search - conduct part of a search */ static of_t /* NOTFOUND if we hit VACANT or error */ search(searcher *sp) { of_t value; unsigned long taboffset = sp->tabno * conf.tsize; if (sp->aborted) return(NOTFOUND); for (;;) { /* go to next location */ if (sp->run++ == MAXRUN) { sp->tabno++; sp->run = 0; taboffset = sp->tabno * conf.tsize; } sp->place = ((sp->shorthash + sp->run) % conf.tsize) + taboffset; debug("search @ %ld", sp->place); /* get the tagged value */ if ((options.pag_incore != INCORE_NO) && (sp->place < conf.tsize)) { debug("search: in core"); value = ((of_t *)pagtab.core)[sp->place]; } else { off_t dest; dest = sp->place * SOF; /* read it */ errno = 0; if (pread(pagtab.fd, &value, sizeof(value), dest) != sizeof(value)) { if (errno != 0) { syswarn("dbz: search: read failed"); pagtab.pos = -1; sp->aborted = 1; return(NOTFOUND); } else value = VACANT; pagtab.pos = -1; } else pagtab.pos += sizeof(value); } /* vacant slot is always cause to return */ if (value == VACANT) { debug("search: empty slot"); return(NOTFOUND); }; /* check the tag */ value = UNBIAS(value); debug("got 0x%lx", value); if (!HASTAG(value)) { debug("tagless"); return(value); } else if (TAG(value) == sp->tag) { debug("match"); return(NOTAG(value)); } else { debug("mismatch 0x%lx", TAG(value)); } } /* NOTREACHED */ } #else /* DO_TAGGED_HASH */ /* search - conduct part of a search * * return false if we hit vacant rec's or error */ static bool search(searcher *sp) { erec value; unsigned long taboffset = 0; if (sp->aborted) return false; for (;;) { /* go to next location */ if (sp->run++ == MAXRUN) { sp->tabno++; sp->run = 0; taboffset = sp->tabno * conf.tsize; } sp->place = ((sp->shorthash + sp->run) % conf.tsize) + taboffset; debug("search @ %ld", (long) sp->place); /* get the value */ if ((options.exists_incore != INCORE_NO) && (sp->place < conf.tsize)) { debug("search: in core"); memcpy(&value, &((erec *)etab.core)[sp->place], sizeof(erec)); } else { off_t dest; dest = sp->place * sizeof(erec); /* read it */ errno = 0; if (pread(etab.fd, &value, sizeof(erec), dest) != sizeof(erec)) { if (errno != 0) { debug("search: read failed"); etab.pos = -1; sp->aborted = 1; return false; } else { memset(&value, '\0', sizeof(erec)); } } /* and finish up */ etab.pos += sizeof(erec); } /* Check for an empty record */ if (!memcmp(&value, &empty_rec, sizeof(erec))) { debug("search: empty slot"); return false; } /* check the value */ debug("got 0x%.*s", DBZ_INTERNAL_HASH_SIZE, value.hash); if (!memcmp(&value.hash, &sp->hash, DBZ_INTERNAL_HASH_SIZE)) { return true; } } /* NOTREACHED */ } #endif /* DO_TAGGED_HASH */ /* set - store a value into a location previously found by search * * Returns: true success, false failure */ static bool set(searcher *sp, hash_table *tab, void *value) { off_t offset; if (sp->aborted) return false; /* If we have the index file in memory, use it */ if ((tab->incore != INCORE_NO) && (sp->place < conf.tsize)) { void *where = (char *)tab->core + (sp->place * tab->reclen); memcpy(where, value, tab->reclen); debug("set: incore"); if (tab->incore == INCORE_MMAP) { if (innconf->nfswriter) { inn_msync_page(where, tab->reclen, MS_ASYNC); } return true; } if (!options.writethrough) return true; } /* seek to spot */ tab->pos = -1; /* invalidate position memory */ offset = sp->place * tab->reclen; /* write in data */ while (pwrite(tab->fd, value, tab->reclen, offset) != tab->reclen) { if (errno == EAGAIN) { fd_set writeset; FD_ZERO(&writeset); FD_SET(tab->fd, &writeset); if (select(tab->fd + 1, NULL, &writeset, NULL, NULL) < 1) { syswarn("dbz: set: select failed"); sp->aborted = 1; return false; } continue; } syswarn("dbz: set: write failed"); sp->aborted = 1; return false; } debug("set: succeeded"); return true; } #ifdef DO_TAGGED_HASH /* - set_pag - store a value into a location previously found by search - on the pag table. - Returns: true success, false failure */ static bool set_pag(searcher *sp, of_t value) { of_t v = value; if (CANTAG(v)) { v |= sp->tag | taghere; if (v != UNBIAS(VACANT)) { /* BIAS(v) won't look VACANT */ if (v != LONG_MAX) { /* and it won't overflow */ value = v; } } } else if (canttag_warned == 0) { fprintf(stderr, "dbz.c(set): can't tag value 0x%lx", v); fprintf(stderr, " tagboth = 0x%lx\n", tagboth); canttag_warned = 1; } debug("tagged value is 0x%lx", value); value = BIAS(value); return set(sp, &pagtab, &value); } #endif /* DO_TAGGED_HASH */ /* dbzsetoptions - set runtime options for the database. */ void dbzsetoptions(const dbzoptions o) { options = o; #ifndef HAVE_MMAP /* Without a working mmap on files, we should avoid it. */ if (options.pag_incore == INCORE_MMAP) options.pag_incore = INCORE_NO; if (options.exists_incore == INCORE_MMAP) options.exists_incore = INCORE_NO; #endif } /* dbzgetoptions - get runtime options for the database. */ void dbzgetoptions(dbzoptions *o) { *o = options; } #ifdef DBZTEST int timediffms(struct timeval start, struct timeval end) { return (((end.tv_sec - start.tv_sec) * 1000) + ((end.tv_usec - start.tv_usec)) / 1000); } void RemoveDBZ(char *filename) { char *fn; #ifdef DO_TAGGED_HASH fn = concat(filename, pag, (char *) 0); unlink(fn); free(fn); #else fn = concat(filename, exists, (char *) 0); unlink(fn); free(fn); fn = concat(filename, idx, (char *) 0); unlink(fn); free(fn); #endif fn = concat(filename, dir, (char *) 0); unlink(fn); free(fn); } static void usage(void) { fprintf(stderr, "usage: dbztest [-i] [-n|m] [-N] [-s size] \n"); #ifdef DO_TAGGED_HASH fprintf(stderr, " -i initialize history. deletes .pag files\n"); #else fprintf(stderr, " -i initialize history. deletes .hash and .index files\n"); #endif fprintf(stderr, " -n or m use INCORE_NO, INCORE_MMAP. default = INCORE_MEM\n"); fprintf(stderr, " -N using nfswriter mode\n"); fprintf(stderr, " -s size number of history lines[2500000]\n"); fprintf(stderr, " history history text file\n"); exit(1); } int main(int argc, char *argv[]) { int i, line; FILE *fpi; char ibuf[2048], *p; HASH key; off_t where; int initialize = 0, size = 2500000; char *history = NULL; dbzoptions opt; dbz_incore_val incore = INCORE_MEM; struct timeval start, end; off_t ivalue; innconf = xcalloc(1, sizeof(struct innconf)); for (i=1; infswriter = true; else if (strcmp(argv[i], "-m") == 0) #if defined(HAVE_MMAP) incore = INCORE_MMAP; #else fprintf (stderr, "can't mmap files\n"); #endif else if (strcmp(argv[i], "-s") == 0) size = atoi(argv[++i]); else if (*argv[i] != '-' && history == NULL) history = argv[i]; else usage(); if (history == NULL) usage(); if ((fpi = fopen(history, "r")) == NULL) { fprintf(stderr, "can't open %s\n", history); usage(); } dbzgetoptions(&opt); opt.pag_incore = incore; dbzsetoptions(opt); if (initialize) { RemoveDBZ(history); gettimeofday(&start, NULL); if (dbzfresh(history, dbzsize(size)) < 0) { fprintf(stderr, "cant dbzfresh %s\n", history); exit(1); } gettimeofday(&end, NULL); printf("dbzfresh: %d msec\n", timediffms(start, end)); } else { gettimeofday(&start, NULL); if (dbzinit(history) < 0) { fprintf(stderr, "cant dbzinit %s\n", history); exit(1); } gettimeofday(&end, NULL); printf("dbzinit: %d msec\n", timediffms(start, end)); } gettimeofday(&start, NULL); where = ftello(fpi); for (line=1; fgets(ibuf, sizeof(ibuf), fpi); line++, where=ftello(fpi)) { if (*ibuf == '<') { if ((p = strchr(ibuf, '\t')) == NULL) { fprintf(stderr, "ignoreing bad line: %s\n", ibuf); continue; } *p = '\0'; key = HashMessageID(ibuf); } else if (*ibuf == '[') key = TextToHash(ibuf+1); else continue; if (initialize) { if (dbzstore(key, where) == DBZSTORE_ERROR) { fprintf(stderr, "cant store %s\n", ibuf); exit(1); } } else { if (!dbzfetch(key, &ivalue)) { fprintf(stderr, "line %d can't fetch %s\n", line, ibuf); exit(1); } } } line--; gettimeofday(&end, NULL); i = timediffms(start, end); printf("%s: %d lines %.3f msec/id\n", (initialize) ? "dbzstore" : "dbzfetch", line, (double)i / (double)line); gettimeofday(&end, NULL); dbzclose(); gettimeofday(&end, NULL); printf("dbzclose: %d msec\n", timediffms(start, end)); return(0); } #endif /* DBZTEST */ inn-2.6.0/lib/makedir.c0000644000175200017520000000205612575023702014251 0ustar iuliusiulius#include "config.h" #include "clibrary.h" #include #include #include "inn/libinn.h" /* ** Try to make one directory. Return false on error. */ static bool MakeDir(char *Name) { struct stat Sb; if (mkdir(Name, GROUPDIR_MODE) >= 0) { return true; } /* See if it failed because it already exists. */ if (stat(Name, &Sb) >= 0 && S_ISDIR(Sb.st_mode)) { errno = 0; return true; } return false; } /* ** Given a directory, comp/foo/bar, create that directory and all ** intermediate directories needed. Return true if ok, else false. */ bool MakeDirectory(char *Name, bool Recurse) { char *p; bool made; /* Optimize common case -- parent almost always exists. */ if (MakeDir(Name)) return true; if (!Recurse) return false; /* Try to make each of comp and comp/foo in turn. */ for (p = (Name[0] == '/') ? &Name[1] : Name; *p; p++) if (*p == '/') { *p = '\0'; made = MakeDir(Name); *p = '/'; if (!made) return false; } return MakeDir(Name); } inn-2.6.0/lib/innconf.c0000644000175200017520000012401712575023702014271 0ustar iuliusiulius/* $Id: innconf.c 9922 2015-07-14 16:43:55Z iulius $ ** ** Manage the global innconf struct. ** ** The functions in this file collapse the parse tree for inn.conf into the ** innconf struct that's used throughout INN. The code to collapse a ** configuration parse tree into a struct is fairly generic and should ** probably be moved into a separate library. ** ** When adding new inn.conf parameters, make sure to add them in all of the ** following places: ** ** * The table in this file ** * include/inn/innconf.h ** * doc/pod/inn.conf.pod (and regenerate doc/man/inn.conf.5) ** * Add the default value to samples/inn.conf.in ** ** Please maintain the current organization of parameters. There are two ** different orders, one of which is a logical order used by the ** documentation, the include file, and the sample file, and the other of ** which is used in this file. The order in this file is documentation of ** where each parameter is used, for later work at breaking up this mess ** of parameters into separate configuration groups for each INN subsystem. */ #include "config.h" #include "clibrary.h" #include #include "inn/confparse.h" #include "inn/innconf.h" #include "inn/messages.h" #include "inn/vector.h" #include "inn/libinn.h" #include "inn/paths.h" /* Instantiation of the global innconf variable. */ struct innconf *innconf = NULL; /* Data types used to express the mappings from the configuration parse into the innconf struct. */ enum type { TYPE_BOOLEAN, TYPE_NUMBER, TYPE_UNUMBER, TYPE_STRING, TYPE_LIST }; struct config { const char *name; size_t location; enum type type; struct { bool boolean; long signed_number; unsigned long unsigned_number; const char *string; const struct vector *list; } defaults; }; /* The following macros are helpers to make it easier to define the table that specifies how to convert the configuration file into a struct. */ #define K(name) (#name), offsetof(struct innconf, name) #define BOOL(def) TYPE_BOOLEAN, { (def), 0, 0, NULL, NULL } #define NUMBER(def) TYPE_NUMBER, { 0, (def), 0, NULL, NULL } #define UNUMBER(def) TYPE_UNUMBER, { 0, 0, (def), NULL, NULL } #define STRING(def) TYPE_STRING, { 0, 0, 0, (def), NULL } #define LIST(def) TYPE_LIST, { 0, 0, 0, NULL, (def) } /* Accessor macros to get a pointer to a value inside a struct. */ #define CONF_BOOL(conf, offset) (bool *) (void *)((char *) (conf) + (offset)) #define CONF_NUMBER(conf, offset) (long *) (void *)((char *) (conf) + (offset)) #define CONF_UNUMBER(conf, offset) (unsigned long *) (void *)((char *) (conf) + (offset)) #define CONF_STRING(conf, offset) (char **) (void *)((char *) (conf) + (offset)) #define CONF_LIST(conf, offset) (struct vector **)(void *)((char *) (conf) + (offset)) /* Special notes: checkincludedtext and localmaxartsize are used by both nnrpd and inews, but inews should probably just let nnrpd do that checking. organization is used by both nnrpd and inews. Perhaps inews should just let nnrpd set it. mergetogroups is currently used by nnrpd for permission checking on posting. I think the check should always be performed based on the newsgroup to which the user is actually posting, and nnrpd should let innd do the merging. useoverchan is only used in innd and overchan. It should probably be something the storage system knows. Ideally, the storage system would handle overchan itself, but that would require a lot of infrastructure; in the interim, it could be something that programs could ask the overview subsystem about. doinnwatch and docnfsstat are used by rc.news currently, but really should be grouped with the appropriate subsystem. timer is currently used in various places, but it may be best to replace it with individual settings for innd and innfeed, and any other code that wants to use it. maxforks is used by rnews and nnrpd. I'm not sure this is that useful of a setting to have. */ const struct config config_table[] = { { K(domain), STRING (NULL) }, { K(enableoverview), BOOL (true) }, { K(extraoverviewadvertised), LIST (NULL) }, { K(extraoverviewhidden), LIST (NULL) }, { K(fromhost), STRING (NULL) }, { K(groupbaseexpiry), BOOL (true) }, { K(mailcmd), STRING (NULL) }, { K(maxforks), UNUMBER (10) }, { K(mta), STRING (NULL) }, { K(nicekids), NUMBER (4) }, { K(ovmethod), STRING (NULL) }, { K(pathhost), STRING (NULL) }, { K(rlimitnofile), NUMBER (-1) }, { K(server), STRING (NULL) }, { K(sourceaddress), STRING (NULL) }, { K(sourceaddress6), STRING (NULL) }, { K(timer), UNUMBER (600) }, { K(runasuser), STRING (RUNASUSER) }, { K(runasgroup), STRING (RUNASGROUP) }, { K(patharchive), STRING (NULL) }, { K(patharticles), STRING (NULL) }, { K(pathbin), STRING (NULL) }, { K(pathcontrol), STRING (NULL) }, { K(pathdb), STRING (NULL) }, { K(pathetc), STRING (NULL) }, { K(pathfilter), STRING (NULL) }, { K(pathhttp), STRING (NULL) }, { K(pathincoming), STRING (NULL) }, { K(pathlog), STRING (NULL) }, { K(pathnews), STRING (NULL) }, { K(pathoutgoing), STRING (NULL) }, { K(pathoverview), STRING (NULL) }, { K(pathrun), STRING (NULL) }, { K(pathspool), STRING (NULL) }, { K(pathtmp), STRING (NULL) }, /* The following settings are specific to innd. */ { K(artcutoff), UNUMBER (10) }, { K(badiocount), UNUMBER (5) }, { K(bindaddress), STRING (NULL) }, { K(bindaddress6), STRING (NULL) }, { K(blockbackoff), UNUMBER (120) }, { K(chaninacttime), UNUMBER (600) }, { K(chanretrytime), UNUMBER (300) }, { K(datamovethreshold), UNUMBER (16384) }, { K(dontrejectfiltered), BOOL (false) }, { K(hiscachesize), UNUMBER (256) }, { K(htmlstatus), BOOL (true) }, { K(icdsynccount), UNUMBER (10) }, { K(ignorenewsgroups), BOOL (false) }, { K(incominglogfrequency), UNUMBER (200) }, { K(linecountfuzz), UNUMBER (0) }, { K(logartsize), BOOL (true) }, { K(logcancelcomm), BOOL (false) }, { K(logipaddr), BOOL (true) }, { K(logsitename), BOOL (true) }, { K(logstatus), BOOL (true) }, { K(logtrash), BOOL (true) }, { K(maxartsize), UNUMBER (1000000) }, { K(maxconnections), UNUMBER (50) }, { K(mergetogroups), BOOL (false) }, { K(nntplinklog), BOOL (false) }, { K(noreader), BOOL (false) }, { K(pathalias), STRING (NULL) }, { K(pathcluster), STRING (NULL) }, { K(pauseretrytime), UNUMBER (300) }, { K(peertimeout), UNUMBER (3600) }, { K(port), UNUMBER (119) }, { K(readerswhenstopped), BOOL (false) }, { K(refusecybercancels), BOOL (false) }, { K(remembertrash), BOOL (true) }, { K(stathist), STRING (NULL) }, { K(status), UNUMBER (600) }, { K(verifycancels), BOOL (false) }, { K(verifygroups), BOOL (false) }, { K(wanttrash), BOOL (false) }, { K(wipcheck), UNUMBER (5) }, { K(wipexpire), UNUMBER (10) }, { K(xrefslave), BOOL (false) }, /* The following settings are specific to nnrpd. */ { K(addinjectiondate), BOOL (true) }, { K(addinjectionpostingaccount), BOOL (false) }, { K(addinjectionpostinghost), BOOL (true) }, { K(allownewnews), BOOL (true) }, { K(backoffauth), BOOL (false) }, { K(backoffdb), STRING (NULL) }, { K(backoffk), UNUMBER (1) }, { K(backoffpostfast), UNUMBER (0) }, { K(backoffpostslow), UNUMBER (1) }, { K(backofftrigger), UNUMBER (10000) }, { K(checkincludedtext), BOOL (false) }, { K(clienttimeout), UNUMBER (1800) }, { K(complaints), STRING (NULL) }, { K(initialtimeout), UNUMBER (10) }, { K(keyartlimit), UNUMBER (100000) }, { K(keylimit), UNUMBER (512) }, { K(keymaxwords), UNUMBER (250) }, { K(keywords), BOOL (false) }, { K(localmaxartsize), UNUMBER (1000000) }, { K(maxcmdreadsize), UNUMBER (BUFSIZ) }, { K(msgidcachesize), UNUMBER (64000) }, { K(moderatormailer), STRING (NULL) }, { K(nfsreader), BOOL (false) }, { K(nfsreaderdelay), UNUMBER (60) }, { K(nicenewnews), UNUMBER (0) }, { K(nicennrpd), UNUMBER (0) }, { K(nnrpdflags), STRING ("") }, { K(nnrpdauthsender), BOOL (false) }, { K(nnrpdloadlimit), UNUMBER (16) }, { K(nnrpdoverstats), BOOL (true) }, { K(organization), STRING (NULL) }, { K(readertrack), BOOL (false) }, { K(spoolfirst), BOOL (false) }, { K(strippostcc), BOOL (false) }, #ifdef HAVE_OPENSSL { K(tlscafile), STRING ("") }, { K(tlscapath), STRING (NULL) }, { K(tlscertfile), STRING (NULL) }, { K(tlskeyfile), STRING (NULL) }, { K(tlsciphers), STRING (NULL) }, { K(tlscompression), BOOL (true) }, { K(tlseccurve), STRING (NULL) }, { K(tlspreferserverciphers), BOOL (true) }, { K(tlsprotocols), LIST (NULL) }, #endif /* HAVE_OPENSSL */ /* The following settings are used by nnrpd and rnews. */ { K(nnrpdposthost), STRING (NULL) }, { K(nnrpdpostport), UNUMBER (119) }, /* The following settings are specific to the storage subsystem. */ { K(articlemmap), BOOL (true) }, { K(cnfscheckfudgesize), UNUMBER (0) }, { K(immediatecancel), BOOL (false) }, { K(keepmmappedthreshold), UNUMBER (1024) }, { K(nfswriter), BOOL (false) }, { K(nnrpdcheckart), BOOL (true) }, { K(overcachesize), UNUMBER (128) }, { K(ovgrouppat), STRING (NULL) }, { K(storeonxref), BOOL (true) }, { K(tradindexedmmap), BOOL (true) }, { K(useoverchan), BOOL (false) }, { K(wireformat), BOOL (true) }, /* The following settings are specific to the history subsystem. */ { K(hismethod), STRING (NULL) }, /* The following settings are specific to rc.news. */ { K(docnfsstat), BOOL (false) }, { K(innflags), STRING (NULL) }, { K(pgpverify), BOOL (false) }, /* The following settings are specific to innwatch. */ { K(doinnwatch), BOOL (true) }, { K(innwatchbatchspace), UNUMBER (4000) }, { K(innwatchlibspace), UNUMBER (25000) }, { K(innwatchloload), UNUMBER (1000) }, { K(innwatchhiload), UNUMBER (2000) }, { K(innwatchpauseload), UNUMBER (1500) }, { K(innwatchsleeptime), UNUMBER (600) }, { K(innwatchspoolnodes), UNUMBER (200) }, { K(innwatchspoolspace), UNUMBER (25000) }, /* The following settings are specific to scanlogs. * Keep a low value by default as it has a privacy impact; three days * should be enough to diagnose the most common cases of malfunction * and abuse. Where more is needed, it is easy for the news admin * to increase it. */ { K(logcycles), UNUMBER (3) }, }; /* ** Set some defaults that cannot be included in the table because they depend ** on other elements or require function calls to set. Called after the ** configuration file is read, so any that have to override what's read have ** to free whatever values are set by the file. */ static void innconf_set_defaults(void) { char *value; /* Some environment variables override settings in inn.conf. */ value = getenv("FROMHOST"); if (value != NULL) { if (innconf->fromhost != NULL) free(innconf->fromhost); innconf->fromhost = xstrdup(value); } value = getenv("NNTPSERVER"); if (value != NULL) { if (innconf->server != NULL) free(innconf->server); innconf->server = xstrdup(value); } value = getenv("ORGANIZATION"); if (value != NULL) { if (innconf->organization != NULL) free(innconf->organization); innconf->organization = xstrdup(value); } value = getenv("INND_BIND_ADDRESS"); if (value != NULL) { if (innconf->bindaddress != NULL) free(innconf->bindaddress); innconf->bindaddress = xstrdup(value); } value = getenv("INND_BIND_ADDRESS6"); if (value != NULL) { if (innconf->bindaddress6 != NULL) free(innconf->bindaddress6); innconf->bindaddress6 = xstrdup(value); } /* Some parameters have defaults that depend on other parameters. */ if (innconf->fromhost == NULL) innconf->fromhost = xstrdup(GetFQDN(innconf->domain)); if (innconf->pathhost == NULL) innconf->pathhost = xstrdup(GetFQDN(innconf->domain)); if (innconf->pathtmp == NULL) innconf->pathtmp = xstrdup(INN_PATH_TMP); /* All of the paths are relative to other paths if not set except for pathnews, which is required to be set by innconf_validate. */ if (innconf->pathbin == NULL) innconf->pathbin = concatpath(innconf->pathnews, "bin"); if (innconf->pathcontrol == NULL) innconf->pathcontrol = concatpath(innconf->pathbin, "control"); if (innconf->pathfilter == NULL) innconf->pathfilter = concatpath(innconf->pathbin, "filter"); if (innconf->pathdb == NULL) innconf->pathdb = concatpath(innconf->pathnews, "db"); if (innconf->pathetc == NULL) innconf->pathetc = concatpath(innconf->pathnews, "etc"); if (innconf->pathrun == NULL) innconf->pathrun = concatpath(innconf->pathnews, "run"); if (innconf->pathlog == NULL) innconf->pathlog = concatpath(innconf->pathnews, "log"); if (innconf->pathhttp == NULL) innconf->pathhttp = concatpath(innconf->pathnews, "http"); if (innconf->pathspool == NULL) innconf->pathspool = concatpath(innconf->pathnews, "spool"); if (innconf->patharticles == NULL) innconf->patharticles = concatpath(innconf->pathspool, "articles"); if (innconf->pathoverview == NULL) innconf->pathoverview = concatpath(innconf->pathspool, "overview"); if (innconf->pathoutgoing == NULL) innconf->pathoutgoing = concatpath(innconf->pathspool, "outgoing"); if (innconf->pathincoming == NULL) innconf->pathincoming = concatpath(innconf->pathspool, "incoming"); if (innconf->patharchive == NULL) innconf->patharchive = concatpath(innconf->pathspool, "archive"); /* One other parameter depends on pathbin. */ if (innconf->mailcmd == NULL) innconf->mailcmd = concatpath(innconf->pathbin, "innmail"); /* Create empty vectors of extra overview fields if they haven't already * been created. */ if (innconf->extraoverviewadvertised == NULL) innconf->extraoverviewadvertised = vector_new(); if (innconf->extraoverviewhidden == NULL) innconf->extraoverviewhidden = vector_new(); /* Defaults used only if TLS (SSL) is supported. */ #ifdef HAVE_OPENSSL if (innconf->tlscapath == NULL) innconf->tlscapath = xstrdup(innconf->pathetc); if (innconf->tlscertfile == NULL) innconf->tlscertfile = concatpath(innconf->pathetc, "cert.pem"); if (innconf->tlskeyfile == NULL) innconf->tlskeyfile = concatpath(innconf->pathetc, "key.pem"); #endif } /* ** Given a config_group struct representing the inn.conf file, parse that ** into an innconf struct and return the newly allocated struct. This ** routine should be pulled out into a library for smashing configuration ** file parse results into structs. */ static struct innconf * innconf_parse(struct config_group *group) { unsigned int i, j; bool *bool_ptr; long *signed_number_ptr; unsigned long *unsigned_number_ptr; const char *char_ptr; char **string; const struct vector *vector_ptr; struct vector **list; struct innconf *config; config = xmalloc(sizeof(struct innconf)); memset(config, 0, sizeof(struct innconf)); for (i = 0; i < ARRAY_SIZE(config_table); i++) switch (config_table[i].type) { case TYPE_BOOLEAN: bool_ptr = CONF_BOOL(config, config_table[i].location); if (!config_param_boolean(group, config_table[i].name, bool_ptr)) *bool_ptr = config_table[i].defaults.boolean; break; case TYPE_NUMBER: signed_number_ptr = CONF_NUMBER(config, config_table[i].location); if (!config_param_signed_number(group, config_table[i].name, signed_number_ptr)) *signed_number_ptr = config_table[i].defaults.signed_number; break; case TYPE_UNUMBER: unsigned_number_ptr = CONF_UNUMBER(config, config_table[i].location); if (!config_param_unsigned_number(group, config_table[i].name, unsigned_number_ptr)) *unsigned_number_ptr = config_table[i].defaults.unsigned_number; break; case TYPE_STRING: if (!config_param_string(group, config_table[i].name, &char_ptr)) char_ptr = config_table[i].defaults.string; string = CONF_STRING(config, config_table[i].location); *string = (char_ptr == NULL) ? NULL : xstrdup(char_ptr); break; case TYPE_LIST: /* vector_ptr contains the value taken from inn.conf or the * default value from config_table; *list points to the inn.conf * structure in memory for this parameter. * We have to do a deep copy of vector_ptr because, like char_ptr, * it is freed by config_free() called by other parts of INN. */ if (!config_param_list(group, config_table[i].name, &vector_ptr)) vector_ptr = config_table[i].defaults.list; list = CONF_LIST(config, config_table[i].location); *list = vector_new(); if (vector_ptr != NULL && vector_ptr->strings != NULL) { vector_resize(*list, vector_ptr->count); for (j = 0; j < vector_ptr->count; j++) { if (vector_ptr->strings[j] != NULL) { vector_add(*list, vector_ptr->strings[j]); } } } break; default: die("internal error: invalid type in row %u of config table", i); break; } return config; } /* ** Check the configuration file for consistency and ensure that mandatory ** settings are present. Returns true if the file is okay and false ** otherwise. */ static bool innconf_validate(struct config_group *group) { bool okay = true; if (GetFQDN(innconf->domain) == NULL) { warn("hostname does not resolve or domain not set in inn.conf"); okay = false; } if (innconf->mta == NULL) { warn("must set mta in inn.conf"); okay = false; } if (innconf->pathnews == NULL) { warn("must set pathnews in inn.conf"); okay = false; } if (innconf->hismethod == NULL) { warn("must set hismethod in inn.conf"); okay = false; } if (innconf->enableoverview && innconf->ovmethod == NULL) { warn("ovmethod must be set in inn.conf if enableoverview is true"); okay = false; } if (innconf->datamovethreshold > 1024 * 1024) { config_error_param(group, "datamovethreshold", "maximum value for datamovethreshold is 1MB"); innconf->datamovethreshold = 1024 * 1024; } if (innconf->keywords) { bool found = false; unsigned int i; if (innconf->extraoverviewadvertised->strings != NULL) { for (i = 0; i < innconf->extraoverviewadvertised->count; i++) { if (innconf->extraoverviewadvertised->strings[i] != NULL && (strcasecmp(innconf->extraoverviewadvertised->strings[i], "Keywords") == 0)) { found = true; break; } } } if (innconf->extraoverviewhidden->strings != NULL) { for (i = 0; i < innconf->extraoverviewhidden->count; i++) { if (innconf->extraoverviewhidden->strings[i] != NULL && (strcasecmp(innconf->extraoverviewhidden->strings[i], "Keywords") == 0)) { found = true; break; } } } if (!found) { config_error_param(group, "keywords", "keyword generation is useless if the Keywords:" " header is not stored in the overview"); innconf->keywords = false; } } return okay; } /* ** Read in inn.conf. Takes a single argument, which is either NULL to read ** the default configuration file or a path to an alternate configuration ** file to read. Returns true if the file was read successfully and false ** otherwise. */ bool innconf_read(const char *path) { struct config_group *group; char *tmpdir; if (innconf != NULL) innconf_free(innconf); if (path == NULL) path = getenv("INNCONF"); group = config_parse_file(path == NULL ? INN_PATH_CONFIG : path); if (group == NULL) return false; innconf = innconf_parse(group); if (!innconf_validate(group)) return false; config_free(group); innconf_set_defaults(); /* It's not clear that this belongs here, but it was done by the old configuration parser, so this is a convenient place to do it. */ tmpdir = getenv("TMPDIR"); if (tmpdir == NULL || strcmp(tmpdir, innconf->pathtmp) != 0) if (setenv("TMPDIR", innconf->pathtmp, true) != 0) { warn("cannot set TMPDIR in the environment"); return false; } return true; } /* ** Check an inn.conf file. This involves reading it in and then additionally ** making sure that there are no keys defined in the inn.conf file that ** aren't recognized. This doesn't have to be very fast (and isn't). ** Returns true if everything checks out successfully, and false otherwise. ** ** A lot of code is duplicated with innconf_read here and should be ** refactored. */ bool innconf_check(const char *path) { struct config_group *group; struct vector *params; size_t set, known; bool found; bool okay = true; if (innconf != NULL) innconf_free(innconf); if (path == NULL) path = getenv("INNCONF"); group = config_parse_file(path == NULL ? INN_PATH_CONFIG : path); if (group == NULL) return false; innconf = innconf_parse(group); if (!innconf_validate(group)) return false; /* Now, do the work that innconf_read doesn't do. Get a list of parameters defined in innconf and then walk our list of valid parameters and see if there are any set that we don't recognize. */ params = config_params(group); for (set = 0; set < params->count; set++) { found = false; for (known = 0; known < ARRAY_SIZE(config_table); known++) if (strcmp(params->strings[set], config_table[known].name) == 0) found = true; if (!found) { config_error_param(group, params->strings[set], "unknown parameter %s", params->strings[set]); okay = false; } } /* Check and warn about a few other parameters. */ if (innconf->peertimeout < 3 * 60) config_error_param(group, "peertimeout", "warning: NNTP RFC 3977 states inactivity" " timeouts MUST be at least three minutes"); if (innconf->clienttimeout < 3 * 60) config_error_param(group, "clienttimeout", "warning: NNTP RFC 3977 states inactivity" " timeouts MUST be at least three minutes"); /* All done. Free the parse tree and return. */ config_free(group); return okay; } /* ** Free innconf, requiring some complexity since all strings stored in the ** innconf struct are allocated memory. This routine is mostly generic to ** any struct smashed down from a configuration file parse. */ void innconf_free(struct innconf *config) { unsigned int i; char *p; struct vector *q; for (i = 0; i < ARRAY_SIZE(config_table); i++) { if (config_table[i].type == TYPE_STRING) { p = *CONF_STRING(config, config_table[i].location); if (p != NULL) free(p); } if (config_table[i].type == TYPE_LIST) { q = *CONF_LIST(config, config_table[i].location); if (q != NULL) vector_free(q); } } free(config); } /* ** Print a single boolean value with appropriate quoting. */ static void print_boolean(FILE *file, const char *key, bool value, enum innconf_quoting quoting) { char *upper, *p; switch (quoting) { case INNCONF_QUOTE_NONE: fprintf(file, "%s\n", value ? "true" : "false"); break; case INNCONF_QUOTE_SHELL: upper = xstrdup(key); for (p = upper; *p != '\0'; p++) *p = toupper((unsigned char) *p); fprintf(file, "%s=%s; export %s;\n", upper, value ? "true" : "false", upper); free(upper); break; case INNCONF_QUOTE_PERL: fprintf(file, "$%s = '%s';\n", key, value ? "true" : "false"); break; case INNCONF_QUOTE_TCL: fprintf(file, "set inn_%s \"%s\"\n", key, value ? "true" : "false"); break; } } /* ** Print a single signed integer value with appropriate quoting. */ static void print_signed_number(FILE *file, const char *key, long value, enum innconf_quoting quoting) { char *upper, *p; switch (quoting) { case INNCONF_QUOTE_NONE: fprintf(file, "%ld\n", value); break; case INNCONF_QUOTE_SHELL: upper = xstrdup(key); for (p = upper; *p != '\0'; p++) *p = toupper((unsigned char) *p); fprintf(file, "%s=%ld; export %s;\n", upper, value, upper); free(upper); break; case INNCONF_QUOTE_PERL: fprintf(file, "$%s = %ld;\n", key, value); break; case INNCONF_QUOTE_TCL: fprintf(file, "set inn_%s %ld\n", key, value); break; } } /* ** Print a single unsigned integer value with appropriate quoting. */ static void print_unsigned_number(FILE *file, const char *key, unsigned long value, enum innconf_quoting quoting) { char *upper, *p; switch (quoting) { case INNCONF_QUOTE_NONE: fprintf(file, "%lu\n", value); break; case INNCONF_QUOTE_SHELL: upper = xstrdup(key); for (p = upper; *p != '\0'; p++) *p = toupper((unsigned char) *p); fprintf(file, "%s=%lu; export %s;\n", upper, value, upper); free(upper); break; case INNCONF_QUOTE_PERL: fprintf(file, "$%s = %lu;\n", key, value); break; case INNCONF_QUOTE_TCL: fprintf(file, "set inn_%s %lu\n", key, value); break; } } /* ** Print a single string value with appropriate quoting. */ static void print_string(FILE *file, const char *key, const char *value, enum innconf_quoting quoting) { char *upper, *p; const char *letter; static const char tcl_unsafe[] = "$[]{}\"\\"; switch (quoting) { case INNCONF_QUOTE_NONE: /* Do not output NULL values. They are not empty strings. */ if (value == NULL) { return; } fprintf(file, "%s\n", value); break; case INNCONF_QUOTE_SHELL: /* Do not output NULL values. They are not empty strings. */ if (value == NULL) { return; } upper = xstrdup(key); for (p = upper; *p != '\0'; p++) *p = toupper((unsigned char) *p); fprintf(file, "%s='", upper); for (letter = value; letter != NULL && *letter != '\0'; letter++) { if (*letter == '\'') fputs("'\\''", file); else if (*letter == '\\') fputs("\\\\", file); else fputc(*letter, file); } fprintf(file, "'; export %s;\n", upper); free(upper); break; case INNCONF_QUOTE_PERL: if (value == NULL) { fprintf(file, "$%s = undef;\n", key); return; } fprintf(file, "$%s = '", key); for (letter = value; letter != NULL && *letter != '\0'; letter++) { if (*letter == '\'' || *letter == '\\') fputc('\\', file); fputc(*letter, file); } fputs("';\n", file); break; case INNCONF_QUOTE_TCL: /* Do not output NULL values. They are not empty strings. */ if (value == NULL) { return; } fprintf(file, "set inn_%s \"", key); for (letter = value; letter != NULL && *letter != '\0'; letter++) { if (strchr(tcl_unsafe, *letter) != NULL) fputc('\\', file); fputc(*letter, file); } fputs("\"\n", file); break; } } /* ** Print a single list value with appropriate quoting. */ static void print_list(FILE *file, const char *key, const struct vector *value, enum innconf_quoting quoting) { char *upper, *p; const char *letter; unsigned int i; static const char tcl_unsafe[] = "$[]{}\"\\"; switch (quoting) { case INNCONF_QUOTE_NONE: /* Do not output NULL values. They are not empty lists. */ if (value == NULL || value->strings == NULL) { return; } fprintf(file, "[ "); if (value != NULL && value->strings != NULL) { for (i = 0; i < value->count; i++) { /* No separation between strings. */ fprintf(file, "%s ", value->strings[i] != NULL ? value->strings[i] : ""); } } fprintf(file, "]\n"); break; case INNCONF_QUOTE_SHELL: /* Do not output NULL values. They are not empty lists. */ if (value == NULL || value->strings == NULL) { return; } upper = xstrdup(key); for (p = upper; *p != '\0'; p++) *p = toupper((unsigned char) *p); /* For interoperability reasons, we return a space-separated string * representing an array (pure Bourne shell does not have the notion * of an array for instance). */ fprintf(file, "%s='", upper); if (value != NULL && value->strings != NULL) { for (i = 0; i < value->count; i++) { fprintf(file, "\""); for (letter = value->strings[i]; letter != NULL && *letter != '\0'; letter++) { if (*letter == '\'') fputs("'\\''", file); else if (*letter == '"') fputs("\\\"", file); else if (*letter == '\\') fputs("\\\\", file); else fputc(*letter, file); } if (i == value->count - 1) { fprintf(file, "\""); } else { fprintf(file, "\" "); } } } fprintf(file, "'; export %s;\n", upper); free(upper); break; case INNCONF_QUOTE_PERL: /* Consider that an empty list is undefined. */ if (value == NULL || value->strings == NULL) { fprintf(file, "@%s = undef;\n", key); return; } fprintf(file, "@%s = ( ", key); if (value != NULL && value->strings != NULL) { for (i = 0; i < value->count; i++) { fprintf(file, "'"); for (letter = value->strings[i]; letter != NULL && *letter != '\0'; letter++) { if (*letter == '\'' || *letter == '\\') fputc('\\', file); fputc(*letter, file); } if (i == value->count - 1) { fprintf(file, "' "); } else { fprintf(file, "', "); } } } fprintf(file, ");\n"); break; case INNCONF_QUOTE_TCL: /* Do not output NULL values. They are not empty lists. */ if (value == NULL || value->strings == NULL) { return; } fprintf(file, "set inn_%s { ", key); if (value != NULL && value->strings != NULL) { for (i = 0; i < value->count; i++) { fprintf(file, "\""); for (letter = value->strings[i]; letter != NULL && *letter != '\0'; letter++) { if (strchr(tcl_unsafe, *letter) != NULL) fputc('\\', file); fputc(*letter, file); } fprintf(file, "\" "); } } fprintf(file, "}\n"); break; } } /* ** Print a single parameter to the given file. Take an index into the table ** specifying the attribute to print and the quoting. */ static void print_parameter(FILE *file, size_t i, enum innconf_quoting quoting) { bool bool_val; long signed_number_val; unsigned long unsigned_number_val; const char *string_val; const struct vector *list_val; switch (config_table[i].type) { case TYPE_BOOLEAN: bool_val = *CONF_BOOL(innconf, config_table[i].location); print_boolean(file, config_table[i].name, bool_val, quoting); break; case TYPE_NUMBER: signed_number_val = *CONF_NUMBER(innconf, config_table[i].location); print_signed_number(file, config_table[i].name, signed_number_val, quoting); break; case TYPE_UNUMBER: unsigned_number_val = *CONF_UNUMBER(innconf, config_table[i].location); print_unsigned_number(file, config_table[i].name, unsigned_number_val, quoting); break; case TYPE_STRING: string_val = *CONF_STRING(innconf, config_table[i].location); print_string(file, config_table[i].name, string_val, quoting); break; case TYPE_LIST: list_val = *CONF_LIST(innconf, config_table[i].location); print_list(file, config_table[i].name, list_val, quoting); break; default: die("internal error: invalid type in row %lu of config table", (unsigned long) i); break; } } /* ** Given a single parameter, find it in the table and print out its value. */ bool innconf_print_value(FILE *file, const char *key, enum innconf_quoting quoting) { size_t i; for (i = 0; i < ARRAY_SIZE(config_table); i++) if (strcmp(key, config_table[i].name) == 0) { print_parameter(file, i, quoting); return true; } return false; } /* ** Dump the entire inn.conf configuration with appropriate quoting. */ void innconf_dump(FILE *file, enum innconf_quoting quoting) { size_t i; for (i = 0; i < ARRAY_SIZE(config_table); i++) print_parameter(file, i, quoting); } /* ** Compare two innconf structs to see if they represent identical ** configurations. This routine is mostly used for testing. Prints warnings ** about where the two configurations differ and return false if they differ, ** true if they match. This too should be moved into a config smashing ** library. */ bool innconf_compare(struct innconf *conf1, struct innconf *conf2) { unsigned int i, j; bool bool1, bool2; long signed_number1, signed_number2; unsigned long unsigned_number1, unsigned_number2; const char *string1, *string2; const struct vector *list1, *list2; bool okay = true; for (i = 0; i < ARRAY_SIZE(config_table); i++) switch (config_table[i].type) { case TYPE_BOOLEAN: bool1 = *CONF_BOOL(conf1, config_table[i].location); bool2 = *CONF_BOOL(conf2, config_table[i].location); if (bool1 != bool2) { warn("boolean variable %s differs: %d != %d", config_table[i].name, bool1, bool2); okay = false; } break; case TYPE_NUMBER: signed_number1 = *CONF_NUMBER(conf1, config_table[i].location); signed_number2 = *CONF_NUMBER(conf2, config_table[i].location); if (signed_number1 != signed_number2) { warn("integer variable %s differs: %ld != %ld", config_table[i].name, signed_number1, signed_number2); okay = false; } break; case TYPE_UNUMBER: unsigned_number1 = *CONF_UNUMBER(conf1, config_table[i].location); unsigned_number2 = *CONF_UNUMBER(conf2, config_table[i].location); if (unsigned_number1 != unsigned_number2) { warn("integer variable %s differs: %lu != %lu", config_table[i].name, unsigned_number1, unsigned_number2); okay = false; } break; case TYPE_STRING: string1 = *CONF_STRING(conf1, config_table[i].location); string2 = *CONF_STRING(conf2, config_table[i].location); if (string1 == NULL && string2 != NULL) { warn("string variable %s differs: NULL != %s", config_table[i].name, string2); okay = false; } else if (string1 != NULL && string2 == NULL) { warn("string variable %s differs: %s != NULL", config_table[i].name, string1); okay = false; } else if (string1 != NULL && string2 != NULL) { if (strcmp(string1, string2) != 0) { warn("string variable %s differs: %s != %s", config_table[i].name, string1, string2); okay = false; } } break; case TYPE_LIST: list1 = *CONF_LIST(conf1, config_table[i].location); list2 = *CONF_LIST(conf2, config_table[i].location); /* Vectors are not resized when created in inn.conf. * Therefore, we can compare their length. */ if ( (list1 == NULL && list2 != NULL) || (list1 != NULL && list2 == NULL)) { warn("list variable %s differs: one is NULL", config_table[i].name); okay = false; } else if (list1 != NULL && list2 != NULL) { if ( (list1->strings == NULL && list2->strings != NULL) || (list1->strings != NULL && list2->strings == NULL)) { warn("list strings variable %s differs: one is NULL", config_table[i].name); okay = false; } else if (list1->strings != NULL && list2->strings != NULL) { if (list1->count != list2->count) { warn("list variable %s differs in length: %lu != %lu", config_table[i].name, (unsigned long) list1->count, (unsigned long) list2->count); okay = false; } else { for (j = 0; j < list1->count; j++) { if (list1->strings[j] == NULL && list2->strings[j] != NULL) { warn("list variable %s differs: NULL != %s", config_table[i].name, list2->strings[j]); okay = false; break; } else if (list1->strings[j] != NULL && list2->strings[j] == NULL) { warn("list variable %s differs: %s != NULL", config_table[i].name, list1->strings[j]); okay = false; break; } else if (list1->strings[j] != NULL && list2->strings[j] != NULL) { if (strcmp(list1->strings[j], list2->strings[j]) != 0) { warn("list variable %s differs at element %u: %s != %s", config_table[i].name, j+1, list1->strings[j], list2->strings[j]); okay = false; break; } } } } } } break; default: die("internal error: invalid type in row %d of config table", i); break; } return okay; } inn-2.6.0/lib/xsignal.c0000644000175200017520000001004512575023702014277 0ustar iuliusiulius/* $Id: xsignal.c 9905 2015-06-20 20:51:22Z iulius $ ** ** A reliable implementation of signal for System V systems. ** ** Two functions are provided, xsignal and xsignal_norestart. The former ** attempts to set system calls to be restarted and the latter does not. ** ** Be aware that there's weird declaration stuff going on here; a signal ** handler is a pointer to a function taking an int and returning void. ** We typedef this as sig_handler_type for clearer code. */ #include "config.h" #include "inn/libinn.h" #include #include typedef void (*sig_handler_type)(int); #ifdef HAVE_SIGACTION static bool signal_masking = false; static int signal_max; static sigset_t signals_masked, signals_unmasked; /* If signal masking is enabled, add/remove signum from * signals_masked, and update the current mask to match. */ static void set_signal_handled(int signum, sig_handler_type sigfunc) { if (signal_masking) { if (signum > signal_max) { signal_max = signum; } if (sigfunc != SIG_IGN && sigfunc != SIG_DFL) { sigaddset(&signals_masked, signum); } else { sigdelset(&signals_masked, signum); } xsignal_mask(); } } sig_handler_type xsignal(int signum, sig_handler_type sigfunc) { struct sigaction act, oact; act.sa_handler = sigfunc; sigemptyset(&act.sa_mask); /* Try to restart system calls if possible. */ #ifdef SA_RESTART act.sa_flags = SA_RESTART; #else act.sa_flags = 0; #endif if (sigaction(signum, &act, &oact) < 0) return SIG_ERR; set_signal_handled(signum, sigfunc); return oact.sa_handler; } sig_handler_type xsignal_norestart(int signum, sig_handler_type sigfunc) { struct sigaction act, oact; act.sa_handler = sigfunc; sigemptyset(&act.sa_mask); /* Try not to restart system calls. */ #ifdef SA_INTERRUPT act.sa_flags = SA_INTERRUPT; #else act.sa_flags = 0; #endif if (sigaction(signum, &act, &oact) < 0) return SIG_ERR; set_signal_handled(signum, sigfunc); return oact.sa_handler; } /* Mask (block) all handled signals. */ void xsignal_mask(void) { int save_errno = errno; sigprocmask(SIG_SETMASK, &signals_masked, NULL); errno = save_errno; } /* Unmask (unblock) all handled signals. */ void xsignal_unmask(void) { int save_errno = errno; sigprocmask(SIG_SETMASK, &signals_unmasked, NULL); errno = save_errno; } /* Enable signal masking. * * The purpose of this is to ensure that signal handlers only run when * nothing else is going on, i.e. so they never access any data * structure while it is in an inconsistent state. */ void xsignal_enable_masking(void) { sigprocmask(SIG_SETMASK, NULL, &signals_masked); sigprocmask(SIG_SETMASK, NULL, &signals_unmasked); signal_masking = true; } /* Clean up signal mask for a forked process. */ void xsignal_forked(void) { if (signal_masking) { int n; /* Remove handlers for signals we handled. The reason for this is that * the child process could in principle receive one of these signals * after the mask is released. If this happens, we don't want the * handler to be run (even, or perhaps especially, inside the child) - * chaos would ensue. */ for (n = 0; n < signal_max; ++n) { if (sigismember(&signals_masked, n) && !sigismember(&signals_unmasked, n)) { signal(n, SIG_DFL); } } /* Now it's OK to unblock signals we handled. */ xsignal_unmask(); } } #else /* !HAVE_SIGACTION */ sig_handler_type xsignal(int signum, sig_handler_type sigfunc) { return signal(signum, sigfunc); } sig_handler_type xsignal_norestart(int signum, sig_handler_type sigfunc) { return signal(signum, sigfunc); } /* Obsolete systems just have to put up with unsafe signal behaviour. * Sorry. */ void xsignal_mask(void) { } void xsignal_unmask(void) { } void xsignal_enable_masking(void) { } void xsignal_forked(void) { } #endif /* !HAVE_SIGACTION */ inn-2.6.0/lib/memcmp.c0000644000175200017520000000223712575023702014114 0ustar iuliusiulius/* $Id: memcmp.c 9767 2014-12-07 21:13:43Z iulius $ ** ** Replacement for a missing or broken memcmp. ** ** Written by Russ Allbery ** This work is hereby placed in the public domain by its author. ** ** Provides the same functionality as the standard library routine memcmp ** for those platforms that don't have it or where it doesn't work right ** (such as on SunOS where it can't deal with eight-bit characters). */ #include "config.h" #include /* If we're running the test suite, rename memcmp to avoid conflicts with the system version. */ #if TESTING # undef memcmp # define memcmp test_memcmp int test_memcmp(const void *, const void *, size_t); #endif int memcmp(const void *s1, const void *s2, size_t n) { size_t i; const unsigned char *p1, *p2; /* It's technically illegal to call memcmp with NULL pointers, but we may as well check anyway. */ if (!s1) return !s2 ? 0 : -1; if (!s2) return 1; p1 = (const unsigned char *) s1; p2 = (const unsigned char *) s2; for (i = 0; i < n; i++, p1++, p2++) if (*p1 != *p2) return (int) *p1 - (int) *p2; return 0; } inn-2.6.0/lib/messageid.c0000644000175200017520000001050112575023702014570 0ustar iuliusiulius/* $Id: messageid.c 8716 2009-11-07 19:46:39Z iulius $ ** ** Routines for message-IDs: generation and checks. */ #include "config.h" #include "clibrary.h" #include #include #include "inn/innconf.h" #include "inn/libinn.h" #include "inn/nntp.h" /* Scale time back a bit, for shorter message-IDs. */ #define OFFSET 673416000L /* ** Flag array, indexed by character. Character classes for message-IDs. */ static char midcclass[256]; #define CC_MSGID_ATOM 01 #define CC_MSGID_NORM 02 #define midnormchar(c) ((midcclass[(unsigned char)(c)] & CC_MSGID_NORM) != 0) #define midatomchar(c) ((midcclass[(unsigned char)(c)] & CC_MSGID_ATOM) != 0) char * GenerateMessageID(char *domain) { static char buff[SMBUF]; static int count; char *p; char sec32[10]; char pid32[10]; time_t now; now = time(NULL); Radix32(now - OFFSET, sec32); Radix32(getpid(), pid32); if ((domain != NULL && innconf->domain == NULL) || (domain != NULL && innconf->domain != NULL && strcmp(domain, innconf->domain) != 0)) { p = domain; } else { if ((p = GetFQDN(domain)) == NULL) return NULL; } snprintf(buff, sizeof(buff), "<%s$%s$%d@%s>", sec32, pid32, ++count, p); return buff; } /* ** Initialize the character class tables. */ void InitializeMessageIDcclass(void) { const unsigned char *p; unsigned int i; /* Set up the character class tables. These are written a * little strangely to work around a GCC2.0 bug. */ memset(midcclass, 0, sizeof(midcclass)); p = (const unsigned char*) "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; while ((i = *p++) != 0) { midcclass[i] = CC_MSGID_ATOM | CC_MSGID_NORM; } p = (const unsigned char*) "!#$%&'*+-/=?^_`{|}~"; while ((i = *p++) != 0) { midcclass[i] = CC_MSGID_ATOM | CC_MSGID_NORM; } p = (const unsigned char*) "\"(),.:;<@"; while ((i = *p++) != 0) { midcclass[i] = CC_MSGID_NORM; } } /* ** According to RFC 3977: ** ** o A message-ID MUST begin with "<", end with ">", and MUST NOT ** contain the latter except at the end. ** ** o A message-ID MUST be between 3 and 250 octets in length. ** ** o A message-ID MUST NOT contain octets other than printable US-ASCII ** characters. ** ** Besides, we check message-ID format based on RFC 5322 grammar, except that ** (as per USEFOR, RFC 5536) whitespace, non-printing, and '>' characters ** are excluded. ** Based on code by Paul Eggert posted to news.software.b on 22-Nov-90 ** in <#*tyo2'~n@twinsun.com>, with additional e-mail discussion. ** Thanks, Paul, for the original implementation based upon RFC 1036. ** Updated to RFC 5536 by Julien Elie. */ bool IsValidMessageID(const char *MessageID, bool stripspaces) { int c; const unsigned char *p; /* Check the length of the message-ID. */ if (MessageID == NULL || strlen(MessageID) > NNTP_MAXLEN_MSGID) return false; p = (const unsigned char *) MessageID; if (stripspaces) { for (; ISWHITE(*p); p++); } /* Scan local-part: "< dot-atom-text". */ if (*p++ != '<') return false; for (; ; p++) { if (midatomchar(*p)) { while (midatomchar(*++p)) continue; } else { return false; } if (*p != '.') break; } /* Scan domain part: "@ dot-atom-text|no-fold-literal > \0" */ if (*p++ != '@') return false; for ( ; ; p++) { if (midatomchar(*p)) { while (midatomchar(*++p)) continue; } else { /* no-fold-literal only */ if (p[-1] != '@' || *p++ != '[') return false; for ( ; ; ) { switch (c = *p++) { default: if (midnormchar(c)) { continue; } else { return false; } case ']': break; } break; } break; } if (*p != '.') break; } if (*p++ != '>') return false; if (stripspaces) { for (; ISWHITE(*p); p++); } return (*p == '\0'); } inn-2.6.0/lib/getfqdn.c0000644000175200017520000000444212575023702014266 0ustar iuliusiulius/* $Id: getfqdn.c 7585 2006-11-21 09:37:51Z eagle $ ** */ #include "config.h" #include "clibrary.h" #include #include "inn/libinn.h" #include "inn/paths.h" /* ** Get the fully-qualified domain name for this host. */ char *GetFQDN(char *domain) { static char buff[SMBUF]; struct hostent *hp; char *p; char **ap; #if 0 /* See comments below. */ char temp[SMBUF + 2]; #endif /* 0 */ /* Return any old results. */ if (buff[0]) return buff; /* Try gethostname. */ if (gethostname(buff, (int)sizeof buff) < 0) return NULL; if (strchr(buff, '.') != NULL) return buff; /* See if DNS (or /etc/hosts) gives us a full domain name. */ if ((hp = gethostbyname(buff)) == NULL) return NULL; #if 0 /* This code is a "feature" that allows multiple domains (NIS or * DNS, I'm not sure) to work with a single INN server. However, * it turns out to cause more problems for people, and they have to * use hacks like __switch_gethostbyname, etc. So if you need this, * turn it on, but don't complain to me. */ if (strchr(hp->h_name, '.') == NULL) { /* Try to force DNS lookup if NIS/whatever gets in the way. */ strlcpy(temp, buff, sizeof(temp)); strlcat(temp, ".", sizeof(temp)); hp = gethostbyname(temp); } #endif /* 0 */ /* First, see if the main name is a FQDN. It should be. */ if (hp != NULL && strchr(hp->h_name, '.') != NULL) { if (strlen(hp->h_name) < sizeof buff - 1) { strlcpy(buff, hp->h_name, sizeof(buff)); return buff; } /* Doesn't fit; make sure we don't return bad data next time. */ buff[0] = '\0'; return hp->h_name; } /* Second, see if any aliases are. */ if ((ap = hp->h_aliases) != NULL) while ((p = *ap++) != NULL) if (strchr(p, '.') != NULL) { /* Deja-vous all over again. */ if (strlen(p) < sizeof buff - 1) { strlcpy(buff, p, sizeof(buff)); return buff; } buff[0] = '\0'; return p ; } /* Give up: Get the domain config param and append it. */ if ((p = domain) == NULL || *p == '\0') return NULL; if (strlen(buff) + 1 + strlen(p) > sizeof buff - 1) /* Doesn't fit. */ return NULL; strlcat(buff, ".", sizeof(buff)); strlcat(buff, p, sizeof(buff)); return buff; } inn-2.6.0/lib/cleanfrom.c0000644000175200017520000000313312575023702014600 0ustar iuliusiulius/* $Id: cleanfrom.c 7585 2006-11-21 09:37:51Z eagle $ ** */ #include "config.h" #include "clibrary.h" #include "inn/libinn.h" #define LPAREN '(' #define RPAREN ')' /* ** Clean up a from line, making the following transformations: ** address address ** address (stuff) address ** stuff

address */ void HeaderCleanFrom(char *from) { char *p; char *end; int len; if ((len = strlen(from)) == 0) return; /* concatenate folded header */ for (p = end = from ; p < from + len ;) { if (*p == '\n') { if ((p + 1 < from + len) && ISWHITE(p[1])) { if ((p - 1 >= from) && (p[-1] == '\r')) { end--; *end = p[1]; p += 2; } else { *end = p[1]; p++; } } else { *end = '\0'; break; } } else *end++ = *p++; } if (end != from) *end = '\0'; /* Do pretty much the equivalent of sed's "s/(.*)//g"; */ while ((p = strchr(from, LPAREN)) && (end = strchr(p, RPAREN))) { while (*++end) *p++ = *end; *p = '\0'; } /* Do pretty much the equivalent of sed's "s/\".*\"//g"; */ while ((p = strchr(from, '"')) && (end = strchr(p, '"'))) { while (*++end) *p++ = *end; *p = '\0'; } /* Do the equivalent of sed's "s/.*<\(.*\)>/\1/" */ if ((p = strrchr(from, '<')) && (end = strrchr(p, '>'))) { while (++p < end) *from++ = *p; *from = '\0'; } /* drop white spaces */ if ((len = strlen(from)) == 0) return; for (p = end = from ; p < from + len ;) { if (ISWHITE(*p)) { p++; continue; } *end++ = *p++; } if (end != from) *end = '\0'; } inn-2.6.0/lib/messages.c0000644000175200017520000003222412575023702014444 0ustar iuliusiulius/* $Id: messages.c 9913 2015-07-07 16:31:52Z iulius $ * * Message and error reporting (possibly fatal). * * Usage: * * extern int cleanup(void); * extern void log(int, const char *, va_list, int); * * message_fatal_cleanup = cleanup; * message_program_name = argv[0]; * * warn("Something horrible happened at %lu", time); * syswarn("Couldn't unlink temporary file %s", tmpfile); * * die("Something fatal happened at %lu", time); * sysdie("open of %s failed", filename); * * debug("Some debugging message about %s", string); * notice("Informational notices"); * * message_handlers_warn(1, log); * warn("This now goes through our log function"); * * These functions implement message reporting through user-configurable * handler functions. debug() only does something if DEBUG is defined, and * notice() and warn() just output messages as configured. die() similarly * outputs a message but then exits, normally with a status of 1. * * The sys* versions do the same, but append a colon, a space, and the results * of strerror(errno) to the end of the message. All functions accept * printf-style formatting strings and arguments. * * If message_fatal_cleanup is non-NULL, it is called before exit by die and * sysdie and its return value is used as the argument to exit. It is a * pointer to a function taking no arguments and returning an int, and can be * used to call cleanup functions or to exit in some alternate fashion (such * as by calling _exit). * * If message_program_name is non-NULL, the string it points to, followed by a * colon and a space, is prepended to all error messages logged through the * message_log_stdout and message_log_stderr message handlers (the former is * the default for notice, and the latter is the default for warn and die). * * Honoring error_program_name and printing to stderr is just the default * handler; with message_handlers_* the handlers for any message function can * be changed. By default, notice prints to stdout, warn and die print to * stderr, and the others don't do anything at all. These functions take a * count of handlers and then that many function pointers, each one to a * function that takes a message length (the number of characters snprintf * generates given the format and arguments), a format, an argument list as a * va_list, and the applicable errno value (if any). * * The canonical version of this file is maintained in the rra-c-util package, * which can be found at . * * Written by Russ Allbery * Copyright 2008, 2009, 2010, 2013 * The Board of Trustees of the Leland Stanford Junior University * Copyright (c) 2004, 2005, 2006 * by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, * 2002, 2003 by The Internet Software Consortium and Rich Salz * * This code is derived from software contributed to the Internet Software * Consortium by Rich Salz. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #include "config.h" #include "clibrary.h" #include #ifdef HAVE_SYSLOG_H # include #endif #ifdef _WIN32 # include # define LOG_DEBUG EVENTLOG_SUCCESS # define LOG_INFO EVENTLOG_INFORMATION_TYPE # define LOG_NOTICE EVENTLOG_INFORMATION_TYPE # define LOG_WARNING EVENTLOG_WARNING_TYPE # define LOG_ERR EVENTLOG_ERROR_TYPE # define LOG_CRIT EVENTLOG_ERROR_TYPE #endif #include "inn/macros.h" #include "inn/messages.h" #include "inn/xmalloc.h" /* The default handler lists. */ static message_handler_func stdout_handlers[2] = { message_log_stdout, NULL }; static message_handler_func stderr_handlers[2] = { message_log_stderr, NULL }; /* The list of logging functions currently in effect. */ static message_handler_func *debug_handlers = NULL; static message_handler_func *notice_handlers = stdout_handlers; static message_handler_func *warn_handlers = stderr_handlers; static message_handler_func *die_handlers = stderr_handlers; /* If non-NULL, called before exit and its return value passed to exit. */ int (*message_fatal_cleanup)(void) = NULL; /* If non-NULL, prepended (followed by ": ") to messages. */ const char *message_program_name = NULL; /* * Set the handlers for a particular message function. Takes a pointer to the * handler list, the count of handlers, and the argument list. */ static void message_handlers(message_handler_func **list, unsigned int count, va_list args) { unsigned int i; if (*list != stdout_handlers && *list != stderr_handlers) free(*list); *list = xcalloc(count + 1, sizeof(message_handler_func)); for (i = 0; i < count; i++) (*list)[i] = (message_handler_func) va_arg(args, message_handler_func); (*list)[count] = NULL; } /* * There's no good way of writing these handlers without a bunch of code * duplication since we can't assume variadic macros, but I can at least make * it easier to write and keep them consistent. */ #define HANDLER_FUNCTION(type) \ void \ message_handlers_ ## type(unsigned int count, ...) \ { \ va_list args; \ \ va_start(args, count); \ message_handlers(& type ## _handlers, count, args); \ va_end(args); \ } HANDLER_FUNCTION(debug) HANDLER_FUNCTION(notice) HANDLER_FUNCTION(warn) HANDLER_FUNCTION(die) /* * Reset all handlers back to the defaults and free all allocated memory. * This is primarily useful for programs that undergo comprehensive memory * allocation analysis. */ void message_handlers_reset(void) { free(debug_handlers); debug_handlers = NULL; if (notice_handlers != stdout_handlers) { free(notice_handlers); notice_handlers = stdout_handlers; } if (warn_handlers != stderr_handlers) { free(warn_handlers); warn_handlers = stderr_handlers; } if (die_handlers != stderr_handlers) { free(die_handlers); die_handlers = stderr_handlers; } } /* * Print a message to stdout, supporting message_program_name. */ void message_log_stdout(size_t len UNUSED, const char *fmt, va_list args, int err) { if (message_program_name != NULL) fprintf(stdout, "%s: ", message_program_name); vfprintf(stdout, fmt, args); if (err) fprintf(stdout, ": %s", strerror(err)); fprintf(stdout, "\n"); fflush(stdout); } /* * Print a message to stderr, supporting message_program_name. Also flush * stdout so that errors and regular output occur in the right order. */ void message_log_stderr(size_t len UNUSED, const char *fmt, va_list args, int err) { fflush(stdout); if (message_program_name != NULL) fprintf(stderr, "%s: ", message_program_name); vfprintf(stderr, fmt, args); if (err) fprintf(stderr, ": %s", strerror(err)); fprintf(stderr, "\n"); } /* * Log a message to syslog. This is a helper function used to implement all * of the syslog message log handlers. It takes the same arguments as a * regular message handler function but with an additional priority argument. * * This needs further attention on Windows. For example, it currently doesn't * log the errno information. */ static void __attribute__((__format__(printf, 3, 0))) message_log_syslog(int pri, size_t len, const char *fmt, va_list args, int err) { char *buffer; int status; buffer = malloc(len + 1); if (buffer == NULL) { fprintf(stderr, "failed to malloc %lu bytes at %s line %d: %s", (unsigned long) len + 1, __FILE__, __LINE__, strerror(errno)); exit(message_fatal_cleanup ? (*message_fatal_cleanup)() : 1); } status = vsnprintf(buffer, len + 1, fmt, args); if (status < 0) { warn("failed to format output with vsnprintf in syslog handler"); free(buffer); return; } #ifdef _WIN32 { HANDLE eventlog; eventlog = RegisterEventSource(NULL, message_program_name); if (eventlog != NULL) { ReportEvent(eventlog, (WORD) pri, 0, 0, NULL, 1, 0, &buffer, NULL); CloseEventLog(eventlog); } } #else /* !_WIN32 */ if (err == 0) syslog(pri, "%s", buffer); else syslog(pri, "%s: %s", buffer, strerror(err)); #endif /* !_WIN32 */ free(buffer); } /* * Do the same sort of wrapper to generate all of the separate syslog logging * functions. */ #define SYSLOG_FUNCTION(name, type) \ void \ message_log_syslog_ ## name(size_t l, const char *f, va_list a, int e) \ { \ message_log_syslog(LOG_ ## type, l, f, a, e); \ } SYSLOG_FUNCTION(debug, DEBUG) SYSLOG_FUNCTION(info, INFO) SYSLOG_FUNCTION(notice, NOTICE) SYSLOG_FUNCTION(warning, WARNING) SYSLOG_FUNCTION(err, ERR) SYSLOG_FUNCTION(crit, CRIT) /* * All of the message handlers. There's a lot of code duplication here too, * but each one is still *slightly* different and va_start has to be called * multiple times, so it's hard to get rid of the duplication. */ void debug(const char *format, ...) { va_list args; message_handler_func *log; ssize_t length; if (debug_handlers == NULL) return; va_start(args, format); length = vsnprintf(NULL, 0, format, args); va_end(args); if (length < 0) return; for (log = debug_handlers; *log != NULL; log++) { va_start(args, format); (**log)((size_t) length, format, args, 0); va_end(args); } } void notice(const char *format, ...) { va_list args; message_handler_func *log; ssize_t length; va_start(args, format); length = vsnprintf(NULL, 0, format, args); va_end(args); if (length < 0) return; for (log = notice_handlers; *log != NULL; log++) { va_start(args, format); (**log)((size_t) length, format, args, 0); va_end(args); } } void sysnotice(const char *format, ...) { va_list args; message_handler_func *log; ssize_t length; int error = errno; va_start(args, format); length = vsnprintf(NULL, 0, format, args); va_end(args); if (length < 0) return; for (log = notice_handlers; *log != NULL; log++) { va_start(args, format); (**log)((size_t) length, format, args, error); va_end(args); } } void warn(const char *format, ...) { va_list args; message_handler_func *log; ssize_t length; va_start(args, format); length = vsnprintf(NULL, 0, format, args); va_end(args); if (length < 0) return; for (log = warn_handlers; *log != NULL; log++) { va_start(args, format); (**log)((size_t) length, format, args, 0); va_end(args); } } void syswarn(const char *format, ...) { va_list args; message_handler_func *log; ssize_t length; int error = errno; va_start(args, format); length = vsnprintf(NULL, 0, format, args); va_end(args); if (length < 0) return; for (log = warn_handlers; *log != NULL; log++) { va_start(args, format); (**log)((size_t) length, format, args, error); va_end(args); } } void die(const char *format, ...) { va_list args; message_handler_func *log; ssize_t length; va_start(args, format); length = vsnprintf(NULL, 0, format, args); va_end(args); if (length >= 0) for (log = die_handlers; *log != NULL; log++) { va_start(args, format); (**log)((size_t) length, format, args, 0); va_end(args); } exit(message_fatal_cleanup ? (*message_fatal_cleanup)() : 1); } void sysdie(const char *format, ...) { va_list args; message_handler_func *log; ssize_t length; int error = errno; va_start(args, format); length = vsnprintf(NULL, 0, format, args); va_end(args); if (length >= 0) for (log = die_handlers; *log != NULL; log++) { va_start(args, format); (**log)((size_t) length, format, args, error); va_end(args); } exit(message_fatal_cleanup ? (*message_fatal_cleanup)() : 1); } inn-2.6.0/lib/strspn.c0000644000175200017520000000334412575023702014167 0ustar iuliusiulius/* $Id: strspn.c 6118 2003-01-13 06:44:24Z rra $ ** ** This file has been modified to get it to compile more easily ** on pre-4.4BSD systems. Rich $alz, June 1991. */ #include "config.h" #include "clibrary.h" /* * Copyright (c) 1989 The Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that: (1) source distributions retain this entire copyright * notice and comment, and (2) distributions including binaries display * the following acknowledgement: ``This product includes software * developed by the University of California, Berkeley and its contributors'' * in the documentation or other materials provided with the distribution * and in all advertising materials mentioning features or use of this * software. Neither the name of the University nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #if 0 #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)strspn.c 5.7 (Berkeley) 6/1/90"; #endif /* LIBC_SCCS and not lint */ #include #include #endif /* * Span the string s2 (skip characters that are in s2). */ size_t strspn(s1, s2) const char *s1; const char *s2; { const char *p = s1, *spanp; char c, sc; /* * Skip any characters in s2, excluding the terminating \0. */ cont: c = *p++; for (spanp = s2; (sc = *spanp++) != 0;) if (sc == c) goto cont; return (p - 1 - s1); } inn-2.6.0/lib/numbers.c0000644000175200017520000000367312575023702014316 0ustar iuliusiulius/* $Id: numbers.c 9019 2010-03-19 21:27:15Z iulius $ ** ** Routines for numbers: manipulation and checks. */ #include "config.h" #include "clibrary.h" #include #include "inn/libinn.h" /* ** Check if the argument is a valid article number according to RFC 3977, ** that is to say it contains from 1 to 16 digits. */ bool IsValidArticleNumber(const char *string) { int len = 0; const unsigned char *p; /* Not NULL. */ if (string == NULL) return false; p = (const unsigned char *) string; for (; *p != '\0'; p++) { len++; if (!isdigit((unsigned char) *p)) return false; } if (len > 0 && len < 17) return true; else return false; } /* ** Return true if the provided string is a valid range, that is to say: ** ** - An article number. ** - An article number followed by a dash to indicate all following. ** - An article number followed by a dash followed by another article ** number. ** ** In addition to RFC 3977, we also accept: ** - A dash followed by an article number to indicate all previous. ** - A dash for everything. */ bool IsValidRange(char *string) { char *p; bool valid; /* Not NULL. */ if (string == NULL) return false; /* Just a dash. */ if (strcmp(string, "-") == 0) return true; p = string; /* Begins with a dash. There must be a number after. */ if (*string == '-') { p++; return IsValidArticleNumber(p); } /* Got just a single number? */ if ((p = strchr(string, '-')) == NULL) return IsValidArticleNumber(string); /* "-" becomes "\0" and we parse the low water mark. */ *p++ = '\0'; if (*p == '\0') { /* Ends with a dash. */ valid = IsValidArticleNumber(string); } else { valid = (IsValidArticleNumber(string) && IsValidArticleNumber(p)); } p--; *p = '-'; return valid; } inn-2.6.0/lib/inet_aton.c0000644000175200017520000001257212575023702014621 0ustar iuliusiulius/* $Id: inet_aton.c 9759 2014-12-04 20:11:38Z iulius $ * * Replacement for a missing inet_aton. * * Provides the same functionality as the standard library routine * inet_aton for those platforms that don't have it. inet_aton is * thread-safe. * * The canonical version of this file is maintained in the rra-c-util package, * which can be found at . * * Written by Russ Allbery * * The authors hereby relinquish any claim to any copyright that they may have * in this work, whether granted under contract or by operation of law or * international treaty, and hereby commit to the public, at large, that they * shall not, at any time in the future, seek to enforce any copyright in this * work against any person or entity, or prevent any person or entity from * copying, publishing, distributing or creating derivative works of this * work. */ #include "config.h" #include "clibrary.h" #include "portable/socket.h" /* * If we're running the test suite, rename inet_aton to avoid conflicts with * the system version. */ #if TESTING # undef inet_aton # define inet_aton test_inet_aton int test_inet_aton(const char *, struct in_addr *); #endif int inet_aton(const char *s, struct in_addr *addr) { unsigned long octet[4], address; const char *p; int base, i; int part = 0; if (s == NULL) return 0; /* * Step through each period-separated part of the address. If we see * more than four parts, the address is invalid. */ for (p = s; *p != 0; part++) { if (part > 3) return 0; /* * Determine the base of the section we're looking at. Numbers are * represented the same as in C; octal starts with 0, hex starts * with 0x, and anything else is decimal. */ if (*p == '0') { p++; if (*p == 'x') { p++; base = 16; } else { base = 8; } } else { base = 10; } /* * Make sure there's actually a number. (A section of just "0" * would set base to 8 and leave us pointing at a period; allow * that.) */ if (*p == '.' && base != 8) return 0; octet[part] = 0; /* * Now, parse this segment of the address. For each digit, multiply * the result so far by the base and then add the value of the digit. * Be careful of arithmetic overflow in cases where an unsigned long * is 32 bits; we need to detect it *before* we multiply by the base * since otherwise we could overflow and wrap and then not detect the * error. */ for (; *p != 0 && *p != '.'; p++) { if (octet[part] > 0xffffffffUL / base) return 0; /* * Use a switch statement to parse each digit rather than assuming * ASCII. Probably pointless portability. */ switch (*p) { case '0': i = 0; break; case '1': i = 1; break; case '2': i = 2; break; case '3': i = 3; break; case '4': i = 4; break; case '5': i = 5; break; case '6': i = 6; break; case '7': i = 7; break; case '8': i = 8; break; case '9': i = 9; break; case 'A': case 'a': i = 10; break; case 'B': case 'b': i = 11; break; case 'C': case 'c': i = 12; break; case 'D': case 'd': i = 13; break; case 'E': case 'e': i = 14; break; case 'F': case 'f': i = 15; break; default: return 0; } if (i >= base) return 0; octet[part] = (octet[part] * base) + i; } /* * Advance over periods; the top of the loop will increment the count * of parts we've seen. We need a check here to detect an illegal * trailing period. */ if (*p == '.') { p++; if (*p == 0) return 0; } } if (part == 0) return 0; /* IPv4 allows three types of address specification: * * a.b * a.b.c * a.b.c.d * * If there are fewer than four segments, the final segment accounts for * all of the remaining portion of the address. For example, in the a.b * form, b is the final 24 bits of the address. We also allow a simple * number, which is interpreted as the 32-bit number corresponding to the * full IPv4 address. * * The first for loop below ensures that any initial segments represent * only 8 bits of the address and builds the upper portion of the IPv4 * address. Then, the remaining segment is checked to make sure it's no * bigger than the remaining space in the address and then is added into * the result. */ address = 0; for (i = 0; i < part - 1; i++) { if (octet[i] > 0xff) return 0; address |= octet[i] << (8 * (3 - i)); } if (octet[i] > (0xffffffffUL >> (i * 8))) return 0; address |= octet[i]; if (addr != NULL) addr->s_addr = htonl(address); return 1; } inn-2.6.0/lib/reservedfd.c0000644000175200017520000000350012575023702014761 0ustar iuliusiulius/* $Id: reservedfd.c 7585 2006-11-21 09:37:51Z eagle $ ** */ #include "config.h" #include "clibrary.h" #include #include "inn/libinn.h" static FILE **Reserved_fd = NULL; static int Maxfd = -1; bool fdreserve(int fdnum) { static int allocated = 0; int i, start = allocated; if (fdnum <= 0) { if (Reserved_fd != NULL) { for (i = 0 ; i < Maxfd ; i++) { fclose(Reserved_fd[i]); } free(Reserved_fd); Reserved_fd = NULL; } Maxfd = -1; allocated = 0; return true; } if (Reserved_fd == NULL) { Reserved_fd = xmalloc(fdnum * sizeof(FILE *)); allocated = fdnum; } else { if (allocated < fdnum) { Reserved_fd = xrealloc(Reserved_fd, fdnum * sizeof(FILE *)); allocated = fdnum; } else if (Maxfd > fdnum) { for (i = fdnum ; i < Maxfd ; i++) { fclose(Reserved_fd[i]); } } } for (i = start ; i < fdnum ; i++) { if (((Reserved_fd[i] = fopen("/dev/null", "r")) == NULL)){ for (--i ; i >= 0 ; i--) fclose(Reserved_fd[i]); free(Reserved_fd); Reserved_fd = NULL; allocated = 0; Maxfd = -1; return false; } } Maxfd = fdnum; return true; } FILE * Fopen(const char *p, const char *type, int xindex) { FILE *nfp; if (p == NULL || *p == '\0') return NULL; if (xindex < 0 || xindex > Maxfd || Reserved_fd[xindex] == NULL) return fopen(p, type); if ((nfp = freopen(p, type, Reserved_fd[xindex])) == NULL) { Reserved_fd[xindex] = freopen("/dev/null", "r", Reserved_fd[xindex]); return NULL; } return (Reserved_fd[xindex] = nfp); } int Fclose(FILE *fp) { int i; if (fp == NULL) return 0; for (i = 0 ; i < Maxfd ; i++) { if (Reserved_fd[i] == fp) break; } if (i >= Maxfd) return fclose(fp); Reserved_fd[i] = freopen("/dev/null", "r", Reserved_fd[i]); return 0; } inn-2.6.0/lib/strlcpy.c0000644000175200017520000000331112575023702014330 0ustar iuliusiulius/* $Id: strlcpy.c 9759 2014-12-04 20:11:38Z iulius $ * * Replacement for a missing strlcpy. * * Provides the same functionality as the *BSD function strlcpy, originally * developed by Todd Miller and Theo de Raadt. strlcpy works similarly to * strncpy, except saner and simpler. The result is always nul-terminated * even if the source string is longer than the destination string, and the * total space required is returned. The destination string is not nul-filled * like strncpy does, just nul-terminated. * * The canonical version of this file is maintained in the rra-c-util package, * which can be found at . * * Written by Russ Allbery * * The authors hereby relinquish any claim to any copyright that they may have * in this work, whether granted under contract or by operation of law or * international treaty, and hereby commit to the public, at large, that they * shall not, at any time in the future, seek to enforce any copyright in this * work against any person or entity, or prevent any person or entity from * copying, publishing, distributing or creating derivative works of this * work. */ #include "config.h" #include "clibrary.h" /* * If we're running the test suite, rename strlcpy to avoid conflicts with * the system version. */ #if TESTING # undef strlcpy # define strlcpy test_strlcpy size_t test_strlcpy(char *, const char *, size_t); #endif size_t strlcpy(char *dst, const char *src, size_t size) { size_t length, copy; length = strlen(src); if (size > 0) { copy = (length >= size) ? size - 1 : length; memcpy(dst, src, copy); dst[copy] = '\0'; } return length; } inn-2.6.0/lib/concat.c0000644000175200017520000000464012575023702014105 0ustar iuliusiulius/* $Id: concat.c 9767 2014-12-07 21:13:43Z iulius $ ** ** Concatenate strings with dynamic memory allocation. ** ** Written by Russ Allbery ** This work is hereby placed in the public domain by its author. ** ** Usage: ** ** string = concat(string1, string2, ..., (char *) 0); ** path = concatpath(base, name); ** ** Dynamically allocates (using xmalloc) sufficient memory to hold all of ** the strings given and then concatenates them together into that ** allocated memory, returning a pointer to it. Caller is responsible for ** freeing. Assumes xmalloc is available. The last argument must be a ** null pointer (to a char *, if you actually find a platform where it ** matters). ** ** concatpath is similar, except that it only takes two arguments. If the ** second argument begins with / or ./, a copy of it is returned; ** otherwise, the first argument, a slash, and the second argument are ** concatenated together and returned. This is useful for building file ** names where names that aren't fully qualified are qualified with some ** particular directory. */ #include "config.h" #include "inn/libinn.h" #include #include /* Abbreviation for cleaner code. */ #define VA_NEXT(var, type) ((var) = (type) va_arg(args, type)) /* ANSI C requires at least one named parameter. */ char * concat(const char *first, ...) { va_list args; char *result, *p; const char *string; size_t length = 0; /* Find the total memory required. */ va_start(args, first); for (string = first; string != NULL; VA_NEXT(string, const char *)) length += strlen(string); va_end(args); length++; /* Create the string. Doing the copy ourselves avoids useless string traversals of result, if using strcat, or string, if using strlen to increment a pointer into result, at the cost of losing the native optimization of strcat if any. */ result = xmalloc(length); p = result; va_start(args, first); for (string = first; string != NULL; VA_NEXT(string, const char *)) while (*string != '\0') *p++ = *string++; va_end(args); *p = '\0'; return result; } char * concatpath(const char *base, const char *name) { if (name[0] == '/' || (name[0] == '.' && name[1] == '/')) return xstrdup(name); else return concat(base != NULL ? base : ".", "/", name, (char *) 0); } inn-2.6.0/lib/setenv.c0000644000175200017520000000442612575023702014144 0ustar iuliusiulius/* $Id: setenv.c 9759 2014-12-04 20:11:38Z iulius $ * * Replacement for a missing setenv. * * Provides the same functionality as the standard library routine setenv for * those platforms that don't have it. * * The canonical version of this file is maintained in the rra-c-util package, * which can be found at . * * Written by Russ Allbery * * The authors hereby relinquish any claim to any copyright that they may have * in this work, whether granted under contract or by operation of law or * international treaty, and hereby commit to the public, at large, that they * shall not, at any time in the future, seek to enforce any copyright in this * work against any person or entity, or prevent any person or entity from * copying, publishing, distributing or creating derivative works of this * work. */ #include "config.h" #include "clibrary.h" /* * If we're running the test suite, rename setenv to avoid conflicts with * the system version. */ #if TESTING # undef setenv # define setenv test_setenv int test_setenv(const char *, const char *, int); #endif int setenv(const char *name, const char *value, int overwrite) { char *envstring; /* Do nothing if not overwriting and the variable is already set. */ if (!overwrite && getenv(name) != NULL) return 0; /* * Build the environment string and add it to the environment using * putenv. Systems without putenv lose, but XPG4 requires it. * * We intentionally don't use the xmalloc family of allocation routines * here, since the intention is to provide a replacement for the standard * library function that sets errno and returns in the event of a memory * allocation failure. */ if (asprintf(&envstring, "%s=%s", name, value) < 0) return -1; return putenv(envstring); /* * Note that the memory allocated is not freed. This is intentional; many * implementations of putenv assume that the string passed to putenv will * never be freed and don't make a copy of it. Repeated use of this * function will therefore leak memory, since most implementations of * putenv also don't free strings removed from the environment (due to * being overwritten). */ } inn-2.6.0/lib/sequence.c0000644000175200017520000000160312575023702014442 0ustar iuliusiulius/* $Id: sequence.c 4871 2001-07-09 08:09:58Z alexk $ ** ** Sequence space arithmetic routines. ** ** This is a set of routines for implementing so called sequence ** space arithmetic (typically used for DNS serial numbers). The ** implementation here is taken from RFC 1982. */ #include "config.h" #include "clibrary.h" #include #include "inn/sequence.h" /* ** compare two unsigned long numbers using sequence space arithmetic ** ** returns: ** 0 - i1 = i2 ** -1 - i1 < i2 ** 1 - i1 > i2 ** INT_MAX - undefined */ int seq_lcompare(unsigned long i1, unsigned long i2) { if (i1 == i2) return 0; else if ((i1 < i2 && i2 - i1 < (1 + ULONG_MAX / 2)) || (i1 > i2 && i1 - i2 > (1 + ULONG_MAX / 2))) return -1; else if ((i1 < i2 && i2 - i1 > (1 + ULONG_MAX / 2)) || (i1 > i2 && i1 - i2 < (1 + ULONG_MAX / 2))) return 1; return INT_MAX; } inn-2.6.0/lib/daemonize.c0000644000175200017520000000315212575023702014606 0ustar iuliusiulius/* $Id: daemonize.c 7585 2006-11-21 09:37:51Z eagle $ ** ** Become a long-running daemon. ** ** Usage: ** ** daemonize(path); ** ** Performs all of the various system-specific stuff required to become a ** long-running daemon. Also chdir to the provided path (which is where ** core dumps will go on most systems). */ #include "config.h" #include "clibrary.h" #include #include #include #include "inn/messages.h" #include "inn/libinn.h" void daemonize(const char *path) { int status; int fd; /* Fork and exit in the parent to disassociate from the current process group and become the leader of a new process group. */ status = fork(); if (status < 0) sysdie("cant fork"); else if (status > 0) _exit(0); /* setsid() should take care of disassociating from the controlling terminal, and FreeBSD at least doesn't like TIOCNOTTY if you don't already have a controlling terminal. So only use the older TIOCNOTTY method if setsid() isn't available. */ #if HAVE_SETSID if (setsid() < 0) syswarn("cant become session leader"); #elif defined(TIOCNOTTY) fd = open("/dev/tty", O_RDWR); if (fd >= 0) { if (ioctl(fd, TIOCNOTTY, NULL) < 0) syswarn("cant disassociate from the terminal"); close(fd); } #endif /* defined(TIOCNOTTY) */ if (chdir(path) < 0) syswarn("cant chdir to %s", path); fd = open("/dev/null", O_RDWR, 0); if (fd != -1) { dup2(fd, STDIN_FILENO); dup2(fd, STDOUT_FILENO); dup2(fd, STDERR_FILENO); if (fd > 2) close(fd); } } inn-2.6.0/lib/getpagesize.c0000644000175200017520000000077112575023702015146 0ustar iuliusiulius/* $Id: getpagesize.c 5596 2002-08-17 21:29:35Z rra $ ** ** Replacement for a missing getpagesize. ** ** Provides getpagesize implemented in terms of sysconf for those systems ** that don't have the getpagesize function. Defaults to a page size of 16KB ** if sysconf isn't available either. */ #include "config.h" #include int getpagesize(void) { int pagesize; #ifdef _SC_PAGESIZE pagesize = sysconf(_SC_PAGESIZE); #else pagesize = 16 * 1024; #endif return pagesize; } inn-2.6.0/lib/list.c0000644000175200017520000000412512575023702013607 0ustar iuliusiulius/* $Id: list.c 6168 2003-01-21 06:27:32Z alexk $ ** */ #include "config.h" #include "clibrary.h" #include "inn/list.h" void list_new(struct list *list) { list->head = (struct node *)&list->tail; list->tailpred = (struct node *)&list->head; list->tail = NULL; } struct node * list_addhead(struct list *list, struct node *node) { node->succ = list->head; node->pred = (struct node *)&list->head; list->head->pred = node; list->head = node; return node; } struct node * list_addtail(struct list *list, struct node *node) { node->succ = (struct node *)&list->tail; node->pred = list->tailpred; list->tailpred->succ = node; list->tailpred = node; return node; } struct node * list_remhead(struct list *list) { struct node *node; node = list->head->succ; if (node) { node->pred = (struct node *)&list->head; node = list->head; list->head = node->succ; } return node; } struct node * list_head(struct list *list) { if (list->head->succ) return list->head; return NULL; } struct node * list_tail(struct list *list) { if (list->tailpred->pred) return list->tailpred; return NULL; } struct node * list_succ(struct node *node) { if (node->succ->succ) return node->succ; return NULL; } struct node * list_pred(struct node *node) { if (node->pred->pred) return node->pred; return NULL; } struct node * list_remove(struct node *node) { node->pred->succ = node->succ; node->succ->pred = node->pred; return node; } struct node * list_remtail(struct list *list) { struct node *node; node = list_tail(list); if (node) list_remove(node); return node; } bool list_isempty(struct list *list) { return list->tailpred == (struct node *)list; } struct node * list_insert(struct list *list, struct node *node, struct node *pred) { if (pred) { if (pred->succ) { node->succ = pred->succ; node->pred = pred; pred->succ->pred = node; pred->succ = node; } else { list_addtail(list, node); } } else { list_addhead(list, node); } return node; } inn-2.6.0/lib/remopen.c0000644000175200017520000000342512575023702014303 0ustar iuliusiulius/* $Id: remopen.c 9691 2014-09-17 16:31:35Z iulius $ ** ** Open a connection to a remote NNTP server. */ #include "config.h" #include "clibrary.h" #include "portable/socket.h" #include #include "inn/innconf.h" #include "inn/libinn.h" #include "inn/network.h" #include "inn/nntp.h" /* ** Open a connection to an NNTP server and create stdio FILE's for talking ** to it. Return -1 on error. */ int NNTPconnect(const char *host, int port, FILE **FromServerp, FILE **ToServerp, char *errbuff, size_t len) { char mybuff[NNTP_MAXLEN_COMMAND + 2]; char *buff; int fd, code, oerrno; FILE *F = NULL; if (errbuff) buff = errbuff; else { buff = mybuff; len = sizeof(mybuff); } *buff = '\0'; fd = network_connect_host(host, port, NULL, DEFAULT_TIMEOUT); if (fd < 0) return -1; /* Connected -- now make sure we can post. If we can't, use EPERM as a reasonable error code. */ F = fdopen(fd, "r"); if (F == NULL) goto fail; if (fgets(buff, len, F) == NULL) goto fail; code = atoi(buff); if (code != NNTP_OK_BANNER_POST && code != NNTP_OK_BANNER_NOPOST) { errno = EPERM; goto fail; } *FromServerp = F; *ToServerp = fdopen(dup(fd), "w"); if (*ToServerp == NULL) goto fail; return 0; fail: oerrno = errno; if (F != NULL) fclose(F); else close(fd); errno = oerrno; return -1; } int NNTPremoteopen(int port, FILE **FromServerp, FILE **ToServerp, char *errbuff, size_t len) { char *p; if ((p = innconf->server) == NULL) { if (errbuff) strlcpy(errbuff, "What server?", len); return -1; } return NNTPconnect(p, port, FromServerp, ToServerp, errbuff, len); } inn-2.6.0/lib/setproctitle.c0000644000175200017520000000551612575023702015362 0ustar iuliusiulius/* $Id: setproctitle.c 5943 2002-12-08 02:28:06Z rra $ ** ** Replacement for a missing setproctitle. ** ** Provides the same functionality as the BSD function setproctitle on hosts ** where modifying argv will produce those results, or on HP-UX (which has ** its own peculiar way of doing this). This may be ineffective on some ** platforms. ** ** Before calling setproctitle, it is *required* that setproctitle_init be ** called, passing it argc and argv as arguments. setproctitle_init will be ** stubbed out on those platforms that don't need it. */ #include "config.h" #include "clibrary.h" #include "portable/setproctitle.h" #include "inn/messages.h" #if HAVE_PSTAT #include #include void setproctitle(const char *format, ...) { va_list args; char title[BUFSIZ]; union pstun un; ssize_t delta = 0; if (message_program_name != NULL) { delta = snprintf(title, sizeof(title), "%s: ", message_program_name); if (delta < 0) delta = 0; } va_start(args, format); vsnprintf(title + delta, sizeof(title) - delta, format, args); va_end(args); un.pst_command = title; pstat(PSTAT_SETCMD, un, strlen(title), 0, 0); } #else static char *title_start = NULL; static char *title_end = NULL; void setproctitle_init(int argc, char *argv[]) { title_start = argv[0]; title_end = argv[argc - 1] + strlen(argv[argc - 1]) - 1; } void setproctitle(const char *format, ...) { va_list args; size_t length; ssize_t delta; char *title; if (title_start == NULL || title_end == NULL) { warn("setproctitle called without setproctitle_init"); return; } /* setproctitle prepends the program name to its arguments. Our emulation should therefore do the same thing. However, some operating systems seem to do that automatically even when we completely overwrite argv, so start our title with a - so that they'll instead put (nnrpd) at the end, thinking we're swapped out. */ title = title_start; *title++ = '-'; *title++ = ' '; length = title_end - title_start - 2; /* Now, put in the actual content. Get the program name from message_program_name if it's set. */ if (message_program_name != NULL) { delta = snprintf(title, length, "%s: ", message_program_name); if (delta < 0 || (size_t) delta > length) return; if (delta > 0) { title += delta; length -= delta; } } va_start(args, format); delta = vsnprintf(title, length, format, args); va_end(args); if (delta < 0 || (size_t) delta > length) return; if (delta > 0) { title += delta; length -= delta; } for (; length > 1; length--, title++) *title = ' '; *title = '\0'; } #endif /* !HAVE_PSTAT */ inn-2.6.0/lib/vector.c0000644000175200017520000003704712575023702014147 0ustar iuliusiulius/* $Id: vector.c 9678 2014-09-06 07:18:09Z iulius $ * * Vector handling (counted lists of char *'s). * * A vector is a table for handling a list of strings with less overhead than * linked list. The intention is for vectors, once allocated, to be reused; * this saves on memory allocations once the array of char *'s reaches a * stable size. * * There are two types of vectors. Standard vectors copy strings when they're * inserted into the vector, whereas cvectors just accept pointers to external * strings to store. There are therefore two entry points for every vector * function, one for vectors and one for cvectors. * * Vectors require list of strings, not arbitrary binary data, and cannot * handle data elements containing nul characters. * * There's a whole bunch of code duplication here. This would be a lot * cleaner with C++ features (either inheritance or templates would probably * help). One could probably in some places just cast a cvector to a vector * and perform the same operations, but I'm leery of doing that as I'm not * sure if it's a violation of the C type aliasing rules. * * The canonical version of this file is maintained in the rra-c-util package, * which can be found at . * * Written by Russ Allbery * * The authors hereby relinquish any claim to any copyright that they may have * in this work, whether granted under contract or by operation of law or * international treaty, and hereby commit to the public, at large, that they * shall not, at any time in the future, seek to enforce any copyright in this * work against any person or entity, or prevent any person or entity from * copying, publishing, distributing or creating derivative works of this * work. */ #include "config.h" #include "clibrary.h" #include #include "inn/vector.h" #include "inn/xmalloc.h" /* * Allocate a new, empty vector. */ struct vector * vector_new(void) { struct vector *vector; vector = xcalloc(1, sizeof(struct vector)); vector->allocated = 1; vector->strings = xcalloc(1, sizeof(char *)); return vector; } struct cvector * cvector_new(void) { struct cvector *vector; vector = xcalloc(1, sizeof(struct cvector)); vector->allocated = 1; vector->strings = xcalloc(1, sizeof(const char *)); return vector; } /* * Resize a vector (using reallocarray to resize the table). Maintain a * minimum allocated size of 1 so that the strings data element is never NULL. * This simplifies other code. */ void vector_resize(struct vector *vector, size_t size) { size_t i; assert(vector != NULL); if (vector->count > size) { for (i = size; i < vector->count; i++) free(vector->strings[i]); vector->count = size; } if (size == 0) size = 1; vector->strings = xreallocarray(vector->strings, size, sizeof(char *)); vector->allocated = size; } void cvector_resize(struct cvector *vector, size_t size) { assert(vector != NULL); if (vector->count > size) vector->count = size; if (size == 0) size = 1; vector->strings = xreallocarray(vector->strings, size, sizeof(const char *)); vector->allocated = size; } /* * Add a new string to the vector, resizing the vector as necessary. The * vector is resized an element at a time; if a lot of resizes are expected, * vector_resize should be called explicitly with a more suitable size. */ void vector_add(struct vector *vector, const char *string) { size_t next = vector->count; assert(vector != NULL); if (vector->count == vector->allocated) vector_resize(vector, vector->allocated + 1); vector->strings[next] = xstrdup(string); vector->count++; } void cvector_add(struct cvector *vector, const char *string) { size_t next = vector->count; assert(vector != NULL); if (vector->count == vector->allocated) cvector_resize(vector, vector->allocated + 1); vector->strings[next] = string; vector->count++; } /* * Add a new string to the vector, copying at most length characters of the * string, resizing the vector as necessary the same as with vector_add. This * function is only available for vectors, not cvectors, since it requires the * duplication of the input string to be sure it's nul-terminated. */ void vector_addn(struct vector *vector, const char *string, size_t length) { size_t next = vector->count; assert(vector != NULL); if (vector->count == vector->allocated) vector_resize(vector, vector->allocated + 1); vector->strings[next] = xstrndup(string, length); vector->count++; } /* * Empty a vector but keep the allocated memory for the pointer table. */ void vector_clear(struct vector *vector) { size_t i; assert(vector != NULL); for (i = 0; i < vector->count; i++) free(vector->strings[i]); vector->count = 0; } void cvector_clear(struct cvector *vector) { assert(vector != NULL); vector->count = 0; } /* * Free a vector completely. */ void vector_free(struct vector *vector) { if (vector == NULL) return; vector_clear(vector); free(vector->strings); free(vector); } void cvector_free(struct cvector *vector) { if (vector == NULL) return; cvector_clear(vector); free(vector->strings); free(vector); } /* * Given a vector that we may be reusing, clear it out. If the argument is * NULL, allocate a new vector. Helper function for vector_split*. */ static struct vector * vector_reuse(struct vector *vector) { if (vector == NULL) return vector_new(); else { vector_clear(vector); return vector; } } static struct cvector * cvector_reuse(struct cvector *vector) { if (vector == NULL) return cvector_new(); else { cvector_clear(vector); return vector; } } /* * Given a string and a separator character, count the number of strings that * it will split into. */ static size_t split_count(const char *string, char separator) { const char *p; size_t count; if (*string == '\0') return 1; for (count = 1, p = string; *p != '\0'; p++) if (*p == separator) count++; return count; } /* * Given a string and a separator character, form a vector by splitting the * string at occurrences of that separator. Consecutive occurrences of the * character will result in empty strings added to the vector. Reuse the * provided vector if non-NULL. */ struct vector * vector_split(const char *string, char separator, struct vector *vector) { const char *p, *start; size_t i, count; /* If the vector argument isn't NULL, reuse it. */ vector = vector_reuse(vector); /* Do a first pass to size the vector. */ count = split_count(string, separator); if (vector->allocated < count) vector_resize(vector, count); /* Walk the string and create the new strings with xstrndup. */ for (start = string, p = string, i = 0; *p != '\0'; p++) if (*p == separator) { vector->strings[i++] = xstrndup(start, p - start); start = p + 1; } vector->strings[i++] = xstrndup(start, p - start); vector->count = i; return vector; } /* * Given a modifiable string and a separator character, form a cvector by * modifying the string in-place to add nuls at the separators and then * building a vector of pointers into the string. Reuse the provided vector * if non-NULL. */ struct cvector * cvector_split(char *string, char separator, struct cvector *vector) { char *p, *start; size_t i, count; /* If the vector argument isn't NULL, reuse it. */ vector = cvector_reuse(vector); /* Do a first pass to size the vector. */ count = split_count(string, separator); if (vector->allocated < count) cvector_resize(vector, count); /* * Walk the string and replace separators with nul, and store the pointers * to the start of each string segment. */ for (start = string, p = string, i = 0; *p; p++) if (*p == separator) { *p = '\0'; vector->strings[i++] = start; start = p + 1; } vector->strings[i++] = start; vector->count = i; return vector; } /* * Given a string and a set of separators expressed as a string, count the * number of strings that it will split into when splitting on those * separators. Unlike with split_count, multiple consecutive separator * characters will be treated the same as a single separator. */ static size_t split_multi_count(const char *string, const char *seps) { const char *p; size_t count; /* The empty string produces no substrings. */ if (*string == '\0') return 0; /* * Walk the string looking for the first separator not preceeded by * another separator (and ignore a separator at the start of the string). */ for (count = 1, p = string + 1; *p != '\0'; p++) if (strchr(seps, *p) != NULL && strchr(seps, p[-1]) == NULL) count++; /* * If the string ends in separators, we've overestimated the number of * strings by one. */ if (strchr(seps, p[-1]) != NULL) count--; return count; } /* * Given a string, split it at any of the provided separators to form a * vector, copying each string segment. Any number of consecutive separators * are considered a single separator. Reuse the provided vector if non-NULL. */ struct vector * vector_split_multi(const char *string, const char *seps, struct vector *vector) { const char *p, *start; size_t i, count; /* If the vector argument isn't NULL, reuse it. */ vector = vector_reuse(vector); /* Count the number of strings we'll create and size the vector. */ count = split_multi_count(string, seps); if (vector->allocated < count) vector_resize(vector, count); /* * Walk the string and look for separators. start tracks the * non-separator that starts a new string, so as long as start == p, we're * tracking a sequence of separators. */ for (start = string, p = string, i = 0; *p != '\0'; p++) if (strchr(seps, *p) != NULL) { if (start != p) vector->strings[i++] = xstrndup(start, p - start); start = p + 1; } if (start != p) vector->strings[i++] = xstrndup(start, p - start); vector->count = i; return vector; } /* * Given a string, split it at any of the provided separators to form a * vector, destructively modifying the string to nul-terminate each segment. * Any number of consecutive separators are considered a single separator. * Reuse the provided vector if non-NULL. */ struct cvector * cvector_split_multi(char *string, const char *seps, struct cvector *vector) { char *p, *start; size_t i, count; /* If the vector argument isn't NULL, reuse it. */ vector = cvector_reuse(vector); /* Count the number of strings we'll create and size the vector. */ count = split_multi_count(string, seps); if (vector->allocated < count) cvector_resize(vector, count); /* * Walk the string and look for separators, replacing the ones that * terminate a substring with a nul. start tracks the non-separator that * starts a new string, so as long as start == p, we're tracking a * sequence of separators. */ for (start = string, p = string, i = 0; *p != '\0'; p++) if (strchr(seps, *p) != NULL) { if (start != p) { *p = '\0'; vector->strings[i++] = start; } start = p + 1; } if (start != p) vector->strings[i++] = start; vector->count = i; return vector; } /* * Given a string, split it at whitespace to form a vector, copying each * string segment. Any number of consecutive whitespace characters are * considered a single separator. Reuse the provided vector if non-NULL. * This is just a special case of vector_split_multi. */ struct vector * vector_split_space(const char *string, struct vector *vector) { return vector_split_multi(string, " \t", vector); } /* * Given a string, split it at whitespace to form a vector, destructively * modifying the string to nul-terminate each segment. Any number of * consecutive whitespace characters are considered a single separator. Reuse * the provided vector if non-NULL. This is just a special case of * cvector_split_multi. */ struct cvector * cvector_split_space(char *string, struct cvector *vector) { return cvector_split_multi(string, " \t", vector); } /* * Given a vector and a separator string, allocate and build a new string * composed of all the strings in the vector separated from each other by the * separator string. Caller is responsible for freeing. */ char * vector_join(const struct vector *vector, const char *separator) { char *string; size_t i, size, seplen; /* If the vector is empty, this is trivial. */ assert(vector != NULL); if (vector->count == 0) return xstrdup(""); /* * Determine the total size of the resulting string. Be careful of * integer overflow while doing so. */ seplen = strlen(separator); for (size = 0, i = 0; i < vector->count; i++) { assert(SIZE_MAX - size >= strlen(vector->strings[i]) + seplen + 1); size += strlen(vector->strings[i]); } assert(SIZE_MAX - size >= (vector->count - 1) * seplen + 1); size += (vector->count - 1) * seplen + 1; /* Allocate the memory and build up the string using strlcat. */ string = xmalloc(size); strlcpy(string, vector->strings[0], size); for (i = 1; i < vector->count; i++) { strlcat(string, separator, size); strlcat(string, vector->strings[i], size); } return string; } char * cvector_join(const struct cvector *vector, const char *separator) { char *string; size_t i, size, seplen; /* If the vector is empty, this is trivial. */ assert(vector != NULL); if (vector->count == 0) return xstrdup(""); /* * Determine the total size of the resulting string. Be careful of * integer overflow while doing so. */ seplen = strlen(separator); for (size = 0, i = 0; i < vector->count; i++) { assert(SIZE_MAX - size >= strlen(vector->strings[i])); size += strlen(vector->strings[i]); } assert(SIZE_MAX - size >= (vector->count - 1) * seplen + 1); size += (vector->count - 1) * seplen + 1; /* Allocate the memory and build up the string using strlcat. */ string = xmalloc(size); strlcpy(string, vector->strings[0], size); for (i = 1; i < vector->count; i++) { strlcat(string, separator, size); strlcat(string, vector->strings[i], size); } return string; } /* * Given a vector and a path to a program, exec that program with the vector * as its arguments. This requires adding a NULL terminator to the vector * (which we do not add to count, so it will be invisible to other users of * the vector) and casting it appropriately. */ int vector_exec(const char *path, struct vector *vector) { assert(vector != NULL); if (vector->allocated == vector->count) vector_resize(vector, vector->count + 1); vector->strings[vector->count] = NULL; return execv(path, (char * const *) vector->strings); } int cvector_exec(const char *path, struct cvector *vector) { assert(vector != NULL); if (vector->allocated == vector->count) cvector_resize(vector, vector->count + 1); vector->strings[vector->count] = NULL; return execv(path, (char * const *) vector->strings); } inn-2.6.0/lib/asprintf.c0000644000175200017520000000430112575023702014456 0ustar iuliusiulius/* $Id: asprintf.c 9913 2015-07-07 16:31:52Z iulius $ * * Replacement for a missing asprintf and vasprintf. * * Provides the same functionality as the standard GNU library routines * asprintf and vasprintf for those platforms that don't have them. * * The canonical version of this file is maintained in the rra-c-util package, * which can be found at . * * Written by Russ Allbery * * The authors hereby relinquish any claim to any copyright that they may have * in this work, whether granted under contract or by operation of law or * international treaty, and hereby commit to the public, at large, that they * shall not, at any time in the future, seek to enforce any copyright in this * work against any person or entity, or prevent any person or entity from * copying, publishing, distributing or creating derivative works of this * work. */ #include "config.h" #include "portable/macros.h" #include "clibrary.h" #include /* * If we're running the test suite, rename the functions to avoid conflicts * with the system versions. */ #if TESTING # undef asprintf # undef vasprintf # define asprintf test_asprintf # define vasprintf test_vasprintf int test_asprintf(char **, const char *, ...) __attribute__((__format__(printf, 2, 3))); int test_vasprintf(char **, const char *, va_list) __attribute__((__format__(printf, 2, 0))); #endif int asprintf(char **strp, const char *fmt, ...) { va_list args; int status; va_start(args, fmt); status = vasprintf(strp, fmt, args); va_end(args); return status; } int vasprintf(char **strp, const char *fmt, va_list args) { va_list args_copy; int status, needed, oerrno; va_copy(args_copy, args); needed = vsnprintf(NULL, 0, fmt, args_copy); va_end(args_copy); if (needed < 0) { *strp = NULL; return needed; } *strp = malloc(needed + 1); if (*strp == NULL) return -1; status = vsnprintf(*strp, needed + 1, fmt, args); if (status >= 0) return status; else { oerrno = errno; free(*strp); *strp = NULL; errno = oerrno; return status; } } inn-2.6.0/lib/xfopena.c0000644000175200017520000000073512575023702014277 0ustar iuliusiulius/* $Id: xfopena.c 7585 2006-11-21 09:37:51Z eagle $ ** */ #include "config.h" #include "clibrary.h" #include #include "inn/libinn.h" /* ** Open a file in append mode. Since not all fopen's set the O_APPEND ** flag, we do it by hand. */ FILE *xfopena(const char *p) { int fd; /* We can't trust stdio to really use O_APPEND, so open, then fdopen. */ fd = open(p, O_WRONLY | O_APPEND | O_CREAT, 0666); return fd >= 0 ? fdopen(fd, "a") : NULL; } inn-2.6.0/lib/Makefile0000644000175200017520000011247712575023702014142 0ustar iuliusiulius## $Id: Makefile 9794 2015-03-17 20:49:15Z iulius $ include ../Makefile.global # This version number should be increased with every change to the library # source following the rules laid out in the libtool manual. This will also # force the file name of the shared library to change so that one can # recover from make update. We can't use .OLD extensions for the shared # library since ldconfig will think .OLD sorts after the regular library and # will point the binaries at the old library. LTVERSION = 3:0:0 top = .. CFLAGS = $(GCFLAGS) # The base library files that are always compiled and included. SOURCES = argparse.c buffer.c cleanfrom.c clientactive.c clientlib.c \ commands.c concat.c conffile.c confparse.c daemonize.c \ date.c dbz.c defdist.c dispatch.c fdflag.c fdlimit.c \ getfqdn.c getmodaddr.c hash.c hashtab.c headers.c hex.c \ innconf.c inndcomm.c list.c localopen.c lockfile.c \ makedir.c md5.c messageid.c messages.c mmap.c network.c \ network-innbind.c newsuser.c nntp.c numbers.c qio.c \ radix32.c readin.c \ remopen.c reservedfd.c resource.c sendarticle.c sendpass.c \ sequence.c timer.c tst.c uwildmat.c vector.c wire.c \ xfopena.c xmalloc.c xsignal.c xwrite.c # Sources for additional functions only built to replace missing system ones. EXTRA_SOURCES = alloca.c asprintf.c fseeko.c ftello.c getaddrinfo.c \ getnameinfo.c getpagesize.c inet_aton.c inet_ntoa.c \ inet_ntop.c memcmp.c mkstemp.c pread.c pwrite.c \ reallocarray.c setenv.c \ seteuid.c setproctitle.c snprintf.c strcasecmp.c strlcat.c \ strlcpy.c strspn.c strtok.c symlink.c OBJECTS = $(LIBOBJS) $(ALLOCA) $(SOURCES:.c=.o) LOBJECTS = $(OBJECTS:.o=.lo) .SUFFIXES: .lo all: libinn.$(EXTLIB) perl.o warnings: $(MAKE) COPT='$(WARNINGS)' all install: all $(LI_LPUB) libinn.$(EXTLIB) $D$(PATHLIB)/libinn.$(EXTLIB) bootstrap: clean clobber distclean maintclean: rm -f *.o *.lo libinn.la libinn.a rm -f profiled perl$(PROFSUFFIX).o libinn$(PROFSUFFIX).a rm -f libinn_pure_*.a .pure rm -rf .libs libinn.la: $(OBJECTS) $(LOBJECTS) $(LIBLD) $(LDFLAGS) -o $@ $(LOBJECTS) $(LIBS) \ -rpath $(PATHLIB) -version-info $(LTVERSION) libinn.a: $(OBJECTS) ar r $@ $(OBJECTS) $(RANLIB) libinn.a .c.o .c.lo: $(LIBCC) $(CFLAGS) -c $*.c perl.o: perl.c $(CC) $(CFLAGS) $(PERL_CPPFLAGS) $(LDFLAGS) -c perl.c ../include/inn/system.h: (cd ../include && $(MAKE)) ## Profiling. The rules are a bit brute-force, but good enough. profiled: libinn$(PROFSUFFIX).a perl$(PROFSUFFIX).o date >$@ libinn$(PROFSUFFIX).a perl$(PROFSUFFIX).o: $(OBJECTS) perl.o rm -f $(OBJECTS) $(MAKEPROFILING) libinn.a $(MAKEPROFILING) perl.o mv libinn.a libinn$(PROFSUFFIX).a mv perl.o perl$(PROFSUFFIX).o $(RANLIB) libinn$(PROFSUFFIX).a rm -f $(OBJECTS) ## Dependencies. Default list, below, is probably good enough. depend: Makefile $(SOURCES) $(EXTRA_SOURCES) perl.c ../include/inn/system.h $(MAKEDEPEND) '$(CFLAGS) $(PERL_CPPFLAGS)' $(SOURCES) \ $(EXTRA_SOURCES) perl.c # Special dependency to teach make to build the include directory properly. ../include/inn/defines.h: ../include/inn/system.h # DO NOT DELETE THIS LINE -- make depend depends on it. argparse.o: argparse.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/innconf.h \ ../include/inn/libinn.h ../include/inn/xmalloc.h ../include/inn/xwrite.h buffer.o: buffer.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/buffer.h \ ../include/inn/xmalloc.h cleanfrom.o: cleanfrom.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/libinn.h \ ../include/inn/xmalloc.h ../include/inn/xwrite.h clientactive.o: clientactive.c ../include/config.h \ ../include/inn/defines.h ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/innconf.h \ ../include/inn/libinn.h ../include/inn/xmalloc.h ../include/inn/xwrite.h \ ../include/inn/nntp.h ../include/inn/paths.h clientlib.o: clientlib.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/innconf.h \ ../include/inn/libinn.h ../include/inn/xmalloc.h ../include/inn/xwrite.h \ ../include/inn/nntp.h commands.o: commands.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/libinn.h \ ../include/inn/xmalloc.h ../include/inn/xwrite.h concat.o: concat.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h \ ../include/inn/libinn.h ../include/inn/xmalloc.h ../include/inn/xwrite.h conffile.o: conffile.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/conffile.h \ ../include/portable/macros.h ../include/inn/libinn.h \ ../include/inn/xmalloc.h ../include/inn/xwrite.h confparse.o: confparse.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/confparse.h \ ../include/inn/hashtab.h ../include/inn/messages.h \ ../include/inn/vector.h ../include/inn/libinn.h ../include/inn/xmalloc.h \ ../include/inn/xwrite.h daemonize.o: daemonize.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/messages.h \ ../include/inn/libinn.h ../include/inn/xmalloc.h ../include/inn/xwrite.h date.o: date.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/libinn.h \ ../include/inn/xmalloc.h ../include/inn/xwrite.h dbz.o: dbz.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/portable/mmap.h \ ../include/inn/dbz.h ../include/inn/libinn.h ../include/inn/xmalloc.h \ ../include/inn/xwrite.h ../include/inn/fdflag.h \ ../include/inn/portable-socket.h ../include/inn/portable-getaddrinfo.h \ ../include/inn/portable-getnameinfo.h ../include/inn/messages.h \ ../include/inn/innconf.h ../include/inn/mmap.h defdist.o: defdist.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/innconf.h \ ../include/inn/libinn.h ../include/inn/xmalloc.h ../include/inn/xwrite.h \ ../include/inn/paths.h dispatch.o: dispatch.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/dispatch.h \ ../include/inn/vector.h fdflag.o: fdflag.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/fdflag.h \ ../include/inn/portable-socket.h ../include/inn/portable-getaddrinfo.h \ ../include/inn/portable-getnameinfo.h fdlimit.o: fdlimit.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/libinn.h \ ../include/inn/xmalloc.h ../include/inn/xwrite.h getfqdn.o: getfqdn.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/libinn.h \ ../include/inn/xmalloc.h ../include/inn/xwrite.h ../include/inn/paths.h getmodaddr.o: getmodaddr.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/innconf.h \ ../include/inn/libinn.h ../include/inn/xmalloc.h ../include/inn/xwrite.h \ ../include/inn/nntp.h ../include/inn/paths.h hash.o: hash.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/md5.h \ ../include/inn/utility.h ../include/inn/libinn.h \ ../include/inn/xmalloc.h ../include/inn/xwrite.h hashtab.o: hashtab.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/hashtab.h \ ../include/inn/libinn.h ../include/inn/xmalloc.h ../include/inn/xwrite.h headers.o: headers.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/libinn.h \ ../include/inn/xmalloc.h ../include/inn/xwrite.h hex.o: hex.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/utility.h innconf.o: innconf.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/confparse.h \ ../include/inn/innconf.h ../include/inn/messages.h \ ../include/inn/vector.h ../include/inn/libinn.h ../include/inn/xmalloc.h \ ../include/inn/xwrite.h ../include/inn/paths.h inndcomm.o: inndcomm.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/portable/socket.h \ ../include/portable/macros.h ../include/portable/getaddrinfo.h \ ../include/portable/getnameinfo.h ../include/portable/socket-unix.h \ ../include/inn/innconf.h ../include/inn/inndcomm.h \ ../include/inn/libinn.h ../include/inn/xmalloc.h ../include/inn/xwrite.h \ ../include/inn/paths.h list.o: list.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/list.h localopen.o: localopen.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/innconf.h \ ../include/inn/libinn.h ../include/inn/xmalloc.h ../include/inn/xwrite.h \ ../include/inn/nntp.h ../include/inn/paths.h \ ../include/portable/socket-unix.h lockfile.o: lockfile.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/libinn.h \ ../include/inn/xmalloc.h ../include/inn/xwrite.h makedir.o: makedir.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/libinn.h \ ../include/inn/xmalloc.h ../include/inn/xwrite.h md5.o: md5.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/md5.h messageid.o: messageid.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/innconf.h \ ../include/inn/libinn.h ../include/inn/xmalloc.h ../include/inn/xwrite.h \ ../include/inn/nntp.h messages.o: messages.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/messages.h \ ../include/inn/xmalloc.h mmap.o: mmap.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/portable/mmap.h \ ../include/inn/messages.h ../include/inn/mmap.h network.o: network.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/portable/socket.h \ ../include/portable/macros.h ../include/portable/getaddrinfo.h \ ../include/portable/getnameinfo.h ../include/inn/fdflag.h \ ../include/inn/portable-socket.h ../include/inn/innconf.h \ ../include/inn/messages.h ../include/inn/network.h \ ../include/inn/xmalloc.h ../include/inn/xwrite.h network-innbind.o: network-innbind.c ../include/config.h \ ../include/inn/defines.h ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/portable/socket.h \ ../include/portable/macros.h ../include/portable/getaddrinfo.h \ ../include/portable/getnameinfo.h ../include/inn/innconf.h \ ../include/inn/libinn.h ../include/inn/xmalloc.h ../include/inn/xwrite.h \ ../include/inn/messages.h ../include/inn/network.h \ ../include/inn/portable-socket.h ../include/inn/network-innbind.h newsuser.o: newsuser.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/innconf.h \ ../include/inn/messages.h ../include/inn/newsuser.h nntp.o: nntp.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/portable/socket.h \ ../include/portable/macros.h ../include/portable/getaddrinfo.h \ ../include/portable/getnameinfo.h ../include/inn/buffer.h \ ../include/inn/innconf.h ../include/inn/network.h \ ../include/inn/portable-socket.h ../include/inn/nntp.h \ ../include/inn/vector.h ../include/inn/libinn.h ../include/inn/xmalloc.h \ ../include/inn/xwrite.h numbers.o: numbers.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/libinn.h \ ../include/inn/xmalloc.h ../include/inn/xwrite.h qio.o: qio.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/qio.h \ ../include/inn/libinn.h ../include/inn/xmalloc.h ../include/inn/xwrite.h radix32.o: radix32.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/libinn.h \ ../include/inn/xmalloc.h ../include/inn/xwrite.h readin.o: readin.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/libinn.h \ ../include/inn/xmalloc.h ../include/inn/xwrite.h remopen.o: remopen.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/portable/socket.h \ ../include/portable/macros.h ../include/portable/getaddrinfo.h \ ../include/portable/getnameinfo.h ../include/inn/innconf.h \ ../include/inn/libinn.h ../include/inn/xmalloc.h ../include/inn/xwrite.h \ ../include/inn/network.h ../include/inn/portable-socket.h \ ../include/inn/nntp.h reservedfd.o: reservedfd.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/libinn.h \ ../include/inn/xmalloc.h ../include/inn/xwrite.h resource.o: resource.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/libinn.h \ ../include/inn/xmalloc.h ../include/inn/xwrite.h sendarticle.o: sendarticle.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/libinn.h \ ../include/inn/xmalloc.h ../include/inn/xwrite.h ../include/inn/nntp.h sendpass.o: sendpass.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/innconf.h \ ../include/inn/libinn.h ../include/inn/xmalloc.h ../include/inn/xwrite.h \ ../include/inn/nntp.h ../include/inn/paths.h sequence.o: sequence.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/sequence.h timer.o: timer.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/messages.h \ ../include/inn/timer.h ../include/inn/libinn.h ../include/inn/xmalloc.h \ ../include/inn/xwrite.h tst.o: tst.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/tst.h \ ../include/inn/libinn.h ../include/inn/xmalloc.h ../include/inn/xwrite.h uwildmat.o: uwildmat.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/libinn.h \ ../include/inn/xmalloc.h ../include/inn/xwrite.h vector.o: vector.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/vector.h \ ../include/inn/xmalloc.h wire.o: wire.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/wire.h \ ../include/inn/libinn.h ../include/inn/xmalloc.h ../include/inn/xwrite.h xfopena.o: xfopena.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/libinn.h \ ../include/inn/xmalloc.h ../include/inn/xwrite.h xmalloc.o: xmalloc.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/messages.h \ ../include/inn/xmalloc.h xsignal.o: xsignal.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h \ ../include/inn/libinn.h ../include/inn/xmalloc.h ../include/inn/xwrite.h xwrite.o: xwrite.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/portable/uio.h \ ../include/inn/xwrite.h alloca.o: alloca.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h asprintf.o: asprintf.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h \ ../include/portable/macros.h ../include/clibrary.h ../include/config.h \ ../include/inn/macros.h ../include/portable/stdbool.h fseeko.o: fseeko.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ftello.o: ftello.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h getaddrinfo.o: getaddrinfo.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/portable/socket.h \ ../include/portable/macros.h ../include/portable/getaddrinfo.h \ ../include/portable/getnameinfo.h getnameinfo.o: getnameinfo.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h \ ../include/portable/macros.h ../include/portable/socket.h \ ../include/portable/getaddrinfo.h ../include/portable/getnameinfo.h \ ../include/clibrary.h ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h getpagesize.o: getpagesize.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h inet_aton.o: inet_aton.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/portable/socket.h \ ../include/portable/macros.h ../include/portable/getaddrinfo.h \ ../include/portable/getnameinfo.h inet_ntoa.o: inet_ntoa.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/portable/socket.h \ ../include/portable/macros.h ../include/portable/getaddrinfo.h \ ../include/portable/getnameinfo.h inet_ntop.o: inet_ntop.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/portable/socket.h \ ../include/portable/macros.h ../include/portable/getaddrinfo.h \ ../include/portable/getnameinfo.h memcmp.o: memcmp.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h mkstemp.o: mkstemp.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h pread.o: pread.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h pwrite.o: pwrite.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h reallocarray.o: reallocarray.c ../include/config.h \ ../include/inn/defines.h ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h setenv.o: setenv.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h seteuid.o: seteuid.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h setproctitle.o: setproctitle.c ../include/config.h \ ../include/inn/defines.h ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/portable/setproctitle.h \ ../include/portable/macros.h ../include/inn/messages.h snprintf.o: snprintf.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h strcasecmp.o: strcasecmp.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h strlcat.o: strlcat.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h strlcpy.o: strlcpy.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h strspn.o: strspn.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h strtok.o: strtok.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h symlink.o: symlink.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h perl.o: perl.c ../include/config.h ../include/inn/defines.h \ ../include/inn/system.h ../include/inn/macros.h \ ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \ ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \ ../include/config.h ../include/inn/macros.h \ ../include/portable/stdbool.h ../include/inn/libinn.h \ ../include/inn/xmalloc.h ../include/inn/xwrite.h \ ../include/ppport.h ../include/innperl.h inn-2.6.0/lib/inet_ntoa.c0000644000175200017520000000332512575023702014615 0ustar iuliusiulius/* $Id: inet_ntoa.c 9759 2014-12-04 20:11:38Z iulius $ * * Replacement for a missing or broken inet_ntoa. * * Provides the same functionality as the standard library routine inet_ntoa * for those platforms that don't have it or where it doesn't work right (such * as on IRIX when using gcc to compile). inet_ntoa is not thread-safe since * it uses static storage (inet_ntop should be used instead when available). * * The canonical version of this file is maintained in the rra-c-util package, * which can be found at . * * Written by Russ Allbery * * The authors hereby relinquish any claim to any copyright that they may have * in this work, whether granted under contract or by operation of law or * international treaty, and hereby commit to the public, at large, that they * shall not, at any time in the future, seek to enforce any copyright in this * work against any person or entity, or prevent any person or entity from * copying, publishing, distributing or creating derivative works of this * work. */ #include "config.h" #include "clibrary.h" #include "portable/socket.h" /* * If we're running the test suite, rename inet_ntoa to avoid conflicts with * the system version. */ #if TESTING # undef inet_ntoa # define inet_ntoa test_inet_ntoa const char *test_inet_ntoa(const struct in_addr); #endif const char * inet_ntoa(const struct in_addr in) { static char buf[16]; const unsigned char *p; p = (const unsigned char *) &in.s_addr; sprintf(buf, "%u.%u.%u.%u", (unsigned int) (p[0] & 0xff), (unsigned int) (p[1] & 0xff), (unsigned int) (p[2] & 0xff), (unsigned int) (p[3] & 0xff)); return buf; } inn-2.6.0/lib/timer.c0000644000175200017520000002761712575023702013767 0ustar iuliusiulius/* $Id: timer.c 9941 2015-09-05 18:03:04Z eagle $ ** ** Timer functions, to gather profiling data. ** ** These functions log profiling information about where the server spends ** its time. While this doesn't provide as detailed of information as a ** profiling build would, it's much faster and simpler, and since it's fast ** enough to always leave on even on production servers, it can gather ** information *before* it's needed and show long-term trends. ** ** Functions that should have their time monitored need to call TMRstart(n) ** at the beginning of the segment of code and TMRstop(n) at the end. The ** time spent will be accumulated and added to the total for the counter n, ** where n should be one of the constants in timer.h or defined in your ** application. If you add new timers in the library code, add them to ** timer.h and also add a description to TMRsummary; if you add them in ** your application add them to your own description array. Also add them ** to innreport. ** ** Calls are sanity-checked to some degree and errors reported via ** warn/die, so all callers should have the proper warn and die handlers ** set up, if appropriate. ** ** Recursion is not allowed on a given timer. Setting multiple timers ** at once is fine (i.e., you may have a timer for the total time to write ** an article, how long the disk write takes, how long the history update ** takes, etc. which are components of the total article write time). If a ** timer is started while another timer is running, the new timer is ** considered to be a sub-timer of the running timer, and must be stopped ** before the parent timer is stopped. Note that the same timer number can ** be a sub-timer of more than one timer or a timer without a parent, and ** each of those counts will be reported separately. ** ** Note that this code is not thread-safe and in fact would need to be ** completely overhauled for a threaded server (since the idea of global ** timing statistics doesn't make as much sense when different tasks are ** done in different threads). */ #include "config.h" #include "clibrary.h" #include #ifdef HAVE_SYS_TIME_H # include #endif #include #include "inn/messages.h" #include "inn/timer.h" #include "inn/libinn.h" /* Timer values are stored in a series of trees. This allows use to use nested timers. Each nested timer node is linked to three of its neighbours to make lookups easy and fast. The current position in the graph is given by timer_current. As an optimization, since most timers aren't nested, timer_list holds an array of pointers to non-nested timers that's filled in as TMRstart is called so that the non-nested case remains O(1). That array is stored in timers. This is the "top level" of the timer trees; if timer_current is NULL, any timer that's started is found in this array. If timer_current isn't NULL, there's a running timer, and starting a new timer adds to that tree. Note that without the parent pointer, this is a tree. id is the identifier of the timer. start stores the time (relative to the last summary) at which TMRstart was last called for each timer. total is the total time accrued by that timer since the last summary. count is the number of times the timer has been stopped since the last summary. */ struct timer { unsigned int id; unsigned long start; unsigned long total; unsigned long count; struct timer *parent; struct timer *brother; struct timer *child; }; static struct timer **timers = NULL; static struct timer *timer_current = NULL; unsigned int timer_count = 0; /* Names for all of the timers. These must be given in the same order as the definition of the enum in timer.h. */ static const char *const timer_name[TMR_APPLICATION] = { "hishave", "hisgrep", "hiswrite", "hissync", }; /* ** Returns the current time as a double. This is not used by any of the ** other timer code, but is used by various programs right now to keep track ** of elapsed time. */ double TMRnow_double(void) { struct timeval tv; gettimeofday(&tv, NULL); return (tv.tv_sec + tv.tv_usec * 1.0e-6); } /* ** Returns the number of milliseconds since the base time. This gives ** better resolution than time, but the return value is a lot easier to ** work with than a struct timeval. If the argument is true, also reset ** the base time. */ static unsigned long TMRgettime(bool reset) { unsigned long now; long usec, msec; struct timeval tv; /* The time of the last summary, used as a base for times returned by TMRnow. Formerly, times were relative to the last call to TMRinit, which was only called once when innd was starting up; with that approach, times may overflow a 32-bit unsigned long about 50 days after the server starts up. While this may still work due to unsigned arithmetic, this approach is less confusing to follow. */ static struct timeval base; gettimeofday(&tv, NULL); now = (unsigned long)(tv.tv_sec - base.tv_sec) * 1000u; usec = tv.tv_usec - base.tv_usec; /* maybe negative */ msec = usec / 1000; /* still maybe negative */ now += (unsigned long)msec; if (reset) base = tv; return now; } /* ** Initialize the timer. Zero out even variables that would initially be ** zero so that this function can be called multiple times if wanted. */ void TMRinit(unsigned int count) { unsigned int i; /* TMRinit(0) disables all timers. */ TMRfree(); if (count != 0) { timers = xmalloc(count * sizeof(struct timer *)); for (i = 0; i < count; i++) timers[i] = NULL; TMRgettime(true); } timer_count = count; } /* ** Recursively destroy a timer node. */ static void TMRfreeone(struct timer *timer) { if (timer == NULL) return; if (timer->child != NULL) TMRfreeone(timer->child); if (timer->brother != NULL) TMRfreeone(timer->brother); free(timer); } /* ** Free all timers and the resources devoted to them. */ void TMRfree(void) { unsigned int i; if (timers != NULL) for (i = 0; i < timer_count; i++) TMRfreeone(timers[i]); free(timers); timers = NULL; timer_count = 0; } /* ** Allocate a new timer node. Takes the id and the parent pointer. */ static struct timer * TMRnew(unsigned int id, struct timer *parent) { struct timer *timer; timer = xmalloc(sizeof(struct timer)); timer->parent = parent; timer->brother = NULL; timer->child = NULL; timer->id = id; timer->start = 0; timer->total = 0; timer->count = 0; return timer; } /* ** Start a particular timer. If no timer is currently running, start one ** of the top-level timers in the timers array (creating a new one if ** needed). Otherwise, search for the timer among the children of the ** currently running timer, again creating a new timer if necessary. */ void TMRstart(unsigned int timer) { struct timer *search; if (timer_count == 0) { /* this should happen if innconf->timer == 0 */ return; } if (timer >= timer_count) { warn("timer %u is larger than the maximum timer %u, ignored", timer, timer_count - 1); return; } /* timers will be non-NULL if timer_count > 0. */ if (timer_current == NULL) { if (timers[timer] == NULL) timers[timer] = TMRnew(timer, NULL); timer_current = timers[timer]; } else { search = timer_current; /* Go to the "child" level and look for the good "brother"; the "brothers" are a simple linked list. */ if (search->child == NULL) { search->child = TMRnew(timer, search); timer_current = search->child; } else { search = search->child; while (search->id != timer && search->brother != NULL) search = search->brother; if (search->id != timer) { search->brother = TMRnew(timer, search->parent); timer_current = search->brother; } else { timer_current = search; } } } timer_current->start = TMRgettime(false); } /* ** Stop a particular timer, adding the total time to total and incrementing ** the count of times that timer has been invoked. */ void TMRstop(unsigned int timer) { if (timer_count == 0) { /* this should happen if innconf->timer == 0 */ return; } if (timer_current == NULL) warn("timer %u stopped when no timer was running", timer); else if (timer != timer_current->id) warn("timer %u stopped doesn't match running timer %u", timer, timer_current->id); else { timer_current->total += TMRgettime(false) - timer_current->start; timer_current->count++; timer_current = timer_current->parent; } } /* ** Return the current time in milliseconds since the last summary or the ** initialization of the timer. This is intended for use by the caller to ** determine when next to call TMRsummary. */ unsigned long TMRnow(void) { return TMRgettime(false); } /* ** Return the label associated with timer number id. Used internally ** to do the right thing when fetching from the timer_name or labels ** arrays */ static const char * TMRlabel(const char *const *labels, unsigned int id) { if (id >= TMR_APPLICATION) return labels[id - TMR_APPLICATION]; else return timer_name[id]; } /* ** Recursively summarize a single timer tree into the supplied buffer, ** returning the number of characters added to the buffer. */ static size_t TMRsumone(const char *const *labels, struct timer *timer, char *buf, size_t len) { struct timer *node; size_t off = 0; /* This results in "child/parent nn(nn)" instead of the arguably more intuitive "parent/child" but it's easy. Since we ensure sane snprintf semantics, it's safe to defer checking for overflow until after formatting all of the timer data. */ for (node = timer; node != NULL; node = node->parent) off += snprintf(buf + off, len - off, "%s/", TMRlabel(labels, node->id)); off--; off += snprintf(buf + off, len - off, " %lu(%lu) ", timer->total, timer->count); if (off == len) { warn("timer log too long while processing %s", TMRlabel(labels, timer->id)); return 0; } timer->total = 0; timer->count = 0; if (timer->child != NULL) off += TMRsumone(labels, timer->child, buf + off, len - off); if (timer->brother != NULL) off += TMRsumone(labels, timer->brother, buf + off, len - off); return off; } /* ** Summarize the current timer statistics, report them to syslog, and then ** reset them for the next polling interval. */ void TMRsummary(const char *prefix, const char *const *labels) { char *buf; unsigned int i; size_t len, off; /* To find the needed buffer size, note that a 64-bit unsigned number can be up to 20 digits long, so each timer can be 52 characters. We also allow another 27 characters for the introductory timestamp, plus some for the prefix. We may have timers recurring at multiple points in the structure, so this may not be long enough, but this is over-sized enough that it shouldn't be a problem. We use snprintf, so if the buffer isn't large enough it will just result in logged errors. */ len = 52 * timer_count + 27 + (prefix == NULL ? 0 : strlen(prefix)) + 1; buf = xmalloc(len); if (prefix == NULL) off = 0; else off = snprintf(buf, len, "%s ", prefix); off += snprintf(buf + off, len - off, "time %lu ", TMRgettime(true)); for (i = 0; i < timer_count; i++) if (timers[i] != NULL) off += TMRsumone(labels, timers[i], buf + off, len - off); notice("%s", buf); free(buf); } inn-2.6.0/lib/inndcomm.c0000644000175200017520000002342712575023702014446 0ustar iuliusiulius/* $Id: inndcomm.c 9761 2014-12-04 20:47:19Z iulius $ ** ** Library routines to let other programs control innd. */ #include "config.h" #include "clibrary.h" #include "portable/socket.h" #include #include #include #include #include #ifdef HAVE_SYS_TIME_H # include #endif #include /* Needed on AIX 4.1 to get fd_set and friends. */ #ifdef HAVE_SYS_SELECT_H # include #endif #ifdef HAVE_UNIX_DOMAIN_SOCKETS # include "portable/socket-unix.h" #endif #include "inn/innconf.h" #include "inn/inndcomm.h" #include "inn/libinn.h" #include "inn/paths.h" static char *ICCsockname = NULL; #ifdef HAVE_UNIX_DOMAIN_SOCKETS static struct sockaddr_un ICCserv; static struct sockaddr_un ICCclient; #endif static int ICCfd; static int ICCtimeout; const char *ICCfailure; /* ** Set the timeout. */ void ICCsettimeout(int i) { ICCtimeout = i; } /* ** Get ready to talk to the server. */ int ICCopen(void) { int mask, oerrno, fd; #ifdef HAVE_UNIX_DOMAIN_SOCKETS int size = 65535; #endif if (innconf == NULL) { if (!innconf_read(NULL)) { ICCfailure = "innconf"; return -1; } } /* Create a temporary name. mkstemp is complete overkill here and is used only because it's convenient. We don't use it properly, since we actually need to create a socket or named pipe, so there is a race condition here. It doesn't matter, since pathrun isn't world-writable (conceivably two processes could end up using the same temporary name at the same time, but the worst that will happen is that one process will delete the other's temporary socket). */ if (ICCsockname == NULL) ICCsockname = concatpath(innconf->pathrun, INN_PATH_TEMPSOCK); fd = mkstemp(ICCsockname); if (fd < 0) { ICCfailure = "mkstemp"; return -1; } close(fd); if (unlink(ICCsockname) < 0 && errno != ENOENT) { ICCfailure = "unlink"; return -1; } #ifdef HAVE_UNIX_DOMAIN_SOCKETS /* Make a socket and give it the name. */ if ((ICCfd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) { ICCfailure = "socket"; return -1; } /* Adjust the socket buffer size to accomodate large responses. Ignore failure; the message may fit anyway, and if not, we'll fail below. */ setsockopt(ICCfd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)); memset(&ICCclient, 0, sizeof ICCclient); ICCclient.sun_family = AF_UNIX; strlcpy(ICCclient.sun_path, ICCsockname, sizeof(ICCclient.sun_path)); mask = umask(0); if (bind(ICCfd, (struct sockaddr *) &ICCclient, SUN_LEN(&ICCclient)) < 0) { oerrno = errno; umask(mask); errno = oerrno; ICCfailure = "bind"; return -1; } umask(mask); /* Name the server's socket. */ memset(&ICCserv, 0, sizeof ICCserv); ICCserv.sun_family = AF_UNIX; strlcpy(ICCserv.sun_path, innconf->pathrun, sizeof(ICCserv.sun_path)); strlcat(ICCserv.sun_path, "/", sizeof(ICCserv.sun_path)); strlcat(ICCserv.sun_path, INN_PATH_NEWSCONTROL, sizeof(ICCserv.sun_path)); #else /* !HAVE_UNIX_DOMAIN_SOCKETS */ /* Make a named pipe and open it. */ mask = umask(0); if (mkfifo(ICCsockname, 0666) < 0) { oerrno = errno; umask(mask); errno = oerrno; ICCfailure = "mkfifo"; return -1; } umask(mask); if ((ICCfd = open(ICCsockname, O_RDWR)) < 0) { ICCfailure = "open"; return -1; } #endif /* !HAVE_UNIX_DOMAIN_SOCKETS */ ICCfailure = NULL; return 0; } /* ** Close down. */ int ICCclose(void) { int i; ICCfailure = NULL; i = 0; if (close(ICCfd) < 0) { ICCfailure = "close"; i = -1; } if (unlink(ICCsockname) < 0 && errno != ENOENT) { ICCfailure = "unlink"; i = -1; } return i; } /* ** Get the server's pid. */ static pid_t ICCserverpid(void) { pid_t pid; FILE *F; char *path; char buff[SMBUF]; pid = 1; path = concatpath(innconf->pathrun, INN_PATH_SERVERPID); F = fopen(path, "r"); free(path); if (F != NULL) { if (fgets(buff, sizeof buff, F) != NULL) pid = atol(buff); fclose(F); } return pid; } /* ** See if the server is still there. When in doubt, assume yes. Cache the ** PID since a rebooted server won't know about our pending message. */ static bool ICCserveralive(pid_t pid) { if (kill(pid, 0) > 0 || errno != ESRCH) return true; return false; } /* ** Send an arbitrary command to the server. ** ** There is a protocol version (one-byte) on the front of the message, ** followed by a two byte length count. The length includes the protocol ** byte and the length itself. This differs from the protocol in much ** earlier versions of INN. */ int ICCcommand(char cmd, const char *argv[], char **replyp) { char *buff; char *p; const char *q; char save; int i ; #ifndef HAVE_UNIX_DOMAIN_SOCKETS int fd; char *path; #endif int len; fd_set Rmask; struct timeval T; pid_t pid; ICC_MSGLENTYPE rlen; ICC_PROTOCOLTYPE protocol; size_t bufsiz = 64 * 1024 - 1; /* Is server there? */ pid = ICCserverpid(); if (!ICCserveralive(pid)) { ICCfailure = "dead server"; return -1; } /* Get the length of the buffer. */ buff = xmalloc(bufsiz); if (replyp) *replyp = NULL; /* Advance to leave space for length + protocol version info. */ buff += HEADER_SIZE; bufsiz -= HEADER_SIZE; /* Format the message. */ snprintf(buff, bufsiz, "%s%c%c", ICCsockname, SC_SEP, cmd); for (p = buff + strlen(buff), i = 0; (q = argv[i]) != NULL; i++) { *p++ = SC_SEP; *p = '\0'; strlcat(buff, q, bufsiz); p += strlen(q); } /* Send message. */ ICCfailure = NULL; len = p - buff + HEADER_SIZE; rlen = htons(len); /* now stick in the protocol version and the length. */ buff -= HEADER_SIZE; bufsiz += HEADER_SIZE; protocol = ICC_PROTOCOL_1; memcpy(buff, &protocol, sizeof(protocol)); memcpy(buff + sizeof(protocol), &rlen, sizeof(rlen)); #ifdef HAVE_UNIX_DOMAIN_SOCKETS if (sendto(ICCfd, buff, len, 0,(struct sockaddr *) &ICCserv, SUN_LEN(&ICCserv)) < 0) { free(buff); ICCfailure = "sendto"; return -1; } #else /* !HAVE_UNIX_DOMAIN_SOCKETS */ path = concatpath(innconf->pathrun, INN_PATH_NEWSCONTROL); fd = open(path, O_WRONLY); free(path); if (fd < 0) { free(buff); ICCfailure = "open"; return -1; } if (write(fd, buff, len) != len) { i = errno; free(buff); close(fd); errno = i; ICCfailure = "write"; return -1; } close(fd); #endif /* !HAVE_UNIX_DOMAIN_SOCKETS */ /* Possibly get a reply. */ switch (cmd) { default: if (ICCtimeout >= 0) break; /* FALLTHROUGH */ case SC_SHUTDOWN: case SC_XABORT: case SC_XEXEC: free(buff); return 0; } /* Wait for the reply. */ for ( ; ; ) { FD_ZERO(&Rmask); FD_SET(ICCfd, &Rmask); T.tv_sec = ICCtimeout ? ICCtimeout : 120; T.tv_usec = 0; i = select(ICCfd + 1, &Rmask, NULL, NULL, &T); if (i < 0) { free(buff); ICCfailure = "select"; return -1; } if (i > 0 && FD_ISSET(ICCfd, &Rmask)) /* Server reply is there; go handle it. */ break; /* No data -- if we timed out, return. */ if (ICCtimeout) { free(buff); errno = ETIMEDOUT; ICCfailure = "timeout"; return -1; } if (!ICCserveralive(pid)) { free(buff); ICCfailure = "dead server"; return -1; } } #ifdef HAVE_UNIX_DOMAIN_SOCKETS /* Read the reply. */ i = RECVorREAD(ICCfd, buff, bufsiz); if ((unsigned int) i < HEADER_SIZE) { free(buff); ICCfailure = "read"; return -1; } memcpy(&protocol, buff, sizeof(protocol)); memcpy(&rlen, buff + sizeof(protocol), sizeof(rlen)); rlen = ntohs(rlen); if (i != rlen) { free(buff); ICCfailure = "short read"; return -1; } if (protocol != ICC_PROTOCOL_1) { free(buff); ICCfailure = "protocol mismatch"; return -1; } memmove(buff, buff + HEADER_SIZE, rlen - HEADER_SIZE); i -= HEADER_SIZE; buff[i] = '\0'; #else /* !HAVE_UNIX_DOMAIN_SOCKETS */ i = RECVorREAD(ICCfd, buff, HEADER_SIZE); if (i != HEADER_SIZE) return -1; memcpy(&protocol, buff, sizeof(protocol)); memcpy(&rlen, buff + sizeof(protocol), sizeof(rlen)); rlen = ntohs(rlen) - HEADER_SIZE; if (rlen > bufsiz) { ICCfailure = "bad length"; return -1; } i = RECVorREAD(ICCfd, buff, rlen); if (i != rlen) { ICCfailure = "short read"; return -1; } buff[i] = '\0'; if (protocol != ICC_PROTOCOL_1) { ICCfailure = "protocol mismatch"; return -1; } #endif /* !HAVE_UNIX_DOMAIN_SOCKETS */ /* Parse the rest of the reply; expected to be like " */ i = 0; if (isdigit((unsigned char) buff[0])) { for (p = buff; *p && isdigit((unsigned char) *p); p++) continue; if (*p) { save = *p; *p = '\0'; i = atoi(buff); *p = save; } } if (replyp) *replyp = buff; else free(buff); return i; } /* ** Send a "cancel" command. */ int ICCcancel(const char *msgid) { const char *args[2]; args[0] = msgid; args[1] = NULL; return ICCcommand(SC_CANCEL, args, NULL); } /* ** Send a "go" command. */ int ICCgo(const char *why) { const char *args[2]; args[0] = why; args[1] = NULL; return ICCcommand(SC_GO, args, NULL); } /* ** Send a "pause" command. */ int ICCpause(const char *why) { const char *args[2]; args[0] = why; args[1] = NULL; return ICCcommand(SC_PAUSE, args, NULL); } /* ** Send a "reserve" command. */ int ICCreserve(const char *why) { const char *args[2]; args[0] = why; args[1] = NULL; return ICCcommand(SC_RESERVE, args, NULL); } inn-2.6.0/lib/xwrite.c0000644000175200017520000001664612575023702014171 0ustar iuliusiulius/* $Id: xwrite.c 9932 2015-08-28 19:23:27Z iulius $ * * write and writev replacements to handle partial writes. * * Usage: * * ssize_t xwrite(int fildes, const void *buf, size_t nbyte); * ssize_t xpwrite(int fildes, const void *buf, size_t nbyte, * off_t offset); * ssize_t xwritev(int fildes, const struct iovec *iov, int iovcnt); * * xwrite, xpwrite, and xwritev behave exactly like their C library * counterparts except that, if write or writev succeeds but returns a number * of bytes written less than the total bytes, the write is repeated picking * up where it left off until the full amount of the data is written. The * write is also repeated if it failed with EINTR. The write will be aborted * after 10 successive writes with no forward progress. * * Both functions return the number of bytes written on success or -1 on an * error, and will leave errno set to whatever the underlying system call set * it to. Note that it is possible for a write to fail after some data was * written, on the subsequent additional write; in that case, these functions * will return -1 and the number of bytes actually written will be lost. * * The canonical version of this file is maintained in the rra-c-util package, * which can be found at . * * Copyright 2008, 2013, 2014 * The Board of Trustees of the Leland Stanford Junior University * Copyright (c) 2004, 2005, 2006 * by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, * 2002, 2003 by The Internet Software Consortium and Rich Salz * * This code is derived from software contributed to the Internet Software * Consortium by Rich Salz. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #include "config.h" #include "clibrary.h" #include "portable/uio.h" #include #include #include "inn/xwrite.h" /* * If we're running the test suite, call testing versions of the write * functions. #undef the functions first since large file support may define * a macro pwrite (pointing to pwrite64) on some platforms (e.g. Solaris), * and it's possible the other functions may be similarly affected. */ #if TESTING # undef pwrite # undef write # undef writev # define pwrite fake_pwrite # define write fake_write # define writev fake_writev ssize_t fake_pwrite(int, const void *, size_t, off_t); ssize_t fake_write(int, const void *, size_t); ssize_t fake_writev(int, const struct iovec *, int); #endif ssize_t xwrite(int fd, const void *buffer, size_t size) { size_t total; ssize_t status; unsigned int count = 0; if (size == 0) return 0; /* Abort the write if we try ten times with no forward progress. */ for (total = 0; total < size; total += status) { if (++count > 10) break; status = write(fd, (const char *) buffer + total, size - total); if (status > 0) count = 0; if (status < 0) { if (errno != EINTR) break; status = 0; } } return (total < size) ? -1 : (ssize_t) total; } #ifndef _WIN32 ssize_t xpwrite(int fd, const void *buffer, size_t size, off_t offset) { size_t total; ssize_t status; unsigned int count = 0; if (size == 0) return 0; /* Abort the write if we try ten times with no forward progress. */ for (total = 0; total < size; total += status) { if (++count > 10) break; status = pwrite(fd, (const char *) buffer + total, size - total, offset + total); if (status > 0) count = 0; if (status < 0) { if (errno != EINTR) break; status = 0; } } return (total < size) ? -1 : (ssize_t) total; } #endif ssize_t xwritev(int fd, const struct iovec iov[], int iovcnt) { ssize_t total, status = 0; size_t left, offset; unsigned int iovleft, i, count; struct iovec *tmpiov; /* * Bounds-check the iovcnt argument. This is just for our safety. The * system will probably impose a lower limit on iovcnt, causing the later * writev to fail with an error we'll return. */ if (iovcnt == 0) return 0; if (iovcnt < 0 || (size_t) iovcnt > SIZE_MAX / sizeof(struct iovec)) { errno = EINVAL; return -1; } /* Get a count of the total number of bytes in the iov array. */ for (total = 0, i = 0; i < (unsigned int) iovcnt; i++) total += iov[i].iov_len; if (total == 0) return 0; /* * First, try just writing it all out. Most of the time this will succeed * and save us lots of work. Abort the write if we try ten times with no * forward progress. */ count = 0; do { if (++count > 10) break; status = writev(fd, iov, iovcnt); if (status > 0) count = 0; } while (status < 0 && errno == EINTR); if (status < 0) return -1; if (status == total) return total; /* * If we fell through to here, the first write partially succeeded. * Figure out how far through the iov array we got, and then duplicate the * rest of it so that we can modify it to reflect how much we manage to * write on successive tries. */ offset = status; left = total - offset; for (i = 0; offset >= (size_t) iov[i].iov_len; i++) offset -= iov[i].iov_len; iovleft = iovcnt - i; assert(iovleft > 0); tmpiov = calloc(iovleft, sizeof(struct iovec)); if (tmpiov == NULL) return -1; memcpy(tmpiov, iov + i, iovleft * sizeof(struct iovec)); /* * status now contains the offset into the first iovec struct in tmpiov. * Go into the write loop, trying to write out everything remaining at * each point. At the top of the loop, status will contain a count of * bytes written out at the beginning of the set of iovec structs. */ i = 0; do { if (++count > 10) break; /* Skip any leading data that has been written out. */ for (; offset >= (size_t) tmpiov[i].iov_len && iovleft > 0; i++) { offset -= tmpiov[i].iov_len; iovleft--; } tmpiov[i].iov_base = (char *) tmpiov[i].iov_base + offset; tmpiov[i].iov_len -= offset; /* Write out what's left and return success if it's all written. */ status = writev(fd, tmpiov + i, iovleft); if (status <= 0) offset = 0; else { offset = status; left -= offset; count = 0; } } while (left > 0 && (status >= 0 || errno == EINTR)); /* We're either done or got an error; if we're done, left is now 0. */ free(tmpiov); return (left == 0) ? total : -1; } inn-2.6.0/lib/strcasecmp.c0000644000175200017520000001001312575023702014771 0ustar iuliusiulius#include "config.h" #include "clibrary.h" /* $Revision: 6118 $ * * Copyright (c) 1987 Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that: (1) source distributions retain this entire copyright * notice and comment, and (2) distributions including binaries display * the following acknowledgement: ``This product includes software * developed by the University of California, Berkeley and its contributors'' * in the documentation or other materials provided with the distribution * and in all advertising materials mentioning features or use of this * software. Neither the name of the University nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #if defined(LIBC_SCCS) && !defined(lint) static const char sccsid[] = "@(#)strcasecmp.c 5.9 (Berkeley) 6/1/90"; #endif /* LIBC_SCCS and not lint */ typedef unsigned char u_char; /* * This array is designed for mapping upper and lower case letter * together for a case independent comparison. The mappings are * based upon ascii character sequences. */ static const u_char charmap[] = { '\000', '\001', '\002', '\003', '\004', '\005', '\006', '\007', '\010', '\011', '\012', '\013', '\014', '\015', '\016', '\017', '\020', '\021', '\022', '\023', '\024', '\025', '\026', '\027', '\030', '\031', '\032', '\033', '\034', '\035', '\036', '\037', '\040', '\041', '\042', '\043', '\044', '\045', '\046', '\047', '\050', '\051', '\052', '\053', '\054', '\055', '\056', '\057', '\060', '\061', '\062', '\063', '\064', '\065', '\066', '\067', '\070', '\071', '\072', '\073', '\074', '\075', '\076', '\077', '\100', '\141', '\142', '\143', '\144', '\145', '\146', '\147', '\150', '\151', '\152', '\153', '\154', '\155', '\156', '\157', '\160', '\161', '\162', '\163', '\164', '\165', '\166', '\167', '\170', '\171', '\172', '\133', '\134', '\135', '\136', '\137', '\140', '\141', '\142', '\143', '\144', '\145', '\146', '\147', '\150', '\151', '\152', '\153', '\154', '\155', '\156', '\157', '\160', '\161', '\162', '\163', '\164', '\165', '\166', '\167', '\170', '\171', '\172', '\173', '\174', '\175', '\176', '\177', '\200', '\201', '\202', '\203', '\204', '\205', '\206', '\207', '\210', '\211', '\212', '\213', '\214', '\215', '\216', '\217', '\220', '\221', '\222', '\223', '\224', '\225', '\226', '\227', '\230', '\231', '\232', '\233', '\234', '\235', '\236', '\237', '\240', '\241', '\242', '\243', '\244', '\245', '\246', '\247', '\250', '\251', '\252', '\253', '\254', '\255', '\256', '\257', '\260', '\261', '\262', '\263', '\264', '\265', '\266', '\267', '\270', '\271', '\272', '\273', '\274', '\275', '\276', '\277', '\300', '\301', '\302', '\303', '\304', '\305', '\306', '\307', '\310', '\311', '\312', '\313', '\314', '\315', '\316', '\317', '\320', '\321', '\322', '\323', '\324', '\325', '\326', '\327', '\330', '\331', '\332', '\333', '\334', '\335', '\336', '\337', '\340', '\341', '\342', '\343', '\344', '\345', '\346', '\347', '\350', '\351', '\352', '\353', '\354', '\355', '\356', '\357', '\360', '\361', '\362', '\363', '\364', '\365', '\366', '\367', '\370', '\371', '\372', '\373', '\374', '\375', '\376', '\377', }; int strcasecmp(s1, s2) const char *s1, *s2; { const u_char *cm = charmap, *us1 = (const u_char *)s1, *us2 = (const u_char *)s2; while (cm[*us1] == cm[*us2++]) if (*us1++ == '\0') return (0); return (cm[*us1] - cm[*--us2]); } int strncasecmp(s1, s2, n) const char *s1, *s2; size_t n; { if (n != 0) { const u_char *cm = charmap, *us1 = (const u_char *)s1, *us2 = (const u_char *)s2; do { if (cm[*us1] != cm[*us2++]) return (cm[*us1] - cm[*--us2]); if (*us1++ == '\0') break; } while (--n != 0); } return (0); } inn-2.6.0/lib/sendpass.c0000644000175200017520000000527012575023702014456 0ustar iuliusiulius/* $Id: sendpass.c 9229 2011-07-07 11:43:41Z iulius $ ** */ #include "config.h" #include "clibrary.h" #include #include #include "inn/innconf.h" #include "inn/libinn.h" #include "inn/nntp.h" #include "inn/paths.h" /* ** Send authentication information to an NNTP server. */ int NNTPsendpassword(char *server, FILE *FromServer, FILE *ToServer) { FILE *F; char *p; char *path; char buff[SMBUF]; char input[SMBUF]; char *user; char *pass; char *style; int oerrno; bool authenticated; /* Default to innconf->server. If that's not set either, error out. Fake errno since some of our callers rely on it. */ if (server == NULL) server = innconf->server; if (server == NULL) { errno = EINVAL; return -1; } /* Open the password file; coarse check on errno, but good enough. */ path = concatpath(innconf->pathetc, INN_PATH_NNTPPASS); F = fopen(path, "r"); free(path); if (F == NULL) return errno == EPERM ? -1 : 0; /* Scan the file, skipping blank and comment lines. */ while (fgets(buff, sizeof buff, F) != NULL) { if ((p = strchr(buff, '\n')) != NULL) *p = '\0'; if (buff[0] == '\0' || buff[0] == '#') continue; /* Parse the line. */ if ((user = strchr(buff, ':')) == NULL) continue; *user++ = '\0'; if ((pass = strchr(user, ':')) == NULL) continue; *pass++ = '\0'; if ((style = strchr(pass, ':')) != NULL) { *style++ = '\0'; if (strcmp(style, "authinfo") != 0) { errno = E2BIG; break; } } if (strcasecmp(server, buff) != 0) continue; authenticated = false; if (*user) { /* Send the first part of the command, get a reply. */ fprintf(ToServer, "AUTHINFO USER %s\r\n", user); if (fflush(ToServer) == EOF || ferror(ToServer)) break; if (fgets(input, sizeof input, FromServer) == NULL) break; if (atoi(input) == NNTP_OK_AUTHINFO) authenticated = true; else if (atoi(input) != NNTP_CONT_AUTHINFO) break; } if (*pass && !authenticated) { /* Send the second part of the command, get a reply. */ fprintf(ToServer, "AUTHINFO PASS %s\r\n", pass); if (fflush(ToServer) == EOF || ferror(ToServer)) break; if (fgets(input, sizeof input, FromServer) == NULL || atoi(input) != NNTP_OK_AUTHINFO) break; } /* Authenticated. */ fclose(F); return 0; } /* End of file without finding a password, that's okay. */ if (feof(F)) { fclose(F); return 0; } /* Save errno, close the file, fail. */ oerrno = errno; fclose(F); errno = oerrno; return -1; } inn-2.6.0/lib/alloca.c0000644000175200017520000003275112575023702014075 0ustar iuliusiulius/* alloca.c -- allocate automatically reclaimed memory (Mostly) portable public-domain implementation -- D A Gwyn Very minor formatting and ANSI C modifications for INN. This implementation of the PWB library alloca function, which is used to allocate space off the run-time stack so that it is automatically reclaimed upon procedure exit, was inspired by discussions with J. Q. Johnson of Cornell. J.Otto Tennant contributed the Cray support. There are some preprocessor constants that can be defined when compiling for your specific system, for improved efficiency; however, the defaults should be okay. The general concept of this implementation is to keep track of all alloca-allocated blocks, and reclaim any that are found to be deeper in the stack than the current invocation. This heuristic does not reclaim storage as soon as it becomes invalid, but it will do so eventually. As a special case, alloca(0) reclaims storage without allocating any. It is a good idea to use alloca(0) in your main control loop, etc. to force garbage collection. */ #include "config.h" #include /* If compiling with GCC, this file's not needed. */ #ifndef alloca /* If your stack is a linked list of frames, you have to provide an "address metric" ADDRESS_FUNCTION macro. */ #ifdef CRAY long i00afunc (); # define ADDRESS_FUNCTION(arg) (char *) i00afunc (&(arg)) #else # define ADDRESS_FUNCTION(arg) &(arg) #endif #ifndef NULL # define NULL 0 #endif /* Define STACK_DIRECTION if you know the direction of stack growth for your system; otherwise it will be automatically deduced at run-time. STACK_DIRECTION > 0 => grows toward higher addresses STACK_DIRECTION < 0 => grows toward lower addresses STACK_DIRECTION = 0 => direction of growth unknown */ #ifndef STACK_DIRECTION # define STACK_DIRECTION 0 /* Direction unknown. */ #endif #if STACK_DIRECTION != 0 # define STACK_DIR STACK_DIRECTION /* Known at compile-time. */ #else /* STACK_DIRECTION == 0; need run-time code. */ static int stack_dir; /* 1 or -1 once known. */ # define STACK_DIR stack_dir static void find_stack_direction (void) { static char *addr = NULL; /* Address of first `dummy', once known. */ auto char dummy; /* To get stack address. */ if (addr == NULL) { /* Initial entry. */ addr = ADDRESS_FUNCTION (dummy); find_stack_direction (); /* Recurse once. */ } else { /* Second entry. */ if (ADDRESS_FUNCTION (dummy) > addr) stack_dir = 1; /* Stack grew upward. */ else stack_dir = -1; /* Stack grew downward. */ } } #endif /* STACK_DIRECTION == 0 */ /* An "alloca header" is used to: (a) chain together all alloca'ed blocks; (b) keep track of stack depth. It is very important that sizeof(header) agree with malloc alignment chunk size. The following default should work okay. */ #ifndef ALIGN_SIZE # define ALIGN_SIZE sizeof(double) #endif typedef union hdr { char align[ALIGN_SIZE]; /* To force sizeof(header). */ struct { union hdr *next; /* For chaining headers. */ char *deep; /* For stack depth measure. */ } h; } header; static header *last_alloca_header = NULL; /* -> last alloca header. */ /* Return a pointer to at least SIZE bytes of storage, which will be automatically reclaimed upon exit from the procedure that called alloca. Originally, this space was supposed to be taken from the current stack frame of the caller, but that method cannot be made to work for some implementations of C, for example under Gould's UTX/32. */ void * alloca (unsigned size) { auto char probe; /* Probes stack depth: */ register char *depth = ADDRESS_FUNCTION (probe); #if STACK_DIRECTION == 0 if (STACK_DIR == 0) /* Unknown growth direction. */ find_stack_direction (); #endif /* Reclaim garbage, defined as all alloca'd storage that was allocated from deeper in the stack than currently. */ { register header *hp; /* Traverses linked list. */ for (hp = last_alloca_header; hp != NULL;) if ((STACK_DIR > 0 && hp->h.deep > depth) || (STACK_DIR < 0 && hp->h.deep < depth)) { register header *np = hp->h.next; free (hp); /* Collect garbage. */ hp = np; /* -> next header. */ } else break; /* Rest are not deeper. */ last_alloca_header = hp; /* -> last valid storage. */ } if (size == 0) return NULL; /* No allocation required. */ /* Allocate combined header + user data storage. */ { register void * new = malloc (sizeof (header) + size); /* Address of header. */ ((header *) new)->h.next = last_alloca_header; ((header *) new)->h.deep = depth; last_alloca_header = (header *) new; /* User storage begins just after header. */ return (void *) ((char *) new + sizeof (header)); } } #ifdef CRAY #ifdef DEBUG_I00AFUNC #include #endif #ifndef CRAY_STACK #define CRAY_STACK #ifndef CRAY2 /* Stack structures for CRAY-1, CRAY X-MP, and CRAY Y-MP */ struct stack_control_header { long shgrow:32; /* Number of times stack has grown. */ long shaseg:32; /* Size of increments to stack. */ long shhwm:32; /* High water mark of stack. */ long shsize:32; /* Current size of stack (all segments). */ }; /* The stack segment linkage control information occurs at the high-address end of a stack segment. (The stack grows from low addresses to high addresses.) The initial part of the stack segment linkage control information is 0200 (octal) words. This provides for register storage for the routine which overflows the stack. */ struct stack_segment_linkage { long ss[0200]; /* 0200 overflow words. */ long sssize:32; /* Number of words in this segment. */ long ssbase:32; /* Offset to stack base. */ long:32; long sspseg:32; /* Offset to linkage control of previous segment of stack. */ long:32; long sstcpt:32; /* Pointer to task common address block. */ long sscsnm; /* Private control structure number for microtasking. */ long ssusr1; /* Reserved for user. */ long ssusr2; /* Reserved for user. */ long sstpid; /* Process ID for pid based multi-tasking. */ long ssgvup; /* Pointer to multitasking thread giveup. */ long sscray[7]; /* Reserved for Cray Research. */ long ssa0; long ssa1; long ssa2; long ssa3; long ssa4; long ssa5; long ssa6; long ssa7; long sss0; long sss1; long sss2; long sss3; long sss4; long sss5; long sss6; long sss7; }; #else /* CRAY2 */ /* The following structure defines the vector of words returned by the STKSTAT library routine. */ struct stk_stat { long now; /* Current total stack size. */ long maxc; /* Amount of contiguous space which would be required to satisfy the maximum stack demand to date. */ long high_water; /* Stack high water mark. */ long overflows; /* Number of stack overflow ($STKOFEN) calls. */ long hits; /* Number of internal buffer hits. */ long extends; /* Number of block extensions. */ long stko_mallocs; /* Block allocations by $STKOFEN. */ long underflows; /* Number of stack underflow calls ($STKRETN). */ long stko_free; /* Number of deallocations by $STKRETN. */ long stkm_free; /* Number of deallocations by $STKMRET. */ long segments; /* Current number of stack segments. */ long maxs; /* Maximum number of stack segments so far. */ long pad_size; /* Stack pad size. */ long current_address; /* Current stack segment address. */ long current_size; /* Current stack segment size. This number is actually corrupted by STKSTAT to include the fifteen word trailer area. */ long initial_address; /* Address of initial segment. */ long initial_size; /* Size of initial segment. */ }; /* The following structure describes the data structure which trails any stack segment. I think that the description in 'asdef' is out of date. I only describe the parts that I am sure about. */ struct stk_trailer { long this_address; /* Address of this block. */ long this_size; /* Size of this block (does not include this trailer). */ long unknown2; long unknown3; long link; /* Address of trailer block of previous segment. */ long unknown5; long unknown6; long unknown7; long unknown8; long unknown9; long unknown10; long unknown11; long unknown12; long unknown13; long unknown14; }; #endif /* CRAY2 */ #endif /* not CRAY_STACK */ #ifdef CRAY2 /* Determine a "stack measure" for an arbitrary ADDRESS. I doubt that "lint" will like this much. */ static long i00afunc (long *address) { struct stk_stat status; struct stk_trailer *trailer; long *block, size; long result = 0; /* We want to iterate through all of the segments. The first step is to get the stack status structure. We could do this more quickly and more directly, perhaps, by referencing the $LM00 common block, but I know that this works. */ STKSTAT (&status); /* Set up the iteration. */ trailer = (struct stk_trailer *) (status.current_address + status.current_size - 15); /* There must be at least one stack segment. Therefore it is a fatal error if "trailer" is null. */ if (trailer == 0) abort (); /* Discard segments that do not contain our argument address. */ while (trailer != 0) { block = (long *) trailer->this_address; size = trailer->this_size; if (block == 0 || size == 0) abort (); trailer = (struct stk_trailer *) trailer->link; if ((block <= address) && (address < (block + size))) break; } /* Set the result to the offset in this segment and add the sizes of all predecessor segments. */ result = address - block; if (trailer == 0) { return result; } do { if (trailer->this_size <= 0) abort (); result += trailer->this_size; trailer = (struct stk_trailer *) trailer->link; } while (trailer != 0); /* We are done. Note that if you present a bogus address (one not in any segment), you will get a different number back, formed from subtracting the address of the first block. This is probably not what you want. */ return (result); } #else /* not CRAY2 */ /* Stack address function for a CRAY-1, CRAY X-MP, or CRAY Y-MP. Determine the number of the cell within the stack, given the address of the cell. The purpose of this routine is to linearize, in some sense, stack addresses for alloca. */ static long i00afunc (long address) { long stkl = 0; long size, pseg, this_segment, stack; long result = 0; struct stack_segment_linkage *ssptr; /* Register B67 contains the address of the end of the current stack segment. If you (as a subprogram) store your registers on the stack and find that you are past the contents of B67, you have overflowed the segment. B67 also points to the stack segment linkage control area, which is what we are really interested in. */ stkl = CRAY_STACKSEG_END (); ssptr = (struct stack_segment_linkage *) stkl; /* If one subtracts 'size' from the end of the segment, one has the address of the first word of the segment. If this is not the first segment, 'pseg' will be nonzero. */ pseg = ssptr->sspseg; size = ssptr->sssize; this_segment = stkl - size; /* It is possible that calling this routine itself caused a stack overflow. Discard stack segments which do not contain the target address. */ while (!(this_segment <= address && address <= stkl)) { #ifdef DEBUG_I00AFUNC fprintf (stderr, "%011o %011o %011o\n", this_segment, address, stkl); #endif if (pseg == 0) break; stkl = stkl - pseg; ssptr = (struct stack_segment_linkage *) stkl; size = ssptr->sssize; pseg = ssptr->sspseg; this_segment = stkl - size; } result = address - this_segment; /* If you subtract pseg from the current end of the stack, you get the address of the previous stack segment's end. This seems a little convoluted to me, but I'll bet you save a cycle somewhere. */ while (pseg != 0) { #ifdef DEBUG_I00AFUNC fprintf (stderr, "%011o %011o\n", pseg, size); #endif stkl = stkl - pseg; ssptr = (struct stack_segment_linkage *) stkl; size = ssptr->sssize; pseg = ssptr->sspseg; result += size; } return (result); } #endif /* not CRAY2 */ #endif /* CRAY */ #endif /* no alloca */ inn-2.6.0/lib/inet_ntop.c0000644000175200017520000000443312575023702014635 0ustar iuliusiulius/* $Id: inet_ntop.c 9759 2014-12-04 20:11:38Z iulius $ * * Replacement for a missing inet_ntop. * * Provides an implementation of inet_ntop that only supports IPv4 addresses * for hosts that are missing it. If you want IPv6 support, you need to have * a real inet_ntop function; this function is only provided so that code can * call inet_ntop unconditionally without needing to worry about whether the * host supports IPv6. * * The canonical version of this file is maintained in the rra-c-util package, * which can be found at . * * Written by Russ Allbery * * The authors hereby relinquish any claim to any copyright that they may have * in this work, whether granted under contract or by operation of law or * international treaty, and hereby commit to the public, at large, that they * shall not, at any time in the future, seek to enforce any copyright in this * work against any person or entity, or prevent any person or entity from * copying, publishing, distributing or creating derivative works of this * work. */ #include "config.h" #include "clibrary.h" #include "portable/socket.h" #include /* This may already be defined by the system headers. */ #ifndef INET_ADDRSTRLEN # define INET_ADDRSTRLEN 16 #endif /* Systems old enough to not support inet_ntop may not have this either. */ #ifndef EAFNOSUPPORT # define EAFNOSUPPORT EDOM #endif /* * If we're running the test suite, rename inet_ntop to avoid conflicts with * the system version. */ #if TESTING # undef inet_ntop # define inet_ntop test_inet_ntop const char *test_inet_ntop(int, const void *, char *, socklen_t); #endif const char * inet_ntop(int af, const void *src, char *dst, socklen_t size) { const unsigned char *p; int status; if (af != AF_INET) { socket_set_errno(EAFNOSUPPORT); return NULL; } if (size < INET_ADDRSTRLEN) { errno = ENOSPC; return NULL; } p = src; status = snprintf(dst, size, "%u.%u.%u.%u", (unsigned int) (p[0] & 0xff), (unsigned int) (p[1] & 0xff), (unsigned int) (p[2] & 0xff), (unsigned int) (p[3] & 0xff)); if (status < 0) return NULL; return dst; } inn-2.6.0/lib/hex.c0000644000175200017520000000500012575023702013411 0ustar iuliusiulius/* $Id: hex.c 7155 2005-04-10 06:27:34Z rra $ ** ** Convert data to or from an ASCII hex representation. ** ** Converts arbitrary binary data to or from a representation as an ASCII ** string of hex digits. Used primarily for converting MD5 hashes into a ** human-readable value. For backward-compatibility reasons, capital letters ** are used for hex digits > 9. */ #include "config.h" #include "clibrary.h" #include "inn/utility.h" /* ** Convert data to an ASCII hex representation. The result will be stored in ** buffer and nul-terminated. buflen is the length of the buffer, which must ** be at least 2 * length + 1 to hold the full representation. If it is not ** long enough, the result will be truncated but buffer will still be ** nul-terminated. */ void inn_encode_hex(const unsigned char *data, size_t length, char *buffer, size_t buflen) { static const char hex[] = "0123456789ABCDEF"; const unsigned char *p; unsigned int i; if (buflen == 0) return; for (p = data, i = 0; i < length && (i * 2) < (buflen - 1); p++, i++) { buffer[i * 2] = hex[(*p & 0xf0) >> 4]; buffer[(i * 2) + 1] = hex[(*p & 0x0f)]; } if (length * 2 > buflen - 1) buffer[buflen - 1] = '\0'; else buffer[length * 2] = '\0'; } /* ** Convert data from an ASCII hex representation. No adjustment is made for ** byte order. The conversion stops at the first character that is not a ** valid hex character. If there are an uneven number of valid input ** characters, the input is zero-padded at the *end* (so the string "F" is ** equivalent to the string "F0"). Lowercase hex digits are tolerated, even ** though inn_encode_hex doesn't produce them. buflen is the length of the ** output buffer and must be at least (input - 1) / 2. If it is too short ** to hold the full data, the result will be truncated. */ void inn_decode_hex(const char *data, unsigned char *buffer, size_t buflen) { const char *p; unsigned int i; unsigned char part; if (buflen == 0) return; memset(buffer, 0, buflen); for (p = data, i = 0; (i / 2) < buflen; p++, i++) { if (data[i] >= '0' && data[i] <= '9') part = data[i] - '0'; else if (data[i] >= 'A' && data[i] <= 'F') part = data[i] - 'A' + 10; else if (data[i] >= 'a' && data[i] <= 'f') part = data[i] - 'a' + 10; else return; if (i % 2 == 0) part <<= 4; buffer[i / 2] |= part; } } inn-2.6.0/lib/fdlimit.c0000644000175200017520000000520612575023702014265 0ustar iuliusiulius/* $Id: fdlimit.c 9754 2014-12-01 21:08:28Z iulius $ ** ** Portably determine or set the limit on open file descriptors. ** ** Pretty much all platforms these days have getrlimit and setrlimit, so ** prefer those, but for determining the current limit preserve the old ** portability (if we don't have getrlimit, try sysconf, then ** getdtablesize, then ulimit, and then try to find a hard-coded constant ** in and failing that fall back to the POSIX-guaranteed ** minimum of 20). ** ** For setting the limit, only setrlimit is supported; if it isn't ** available, return -1 always. We also refuse to set the limit to ** something higher than select can handle, checking against FD_SETSIZE. ** ** Note that on some versions of Linux (2.2.x reported), sysconf may return ** the wrong value for the maximum file descriptors. getrlimit is correct, ** so always prefer it. */ #include "config.h" #include "clibrary.h" #include #if HAVE_SYS_SELECT_H # include #endif /* FreeBSD 3.4 RELEASE needs before . */ #if HAVE_GETRLIMIT || HAVE_SETRLIMIT # if HAVE_SYS_TIME_H # include # endif # include # include #endif #include "inn/libinn.h" #if HAVE_SETRLIMIT && defined(RLIMIT_NOFILE) int setfdlimit(unsigned int limit) { struct rlimit rl; #ifdef FD_SETSIZE if (limit > FD_SETSIZE) { errno = EINVAL; return -1; } #endif rl.rlim_cur = 0; rl.rlim_max = 0; #if HAVE_GETRLIMIT if (getrlimit(RLIMIT_NOFILE, &rl) < 0) { rl.rlim_cur = 0; rl.rlim_max = 0; } #endif rl.rlim_cur = limit; if (limit > rl.rlim_max) rl.rlim_max = limit; return setrlimit(RLIMIT_NOFILE, &rl); } #else /* !(HAVE_SETRLIMIT && RLIMIT_NOFILE) */ int setfdlimit(unsigned int limit UNUSED) { /* Unimplemented system call is close enough. */ errno = ENOSYS; return -1; } #endif /* !(HAVE_SETRLIMIT && RLIMIT_NOFILE) */ #if HAVE_GETRLIMIT && defined(RLIMIT_NOFILE) int getfdlimit(void) { struct rlimit rl; if (getrlimit(RLIMIT_NOFILE, &rl) < 0) return -1; return rl.rlim_cur; } #elif HAVE_SYSCONF int getfdlimit(void) { return sysconf(_SC_OPEN_MAX); } #elif HAVE_GETDTABLESIZE int getfdlimit(void) { return getdtablesize(); } #elif HAVE_ULIMIT int getfdlimit(void) { # ifdef UL_GDESLIM return ulimit(UL_GDESLIM, 0); # else return ulimit(4, 0); # endif } #else /* no function mechanism available */ # if HAVE_LIMITS_H # include # endif # include int getfdcount(void) { # ifdef NOFILE return NOFILE; # else return 20; # endif } #endif inn-2.6.0/lib/newsuser.c0000644000175200017520000000510712575023702014510 0ustar iuliusiulius/* $Id: newsuser.c 9854 2015-05-14 13:12:36Z iulius $ * * Ensure running as "news" user/group. * * By Ivan Shmakov, 2007. * This code is in the public domain. * */ #include "config.h" #include "clibrary.h" #include #include #include "inn/innconf.h" #include "inn/messages.h" #include "inn/newsuser.h" /* * Resolve runasuser to a UID and runasgroup to a GID. */ int get_news_uid_gid(uid_t *uid, gid_t *gid, bool may_die) { /* NB: get_news_uid_gid() may be called before innconf_read(). */ const char *runasuser = innconf != NULL ? innconf->runasuser : RUNASUSER; const char *runasgroup = innconf != NULL ? innconf->runasgroup : RUNASGROUP; bool fail = false; struct passwd *pwd; struct group *grp; /* Resolve runasuser to a UID. */ if (!uid) { /* Do nothing. */ } else if ((pwd = getpwnam(runasuser)) != 0) { *uid = pwd->pw_uid; } else if (may_die) { die ("can't resolve %s to a UID" " (account doesn't exist?)", runasuser); } else { fail = true; } /* Resolve runasgroup to a GID. */ if (!gid) { /* Do nothing. */ } else if ((grp = getgrnam(runasgroup)) != 0) { *gid = grp->gr_gid; } else if (may_die) { die ("can't resolve %s to a GID" " (group doesn't exist?)", runasgroup); } else { fail = true; } return fail ? -1 : 0; } /* * Ensure running as runasuser user. */ void ensure_news_user(bool may_setuid) { uid_t uid; get_news_uid_gid(&uid, false, true); if (geteuid() == 0) { if (! may_setuid) { /* NB: mustn't be run as root, unless "may_setuid" is true. */ die("must be run as %s, not as root", innconf->runasuser); } if (setuid(uid) < 0) { sysdie("failed to setuid"); } } if (geteuid() != uid || getuid() != uid) { die("must be run as %s", innconf->runasuser); } } /* * Ensure running as runasgroup group. */ void ensure_news_grp(bool may_setgid) { gid_t gid; get_news_uid_gid(false, &gid, true); if (may_setgid && geteuid() == 0) { if (setgid(gid) < 0) { sysdie("failed to setgid"); } } if (getegid() != gid || getgid() != gid) { die ("must be run as %s group", innconf->runasgroup); } } /* * Ensure running as runasuser user and runasgroup group. */ void ensure_news_user_grp(bool may_setuid, bool may_setgid) { /* NB: changing the group first to lose root privileges last. */ ensure_news_grp(may_setgid); ensure_news_user(may_setuid); } inn-2.6.0/lib/readin.c0000644000175200017520000000275712575023702014107 0ustar iuliusiulius/* $Id: readin.c 7585 2006-11-21 09:37:51Z eagle $ ** */ #include "config.h" #include "clibrary.h" #include #include #include #include "inn/libinn.h" /* ** Read a big amount, looping until it is all done. Return true if ** successful. */ int xread(int fd, char *p, off_t i) { int count; for ( ; i; p += count, i -= count) { do { count = read(fd, p, i); } while (count == -1 && errno == EINTR); if (count <= 0) return -1; } return 0; } /* ** Read an already-open file into memory. */ char *ReadInDescriptor(int fd, struct stat *Sbp) { struct stat mystat; char *p; int oerrno; if (Sbp == NULL) Sbp = &mystat; /* Get the size, and enough memory. */ if (fstat(fd, Sbp) < 0) { oerrno = errno; close(fd); errno = oerrno; return NULL; } p = xmalloc(Sbp->st_size + 1); /* Slurp, slurp. */ if (xread(fd, p, Sbp->st_size) < 0) { oerrno = errno; free(p); close(fd); errno = oerrno; return NULL; } /* Terminate the string; terminate the routine. */ p[Sbp->st_size] = '\0'; return p; } /* ** Read a file into allocated memory. Optionally fill in the stat(2) data. ** Return a pointer to the file contents, or NULL on error. */ char *ReadInFile(const char *name, struct stat *Sbp) { char *p; int fd; if ((fd = open(name, O_RDONLY)) < 0) return NULL; p = ReadInDescriptor(fd, Sbp); close(fd); return p; } inn-2.6.0/lib/commands.c0000644000175200017520000000200012575023702014423 0ustar iuliusiulius/* $Id: commands.c 9019 2010-03-19 21:27:15Z iulius $ ** ** Routines for NNTP commands: manipulation and checks. */ #include "config.h" #include "clibrary.h" #include #include "inn/libinn.h" /* ** Return true if the given string is a keyword according to RFC 3977: ** ** A "keyword" MUST consist only of US-ASCII letters, digits, and the ** characters dot (".") and dash ("-") and MUST begin with a letter. ** Keywords MUST be at least three characters in length. */ bool IsValidKeyword(const char *string) { int len = 0; /* Not NULL. */ if (string == NULL) return false; /* Begins with a letter. */ if (!isalpha((unsigned char) string[0])) return false; for (; *string != '\0'; string++) { if (isalnum((unsigned char) *string) || *string == '.' || *string == '-') len++; else return false; } /* At least 3 octets in length. */ if (len > 2) return true; else return false; } inn-2.6.0/lib/defdist.c0000644000175200017520000000733012575023702014257 0ustar iuliusiulius/* $Id: defdist.c 8725 2009-11-08 18:29:06Z iulius $ ** */ #include "config.h" #include "clibrary.h" #include #include #include "inn/innconf.h" #include "inn/libinn.h" #include "inn/paths.h" typedef struct _DDENTRY { char *Pattern; char *Value; int Weight; } DDENTRY; struct _DDHANDLE { int Count; DDENTRY *Entries; DDENTRY *Current; }; typedef struct _DDHANDLE DDHANDLE; struct _DDHANDLE * DDstart(FILE *FromServer, FILE *ToServer) { DDHANDLE *h; DDENTRY *ep; FILE *F; char buff[BUFSIZ]; char *p; char *q; char *path; int i; int fd; char *name = NULL; /* Open the file. */ path = concatpath(innconf->pathetc, INN_PATH_DISTPATS); F = fopen(path, "r"); free(path); if (F == NULL) { /* Not available locally; try remotely. */ if (FromServer == NULL || ToServer == NULL) /* We're probably nnrpd running on the server and the * file isn't installed. Oh well. */ return NULL; name = concatpath(innconf->pathtmp, INN_PATH_TEMPACTIVE); fd = mkstemp(name); if (fd < 0) return NULL; close(fd); if ((F = CA_listopen(name, FromServer, ToServer, "DISTRIB.PATS")) == NULL) return NULL; } /* Count lines. */ for (i = 0; fgets(buff, sizeof buff, F) != NULL; i++) continue; /* Allocate space for the handle. */ if ((h = xmalloc(sizeof(DDHANDLE))) == NULL) { i = errno; fclose(F); if (name != NULL) unlink(name); errno = i; return NULL; } h->Count = 0; h->Current = NULL; if (i == 0) { return NULL ; } else if ((h->Entries = xmalloc(sizeof(DDENTRY) * i)) == NULL) { i = errno; free(h); fclose(F); if (name != NULL) unlink(name); errno = i; return NULL; } fseeko(F, 0, SEEK_SET); for (ep = h->Entries; fgets(buff, sizeof buff, F) != NULL; ) { if ((p = strchr(buff, '\n')) != NULL) *p = '\0'; if (buff[0] == '\0' || buff[0] == '#') continue; if ((p = strchr(buff, ':')) == NULL || (q = strchr(p + 1, ':')) == NULL) continue; *p++ = '\0'; ep->Weight = atoi(buff); ep->Pattern = xstrdup(p); q = strchr(ep->Pattern, ':'); *q++ = '\0'; ep->Value = q; ep++; } h->Count = ep - h->Entries; fclose(F); if (name != NULL) unlink(name); return h; } void DDcheck(DDHANDLE *h, char *group) { DDENTRY *ep; int i; int w; if (h == NULL || group == NULL) return; w = h->Current ? h->Current->Weight : -1; for (ep = h->Entries, i = h->Count; --i >= 0; ep++) if (ep->Weight > w && uwildmat(group, ep->Pattern)) { h->Current = ep; w = ep->Weight; } } char * DDend(DDHANDLE *h) { static char NIL[] = ""; char *p; int i; DDENTRY *ep; if (h == NULL) { p = NIL; return xstrdup(p); } if (h->Current == NULL) p = NIL; else p = h->Current->Value; p = xstrdup(p); for (ep = h->Entries, i = h->Count; --i >= 0; ep++) free(ep->Pattern); free(h->Entries); free(h); return p; } #if defined(TEST) int main(int ac, char *av[]) { struct _DDHANDLE *h; char *p; FILE *FromServer; FILE *ToServer; char buff[SMBUF]; if (NNTPremoteopen(NNTP_PORT, &FromServer, &ToServer, buff, sizeof(buff)) < 0) { if ((p = strchr(buff, '\n')) != NULL) *p = '\0'; if ((p = strchr(buff, '\r')) != NULL) *p = '\0'; if (buff[0]) fprintf(stderr, "%s\n", buff); else perror("Can't connect"); exit(1); } if ((h = DDstart(FromServer, ToServer)) == NULL) perror("Init failed, proceeding anyway"); while ((p = *++av) != NULL) DDcheck(h, p); p = DDend(h); printf(">%s<\n", p); exit(0); /* NOTREACHED */ } #endif /* defined(TEST) */ inn-2.6.0/lib/md5.c0000644000175200017520000005034212575023702013323 0ustar iuliusiulius/* $Id: md5.c 4154 2000-10-31 16:28:53Z kondou $ ** ** RSA Data Security, Inc. MD5 Message-Digest Algorithm ** ** The standard MD5 message digest routines, from RSA Data Security, Inc. ** by way of Landon Curt Noll, modified and integrated into INN by Clayton ** O'Neill and then simplified somewhat, converted to INN coding style, ** and commented by Russ Allbery. ** ** To form the message digest for a message M: ** (1) Initialize a context buffer md5_context using md5_init ** (2) Call md5_update on md5_context and M ** (3) Call md5_final on md5_context ** The message digest is now in md5_context->digest[0...15]. ** ** Alternately, just call md5_hash on M, passing it a buffer of at least ** 16 bytes into which to put the digest. md5_hash does the above ** internally for you and is the most convenient interface; the interface ** described above, however, is better when all of the data to hash isn't ** available neatly in a single buffer (such as hashing data aquired a ** block at a time). ** ** For information about MD5, see RFC 1321. ** ** LANDON CURT NOLL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, ** INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO ** EVENT SHALL LANDON CURT NOLL BE LIABLE FOR ANY SPECIAL, INDIRECT OR ** CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF ** USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR ** OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR ** PERFORMANCE OF THIS SOFTWARE. ** ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. ** ** License to copy and use this software is granted provided that it is ** identified as the "RSA Data Security, Inc. MD5 Message-Digest Algorithm" ** in all material mentioning or referencing this software or this ** function. ** ** License is also granted to make and use derivative works provided that ** such works are identified as "derived from the RSA Data Security, ** Inc. MD5 Message-Digest Algorithm" in all material mentioning or ** referencing the derived work. ** ** RSA Data Security, Inc. makes no representations concerning either the ** merchantability of this software or the suitability of this software for ** any particular purpose. It is provided "as is" without express or ** implied warranty of any kind. ** ** These notices must be retained in any copies of any part of this ** documentation and/or software. */ /* ** The actual mathematics and cryptography are at the bottom of this file, ** as those parts should be fully debugged and very infrequently changed. ** The core of actual work is done by md5_transform. The beginning of this ** file contains the infrastructure around the mathematics. */ #include "config.h" #include "clibrary.h" #include "inn/md5.h" /* Rotate a 32-bit value left, used by both the MD5 mathematics and by the routine below to byteswap data on big-endian machines. */ #define ROT(X, n) (((X) << (n)) | ((X) >> (32 - (n)))) /* Almost zero fill padding, used by md5_final. The 0x80 is part of the MD5 hash algorithm, from RFC 1321 section 3.1: Padding is performed as follows: a single "1" bit is appended to the message, and then "0" bits are appended so that the length in bits of the padded message becomes congruent to 448, modulo 512. In all, at least one bit and at most 512 bits are appended. Let the compiler zero the remainder of the array for us, guaranteed by ISO C99 6.7.8 paragraph 21. */ static const unsigned char padding[MD5_CHUNKSIZE] = { 0x80, 0 /* 0, ... */ }; /* Internal prototypes. */ static void md5_transform(uint32_t *, const uint32_t *); static void md5_update_block(struct md5_context *, const unsigned char *, size_t); /* MD5 requires that input data be treated as words in little-endian byte order. From RFC 1321 section 2: Similarly, a sequence of bytes can be interpreted as a sequence of 32-bit words, where each consecutive group of four bytes is interpreted as a word with the low-order (least significant) byte given first. Input data must therefore be byteswapped on big-endian machines, as must the 16-byte result digest. Since we have to make a copy of the incoming data anyway to ensure alignment for 4-byte access, we can byteswap it in place. */ #if !WORDS_BIGENDIAN # define decode(data) /* empty */ # define encode(data, out) memcpy((out), (data), MD5_DIGESTSIZE) #else /* The obvious way to do this is to pull single bytes at a time out of the input array and build words from them; this requires three shifts and three Boolean or operations, but it requires four memory reads per word unless the compiler is really smart. Since we can assume four-byte alignment for the input data, use this optimized routine from J. Touch, USC/ISI. This requires four shifts, two ands, and two ors, but only one memory read per word. */ #define swap(word) \ do { \ htmp = ROT((word), 16); \ ltmp = htmp >> 8; \ htmp &= 0x00ff00ff; \ ltmp &= 0x00ff00ff; \ htmp <<= 8; \ (word) = htmp | ltmp; \ } while (0) /* We process 16 words of data (one MD5 block) of data at a time, completely unrolling the loop manually since it should allow the compiler to take advantage of pipelining and parallel arithmetic units. */ static void decode(uint32_t *data) { uint32_t ltmp, htmp; swap(data[ 0]); swap(data[ 1]); swap(data[ 2]); swap(data[ 3]); swap(data[ 4]); swap(data[ 5]); swap(data[ 6]); swap(data[ 7]); swap(data[ 8]); swap(data[ 9]); swap(data[10]); swap(data[11]); swap(data[12]); swap(data[13]); swap(data[14]); swap(data[15]); } /* Used by md5_final to generate the final digest. The digest is not guaranteed to be aligned for 4-byte access but we are allowed to be destructive, so byteswap first and then copy the result over. */ static void encode(uint32_t *data, unsigned char *out) { uint32_t ltmp, htmp; swap(data[0]); swap(data[1]); swap(data[2]); swap(data[3]); memcpy(out, data, MD5_DIGESTSIZE); } #endif /* WORDS_BIGENDIAN */ /* ** That takes care of the preliminaries; now the real fun begins. The ** initialization function for a struct md5_context; set lengths to zero ** and initialize our working buffer with the magic constants (c.f. RFC ** 1321 section 3.3). */ void md5_init(struct md5_context *context) { context->buf[0] = 0x67452301U; context->buf[1] = 0xefcdab89U; context->buf[2] = 0x98badcfeU; context->buf[3] = 0x10325476U; context->count[0] = 0; context->count[1] = 0; context->datalen = 0; } /* ** The workhorse function, called for each chunk of data. Update the ** message-digest context to account for the presence of each of the ** characters data[0 .. count - 1] in the message whose digest is being ** computed. Accepts any length of data; all whole blocks are processed ** and the remaining data is buffered for later processing by either ** another call to md5_update or by md5_final. */ void md5_update(struct md5_context *context, const unsigned char *data, size_t count) { unsigned int datalen = context->datalen; unsigned int left; size_t high_count, low_count, used; /* Update the count of hashed bytes. The machinations here are to do the right thing on a platform where size_t > 32 bits without causing compiler warnings on platforms where it's 32 bits. RFC 1321 section 3.2 says: A 64-bit representation of b (the length of the message before the padding bits were added) is appended to the result of the previous step. In the unlikely event that b is greater than 2^64, then only the low-order 64 bits of b are used. so we can just ignore the higher bits of count if size_t is larger than 64 bits. (Think ahead!) If size_t is only 32 bits, the compiler should kill the whole if statement as dead code. */ if (sizeof(count) > 4) { high_count = count >> 31; context->count[1] += (high_count >> 1) & 0xffffffffU; } /* Now deal with count[0]. Add in the low 32 bits of count and increment count[1] if count[0] wraps. Isn't unsigned arithmetic cool? */ low_count = count & 0xffffffffU; context->count[0] += low_count; if (context->count[0] < low_count) context->count[1]++; /* See if we already have some data queued. If so, try to complete a block and then deal with it first. If the new data still doesn't fill the buffer, add it to the buffer and return. Otherwise, transform that block and update data and count to account for the data we've already used. */ if (datalen > 0) { left = MD5_CHUNKSIZE - datalen; if (left > count) { memcpy(context->in.byte + datalen, data, count); context->datalen += count; return; } else { memcpy(context->in.byte + datalen, data, left); decode(context->in.word); md5_transform(context->buf, context->in.word); data += left; count -= left; context->datalen = 0; } } /* If we have a block of data or more left, pass the rest off to md5_update_block to deal with all of the full blocks available. */ if (count >= MD5_CHUNKSIZE) { md5_update_block(context, data, count); used = (count / MD5_CHUNKSIZE) * MD5_CHUNKSIZE; data += used; count -= used; } /* If there's anything left, buffer it until we can complete a block or for md5_final to deal with. */ if (count > 0) { memcpy(context->in.byte, data, count); context->datalen = count; } } /* ** Update the message-digest context to account for the presence of each of ** the characters data[0 .. count - 1] in the message whose digest is being ** computed, except that we only deal with full blocks of data. The data ** is processed one block at a time, and partial blocks at the end are ** ignored (they're dealt with by md5_update, which calls this routine. ** ** Note that we always make a copy of the input data into an array of ** 4-byte values. If our input data were guaranteed to be aligned for ** 4-byte access, we could just use the input buffer directly on ** little-endian machines, and in fact this implementation used to do that. ** However, a requirement to align isn't always easily detectable, even by ** configure (an Alpha running Tru64 will appear to allow unaligned ** accesses, but will spew errors to the terminal if you do it). On top of ** that, even when it's allowed, unaligned access is quite frequently ** slower; we're about to do four reads of each word of the input data for ** the calculations, so doing one additional copy of the data up-front is ** probably worth it. */ static void md5_update_block(struct md5_context *context, const unsigned char *data, size_t count) { uint32_t in[MD5_CHUNKWORDS]; /* Process data in MD5_CHUNKSIZE blocks. */ while (count >= MD5_CHUNKSIZE) { memcpy(in, data, MD5_CHUNKSIZE); decode(in); md5_transform(context->buf, in); data += MD5_CHUNKSIZE; count -= MD5_CHUNKSIZE; } } /* ** Terminates the message-digest computation, accounting for any final ** trailing data and adding the message length to the hashed data, and ends ** with the desired message digest in context->digest[0...15]. */ void md5_final(struct md5_context *context) { unsigned int pad_needed, left; uint32_t count[2]; uint32_t *countloc; /* Save the count before appending the padding. */ count[0] = context->count[0]; count[1] = context->count[1]; /* Pad the final block of data. RFC 1321 section 3.1: The message is "padded" (extended) so that its length (in bits) is congruent to 448, modulo 512. That is, the message is extended so that it is just 64 bits shy of being a multiple of 512 bits long. Padding is always performed, even if the length of the message is already congruent to 448, modulo 512. The 64 bits (two words) left are for the 64-bit count of bits hashed. We'll need at most 64 bytes of padding; lucky that the padding array is exactly that size! */ left = context->datalen; pad_needed = (left < 64 - 8) ? (64 - 8 - left) : (128 - 8 - left); md5_update(context, padding, pad_needed); /* Append length in bits and transform. Note that we cheat slightly here to save a few operations on big-endian machines; the algorithm says that we should add the length, in little-endian byte order, to the last block of data. We'd then transform it into big-endian order on a big-endian machine. But the count is *already* in big-endian order on a big-endian machine, so effectively we'd be byteswapping it twice. Instead, add it to the block after doing byte swapping on the rest. Note that we've been counting *bytes*, but the algorithm demands the length in *bits*, so shift things around a bit. */ decode(context->in.word); countloc = &context->in.word[MD5_CHUNKWORDS - 2]; countloc[0] = count[0] << 3; countloc[1] = (count[1] << 3) | (count[0] >> 29); md5_transform(context->buf, context->in.word); /* Recover the final digest. Whoo-hoo, we're done! */ encode(context->buf, context->digest); } /* ** A convenience wrapper around md5_init, md5_update, and md5_final. Takes ** a pointer to a buffer of data, the length of the data, and a pointer to ** a buffer of at least 16 bytes into which to write the message digest. */ void md5_hash(const unsigned char *data, size_t length, unsigned char *digest) { struct md5_context context; md5_init(&context); md5_update(&context, data, length); md5_final(&context); memcpy(digest, context.digest, MD5_DIGESTSIZE); } /* ** Look out, here comes the math. ** ** There are no user-serviceable parts below this point unless you know ** quite a bit about MD5 or optimization of integer math. The only ** function remaining, down below, is md5_transform; the rest of this is ** setting up macros to make that function more readable. ** ** The F, G, H and I are basic MD5 functions. The following identity saves ** one boolean operation. ** ** F: (((x) & (y)) | (~(x) & (z))) == ((z) ^ ((x) & ((y) ^ (z)))) ** G: (((x) & (z)) | ((y) & ~(z))) == ((y) ^ ((z) & ((x) ^ (y)))) */ #define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) #define G(x, y, z) ((y) ^ ((z) & ((x) ^ (y)))) #define H(x, y, z) ((x) ^ (y) ^ (z)) #define I(x, y, z) ((y) ^ ((x) | (~z))) /* ** FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. Rotation ** is separate from addition to prevent recomputation. S?? are the shift ** values for each round. */ #define S11 7 #define S12 12 #define S13 17 #define S14 22 #define FF(a, b, c, d, x, s, ac) \ { \ (a) += F((b), (c), (d)) + (x) + (uint32_t) (ac); \ (a) = ROT((a), (s)); \ (a) += (b); \ } #define S21 5 #define S22 9 #define S23 14 #define S24 20 #define GG(a, b, c, d, x, s, ac) \ { \ (a) += G((b), (c), (d)) + (x) + (uint32_t) (ac); \ (a) = ROT((a), (s)); \ (a) += (b); \ } #define S31 4 #define S32 11 #define S33 16 #define S34 23 #define HH(a, b, c, d, x, s, ac) \ { \ (a) += H((b), (c), (d)) + (x) + (uint32_t) (ac); \ (a) = ROT((a), (s)); \ (a) += (b); \ } #define S41 6 #define S42 10 #define S43 15 #define S44 21 #define II(a, b, c, d, x, s, ac) \ { \ (a) += I((b), (c), (d)) + (x) + (uint32_t) (ac); \ (a) = ROT((a), (s)); \ (a) += (b); \ } /* ** Basic MD5 step. Transforms buf based on in. */ static void md5_transform(uint32_t *buf, const uint32_t *in) { uint32_t a = buf[0]; uint32_t b = buf[1]; uint32_t c = buf[2]; uint32_t d = buf[3]; /* Round 1 */ FF(a, b, c, d, in[ 0], S11, 3614090360UL); /* 1 */ FF(d, a, b, c, in[ 1], S12, 3905402710UL); /* 2 */ FF(c, d, a, b, in[ 2], S13, 606105819UL); /* 3 */ FF(b, c, d, a, in[ 3], S14, 3250441966UL); /* 4 */ FF(a, b, c, d, in[ 4], S11, 4118548399UL); /* 5 */ FF(d, a, b, c, in[ 5], S12, 1200080426UL); /* 6 */ FF(c, d, a, b, in[ 6], S13, 2821735955UL); /* 7 */ FF(b, c, d, a, in[ 7], S14, 4249261313UL); /* 8 */ FF(a, b, c, d, in[ 8], S11, 1770035416UL); /* 9 */ FF(d, a, b, c, in[ 9], S12, 2336552879UL); /* 10 */ FF(c, d, a, b, in[10], S13, 4294925233UL); /* 11 */ FF(b, c, d, a, in[11], S14, 2304563134UL); /* 12 */ FF(a, b, c, d, in[12], S11, 1804603682UL); /* 13 */ FF(d, a, b, c, in[13], S12, 4254626195UL); /* 14 */ FF(c, d, a, b, in[14], S13, 2792965006UL); /* 15 */ FF(b, c, d, a, in[15], S14, 1236535329UL); /* 16 */ /* Round 2 */ GG(a, b, c, d, in[ 1], S21, 4129170786UL); /* 17 */ GG(d, a, b, c, in[ 6], S22, 3225465664UL); /* 18 */ GG(c, d, a, b, in[11], S23, 643717713UL); /* 19 */ GG(b, c, d, a, in[ 0], S24, 3921069994UL); /* 20 */ GG(a, b, c, d, in[ 5], S21, 3593408605UL); /* 21 */ GG(d, a, b, c, in[10], S22, 38016083UL); /* 22 */ GG(c, d, a, b, in[15], S23, 3634488961UL); /* 23 */ GG(b, c, d, a, in[ 4], S24, 3889429448UL); /* 24 */ GG(a, b, c, d, in[ 9], S21, 568446438UL); /* 25 */ GG(d, a, b, c, in[14], S22, 3275163606UL); /* 26 */ GG(c, d, a, b, in[ 3], S23, 4107603335UL); /* 27 */ GG(b, c, d, a, in[ 8], S24, 1163531501UL); /* 28 */ GG(a, b, c, d, in[13], S21, 2850285829UL); /* 29 */ GG(d, a, b, c, in[ 2], S22, 4243563512UL); /* 30 */ GG(c, d, a, b, in[ 7], S23, 1735328473UL); /* 31 */ GG(b, c, d, a, in[12], S24, 2368359562UL); /* 32 */ /* Round 3 */ HH(a, b, c, d, in[ 5], S31, 4294588738UL); /* 33 */ HH(d, a, b, c, in[ 8], S32, 2272392833UL); /* 34 */ HH(c, d, a, b, in[11], S33, 1839030562UL); /* 35 */ HH(b, c, d, a, in[14], S34, 4259657740UL); /* 36 */ HH(a, b, c, d, in[ 1], S31, 2763975236UL); /* 37 */ HH(d, a, b, c, in[ 4], S32, 1272893353UL); /* 38 */ HH(c, d, a, b, in[ 7], S33, 4139469664UL); /* 39 */ HH(b, c, d, a, in[10], S34, 3200236656UL); /* 40 */ HH(a, b, c, d, in[13], S31, 681279174UL); /* 41 */ HH(d, a, b, c, in[ 0], S32, 3936430074UL); /* 42 */ HH(c, d, a, b, in[ 3], S33, 3572445317UL); /* 43 */ HH(b, c, d, a, in[ 6], S34, 76029189UL); /* 44 */ HH(a, b, c, d, in[ 9], S31, 3654602809UL); /* 45 */ HH(d, a, b, c, in[12], S32, 3873151461UL); /* 46 */ HH(c, d, a, b, in[15], S33, 530742520UL); /* 47 */ HH(b, c, d, a, in[ 2], S34, 3299628645UL); /* 48 */ /* Round 4 */ II(a, b, c, d, in[ 0], S41, 4096336452UL); /* 49 */ II(d, a, b, c, in[ 7], S42, 1126891415UL); /* 50 */ II(c, d, a, b, in[14], S43, 2878612391UL); /* 51 */ II(b, c, d, a, in[ 5], S44, 4237533241UL); /* 52 */ II(a, b, c, d, in[12], S41, 1700485571UL); /* 53 */ II(d, a, b, c, in[ 3], S42, 2399980690UL); /* 54 */ II(c, d, a, b, in[10], S43, 4293915773UL); /* 55 */ II(b, c, d, a, in[ 1], S44, 2240044497UL); /* 56 */ II(a, b, c, d, in[ 8], S41, 1873313359UL); /* 57 */ II(d, a, b, c, in[15], S42, 4264355552UL); /* 58 */ II(c, d, a, b, in[ 6], S43, 2734768916UL); /* 59 */ II(b, c, d, a, in[13], S44, 1309151649UL); /* 60 */ II(a, b, c, d, in[ 4], S41, 4149444226UL); /* 61 */ II(d, a, b, c, in[11], S42, 3174756917UL); /* 62 */ II(c, d, a, b, in[ 2], S43, 718787259UL); /* 63 */ II(b, c, d, a, in[ 9], S44, 3951481745UL); /* 64 */ buf[0] += a; buf[1] += b; buf[2] += c; buf[3] += d; } inn-2.6.0/lib/ftello.c0000644000175200017520000000147712575023702014130 0ustar iuliusiulius/* $Id: ftello.c 3681 2000-07-29 22:55:18Z rra $ ** ** Replacement for a missing ftello. ** ** ftello is a version of ftell that returns an off_t instead of a long. ** For large file support (and because it's a more logical interface), INN ** uses ftello unconditionally; if ftello isn't provided by a platform but ** fpos_t is compatible with off_t (as in BSDI), define it in terms of ** fgetpos. Otherwise, just call ftell (which won't work for files over ** 2GB). */ #include "config.h" #include "clibrary.h" #if HAVE_LARGE_FPOS_T off_t ftello(FILE *stream) { fpos_t fpos; if (fgetpos(stream, &fpos) < 0) { return -1; } else { return (off_t) fpos; } } #else /* !HAVE_LARGE_FPOS_T */ off_t ftello(FILE *stream) { return (off_t) ftell(stream); } #endif /* !HAVE_LARGE_FPOS_T */ inn-2.6.0/lib/pread.c0000644000175200017520000000303512575023702013726 0ustar iuliusiulius/* $Id: pread.c 9767 2014-12-07 21:13:43Z iulius $ ** ** Replacement for a missing pread. ** ** Written by Russ Allbery ** This work is hereby placed in the public domain by its author. ** ** Provides the same functionality as the standard library routine pread ** for those platforms that don't have it. Note that pread requires that ** the file pointer not move and without the library function, we can't ** copy that behavior; instead, we approximate it by moving the file ** pointer and then moving it back. This may break threaded programs. */ #include "config.h" #include "clibrary.h" #include /* If we're running the test suite, rename pread to avoid conflicts with the system version. #undef first because large file support may define a macro pread (pointing to pread64) on some platforms (e.g. Solaris). */ #if TESTING # undef pread # define pread test_pread ssize_t test_pread(int, void *, size_t, off_t); #endif ssize_t pread(int fd, void *buf, size_t nbyte, off_t offset) { off_t current; ssize_t nread; int oerrno; current = lseek(fd, 0, SEEK_CUR); if (current == (off_t) -1 || lseek(fd, offset, SEEK_SET) == (off_t) -1) return -1; nread = read(fd, buf, nbyte); /* Ignore errors in restoring the file position; this isn't ideal, but reporting a failed read when the read succeeded is worse. Make sure that errno, if set, is set by read and not lseek. */ oerrno = errno; lseek(fd, current, SEEK_SET); errno = oerrno; return nread; } inn-2.6.0/lib/clientactive.c0000644000175200017520000000625712575023702015316 0ustar iuliusiulius/* $Id: clientactive.c 9496 2013-06-25 17:40:26Z iulius $ ** */ #include "config.h" #include "clibrary.h" #include #include "inn/innconf.h" #include "inn/libinn.h" #include "inn/nntp.h" #include "inn/paths.h" static char *CApathname; static FILE *CAfp; /* ** Get a copy of the active file for a client host to use, locally or ** remotely. */ FILE * CAopen(FILE *FromServer, FILE *ToServer) { char *path; /* Use a local (or NFS-mounted) copy if available. Make sure we don't * try to delete it when we close it. */ path = concatpath(innconf->pathdb, INN_PATH_CLIENTACTIVE); CAfp = fopen(path, "r"); free(path); if (CAfp != NULL) { CApathname = NULL; return CAfp; } /* Use the active file from the server */ return CAlistopen(FromServer, ToServer, (char *)NULL); } /* ** Internal library routine. */ FILE * CA_listopen(char *pathname, FILE *FromServer, FILE *ToServer, const char *request) { char buff[BUFSIZ]; char expectedanswer[BUFSIZ]; char *p; int oerrno; FILE *F; F = fopen(pathname, "w"); if (F == NULL) return NULL; /* Send a LIST command and capture the output. */ if (request == NULL) fprintf(ToServer, "LIST\r\n"); else fprintf(ToServer, "LIST %s\r\n", request); fflush(ToServer); snprintf(expectedanswer, sizeof(expectedanswer), "%d", NNTP_OK_LIST); /* Get the server's reply to our command. */ if (fgets(buff, sizeof buff, FromServer) == NULL || strncmp(buff, expectedanswer, strlen(expectedanswer)) != 0) { oerrno = errno; /* Only call CAclose() if opened through CAopen(). */ if (strcmp(CApathname, pathname) == 0) CAclose(); errno = oerrno; fclose(F); return NULL; } /* Slurp up the rest of the response. */ while (fgets(buff, sizeof buff, FromServer) != NULL) { if ((p = strchr(buff, '\r')) != NULL) *p = '\0'; if ((p = strchr(buff, '\n')) != NULL) *p = '\0'; if (buff[0] == '.' && buff[1] == '\0') { if (ferror(F) || fflush(F) == EOF || fclose(F) == EOF) break; return fopen(pathname, "r"); } fprintf(F, "%s\n", buff); } /* Ran out of input before finding the terminator; quit. */ oerrno = errno; fclose(F); CAclose(); errno = oerrno; return NULL; } /* ** Use the NNTP list command to get a file from a server. Default is ** the active file, otherwise ask for whatever is in the request param. */ FILE * CAlistopen(FILE *FromServer, FILE *ToServer, const char *request) { int fd, oerrno; /* Gotta talk to the server -- see if we can. */ if (FromServer == NULL || ToServer == NULL) { errno = EBADF; return NULL; } CApathname = concatpath(innconf->pathtmp, INN_PATH_TEMPACTIVE); fd = mkstemp(CApathname); if (fd < 0) { oerrno = errno; free(CApathname); CApathname = 0; errno = oerrno; return NULL; } close(fd); return CAfp = CA_listopen(CApathname, FromServer, ToServer, request); } /* ** Close the file opened by CAopen or CAlistopen. */ void CAclose(void) { if (CAfp) { fclose(CAfp); CAfp = NULL; } if (CApathname != NULL) { unlink(CApathname); CApathname = NULL; } } inn-2.6.0/lib/symlink.c0000644000175200017520000000075212575023702014324 0ustar iuliusiulius/* $Id: symlink.c 6613 2004-01-12 00:49:30Z rra $ ** ** Replacement symlink that always fails for systems without it. ** ** This is basically a hack to make code in INN simpler and more ** straightforward. On systems where symlink isn't available, we link in ** this replacement that just always fails after setting errno to ENOSYS. */ #include "config.h" #include int symlink(const char *oldpath UNUSED, const char *newpath UNUSED) { errno = ENOSYS; return -1; } inn-2.6.0/lib/uwildmat.c0000644000175200017520000003555712575023702014477 0ustar iuliusiulius/* $Id: uwildmat.c 9767 2014-12-07 21:13:43Z iulius $ ** ** wildmat pattern matching with Unicode UTF-8 extensions. ** ** Do shell-style pattern matching for ?, \, [], and * characters. Might not ** be robust in face of malformed patterns; e.g., "foo[a-" could cause a ** segmentation violation. It is 8-bit clean. (Robustness hopefully fixed ** July 2000; all malformed patterns should now just fail to match anything.) ** ** Original by Rich $alz, mirror!rs, Wed Nov 26 19:03:17 EST 1986. ** Rich $alz is now . ** ** April, 1991: Replaced mutually-recursive calls with in-line code for the ** star character. ** ** Special thanks to Lars Mathiesen for the ABORT code. ** This can greatly speed up failing wildcard patterns. For example: ** ** pattern: -*-*-*-*-*-*-12-*-*-*-m-*-*-* ** text 1: -adobe-courier-bold-o-normal--12-120-75-75-m-70-iso8859-1 ** text 2: -adobe-courier-bold-o-normal--12-120-75-75-X-70-iso8859-1 ** ** Text 1 matches with 51 calls, while text 2 fails with 54 calls. Without ** the ABORT code, it takes 22310 calls to fail. Ugh. The following ** explanation is from Lars: ** ** The precondition that must be fulfilled is that DoMatch will consume at ** least one character in text. This is true if *p is neither '*' nor '\0'.) ** The last return has ABORT instead of false to avoid quadratic behaviour in ** cases like pattern "*a*b*c*d" with text "abcxxxxx". With false, each ** star-loop has to run to the end of the text; with ABORT only the last one ** does. ** ** Once the control of one instance of DoMatch enters the star-loop, that ** instance will return either true or ABORT, and any calling instance will ** therefore return immediately after (without calling recursively again). ** In effect, only one star-loop is ever active. It would be possible to ** modify the code to maintain this context explicitly, eliminating all ** recursive calls at the cost of some complication and loss of clarity (and ** the ABORT stuff seems to be unclear enough by itself). I think it would ** be unwise to try to get this into a released version unless you have a ** good test data base to try it out on. ** ** June, 1991: Robert Elz added minus and close bracket ** handling for character sets. ** ** July, 2000: Largely rewritten by Russ Allbery to add ** support for ',', '!', and optionally '@' to the core wildmat routine. ** Broke the character class matching into a separate function for clarity ** since it's infrequently used in practice, and added some simple lookahead ** to significantly decrease the recursive calls in the '*' matching code. ** Added support for UTF-8 as the default character set for any high-bit ** characters. ** ** For more information on UTF-8, see RFC 3629. ** ** Please note that this file is intentionally written so that conditionally ** executed expressions are on separate lines from the condition to ** facilitate analysis of the coverage of the test suite using purecov. ** Please preserve this. As of March 11, 2001, purecov reports that the ** accompanying test suite achieves 100% coverage of this file. */ #include "config.h" #include "clibrary.h" #include "inn/libinn.h" #define ABORT -1 /* Whether or not an octet looks like the start of a UTF-8 character. */ #define ISUTF8(c) (((c) & 0xc0) == 0xc0) /* ** Determine the length of a non-ASCII character in octets (for advancing ** pointers when skipping over characters). Takes a pointer to the start of ** the character and to the last octet of the string. If end is NULL, expect ** the string pointed to by start to be nul-terminated. If the character is ** malformed UTF-8, return 1 to treat it like an eight-bit local character. */ static int utf8_length(const unsigned char *start, const unsigned char *end) { unsigned char mask = 0x80; const unsigned char *p; int length = 0; int left; for (; mask > 0 && (*start & mask) == mask; mask >>= 1) length++; if (length < 2 || length > 6) return 1; if (end != NULL && (end - start + 1) < length) return 1; left = length - 1; for (p = start + 1; left > 0 && (*p & 0xc0) == 0x80; p++) left--; return (left == 0) ? length : 1; } /* ** Check whether a string contains only valid UTF-8 characters. */ bool is_valid_utf8(const char *text) { unsigned char mask; const unsigned char *p; int length; int left; for (p = (const unsigned char *)text; *p != '\0';) { mask = 0x80; length = 0; /* Find out the expected length of the character. */ for (; mask > 0 && (*p & mask) == mask; mask >>= 1) length++; p++; /* Valid ASCII. */ if (length == 0) continue; /* Invalid length. */ if (length < 2 || length > 6) return false; /* Check that each byte looks like 10xxxxxx, except for the first. */ left = length - 1; for (; left > 0 && (*p & 0xc0) == 0x80; p++) left--; if (left > 0) return false; } return true; } /* ** Convert a UTF-8 character to UCS-4. Takes a pointer to the start of the ** character and to the last octet of the string, and to a uint32_t into ** which to put the decoded UCS-4 value. If end is NULL, expect the string ** pointed to by start to be nul-terminated. Returns the number of octets in ** the UTF-8 encoding. If the UTF-8 character is malformed, set result to ** the decimal value of the first octet; this is wrong, but it will generally ** cause the rest of the wildmat matching to do the right thing for non-UTF-8 ** input. */ static int utf8_decode(const unsigned char *start, const unsigned char *end, uint32_t *result) { uint32_t value = 0; int length, i; const unsigned char *p = start; unsigned char mask; length = utf8_length(start, end); if (length < 2) { *result = *start; return 1; } mask = (1 << (7 - length)) - 1; value = *p & mask; p++; for (i = length - 1; i > 0; i--) { value = (value << 6) | (*p & 0x3f); p++; } *result = value; return length; } /* ** Match a character class against text, a UCS-4 character. start is a ** pointer to the first character of the character class, end a pointer to ** the last. Returns whether the class matches that character. */ static bool match_class(uint32_t text, const unsigned char *start, const unsigned char *end) { bool reversed, allowrange; const unsigned char *p = start; uint32_t first = 0; uint32_t last; /* Check for an inverted character class (starting with ^). If the character matches the character class, we return !reversed; that way, we return true if it's a regular character class and false if it's a reversed one. If the character doesn't match, we return reversed. */ reversed = (*p == '^'); if (reversed) p++; /* Walk through the character class until we reach the end or find a match, handling character ranges as we go. Only permit a range to start when allowrange is true; this allows - to be treated like a normal character as the first character of the class and catches malformed ranges like a-e-n. We treat the character at the beginning of a range as both a regular member of the class and the beginning of the range; this is harmless (although it means that malformed ranges like m-a will match m and nothing else). */ allowrange = false; while (p <= end) { if (allowrange && *p == '-' && p < end) { p++; p += utf8_decode(p, end, &last); if (text >= first && text <= last) return !reversed; allowrange = false; } else { p += utf8_decode(p, end, &first); if (text == first) return !reversed; allowrange = true; } } return reversed; } /* ** Match the text against the pattern between start and end. This is a ** single pattern; a leading ! or @ must already be taken care of, and ** commas must be dealt with outside of this routine. */ static int match_pattern(const unsigned char *text, const unsigned char *start, const unsigned char *end) { const unsigned char *q, *endclass; const unsigned char *p = start; bool ismeta; int matched, width; uint32_t c; for (; p <= end; p++) { if (!*text && *p != '*') return ABORT; switch (*p) { case '\\': if (!*++p) return ABORT; /* Fall through. */ default: if (*text++ != *p) return false; break; case '?': text += ISUTF8(*text) ? utf8_length(text, NULL) : 1; break; case '*': /* Consecutive stars are equivalent to one. Advance pattern to the character after the star. */ for (++p; *p == '*'; p++) ; /* A trailing star will match anything. */ if (p > end) return true; /* Basic algorithm: Recurse at each point where the * could possibly match. If the match succeeds or aborts, return immediately; otherwise, try the next position. Optimization: If the character after the * in the pattern isn't a metacharacter (the common case), then the * has to consume characters at least up to the next occurrence of that character in the text. Scan forward for those points rather than recursing at every possible point to save the extra function call overhead. */ ismeta = (*p == '[' || *p == '?' || *p == '\\'); while (*text) { width = ISUTF8(*text) ? utf8_length(text, NULL) : 1; if (ismeta) { matched = match_pattern(text, p, end); text += width; } else { while (*text && *text != *p) { text += width; width = ISUTF8(*text) ? utf8_length(text, NULL) : 1; } if (!*text) return ABORT; matched = match_pattern(++text, p + 1, end); } if (matched != false) return matched; } return ABORT; case '[': /* Find the end of the character class, making sure not to pick up a close bracket at the beginning of the class. */ p++; q = p + (*p == '^') + 1; if (q > end) return ABORT; endclass = memchr(q, ']', (size_t) (end - q + 1)); if (!endclass) return ABORT; /* Do the heavy lifting in another function for clarity, since character classes are an uncommon case. */ text += utf8_decode(text, NULL, &c); if (!match_class(c, p, endclass - 1)) return false; p = endclass; break; } } return (*text == '\0'); } /* ** Takes text and a wildmat expression; a wildmat expression is a ** comma-separated list of wildmat patterns, optionally preceded by ! to ** invert the sense of the expression. Returns UWILDMAT_MATCH if that ** expression matches the text, UWILDMAT_FAIL otherwise. If allowpoison is ** set, allow @ to introduce a poison expression (the same as !, but if it ** triggers the failed match the routine returns UWILDMAT_POISON instead). */ static enum uwildmat match_expression(const unsigned char *text, const unsigned char *start, bool allowpoison) { const unsigned char *end, *split; const unsigned char *p = start; bool reverse, escaped; bool match = false; bool poison = false; bool poisoned = false; /* Handle the empty expression separately, since otherwise end will be set to an invalid pointer. */ if (!*p) return !*text ? UWILDMAT_MATCH : UWILDMAT_FAIL; end = start + strlen((const char *) start) - 1; /* Main match loop. Find each comma that separates patterns, and attempt to match the text with each pattern in order. The last matching pattern determines whether the whole expression matches. */ for (; p <= end + 1; p = split + 1) { if (allowpoison) poison = (*p == '@'); reverse = (*p == '!') || poison; if (reverse) p++; /* Find the first unescaped comma, if any. If there is none, split will be one greater than end and point at the nul at the end of the string. */ for (escaped = false, split = p; split <= end; split++) { if (*split == '[') { split++; if (*split == ']') split++; while (split <= end && *split != ']') split++; } if (*split == ',' && !escaped) break; escaped = (*split == '\\') ? !escaped : false; } /* Optimization: If match == !reverse and poison == poisoned, this pattern can't change the result, so don't do any work. */ if (match == !reverse && poison == poisoned) continue; if (match_pattern(text, p, split - 1) == true) { poisoned = poison; match = !reverse; } } if (poisoned) return UWILDMAT_POISON; return match ? UWILDMAT_MATCH : UWILDMAT_FAIL; } /* ** User-level routine used for wildmats where @ should be treated as a ** regular character. */ bool uwildmat(const char *text, const char *pat) { const unsigned char *utext = (const unsigned char *) text; const unsigned char *upat = (const unsigned char *) pat; if (upat[0] == '*' && upat[1] == '\0') return true; else return (match_expression(utext, upat, false) == UWILDMAT_MATCH); } /* ** User-level routine used for wildmats that support poison matches. */ enum uwildmat uwildmat_poison(const char *text, const char *pat) { const unsigned char *utext = (const unsigned char *) text; const unsigned char *upat = (const unsigned char *) pat; if (upat[0] == '*' && upat[1] == '\0') return UWILDMAT_MATCH; else return match_expression(utext, upat, true); } /* ** User-level routine for simple expressions (neither , nor ! are special). */ bool uwildmat_simple(const char *text, const char *pat) { const unsigned char *utext = (const unsigned char *) text; const unsigned char *upat = (const unsigned char *) pat; size_t length; if (upat[0] == '*' && upat[1] == '\0') return true; else { length = strlen(pat); return (match_pattern(utext, upat, upat + length - 1) == true); } } inn-2.6.0/lib/snprintf.c0000644000175200017520000005540112575023702014502 0ustar iuliusiulius/* $Id: snprintf.c 9759 2014-12-04 20:11:38Z iulius $ * * Replacement for a missing snprintf or vsnprintf. * * The following implementation of snprintf was taken mostly verbatim from * ; it is the version of snprintf * used in Mutt. A possibly newer version is used in wget, found at * . * * Please do not reformat or otherwise change this file more than necessary so * that later merges with the original source are easy. Bug fixes and * improvements should be sent back to the original author. * * The canonical version of this file is maintained in the rra-c-util package, * which can be found at . */ /* * If we're running the test suite, rename snprintf and vsnprintf to avoid * conflicts with the system version. */ #if TESTING # undef snprintf # undef vsnprintf # define snprintf test_snprintf # define vsnprintf test_vsnprintf #endif /* * Copyright Patrick Powell 1995 * This code is based on code written by Patrick Powell (papowell@astart.com) * It may be used for any purpose as long as this notice remains intact * on all source code distributions */ /************************************************************** * Original: * Patrick Powell Tue Apr 11 09:48:21 PDT 1995 * A bombproof version of doprnt (dopr) included. * Sigh. This sort of thing is always nasty do deal with. Note that * the version here does not include floating point... * * snprintf() is used instead of sprintf() as it does limit checks * for string length. This covers a nasty loophole. * * The other functions are there to prevent NULL pointers from * causing nast effects. * * More Recently: * Brandon Long 9/15/96 for mutt 0.43 * This was ugly. It is still ugly. I opted out of floating point * numbers, but the formatter understands just about everything * from the normal C string format, at least as far as I can tell from * the Solaris 2.5 printf(3S) man page. * * Brandon Long 10/22/97 for mutt 0.87.1 * Ok, added some minimal floating point support, which means this * probably requires libm on most operating systems. Don't yet * support the exponent (e,E) and sigfig (g,G). Also, fmtint() * was pretty badly broken, it just wasn't being exercised in ways * which showed it, so that's been fixed. Also, formated the code * to mutt conventions, and removed dead code left over from the * original. Also, there is now a builtin-test, just compile with: * gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm * and run snprintf for results. * * Thomas Roessler 01/27/98 for mutt 0.89i * The PGP code was using unsigned hexadecimal formats. * Unfortunately, unsigned formats simply didn't work. * * Michael Elkins 03/05/98 for mutt 0.90.8 * The original code assumed that both snprintf() and vsnprintf() were * missing. Some systems only have snprintf() but not vsnprintf(), so * the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF. * * Andrew Tridgell (tridge@samba.org) Oct 1998 * fixed handling of %.0f * added test for HAVE_LONG_DOUBLE * * Russ Allbery 2000-08-26 * fixed return value to comply with C99 * fixed handling of snprintf(NULL, ...) * * Hrvoje Niksic 2000-11-04 * include for NULL. * added support for long long. * don't declare argument types to (v)snprintf if stdarg is not used. * * Hrvoje Niksic 2005-04-15 * use the PARAMS macro to handle prototypes. * write function definitions in the ansi2knr-friendly way. * if string precision is specified, don't read VALUE past it. * fix bug in fmtfp that caused 0.01 to be printed as 0.1. * don't include because none of it is used. * interpret precision as number of significant digits with %g * omit trailing decimal zeros with %g * **************************************************************/ #include "config.h" #include #include #include #ifndef NULL # define NULL 0 #endif /* varargs declarations: */ #include #define HAVE_STDARGS /* let's hope that works everywhere (mj) */ #define VA_LOCAL_DECL va_list ap #define VA_START(f) va_start(ap, f) #define VA_SHIFT(v,t) ; /* no-op for ANSI */ #define VA_END va_end(ap) /* Assume all compilers support long double, per Autoconf documentation. */ #define LDOUBLE long double #ifdef HAVE_LONG_LONG_INT # define LLONG long long #else # define LLONG long #endif int snprintf (char *str, size_t count, const char *fmt, ...); int vsnprintf (char *str, size_t count, const char *fmt, va_list arg); static int dopr (char *buffer, size_t maxlen, const char *format, va_list args); static int fmtstr (char *buffer, size_t *currlen, size_t maxlen, const char *value, int flags, int min, int max); static int fmtint (char *buffer, size_t *currlen, size_t maxlen, LLONG value, int base, int min, int max, int flags); static int fmtfp (char *buffer, size_t *currlen, size_t maxlen, LDOUBLE fvalue, int min, int max, int flags); static int dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c ); /* * dopr(): poor man's version of doprintf */ /* format read states */ #define DP_S_DEFAULT 0 #define DP_S_FLAGS 1 #define DP_S_MIN 2 #define DP_S_DOT 3 #define DP_S_MAX 4 #define DP_S_MOD 5 #define DP_S_MOD_L 6 #define DP_S_CONV 7 #define DP_S_DONE 8 /* format flags - Bits */ #define DP_F_MINUS (1 << 0) #define DP_F_PLUS (1 << 1) #define DP_F_SPACE (1 << 2) #define DP_F_NUM (1 << 3) #define DP_F_ZERO (1 << 4) #define DP_F_UP (1 << 5) #define DP_F_UNSIGNED (1 << 6) #define DP_F_FP_G (1 << 7) /* Conversion Flags */ #define DP_C_SHORT 1 #define DP_C_LONG 2 #define DP_C_LLONG 3 #define DP_C_LDOUBLE 4 #define char_to_int(p) (p - '0') #define MAX(p,q) ((p >= q) ? p : q) #define MIN(p,q) ((p <= q) ? p : q) static int dopr (char *buffer, size_t maxlen, const char *format, va_list args) { char ch; LLONG value; LDOUBLE fvalue; char *strvalue; int min; int max; int state; int flags; int cflags; int total; size_t currlen; state = DP_S_DEFAULT; currlen = flags = cflags = min = 0; max = -1; ch = *format++; total = 0; while (state != DP_S_DONE) { if (ch == '\0') state = DP_S_DONE; switch(state) { case DP_S_DEFAULT: if (ch == '%') state = DP_S_FLAGS; else total += dopr_outch (buffer, &currlen, maxlen, ch); ch = *format++; break; case DP_S_FLAGS: switch (ch) { case '-': flags |= DP_F_MINUS; ch = *format++; break; case '+': flags |= DP_F_PLUS; ch = *format++; break; case ' ': flags |= DP_F_SPACE; ch = *format++; break; case '#': flags |= DP_F_NUM; ch = *format++; break; case '0': flags |= DP_F_ZERO; ch = *format++; break; default: state = DP_S_MIN; break; } break; case DP_S_MIN: if ('0' <= ch && ch <= '9') { min = 10*min + char_to_int (ch); ch = *format++; } else if (ch == '*') { min = va_arg (args, int); ch = *format++; state = DP_S_DOT; } else state = DP_S_DOT; break; case DP_S_DOT: if (ch == '.') { state = DP_S_MAX; ch = *format++; } else state = DP_S_MOD; break; case DP_S_MAX: if ('0' <= ch && ch <= '9') { if (max < 0) max = 0; max = 10*max + char_to_int (ch); ch = *format++; } else if (ch == '*') { max = va_arg (args, int); ch = *format++; state = DP_S_MOD; } else state = DP_S_MOD; break; case DP_S_MOD: switch (ch) { case 'h': cflags = DP_C_SHORT; ch = *format++; break; case 'l': cflags = DP_C_LONG; ch = *format++; break; case 'L': cflags = DP_C_LDOUBLE; ch = *format++; break; default: break; } if (cflags != DP_C_LONG) state = DP_S_CONV; else state = DP_S_MOD_L; break; case DP_S_MOD_L: switch (ch) { case 'l': cflags = DP_C_LLONG; ch = *format++; break; default: break; } state = DP_S_CONV; break; case DP_S_CONV: switch (ch) { case 'd': case 'i': if (cflags == DP_C_SHORT) value = (short int) va_arg (args, int); else if (cflags == DP_C_LONG) value = va_arg (args, long int); else if (cflags == DP_C_LLONG) value = va_arg (args, LLONG); else value = va_arg (args, int); total += fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags); break; case 'o': flags |= DP_F_UNSIGNED; if (cflags == DP_C_SHORT) value = (unsigned short int) va_arg (args, unsigned int); else if (cflags == DP_C_LONG) value = va_arg (args, unsigned long int); else if (cflags == DP_C_LLONG) value = va_arg (args, unsigned LLONG); else value = va_arg (args, unsigned int); total += fmtint (buffer, &currlen, maxlen, value, 8, min, max, flags); break; case 'u': flags |= DP_F_UNSIGNED; if (cflags == DP_C_SHORT) value = (unsigned short int) va_arg (args, unsigned int); else if (cflags == DP_C_LONG) value = va_arg (args, unsigned long int); else if (cflags == DP_C_LLONG) value = va_arg (args, unsigned LLONG); else value = va_arg (args, unsigned int); total += fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags); break; case 'X': flags |= DP_F_UP; case 'x': flags |= DP_F_UNSIGNED; if (cflags == DP_C_SHORT) value = (unsigned short int) va_arg (args, unsigned int); else if (cflags == DP_C_LONG) value = va_arg (args, unsigned long int); else if (cflags == DP_C_LLONG) value = va_arg (args, unsigned LLONG); else value = va_arg (args, unsigned int); total += fmtint (buffer, &currlen, maxlen, value, 16, min, max, flags); break; case 'f': if (cflags == DP_C_LDOUBLE) fvalue = va_arg (args, LDOUBLE); else fvalue = va_arg (args, double); total += fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags); break; case 'E': flags |= DP_F_UP; case 'e': if (cflags == DP_C_LDOUBLE) fvalue = va_arg (args, LDOUBLE); else fvalue = va_arg (args, double); total += fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags); break; case 'G': flags |= DP_F_UP; case 'g': flags |= DP_F_FP_G; if (cflags == DP_C_LDOUBLE) fvalue = va_arg (args, LDOUBLE); else fvalue = va_arg (args, double); if (max == 0) /* C99 says: if precision [for %g] is zero, it is taken as one */ max = 1; total += fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags); break; case 'c': total += dopr_outch (buffer, &currlen, maxlen, va_arg (args, int)); break; case 's': strvalue = va_arg (args, char *); total += fmtstr (buffer, &currlen, maxlen, strvalue, flags, min, max); break; case 'p': strvalue = va_arg (args, void *); total += fmtint (buffer, &currlen, maxlen, (long) strvalue, 16, min, max, flags); break; case 'n': if (cflags == DP_C_SHORT) { short int *num; num = va_arg (args, short int *); *num = currlen; } else if (cflags == DP_C_LONG) { long int *num; num = va_arg (args, long int *); *num = currlen; } else if (cflags == DP_C_LLONG) { LLONG *num; num = va_arg (args, LLONG *); *num = currlen; } else { int *num; num = va_arg (args, int *); *num = currlen; } break; case '%': total += dopr_outch (buffer, &currlen, maxlen, ch); break; case 'w': /* not supported yet, treat as next char */ format++; break; default: /* Unknown, skip */ break; } ch = *format++; state = DP_S_DEFAULT; flags = cflags = min = 0; max = -1; break; case DP_S_DONE: break; default: /* hmm? */ break; /* some picky compilers need this */ } } if (buffer != NULL) { if (currlen < maxlen - 1) buffer[currlen] = '\0'; else buffer[maxlen - 1] = '\0'; } return total; } static int fmtstr (char *buffer, size_t *currlen, size_t maxlen, const char *value, int flags, int min, int max) { int padlen, strln; /* amount to pad */ int cnt = 0; int total = 0; if (value == 0) { value = "(null)"; } if (max < 0) strln = strlen (value); else /* When precision is specified, don't read VALUE past precision. */ /*strln = strnlen (value, max);*/ for (strln = 0; strln < max && value[strln]; ++strln); padlen = min - strln; if (padlen < 0) padlen = 0; if (flags & DP_F_MINUS) padlen = -padlen; /* Left Justify */ while (padlen > 0) { total += dopr_outch (buffer, currlen, maxlen, ' '); --padlen; } while (*value && ((max < 0) || (cnt < max))) { total += dopr_outch (buffer, currlen, maxlen, *value++); ++cnt; } while (padlen < 0) { total += dopr_outch (buffer, currlen, maxlen, ' '); ++padlen; } return total; } /* Have to handle DP_F_NUM (ie 0x and 0 alternates) */ static int fmtint (char *buffer, size_t *currlen, size_t maxlen, LLONG value, int base, int min, int max, int flags) { int signvalue = 0; unsigned LLONG uvalue; char convert[24]; unsigned int place = 0; int spadlen = 0; /* amount to space pad */ int zpadlen = 0; /* amount to zero pad */ const char *digits; int total = 0; if (max < 0) max = 0; uvalue = value; if(!(flags & DP_F_UNSIGNED)) { if( value < 0 ) { signvalue = '-'; uvalue = -value; } else if (flags & DP_F_PLUS) /* Do a sign (+/i) */ signvalue = '+'; else if (flags & DP_F_SPACE) signvalue = ' '; } if (flags & DP_F_UP) /* Should characters be upper case? */ digits = "0123456789ABCDEF"; else digits = "0123456789abcdef"; do { convert[place++] = digits[uvalue % (unsigned)base]; uvalue = (uvalue / (unsigned)base ); } while(uvalue && (place < sizeof (convert))); if (place == sizeof (convert)) place--; convert[place] = 0; zpadlen = max - place; spadlen = min - MAX ((unsigned int)max, place) - (signvalue ? 1 : 0); if (zpadlen < 0) zpadlen = 0; if (spadlen < 0) spadlen = 0; if (flags & DP_F_ZERO) { zpadlen = MAX(zpadlen, spadlen); spadlen = 0; } if (flags & DP_F_MINUS) spadlen = -spadlen; /* Left Justifty */ #ifdef DEBUG_SNPRINTF dprint (1, (debugfile, "zpad: %d, spad: %d, min: %d, max: %d, place: %d\n", zpadlen, spadlen, min, max, place)); #endif /* Spaces */ while (spadlen > 0) { total += dopr_outch (buffer, currlen, maxlen, ' '); --spadlen; } /* Sign */ if (signvalue) total += dopr_outch (buffer, currlen, maxlen, signvalue); /* Zeros */ if (zpadlen > 0) { while (zpadlen > 0) { total += dopr_outch (buffer, currlen, maxlen, '0'); --zpadlen; } } /* Digits */ while (place > 0) total += dopr_outch (buffer, currlen, maxlen, convert[--place]); /* Left Justified spaces */ while (spadlen < 0) { total += dopr_outch (buffer, currlen, maxlen, ' '); ++spadlen; } return total; } static LDOUBLE abs_val (LDOUBLE value) { LDOUBLE result = value; if (value < 0) result = -value; return result; } static LDOUBLE pow10_int (int exp) { LDOUBLE result = 1; while (exp) { result *= 10; exp--; } return result; } static LLONG round_int (LDOUBLE value) { LLONG intpart; intpart = value; value = value - intpart; if (value >= 0.5) intpart++; return intpart; } static int fmtfp (char *buffer, size_t *currlen, size_t maxlen, LDOUBLE fvalue, int min, int max, int flags) { int signvalue = 0; LDOUBLE ufvalue; char iconvert[24]; char fconvert[24]; size_t iplace = 0; size_t fplace = 0; int padlen = 0; /* amount to pad */ int zpadlen = 0; int total = 0; LLONG intpart; LLONG fracpart; LLONG mask10; int leadingfrac0s = 0; /* zeroes at the start of fractional part */ int omitzeros = 0; size_t omitcount = 0; /* * AIX manpage says the default is 0, but Solaris says the default * is 6, and sprintf on AIX defaults to 6 */ if (max < 0) max = 6; ufvalue = abs_val (fvalue); if (fvalue < 0) signvalue = '-'; else if (flags & DP_F_PLUS) /* Do a sign (+/i) */ signvalue = '+'; else if (flags & DP_F_SPACE) signvalue = ' '; #if 0 if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */ #endif intpart = ufvalue; /* With %g precision is the number of significant digits, which includes the digits in intpart. */ if (flags & DP_F_FP_G) { if (intpart != 0) { /* For each digit of INTPART, print one less fractional digit. */ LLONG temp = intpart; for (temp = intpart; temp != 0; temp /= 10) --max; if (max < 0) max = 0; } else { /* For each leading 0 in fractional part, print one more fractional digit. */ LDOUBLE temp; if (ufvalue > 0) for (temp = ufvalue; temp < 0.1; temp *= 10) ++max; } } /* C99: trailing zeros are removed from the fractional portion of the result unless the # flag is specified */ if ((flags & DP_F_FP_G) && !(flags & DP_F_NUM)) omitzeros = 1; #if SIZEOF_LONG_LONG > 0 # define MAX_DIGITS 18 /* grok more digits with long long */ #else # define MAX_DIGITS 9 /* just long */ #endif /* * Sorry, we only support several digits past the decimal because of * our conversion method */ if (max > MAX_DIGITS) max = MAX_DIGITS; /* Factor of 10 with the needed number of digits, e.g. 1000 for max==3 */ mask10 = pow10_int (max); /* We "cheat" by converting the fractional part to integer by * multiplying by a factor of 10 */ fracpart = round_int (mask10 * (ufvalue - intpart)); if (fracpart >= mask10) { intpart++; fracpart -= mask10; } else if (fracpart != 0) /* If fracpart has less digits than the 10* mask, we need to manually insert leading 0s. For example 2.01's fractional part requires one leading zero to distinguish it from 2.1. */ while (fracpart < mask10 / 10) { ++leadingfrac0s; mask10 /= 10; } #ifdef DEBUG_SNPRINTF dprint (1, (debugfile, "fmtfp: %f =? %d.%d\n", fvalue, intpart, fracpart)); #endif /* Convert integer part */ do { iconvert[iplace++] = '0' + intpart % 10; intpart = (intpart / 10); } while(intpart && (iplace < sizeof(iconvert))); if (iplace == sizeof(iconvert)) iplace--; iconvert[iplace] = 0; /* Convert fractional part */ do { fconvert[fplace++] = '0' + fracpart % 10; fracpart = (fracpart / 10); } while(fracpart && (fplace < sizeof(fconvert))); while (leadingfrac0s-- > 0 && fplace < sizeof(fconvert)) fconvert[fplace++] = '0'; if (fplace == sizeof(fconvert)) fplace--; fconvert[fplace] = 0; if (omitzeros) while (omitcount < fplace && fconvert[omitcount] == '0') ++omitcount; /* -1 for decimal point, another -1 if we are printing a sign */ padlen = min - iplace - (max - omitcount) - 1 - ((signvalue) ? 1 : 0); if (!omitzeros) zpadlen = max - fplace; if (zpadlen < 0) zpadlen = 0; if (padlen < 0) padlen = 0; if (flags & DP_F_MINUS) padlen = -padlen; /* Left Justifty */ if ((flags & DP_F_ZERO) && (padlen > 0)) { if (signvalue) { total += dopr_outch (buffer, currlen, maxlen, signvalue); --padlen; signvalue = 0; } while (padlen > 0) { total += dopr_outch (buffer, currlen, maxlen, '0'); --padlen; } } while (padlen > 0) { total += dopr_outch (buffer, currlen, maxlen, ' '); --padlen; } if (signvalue) total += dopr_outch (buffer, currlen, maxlen, signvalue); while (iplace > 0) total += dopr_outch (buffer, currlen, maxlen, iconvert[--iplace]); /* * Decimal point. This should probably use locale to find the correct * char to print out. */ if (max > 0 && (fplace > omitcount || zpadlen > 0)) { total += dopr_outch (buffer, currlen, maxlen, '.'); while (fplace > omitcount) total += dopr_outch (buffer, currlen, maxlen, fconvert[--fplace]); } while (zpadlen > 0) { total += dopr_outch (buffer, currlen, maxlen, '0'); --zpadlen; } while (padlen < 0) { total += dopr_outch (buffer, currlen, maxlen, ' '); ++padlen; } return total; } static int dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c) { if (*currlen + 1 < maxlen) buffer[(*currlen)++] = c; return 1; } int vsnprintf (char *str, size_t count, const char *fmt, va_list args) { if (str != NULL) str[0] = 0; return dopr(str, count, fmt, args); } /* VARARGS3 */ #ifdef HAVE_STDARGS int snprintf (char *str,size_t count,const char *fmt,...) #else int snprintf (va_alist) va_dcl #endif { #ifndef HAVE_STDARGS char *str; size_t count; char *fmt; #endif VA_LOCAL_DECL; int total; VA_START (fmt); VA_SHIFT (str, char *); VA_SHIFT (count, size_t ); VA_SHIFT (fmt, char *); total = vsnprintf(str, count, fmt, ap); VA_END; return total; } #ifdef TEST_SNPRINTF #ifndef LONG_STRING #define LONG_STRING 1024 #endif int main (void) { char buf1[LONG_STRING]; char buf2[LONG_STRING]; char *fp_fmt[] = { "%-1.5f", "%1.5f", "%123.9f", "%10.5f", "% 10.5f", "%+22.9f", "%+4.9f", "%01.3f", "%4f", "%3.1f", "%3.2f", "%.0f", "%.1f", NULL }; double fp_nums[] = { -1.5, 134.21, 91340.2, 341.1234, 0203.9, 0.96, 0.996, 0.9996, 1.996, 4.136, 0}; char *int_fmt[] = { "%-1.5d", "%1.5d", "%123.9d", "%5.5d", "%10.5d", "% 10.5d", "%+22.33d", "%01.3d", "%4d", NULL }; long int_nums[] = { -1, 134, 91340, 341, 0203, 0}; int x, y; int fail = 0; int num = 0; printf ("Testing snprintf format codes against system sprintf...\n"); for (x = 0; fp_fmt[x] != NULL ; x++) for (y = 0; fp_nums[y] != 0 ; y++) { snprintf (buf1, sizeof (buf1), fp_fmt[x], fp_nums[y]); sprintf (buf2, fp_fmt[x], fp_nums[y]); if (strcmp (buf1, buf2)) { printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf = %s\n", fp_fmt[x], buf1, buf2); fail++; } num++; } for (x = 0; int_fmt[x] != NULL ; x++) for (y = 0; int_nums[y] != 0 ; y++) { snprintf (buf1, sizeof (buf1), int_fmt[x], int_nums[y]); sprintf (buf2, int_fmt[x], int_nums[y]); if (strcmp (buf1, buf2)) { printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf = %s\n", int_fmt[x], buf1, buf2); fail++; } num++; } printf ("%d tests failed out of %d.\n", fail, num); } #endif /* SNPRINTF_TEST */ inn-2.6.0/lib/strtok.c0000644000175200017520000000424312575023702014163 0ustar iuliusiulius/* $Id: strtok.c 6118 2003-01-13 06:44:24Z rra $ ** ** This file has been modified to get it to compile more easily ** on pre-4.4BSD systems. Rich $alz, June 1991. */ #include "config.h" #include "clibrary.h" /* * Copyright (c) 1988 Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that: (1) source distributions retain this entire copyright * notice and comment, and (2) distributions including binaries display * the following acknowledgement: ``This product includes software * developed by the University of California, Berkeley and its contributors'' * in the documentation or other materials provided with the distribution * and in all advertising materials mentioning features or use of this * software. Neither the name of the University nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #if 0 #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)strtok.c 5.7 (Berkeley) 6/1/90"; #endif /* LIBC_SCCS and not lint */ #include #include #endif char * strtok(s, delim) char *s, *delim; { char *spanp; int c, sc; char *tok; static char *last; if (s == NULL && (s = last) == NULL) return (NULL); /* * Skip (span) leading delimiters (s += strspn(s, delim), sort of). */ cont: c = *s++; for (spanp = delim; (sc = *spanp++) != 0;) { if (c == sc) goto cont; } if (c == 0) { /* no non-delimiter characters */ last = NULL; return (NULL); } tok = s - 1; /* * Scan token (scan for delimiters: s += strcspn(s, delim), sort of). * Note that delim must have one NUL; we stop if we see that, too. */ for (;;) { c = *s++; spanp = delim; do { if ((sc = *spanp++) == c) { if (c == 0) s = NULL; else s[-1] = 0; last = s; return (tok); } } while (sc != 0); } /* NOTREACHED */ } inn-2.6.0/lib/nntp.c0000644000175200017520000002526412575023702013622 0ustar iuliusiulius/* $Id: nntp.c 9695 2014-09-20 05:53:22Z iulius $ ** ** Utility functions for speaking the NNTP protocol. ** ** These functions speak the NNTP protocol over stdio FILEs. So far, only ** the server functions are implemented; there is no client support as yet. */ #include "config.h" #include "clibrary.h" #include "portable/socket.h" #include #include #ifdef HAVE_SYS_TIME_H # include #endif #include #include "inn/buffer.h" #include "inn/innconf.h" #include "inn/network.h" #include "inn/nntp.h" #include "inn/vector.h" #include "inn/libinn.h" /* State for an NNTP connection. */ struct nntp { int in_fd; int out_fd; struct buffer in; struct buffer out; size_t maxsize; time_t timeout; }; /* ** Allocate a new nntp struct and return it. Takes the file descriptor to ** read from, the file descriptor to write to, the maximum multiline size ** allowed, and the timeout in seconds for reads. */ struct nntp * nntp_new(int in, int out, size_t maxsize, time_t timeout) { struct nntp *nntp; nntp = xmalloc(sizeof(struct nntp)); nntp->in_fd = in; nntp->out_fd = out; nntp->maxsize = maxsize; nntp->timeout = timeout; nntp->in.data = NULL; nntp->in.size = 0; nntp->in.used = 0; nntp->in.left = 0; nntp->out.data = NULL; nntp->out.size = 0; nntp->out.used = 0; nntp->out.left = 0; return nntp; } /* ** Free an nntp struct and close the associated file descriptors. */ void nntp_free(struct nntp *nntp) { if (nntp == NULL) return; if (nntp->in.data != NULL) free(nntp->in.data); if (nntp->out.data != NULL) free(nntp->out.data); if (nntp->in_fd >= 0) close(nntp->in_fd); if (nntp->out_fd >= 0 && nntp->out_fd != nntp->in_fd) close(nntp->out_fd); free(nntp); } /* ** Connect to a remote NNTP server. Allocates and returns a new nntp struct. ** Takes the server name, the port to connect to, the maximum buffer size ** to use, and the timeout value. On failure to connect to the remote host, ** returns NULL. */ struct nntp * nntp_connect(const char *host, unsigned short port, size_t maxsize, time_t timeout) { int fd; fd = network_connect_host(host, port, NULL, timeout); if (fd < 0) return NULL; return nntp_new(fd, fd, maxsize, timeout); } /* ** Sets the read timeout in seconds (0 to wait forever). */ void nntp_timeout(struct nntp *nntp, time_t timeout) { nntp->timeout = timeout; } /* ** Read data from an NNTP connection, as much as is available, putting it ** into the reader buffer. Return an nntp_status code. */ static enum nntp_status nntp_read_data(struct nntp *nntp) { ssize_t count; int status; /* Resize the buffer if we're out of space, but don't allow the buffer to grow longer than maxsize. */ if (nntp->in.size == 0) buffer_resize(&nntp->in, 1024); if (nntp->in.used + nntp->in.left == nntp->in.size) { size_t size; if (nntp->maxsize > 0 && nntp->in.size >= nntp->maxsize) return NNTP_READ_LONG; if (nntp->in.size >= 1024 * 1024) size = nntp->in.size + 1024 * 1024; else size = nntp->in.size * 2; if (nntp->maxsize > 0 && size > nntp->maxsize) size = nntp->maxsize; buffer_resize(&nntp->in, size); } /* Wait for activity. */ do { fd_set mask; struct timeval tv; FD_ZERO(&mask); FD_SET(nntp->in_fd, &mask); tv.tv_sec = nntp->timeout; tv.tv_usec = 0; status = select(nntp->in_fd + 1, &mask, NULL, NULL, &tv); if (status == -1 && errno != EINTR) return NNTP_READ_ERROR; } while (status == -1); /* Check for timeout. */ if (status == 0) return NNTP_READ_TIMEOUT; /* Do the actual read. */ count = buffer_read(&nntp->in, nntp->in_fd); if (count < 0) return NNTP_READ_ERROR; else if (count == 0) return NNTP_READ_EOF; else return NNTP_READ_OK; } /* ** Read a single line of data from an NNTP connection. Puts the ** nul-terminated line in the second argument; it points into the nntp input ** buffer and will therefore be invalidated on the next read or on nntp_free, ** but not until then. Returns an nntp_status. */ enum nntp_status nntp_read_line(struct nntp *nntp, char **line) { struct buffer *in = &nntp->in; enum nntp_status status = NNTP_READ_OK; size_t offset; size_t start = 0; /* Compact the buffer if there are fewer than 128 characters left; this limit is chosen somewhat arbitrarily, but note that most NNTP lines are fairly short. */ if (in->used + in->left + 128 >= in->size) buffer_compact(in); while (status == NNTP_READ_OK) { if (buffer_find_string(in, "\r\n", start, &offset)) { in->data[in->used + offset] = '\0'; in->left -= offset + 2; *line = in->data + in->used; in->used += offset + 2; return NNTP_READ_OK; } /* Back up one character in case \r\n was split across read boundaries. */ start = (in->left > 0) ? in->left - 1 : 0; status = nntp_read_data(nntp); if (in->used + in->left + 128 >= in->size) buffer_compact(in); } return status; } /* ** Read a response to an NNTP command. Puts the status code in the second ** argument and the rest of the line in the third argument, using a status ** code of 0 if no status code could be found at the beginning of the line. ** The line will be invalidated on the next read or on nntp_free. Returns an ** nntp_status. */ enum nntp_status nntp_read_response(struct nntp *nntp, enum nntp_code *code, char **rest) { char *line; enum nntp_status status; status = nntp_read_line(nntp, &line); if (status != NNTP_READ_OK) return status; *code = strtol(line, rest, 10); if (*rest != line + 3) *code = 0; else if (isspace((unsigned char) *rest[0])) (*rest)++; return status; } /* ** Read a command from an NNTP connection. Takes a cvector as the second ** argument and stores in it the command and arguments, split on whitespace. ** The vectors will point into the nntp input buffer and will therefore be ** invalidated on the next read or on nntp_free, but not until then. Returns ** an nntp_status. */ enum nntp_status nntp_read_command(struct nntp *nntp, struct cvector *command) { enum nntp_status status; char *line; status = nntp_read_line(nntp, &line); if (status == NNTP_READ_OK) cvector_split_space(line, command); return status; } /* ** Read multiline data from an NNTP connection. Sets the second argument to ** a pointer to the data (still in wire format) and the third argument to its ** length. Returns an nntp_status code. */ enum nntp_status nntp_read_multiline(struct nntp *nntp, char **data, size_t *length) { struct buffer *in = &nntp->in; enum nntp_status status = NNTP_READ_OK; size_t offset; size_t start = 0; buffer_compact(in); while (status == NNTP_READ_OK) { if (buffer_find_string(in, "\r\n.\r\n", start, &offset)) { offset += 5; in->left -= offset; *length = offset; *data = in->data + in->used; in->used += offset; return NNTP_READ_OK; } /* Back up by up to four characters in case our reads split on the boundary of the delimiter. */ start = (in->left < 4) ? 0 : in->left - 4; status = nntp_read_data(nntp); } return status; } /* ** Flush the NNTP output channel and makes sure that it has no errors. ** Returns true on success and false on failure. */ bool nntp_flush(struct nntp *nntp) { ssize_t status; if (nntp->out.left == 0) return true; status = xwrite(nntp->out_fd, nntp->out.data, nntp->out.left); if (status < 0) return false; nntp->out.left = 0; nntp->out.used = 0; return true; } /* ** Send verbatim data to the NNTP stream. Flush any buffered data before ** sending and then send the data immediately without buffering (to avoid ** making an in-memory copy of it). The caller is responsible for making ** sure it's properly formatted. Returns true on success and false on ** failure. */ bool nntp_write(struct nntp *nntp, const char *buffer, size_t length) { ssize_t status; if (!nntp_flush(nntp)) return false; status = xwrite(nntp->out_fd, buffer, length); return (status > 0); } /* ** Send a line of data to an NNTP stream, flushing after sending it. Takes ** the nntp struct and printf-style arguments for the rest of the line. ** Returns true on success and false on an error. */ bool nntp_send_line(struct nntp *nntp, const char *format, ...) { va_list args; va_start(args, format); buffer_append_vsprintf(&nntp->out, format, args); va_end(args); buffer_append(&nntp->out, "\r\n", 2); return nntp_flush(nntp); } /* ** The same as nntp_send_line, but don't flush after sending the repsonse. ** Used for accumulating multiline responses, mostly. */ bool nntp_send_line_noflush(struct nntp *nntp, const char *format, ...) { va_list args; va_start(args, format); buffer_append_vsprintf(&nntp->out, format, args); va_end(args); buffer_append(&nntp->out, "\r\n", 2); return nntp_flush(nntp); } /* ** Send a response to an NNTP command, or the opening banner of a server. ** Takes the nntp struct, a response code, and then printf-style arguments ** for the rest of the line. Format may be NULL, indicating nothing should ** be printed after the response code. Returns true on success and false on ** an error. */ bool nntp_respond(struct nntp *nntp, enum nntp_code code, const char *format, ...) { va_list args; if (format == NULL) buffer_append_sprintf(&nntp->out, "%d\r\n", code); else { buffer_append_sprintf(&nntp->out, "%d ", code); va_start(args, format); buffer_append_vsprintf(&nntp->out, format, args); va_end(args); buffer_append(&nntp->out, "\r\n", 2); } return nntp_flush(nntp); } /* ** The same as nntp_respond(), but don't flush after sending the response. ** Used for beginning multiline responses primarily. */ void nntp_respond_noflush(struct nntp *nntp, enum nntp_code code, const char *format, ...) { va_list args; if (format == NULL) buffer_append_sprintf(&nntp->out, "%d\r\n", code); else { buffer_append_sprintf(&nntp->out, "%d ", code); va_start(args, format); buffer_append_vsprintf(&nntp->out, format, args); va_end(args); buffer_append(&nntp->out, "\r\n", 2); } } inn-2.6.0/lib/mmap.c0000644000175200017520000000154712575023702013573 0ustar iuliusiulius/* $Id: mmap.c 7599 2007-02-09 02:46:39Z eagle $ ** ** Manipulation routines for memory-mapped pages. ** ** Written by Alex Kiernan (alex.kiernan@thus.net) */ #include "config.h" #include "clibrary.h" #include "portable/mmap.h" #include "inn/messages.h" #include "inn/mmap.h" /* ** Figure out what page an address is in and call msync on the appropriate ** page. This routine assumes that all pointers fit into a size_t. */ int inn__msync_page(void *p, size_t length, int flags) { int pagesize; pagesize = getpagesize(); if (pagesize == -1) { syswarn("getpagesize failed"); return -1; } else { const size_t mask = ~(size_t)(pagesize - 1); char *start = (char *) ((size_t) p & mask); char *end = (char *) (((size_t) p + length + pagesize) & mask); return msync(start, end - start, flags); } } inn-2.6.0/lib/pwrite.c0000644000175200017520000000310112575023702014137 0ustar iuliusiulius/* $Id: pwrite.c 9767 2014-12-07 21:13:43Z iulius $ ** ** Replacement for a missing pwrite. ** ** Written by Russ Allbery ** This work is hereby placed in the public domain by its author. ** ** Provides the same functionality as the standard library routine pwrite ** for those platforms that don't have it. Note that pwrite requires that ** the file pointer not move and without the library function, we can't ** copy that behavior; instead, we approximate it by moving the file ** pointer and then moving it back. This may break threaded programs. */ #include "config.h" #include "clibrary.h" #include /* If we're running the test suite, rename pread to avoid conflicts with the system version. #undef first because large file support may define a macro pwrite (pointing to pwrite64) on some platforms (e.g. Solaris). */ #if TESTING # undef pwrite # define pwrite test_pwrite ssize_t test_pwrite(int, const void *, size_t, off_t); #endif ssize_t pwrite(int fd, const void *buf, size_t nbyte, off_t offset) { off_t current; ssize_t nwritten; int oerrno; current = lseek(fd, 0, SEEK_CUR); if (current == (off_t) -1 || lseek(fd, offset, SEEK_SET) == (off_t) -1) return -1; nwritten = write(fd, buf, nbyte); /* Ignore errors in restoring the file position; this isn't ideal, but reporting a failed write when the write succeeded is worse. Make sure that errno, if set, is set by write and not lseek. */ oerrno = errno; lseek(fd, current, SEEK_SET); errno = oerrno; return nwritten; } inn-2.6.0/lib/wire.c0000644000175200017520000002135712575023702013610 0ustar iuliusiulius/* $Id: wire.c 8942 2010-02-04 21:24:42Z iulius $ ** ** Wire format article utilities. ** ** Originally written by Alex Kiernan (alex.kiernan@thus.net) ** ** These routines manipulate wire format articles; in particular, they should ** be safe in the presence of embedded NULs. They assume wire format ** conventions (\r\n as a line ending, in particular) and will not work with ** articles in native format (with the exception of wire_from_native, of ** course). ** ** The functions in this file take const char * pointers and return char * ** pointers so that they can work on both const char * and char * article ** bodies without changing the const sense. This unfortunately means that ** the routines in this file will produce warnings about const being cast ** away. To avoid those, one would need to duplicate all the code in this ** file or use C++. */ #include "config.h" #include "clibrary.h" #include #include "inn/wire.h" #include "inn/libinn.h" /* ** Given a pointer to the start of an article, locate the first octet of the ** body (which may be the octet beyond the end of the buffer if your article ** is bodiless). */ char * wire_findbody(const char *article, size_t length) { char *p; const char *end; /* Handle the degenerate case of an article with no headers. */ if (length > 5 && article[0] == '\r' && article[1] == '\n') return (char *) article + 2; /* Jump from \r to \r and give up if we're too close to the end. */ end = article + length; for (p = (char *) article; (p + 4) <= end; ++p) { p = memchr(p, '\r', end - p - 3); if (p == NULL) break; if (memcmp(p, "\r\n\r\n", 4) == 0) { p += 4; return p; } } return NULL; } /* ** Given a pointer into an article and a pointer to the last octet of the ** article, find the next line ending and return a pointer to the first ** character after that line ending. If no line ending is found in the ** article or if it is at the end of the article, return NULL. */ char * wire_nextline(const char *article, const char *end) { char *p; for (p = (char *) article; (p + 2) <= end; ++p) { p = memchr(p, '\r', end - p - 2); if (p == NULL) break; if (p[1] == '\n') { p += 2; return p; } } return NULL; } /* ** Returns true if line is the beginning of a valid header for header, also ** taking the length of the header name as a third argument. Assumes that ** there is at least length + 2 bytes of data at line, and that the header ** name doesn't contain nul. */ static bool isheader(const char *line, const char *header, size_t length) { if (line[length] != ':' || !ISWHITE(line[length + 1])) return false; return strncasecmp(line, header, length) == 0; } /* ** Skip over folding whitespace, as defined by RFC 5322. Takes a pointer to ** where to start skipping and a pointer to the end of the data, and will not ** return a pointer past the end pointer. If skipping folding whitespace ** takes us past the end of data, return NULL. */ static char * skip_fws_bounded(char *text, const char *end) { char *p; for (p = text; p <= end; p++) { if (p < end + 1 && p[0] == '\r' && p[1] == '\n' && ISWHITE(p[2])) p += 2; if (!ISWHITE(*p)) return p; } return NULL; } /* ** Given a pointer to the start of the article, the article length, and the ** header to look for, find the first occurrence of that header in the ** article. Skip over headers with no content, but allow for headers that ** are folded before the first text in the header. If no matching headers ** with content other than spaces and tabs are found, return NULL. */ char * wire_findheader(const char *article, size_t length, const char *header, bool stripspaces) { char *p; const char *end; ptrdiff_t headerlen; headerlen = strlen(header); end = article + length - 1; /* There has to be enough space left in the article for at least the header, the colon, whitespace, and one non-whitespace character, hence 3, minus 1 since the character pointed to by end is part of the article. */ p = (char *) article; while (p != NULL && end - p > headerlen + 2) { if (p[0] == '\r' && p[1] == '\n') return NULL; else if (isheader(p, header, headerlen)) { p += headerlen + 2; if (stripspaces) p = skip_fws_bounded(p, end); if (p == NULL) return NULL; if (p >= end || p[0] != '\r' || p[1] != '\n') return p; } p = wire_nextline(p, end); } return NULL; } /* ** Given a pointer to a header and a pointer to the last octet of the ** article, find the end of the header (a pointer to the final \n of the ** header value). If the header contents don't end in \r\n, return NULL. */ char * wire_endheader(const char *header, const char *end) { char *p; p = wire_nextline(header, end); while (p != NULL) { if (!ISWHITE(*p)) return p - 1; p = wire_nextline(p, end); } if (end - header >= 1 && *end == '\n' && *(end - 1) == '\r') return (char *) end; return NULL; } /* ** Given an article and length in non-wire format, return a malloced region ** containing the article in wire format. Set *newlen to the length of the ** new article. The caller is responsible for freeing the allocated memory. */ char * wire_from_native(const char *article, size_t len, size_t *newlen) { size_t bytes; char *newart; const char *p; char *dest; bool at_start = true; /* First go thru article and count number of bytes we need. Add a CR for every LF and an extra character for any period at the beginning of a line for dot-stuffing. Add 3 characters at the end for .\r\n. */ for (bytes = 0, p = article; p < article + len; p++) { if (at_start && *p == '.') bytes++; bytes++; at_start = (*p == '\n'); if (at_start) bytes++; } bytes += 3; /* Now copy the article, making the required changes. */ newart = xmalloc(bytes + 1); *newlen = bytes; at_start = true; for (p = article, dest = newart; p < article + len; p++) { if (*p == '\n') { *dest++ = '\r'; *dest++ = '\n'; at_start = true; } else { if (at_start && *p == '.') *dest++ = '.'; *dest++ = *p; at_start = false; } } *dest++ = '.'; *dest++ = '\r'; *dest++ = '\n'; *dest = '\0'; return newart; } /* ** Given an article and length in wire format, return a malloced region ** containing the article in native format. Set *newlen to the length of the ** new article. The caller is responsible for freeing the allocated memory. */ char * wire_to_native(const char *article, size_t len, size_t *newlen) { size_t bytes; char *newart; const char *p, *end; char *dest; bool at_start = true; /* If the article is shorter than three bytes, it's definitely not in wire format. Just return a copy of it. */ if (len < 3) { *newlen = len; return xstrndup(article, len); } end = article + len - 3; /* First go thru article and count number of bytes we need. Once we reach .\r\n, we're done. We'll remove one . from .. at the start of a line and change CRLF to just LF. */ for (bytes = 0, p = article; p < article + len; ) { if (p == end && p[0] == '.' && p[1] == '\r' && p[2] == '\n') break; if (at_start && p < article + len - 1 && p[0] == '.' && p[1] == '.') { bytes++; p += 2; at_start = false; } else if (p < article + len - 1 && p[0] == '\r' && p[1] == '\n') { bytes++; p += 2; at_start = true; } else { bytes++; p++; at_start = false; } } /* Now, create the new space and copy the article over. */ newart = xmalloc(bytes + 1); *newlen = bytes; at_start = true; for (p = article, dest = newart; p < article + len; ) { if (p == end && p[0] == '.' && p[1] == '\r' && p[2] == '\n') break; if (at_start && p < article + len - 1 && p[0] == '.' && p[1] == '.') { *dest++ = '.'; p += 2; at_start = false; } else if (p < article + len - 1 && p[0] == '\r' && p[1] == '\n') { *dest++ = '\n'; p += 2; at_start = true; } else { *dest++ = *p++; at_start = false; } } *dest = '\0'; return newart; } inn-2.6.0/lib/network.c0000644000175200017520000007733412575023702014341 0ustar iuliusiulius/* $Id: network.c 9932 2015-08-28 19:23:27Z iulius $ * * Utility functions for network connections. * * This is a collection of utility functions for network connections and * socket creation, encapsulating some of the complexities of IPv4 and IPv6 * support and abstracting operations common to most network code. * * All of the portability difficulties with supporting IPv4 and IPv6 should be * encapsulated in the combination of this code and replacement * implementations for functions that aren't found on some pre-IPv6 systems. * No other part of the source tree should have to care about IPv4 vs. IPv6. * * In this file, casts through void * or const void * of struct sockaddr * * parameters are to silence gcc warnings with -Wcast-align. The specific * address types often require stronger alignment than a struct sockaddr, and * were originally allocated with that alignment. GCC doesn't have a good way * of knowing that this code is correct. * * The canonical version of this file is maintained in the rra-c-util package, * which can be found at . * * Written by Russ Allbery * Copyright 2014, 2015 Russ Allbery * Copyright 2009, 2011, 2012, 2013, 2014 * The Board of Trustees of the Leland Stanford Junior University * Copyright (c) 2004, 2005, 2006, 2007, 2008 * by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, * 2002, 2003 by The Internet Software Consortium and Rich Salz * * This code is derived from software contributed to the Internet Software * Consortium by Rich Salz. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #include "config.h" #include "clibrary.h" #include "portable/socket.h" #include #ifdef HAVE_SYS_SELECT_H # include #endif #ifdef HAVE_SYS_TIME_H # include #endif #include #include "inn/fdflag.h" #include "inn/innconf.h" #include "inn/macros.h" #include "inn/messages.h" #include "inn/network.h" #include "inn/xmalloc.h" #include "inn/xwrite.h" /* Macros to set the len attribute of sockaddrs. */ #if HAVE_STRUCT_SOCKADDR_SA_LEN # define sin_set_length(s) ((s)->sin_len = sizeof(struct sockaddr_in)) # define sin6_set_length(s) ((s)->sin6_len = sizeof(struct sockaddr_in6)) #else # define sin_set_length(s) /* empty */ # define sin6_set_length(s) /* empty */ #endif /* If SO_REUSEADDR isn't available, make calls to set_reuseaddr go away. */ #ifndef SO_REUSEADDR # define network_set_reuseaddr(fd) /* empty */ #endif /* If IPV6_V6ONLY isn't available, make calls to set_v6only go away. */ #ifndef IPV6_V6ONLY # define network_set_v6only(fd) /* empty */ #endif /* If IP_FREEBIND isn't available, make calls to set_freebind go away. */ #ifndef IP_FREEBIND # define network_set_freebind(fd) /* empty */ #endif /* * Windows requires a different function when sending to sockets, but can't * return short writes on blocking sockets. */ #ifdef _WIN32 # define socket_xwrite(fd, b, s) send((fd), (b), (s), 0) #else # define socket_xwrite(fd, b, s) xwrite((fd), (b), (s)) #endif /* * Set SO_REUSEADDR on a socket if possible (so that something new can listen * on the same port immediately if the daemon dies unexpectedly). */ #ifdef SO_REUSEADDR static void network_set_reuseaddr(socket_type fd) { int flag = 1; const void *flagaddr = &flag; if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, flagaddr, sizeof(flag)) < 0) syswarn("cannot mark bind address reusable"); } #endif /* * Set IPV6_V6ONLY on a socket if possible, since the IPv6 behavior is more * consistent and easier to understand. */ #ifdef IPV6_V6ONLY static void network_set_v6only(socket_type fd) { int flag = 1; if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &flag, sizeof(flag)) < 0) syswarn("cannot set IPv6 socket to v6only"); } #endif /* * Set IP_FREEBIND on a socket if possible, which allows binding servers to * IPv6 addresses that may not have been set up yet. */ #ifdef IP_FREEBIND static void network_set_freebind(socket_type fd) { int flag = 1; if (setsockopt(fd, IPPROTO_IP, IP_FREEBIND, &flag, sizeof(flag)) < 0) syswarn("cannot set IPv6 socket to free binding"); } #endif /* * Create an IPv4 socket and bind it, returning the resulting file descriptor * (or INVALID_SOCKET on a failure). */ socket_type network_bind_ipv4(int type, const char *address, unsigned short port) { socket_type fd; struct sockaddr_in server; struct in_addr addr; /* Create the socket. */ fd = socket(PF_INET, type, IPPROTO_IP); if (fd == INVALID_SOCKET) { syswarn("cannot create IPv4 socket for %s, port %hu", address, port); return INVALID_SOCKET; } network_set_reuseaddr(fd); /* Accept "any" or "all" in the bind address to mean 0.0.0.0. */ if (!strcmp(address, "any") || !strcmp(address, "all")) address = "0.0.0.0"; /* Flesh out the socket and do the bind. */ memset(&server, 0, sizeof(server)); server.sin_family = AF_INET; server.sin_port = htons(port); if (!inet_aton(address, &addr)) { warn("invalid IPv4 address %s", address); socket_set_errno_einval(); return INVALID_SOCKET; } server.sin_addr = addr; sin_set_length(&server); if (bind(fd, (struct sockaddr *) &server, sizeof(server)) < 0) { syswarn("cannot bind socket for %s, port %hu", address, port); socket_close(fd); return INVALID_SOCKET; } return fd; } /* * Create an IPv6 socket and bind it, returning the resulting file descriptor * (or INVALID_SOCKET on a failure). This socket will be restricted to IPv6 * only if possible (as opposed to the standard behavior of binding IPv6 * sockets to both IPv6 and IPv4). * * Note that we don't warn (but still return failure) if the reason for the * socket creation failure is that IPv6 isn't supported; this is to handle * systems like many Linux hosts where IPv6 is available in userland but the * kernel doesn't support it. */ #if HAVE_INET6 socket_type network_bind_ipv6(int type, const char *address, unsigned short port) { socket_type fd; struct sockaddr_in6 server; struct in6_addr addr; /* Create the socket. */ fd = socket(PF_INET6, type, IPPROTO_IP); if (fd == INVALID_SOCKET) { if (socket_errno != EAFNOSUPPORT && socket_errno != EPROTONOSUPPORT) syswarn("cannot create IPv6 socket for %s, port %hu", address, port); return INVALID_SOCKET; } network_set_reuseaddr(fd); /* * Restrict the socket to IPv6 only if possible. The default behavior is * to bind IPv6 sockets to both IPv6 and IPv4 for backward compatibility, * but this causes various other problems (such as with reusing sockets * and requiring handling of mapped addresses). Continue on if this * fails, however. */ network_set_v6only(fd); /* Accept "any" or "all" in the bind address to mean ::. */ if (!strcmp(address, "any") || !strcmp(address, "all")) address = "::"; /* * If the address is not ::, use IP_FREEBIND if it's available. This * allows the network stack to bind to an address that isn't configured. * We lose diagnosis of errors from specifying bind addresses that don't * exist on the system, but we gain the ability to bind to IPv6 addresses * that aren't yet configured. Since IPv6 address configuration can take * unpredictable amounts of time during system setup, this is more robust. * * Ensure there is always a block here to avoid compiler warnings, since * network_set_freebind() may expand into nothing. */ if (strcmp(address, "::") != 0) { network_set_freebind(fd); } /* Flesh out the socket and do the bind. */ memset(&server, 0, sizeof(server)); server.sin6_family = AF_INET6; server.sin6_port = htons(port); if (inet_pton(AF_INET6, address, &addr) < 1) { warn("invalid IPv6 address %s", address); socket_set_errno_einval(); return INVALID_SOCKET; } server.sin6_addr = addr; sin6_set_length(&server); if (bind(fd, (struct sockaddr *) &server, sizeof(server)) < 0) { syswarn("cannot bind socket for %s, port %hu", address, port); socket_close(fd); return INVALID_SOCKET; } return fd; } #else /* HAVE_INET6 */ socket_type network_bind_ipv6(int type UNUSED, const char *address, unsigned short port) { warn("cannot bind %s, port %hu: IPv6 not supported", address, port); socket_set_errno(EPROTONOSUPPORT); return INVALID_SOCKET; } #endif /* HAVE_INET6 */ /* * Create and bind sockets for every local address, as determined by * getaddrinfo if IPv6 is available (otherwise, just use the IPv4 loopback * address). Takes the socket type and port number, and then a pointer to an * array of integers and a pointer to a count of them. Allocates a new array * to hold the file descriptors and stores the count in the fourth argument. */ #if HAVE_INET6 bool network_bind_all(int type, unsigned short port, socket_type **fds, unsigned int *count) { struct addrinfo hints, *addrs, *addr; unsigned int size; int status; socket_type fd; char service[16], name[INET6_ADDRSTRLEN]; *count = 0; /* Do the query to find all the available addresses. */ memset(&hints, 0, sizeof(hints)); hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG; hints.ai_family = AF_UNSPEC; hints.ai_socktype = type; status = snprintf(service, sizeof(service), "%hu", port); if (status < 0 || (size_t) status > sizeof(service)) { warn("cannot convert port %hu to string", port); socket_set_errno_einval(); return false; } status = getaddrinfo(NULL, service, &hints, &addrs); if (status < 0) { warn("getaddrinfo for %s failed: %s", service, gai_strerror(status)); socket_set_errno_einval(); return false; } /* * Now, try to bind each of them. Start the fds array at two entries, * assuming an IPv6 and IPv4 socket, and grow it by two when necessary. */ size = 2; *fds = xcalloc(size, sizeof(socket_type)); for (addr = addrs; addr != NULL; addr = addr->ai_next) { network_sockaddr_sprint(name, sizeof(name), addr->ai_addr); if (addr->ai_family == AF_INET) fd = network_bind_ipv4(type, name, port); else if (addr->ai_family == AF_INET6) fd = network_bind_ipv6(type, name, port); else continue; if (fd != INVALID_SOCKET) { if (*count >= size) { size += 2; *fds = xreallocarray(*fds, size, sizeof(socket_type)); } (*fds)[*count] = fd; (*count)++; } } freeaddrinfo(addrs); return (*count > 0); } #else /* HAVE_INET6 */ bool network_bind_all(int type, unsigned short port, socket_type **fds, unsigned int *count) { socket_type fd; fd = network_bind_ipv4(type, "0.0.0.0", port); if (fd == INVALID_SOCKET) { *fds = NULL; *count = 0; return false; } *fds = xmalloc(sizeof(socket_type)); *fds[0] = fd; *count = 1; return true; } #endif /* HAVE_INET6 */ /* * Free the array of file descriptors allocated by network_bind_all. This is * a simple wrapper around free, needed on platforms where libraries allocate * memory from a different memory domain than programs (such as Windows). */ void network_bind_all_free(socket_type *fds) { free(fds); } /* * Given an array of file descriptors and the length of that array (the same * data that's returned by network_bind_all), wait for an incoming connection * on any of those sockets and return the file descriptor that selects ready * for read. * * This is primarily intended for UDP services listening on multiple file * descriptors, and also provides part of the code for network_accept_any. * TCP services will probably want to use network_accept_any instead. * * Returns the new socket on success or INVALID_SOCKET on failure. Note that * INVALID_SOCKET may be returned if the timeout is interrupted by a signal, * which is not, precisely speaking, an error condition. In this case, errno * will be set to EINTR. * * This is not intended to be a replacement for a full event loop, just some * simple shared code for UDP services. */ socket_type network_wait_any(socket_type fds[], unsigned int count) { fd_set readfds; socket_type maxfd, fd; unsigned int i; int status; FD_ZERO(&readfds); maxfd = -1; for (i = 0; i < count; i++) { FD_SET(fds[i], &readfds); if (fds[i] > maxfd) maxfd = fds[i]; } status = select(maxfd + 1, &readfds, NULL, NULL, NULL); if (status < 0) return INVALID_SOCKET; fd = INVALID_SOCKET; for (i = 0; i < count; i++) if (FD_ISSET(fds[i], &readfds)) { fd = fds[i]; break; } return fd; } /* * Given an array of file descriptors and the length of that array (the same * data that's returned by network_bind_all), wait for an incoming connection * on any of those sockets, accept the connection with accept(), and return * the new file descriptor. * * This is essentially a replacement for accept() with a single socket for * daemons that are listening to multiple separate bound sockets, possibly * because they need to listen to specific interfaces or possibly because * they're listening for both IPv4 and IPv6 connections. * * Returns the new socket on success or INVALID_SOCKET on failure. On * success, fills out the arguments with the address and address length of the * accepted client. No error will be reported, so the caller should do that. * Note that INVALID_SOCKET may be returned if the timeout is interrupted by a * signal, which is not, precisely speaking, an error condition. In this * case, errno will be set to EINTR. */ socket_type network_accept_any(socket_type fds[], unsigned int count, struct sockaddr *addr, socklen_t *addrlen) { socket_type fd; fd = network_wait_any(fds, count); if (fd == INVALID_SOCKET) return INVALID_SOCKET; else return accept(fd, addr, addrlen); } /* * Binds the given socket to an appropriate source address for its family * using the provided source address. Returns true on success and false on * failure. */ static bool network_source(socket_type fd, int family, const char *source) { if (source == NULL && innconf == NULL) return true; if (family == AF_INET) { struct sockaddr_in saddr; if (source == NULL && innconf != NULL) source = innconf->sourceaddress; if (source == NULL || strcmp(source, "all") == 0 || strcmp(source, "any") == 0) return true; memset(&saddr, 0, sizeof(saddr)); saddr.sin_family = AF_INET; if (!inet_aton(source, &saddr.sin_addr)) { socket_set_errno_einval(); return false; } return bind(fd, (struct sockaddr *) &saddr, sizeof(saddr)) == 0; } #ifdef HAVE_INET6 else if (family == AF_INET6) { struct sockaddr_in6 saddr; memset(&saddr, 0, sizeof(saddr)); if (source == NULL && innconf != NULL) source = innconf->sourceaddress6; if (source == NULL || strcmp(source, "all") == 0 || strcmp(source, "any") == 0) return true; saddr.sin6_family = AF_INET6; if (inet_pton(AF_INET6, source, &saddr.sin6_addr) < 1) { socket_set_errno_einval(); return false; } return bind(fd, (struct sockaddr *) &saddr, sizeof(saddr)) == 0; } #endif else { socket_set_errno(EAFNOSUPPORT); return false; } } /* * Internal helper function that waits for a non-blocking connect to complete * on a socket. Takes the file descriptor and the timeout. Returns 0 on a * successful completion of the connect within the timeout and -1 on failure. * On failure, sets the socket errno. */ static int connect_wait(socket_type fd, time_t timeout) { int status, err; socklen_t length; struct timeval tv; fd_set set; /* * Use select to poll the file descriptor. Loop if interrupted by a * caught signal. This means we could wait for longer than the timeout * when interrupted, but there's no good way of recovering the elapsed * time that's worth the hassle. */ do { tv.tv_sec = timeout; tv.tv_usec = 0; FD_ZERO(&set); FD_SET(fd, &set); status = select(fd + 1, NULL, &set, NULL, &tv); } while (status < 0 && socket_errno == EINTR); /* * If we timed out, set errno appropriately. If the connection completes, * retrieve the actual status from the socket. */ if (status == 0 && !FD_ISSET(fd, &set)) { status = -1; socket_set_errno(ETIMEDOUT); } else if (status > 0 && FD_ISSET(fd, &set)) { length = sizeof(err); status = getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &length); if (status == 0) { status = (err == 0) ? 0 : -1; socket_set_errno(err); } } return status; } /* * Given a linked list of addrinfo structs representing the remote service, * try to create a local socket and connect to that service. Takes an * optional source address. Try each address in turn until one of them * connects. Returns the file descriptor of the open socket on success, or * INVALID_SOCKET on failure. Tries to leave the reason for the failure in * errno. */ socket_type network_connect(const struct addrinfo *ai, const char *source, time_t timeout) { socket_type fd = INVALID_SOCKET; int oerrno, status; for (status = -1; status != 0 && ai != NULL; ai = ai->ai_next) { if (fd != INVALID_SOCKET) socket_close(fd); fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if (fd == INVALID_SOCKET) continue; if (!network_source(fd, ai->ai_family, source)) continue; if (timeout == 0) status = connect(fd, ai->ai_addr, ai->ai_addrlen); else { fdflag_nonblocking(fd, true); status = connect(fd, ai->ai_addr, ai->ai_addrlen); if (status < 0 && socket_errno == EINPROGRESS) status = connect_wait(fd, timeout); oerrno = socket_errno; fdflag_nonblocking(fd, false); socket_set_errno(oerrno); } } if (status == 0) return fd; else { if (fd != INVALID_SOCKET) { oerrno = socket_errno; socket_close(fd); socket_set_errno(oerrno); } return INVALID_SOCKET; } } /* * Like network_connect, but takes a host and a port instead of an addrinfo * struct list. Returns the file descriptor of the open socket on success, or * INVALID_SOCKET on failure. If getaddrinfo fails, errno may not be set to * anything useful. */ socket_type network_connect_host(const char *host, unsigned short port, const char *source, time_t timeout) { struct addrinfo hints, *ai; char portbuf[16]; socket_type fd; int status, oerrno; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; status = snprintf(portbuf, sizeof(portbuf), "%hu", port); if (status > 0 && (size_t) status > sizeof(portbuf)) { status = -1; socket_set_errno_einval(); } if (status < 0) return INVALID_SOCKET; if (getaddrinfo(host, portbuf, &hints, &ai) != 0) return INVALID_SOCKET; fd = network_connect(ai, source, timeout); oerrno = socket_errno; freeaddrinfo(ai); socket_set_errno(oerrno); return fd; } /* * Create a new socket of the specified domain and type and do the binding as * if we were a regular client socket, but then return before connecting. * Returns the file descriptor of the open socket on success, or * INVALID_SOCKET on failure. Intended primarily for the use of clients that * will then go on to do a non-blocking connect. */ socket_type network_client_create(int domain, int type, const char *source) { socket_type fd; int oerrno; fd = socket(domain, type, 0); if (fd == INVALID_SOCKET) return INVALID_SOCKET; if (!network_source(fd, domain, source)) { oerrno = socket_errno; socket_close(fd); socket_set_errno(oerrno); return INVALID_SOCKET; } return fd; } /* * Equivalent to read, but reads all the available data up to the buffer * length, using multiple reads if needed and handling EINTR and EAGAIN. If * we get EOF before we get enough data, set the socket errno to EPIPE. */ static ssize_t socket_xread(socket_type fd, void *buffer, size_t size) { size_t total; ssize_t status; unsigned int count = 0; /* Abort the read if we try 100 times with no forward progress. */ for (total = 0, status = 0; total < size; total += status) { if (++count > 100) break; status = socket_read(fd, (char *) buffer + total, size - total); if (status > 0) count = 0; else if (status == 0) break; else { if ((socket_errno != EINTR) && (socket_errno != EAGAIN)) break; status = 0; } } if (status == 0 && total < size) socket_set_errno(EPIPE); return (total < size) ? -1 : (ssize_t) total; } /* * Read the specified number of bytes from the network, enforcing a timeout * (in seconds). We use select to wait for data to become available and then * keep reading until either we time out or we've gotten all the data we're * looking for. timeout may be 0 to never time out. Return true on success * and false (setting socket_errno) on failure. */ bool network_read(socket_type fd, void *buffer, size_t total, time_t timeout) { time_t start, now; fd_set set; struct timeval tv; size_t got = 0; ssize_t status; /* If there's no timeout, do this the easy way. */ if (timeout == 0) return (socket_xread(fd, buffer, total) >= 0); /* * The hard way. We try to apply the timeout on the whole read. If * either select or read fails with EINTR, restart the loop, and rely on * the overall timeout to limit how long we wait without forward * progress. */ start = time(NULL); now = start; do { FD_ZERO(&set); FD_SET(fd, &set); tv.tv_sec = timeout - (now - start); if (tv.tv_sec < 1) tv.tv_sec = 1; tv.tv_usec = 0; status = select(fd + 1, &set, NULL, NULL, &tv); if (status < 0) { if (socket_errno == EINTR) continue; return false; } else if (status == 0) { socket_set_errno(ETIMEDOUT); return false; } status = socket_read(fd, (char *) buffer + got, total - got); if (status < 0) { if (socket_errno == EINTR) continue; return false; } else if (status == 0) { socket_set_errno(EPIPE); return false; } got += status; if (got == total) return true; now = time(NULL); } while (now - start < timeout); socket_set_errno(ETIMEDOUT); return false; } /* * Write the specified number of bytes from the network, enforcing a timeout * (in seconds). We use select to wait for the socket to become available and * then keep reading until either we time out or we've sent all the data. * timeout may be 0 to never time out. Return true on success and false * (setting socket_errno) on failure. */ bool network_write(socket_type fd, const void *buffer, size_t total, time_t timeout) { time_t start, now; fd_set set; struct timeval tv; size_t sent = 0; ssize_t status; int err; /* If there's no timeout, do this the easy way. */ if (timeout == 0) return (socket_xwrite(fd, buffer, total) >= 0); /* The hard way. We try to apply the timeout on the whole write. If * either select or read fails with EINTR, restart the loop, and rely on * the overall timeout to limit how long we wait without forward progress. */ fdflag_nonblocking(fd, true); start = time(NULL); now = start; do { FD_ZERO(&set); FD_SET(fd, &set); tv.tv_sec = timeout - (now - start); if (tv.tv_sec < 1) tv.tv_sec = 1; tv.tv_usec = 0; status = select(fd + 1, NULL, &set, NULL, &tv); if (status < 0) { if (socket_errno == EINTR) continue; goto fail; } else if (status == 0) { socket_set_errno(ETIMEDOUT); goto fail; } status = socket_write(fd, (const char *) buffer + sent, total - sent); if (status < 0) { if (socket_errno == EINTR) continue; goto fail; } sent += status; if (sent == total) { fdflag_nonblocking(fd, false); return true; } now = time(NULL); } while (now - start < timeout); socket_set_errno(ETIMEDOUT); fail: err = socket_errno; fdflag_nonblocking(fd, false); socket_set_errno(err); return false; } /* * Print an ASCII representation of the address of the given sockaddr into the * provided buffer. This buffer must hold at least INET_ADDRSTRLEN characters * for IPv4 addresses and INET6_ADDRSTRLEN characters for IPv6, so generally * it should always be as large as the latter. Returns success or failure. */ bool network_sockaddr_sprint(char *dst, size_t size, const struct sockaddr *addr) { const char *result; #ifdef HAVE_INET6 if (addr->sa_family == AF_INET6) { const struct sockaddr_in6 *sin6; sin6 = (const struct sockaddr_in6 *) (const void *) addr; if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { struct in_addr in; memcpy(&in, sin6->sin6_addr.s6_addr + 12, sizeof(in)); result = inet_ntop(AF_INET, &in, dst, size); } else result = inet_ntop(AF_INET6, &sin6->sin6_addr, dst, size); return (result != NULL); } #endif if (addr->sa_family == AF_INET) { const struct sockaddr_in *sin; sin = (const struct sockaddr_in *) (const void *) addr; result = inet_ntop(AF_INET, &sin->sin_addr, dst, size); return (result != NULL); } else { socket_set_errno(EAFNOSUPPORT); return false; } } /* * Compare the addresses from two sockaddrs and see whether they're equal. * IPv4 addresses that have been mapped to IPv6 addresses compare equal to the * corresponding IPv4 address. */ bool network_sockaddr_equal(const struct sockaddr *a, const struct sockaddr *b) { const struct sockaddr_in *a4; const struct sockaddr_in *b4; #ifdef HAVE_INET6 const struct sockaddr_in6 *a6; const struct sockaddr_in6 *b6; const struct sockaddr *tmp; #endif a4 = (const struct sockaddr_in *) (const void *) a; b4 = (const struct sockaddr_in *) (const void *) b; #ifdef HAVE_INET6 a6 = (const struct sockaddr_in6 *) (const void *) a; b6 = (const struct sockaddr_in6 *) (const void *) b; if (a->sa_family == AF_INET && b->sa_family == AF_INET6) { tmp = a; a = b; b = tmp; a6 = (const struct sockaddr_in6 *) (const void *) a; b4 = (const struct sockaddr_in *) (const void *) b; } if (a->sa_family == AF_INET6) { if (b->sa_family == AF_INET6) return IN6_ARE_ADDR_EQUAL(&a6->sin6_addr, &b6->sin6_addr); else if (b->sa_family != AF_INET) return false; else if (!IN6_IS_ADDR_V4MAPPED(&a6->sin6_addr)) return false; else { struct in_addr in; memcpy(&in, a6->sin6_addr.s6_addr + 12, sizeof(in)); return (in.s_addr == b4->sin_addr.s_addr); } } #endif if (a->sa_family != AF_INET || b->sa_family != AF_INET) return false; return (a4->sin_addr.s_addr == b4->sin_addr.s_addr); } /* * Returns the port of a sockaddr or 0 on error. */ unsigned short network_sockaddr_port(const struct sockaddr *sa) { const struct sockaddr_in *sin; #ifdef HAVE_INET6 const struct sockaddr_in6 *sin6; if (sa->sa_family == AF_INET6) { sin6 = (const struct sockaddr_in6 *) (const void *) sa; return htons(sin6->sin6_port); } #endif if (sa->sa_family != AF_INET) return 0; else { sin = (const struct sockaddr_in *) (const void *) sa; return htons(sin->sin_port); } } /* * Compare two addresses given as strings, applying an optional mask. Returns * true if the addresses are equal modulo the mask and false otherwise, * including on syntax errors in the addresses or mask specification. */ bool network_addr_match(const char *a, const char *b, const char *mask) { struct in_addr a4, b4, tmp; unsigned long cidr; char *end; unsigned int i; unsigned long bits, addr_mask; #ifdef HAVE_INET6 struct in6_addr a6, b6; #endif /* * AIX 7.1 treats the empty string as equivalent to 0.0.0.0 and allows it * to match, but it's too easy to get the empty string from some sort of * syntax error. Special-case the empty string to always return false. */ if (a[0] == '\0' || b[0] == '\0') return false; /* * If the addresses are IPv4, the mask may be in one of two forms. It can * either be a traditional mask, like 255.255.0.0, or it can be a CIDR * subnet designation, like 16. (The caller should have already removed * the slash separating it from the address.) */ if (inet_aton(a, &a4) && inet_aton(b, &b4)) { if (mask == NULL) addr_mask = htonl(0xffffffffUL); else if (strchr(mask, '.') == NULL) { cidr = strtoul(mask, &end, 10); if (cidr > 32 || *end != '\0') return false; for (bits = 0, i = 0; i < cidr; i++) bits |= (1UL << (31 - i)); addr_mask = htonl(bits); } else if (inet_aton(mask, &tmp)) addr_mask = tmp.s_addr; else return false; return (a4.s_addr & addr_mask) == (b4.s_addr & addr_mask); } #ifdef HAVE_INET6 /* * Otherwise, if the address is IPv6, the mask is required to be a CIDR * subnet designation. */ if (!inet_pton(AF_INET6, a, &a6) || !inet_pton(AF_INET6, b, &b6)) return false; if (mask == NULL) cidr = 128; else { cidr = strtoul(mask, &end, 10); if (cidr > 128 || *end != '\0') return false; } for (i = 0; i * 8 < cidr; i++) { if ((i + 1) * 8 <= cidr) { if (a6.s6_addr[i] != b6.s6_addr[i]) return false; } else { for (addr_mask = 0, bits = 0; bits < cidr % 8; bits++) addr_mask |= (1UL << (7 - bits)); if ((a6.s6_addr[i] & addr_mask) != (b6.s6_addr[i] & addr_mask)) return false; } } return true; #else return false; #endif } inn-2.6.0/lib/dispatch.c0000644000175200017520000000317712575023702014441 0ustar iuliusiulius/* $Id: dispatch.c 6376 2003-06-01 23:24:34Z rra $ ** ** Dispatch cvectors of commands to functions. ** ** This is a generic command dispatching system designed primary to handle ** dispatching NNTP commands to functions that handle them, for NNTP server ** software. */ #include "config.h" #include "clibrary.h" #include "inn/dispatch.h" #include "inn/vector.h" /* ** Comparison function for bsearch on the table of dispatch instructions. */ static int compare_dispatch(const void *key, const void *element) { const char *command = key; const struct dispatch *rule = element; return strcasecmp(command, rule->command); } /* ** The dispatch function. Takes a command (as a struct cvector), the ** dispatch table (a SORTED array of dispatch structs), the number of ** elements in the table, the callback function for unknown commands, the ** callback function for syntax errors (commands called with the wrong number ** of arguments), and an opaque void * that will be passed to the callback ** functions. */ void dispatch(struct cvector *command, const struct dispatch *table, size_t count, dispatch_func unknown, dispatch_func syntax, void *cookie) { struct dispatch *rule; int argc = command->count - 1; if (argc < 0) { (*unknown)(command, cookie); return; } rule = bsearch(command->strings[0], table, count, sizeof(struct dispatch), compare_dispatch); if (rule == NULL) (*unknown)(command, cookie); else if (argc < rule->min_args || argc > rule->max_args) (*syntax)(command, cookie); else (*rule->callback)(command, cookie); } inn-2.6.0/lib/lockfile.c0000644000175200017520000000210712575023702014422 0ustar iuliusiulius/* $Id: lockfile.c 7585 2006-11-21 09:37:51Z eagle $ ** ** Lock a file or a range in a file. ** ** Provides inn_lock_file and inn_lock_range functions to lock or unlock a ** file or ranges within a file with a more convenient syntax than fcntl. ** Assume that fcntl is available. */ #include "config.h" #include "clibrary.h" #include #include #include "inn/libinn.h" bool inn_lock_file(int fd, enum inn_locktype type, bool block) { return inn_lock_range(fd, type, block, 0, 0); } bool inn_lock_range(int fd, enum inn_locktype type, bool block, off_t offset, off_t size) { struct flock fl; int status; switch (type) { case INN_LOCK_READ: fl.l_type = F_RDLCK; break; case INN_LOCK_WRITE: fl.l_type = F_WRLCK; break; default: case INN_LOCK_UNLOCK: fl.l_type = F_UNLCK; break; } do { fl.l_whence = SEEK_SET; fl.l_start = offset; fl.l_len = size; status = fcntl(fd, block ? F_SETLKW : F_SETLK, &fl); } while (status == -1 && errno == EINTR); return (status != -1); } inn-2.6.0/lib/resource.c0000644000175200017520000000212212575023702014456 0ustar iuliusiulius/* $Id: resource.c 9754 2014-12-01 21:08:28Z iulius $ ** */ #include "config.h" #include "clibrary.h" #include "inn/libinn.h" #ifdef HAVE_GETRUSAGE #ifdef HAVE_SYS_TIME_H # include #endif #include #include #define TIMEVALasDOUBLE(t) \ ((double)(t).tv_sec + ((double)(t).tv_usec) / 1000000.0) int getrusage(int who, struct rusage *rusage); int GetResourceUsage(double *usertime, double *systime) { struct rusage R; if (getrusage(RUSAGE_SELF, &R) < 0) return -1; *usertime = TIMEVALasDOUBLE(R.ru_utime); *systime = TIMEVALasDOUBLE(R.ru_stime); return 0; } #else /* HAVE_GETRUSAGE */ #include #include #if !defined(HZ) #define HZ 60 #endif /* !defined(HZ) */ #define CPUTIMEasDOUBLE(t1, t2) ((double)(t1 + t2) / (double)HZ) int GetResourceUsage(double *usertime, double *systime) { struct tms T; if (times(&T) == -1) return -1; *usertime = CPUTIMEasDOUBLE(T.tms_utime, T.tms_cutime); *systime = CPUTIMEasDOUBLE(T.tms_stime, T.tms_cstime); return 0; } #endif /* !HAVE_GETRUSAGE */ inn-2.6.0/perl/0000755000175200017520000000000012575023701012661 5ustar iuliusiuliusinn-2.6.0/perl/INN/0000755000175200017520000000000012575023701013305 5ustar iuliusiuliusinn-2.6.0/perl/INN/Config.pm.in0000644000175200017520000002057512575023702015467 0ustar iuliusiulius## $Id: Config.pm.in 9567 2013-11-17 20:24:35Z iulius $ ## ## Perl module which sets up any and all variables that an ## INN Perl script might need. ## package INN::Config; use warnings; use Exporter; our @ISA = qw(Exporter); our $VERSION = '@PACKAGE_VERSION@'; ## First, two necessary variables (listed below in @DIRVAR). our $prefix = '@prefix@'; our $exec_prefix = "@exec_prefix@"; ## Then, process the variables provided by innconfval. my @INNCONFVAR = (); my @values = `${exec_prefix}/bin/innconfval -p`; foreach my $line (@values) { eval 'our '.$line; if ($line =~ /^(.*?) = /m) { push(@INNCONFVAR, $1); } } ## Paths of useful directories. my @DIRVAR = qw($prefix $exec_prefix $newshome $home $newslib $newsbin $controlprogs $newslbin $newsetc $spooldir $archivedir $spool $incoming $spoolnews $badnews $batch $overviewdir $most_logs $locks $innddir $tmpdir $spooltemp $path); our $newshome = $pathnews; our $home = $pathnews; our $newslib = "@libdir@"; our $newsbin = $pathbin; our $controlprogs = $pathcontrol; our $newslbin = "$pathnews/local"; our $newsetc = $pathetc; our $spooldir = $pathspool; our $archivedir = $patharchive; our $spool = $patharticles; our $incoming = $pathincoming; our $spoolnews = $pathincoming; our $badnews = "$pathincoming/bad"; our $batch = $pathoutgoing; our $overviewdir = $pathoverview; our $most_logs = $pathlog; our $locks = $pathrun; our $innddir = $pathrun; our $tmpdir = $pathtmp; our $spooltemp = $pathtmp; our $path = $ENV{'PATH'} || ''; $path = "${newslbin}:${newsbin}:${path}:/bin:/usr/bin"; ## Paths of useful files. my @FILESVAR = qw($active $activetimes $newactive $oldactive $history $newsgroups $ctlfile $ctlwatch $localgroups $newsfeeds $path_motd_innd $path_motd_nnrpd $path_motd $expirectl $errlog $log); our $active = "${pathdb}/active"; our $activetimes = "${pathdb}/active.times"; our $newactive = "${pathdb}/active.tmp"; our $oldactive = "${pathdb}/active.old"; our $history = "${pathdb}/history"; our $newsgroups = "${pathdb}/newsgroups"; our $ctlfile = "${newsetc}/control.ctl"; our $ctlwatch = "${newsetc}/innwatch.ctl"; our $localgroups = "${newsetc}/localgroups"; our $newsfeeds = "${newsetc}/newsfeeds"; our $path_motd_innd = "${newsetc}/motd.innd"; our $path_motd_nnrpd = "${newsetc}/motd.nnrpd"; our $path_motd = "${path_motd_nnrpd}"; our $expirectl = "${newsetc}/expire.ctl"; our $errlog = "${most_logs}/errlog"; our $log = "${most_logs}/news"; ## Paths of useful programs. my @PROGVAR = qw($inews $innconfval $innd $inndf $innwatch $rnews $perl_startup_innd $perl_filter_innd $perl_filter_nnrpd $python_filter_innd $path_python_inn_module $path_tcl_startup $path_tcl_filter); our $inews = "${newsbin}/inews"; our $innconfval = "${newsbin}/innconfval"; our $innd = "${newsbin}/innd"; our $inndf = "${newsbin}/inndf"; our $innwatch = "${newsbin}/innwatch"; our $rnews = "${newsbin}/rnews"; our $perl_startup_innd = "$pathfilter/startup_innd.pl"; our $perl_filter_innd = "$pathfilter/filter_innd.pl"; our $perl_filter_nnrpd = "$pathfilter/filter_nnrpd.pl"; our $python_filter_innd = "$pathfilter/filter_innd.py"; our $path_python_inn_module = "$pathfilter/INN.py"; our $path_tcl_startup = "$pathfilter/startup.tcl"; our $path_tcl_filter = "$pathfilter/filter.tcl"; ## Paths of lock/PID/status files. my @LOCKVAR = qw($daily $newscontrol $nntpconnect $serverpid $innwstatus $watchpid $tempsock $tempsockdir); our $daily = "${locks}/LOCK.news.daily"; our $newscontrol = "${innddir}/control"; our $nntpconnect = "${innddir}/nntpin"; our $serverpid = "${innddir}/innd.pid"; our $innwstatus = "${innddir}/innwatch.status"; our $watchpid = "${innddir}/innwatch.pid"; (our $tempsock = "${innddir}/ctlinndXXXXXX") =~ s!.*/(.*)XXXXXX$!$1*!; (our $tempsockdir = "${innddir}/ctlinndXXXXXX") =~ s!/[^/]*$!!; ## Paths of external used programs. my @EXTPROGVAR = qw($awk $egrep $gpgv $perl $pgp $sed $sort $getftp $uustat $uux); our $awk = '@AWK@'; our $egrep = '@EGREP@'; our $gpgv = '@GPGV@'; our $perl = '@PERL@'; our $pgp = '@PGP@'; our $sed = '@SED@'; our $sort = '@SORT@'; our $getftp = "@PATH_GETFTP@"; our $uustat = '@UUSTAT@'; our $uux = '@UUX@'; ## Paths of external used compressors. my @EXTCOMPVAR = qw($bzip2 $compress $gzip $uncompress $log_compress $z); our $bzip2 = '@BZIP2@'; our $compress = '@COMPRESS@'; our $gzip = '@GZIP@'; our $uncompress = '@UNCOMPRESS@'; our $log_compress = '@LOG_COMPRESS@'; our $z = '@LOG_COMPRESSEXT@'; ## Set up some useful system file information. my @SYSVAR = qw($newsmaster $newsuser $newsgroup $filemode $inewsmode $rnewsmode $umask $syslog_facility); our $newsmaster = '@NEWSMASTER@'; our $newsuser = ${runasuser}; our $newsgroup = ${runasgroup}; our $filemode = @FILEMODE@; # It is a number. our $inewsmode = @INEWSMODE@; our $rnewsmode = @RNEWSMODE@; our $umask = @NEWSUMASK@; our $syslog_facility = lc('@SYSLOG_FACILITY@'); $syslog_facility =~ s/log_//; ## Set up some useful parameters. my @PARAMVAR = qw($do_dbz_tagged_hash); our $do_dbz_tagged_hash = '@DO_DBZ_TAGGED_HASH@'; ## Set up some environment values. if ($ovmethod && $ovmethod eq "ovdb") { $ENV{'DB_HOME'} = $pathoverview; } $ENV{'INNDDIR'} = $innddir; $ENV{'LOCKS'} = $locks; $ENV{'MOST_LOGS'} = $most_logs; $ENV{'NEWSBIN'} = $newsbin; $ENV{'NEWSETC'} = $newsetc; $ENV{'NEWSHOME'} = $pathnews; $ENV{'NEWSLBIN'} = ${newslbin}; $ENV{'NEWSLIB'} = $newslib; $ENV{'PATH'} = $path; $ENV{'SPOOLDIR'} = $spooldir; $ENV{'TMPDIR'} = $pathtmp; ## Set up the locale. $ENV{'LC_CTYPE'} = "C"; ## Set up umask. umask @NEWSUMASK@; ## This array will contain what it is possible to export. our @EXPORT_OK = (@INNCONFVAR, @DIRVAR, @FILESVAR, @PROGVAR, @LOCKVAR, @EXTPROGVAR, @EXTCOMPVAR, @SYSVAR, @PARAMVAR, qw($VERSION)); ## Load another script that can override or add variables. if (-x "${pathetc}/innshellvars.pl.local") { do "${pathetc}/innshellvars.pl.local"; } ## That's all. 1; __END__ =head1 NAME INN::Config - Export all the variables an INN Perl script might need =head1 DESCRIPTION This Perl module sets up any and all the variables that an INN Perl script might need. More particularly, it allows to use F variables: they are all provided by B, as well as the version of INN (in the variable C<$INN::Config::VERSION> for its short number form, on in C<$INN::Config::version> for its complete form). Other useful variables are also provided (directories, files, programs, masks, parameters). The complete list can be obtained with the following script that prints them out: use lib '/lib/perl'; use INN::Config; use Data::Dumper; my ($varname, $value); foreach my $var (@INN::Config::EXPORT_OK) { if ($var =~ /^\$(.*)$/) { $varname = "INN::Config::$1"; $value = Dumper($$varname); $value =~ s/^\$VAR1 = //; print "\$$varname = $value"; } elsif ($var =~ /^\@(.*)$/) { $varname = "INN::Config::$1"; $value = Dumper(\@$varname); $value =~ s/^\$VAR1 = //; print "\@$varname = $value"; } } A local Perl script named F in I will be loaded, if present and executable, at the end of the run of this module. A typical use is to add or override variables. You only have to declare the module at the beginning of them: use lib '/lib/perl'; use INN::Config; Then, you can for instance use: print $INN::Config::localmaxartsize; to print the value of I as it is set in F. You can also specify a version when you import the module. If you write: use INN::Config 2.5.0; only versions of INN superior to 2.5.0 will be able to run the Perl script. It is also possible to import the variables directly in your namespace if you specify what you want to import: use INN::Config qw($localmaxartsize $pathbin); Note that a legacy F is also provided in I/lib for compatibility reasons with old Perl scripts not shipped with INN. It was used by versions of INN anterior to 2.5.0. The corresponding scripts for Shell and Tcl are, however, still in use: F and F. They offer the same capabilities as this module. =head1 HISTORY F was written by James Brister for InterNetNews in 1996. It was converted to the INN::Config Perl module by Julien Elie in 2007. $Id: Config.pm.in 9567 2013-11-17 20:24:35Z iulius $ =head1 SEE ALSO inn.conf(5), innconfval(1), perl(1). =cut inn-2.6.0/perl/INN/Utils/0000755000175200017520000000000012575023701014405 5ustar iuliusiuliusinn-2.6.0/perl/INN/Utils/Shlock.pm.in0000644000175200017520000001023012575023702016570 0ustar iuliusiulius#! /usr/bin/perl -w # fixscript will replace this line with code to load INN::Config ## $Id: Shlock.pm.in 9408 2012-05-28 18:42:29Z iulius $ ## ## Perl module for wrapping the shlock program shipped with INN. ## package INN::Utils::Shlock; use strict; use warnings; use Exporter; our @ISA = qw(Exporter); our $VERSION = "$INN::Config::VERSION"; my %lockfiles; ## ## Returns true if the file is properly locked. ## sub lock { my $lockfile = shift; my $lockretrymax = shift; my $lockwait = shift; my $locktry = 0; $lockretrymax = 1 if not defined $lockretrymax; $lockwait = 2 if not defined $lockwait; while ($locktry < $lockretrymax) { if (system("$INN::Config::newsbin/shlock", '-p', $$, '-f', $lockfile) == 0) { $lockfiles{$lockfile} = 1; return 1; } $locktry++; sleep $lockwait; } # Failed to lock. return 0; } ## ## Returns true if the file is properly unlocked. ## sub unlock { my $lockfile = shift; if (unlink $lockfile) { delete $lockfiles{$lockfile}; return 1; } else { return 0; } } ## ## Attempts to unlock any leftover locks. ## Returns the number of removed locks. ## sub releaselocks { my $key; my $count = 0; foreach $key (keys(%lockfiles)) { $count += unlock($key); } undef(%lockfiles); return $count; } ## This array will contain what it is possible to export. our @EXPORT_OK = qw(lock unlock releaselocks); ## That's all. 1; __END__ =head1 NAME INN::Utils::Shlock - Wrapper around the shlock program =head1 DESCRIPTION This Perl module wraps the shlock(1) program so that it can easily be used. Calling B is more portable than using flock(2) and its corresponding Perl function because this function does not work as expected on all existing systems. See the shlock(1) documentation for more information. Using INN::Utils::Shlock is straight-forward: use lib '/lib/perl'; use INN::Utils::Shlock; my $lockfile = "myprogram.LOCK"; # Acquire a lock. INN::Utils::Shlock::lock($lockfile); # Do whatever you want. The lock prevents concurrent accesses. # Unlock. INN::Utils::Shlock::unlock($lockfile); These two functions return C<1> on success, C<0> on failure. For example, the success of (un)locking can be checked as: INN::Utils::Shlock::lock($lockfile) or die "cannot create lock file"; or: if (! INN::Utils::Shlock::lock($lockfile, 4)) { die "giving up after 4 unsuccessful attempts to create lock file"; } Instead of calling C<< unlock(I) >>, the C function can be called. It removes any leftover locks, which is useful when several different locks are used. Another possible use is to call it in an END code block: END { # In case we bail out, while holding a lock. INN::Utils::Shlock::releaselocks(); } =head1 INTERFACE =over 4 =item lock(I) Tries to create a lock file named I. This function returns C<1> on success, C<0> on failure. =item lock(I, I) Tries to create a lock file named I. If it fails, locking attempts are repeated once every 2 seconds for at most I times (including the first unsuccessful attempt). This function returns C<1> on success, C<0> on failure. =item lock(I, I, I) Tries to create a lock file named F. If it fails, locking attempts are repeated once every I seconds for at most I times (including the first unsuccessful attempt). Note that C<< lock(I) >> is equivalent to C<< lock(I, 1, 2) >>. This function returns C<1> on success, C<0> on failure. =item releaselocks() Removes all the lock files previously created by calling the C<< lock(I) >> function. This function returns the number of removed lock files. =item unlock(I) Removes the file named I. This function returns C<1> on success, C<0> on failure. =back =head1 HISTORY Documentation written by Julien Elie for InterNetNews. $Id: Shlock.pm.in 9408 2012-05-28 18:42:29Z iulius $ =head1 SEE ALSO perl(1), shlock(1). =cut inn-2.6.0/perl/Makefile0000644000175200017520000000243012575023702014321 0ustar iuliusiulius## $Id: Makefile 9303 2011-08-04 22:09:57Z iulius $ include ../Makefile.global top = .. ALL = INN/Utils/Shlock.pm EXTRA = INN/Config.pm ## The double underscore '__' will be converted into two colons '::' ## during the install process (otherwise, make would complain in this ## file, and we cannot generate the right file with '::' in the ## Subversion man directory because it would always be regenerated: ## the file containing '__' would indeed not be found). MAN = ../doc/man/INN__Config.3pm \ ../doc/man/INN__Utils__Shlock.3pm all: $(ALL) $(EXTRA) $(MAN) install: all for F in $(ALL) $(EXTRA) ; do \ $(CP_RPUB) $$F $D$(PATHLIBPERL)/$$F ; \ done bootstrap: $(MAN) clean: rm -f $(ALL) clobber distclean: clean rm -f $(EXTRA) maintclean: distclean rm -f $(MAN) depend: profiled: all $(EXTRA) $(FIXSCRIPT): @echo Run configure before running make. See INSTALL for details. @exit 1 ## Compilation rules. FIX = $(FIXSCRIPT) INN/Utils/Shlock.pm: INN/Utils/Shlock.pm.in $(FIX) ; $(FIX) INN/Utils/Shlock.pm.in ../doc/man/INN__Config.3pm: INN/Config.pm.in $(POD2MAN) -s '$(MAN3PM_EXT)' -n "INN::Config" $? > $@ ../doc/man/INN__Utils__Shlock.3pm: INN/Utils/Shlock.pm.in $(POD2MAN) -s '$(MAN3PM_EXT)' -n "INN::Utils::Shlock" $? > $@ inn-2.6.0/site/0000755000175200017520000000000012575023701012663 5ustar iuliusiuliusinn-2.6.0/site/Makefile0000644000175200017520000003007112575023702014325 0ustar iuliusiulius## $Revision: 9832 $ include ../Makefile.global top = .. ## If you want to do ctlinnd pause/reload/go, uncomment these lines. #PAUSE = pause #RELOAD_AND_GO = reload go DIFF="diff" # Added a default rule for ".csh" because Digital UNIX has a builtin # rule which would overwite the innshellvars file. .csh: CTLINND = ${PATHBIN}/ctlinnd FILTBIN = ${PATHFILTER} PATH_PERL_STARTUP_INND = ${PATHFILTER}/startup_innd.pl PATH_PERL_FILTER_INND = ${PATHFILTER}/filter_innd.pl PATH_PERL_FILTER_NNRPD = ${PATHFILTER}/filter_nnrpd.pl PATH_PYTHON_FILTER_INND = ${PATHFILTER}/filter_innd.py PATH_PYTHON_INN_MODULE = ${PATHFILTER}/INN.py PATH_PYTHON_NNRPD_MODULE= ${PATHFILTER}/nnrpd.py PATH_NNRPAUTH = ${PATHFILTER}/nnrpd_auth.pl PATH_NNRPYAUTH = ${PATHFILTER}/nnrpd_auth.py PATH_NNRPACCESS = ${PATHFILTER}/nnrpd_access.pl PATH_NNRPYACCESS = ${PATHFILTER}/nnrpd_access.py PATH_NNRPYDYNAMIC = ${PATHFILTER}/nnrpd_dynamic.py PATH_CONFIG = ${PATHETC}/inn.conf PATH_CONTROLCTL = ${PATHETC}/control.ctl PATH_CONTROLCTLLOCAL = ${PATHETC}/control.ctl.local PATH_EXPIRECTL = ${PATHETC}/expire.ctl PATH_INNDHOSTS = ${PATHETC}/incoming.conf PATH_MODERATORS = ${PATHETC}/moderators PATH_DISTPATS = ${PATHETC}/distrib.pats PATH_DISTRIBUTIONS = ${PATHETC}/distributions PATH_NEWSFEEDS = ${PATHETC}/newsfeeds PATH_READERSCONF = ${PATHETC}/readers.conf PATH_NNRPDTRACK = ${PATHETC}/nnrpd.track PATH_NNTPPASS = ${PATHETC}/passwd.nntp PATH_CTLWATCH = ${PATHETC}/innwatch.ctl PATH_ACTSYNC_IGN = ${PATHETC}/actsync.ign PATH_ACTSYNC_CFG = ${PATHETC}/actsync.cfg PATH_MOTD_INND = ${PATHETC}/motd.innd.sample PATH_MOTD_NNRPD = ${PATHETC}/motd.nnrpd.sample PATH_STORAGECONF = ${PATHETC}/storage.conf PATH_CYCBUFFCONFIG = ${PATHETC}/cycbuff.conf PATH_INNFEEDCTL = ${PATHETC}/innfeed.conf PATH_BUFFINDEXED = ${PATHETC}/buffindexed.conf PATH_RADIUS_CONF = ${PATHETC}/inn-radius.conf PATH_OVDB_CONF = ${PATHETC}/ovdb.conf PATH_SENDUUCP_CF = ${PATHETC}/send-uucp.cf PATH_SUBSCRIPTIONS = ${PATHETC}/subscriptions PATH_ACTIVE = ${PATHDB}/active PATH_ACTIVE_TIMES = ${PATHDB}/active.times PATH_HISTORY = ${PATHDB}/history PATH_NEWSGROUPS = ${PATHDB}/newsgroups ## Scripts from above, plus site-specific config files. REST = \ newsfeeds incoming.conf nnrpd.track passwd.nntp \ inn.conf moderators innreport.conf innreport.css localgroups \ control.ctl control.ctl.local expire.ctl nntpsend.ctl \ innwatch.ctl distrib.pats distributions actsync.cfg actsync.ign \ motd.innd motd.nnrpd storage.conf cycbuff.conf buffindexed.conf \ innfeed.conf startup_innd.pl filter_innd.pl filter_nnrpd.pl \ filter_innd.py INN.py \ innshellvars.local innshellvars.pl.local innshellvars.tcl.local \ nnrpd.py \ nnrpd_auth.pl nnrpd_access.pl nocem.ctl \ news2mail.cf readers.conf \ inn-radius.conf nnrpd_auth.py nnrpd_access.py nnrpd_dynamic.py \ ovdb.conf active.minimal \ newsgroups.minimal send-uucp.cf subscriptions ALL = $(REST) REST_INSTALLED = \ $D$(PATH_NEWSFEEDS) $D$(PATH_INNDHOSTS) \ $D$(PATH_NNRPDTRACK) $D$(PATH_NNTPPASS) \ $D$(PATH_CONFIG) $D$(PATH_MODERATORS) \ $D$(PATH_CONTROLCTL) $D$(PATH_CONTROLCTLLOCAL) $D$(PATH_EXPIRECTL) \ $D$(PATHETC)/nntpsend.ctl \ $D$(PATHETC)/innreport.conf $D$(PATHHTTP)/innreport.css \ $D$(PATHETC)/localgroups \ $D$(PATH_CTLWATCH) $D$(PATH_DISTPATS) $D$(PATH_DISTRIBUTIONS) \ $D$(PATH_ACTSYNC_CFG) $D$(PATH_ACTSYNC_IGN) \ $D$(PATH_MOTD_INND) $D$(PATH_MOTD_NNRPD) $D$(PATH_STORAGECONF) \ $D$(PATH_CYCBUFFCONFIG) $D$(PATH_BUFFINDEXED) \ $D$(PATH_INNFEEDCTL) $D$(PATH_PERL_STARTUP_INND) \ $D$(PATH_PERL_FILTER_INND) $D$(PATH_PERL_FILTER_NNRPD) \ $D$(PATH_PYTHON_FILTER_INND) $D$(PATH_PYTHON_INN_MODULE) \ $D$(PATH_PYTHON_NNRPD_MODULE) \ $D$(PATH_TCL_STARTUP) $D$(PATH_TCL_FILTER) \ $D$(PATHETC)/innshellvars.local $D$(PATHETC)/innshellvars.pl.local \ $D$(PATHETC)/innshellvars.tcl.local \ $D$(PATHETC)/nocem.ctl \ $D$(PATH_NNRPAUTH) $D$(PATHETC)/news2mail.cf $D$(PATH_READERSCONF) \ $D$(PATH_RADIUS_CONF) $D$(PATH_NNRPYAUTH) $D$(PATH_NNRPYACCESS) $D$(PATH_NNRPYDYNAMIC) \ $D$(PATH_OVDB_CONF) \ $D$(PATH_SENDUUCP_CF) $D$(PATH_SUBSCRIPTIONS) $D$(PATH_NNRPACCESS) ALL_INSTALLED = $(REST_INSTALLED) SPECIAL = $D$(PATH_ACTIVE) $D$(PATH_ACTIVE_TIMES) \ $D$(PATH_NEWSGROUPS) $D$(PATH_HISTORY) ## Get new versions of everything from samples directory. all: $(ALL) config ## Show changes between files here and ones in samples. diff: @$(MAKE) COPY=-${DIFF} all ## Show changes between files here and installed versions. diff-installed: @$(MAKE) COPY_RPRI=-${DIFF} COPY_RPUB=-${DIFF} COPY_XPRI=-${DIFF} COPY_XPUB=-${DIFF} $(ALL_INSTALLED) ## Show what would be copied from samples directory. what: @$(MAKE) -s 'COPY=@echo' $(ALL) | ${AWK} 'NF==2 { print $$2; }' config: $(ALL) date >config ## Don't use parallel rules -- we want this to be viewed carefully. install: all $(PAUSE) install-config $(RELOAD_AND_GO) reload-install: all pause install-config reload go install-config: update $(REST_INSTALLED) $(SPECIAL) ## Install scripts, not per-host config files. update: all @echo '' ; echo 'inn.conf in site directory may have newly added parameters' @echo 'which installed inn.conf does not have. Check those parameters' @echo 'before you run innd.' ; echo '' date >update ## Special rules for files that should never be overwritten if they are ## already installed. These are used only for the initial install of a ## brand new server. $D$(PATH_ACTIVE): ; $(CP_DATA) active.minimal $@ $D$(PATH_NEWSGROUPS): ; $(CP_DATA) newsgroups.minimal $@ $D$(PATH_ACTIVE_TIMES): touch $@ @ME=`$(WHOAMI)` ; \ if [ x"$$ME" = xroot ] ; then \ chown $(RUNASUSER) $@ ; \ chgrp $(RUNASGROUP) $@ ; \ fi chmod $(FILEMODE) $@ $D$(PATH_HISTORY): touch $@ @ME=`$(WHOAMI)` ; \ if [ x"$$ME" = xroot ] ; then \ chown $(RUNASUSER) $@ ; \ chgrp $(RUNASGROUP) $@ ; \ fi chmod $(FILEMODE) $@ @ME=`$(WHOAMI)` ; \ if [ -z "$D" ] ; then \ if [ x"$$ME" = xroot ] || [ x"$$ME" = x"$(RUNASUSER)" ] ; then \ $(PATHBIN)/makedbz -i -o ; \ fi ; \ fi bootstrap: ## Remove files that are unchanged from the release version. clean: @-for I in $(ALL) ; do \ cmp -s $$I ../samples/$$I && echo rm -f $$I && rm -f $$I ; \ done clobber distclean maintclean: rm -f $(ALL) profiled config update profiled: cp /dev/null profiled depend: ## Commands to make private or public, read or executable files. COPY_RPRI = $(CP_RPRI) COPY_RPUB = $(CP_RPUB) COPY_XPRI = $(CP_XPRI) COPY_XPUB = $(CP_XPUB) ## Files to copy. $D$(PATH_INNDHOSTS): incoming.conf ; $(COPY_RPRI) $? $@ $D$(PATH_NEWSFEEDS): newsfeeds ; $(COPY_RPUB) $? $@ $D$(PATH_READERSCONF): readers.conf ; $(COPY_RPUB) $? $@ $D$(PATH_RADIUS_CONF): inn-radius.conf ; $(COPY_RPRI) $? $@ $D$(PATH_NNRPDTRACK): nnrpd.track ; $(COPY_RPUB) $? $@ $D$(PATH_CONTROLCTL): control.ctl ; $(COPY_RPUB) $? $@ $D$(PATH_CONTROLCTLLOCAL): control.ctl.local ; $(COPY_RPUB) $? $@ $D$(PATH_CTLWATCH): innwatch.ctl ; $(COPY_RPUB) $? $@ $D$(PATH_EXPIRECTL): expire.ctl ; $(COPY_RPUB) $? $@ $D$(PATH_CONFIG): inn.conf ; $(COPY_RPUB) $? $@ $D$(PATH_MODERATORS): moderators ; $(COPY_RPUB) $? $@ $D$(PATH_DISTPATS): distrib.pats ; $(COPY_RPUB) $? $@ $D$(PATH_DISTRIBUTIONS): distributions ; $(COPY_RPUB) $? $@ $D$(PATH_NNTPPASS): passwd.nntp ; $(COPY_RPRI) $? $@ $D$(PATHETC)/nntpsend.ctl: nntpsend.ctl ; $(COPY_RPUB) $? $@ $D$(PATHETC)/news2mail.cf: news2mail.cf ; $(COPY_RPUB) $? $@ $D$(PATHETC)/innreport.conf: innreport.conf ; $(COPY_RPUB) $? $@ $D$(PATHHTTP)/innreport.css: innreport.css ; $(COPY_RPUB) $? $@ $D$(PATHETC)/innshellvars.local: innshellvars.local ; $(COPY_XPUB) $? $@ $D$(PATHETC)/innshellvars.pl.local: innshellvars.pl.local ; $(COPY_XPUB) $? $@ $D$(PATHETC)/innshellvars.tcl.local: innshellvars.tcl.local ; $(COPY_XPUB) $? $@ $D$(PATHETC)/localgroups: localgroups ; $(COPY_RPUB) $? $@ $D$(PATH_STORAGECONF): storage.conf ; $(COPY_RPUB) $? $@ $D$(PATH_CYCBUFFCONFIG): cycbuff.conf ; $(COPY_RPUB) $? $@ $D$(PATH_BUFFINDEXED): buffindexed.conf ; $(COPY_RPUB) $? $@ $D$(PATH_OVDB_CONF): ovdb.conf ; $(COPY_RPUB) $? $@ $D$(PATH_PERL_STARTUP_INND): startup_innd.pl ; $(COPY_RPUB) $? $@ $D$(PATH_PERL_FILTER_INND): filter_innd.pl ; $(COPY_RPUB) $? $@ $D$(PATH_PERL_FILTER_NNRPD): filter_nnrpd.pl ; $(COPY_RPUB) $? $@ $D$(PATH_PYTHON_FILTER_INND): filter_innd.py ; $(COPY_RPUB) $? $@ $D$(PATH_PYTHON_INN_MODULE): INN.py ; $(COPY_RPUB) $? $@ $D$(PATH_PYTHON_NNRPD_MODULE): nnrpd.py ; $(COPY_RPUB) $? $@ $D$(PATH_NNRPAUTH): nnrpd_auth.pl ; $(COPY_RPUB) $? $@ $D$(PATH_NNRPACCESS): nnrpd_access.pl ; $(COPY_RPUB) $? $@ $D$(PATH_NNRPYAUTH): nnrpd_auth.py ; $(COPY_RPUB) $? $@ $D$(PATH_NNRPYACCESS): nnrpd_access.py ; $(COPY_RPUB) $? $@ $D$(PATH_NNRPYDYNAMIC): nnrpd_dynamic.py ; $(COPY_RPUB) $? $@ $D$(PATHETC)/nocem.ctl: nocem.ctl ; $(COPY_RPUB) $? $@ $D$(PATH_ACTSYNC_CFG): actsync.cfg ; $(COPY_RPUB) $? $@ $D$(PATH_ACTSYNC_IGN): actsync.ign ; $(COPY_RPUB) $? $@ $D$(PATH_MOTD_INND): motd.innd ; $(COPY_RPUB) $? $@ $D$(PATH_MOTD_NNRPD): motd.nnrpd ; $(COPY_RPUB) $? $@ $D$(PATH_INNFEEDCTL): innfeed.conf ; $(COPY_RPRI) $? $@ $D$(PATH_SENDUUCP_CF): send-uucp.cf ; $(COPY_RPUB) $? $@ $D$(PATH_SUBSCRIPTIONS): subscriptions ; $(COPY_RPUB) $? $@ REASON = 'Installing site config files from site/Makefile' go pause: -${CTLINND} $@ $(REASON) reload: -${CTLINND} reload all $(REASON) ## Use this to just replace any changed files you might have made. Only ## do this after you've examined the output of "make -n"! replace: $(MAKE) COPY=cp all ## Get files from the samples directory. COPY = $(SHELL) ./getsafe.sh actsync.cfg: ../samples/actsync.cfg ; $(COPY) $? $@ actsync.ign: ../samples/actsync.ign ; $(COPY) $? $@ control.ctl: ../samples/control.ctl ; $(COPY) $? $@ control.ctl.local: ../samples/control.ctl.local ; $(COPY) $? $@ expire.ctl: ../samples/expire.ctl ; $(COPY) $? $@ nnrpd_auth.pl: ../samples/nnrpd_auth.pl ; $(COPY) $? $@ nnrpd_access.pl: ../samples/nnrpd_access.pl ; $(COPY) $? $@ nnrpd_auth.py: ../samples/nnrpd_auth.py ; $(COPY) $? $@ nnrpd_access.py: ../samples/nnrpd_access.py ; $(COPY) $? $@ nnrpd_dynamic.py: ../samples/nnrpd_dynamic.py ; $(COPY) $? $@ filter_innd.pl: ../samples/filter_innd.pl ; $(COPY) $? $@ filter_nnrpd.pl: ../samples/filter_nnrpd.pl ; $(COPY) $? $@ filter_innd.py: ../samples/filter_innd.py ; $(COPY) $? $@ INN.py: ../samples/INN.py ; $(COPY) $? $@ nnrpd.py: ../samples/nnrpd.py ; $(COPY) $? $@ incoming.conf: ../samples/incoming.conf ; $(COPY) $? $@ inn.conf: ../samples/inn.conf ; $(COPY) $? $@ innreport.conf: ../samples/innreport.conf ; $(COPY) $? $@ innreport.css: ../samples/innreport.css ; $(COPY) $? $@ innshellvars.local: ../samples/innshellvars.local ; $(COPY) $? $@ innshellvars.pl.local: ../samples/innshellvars.pl.local ; $(COPY) $? $@ innshellvars.tcl.local: ../samples/innshellvars.tcl.local ; $(COPY) $? $@ localgroups: ../samples/localgroups ; $(COPY) $? $@ storage.conf: ../samples/storage.conf ; $(COPY) $? $@ cycbuff.conf: ../samples/cycbuff.conf ; $(COPY) $? $@ buffindexed.conf: ../samples/buffindexed.conf ; $(COPY) $? $@ ovdb.conf: ../samples/ovdb.conf ; $(COPY) $? $@ innwatch.ctl: ../samples/innwatch.ctl ; $(COPY) $? $@ innfeed.conf: ../samples/innfeed.conf ; $(COPY) $? $@ moderators: ../samples/moderators ; $(COPY) $? $@ distrib.pats: ../samples/distrib.pats ; $(COPY) $? $@ distributions: ../samples/distributions ; $(COPY) $? $@ motd.innd: ../samples/motd.innd ; $(COPY) $? $@ motd.nnrpd: ../samples/motd.nnrpd ; $(COPY) $? $@ news2mail.cf: ../samples/news2mail.cf ; $(COPY) $? $@ newsfeeds: ../samples/newsfeeds ; $(COPY) $? $@ nnrpd.track: ../samples/nnrpd.track ; $(COPY) $? $@ nntpsend.ctl: ../samples/nntpsend.ctl ; $(COPY) $? $@ nocem.ctl: ../samples/nocem.ctl ; $(COPY) $? $@ parsecontrol: ../samples/parsecontrol ; $(COPY) $? $@ passwd.nntp: ../samples/passwd.nntp ; $(COPY) $? $@ readers.conf: ../samples/readers.conf ; $(COPY) $? $@ inn-radius.conf: ../samples/inn-radius.conf ; $(COPY) $? $@ startup_innd.pl: ../samples/startup_innd.pl ; $(COPY) $? $@ send-uucp.cf: ../samples/send-uucp.cf ; $(COPY) $? $@ subscriptions: ../samples/subscriptions ; $(COPY) $? $@ active.minimal: ../samples/active.minimal ; $(COPY) $? $@ newsgroups.minimal: ../samples/newsgroups.minimal ; $(COPY) $? $@ inn-2.6.0/site/getsafe.sh0000644000175200017520000000150512575023702014637 0ustar iuliusiulius#! /bin/sh ## $Revision: 847 $ ## ## Safely get a file from the samples directory. Usage: ## getsafe case $# in 2) ;; *) echo "Can't get INN sample file: wrong number of arguments." 1>&2 exit 1 ;; esac SRC=$1 DEST=$2 ## Try RCS. if [ -f RCS/${DEST},v ] ; then echo "Note: ${SRC} has changed; please compare." test -f ${DEST} && exit 0 exec co -q ${DEST} fi ## Try SCCS. if [ -f SCCS/s.${DEST} ] ; then echo "Note: ${SRC} has changed; please compare." test -f ${DEST} && exit 0 exec sccs get -s ${DEST} fi ## File exist locally? if [ -f ${DEST} ] ; then cmp ${SRC} ${DEST} if [ $? -eq 0 ] ; then touch ${DEST} exit 0 fi echo "${SRC} has changed; please update ${DEST}" exit 1 fi echo Using sample version of ${DEST} cp ${SRC} ${DEST} exit 0 inn-2.6.0/doc/0000755000175200017520000000000012575023700012463 5ustar iuliusiuliusinn-2.6.0/doc/man/0000755000175200017520000000000012575023677013253 5ustar iuliusiuliusinn-2.6.0/doc/man/inndf.80000644000175200017520000001762512575023702014442 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "INNDF 8" .TH INNDF 8 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" inndf \- Report free disk, inodes, and overview information .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fBinndf\fR [\fB\-Fhi\fR] [\fB\-f\fR \fIfilename\fR] \fIdirectory\fR [\fIdirectory\fR ...] .PP \&\fBinndf\fR \fB\-n\fR .PP \&\fBinndf\fR \fB\-o\fR .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\fBinndf\fR was originally a replacement for \f(CW\*(C`df | awk\*(C'\fR in \fIinnwatch.ctl\fR\|(5) and \fIinnstat\fR\|(8), and now also reports various other usage information about \&\s-1INN\s0's storage that \fIdf\fR\|(1) doesn't understand. \fBinndf\fR doesn't sync, forks less, and is generally less complicated than \fIdf\fR\|(1). .PP Its default behavior is to report free kilobytes (not disk blocks), or free inodes if \fB\-i\fR is used, in the file systems holding the directories given on the command line. (A kilobyte in this case is 1024 bytes.) If only one directory is given, the output will be a simple number; if more than one directory is given, the output will be formatted for human readability. .PP If \fIenableoverview\fR is set to true in \fIinn.conf\fR, \fBinndf\fR can also be used to get information about the overview database. With the \fB\-n\fR option, it reports a count of the total number of overview records stored. With \fB\-o\fR, it reports the percentage of space used in the overview database (for those overview methods where this is meaningful data). .SH "OPTIONS" .IX Header "OPTIONS" .IP "\fB\-f\fR \fIfilename\fR" 4 .IX Item "-f filename" \&\fIfilename\fR should contain a list of directories to use in addition to those given by the arguments, one per line. Blank lines and anything after \f(CW\*(C`#\*(C'\fR on any line are ignored. .IP "\fB\-F\fR" 4 .IX Item "-F" Like \fB\-f\fR except that the filename is \fIpathetc\fR/filesystems and it is not an error if this file doesn't exist. (This option is used primarily by such things as \fIinnstat\fR\|(8), so that the news administrator can add additional file systems to check to \fIpathetc\fR/filesystems without having to modify the script.) .IP "\fB\-h\fR" 4 .IX Item "-h" Print a usage message and exit. .IP "\fB\-i\fR" 4 .IX Item "-i" Report the number of free inodes rather than the amount of free disk space. .IP "\fB\-n\fR" 4 .IX Item "-n" Report the total number of records in the overview database. Note that crossposted articles will have one overview record for each newsgroup they're posted to. .IP "\fB\-o\fR" 4 .IX Item "-o" Report the percentage usage of the overview database space. This is only meaningful for overview methods that pre-allocate a certain amount of space rather than grow to accomodate more records. Currently, this flag is only useful for the buffindexed overview method. .SH "EXAMPLES" .IX Header "EXAMPLES" Print the free kilobytes in \fIpathspool\fR as a simple number: .PP .Vb 1 \& inndf .Ve .PP Report the free inodes in \fIpathnews\fR and \fIpathspool\fR in a format designed for human readability: .PP .Vb 1 \& inndf \-i .Ve .PP The same, but also add in all file systems in \fIpathetc\fR/filesystems: .PP .Vb 1 \& inndf \-i \-F .Ve .PP Print out the number of overview records and the percentage space used by a buffindexed overview database: .PP .Vb 1 \& inndf \-no .Ve .SH "HISTORY" .IX Header "HISTORY" \&\fBinndf\fR was written by Ian Dickinson . This manual page was written by Swa Frantzen . Thanks also to the following folks for ports, patches, and comments: .PP .Vb 7 \& Mahesh Ramachandran \& Chuck Swiger \& Sang\-yong Suh \& Brad Dickey \& Taso N. Devetzis \& Wei\-Yeh Lee \& Jeff Garzik .Ve .PP and to all the other folks I met and worked with during my 10 years as a newsadmin. .PP Katsuhiro Kondou added the \fB\-n\fR and \fB\-o\fR options. Russ Allbery added reporting of percentage free disk space. Support for \fB\-f\fR and \fB\-F\fR was added by Fabien Tassin . .PP \&\f(CW$Id:\fR inndf.pod 8232 2008\-12\-14 17:05:57Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIdf\fR\|(1), \fIinnwatch.ctl\fR\|(5), \fIinnstat\fR\|(8). inn-2.6.0/doc/man/scanlogs.80000644000175200017520000001422012575023702015141 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "SCANLOGS 8" .TH SCANLOGS 8 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" scanlogs \- Summarize and rotate INN log files .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fBscanlogs\fR [\fBnorotate\fR] .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\fBscanlogs\fR summarizes the information recorded in the \s-1INN\s0 log files which reside in the \fIpathlog\fR directory set in \fIinn.conf\fR (see \fInewslog\fR\|(5) for further details about these log files). It is normally invoked by the \&\fInews.daily\fR\|(8) script which performs daily server maintenance tasks. .PP It invokes \f(CW\*(C`ctlinnd flushlogs\*(C'\fR to close the news and error log files, rename them to add \f(CW\*(C`.old\*(C'\fR to the file names and open fresh news and error logs; the \fIactive\fR file is also flushed to disk, along with the history database. .PP By default, \fBscanlogs\fR rotates and cleans out the logs. It keeps up to \&\fIlogcycles\fR old compressed log files in \fIpathlog\fR/OLD (the \fIlogcycles\fR parameter can be set in \fIinn.conf\fR). \fBscanlogs\fR also keeps archives of the \fIactive\fR file in this directory. .PP It invokes \fBtally.control\fR if \fInewgroup.log\fR or \fIrmgroup.log\fR exists in \fIpathlog\fR (see the \fIcontrol.log\fR entry of \fInewslog\fR\|(5) for more information about that). .PP \&\fBscanlogs\fR displays the first 50 lines of \fIerrlog\fR, \fInews.err\fR and \fInews.crit\fR, if non-empty, and runs \fBinnreport\fR to summarize the contents of \fInews\fR and \fInews.notice\fR, and to update the \&\fIunwanted.log\fR file amongst other things (see more information about that in \fIinnreport\fR\|(8)). .SH "OPTIONS" .IX Header "OPTIONS" Only one option is currently accepted: .IP "\fBnorotate\fR" 4 .IX Item "norotate" Using this option disables the rotating and cleaning aspect of the log processing: the logs files are only scanned for information and no contents are altered. If \fBscanlogs\fR is invoked more than once a day, the \fBnorotate\fR option should be used to prevent premature log cleaning. .SH "FILES" .IX Header "FILES" See \fInewslog\fR\|(5) for the list of log files processed by \fBscanlogs\fR. .SH "HISTORY" .IX Header "HISTORY" Written by Landon Curt Noll and Rich \f(CW$alz\fR for InterNetNews. Converted to \s-1POD\s0 by Julien Elie. .PP \&\f(CW$Id:\fR scanlogs.pod 9903 2015\-06\-20 17:20:46Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIinn.conf\fR\|(5), \fIinnreport\fR\|(8), \fInews.daily\fR\|(8), \fInewslog\fR\|(5), \fIshlock\fR\|(1), \&\fItally.control\fR\|(8). inn-2.6.0/doc/man/innxmit.80000644000175200017520000002200312575023702015014 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "INNXMIT 8" .TH INNXMIT 8 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" innxmit \- Send Usenet articles to a remote NNTP server .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fBinnxmit\fR [\fB\-acdHlprsv\fR] [\fB\-P\fR \fIportnum\fR] [\fB\-T\fR \fIseconds\fR] [\fB\-t\fR \fIseconds\fR] \fIhost\fR \fIfile\fR .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\fBinnxmit\fR connects to the \s-1NNTP\s0 server at the specified \fIhost\fR (validating itself via \fIpasswd.nntp\fR if possible) and sends it the articles specified in the batch file named \fIfile\fR. It is normally invoked by a script run out of \fIcron\fR\|(8) that uses \fBshlock\fR to lock the host name, followed by a \fBctlinnd\fR command to flush the batch file. If \fIfile\fR is not an absolute path name, it is taken relative to the directory specified by the \fIpathoutgoing\fR parameter in \fIinn.conf\fR. It is normally written by specifying the \f(CW\*(C`Wnm\*(C'\fR flags in the \fInewsfeeds\fR file. Each line in the batch file should be in one of the following formats: .PP .Vb 2 \& article Message\-ID \& article .Ve .PP The first field is either the storage \s-1API\s0 token of an article or the name of a file holding an article. If it is not an absolute pathname or a storage \s-1API\s0 token, it is taken to be a file name relative to \&\fIpatharticles\fR in \fIinn.conf\fR. If the second field is not specified, the message-ID will be obtained by scanning the article. The two fields, if present, are separated by a space. If a communication error such as a \fIwrite\fR\|(2) failure occurs, \fBinnxmit\fR will stop sending and rewrite the batch file to contain the current article and any other unsent articles. .PP An alternative to \fBinnxmit\fR can be \fBinnduct\fR, mentioned in the \&\fIinnfeed\fR\|(8) man page. .SH "OPTIONS" .IX Header "OPTIONS" .IP "\fB\-a\fR" 4 .IX Item "-a" If all articles were sent successfully, \fBinnxmit\fR will remove the batch file; otherwise it will rewrite it to contain the list of unsent articles. If no articles were sent or rejected, the file is left untouched. This can cause the batch file to grow excessively large if many articles have been expired and there are communication problems. To always rewrite the batch file, use the \fB\-a\fR flag. .IP "\fB\-c\fR" 4 .IX Item "-c" In streaming mode, a check of each message-ID is still made to avoid sending articles already on the server. The \fB\-c\fR flag will, if streaming mode is supported, result in sending articles without checking. This results in slightly greater throughput and may be appropriate when it is known that the site could not already have the articles such as in the case of a \*(L"leaf\*(R" site. .IP "\fB\-d\fR" 4 .IX Item "-d" Use the \fB\-d\fR flag to print debugging information on standard error. This will show the protocol transactions between \fBinnxmit\fR and the \s-1NNTP\s0 server on the remote host. .IP "\fB\-H\fR" 4 .IX Item "-H" If the \fB\-H\fR flag is given, then only headers are sent to \fIhost\fR for all articles except control messages. And the Bytes: header is also included even if it does not exist in the original article. The \fB\-H\fR flag is useful for Diablo reader. .IP "\fB\-l\fR" 4 .IX Item "-l" The \fB\-l\fR flag is used to turn on logging of reasons the remote gives for rejecting an article. .IP "\fB\-P\fR \fIportnum\fR" 4 .IX Item "-P portnum" To specify a port number other than the default, use the \fB\-P\fR flag. .IP "\fB\-p\fR" 4 .IX Item "-p" If the \fB\-p\fR flag is given, then no connection is made and the batch file is purged of entries that refer to files that no longer exist. This implies the \fB\-a\fR flag. .IP "\fB\-r\fR" 4 .IX Item "-r" If the remote server sends an unexpected reply code, \fBinnxmit\fR will requeue the article and proceed. Use the \fB\-r\fR flag if the article should not be requeued. .IP "\fB\-s\fR" 4 .IX Item "-s" \&\fBinnxmit\fR will attempt to negotiate a streaming mode extension of the \s-1NNTP\s0 protocol with the server at connect time. If successful, it will use a slightly different protocol that enhances throughput. If the server does not recognize the streaming mode negotiation, \fBinnxmit\fR will revert to normal \s-1NNTP\s0 transfer mode. Use the \fB\-s\fR flag to disable the attempt to negotiate the streaming mode extension. .IP "\fB\-T\fR \fIseconds\fR" 4 .IX Item "-T seconds" To specify the total amount of time that should be allowed for article transfers, use the \fB\-T\fR flag. The default is to wait until an I/O error occurs, or all the articles have been transferred. If the \fB\-T\fR flag is used, the time is checked just before each article is started; it will not abort a transfer that is in progress. .IP "\fB\-t\fR \fIseconds\fR" 4 .IX Item "-t seconds" \&\fBinnxmit\fR normally blocks until the connection is made. To specify a timeout on how long to try to make the connection, use the \fB\-t\fR flag. .IP "\fB\-v\fR" 4 .IX Item "-v" Upon exit, \fBinnxmit\fR reports transfer and \s-1CPU\s0 usage statistics via syslog. If the \fB\-v\fR flag is used, they will also be printed on the standard output. .SH "HISTORY" .IX Header "HISTORY" Written by Rich \f(CW$alz\fR for InterNetNews. Converted to \s-1POD\s0 by Julien Elie. .PP \&\f(CW$Id:\fR innxmit.pod 9588 2013\-12\-19 17:46:41Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIctlinnd\fR\|(8), \fIinn.conf\fR\|(5), \fIinnd\fR\|(8), \fInewsfeeds\fR\|(5), \fIpasswd.nntp\fR\|(5), \fIshlock\fR\|(1). inn-2.6.0/doc/man/innxbatch.80000644000175200017520000000543012575023702015311 0ustar iuliusiulius.TH INNXBATCH 8 .SH NAME innxbatch \- send xbatched Usenet articles to a remote NNTP server .SH SYNOPSIS .I innxbatch [ .B \-D ] [ .BI \-t " timeout" ] [ .BI \-T " timeout" ] [ .B \-v ] .I host .I file ... .SH DESCRIPTION .I Innxbatch connects to the NNTP server at the specified .I host and sends it the specified xbatch files, using the XBATCH extension to the NNTP protocol. It is normally invoked by a script run out of .IR cron (8) that uses .IR shlock (1) to lock the host name, followed by a .IR ctlinnd (8) command to flush the batchfile. .PP Each file is removed after it has been successfully transferred. .PP If a communication error such as a .IR write (2) failure, or an unexpected reply from the remote server occurs, .I innxbatch will stop sending and leave all remaining files untouched for later retry. .SH OPTIONS .TP .B \-t seconds .I Innxbatch normally blocks until the connection is made. To specify a timeout on how long to try to make the connection, use the ``\-t'' flag. .TP .B \-T seconds To specify the total amount of time that should be allowed for article transfers, use the ``\-T'' flag. .br The default is to wait until an I/O error occurs, or all the articles have been transferred. If the ``\-T'' flag is used, the time is checked just before each article is started; it will not abort a transfer that is in progress. .TP .B \-v Upon exit, .I innxbatch reports transfer and CPU usage statistics via .IR syslog (3). If the ``\-v'' flag is used, they will also be printed on the standard output. .TP .B \-D Use the ``\-D'' flag to print debugging information on standard error. This will show the protocol transactions between .I innxbatch and the NNTP server on the remote host. .SH EXAMPLES A sample .I newsfeeds(5) entry to produce appropriate xbatch files (thanks to Karsten Leipold ): .sp 1 .nf nase\e :*\e :Tc,Wnb\e .ds R$ :\*(R$/batcher \e .ds R$ <$ac_cv_path_COMPRESS in config.cache> .ds P$ -p "(\*(R$ >\e \*(P$/nase.\e$\e$)" \e nase.do.main .fi .sp 1 A sample script to invoke .I innxbatch(8) is: .sp 1 .nf #!/bin/sh ## SH script to send xbatches for a site, wrapped around innxbatch ## Invocation: ## sendxbatches.sh ... if [ $# -le 3 ] then echo "usage: $0 " exit 1 fi . /innshellvars site="$1"; host="$2"; shift; shift ctlinnd flush "$site" \e && sleep 5 \e && exec $NEWSBIN/innxbatch -v -D "$host" $* .fi .SH HISTORY Written by Stefan Petri , modelled after .IR innxmit (8) and the XBATCH patch for the nntp reference implementation. .SH "SEE ALSO" ctlinnd(8), inn.conf(5), innd(8), innxmit(8), newsfeeds(5), nntpsend(8), shlock(1). inn-2.6.0/doc/man/buffindexed.conf.50000644000175200017520000002434412575023702016544 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "BUFFINDEXED.CONF 5" .TH BUFFINDEXED.CONF 5 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" buffindexed.conf \- Configuration for the buffindexed overview method .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\fIbuffindexed.conf\fR, found in \fIpathetc\fR, specifies the buffers that the buffindexed overview method should use. It is required if the server uses buffindexed (as configured by the \fIovmethod\fR parameter in \fIinn.conf\fR). .PP Buffindexed uses pre-built buffer files to store overview data and indexes to that data. The buffers are divided into 8\ \s-1KB\s0 internally, and a given block is used either for overview data or for index data. A block is always allocated to a single newsgroup and is never shared among newsgroups. .PP In addition to the buffers, buffindexed also stores information in a file named \fIgroup.index\fR in \fIpathdb\fR. (This file should not be mistaken for the one named \fIgroup.index\fR in \fIpathoverview\fR which is used by the tradindexed overview method.) It contains information about each newsgroup: the pointer to the index block for the newsgroup, the high mark, the low mark, the flag of the group, the number of articles, and so forth. This file is created automatically when all buffers are initialized and should not be manually edited. .PP Buffindexed buffers are of fixed size, so buffindexed will never use more space than what is available in those buffers. If all buffers are full, \&\fBinnd\fR will throttle when it attempts to store overview information for any additional articles until space is freed (with \fBexpireover\fR, for instance) or another buffer is added. This is unlike the \s-1CNFS\s0 storage method. .PP You can see the current usage of the buffers with the \fB\-o\fR option to \&\fBinndf\fR. .PP In the \fIbuffindexed.conf\fR file, blank lines and lines beginning with a number sign (\f(CW\*(C`#\*(C'\fR) are ignored. All other lines must be of the format: .PP .Vb 1 \& :: .Ve .PP The order of lines is not significant. .PP is the index of this overview buffer and must be unique. Other than that constraint, it can be any number between 0 and 65535. .PP is the path to the buffer. The length of the path should not be longer than 63 characters. .PP is the length of the buffer in kilobytes (1\ \s-1KB\s0\ =\ 1024\ bytes). If does not specify a special device, the file size of the buffer must be \ *\ 1024\ bytes. If it does specify a special device, that device must have at least space available. For more information on setting up the buffers, see \*(L"\s-1CREATING\s0 \s-1BUFFERS\s0\*(R". .PP An example of \fIbuffindexed.conf\fR file can be: .PP .Vb 2 \& 0:/OV1:1536000 \& 1:/OV2:1536000 .Ve .PP When you first start \fBinnd\fR with everything configured properly, you should see messages like this in \fIpathlog\fR/news.notice: .PP .Vb 2 \& Aug 27 00:00:00 kevlar innd: buffindexed: no magic cookie found \& for ovbuff 0, initializing .Ve .PP You \s-1MUST\s0 recreate overview completely using \fBmakehistory\fR if you remove or replace buffers. However, new buffers can be added without any special care (other than restarting \fBinnd\fR after modifying \fIbuffindexed.conf\fR). If you need to rebuild overview, you should zero all of the buffers first. .SH "CREATING BUFFERS" .IX Header "CREATING BUFFERS" There are two methods to create a new buffindexed buffer: .IP "1." 4 Create a large file on top of a regular file system. The easiest way to do this is probably with \fIdd\fR\|(1), using a command like: .Sp .Vb 1 \& dd if=/dev/zero of=/path/to/cycbuff bs=1024 count= .Ve .Sp where is the size from the relevant line in \fIbuffindexed.conf\fR. .Sp This is the simplest method, but has the disadvantage that very large files on regular file systems can be fairly slow to access, particularly at the end of the file, and \s-1INN\s0 incurs unnecessary file system overhead when accessing the buffer. .IP "2." 4 Use block devices directly. If your operating system allows you to call \&\fImmap()\fR on block devices (Solaris and recent versions of Linux do, FreeBSD at last report does not), this method can avoid all of the native file system overhead. Note, however, that Solaris has problems with byte range locking on block devices, and therefore this method should not be used on Solaris. .Sp Partition the disk. If you're using Solaris, set up your partitions to avoid the first cylinder of the disk (or otherwise the buffindexed header will overwrite the disk partition table and render the buffers inaccessible). Then, create device files for each block device you're going to use. .Sp It's not recommended to use the block device files in \fI/dev\fR, since the news system doesn't have permission to write to them and changing the permissions of the system device files may affect something else. Instead, use \fImknod\fR\|(1) to create a new set of block devices (in somewhere like \fIpathspool\fR/overview that's only writable by the news user). To do this, run \f(CW\*(C`ls \-Ll\*(C'\fR on the devices in \fI/dev\fR that correspond to the block devices that you want to use. The major and minor device numbers are in the fifth and sixth columns (right before the date), respectively. Then run mknod like: .Sp .Vb 1 \& mknod b .Ve .Sp where is the path to the device to create (matching the part of the buffindexed configuration line) and and are the major and minor device numbers as discovered above. .Sp Here's a short script to do this when given the path to the system device file as an argument: .Sp .Vb 8 \& #!/bin/sh \& base=\`echo "$1" | sed \*(Aqs%.*/%%\*(Aq\` \& major=\`ls \-Ll "$1" | awk \*(Aq{print $5}\*(Aq | tr \-d ,\` \& minor=\`ls \-Ll "$1" | awk \*(Aq{print $6}\` \& mkdir \-p \& mknod /"$base" b "$major" "$minor" \& chown news:news /"$base" \& chmod 644 /"$base" .Ve .Sp Make sure that the created files are owned by the news user and news group, as specified at configure time (the default being \f(CW\*(C`news\*(C'\fR for both). Also make sure that the permissions on the devices allow the news user to read and write. .SH "HISTORY" .IX Header "HISTORY" Written by Katsuhiro Kondou for InterNetNews. Converted to \s-1POD\s0 by Russ Allbery . .PP \&\f(CW$Id:\fR buffindexed.conf.pod 9925 2015\-08\-08 17:05:43Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIexpireover\fR\|(8), \fIinn.conf\fR\|(5), \fIinndf\fR\|(8), \fImakehistory\fR\|(8). inn-2.6.0/doc/man/domain.80000644000175200017520000001332612575023702014605 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "DOMAIN 8" .TH DOMAIN 8 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" domain \- nnrpd domain resolver .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fBdomain\fR \fBdomainname\fR .SH "DESCRIPTION" .IX Header "DESCRIPTION" This program can be used in \fIreaders.conf\fR to grant access based on the subdomain part of the remote hostname. In particular, it only returns success if the remote hostname ends in \fBdomainname\fR. (A leading dot on \&\fBdomainname\fR is optional; even without it, the argument must match on dot-separated boundaries). The \*(L"username\*(R" returned is whatever initial part of the remote hostname remains after \fBdomainname\fR is removed. It is an error if there is no initial part (that is, if the remote hostname is \fIexactly\fR the specified \fBdomainname\fR). .SH "EXAMPLE" .IX Header "EXAMPLE" The following \fIreaders.conf\fR\|(5) fragment grants access to hosts with internal domain names: .PP .Vb 4 \& auth internal { \& res: "domain .internal" \& default\-domain: "example.com" \& } \& \& access internal { \& users: "*@example.com" \& newsgroups: example.* \& } .Ve .PP Access is granted to the example.* groups for all connections from hosts that resolve to hostnames ending in \f(CW\*(C`.internal\*(C'\fR; a connection from \&\*(L"foo.internal\*(R" would match access groups as \*(L"foo@example.com\*(R". .SH "BUGS" .IX Header "BUGS" It seems the code does not confirm that the matching part is actually at the end of the remote hostname (e.g., \*(L"domain: example.com\*(R" would match the remote host \*(L"foo.example.com.org\*(R" by ignoring the trailing \*(L".org\*(R" part). .PP Does this resolver actually provide any useful functionality not available by using wildcards in the \fIreaders.conf\fR\|(5) \fIhosts\fR parameter? If so, the example above should reflect this functionality. .SH "HISTORY" .IX Header "HISTORY" This documentation was written by Jeffrey M.\ Vinocur . .PP \&\f(CW$Id:\fR domain.pod 8200 2008\-11\-30 13:31:30Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fInnrpd\fR\|(8), \fIreaders.conf\fR\|(5) inn-2.6.0/doc/man/shrinkfile.10000644000175200017520000000416412575023702015465 0ustar iuliusiulius.\" $Revision: 5909 $ .TH SHRINKFILE 1 .SH NAME shrinkfile \- shrink a file on a line boundary .SH SYNOPSIS .B shrinkfile [ .B \-n ] [ .BI \-m " maxsize" ] [ .BI \-s " size" ] [ .B \-v ] .I file... .SH DESCRIPTION The .I shrinkfile program shrinks files to a given .I size if the size is larger than .IR maxsize , preserving the data at the end of the file. Truncation is performed on line boundaries, where a line is a series of bytes ending with a newline, ``\en''. There is no line length restriction and files may contain any binary data. .PP Temporary files are created in the .I directory. The ``TMPDIR'' environment variable may be used to specify a different directory. .PP A newline will be added to any non-empty file that does not end with a newline. The maximum file size will not be exceeded by this addition. .SH OPTIONS .TP .B \-s By default, .I size is assumed to be zero and files are truncated to zero bytes. By default, .I maxsize is the same as .IR size . If .I maxsize is less than .IR size , .I maxsize is reset to .IR size . The ``\fB\-s\fP'' flag may be used to change the truncation size. Because the program truncates only on line boundaries, the final size may be smaller then the specified truncation size. The .I size and .I maxsize parameter may end with a ``k'', ``m'', or ``g'', indicating kilobyte (1024), megabyte (1048576) or gigabyte (1073741824) lengths. Uppercase letters are also allowed. The maximum file size is 2147483647 bytes. .TP .B \-v If the ``\fB\-v\fP'' flag is used, then .I shrinkfile will print a status line if a file was shrunk. .TP .B \-n If the ``\fB\-n\fP'' flag is used, then .I shrinkfile will exit 0 if any file is larger than .I maxsize and exit 1 otherwise. No files will be altered. .SH EXAMPLES .PP Example usage: .sp 1 .RS .nf shrinkfile -s 4m curds shrinkfile -s 1g -v whey shrinkfile -s 500k -m 4m -v curds whey if shrinkfile -n -s 100m whey; then echo whey is way too big; fi .fi .RE .PP .SH HISTORY Written by Landon Curt Noll and Rich $alz for InterNetNews. .de R$ This is revision \\$3, dated \\$4. .. .SH "SEE ALSO" inn.conf(5) inn-2.6.0/doc/man/actsync.80000644000175200017520000010016612575023702015001 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "ACTSYNC 8" .TH ACTSYNC 8 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" actsync, actsyncd \- Synchronize newsgroups .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fBactsync\fR [\fB\-AkmT\fR] [\fB\-b\fR \fIhostid\fR] [\fB\-d\fR \fIhostid\fR] [\fB\-g\fR \fImax\fR] [\fB\-i\fR \fIignore-file\fR] [\fB\-I\fR \fIhostid\fR] [\fB\-l\fR \fIhostid\fR] [\fB\-n\fR \fIname\fR] [\fB\-o\fR \fIformat\fR] [\fB\-p\fR \fImin-unchanged\fR] [\fB\-q\fR \fIhostid\fR] [\fB\-s\fR \fIsize\fR] [\fB\-t\fR \fIhostid\fR] [\fB\-v\fR \fIverbosity\fR] [\fB\-w\fR \fIseconds\fR] [\fB\-z\fR \fIseconds\fR] [\fIhost\fR] \fIhost\fR .PP \&\fBactsyncd\fR \fIconfig\fR [\fIdebug-level\fR [\fIdebug-format\fR]] .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\fBactsync\fR permits one to synchronize, compare, or merge two \fIactive\fR files. With this utility one may add, change, or remove newsgroups on the local news server to make it similar to the list of the newsgroups found on another system or file. The synchronization need not be exact. Local differences in newsgroup lists may be maintained and preserved. Certain newsgroup errors may be detected and optionally corrected. .PP There are several reasons to run \fBactsync\fR (or \fBactsyncd\fR) on a periodic basis. Among the reasons are: .IP "\(bu" 2 A control message to add, change or remove a newsgroup may fail to reach your site. .IP "\(bu" 2 Your \fIcontrol.ctl\fR may be out of date or incomplete. .IP "\(bu" 2 News articles for a new newsgroup may arrive ahead (sometimes days ahead) of the control message. .IP "\(bu" 2 Control messages may be forged, thus bypassing the restrictions found in \fIcontrol.ctl\fR unless you set up \s-1PGP\s0 authentication (and even then, not all hierarchies use \s-1PGP\s0 authentication). .IP "\(bu" 2 Your \fIactive\fR file may have been trashed. .PP If either \fIhost\fR argument begins with \f(CW\*(C`.\*(C'\fR or \f(CW\*(C`/\*(C'\fR, it is assumed to be the name of a file containing information in the \fIactive\fR\|(5) format. The \&\fIgetlist\fR\|(1) utility may be used to obtain a copy of a remote system's \fIactive\fR file via its \s-1NNTP\s0 server, or an \s-1FTP\s0 client program can retrieve such a file from an \s-1FTP\s0 archive (such as ; see more about this below). Newsgroup information from a file may be treated as if it was obtained from a host. In this man page, the \fIhost\fR arguments on the command line are called hosts, even though they may be file names. .PP If a host argument does not begin with \f(CW\*(C`.\*(C'\fR or \f(CW\*(C`/\*(C'\fR, it is assumed to be a hostname or Internet address. In this case, \fBactsync\fR will attempt to use the \s-1NNTP\s0 protocol to obtain a copy of the specified system's \&\fIactive\fR file. If the host argument contains \f(CW\*(C`:\*(C'\fR, the right side will be considered the port to connect to on the remote system. If no port number is specified, \fBactsync\fR will connect to port \f(CW119\fR. .PP Regardless how the \fIactive\fR file information is obtained, the actions of \&\fBactsync\fR remain the same. .PP The first host specified is taken to be the local host, the one where any changes would be made. The second host specified is the remote host that is being synchronized with. If only one host is specified, it is assumed to be the remote host to synchronize with, and the local host is assumed to be the default local \s-1NNTP\s0 server as specified by the \s-1NNTPSERVER\s0 environment variable or by the \fIserver\fR value found in \fIinn.conf\fR. .PP If either host is specified as \f(CW\*(C`\-\*(C'\fR, the default server will be used for that host, determined as described above. .PP The newsgroup synchronization, by default, involves all newsgroups found on both hosts. One may also synchronize a subset of newsgroups by directing \fBactsync\fR to ignore certain newsgroups from both systems. Only newsgroups with valid names will be synchronized. To be valid, a newsgroup name must consist only of alphanumeric characters, \f(CW\*(C`.\*(C'\fR, \f(CW\*(C`+\*(C'\fR, \&\f(CW\*(C`\-\*(C'\fR, and \f(CW\*(C`_\*(C'\fR. One may not have two \f(CW\*(C`.\*(C'\fR characters in a row. The first character must be alphanumeric, as must any character following \f(CW\*(C`.\*(C'\fR. The name may not end in a \f(CW\*(C`.\*(C'\fR character. .PP The \fBactsyncd\fR daemon provides a convenient interface to configure and run \fBactsync\fR. If a host is not initially reachable, the daemon will retry up to 9 additional times, waiting 6 minutes before each retry. This daemon runs in the foreground, sending output to standard output and standard error. It then uses \fImod\-active\fR\|(8) to update the \fIactive\fR file if there are commands for \fBctlinnd\fR in its output. .PP The configuration filename for the daemon is given as a command line argument, usually \fIactsync.cfg\fR in \fIpathetc\fR. The config file can contain the following options: .PP .Vb 4 \& host= \& ftppath= \& ignore_file= \& flags= .Ve .PP The host=, ignore_file=, and flags= lines are mandatory. Each keyword must start at the beginning of the line, and there may be no whitespace before the \f(CW\*(C`=\*(C'\fR character. Blank lines are ignored, as are comment lines that start with \f(CW\*(C`#\*(C'\fR. Any other lines may produce undefined results. .PP The setting refers to the second (remote) \fIhost\fR parameter to \&\fBactsync\fR. If is provided, is accessed as an \s-1FTP\s0 server, retrieving the file named. If the filename ends in \f(CW\*(C`.gz\*(C'\fR or \&\f(CW\*(C`.Z\*(C'\fR, it will be automatically uncompressed after retrieval. names the ignore file used by \fBactsync\fR (the \fB\-i\fR option). contains any other flags that you wish to pass to \fBactsync\fR. .PP Note that one should not include \fB\-i\fR or \fB\-o\fR options in the flags= line; they are automatically taken care of by \fBactsyncd\fR. .PP One may produce a trial \fBactsyncd\fR run without changing anything on the server by supplying the \fIdebug-level\fR argument: .PP .Vb 1 \& actsyncd /actsync.cfg 2 .Ve .PP The \fIdebug-level\fR causes \fBactsyncd\fR to run \fBactsync\fR with a \fB\-v\fR \&\fIdebug-level\fR flag (overriding any \fB\-v\fR flag on the flags= line), not make any changes to the \fIactive\fR file, write a new \fIactive\fR file to standard output, and write debug messages to standard error. .PP If the \fIdebug-format\fR argument is also given to \fBactsyncd\fR, the data written to standard output will be in \fB\-o \f(BIdebug-format\fB\fR instead of in \&\f(CW\*(C`\-o a1\*(C'\fR format. .PP \&\s-1INN\s0 comes with default values of \f(CW\*(C`ftp.isc.org\*(C'\fR for and \&\f(CW\*(C`/pub/usenet/CONFIG/active.gz\*(C'\fR for . You can read about the policies used for maintaining that \fIactive\fR file at . Consider synchronizing from this file on a daily basis by using \fBcron\fR. .SH "OPTIONS" .IX Header "OPTIONS" \&\fBactsync\fR takes the following options. .PP In all of the following options, the \fIhostid\fR parameter takes one of the following values: .PP .Vb 5 \& 0 neither server \& 1 local default server \& 2 remote server \& 12 both servers \& 21 both servers .Ve .PP In other words, \f(CW1\fR refers to the local host (the first \fIhost\fR argument on the \fBactsync\fR command line) and \f(CW2\fR refers to the remote host (the second \fIhost\fR argument, or the only one if only one is given). .IP "\fB\-A\fR" 4 .IX Item "-A" \&\fBactsync\fR tries to authenticate before issuing the \s-1LIST\s0 command. .IP "\fB\-b\fR \fIhostid\fR" 4 .IX Item "-b hostid" This flag causes \fBactsync\fR to ignore for synchronization purposes newsgroups with \f(CW\*(C`bork.bork.bork\*(C'\fR\-style names (newsgroups whose last 3 components are identical). For example, the following newsgroups have bork-style names: .Sp .Vb 3 \& alt.helms.dork.dork.dork \& alt.auto.accident.sue.sue.sue \& alt.election.vote.vote.vote .Ve .Sp The default is \f(CW\*(C`\-b 0\*(C'\fR; no newsgroups are ignored because of bork-style names. .IP "\fB\-d\fR \fIhostid\fR" 4 .IX Item "-d hostid" This flag causes \fBactsync\fR to ignore newsgroups that have all numeric path components. For example, the following newsgroups have numeric path components: .Sp .Vb 4 \& alt.prime.chongo.23209 \& 391581.times.2.to_the.216193.power.\-1 \& 99.bottles.of.treacle.on.the.wall \& linfield.class.envio_bio.101.d .Ve .Sp The newsgroups directory of a newsgroup with a all numeric component could conflict with an article from another group if stored using the tradspool storage method; see \fIstorage.conf\fR\|(5). For example, the directory for the first newsgroup listed above is the same path as article number 23209 from the newsgroup: .Sp .Vb 1 \& alt.prime.chongo .Ve .Sp The default is \f(CW\*(C`\-d 0\*(C'\fR; all numeric newsgroups from both hosts will be processed. .IP "\fB\-g\fR \fImax\fR" 4 .IX Item "-g max" Ignore any newsgroup with more than \fImax\fR levels. For example, \f(CW\*(C`\-g 6\*(C'\fR would ignore: .Sp .Vb 3 \& alt.feinstien.votes.to.trash.freedom.of.speech \& alt.senator.exon.enemy.of.the.internet \& alt.crypto.export.laws.dumb.dumb.dumb .Ve .Sp but would not ignore: .Sp .Vb 3 \& alt.feinstien.acts.like.a.republican \& alt.exon.amendment \& alt.crypto.export.laws .Ve .Sp If \fImax\fR is \f(CW0\fR, then the max level feature is disabled. .Sp By default, the max level feature is disabled. .IP "\fB\-i\fR \fIignore-file\fR" 4 .IX Item "-i ignore-file" The \fIignore-file\fR, usually \fIactsync.ign\fR in \fIpathetc\fR, allows one to have a fine degree of control over which newsgroups are ignored. It contains a set of rules that specifies which newsgroups will be checked and which will be ignored. .Sp By default, these rules apply to both hosts. This can be modified by using the \fB\-I\fR flag. .Sp Blank lines and text after a \f(CW\*(C`#\*(C'\fR are considered comments and are ignored. .Sp Rule lines consist of tokens separated by whitespace. Rule lines may be one of two forms: .Sp .Vb 2 \& c [ ...] \& i [ ...] .Ve .Sp If the rule begins with a \f(CW\*(C`c\*(C'\fR, the rule requests certain newsgroups to be checked. If the rule begins with an \f(CW\*(C`i\*(C'\fR, the rule requests certain newsgroups to be ignored. The field may be a specific newsgroup, or a \fIuwildmat\fR\|(3) pattern. .Sp If one or more s are specified, then the rule applies to the newsgroup only if it is of the specified type. Types refer to the 4th field of the \fIactive\fR file; that is, a type may be one of: .Sp .Vb 6 \& y \& n \& m \& j \& x \& =group.name .Ve .Sp Unlike \fIactive\fR files, the \f(CW\*(C`group.name\*(C'\fR in an alias type may be a newsgroup name or a \fIuwildmat\fR\|(3) pattern. Also, \f(CW\*(C`=\*(C'\fR is equivalent to \f(CW\*(C`=*\*(C'\fR. .Sp On each rule line, no pattern type may be repeated. For example, one may not have more than one type that begins with \f(CW\*(C`=\*(C'\fR, per line. However, one may achieve an effect equivalent to using multiple \f(CW\*(C`=\*(C'\fR types by using multiple rule lines affecting the same newsgroup. .Sp By default, all newsgroups are candidates to be checked. If no \fIignore-file\fR is specified, or if the ignore file contains no rule lines, all newsgroups will be checked. If an ignore file is used, each newsgroup in turn is checked against the ignore file. If multiple lines match a given newsgroup, the last line in the ignore file is used. .Sp For example, consider the following ignore file lines: .Sp .Vb 3 \& i *.general \& c *.general m \& i nsa.general .Ve .Sp The newsgroups ba.general and mod.general would be synchronized if moderated and ignored if not moderated. The newsgroup nsa.general would be ignored regardless of moderation status. All newsgroups not matching *.general would be synchronized by default. .IP "\fB\-I\fR \fIhostid\fR" 4 .IX Item "-I hostid" This flag restricts which hosts are affected by the ignore file. This flag may be useful in conjunction with the \fB\-m\fR flag. For example: .Sp .Vb 1 \& actsync \-i actsync.ign \-I 2 \-m host1 host2 .Ve .Sp will keep all newsgroups currently on host1. It will also only compare host1 groups with non-ignored newsgroups from host2. .Sp The default is \f(CW\*(C`\-I 12\*(C'\fR; newsgroups from both hosts are ignored per the file specified with \fB\-i\fR. .IP "\fB\-k\fR" 4 .IX Item "-k" By default, any newsgroup on the local host that has an invalid name will be considered for removal. This causes \fBactsync\fR simply ignore such newsgroups. This flag, used in combination with \fB\-m\fR, will prevent any newsgroup from being scheduled for removal. .IP "\fB\-l\fR \fIhostid\fR" 4 .IX Item "-l hostid" This flag causes problem newsgroups of type \f(CW\*(C`=\*(C'\fR to be considered as errors. Newsgroups of type \f(CW\*(C`=\*(C'\fR are newsgroups \fIactive\fR entries that have a fourth field that begins with \f(CW\*(C`=\*(C'\fR; i.e., newsgroups that are aliased to other newsgroups. A problem newsgroup is one for which one of the following is true: .RS 4 .IP "\(bu" 2 Aliased to itself. .IP "\(bu" 2 In an alias chain that loops around to itself. .IP "\(bu" 2 In an alias chain longer than 16 groups. .IP "\(bu" 2 Aliased to a non-existant newsgroup. .IP "\(bu" 2 Aliased to a newsgroup that has an error of some kind. .RE .RS 4 .Sp However, a newsgroup that is equivalent to an ignored newsgroup is not a problem. .Sp The default is \f(CW\*(C`\-l 12\*(C'\fR: problem newsgroups from both hosts are marked as errors. .RE .IP "\fB\-m\fR" 4 .IX Item "-m" Merge newsgroups instead of sync. By default, if a newsgroup exists on the local host but not the remote, it will be scheduled to be removed. This flag disables this process, permitting newsgroups unique to the local host to be kept. .IP "\fB\-n\fR \fIname\fR" 4 .IX Item "-n name" Depending on \fB\-o\fR, the \fIctlinnd\fR\|(8) command may be used to create newsgroups as necessary. When this is done, the default creator name used is \f(CW\*(C`actsync\*(C'\fR. This flag changes the creator name to \fIname\fR. .IP "\fB\-o\fR \fIformat\fR" 4 .IX Item "-o format" Determine the output or action format of this utility. \fIformat\fR may be one of: .RS 4 .IP "a" 4 .IX Item "a" Output in \fIactive\fR\|(5) format. .IP "a1" 4 .IX Item "a1" Output in \fIactive\fR\|(5) format and output non-error ignored groups from the local host. .IP "ak" 4 .IX Item "ak" Output in \fIactive\fR\|(5) format, but use the high and low (2nd and 3rd active fields) values from the remote host for any newsgroup being created. .IP "aK" 4 .IX Item "aK" Output in \fIactive\fR\|(5) format, but use the high and low (2nd and 3rd active fields) values from the remote host for all newsgroups found on that host. .IP "a1k" 4 .IX Item "a1k" Output in \fIactive\fR\|(5) format, but use the high and low (2nd and 3rd active fields) values from the remote host for any newsgroup being created and output non-error ignored groups from the local host. .IP "a1K" 4 .IX Item "a1K" Output in \fIactive\fR\|(5) format, but use the high and low (2nd and 3rd active fields) values from the remote host for all newsgroups found on that host and output non-error ignored groups from the local host. .IP "ak1" 4 .IX Item "ak1" Same as \f(CW\*(C`a1k\*(C'\fR. .IP "aK1" 4 .IX Item "aK1" Same as \f(CW\*(C`a1K\*(C'\fR. .IP "c" 4 .IX Item "c" Output as commands to \fBctlinnd\fR. .IP "x" 4 .IX Item "x" No output. Instead, directly run \fBctlinnd\fR commands. .IP "xi" 4 .IX Item "xi" No output. Instead, directly run \fBctlinnd\fR commands in an interactive mode. .RE .RS 4 .Sp The \f(CW\*(C`a\*(C'\fR, \f(CW\*(C`a1\*(C'\fR, \f(CW\*(C`ak\*(C'\fR, \f(CW\*(C`aK\*(C'\fR, \f(CW\*(C`a1k\*(C'\fR, \f(CW\*(C`a1K\*(C'\fR, \f(CW\*(C`ak1\*(C'\fR, and \f(CW\*(C`aK1\*(C'\fR style formats allow one to format new \fIactive\fR file instead of producing \fBctlinnd\fR commands. They use high and low values of \f(CW0000000000\fR and \f(CW0000000001\fR respectively for newsgroups that are created unless otherwise specified. The \f(CW\*(C`ak\*(C'\fR and \f(CW\*(C`aK\*(C'\fR variants change the high and low values (2nd and 3rd \fIactive\fR fields). In the case of \f(CW\*(C`ak\*(C'\fR, newsgroups created take their high and low values from the remote host. In the case of \f(CW\*(C`aK\*(C'\fR, all newsgroups found on the remote host take their high and low values from it. .Sp The \f(CW\*(C`c\*(C'\fR format produces \fBctlinnd\fR commands. No actions are taken because \fBactsync\fR simply prints \fBctlinnd\fR commands on standard output. This output format is useful to let you see how the local host will be affected by the sync (or merge) with the remote host. .Sp The sync (or merge) may be accomplished directly by use of the \f(CW\*(C`x\*(C'\fR or \&\f(CW\*(C`xi\*(C'\fR format. With this format, \fBactsync\fR uses the \fIexecl\fR\|(2) system call to directly execute \fBctlinnd\fR commands. The output of such exec calls may be seen if the verbosity level is at least \f(CW2\fR. .Sp The \fBactsync\fR utility will pause for 4 seconds before each command is executed if \f(CW\*(C`\-o x\*(C'\fR is selected. See the \fB\-z\fR flag below for discussion of this delay and how to customize it. .Sp The \f(CW\*(C`xi\*(C'\fR format interactively prompts on standard output and reads directives on standard input. One may pick and choose changes using this format. .Sp Care should be taken when producing \fIactive\fR\|(5) formatted output. One should check to be sure that \fBactsync\fR exited with a zero status prior to using such output. Also one should realize that such output will not contain lines ignored due to \fB\-i\fR even if \f(CW\*(C`\-p 100\*(C'\fR is used. .Sp By default, \f(CW\*(C`\-o c\*(C'\fR is assumed. .RE .IP "\fB\-p\fR \fImin-unchanged\fR" 4 .IX Item "-p min-unchanged" By default, the \fBactsync\fR utility has safeguards against performing massive changes. If fewer than \fImin-unchanged\fR percent of the non-ignored lines from the local host remain unchanged, no actions (output, execution, etc.) are performed and \fBactsync\fR exits with a non-zero exit status. The \fImin-unchanged\fR value may be a floating point value such as \f(CW66.667\fR. .Sp A change is a local newsgroup line that was removed, added, changed, or found to be in error. Changing the 2nd or 3rd \fIactive\fR fields via \&\f(CW\*(C`\-o ak\*(C'\fR or \f(CW\*(C`\-o aK\*(C'\fR are not considered changes by \fB\-p\fR. .Sp To force \fBactsync\fR to accept any amount of change, use the \f(CW\*(C`\-p 0\*(C'\fR option. To force \fBactsync\fR to reject any changes, use the \f(CW\*(C`\-p 100\*(C'\fR option. .Sp Care should be taken when producing \fIactive\fR\|(5) formatted output. One should check to be sure that \fBactsync\fR exited with a zero status prior to using such output. Also one should realize that such output will not contain lines ignored due to \fB\-i\fR even if \f(CW\*(C`\-p 100\*(C'\fR is used. .Sp By default, 96% of the lines not ignored in the first \fIhost\fR argument on the \fBactsync\fR command line must be unchanged. That is, by default, \&\f(CW\*(C`\-p 96\*(C'\fR is assumed. .IP "\fB\-q\fR \fIhostid\fR" 4 .IX Item "-q hostid" By default, all newsgroup errors are reported on standard error. This flag quiets errors from the specified \fIhostid\fR. .IP "\fB\-s\fR \fIsize\fR" 4 .IX Item "-s size" If \fIsize\fR is greater than 0, then ignore newsgroups with names longer than \fIsize\fR and ignore newsgroups aliased (by following \f(CW\*(C`=\*(C'\fR chains) to names longer than \fIsize\fR. Length checking is performed on both the local and remote hosts. .Sp By default, \fIsize\fR is \f(CW0\fR and thus no length checking is performed. .IP "\fB\-t\fR \fIhostid\fR" 4 .IX Item "-t hostid" Ignore improper newsgroups consisting of only a top component from the specified \fIhostid\fR. The following newsgroups are considered proper newsgroups despite top only names and therefore are exempt from this flag: .Sp .Vb 5 \& control \& general \& junk \& test \& to .Ve .Sp For example, the following newsgroup names are improper because they only contain a top level component: .Sp .Vb 4 \& dole_for_pres \& dos \& microsoft \& windows95 .Ve .Sp The default is \f(CW\*(C`\-t 2\*(C'\fR; that is, all improper top-level-only newsgroups from the remote host are ignored. .IP "\fB\-T\fR" 4 .IX Item "-T" This flag causes newsgroups on the remote host in new hierarchies to be ignored. Normally a newsgroup which only exists on the remote host, chongo.was.here for example, is created on the local host. However, if this flag is given and the local host does not have any other newsgroups in the same hierarchy (chongo.* in this case), the newsgroup in question will be ignored and will not be created on the local host. .IP "\fB\-v\fR \fIverbosity\fR" 4 .IX Item "-v verbosity" By default, \fBactsync\fR is not verbose. This flag controls the verbosity level as follows: .RS 4 .IP "0" 2 No debug or status reports (default). .IP "1" 2 .IX Item "1" Print summary, but only if work was needed or done. .IP "2" 2 .IX Item "2" Print actions, exec output, and summary, but only if work was needed or done. .IP "3" 2 .IX Item "3" Print actions, exec output, and summary. .IP "4" 2 .IX Item "4" Full debug output. .RE .RS 4 .RE .IP "\fB\-w\fR \fIseconds\fR" 4 .IX Item "-w seconds" If \f(CW\*(C`\-o x\*(C'\fR or \f(CW\*(C`\-o xi\*(C'\fR is selected, \fBctlinnd\fR will wait \fIseconds\fR seconds before timing out. The default value is \f(CW\*(C`\-w 30\*(C'\fR. .IP "\fB\-z\fR \fIseconds\fR" 4 .IX Item "-z seconds" If \f(CW\*(C`\-o x\*(C'\fR is selected, \fBactsync\fR will pause for \fIseconds\fR seconds before each command is executed. This helps prevent \fBinnd\fR from being busied-out if a large number of \fBctlinnd\fR commands are needed. One can entirely disable this sleeping by using \f(CW\*(C`\-z 0\*(C'\fR. .Sp By default, \fBactsync\fR will pause for \f(CW4\fR seconds before each command is executed if \f(CW\*(C`\-o x\*(C'\fR is selected. .SH "EXAMPLES" .IX Header "EXAMPLES" Determine the difference (but don't change anything) between your newsgroup set and uunet's set: .PP .Vb 1 \& actsync news.uu.net .Ve .PP Same as above, with full debug and progress reports: .PP .Vb 1 \& actsync \-v 4 news.uu.net .Ve .PP Force a site to have the same newsgroups as some other site: .PP .Vb 1 \& actsync \-o x master .Ve .PP This may be useful to sync a slave site to its master, or to sync internal site to a gateway. .PP Compare your site with uunet, disregarding local groups and certain local differences with uunet. Produce a report if any differences were encountered: .PP .Vb 1 \& actsync \-v 2 \-i actsync.ign news.uu.net .Ve .PP where \fIactsync.ign\fR contains: .PP .Vb 3 \& # Don\*(Aqt compare to.* groups as they will differ. \& # \& i to.* \& \& # These are our local groups that nobody else \& # (should) carry. So ignore them for the sake \& # of the compare. \& # \& i nsa.* \& \& # These groups are local favorites, so keep them \& # even if uunet does not carry them. \& # \& i ca.dump.bob.dorman \& i ca.keep.bob.dorman \& i alt.tv.dinosaurs.barney.die.die.die \& i alt.tv.dinosaurs.barney.love.love.love \& i alt.sounds.* =alt.binaries.sounds.* .Ve .PP To interactively sync against news.uu.net, using the same ignore file: .PP .Vb 1 \& actsync \-o xi \-v 2 \-i actsync.ign news.uu.net .Ve .PP Based on newsgroups that you decided to keep, one could make changes to the \fIactsync.ign\fR file: .PP .Vb 3 \& # Don\*(Aqt compare to.* groups as they will differ. \& # \& i to.* \& \& # These are our local groups that nobody else \& # (should) carry. So ignore them for the sake \& # of the compare. \& # \& i nsa.* \& \& # These groups are local favorites, so keep them \& # even if uunet does not carry them. \& # \& i ca.dump.bob.dorman \& i alt.tv.dinosaurs.barney.die.die.die \& i alt.sounds.* =alt.binaries.sounds.* \& \& # Don\*(Aqt sync test groups, except for ones that are \& # moderated or that are under the gnu hierarchy. \& # \& i *.test \& c *.test m # check moderated test groups \& c gnu.*.test \& c gnu.test # just in case it ever exists .Ve .PP Automatic processing may be set up by using the following \fIactsync.cfg\fR file: .PP .Vb 2 \& # Host to sync off of (host2). \& host=news.uu.net \& \& # Location of the ignore file. \& ignore_file=/actsync.ign \& \& # Where news articles are kept. \& spool= \& \& # actsync(8) flags \& # \& # Automatic execs, report if something was done, \& # otherwise don\*(Aqt say anything, don\*(Aqt report \& # uunet active file problems, just ignore \& # the affected entries. \& flags=\-o x \-v 2 \-q 2 .Ve .PP and then by running \fBactsyncd\fR with the path to the config file: .PP .Vb 1 \& actsyncd /actsync.cfg .Ve .PP The command .PP .Vb 1 \& actsyncd /actsync.cfg 4 >cmd.log 2>dbg.log .Ve .PP will operate in debug mode, not change the \fIactive\fR file, write \&\fBctlinnd\fR style commands to \fIcmd.log\fR, and write debug statements to \&\fIdbg.log\fR. .PP To check only the major hierarchies against news.uu,net, use the following \&\fIactsync.ign\fR file: .PP .Vb 3 \& # By default, ignore everything. \& # \& i * \& \& # Check the major groups. \& # \& c alt.* \& c comp.* \& c gnu.* \& c humanities.* \& c misc.* \& c news.* \& c rec.* \& c sci.* \& c soc.* \& c talk.* .Ve .PP and the command: .PP .Vb 1 \& actsync \-i actsync.ign news.uu.net .Ve .PP To determine the differences between your old \fIactive\fR and your current default server: .PP .Vb 1 \& actsync /active.old \- .Ve .PP To report but not fix any newsgroup problems with the current \fIactive\fR file: .PP .Vb 1 \& actsync \- \- .Ve .PP To detect any newsgroup errors on your local server, and to remove any *.bork.bork.bork\-style silly newsgroup names: .PP .Vb 1 \& actsync \-b 2 \- \- .Ve .PP The \fIactive\fR file produced by: .PP .Vb 1 \& actsync \-o x erehwon.honey.edu .Ve .PP is effectively the same as the \fIactive\fR file produced by: .PP .Vb 9 \& cd \& ctlinnd pause \*(Aqrunning actsync\*(Aq \& rm \-f active.new \& actsync \-o a1 erehwon.honey.edu > active.new \& rm \-f active.old \& ln active active.old \& mv active.new active \& ctlinnd reload active \*(Aqrunning actsync\*(Aq \& ctlinnd go \*(Aqrunning actsync\*(Aq .Ve .PP It should be noted that the final method above, pausing the server and simply replacing the \fIactive\fR file, may be faster if you are making lots of changes. .SH "FILES" .IX Header "FILES" .IP "\fIpathbin\fR/actsync" 4 .IX Item "pathbin/actsync" The C program itself used to synchronize, compare, or merge two \fIactive\fR files. .IP "\fIpathbin\fR/actsyncd" 4 .IX Item "pathbin/actsyncd" The Shell daemon which provides a convenient interface to configure and run \fBactsync\fR. .IP "\fIpathetc\fR/actsync.cfg" 4 .IX Item "pathetc/actsync.cfg" The configuration file which specifies the settings to use. .IP "\fIpathetc\fR/actsync.ign" 4 .IX Item "pathetc/actsync.ign" The ignore file which contains a set of synchronization rules that specifies which newsgroups will be checked and which will be ignored. .SH "CAUTION" .IX Header "CAUTION" Careless use of this tool may result in the unintended addition, change, or removal of newsgroups. You should avoid using the \f(CW\*(C`x\*(C'\fR output format until you are sure it will do what you want. .SH "BUGS" .IX Header "BUGS" If a newsgroup appears multiple times, \fBactsync\fR will treat all copies as errors. However, if the group is marked for removal, only one rmgroup will be issued. .SH "HISTORY" .IX Header "HISTORY" Written by Landon Curt Noll for InterNetNews. Updated to support \s-1FTP\s0 fetching by David Lawrence . Converted to \&\s-1POD\s0 by Russ Allbery . .PP \&\f(CW$Id:\fR actsync.pod 9767 2014\-12\-07 21:13:43Z iulius $ .PP By Landon Curt Noll (chongo was here /\e../\e). .PP Copyright (c) Landon Curt Noll, 1993. All rights reserved. .PP Permission to use and modify is hereby granted so long as this notice remains. Use at your own risk. No warranty is implied. .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIactive\fR\|(5), \fIctlinnd\fR\|(8), \fIgetlist\fR\|(8), \fIinn.conf\fR\|(5), \fImod\-active\fR\|(8), \fIsimpleftp\fR\|(1). inn-2.6.0/doc/man/writelog.80000644000175200017520000000167112575023702015172 0ustar iuliusiulius.TH WRITELOG 8 .SH NAME writelog \- add a entry to an INN log file. .SH SYNOPSIS .B writelog .I name .I text... .SH DESCRIPTION .PP The .I writelog script is used to write a log entry or send it as mail. The .I name parameter specifies the name of the log file where the entry should be written. If it is the word ``mail'' then the entry is mailed to the news administrator, .IR . The data that is written or sent consists of the .I text given on the command line, followed by standard input indented by four spaces. .IR Shlock (1) is used to avoid simultaneous updates to a single log file. .SH HISTORY Written by Landon Curt Noll and Rich $alz for InterNetNews. .de R$ This is revision \\$3, dated \\$4. .. .R$ $Id: writelog.8 5909 2002-12-03 05:17:18Z vinocur $ .SH "SEE ALSO" innd(8), innstat(8), news.daily(8), newslog(5), nnrpd(8), scanlogs(8). inn-2.6.0/doc/man/ovdb.50000644000175200017520000004663712575023702014300 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "OVDB 5" .TH OVDB 5 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" ovdb \- Overview storage method for INN .SH "DESCRIPTION" .IX Header "DESCRIPTION" Ovdb is a storage method that uses the Berkeley\ \s-1DB\s0 library to store overview data. It requires version 4.4 or later of the Berkeley\ \s-1DB\s0 library (4.7+ is recommended because older versions suffer from various issues). .PP Ovdb makes use of the full transaction/logging/locking functionality of the Berkeley\ \s-1DB\s0 environment. Berkeley\ \s-1DB\s0 may be downloaded from http://www.oracle.com/technetwork/database/database\-technologies/berkeleydb/overview/index.html and is needed to build the ovdb backend. .SH "UPGRADING" .IX Header "UPGRADING" This is version 2 of ovdb. If you have a database created with a previous version of ovdb (such as the one shipped with \s-1INN\s0\ 2.3.0) your database will need to be upgraded using \fIovdb_init\fR\|(8). See the man page \&\fIovdb_init\fR\|(8) for upgrade instructions. .SH "INSTALLATION" .IX Header "INSTALLATION" To build ovdb support into \s-1INN\s0, specify the option \fB\-\-with\-bdb\fR when running the configure script. By default, configure will search for Berkeley\ \s-1DB\s0 in default search paths; there will be a message in the configure output indicating the pathname that will be used. .PP You can override this pathname by adding a path to the option, for instance \fB\-\-with\-bdb=/usr/BerkeleyDB.4.4\fR. This directory is expected to have subdirectories \fIinclude\fR and \fIlib\fR (\fIlib32\fR and \fIlib64\fR are also checked), containing respectively \fIdb.h\fR, and the library itself. In case non-standard paths to the Berkeley\ \s-1DB\s0 libraries are used, one or both of the options \fB\-\-with\-bdb\-include\fR and \fB\-\-with\-bdb\-lib\fR can be given to configure with a path. .PP The ovdb database may take up more disk space for a given spool than the other overview methods. Plan on needing at least 1.1\ \s-1KB\s0 for every article in your spool (not counting crossposts). So, if you have 5 million articles, you'll need at least 5.5\ \s-1GB\s0 of disk space for ovdb. With compression enabled, this estimate changes to 0.7\ \s-1KB\s0 per article. See the \s-1COMPRESSION\s0 section below. Plus, you'll need additional space for transaction logs: at least 100\ \s-1MB\s0. By default the transaction logs go in the same directory as the database. To improve performance, they can be placed on a different disk \-\-\ see the \s-1DB_CONFIG\s0 section. .SH "CONFIGURATION" .IX Header "CONFIGURATION" To enable ovdb, set the \fIovmethod\fR parameter in \fIinn.conf\fR to \f(CW\*(C`ovdb\*(C'\fR. The ovdb database is stored in the directory specified by the \&\fIpathoverview\fR parameter in \fIinn.conf\fR. This is the \*(L"\s-1DB_HOME\s0\*(R" directory. To start out, this directory should be empty (other than an optional \&\fI\s-1DB_CONFIG\s0\fR file; see \s-1DB_CONFIG\s0 for details) and \fBinnd\fR (or \&\fBmakehistory\fR) will create the files as necessary in that directory. Make sure the directory is owned by the news user. .PP Other parameters for configuring ovdb are in the \fIovdb.conf\fR\|(5) configuration file. See also the sample \fIovdb.conf\fR. .IP "cachesize" 4 .IX Item "cachesize" Size of the memory pool cache, in kilobytes. The cache will have a backing store file in the \s-1DB\s0 directory which will be at least as big. In general, the bigger the cache, the better. Use \f(CW\*(C`ovdb_stat \-m\*(C'\fR to see cache hit percentages. To make a change of this parameter take effect, shut down and restart \s-1INN\s0 (be sure to kill all of the nnrpds when shutting down). Default is 8000, which is adequate for small to medium sized servers. Large servers will probably need at least 20000. .IP "compress" 4 .IX Item "compress" If \s-1INN\s0 was compiled with zlib, and this compress parameter is true, \s-1OVDB\s0 will compress overview records that are longer than 600 bytes. See the \s-1COMPRESSION\s0 section below. .IP "numdbfiles" 4 .IX Item "numdbfiles" Overview data is split between this many files. Currently, \fBinnd\fR will keep all of the files open, so don't set this too high or \fBinnd\fR may run out of file descriptors. \fBnnrpd\fR only opens one at a time, regardless. May be set to one, or just a few, but only do that if your \s-1OS\s0 supports large (>2G) files. Changing this parameter has no effect on an already-established database. Default is 32. .IP "txn_nosync" 4 .IX Item "txn_nosync" If txn_nosync is set to false, Berkeley\ \s-1DB\s0 flushes the log after every transaction. This minimizes the number of transactions that may be lost in the event of a crash, but results in significantly degraded performance. Default is true. .IP "useshm" 4 .IX Item "useshm" If useshm is set to true, Berkeley\ \s-1DB\s0 will use shared memory instead of mmap for its environment regions (cache, lock, etc). With some platforms, this may improve performance. Default is false. .IP "shmkey" 4 .IX Item "shmkey" Sets the shared memory key used by Berkeley\ \s-1DB\s0 when 'useshm' is true. Berkeley\ \s-1DB\s0 will create several (usually 5) shared memory segments, using sequentially numbered keys starting with 'shmkey'. Choose a key that does not conflict with any existing shared memory segments on your system. Default is 6400. .IP "pagesize" 4 .IX Item "pagesize" Sets the page size for the \s-1DB\s0 files (in bytes). Must be a power of 2. Best choices are 4096 or 8192. The default is 8192. Changing this parameter has no effect on an already-established database. .IP "minkey" 4 .IX Item "minkey" Sets the minimum number of keys per page. See the Berkeley\ \s-1DB\s0 documentation for more info. Default is based on page size and whether compression is enabled: .Sp .Vb 2 \& default_minkey = MAX(2, pagesize / 2600) if compress is false \& default_minkey = MAX(2, pagesize / 1500) if compress is true .Ve .Sp The lowest allowed minkey is 2. Setting minkey higher than the default is not recommended, as it will cause the databases to have a lot of overflow pages. Changing this parameter has no effect on an already-established database. .IP "maxlocks" 4 .IX Item "maxlocks" Sets the Berkeley\ \s-1DB\s0 \*(L"lk_max\*(R" parameter, which is the maximum number of locks that can exist in the database at the same time. Default is 4000. .IP "nocompact" 4 .IX Item "nocompact" The nocompact parameter affects expireover's behavior. The expireover function in ovdb can do its job in one of two ways: by simply deleting expired records from the database, or by re-writing the overview records into a different location leaving out the expired records. The first method is faster, but it leaves 'holes' that result in space that can not immediately be reused. The second method 'compacts' the records by rewriting them. .Sp If this parameter is set to 0, expireover will compact all newsgroups; if set to 1, expireover will not compact any newsgroups; and if set to a value greater than one, expireover will only compact groups that have less than that number of articles. .Sp Experience has shown that compacting has minimal effect (other than making expireover take longer) so the default is now 1. This parameter will probably be removed in the future. .IP "readserver" 4 .IX Item "readserver" Normally, each nnrpd process directly accesses the Berkeley\ \s-1DB\s0 environment. The process of attaching to the database (and detaching when finished) is fairly expensive, and can result in high loads in situations when there are lots of reader connections of relatively short duration. .Sp When the \fIreadserver\fR parameter is true, the nnrpds will access overview via a helper server (\fBovdb_server\fR \-\-\ which is started by \fBovdb_init\fR). This can also result in cleaner shutdowns for the database, improving stability and avoiding deadlocks and corrupted databases. If you are experiencing any instability in ovdb, try setting this parameter to true. Default is false. .IP "numrsprocs" 4 .IX Item "numrsprocs" This parameter is only used when \fIreadserver\fR is true. It sets the number of ovdb_server processes. As each ovdb_server can process only one transaction at a time, running more servers can improve reader response times. Default is 5. .IP "maxrsconn" 4 .IX Item "maxrsconn" This parameter is only used when \fIreadserver\fR is true. It sets a maximum number of readers that a given ovdb_server process will serve at one time. This means the maximum number of readers for all of the ovdb_server processes is (numrsprocs * maxrsconn). This does \fBnot\fR limit the actual number of readers, since nnrpd will fall back to opening the database directly if it can't connect to a readserver. Default is 0, which means an umlimited number of connections is allowed. .SH "COMPRESSION" .IX Header "COMPRESSION" New in this version of \s-1OVDB\s0 is the ability to compress overview data before it is stored into the database. In addition to consuming less disk space, compression keeps the average size of the database keys smaller. This in turn increases the average number of keys per page, which can significantly improve performance and also helps keep the database more compact. This feature requires that \s-1INN\s0 be built with zlib. Only records larger than 600 bytes get compressed, because that is the point at which compression starts to become significant. .PP If compression is not enabled (either from the \f(CW\*(C`compress\*(C'\fR option in \&\fIovdb.conf\fR or \s-1INN\s0 was not built from zlib), the database will be backward compatible with older versions of \s-1OVDB\s0. However, if compression is enabled, the database is marked with a newer version that will prevent older versions of \s-1OVDB\s0 from opening the database. .PP You can upgrade an existing database to use compression simply by setting \&\fIcompress\fR to true in \fIovdb.conf\fR. Note that existing records in the database will remain uncompressed; only new records added after enabling compression will be compressed. .PP If you disable compression on a database that previously had it enabled, new records will be stored uncompressed, but the database will still be incompatible with older versions of \s-1OVDB\s0 (and will also be incompatible with this version of \s-1OVDB\s0 if it was not built with zlib). So to downgrade to a completely uncompressed database you will have to rebuild the database using makehistory. .SH "DB_CONFIG" .IX Header "DB_CONFIG" A file called \fI\s-1DB_CONFIG\s0\fR may be placed in the database directory to customize where the various database files and transaction logs are written. By default, all of the files are written in the \*(L"\s-1DB_HOME\s0\*(R" directory. One way to improve performance is to put the transaction logs on a different disk. To do this, put: .PP .Vb 1 \& DB_LOG_DIR /path/to/logs .Ve .PP in the \fI\s-1DB_CONFIG\s0\fR file. If the pathname you give starts with a /, it is treated as an absolute path; otherwise, it is relative to the \*(L"\s-1DB_HOME\s0\*(R" directory. Make sure that any directories you specify exist and have proper ownership/mode before starting \s-1INN\s0, because they won't be created automatically. Also, don't change the \s-1DB_CONFIG\s0 file while anything that uses ovdb is running. .PP Another thing that you can do with this file is to split the overview database across multiple disks. In the \fI\s-1DB_CONFIG\s0\fR file, you can list directories that Berkeley\ \s-1DB\s0 will search when it goes to open a database. .PP For example, let's say that you have \fIpathoverview\fR set to \&\fI/mnt/overview\fR and you have four additional file systems created on \&\fI/mnt/ov?\fR. You would create a file \*(L"/mnt/overview/DB_CONFIG\*(R" containing the following lines: .PP .Vb 5 \& set_data_dir /mnt/overview \& set_data_dir /mnt/ov1 \& set_data_dir /mnt/ov2 \& set_data_dir /mnt/ov3 \& set_data_dir /mnt/ov4 .Ve .PP Distribute your ovNNNNN files into the four filesystems. (say, 8 each). When called upon to open a database file, the db library will look for it in each of the specified directories (in order). If said file is not found, one will be created in the first of those directories. .PP Whenever you change \s-1DB_CONFIG\s0 or move database files around, make sure all news processes that use the database are shut down first (including nnrpds). .PP The \s-1DB_CONFIG\s0 functionality is part of Berkeley\ \s-1DB\s0 itself, rather than something provided by ovdb. See the Berkeley\ \s-1DB\s0 documentation for complete details for the version of Berkeley\ \s-1DB\s0 that you're running. .SH "RUNNING" .IX Header "RUNNING" When starting the news system, \fBrc.news\fR will invoke \fBovdb_init\fR. \&\fBovdb_init\fR must be run before using the database. It performs the following tasks: .IP "\(bu" 4 Creates the database environment, if necessary. .IP "\(bu" 4 If the database is idle, it performs a normal recovery. The recovery will remove stale locks, recreate the memory pool cache, and repair any damage caused by a system crash or improper shutdown. .IP "\(bu" 4 Starts the \s-1DB\s0 housekeeping processes (\fBovdb_monitor\fR) if they're not already running. .PP And when stopping \s-1INN\s0, \fBrc.news\fR kills the ovdb_monitor processes after the other \s-1INN\s0 processes have been shut down. .SH "DIAGNOSTICS" .IX Header "DIAGNOSTICS" Problems relating to ovdb are logged to news.err with \*(L"\s-1OVDB\s0\*(R" in the error message. .PP \&\s-1INN\s0 programs that use overview will fail to start up if the ovdb_monitor processes aren't running. Be sure to run \fBovdb_init\fR before running anything that accesses overview. .PP Also, \s-1INN\s0 programs that use overview will fail to start up if the user running them is not the \*(L"news\*(R" user. .PP If a program accessing the database crashes, or otherwise exits uncleanly, it might leave a stale lock in the database. This lock could cause other processes to deadlock on that stale lock. To fix this, shut down all news processes (using \f(CW\*(C`kill \-9\*(C'\fR if necessary) and then restart. \fBovdb_init\fR should perform a recovery operation which will remove the locks and repair damage caused by killing the deadlocked processes. .SH "FILES" .IX Header "FILES" .IP "inn.conf" 4 .IX Item "inn.conf" The \fIovmethod\fR and \fIpathoverview\fR parameters are relevant to ovdb. .IP "ovdb.conf" 4 .IX Item "ovdb.conf" Optional configuration file for tuning. See \s-1CONFIGURATION\s0 above. .IP "\fIpathoverview\fR" 4 .IX Item "pathoverview" Directory where the database goes. Berkeley\ \s-1DB\s0 calls it the '\s-1DB_HOME\s0' directory. .IP "\fIpathoverview\fR/DB_CONFIG" 4 .IX Item "pathoverview/DB_CONFIG" Optional file to configure the layout of the database files. .IP "\fIpathrun\fR/ovdb.sem" 4 .IX Item "pathrun/ovdb.sem" A file that gets locked by every process that is accessing the database. This is used by \fBovdb_init\fR to determine whether the database is active or quiescent. .IP "\fIpathrun\fR/ovdb_monitor.pid" 4 .IX Item "pathrun/ovdb_monitor.pid" Contains the process \s-1ID\s0 of \fBovdb_monitor\fR. .SH "TO DO" .IX Header "TO DO" Implement a way to limit how many databases can be open at once (to reduce file descriptor usage); maybe using something similar to the cache code in ov3.c .SH "HISTORY" .IX Header "HISTORY" Written by Heath Kehoe for InterNetNews .PP \&\f(CW$Id:\fR ovdb.pod 9593 2013\-12\-27 21:16:09Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIinn.conf\fR\|(5), \fIinnd\fR\|(8), \fInnrpd\fR\|(8), \fIovdb_init\fR\|(8), \fIovdb_monitor\fR\|(8), \&\fIovdb_stat\fR\|(8) .PP Berkeley\ \s-1DB\s0 documentation: in the \fIdocs\fR directory of the Berkeley\ \s-1DB\s0 source distribution, or on the Oracle Berkeley\ \s-1DB\s0 web page (http://www.oracle.com/technetwork/database/database\-technologies/berkeleydb/overview/index.html ). inn-2.6.0/doc/man/cnfsheadconf.80000644000175200017520000001150612575023702015755 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "CNFSHEADCONF 8" .TH CNFSHEADCONF 8 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" cnfsheadconf \- Read and write CNFS headers .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fBcnfsheadconf\fR [\fB\-hw\fR] [\fB\-c\fR \fIclass\fR] .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\fIcnfsheadconf\fR reads \fIpathetc\fR/cycbuff.conf and \fIpathetc\fR/storage.conf to determine which \s-1CNFS\s0 buffers are available. It then reads all of them or the specified cyclic buffer via the \fB\-c\fR flag, and modifies the header as directed by the interactive user if \fB\-w\fR is used. .SH "OPTIONS" .IX Header "OPTIONS" .IP "\fB\-c\fR \fIclass\fR" 4 .IX Item "-c class" Print the status of the specified class. It also modifies it in case \fB\-w\fR is used. .IP "\fB\-h\fR" 4 .IX Item "-h" Print usage information and exit. .IP "\fB\-w\fR" 4 .IX Item "-w" Prompt for modifications to make to cycbuff header. .SH "HISTORY" .IX Header "HISTORY" Written by Katsuhiro Kondou for InterNetNews. Converted to \s-1POD\s0 by Julien Elie. .PP \&\f(CW$Id:\fR cnfsheadconf.pod 8545 2009\-07\-03 21:56:39Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIcycbuff.conf\fR\|(5), \fIinn.conf\fR\|(5), \fIstorage.conf\fR\|(5). inn-2.6.0/doc/man/ovdb_init.80000644000175200017520000001645312575023702015317 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "OVDB_INIT 8" .TH OVDB_INIT 8 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" ovdb_init \- Prepare ovdb database for use .SH "SYNOPSIS" .IX Header "SYNOPSIS" ovdb_init [\f(CW\*(C`\-u\*(C'\fR|\f(CW\*(C`\-r\*(C'\fR] .SH "DESCRIPTION" .IX Header "DESCRIPTION" This command must be run before any other process can access the overview database. It performs the following steps: .IP "1." 4 Creates the database environment, if necessary .IP "2." 4 If the database is idle (and if the \f(CW\*(C`\-u\*(C'\fR option is not specified), it performs a normal recovery. The recovery will remove stale locks, recreate the memory pool cache, and repair any damage caused by a system crash or improper shutdown. .IP "3." 4 If the \f(CW\*(C`\-u\*(C'\fR option is specified, it performs any necessary upgrades to the database. See the \s-1UPGRADING\s0 section below. .IP "4." 4 Starts the \s-1DB\s0 housekeeping processes (ovdb_monitor) if they're not already running. (Unless the \f(CW\*(C`\-r\*(C'\fR option is specified). .IP "5." 4 Starts the ovdb readserver (ovdb_server) processes if \fIreadserver\fR in \fIovdb.conf\fR is true, and if they are not already running. (Unless the \f(CW\*(C`\-r\*(C'\fR option is specified). .PP Returns exit status of 0 if all steps were completed successfully. In the event of an error, messages are written to syslog and/or stderr. .PP If a recovery was attempted but it failed, the database may be damaged beyond repair, requiring a rebuild with \fImakehistory\fR\|(8). .PP This command is normally invoked automatically by \fIrc.news\fR\|(8). .PP It is \s-1OK\s0 to run this command multiple times. .SH "OPTIONS" .IX Header "OPTIONS" .ie n .IP """\-r""" 4 .el .IP "\f(CW\-r\fR" 4 .IX Item "-r" Perform recovery only. \f(CW\*(C`ovdb_monitor\*(C'\fR is not started. .ie n .IP """\-u""" 4 .el .IP "\f(CW\-u\fR" 4 .IX Item "-u" Perform any needed upgrades. Recovery is not attempted. \&\f(CW\*(C`ovdb_monitor\*(C'\fR is started if the upgrade succeeded. .SH "UPGRADING" .IX Header "UPGRADING" There are two situations in which the database will need to be upgraded: .IP "\(bu" 4 You upgrade the Berkeley\ \s-1DB\s0 library to a newer version, for example from 2.7.7 to 3.1.17. In this case, the Berkeley\ \s-1DB\s0 db\->\fIupgrade()\fR method is used. .IP "\(bu" 4 You upgrade ovdb to a newer major version; i.e., ovdb\-1.0 to ovdb\-2.0. .PP In both of these cases, the database is upgraded in-place; and the upgrade can not be undone. Do not interrupt the upgrade process once it has started, because there is a risk of irrepairable corruption. The upgrade may take several minutes to complete. If an upgrade does get interrupted, try running the upgrade again. .PP Here's an example procedure to upgrade a database created with Berkeley\ \s-1DB\s0\ 2.7.7 to use Berkeley\ \s-1DB\s0\ 3.1.17: .IP "1." 4 Build and install the Berkeley\ \s-1DB\s0\ 3.1.17; .IP "2." 4 Run configure in the \s-1INN\s0 source tree and make sure it picks up the right Berkeley\ \s-1DB\s0 directory (e.g., \fI/usr/local/BerkeleyDB.3.1\fR); .IP "3." 4 Do a \f(CW\*(C`make\*(C'\fR; .IP "4." 4 Shut down \s-1INN\s0 (e.g., with \f(CW\*(C`rc.news stop\*(C'\fR) and be sure to kill all instances of \fBnnrpd\fR as well; .IP "5." 4 Do a \f(CW\*(C`make update\*(C'\fR to install the new binaries; .IP "6." 4 Run \f(CW\*(C`ovdb_init \-u\*(C'\fR as the news user; .IP "7." 4 Start \s-1INN\s0 with \f(CW\*(C`rc.news\*(C'\fR. .PP It is \s-1OK\s0 to specify \f(CW\*(C`\-u\*(C'\fR even if no upgrades are needed. .SH "HISTORY" .IX Header "HISTORY" Written by Heath Kehoe for InterNetNews. .PP \&\f(CW$Id:\fR ovdb_init.pod 9765 2014\-12\-07 21:07:34Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIovdb\fR\|(5), \fImakehistory\fR\|(8) inn-2.6.0/doc/man/tdx-util.80000644000175200017520000003376612575023702015122 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "TDX-UTIL 8" .TH TDX-UTIL 8 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" tdx\-util \- Tradindexed overview manipulation utility .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fBtdx-util\fR [\fB\-AFcgiOo\fR] [\fB\-a\fR \fIarticle\fR] [\fB\-f\fR \fIstatus\fR] [\fB\-n\fR \fInewsgroup\fR] [\fB\-p\fR \fIpath\fR] [\fB\-R\fR \fIpath\fR] .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\fBtdx-util\fR is an administrative interface to the tradindexed overview method for \s-1INN\s0. It only works on tradindexed overview databases, not on any other type of \s-1INN\s0 overview. It allows the administrator to dump various information about the internal state of the overview, audit it for errors, and rebuild portions of the overview database. .PP The tradindexed overview method should lock properly and therefore it should be safe to run this utility and perform any operation it performs, including full repairs or per-group overview rebuilds, while the server is running. However, note that some of the operations performed by this utility can take an extended period of time and will hold locks in the overview database during that period, which depending on what the server is doing may cause the server to stall until \fBtdx-util\fR completes its operation. .PP The dump operations are \fB\-i\fR, which dumps the master index for the overview database, \fB\-g\fR, which dumps the index for an individual group, and \fB\-o\fR and \fB\-O\fR, which dump the overview information for a particular group (including the additional metadata stored in the index) in two different formats. For \fB\-g\fR, \fB\-o\fR, and \fB\-O\fR, the \fB\-n\fR option must also be given to specify a newsgroup to operate on. .PP To add a new newsgroup to the overview database, use \fB\-c\fR. A group must be specified with \fB\-n\fR. If the group status is something other than \f(CW\*(C`y\*(C'\fR, it should be specified with \fB\-f\fR, and the low and high article numbers may be specified with \fB\-a\fR. If only one number is given rather than a range, it will be taken to be the high water mark and the low mark will be set to 1. .PP To audit the entire overview database for problems, use \fB\-A\fR. Any problems found will be reported to standard error. Use \fB\-F\fR to correct the errors found. .PP To rebuild the database for a particular newsgroup, use \fB\-R\fR. The \fB\-R\fR option takes a path to a directory which contains all of the articles for that newsgroup, one per file. The names of the files must be the numbers of the articles in that group. (In other words, this directory must be a traditional spool directory for that group.) The \fB\-n\fR option must also be given to specify the newsgroup for which the overview is being rebuilt. .PP For all operations performed by \fBtdx-util\fR, a different overview database than the one specified in \fIinn.conf\fR may be specified using the \fB\-p\fR option. .SH "OPTIONS" .IX Header "OPTIONS" .IP "\fB\-A\fR" 4 .IX Item "-A" Audit the entire overview database for problems. This runs the internal consistency checks built into the tradindexed overview implementation, checking such things as the validity and reachability of all group index entries, the format of the individual overview entries, the correspondence of index entries to overview data, and similar such things. No changes will be made to the database, but problems will be reported to standard error. .IP "\fB\-a\fR \fIarticle\fR" 4 .IX Item "-a article" The article number or numbers to act on. \fIarticle\fR is a valid \s-1NNTP\s0 range, meaning that it can be either a single article number or a range of article numbers (like \f(CW\*(C`1\-5\*(C'\fR). Either the start or the end (or both) of the range may be omitted, in which case they will be set to the first or last article number in the group. Passing \f(CW\*(C`\-\*(C'\fR for \fIarticle\fR is therefore equivalent to not using the \fB\-a\fR option at all. .Sp Only useful in combination with the \fB\-o\fR option to dump overview information or with \fB\-c\fR to specify the low and high article numbers when creating a group. .IP "\fB\-c\fR" 4 .IX Item "-c" Create a new group in the overview database. The group must be specified with \fB\-n\fR. The newsgroup status defaults to \f(CW\*(C`y\*(C'\fR but may be set with \&\fB\-f\fR. The low and high article numbers default to 1 and 0 respectively, but may be set with \fB\-a\fR. If only one number is given to \fB\-a\fR, it is taken as the high article number. .IP "\fB\-F\fR" 4 .IX Item "-F" Audit the entire overview database for problems, fixing them as they're detected where possible. This runs the internal consistency checks built into the tradindexed overview implementation, checking such things as the validity and reachability of all group index entries, the format of the individual overview entries, the correspondence of index entries to overview data, and similar such things. The strategy used when fixing problems is to throw away data that's unrecoverable, so be warned that using this option may result in inaccessible articles if their overview data has been corrupted. .Sp To see what would be changed by \fB\-F\fR, run \fBtdx-util\fR with \fB\-A\fR first. .IP "\fB\-f\fR \fIstatus\fR" 4 .IX Item "-f status" When creating a newsgroup with \fB\-c\fR, set the status of the newly created group to \fIstatus\fR instead of \f(CW\*(C`y\*(C'\fR. Only useful with \fB\-c\fR. .IP "\fB\-g\fR" 4 .IX Item "-g" Dump the index of a particular group. The fields are, in order, the article number, the offset of the data for that article in the overview data file for that group, the length of the overview data, the time (in seconds since epoch) when the article arrived on the server, the time (in seconds since epoch) when the article should expire based on its Expires: header (or 0 if there is no Expires: header), and the storage \s-1API\s0 token of the article. .Sp If this option is given, the \fB\-n\fR option must also be given to specify the newsgroup on which to act. .IP "\fB\-i\fR" 4 .IX Item "-i" Dump the master index of the overview database. This contains similar information to the server \fIactive\fR file, such as high and low water marks and moderation status, and is the information that \fBnnrpd\fR hands out to clients. .Sp The fields are, in order, the newsgroup name, the high water mark, the low water mark, the base article number (the point at which the group index begins), the count of articles in the group, the group status, the time (in seconds since epoch) when that newsgroup was deleted or 0 if it hasn't been, and the inode of the index file for that group. .Sp A particular newsgroup can be specified with the \fB\-n\fR option. If \fB\-n\fR is not given, the entire master index will be dumped. .IP "\fB\-n\fR \fInewsgroup\fR" 4 .IX Item "-n newsgroup" Specify the newsgroup on which to act, required for the \fB\-i\fR, \fB\-o\fR, and \&\fB\-R\fR options. .IP "\fB\-O\fR" 4 .IX Item "-O" Dump the overview information for a newsgroup in the format used by \&\fBoverchan\fR as input. Each line will start with the storage \s-1API\s0 token, the arrival timestamp in seconds since epoch, the expires timestamp in the same format (or 0 if there is no Expires: header), and then the overview data. .Sp If this option is given, the \fB\-n\fR option must also be given to specify the newsgroup on which to act. By default, all of the overview information for that newsgroup is dumped, but the \fB\-a\fR option may be given to restrict the dump to the information for a single article. .IP "\fB\-o\fR" 4 .IX Item "-o" Dump the overview information for a newsgroup, in the same format as it would be returned to clients but with one modification. Appended to the end of each entry will be four additional pieces of data: the article number according to the index file for that group labelled with \&\f(CW\*(C`Article:\*(C'\fR, the storage \s-1API\s0 token for that article labelled with \&\f(CW\*(C`Token:\*(C'\fR, the arrival date for that article on the server in \s-1RFC\s0\ 5322 date format labelled with \f(CW\*(C`Arrived:\*(C'\fR, and the expiration date for that article (from the Expires: header) in \s-1RFC\s0\ 5322 date format if there is any, labelled with \f(CW\*(C`Expires:\*(C'\fR. .Sp If this option is given, the \fB\-n\fR option must also be given to specify the newsgroup on which to act. By default, all of the overview information for that newsgroup is dumped, but the \fB\-a\fR option may be given to restrict the dump to the information for a single article. .IP "\fB\-p\fR \fIpath\fR" 4 .IX Item "-p path" Act on the overview database rooted in \fIpath\fR, overriding the overview path specified in \fIinn.conf\fR. .IP "\fB\-R\fR \fIpath\fR" 4 .IX Item "-R path" Rebuild the overview for a given group from the articles stored in \&\fIpath\fR. The articles must be in the form of a traditional spool directory; in other words, each article must be in a separate file and the name of the file must match the article number of the article. .Sp If this option is given, the \fB\-n\fR option must also be given to specify the newsgroup on which to act. .SH "EXAMPLES" .IX Header "EXAMPLES" Dump the master index for the overview database in \fI/news/overview\fR, regardless of the overview path specified in \fIinn.conf\fR: .PP .Vb 1 \& tdx\-util \-i \-p /news/overview .Ve .PP Dump the group index for example.test: .PP .Vb 1 \& tdx\-util \-g \-n example.test .Ve .PP Dump the complete overview information for example.test: .PP .Vb 1 \& tdx\-util \-o \-n example.test .Ve .PP Dump the overview information for articles 45 and higher in example.test: .PP .Vb 1 \& tdx\-util \-o \-n example.test \-a 45\- .Ve .PP Create an entry for example.test with mode m and low and high article numbers of 4 and 23, respectively. .PP .Vb 1 \& tdx\-util \-c \-n example.test \-f m \-a 4\-23 .Ve .PP Audit the entire overview database for any problems: .PP .Vb 1 \& tdx\-util \-A .Ve .PP Rebuild the overview information for example.test from a traditional spool directory: .PP .Vb 1 \& tdx\-util \-R /example/test \-n example.test .Ve .PP The last command may be useful for recovering from a bad crash or corrupted overview information for a particular group, if you are also using the tradspool article storage method. .SH "HISTORY" .IX Header "HISTORY" Written by Russ Allbery for InterNetNews. .PP \&\f(CW$Id:\fR tdx\-util.pod 9767 2014\-12\-07 21:13:43Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fImakehistory\fR\|(8), \fInnrpd\fR\|(8). inn-2.6.0/doc/man/grephistory.10000644000175200017520000002032612575023702015704 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "GREPHISTORY 1" .TH GREPHISTORY 1 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" grephistory \- Query the INN history database .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fBgrephistory\fR [\fB\-eilnqsv\fR] [\fB\-f\fR \fIdb\fR] [\fImessage-id\fR] .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\fBgrephistory\fR queries the \s-1INN\s0 history database for information about the specified message-ID. If no flags are given, the program prints the storage \s-1API\s0 token of the corresponding article, or \f(CW\*(C`/dev/null\*(C'\fR if the article is listed in the history database but not stored on the server. If the message-ID cannot be found in the database, \fBgrephistory\fR will print \f(CW\*(C`grephistory: not found\*(C'\fR and exit with a non-zero status. .PP Be sure to escape any special characters in the message \s-1ID\s0 from the shell. Single quotes are recommended for this purpose since many message-IDs contain dollar signs. .SH "OPTIONS" .IX Header "OPTIONS" .IP "\fB\-e\fR" 4 .IX Item "-e" Only print the storage token if the article is stored on the system. (In other words, suppress the \f(CW\*(C`/dev/null\*(C'\fR or \f(CW\*(C`not found\*(C'\fR output for missing or remembered articles.) .IP "\fB\-f\fR \fIdb\fR" 4 .IX Item "-f db" Query the history database \fIdb\fR rather than the default history database. .IP "\fB\-i\fR" 4 .IX Item "-i" Rather than expecting a message-ID on the command line, \fBgrephistory\fR will read a list of message-IDs on standard input, one per line. Leading and trailing whitespace is ignored, as are any malformed lines. It will print on standard output those message-IDs which are not found in the history database. This is used when processing \f(CW\*(C`ihave\*(C'\fR control messages. .IP "\fB\-l\fR" 4 .IX Item "-l" Display the entire line from the history database, rather than just the storage \s-1API\s0 token. If the message-ID is present in the history database but has no storage \s-1API\s0 token, \fBgrephistory\fR does not print anything. .IP "\fB\-n\fR" 4 .IX Item "-n" If the message-ID is present in the history database but has no storage \&\s-1API\s0 token, print \f(CW\*(C`/dev/null\*(C'\fR and exit successfully. This can happen if an article has been cancelled or expired, but history information has still been retained. This is the default behavior. .IP "\fB\-q\fR" 4 .IX Item "-q" Don't print any message, but still exit with the appropriate status. .IP "\fB\-s\fR" 4 .IX Item "-s" Rather than expecting a message-ID on the command line, \fBgrephistory\fR will read a list of message-IDs on standard input, one per line. Leading and trailing whitespace is ignored, as are any malformed lines. It will print on standard output the storage \s-1API\s0 tokens for any articles that are still available, one per line. This flag is used when processing \&\f(CW\*(C`sendme\*(C'\fR control messages. .IP "\fB\-v\fR" 4 .IX Item "-v" Print out the hash of the message-ID for diagnostic purposes, as well as any other requested information. This flag is not useful with \fB\-i\fR or \&\fB\-s\fR. .SH "EXAMPLES" .IX Header "EXAMPLES" In case the requested article is not listed in the history database: .PP .Vb 4 \& % grephistory \*(Aq\*(Aq \& grephistory: not found \& % grephistory \-v \*(Aq\*(Aq \& grephistory: not found (hash is 501C66C22932BA91131186D7218201EB) .Ve .PP In case the requested article is listed in the history database but not stored on the server: .PP .Vb 2 \& % grephistory \*(Aq\*(Aq \& /dev/null .Ve .PP In case the requested article is stored on the server: .PP .Vb 4 \& % grephistory \*(Aq<87fxeaay1z.fsf@windlord.stanford.edu>\*(Aq \& @02014A2DD6231FCC00000000000000000000@ \& % grephistory \-l \*(Aq<87fxeaay1z.fsf@windlord.stanford.edu>\*(Aq \& [B6DDF69376E3CC199246CEC949B3ACAC] 1244517923~\-~1244517912 @02014A2DD6231FCC00000000000000000000@ .Ve .PP With \fBsm\fR, we can retrieve the article, and get its posting date: .PP .Vb 2 \& % grephistory \*(Aq<87fxeaay1z.fsf@windlord.stanford.edu>\*(Aq | sm | grep Date \& Date: Mon, 08 Jun 2009 20:25:12 \-0700 \& \& % convdate \-dc 1244517912 \& Tue, 9 Jun 2009 03:25:12 +0000 (UTC) .Ve .PP It matches the number recorded in history as for its posting date. .SH "HISTORY" .IX Header "HISTORY" Written by Rich \f(CW$alz\fR for InterNetNews. Rewritten in \&\s-1POD\s0 by Russ Allbery . .PP \&\f(CW$Id:\fR grephistory.pod 9767 2014\-12\-07 21:13:43Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIhistory\fR\|(5), \fIinn.conf\fR\|(5), \fIsm\fR\|(1). inn-2.6.0/doc/man/pullnews.10000644000175200017520000004435012575023702015201 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "PULLNEWS 1" .TH PULLNEWS 1 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" pullnews \- Pull news from multiple news servers and feed it to another .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fBpullnews\fR [\fB\-BhnOqRx\fR] [\fB\-a\fR \fIhashfeed\fR] [\fB\-b\fR \fIfraction\fR] [\fB\-c\fR \fIconfig\fR] [\fB\-C\fR \fIwidth\fR] [\fB\-d\fR \fIlevel\fR] [\fB\-f\fR \fIfraction\fR] [\fB\-F\fR \fIfakehop\fR] [\fB\-g\fR \fIgroups\fR] [\fB\-G\fR \fInewsgroups\fR] [\fB\-H\fR \fIheaders\fR] [\fB\-k\fR \fIcheckpt\fR] [\fB\-l\fR \fIlogfile\fR] [\fB\-m\fR \fIheader_pats\fR] [\fB\-M\fR \fInum\fR] [\fB\-N\fR \fItimeout\fR] [\fB\-p\fR \fIport\fR] [\fB\-P\fR \fIhop_limit\fR] [\fB\-Q\fR \fIlevel\fR] [\fB\-r\fR \fIfile\fR] [\fB\-s\fR \fIto-server\fR[:\fIport\fR]] [\fB\-S\fR \fImax-run\fR] [\fB\-t\fR \fIretries\fR] [\fB\-T\fR \fIconnect-pause\fR] [\fB\-w\fR \fInum\fR] [\fB\-z\fR \fIarticle-pause\fR] [\fB\-Z\fR \fIgroup-pause\fR] [\fIfrom-server\fR ...] .SH "REQUIREMENTS" .IX Header "REQUIREMENTS" The \f(CW\*(C`Net::NNTP\*(C'\fR module must be installed. This module is available as part of the libnet distribution and comes with recent versions of Perl. For older versions of Perl, you can download it from . .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\fBpullnews\fR reads a config file named \fIpullnews.marks\fR, and connects to the upstream servers given there as a reader client. This file is looked for in \fIpathdb\fR when \fBpullnews\fR is run as the user set in \&\fIrunasuser\fR in \fIinn.conf\fR (which is by default the \f(CW\*(C`news\*(C'\fR user); otherwise, this file is looked for in the running user's home directory. .PP By default, \fBpullnews\fR connects to all servers listed in the configuration file, but you can limit \fBpullnews\fR to specific servers by listing them on the command line: a whitespace-separated list of server names can be specified, like \fIfrom-server\fR for one of them. For each server it connects to, it pulls over articles and feeds them to the destination server via the \s-1IHAVE\s0 or \s-1POST\s0 commands. This means that the system \fBpullnews\fR is run on must have feeding access to the destination news server. .PP \&\fBpullnews\fR is designed for very small sites that do not want to bother setting up traditional peering and is not meant for handling large feeds. .SH "OPTIONS" .IX Header "OPTIONS" .IP "\fB\-a\fR \fIhashfeed\fR" 4 .IX Item "-a hashfeed" This option is a deterministic way to control the flow of articles and to split a feed. The \fIhashfeed\fR parameter must be in the form \f(CW\*(C`value/mod\*(C'\fR or \f(CW\*(C`start\-end/mod\*(C'\fR. The Message-ID of each article is hashed using \s-1MD5\s0, which results in a 128\-bit hash. The lowest 32\ bits are then taken by default as the hashfeed value (which is an integer). If the hashfeed value modulus \f(CW\*(C`mod\*(C'\fR plus one equals \f(CW\*(C`value\*(C'\fR or is between \f(CW\*(C`start\*(C'\fR and \f(CW\*(C`end\*(C'\fR, \fBpullnews\fR will feed the article. All these numbers must be integers. .Sp For instance: .Sp .Vb 2 \& pullnews \-a 1/2 Feeds about 50% of all articles. \& pullnews \-a 2/2 Feeds the other 50% of all articles. .Ve .Sp Another example: .Sp .Vb 3 \& pullnews \-a 1\-3/10 Feeds about 30% of all articles. \& pullnews \-a 4\-5/10 Feeds about 20% of all articles. \& pullnews \-a 6\-10/10 Feeds about 50% of all articles. .Ve .Sp You can use an extended syntax of the form \f(CW\*(C`value/mod:offset\*(C'\fR or \&\f(CW\*(C`start\-end/mod:offset\*(C'\fR (using an underscore \f(CW\*(C`_\*(C'\fR instead of a colon \&\f(CW\*(C`:\*(C'\fR is also recognized). As \s-1MD5\s0 generates a 128\-bit return value, it is possible to specify from which byte-offset the 32\-bit integer used by hashfeed starts. The default value for \f(CW\*(C`offset\*(C'\fR is \f(CW\*(C`:0\*(C'\fR and thirteen overlapping values from \f(CW\*(C`:0\*(C'\fR to \f(CW\*(C`:12\*(C'\fR can be used. Only up to four totally independent values exist: \f(CW\*(C`:0\*(C'\fR, \f(CW\*(C`:4\*(C'\fR, \f(CW\*(C`:8\*(C'\fR and \f(CW\*(C`:12\*(C'\fR. .Sp Therefore, it allows to a generate a second level of deterministic distribution. Indeed, if \fBpullnews\fR feeds \f(CW\*(C`1/2\*(C'\fR, it can go on splitting thanks to \f(CW\*(C`1\-3/9:4\*(C'\fR for instance. Up to four levels of deterministic distribution can be used. .Sp The algorithm is compatible with the one used by Diablo\ 5.1 and up. .IP "\fB\-b\fR \fIfraction\fR" 4 .IX Item "-b fraction" Backtrack on server numbering reset. Specify the proportion (\f(CW0.0\fR to \f(CW1.0\fR) of a group's articles to pull when the server's article number is less than our high for that group. When \fIfraction\fR is \f(CW1.0\fR, pull all the articles on a renumbered server. The default is to do nothing. .IP "\fB\-B\fR" 4 .IX Item "-B" Feed is header-only, that is to say \fBpullnews\fR only feeds the headers of the articles, plus one blank line. It adds the Bytes: header field if the article does not already have one, and keeps the body only if the article is a control article. .IP "\fB\-c\fR \fIconfig\fR" 4 .IX Item "-c config" Normally, the config file is stored in \fIpullnews.marks\fR in \fIpathdb\fR when \fBpullnews\fR is run as the news user, or otherwise in the running user's home directory. If \fB\-c\fR is given, \fIconfig\fR will be used as the config file instead. This is useful if you're running \fBpullnews\fR as a system user on an automated basis out of cron or as an individual user, rather than the news user. .Sp See \*(L"\s-1CONFIG\s0 \s-1FILE\s0\*(R" below for the format of this file. .IP "\fB\-C\fR \fIwidth\fR" 4 .IX Item "-C width" Use \fIwidth\fR characters per line for the progress table. The default value is \f(CW50\fR. .IP "\fB\-d\fR \fIlevel\fR" 4 .IX Item "-d level" Set the debugging level to the integer \fIlevel\fR; more debugging output will be logged as this increases. The default value is \f(CW0\fR. .IP "\fB\-f\fR \fIfraction\fR" 4 .IX Item "-f fraction" This changes the proportion of articles to get from each group to \&\fIfraction\fR and should be in the range \f(CW0.0\fR to \f(CW1.0\fR (\f(CW1.0\fR being the default). .IP "\fB\-F\fR \fIfakehop\fR" 4 .IX Item "-F fakehop" Prepend \fIfakehop\fR as a host to the Path: header of articles fed. .IP "\fB\-g\fR \fIgroups\fR" 4 .IX Item "-g groups" Specify a collection of groups to get. \fIgroups\fR is a list of newsgroups separated by commas (only commas, no spaces). Each group must be defined in the config file, and only the remote hosts that carry those groups will be contacted. Note that this is a simple list of groups, not a wildmat expression, and wildcards are not supported. .IP "\fB\-G\fR \fInewsgroups\fR" 4 .IX Item "-G newsgroups" Add the comma-separated list of groups \fInewsgroups\fR to each server in the configuration file (see also \fB\-g\fR and \fB\-w\fR). .IP "\fB\-h\fR" 4 .IX Item "-h" Print a usage message and exit. .IP "\fB\-H\fR \fIheaders\fR" 4 .IX Item "-H headers" Remove these named headers (colon-separated list) from fed articles. .IP "\fB\-k\fR \fIcheckpt\fR" 4 .IX Item "-k checkpt" Checkpoint (save) the config file every \fIcheckpt\fR articles (default is \f(CW0\fR, that is to say at the end of the session). .IP "\fB\-l\fR \fIlogfile\fR" 4 .IX Item "-l logfile" Log progress/stats to \fIlogfile\fR (default is \f(CW\*(C`stdout\*(C'\fR). .IP "\fB\-m\fR \fIheader_pats\fR" 4 .IX Item "-m header_pats" Feed an article based on header matching. The argument is a number of whitespace-separated tuples (each tuple being a colon-separated header and regular expression). For instance: .Sp .Vb 1 \& \-m "Hdr1:regexp1 !Hdr2:regexp2 #Hdr3:regexp3 !#Hdr4:regexp4" .Ve .Sp specifies that the article will be passed only if the \f(CW\*(C`Hdr1:\*(C'\fR header matches \f(CW\*(C`regexp1\*(C'\fR and the \f(CW\*(C`Hdr2:\*(C'\fR header does not match \f(CW\*(C`regexp2\*(C'\fR. Besides, if the \f(CW\*(C`Hdr3:\*(C'\fR header matches \f(CW\*(C`regexp3\*(C'\fR, that header is removed; and if the \f(CW\*(C`Hdr4:\*(C'\fR header does not match \f(CW\*(C`regexp4\*(C'\fR, that header is removed. .IP "\fB\-M\fR \fInum\fR" 4 .IX Item "-M num" Specify the maximum number of articles (per group) to process. The default is to process all new articles. See also \fB\-f\fR. .IP "\fB\-n\fR" 4 .IX Item "-n" Do nothing but read articles \-\-\ does not feed articles downstream, writes no \fBrnews\fR file, does not update the config file. .IP "\fB\-N\fR \fItimeout\fR" 4 .IX Item "-N timeout" Specify the timeout length, as \fItimeout\fR seconds, when establishing an \s-1NNTP\s0 connection. .IP "\fB\-O\fR" 4 .IX Item "-O" Use an optimized mode: \fBpullnews\fR checks whether the article already exists on the downstream server, before downloading it. It may help for huge articles or a slow link to upstream hosts. .IP "\fB\-p\fR \fIport\fR" 4 .IX Item "-p port" Connect to the destination news server on a port other than the default of \&\f(CW119\fR. This option does not change the port used to connect to the source news servers. .IP "\fB\-P\fR \fIhop_limit\fR" 4 .IX Item "-P hop_limit" Restrict feeding an article based on the number of hops it has already made. Count the hops in the Path: header (\fIhop_count\fR), feeding the article only when \fIhop_limit\fR is \f(CW\*(C`+num\*(C'\fR and \fIhop_count\fR is more than \fInum\fR; or \fIhop_limit\fR is \f(CW\*(C`\-num\*(C'\fR and \fIhop_count\fR is less than \fInum\fR. .IP "\fB\-q\fR" 4 .IX Item "-q" Print out less status information while running. .IP "\fB\-Q\fR \fIlevel\fR" 4 .IX Item "-Q level" Set the quietness level (\f(CW\*(C`\-Q 2\*(C'\fR is equivalent to \f(CW\*(C`\-q\*(C'\fR). The higher this value, the less gets logged. The default is \f(CW0\fR. .IP "\fB\-r\fR \fIfile\fR" 4 .IX Item "-r file" Rather than feeding the downloaded articles to a destination server, instead create a batch file that can later be fed to a server using \fBrnews\fR. See \&\fIrnews\fR\|(1) for more information about the batch file format. .IP "\fB\-R\fR" 4 .IX Item "-R" Be a reader (use \s-1MODE\s0 \s-1READER\s0 and \s-1POST\s0 commands) to the downstream server. The default is to use the \s-1IHAVE\s0 command. .IP "\fB\-s\fR \fIto-server\fR[:\fIport\fR]" 4 .IX Item "-s to-server[:port]" Normally, \fBpullnews\fR will feed the articles it retrieves to the news server running on localhost. To connect to a different host, specify a server with the \fB\-s\fR flag. You can also specify the port with this same flag or use \fB\-p\fR. .IP "\fB\-S\fR \fImax-run\fR" 4 .IX Item "-S max-run" Specify the maximum time \fImax-run\fR in seconds for \fBpullnews\fR to run. .IP "\fB\-t\fR \fIretries\fR" 4 .IX Item "-t retries" The maximum number (\fIretries\fR) of attempts to connect to a server (see also \fB\-T\fR). The default is \f(CW0\fR. .IP "\fB\-T\fR \fIconnect-pause\fR" 4 .IX Item "-T connect-pause" Pause \fIconnect-pause\fR seconds between connection retries (see also \fB\-t\fR). The default is \f(CW1\fR. .IP "\fB\-w\fR \fInum\fR" 4 .IX Item "-w num" Set each group's high water mark (last received article number) to \fInum\fR. If \fInum\fR is negative, calculate \fICurrent\fR+\fInum\fR instead (i.e. get the last \&\fInum\fR articles). Therefore, a \fInum\fR of \f(CW0\fR will re-get all articles on the server; whereas a \fInum\fR of \f(CW\*(C`\-0\*(C'\fR will get no old articles, setting the water mark to \fICurrent\fR (the most recent article on the server). .IP "\fB\-x\fR" 4 .IX Item "-x" If the \fB\-x\fR flag is used, an Xref: header is added to any article that lacks one. It can be useful for instance if articles are fed to a news server which has \fIxrefslave\fR set in \fIinn.conf\fR. .IP "\fB\-z\fR \fIarticle-pause\fR" 4 .IX Item "-z article-pause" Sleep \fIarticle-pause\fR seconds between articles. The default is \f(CW0\fR. .IP "\fB\-Z\fR \fIgroup-pause\fR" 4 .IX Item "-Z group-pause" Sleep \fIgroup-pause\fR seconds between groups. The default is \f(CW0\fR. .SH "CONFIG FILE" .IX Header "CONFIG FILE" The config file for \fBpullnews\fR is divided into blocks, one block for each remote server to connect to. A block begins with the host line (which must have no leading whitespace) and contains just the hostname of the remote server, optionally followed by authentication details (username and password for that server). Note that authentication details can also be provided for the downstream server (a host line could be added for it in the configuration file, with no newsgroup to fetch). .PP Following the host line should be one or more newsgroup lines which start with whitespace followed by the name of a newsgroup to retrieve. Only one newsgroup should be listed on each line. .PP \&\fBpullnews\fR will update the config file to include the time the group was last checked and the highest numbered article successfully retrieved and transferred to the destination server. It uses this data to avoid doing duplicate work the next time it runs. .PP The full syntax is: .PP .Vb 3 \& [ ] \& [
'u address --> address address (stuff) --> address stuff
--> address .fi .RE The transformations are simple, based on RFC\ 5536 which limits the format of the header. .PP .I CAopen and .I CAclose provide news clients with access to the active file; the ``CA'' stands for .IR C lient .IR A ctive. .I CAopen opens the .I active file for reading. It returns a pointer to an open FILE, or NULL on error. If a local or NFS-mounted copy exists, .I CAopen will use that file. The .I FromServer and .I ToServer parameters should be FILE's connected to the NNTP server for input and output, respectively. See .I NNTPremoteopen or .IR NNTPlocalopen , below. If either parameter is NULL, then .I CAopen will just return NULL if the file is not locally available. If they are not NULL, .I CAopen will use them to query the NNTP server using the ``list'' command to make a local temporary copy. .PP The .I CAlistopen sends a ``list'' command to the server and returns a temporary file containing the results. The .I request parameter, if not NULL, will be sent as an argument to the command. Unlike .IR CAopen , this routine will never use a locally-available copy of the active file. .PP .I CAclose closes the active file and removes any temporary file that might have been created by .I CAopen or .IR CAlistopen . .PP .I fdflag_close_exec can make a descriptor ``close-on-exec'' so that it is not shared with any child processes. If the flag is non-zero, the file is so marked; if zero, the ``close-on-exec'' mode is cleared. .PP .IR DDstart , .IR DDcheck , and .I DDend are used to set the Distribution header; the ``DD'' stands for .IR D efault .IR D istribution. The .I distrib.pats file is consulted to determine the proper value for the Distribution header after all newsgroups have been checked. .I DDstart begins the parsing. It returns a pointer to an opaque handle that should be used on subsequent calls. The .I FromServer and .I ToServer parameters should be FILE's connected to the NNTP server for input and output, respectively. If either parameter is NULL, then an empty default will ultimately be returned if the file is not locally available. .PP .I DDcheck should be called with the handle, .IR h , returned by .I DDstart and a newgroups, .IR group , to check. It can be called as often as necessary. .PP .I DDend releases any state maintained in the handle and returns an allocated copy of the text that should be used for the Distribution header. .PP .I fdflag_nonblocking enables (if .I flag is non-zero) or disables (if .I flag is zero) non-blocking I/O on the indicated descriptor. It returns \-1 on failure or zero on success. .PP .I inn_lock_file tries to lock the file descriptor .IR fd . If .I block is true it will block until the lock can be made, otherwise it will return false if the file cannot be locked. .I type is one of: INN_LOCK_READ, INN_LOCK_WRITE, or INN_LOCK_UNLOCK. It returns false on failure or true on success. .PP .I GetFQDN returns the fully-qualified domain name of the local host. .I Domain is used if local host can not be resolved. The returned value points to static space that is reused on subsequent calls, or NULL on error. .PP .I GetModeratorAddress returns the mailing address of the moderator for specified .I group or NULL on error. .I Moderatormailer is used as its address, if there is no matched moderator. See .IR moderators (5) for details on how the address is determined. .I GetModeratorAddress does no checking to see if the specified group is actually moderated. The returned value points to static space that is reused on subsequent calls. The .I FromServer and .I ToServer parameters should be FILE's connected to the NNTP server for input and output, respectively. If either of these parameters is NULL, then an attempt to get the list from a local copy is made. .PP .I GetResourceUsage fills in the .I usertime and .I systime parameters with the total user and system time used by the current process and any children it may have spawned. If .I is defined, it gets the values by doing a .IR getrusage (2) system call; otherwise it calls .IR times (2). It returns \-1 on failure, or zero on success. .PP .I NNTPlocalopen opens a connection to the private port of an InterNetNews server running on the local host, if .I is defined. It returns \-1 on failure, or zero on success. .I FromServerp and .I ToServerp will be filled in with FILE's which can be used to communicate with the server. .I Errbuff can either be NULL or a pointer to a buffer at least 512 bytes long. If not NULL, and the server refuses the connection, then it will be filled in with the text of the server's reply. .I Len should be the length of the buffer. This routine is not for general use. If .I is not defined, this is a stub routine, for compatibility with systems that have Unix-domain stream sockets. It always returns \-1. .PP .I NNTPremoteopen does the same except that it uses ``innconf->server'' as the local server, and opens a connection to the .IR port . Any client program can use this routine. It returns \-1 on failure, or zero on success. .PP .I NNTPconnect is the same as .I NNTPremoteopen except that the desired host is given as the .I host parameter. .PP .I NNTPsendarticle writes .I text on .I ToServer using NNTP conventions for line termination. The text should consist of one or more lines ending with a newline. If .I Terminate is non-zero, then the routine will also write the NNTP data-termination marker on the stream. It returns \-1 on failure, or zero on success. .PP .I NNTPsendpassword sends authentication information to an NNTP server by finding the appropriate entry in the .I passwd.nntp file. .I Server contains the name of the host; ``innconf->server'' will be used if .I server is NULL. .I FromServer and .I ToServer should be FILE's that are connected to the server. No action is taken if the specified host is not listed in the password file. .PP .I Radix32 converts the number in .I value into a radix-32 string into the buffer pointed to by .IR p . The number is split into five-bit pieces and each pieces is converted into a character using the alphabet .I "0..9a..v" to represent the numbers 0..32. Only the lowest 32 bits of .I value are used, so .I p need only point to a buffer of eight bytes (seven characters and the trailing \e0). .PP .I ReadInFile reads the file named .I name into allocated memory, appending a terminating \e0 byte. It returns a pointer to the space, or NULL on error. If .I Sbp is not NULL, it is taken as the address of a place to store the results of a .IR stat (2) call. .PP .I ReadInDescriptor performs the same function as .I ReadInFile except that .I fd refers to an already-open file. .PP .I HashMessageID returns hashed message-id using MD5. .SH EXAMPLES .RS .nf char *p; char *Article; char buff[256], errbuff[256]; FILE *F; FILE *ToServer; FILE *FromServer; int port = 119; if ((p = HeaderFind(Article, "From", 4)) == NULL) Fatal("Can't find From line"); (void)strcpy(buff, p); HeaderCleanFrom(buff); if ((F = CAopen(FromServer, ToServer)) == NULL) Fatal("Can't open active file"); /* Don't pass the file on to our children. */ fdflag_close_exec(fileno(F), 1); /* Make a local copy. */ p = ReadInDescriptor(fileno(F), (struct stat *)NULL); /* Close the file. */ CAclose(); if (NNTPremoteopen(port, &FromServer, &ToServer, errbuff) < 0) Fatal("Can't connect to server"); if ((p = GetModeratorAddress("comp.sources.unix")) == NULL) Fatal("Can't find moderator's address"); .fi .RE .SH HISTORY Written by Rich $alz for InterNetNews. .de R$ This is revision \\$3, dated \\$4. .. .R$ $Id: libinn.3 9659 2014-08-30 08:08:11Z iulius $ .SH "SEE ALSO" active(5), dbz(3z), inn.conf(5), inndcomm(3), moderators(5), passwd.nntp(5). inn-2.6.0/doc/man/ckpasswd.80000644000175200017520000003041012575023702015146 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "CKPASSWD 8" .TH CKPASSWD 8 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" ckpasswd \- nnrpd password authenticator .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fBckpasswd\fR [\fB\-gs\fR] [\fB\-d\fR \fIdatabase\fR] [\fB\-f\fR \fIfilename\fR] [\fB\-u\fR \fIusername\fR \fB\-p\fR \fIpassword\fR] .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\fBckpasswd\fR is the basic password authenticator for \fBnnrpd\fR, suitable for being run from an auth stanza in \fIreaders.conf\fR. See \fIreaders.conf\fR\|(5) for more information on how to configure an \fBnnrpd\fR authenticator. .PP \&\fBckpasswd\fR accepts a username and password from \fBnnrpd\fR and tells \fInnrpd\fR\|(8) whether that's the correct password for that username. By default, when given no arguments, it tries to check the password using \s-1PAM\s0 if support for \s-1PAM\s0 was found when \s-1INN\s0 was built. Failing that, it tries to check the password against the password field returned by \fIgetpwnam\fR\|(3). Note that these days most systems no longer make real passwords available via \&\fIgetpwnam\fR\|(3) (some still do if and only if the program calling \fIgetpwnam\fR\|(3) is running as root). .PP When using \s-1PAM\s0, \fBckpasswd\fR identifies itself as \f(CW\*(C`nnrpd\*(C'\fR, not as \&\f(CW\*(C`ckpasswd\*(C'\fR, and the \s-1PAM\s0 configuration must be set up accordingly. The details of \s-1PAM\s0 configuration are different on different operating systems (and even different Linux distributions); see \s-1EXAMPLES\s0 below for help getting started, and look for a \fIpam\fR\|(7) or \fIpam.conf\fR\|(4) manual page on your system. .PP When using any method other than \s-1PAM\s0, \fBckpasswd\fR expects all passwords to be stored encrypted by the system \fIcrypt\fR\|(3) function and calls \fIcrypt\fR\|(3) on the supplied password before comparing it to the expected password. If you're using a different password hash scheme (like \s-1MD5\s0), you must use \&\s-1PAM\s0. .SH "OPTIONS" .IX Header "OPTIONS" .IP "\fB\-d\fR \fIdatabase\fR" 4 .IX Item "-d database" Read passwords from a database (ndbm, gdbm or dbm format depending on what your system has) rather than by using \fIgetpwnam\fR\|(3). \fBckpasswd\fR expects \fIdatabase\fR.dir and \fIdatabase\fR.pag to exist and to be a database keyed by username with the encrypted passwords as the values. .Sp While \s-1INN\s0 doesn't come with a program intended specifically to create such databases, on most systems it's fairly easy to write a Perl script to do so. Something like: .Sp .Vb 10 \& #!/usr/bin/perl \& use NDBM_File; \& use Fcntl; \& tie (%db, \*(AqNDBM_File\*(Aq, \*(Aq/path/to/database\*(Aq, O_RDWR|O_CREAT, 0640) \& or die "Cannot open /path/to/database: $!\en"; \& $| = 1; \& print "Username: "; \& my $user = ; \& chomp $user; \& print "Password: "; \& my $passwd = ; \& chomp $passwd; \& my @alphabet = (\*(Aq.\*(Aq, \*(Aq/\*(Aq, 0..9, \*(AqA\*(Aq..\*(AqZ\*(Aq, \*(Aqa\*(Aq..\*(Aqz\*(Aq); \& my $salt = join \*(Aq\*(Aq, @alphabet[rand 64, rand 64]; \& $db{$user} = crypt ($passwd, $salt); \& untie %db; .Ve .Sp Note that this will echo back the password when typed; there are obvious improvements that could be made to this, but it should be a reasonable start. Sometimes a program like this will be available with the name \&\fBdbmpasswd\fR. .Sp This option will not be available on systems without ndbm, gdbm or dbm libraries. .IP "\fB\-f\fR \fIfilename\fR" 4 .IX Item "-f filename" Read passwords from the given file rather than using \fIgetpwnam\fR\|(3). The file is expected to be formatted like a system password file, at least vaguely. That means each line should look something like: .Sp .Vb 1 \& username:pdIh9NCNslkq6 .Ve .Sp (and each line may have an additional colon after the encrypted password and additional data; that data will be ignored by \fBckpasswd\fR). Lines starting with a number sign (\f(CW\*(C`#\*(C'\fR) are ignored. \s-1INN\s0 does not come with a utility to create the encrypted passwords, but \fBhtpasswd\fR (which comes with Apache) can do so and it's a quick job with Perl (see the example script under \fB\-d\fR, or also below). If using Apache's \&\fBhtpasswd\fR program, be sure to give it the \fB\-d\fR option so that it will use \fIcrypt\fR\|(3). .Sp A line in \fIfilename\fR for the user \f(CW\*(C`user\*(C'\fR with the password \f(CW\*(C`pass\*(C'\fR would be \f(CW\*(C`user:LIfOpbjNaEQYE\*(C'\fR as obtained by the following command: .Sp .Vb 2 \& % htpasswd \-nbd user pass \& user:LIfOpbjNaEQYE .Ve .Sp In case \fBhtpasswd\fR is not installed or if you do not want to depend on it, another command involving Perl does a similar job: .Sp .Vb 2 \& % perl \-e \*(Aqprint "user:".crypt("pass", "LI")."\en";\*(Aq \& user:LIfOpbjNaEQYE .Ve .IP "\fB\-g\fR" 4 .IX Item "-g" Attempt to look up system group corresponding to username and return a string like \f(CW\*(C`user@group\*(C'\fR to be matched against in \fIreaders.conf\fR. This option is incompatible with the \fB\-d\fR and \fB\-f\fR options. .IP "\fB\-p\fR \fIpassword\fR" 4 .IX Item "-p password" Use \fIpassword\fR as the password for authentication rather than reading a password using the \fBnnrpd\fR authenticator protocol. This option is useful only for testing your authentication system (particularly since it involves putting a password on the command line), and does not work when \&\fBckpasswd\fR is run by \fBnnrpd\fR. If this option is given, \fB\-u\fR must also be given. .IP "\fB\-s\fR" 4 .IX Item "-s" Check passwords against the result of \fIgetspnam\fR\|(3) instead of \fIgetpwnam\fR\|(3). This function, on those systems that supports it, reads from \fI/etc/shadow\fR or similar more restricted files. If you want to check passwords supplied to \fInnrpd\fR\|(8) against system account passwords, you will probably have to use this option on most systems. .Sp Most systems require special privileges to call \fIgetspnam\fR\|(3), so in order to use this option you may need to make \fBckpasswd\fR setgid to some group (like group \f(CW\*(C`shadow\*(C'\fR) or even setuid root. \fBckpasswd\fR has not been specifically audited for such uses! It is, however, a very small program that you should be able to check by hand for security. .Sp This configuration is not recommended if it can be avoided, for serious security reasons. See \*(L"\s-1SECURITY\s0 \s-1CONSIDERATIONS\s0\*(R" in \fIreaders.conf\fR\|(5) for discussion. .IP "\fB\-u\fR \fIusername\fR" 4 .IX Item "-u username" Authenticate as \fIusername\fR. This option is useful only for testing (so that you can test your authentication system easily) and does not work when \fBckpasswd\fR is run by \fBnnrpd\fR. If this option is given, \fB\-p\fR must also be given. .SH "EXAMPLES" .IX Header "EXAMPLES" See \fIreaders.conf\fR\|(5) for examples of \fInnrpd\fR\|(8) authentication configuration that uses \fBckpasswd\fR to check passwords. .PP An example \s-1PAM\s0 configuration for \fI/etc/pam.conf\fR that tells \fBckpasswd\fR to check usernames and passwords against system accounts is: .PP .Vb 2 \& nnrpd auth required pam_unix.so \& nnrpd account required pam_unix.so .Ve .PP Your system may want you to instead create a file named \fInnrpd\fR in \&\fI/etc/pam.d\fR with lines like: .PP .Vb 2 \& auth required pam_unix.so \& account required pam_unix.so .Ve .PP This is only the simplest configuration. You may be able to include common shared files, and you may want to stack other modules, either to allow different authentication methods or to apply restrictions like lists of users who can't authenticate using \fBckpasswd\fR. The best guide is the documentation for your system and the other \s-1PAM\s0 configurations you're already using. .PP To test to make sure that \fBckpasswd\fR is working correctly, you can run it manually and then give it the username (prefixed with \f(CW\*(C`ClientAuthname:\*(C'\fR) and password (prefixed with \f(CW\*(C`ClientPassword:\*(C'\fR) on standard input. For example: .PP .Vb 2 \& (echo \*(AqClientAuthname: test\*(Aq ; echo \*(AqClientPassword: testing\*(Aq) \e \& | ckpasswd \-f /path/to/passwd/file .Ve .PP will check a username of \f(CW\*(C`test\*(C'\fR and a password of \f(CW\*(C`testing\*(C'\fR against the username and passwords stored in \fI/path/to/passwd/file\fR. On success, \&\fBckpasswd\fR will print \f(CW\*(C`User:test\*(C'\fR and exit with status \f(CW0\fR. On failure, it will print some sort of error message and exit a non-zero status. .SH "HISTORY" .IX Header "HISTORY" Written by Russ Allbery for InterNetNews. .PP \&\f(CW$Id:\fR ckpasswd.pod 9937 2015\-09\-02 12:44:39Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIcrypt\fR\|(3), \fInnrpd\fR\|(8), \fIpam\fR\|(7), \fIreaders.conf\fR\|(5). inn-2.6.0/doc/man/fastrm.10000644000175200017520000003123212575023702014617 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "FASTRM 1" .TH FASTRM 1 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" fastrm \- Quickly remove a list of files .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fBfastrm\fR [\fB\-de\fR] [\fB\-c\fR|\fB\-c\fR\fII\fR] [\fB\-s\fR|\fB\-s\fR\fIM\fR] [\fB\-u\fR|\fB\-u\fR\fIN\fR] \&\fIbase-directory\fR .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\fBfastrm\fR reads a list of either file names or storage \s-1API\s0 tokens, one per line, from its standard input and removes them. Storage \s-1API\s0 tokens are removed via the \fISMcancel()\fR interface. \fBfastrm\fR does not delete files safely or with an eye to security, but rather cuts every corner it can to delete files as fast as it can. It should therefore never be run on publically writable directories, or in any other environment where a hostile party may control the directory structure in which it is working. .PP If a file name is not an absolute path name, it is considered to be relative to \fIbase-directory\fR as given on the command line. The \&\fIbase-directory\fR parameter must be a simple absolute pathname (it must not contain multiple consecutive slashes or references to the special directories \f(CW\*(C`.\*(C'\fR or \f(CW\*(C`..\*(C'\fR). .PP \&\fBfastrm\fR is designed to be faster than the typical \f(CW\*(C`| xargs rm\*(C'\fR pipeline when given a sorted list of file names as input. For example, \fBfastrm\fR will usually \fIchdir\fR\|(2) into a directory before removing files from it, meaning that if its input is sorted, most names passed to \fIunlink\fR\|(2) will be simple names. This can substantially reduce the operating system overhead from directory lookups. .PP \&\fBfastrm\fR assumes that its input is valid and that it is safe to call \&\fIunlink\fR\|(2) on every file name it is given. As a safety measure, however, \&\fBfastrm\fR when running as root will check with \fIstat\fR\|(2) that a file name doesn't specify a directory before removing it. (In some operating systems, root is allowed to unlink directories, even directories which aren't empty, which can cause file system corruption.) .PP The input to \fBfastrm\fR should always be sorted \-\-\ or even better be in the order file names are output by \fIfind\fR\|(1)\ \*(-- if speed is an issue and the input isn't solely storage \s-1API\s0 tokens. (It deals fine with unsorted input, but is unlikely to be any faster in that case than a simple \f(CW\*(C`| xargs rm\*(C'\fR command.) Sorting may even slightly speed up the removal of storage \&\s-1API\s0 tokens due to caching effects, since sorting will tend to keep all of the tokens from a particular storage method together. .PP Various additional optimizations for removing files can be turned on and/or tuned with options (see below). Which options will be most effective depends heavily on the underlying structure of the file system, the way in which directories are stored and searched, and similar, often underdocumented, operating system implementation details. The more sophisticated the underlying operating system and file system, the more likely that it will already perform the equivalent of these optimizations internally. .SH "OPTIONS" .IX Header "OPTIONS" .IP "\fB\-c\fR[\fII\fR]" 4 .IX Item "-c[I]" Controls when \fBfastrm\fR calls \fIchdir\fR\|(2). If the number of files to be unlinked from a given directory is at least \fII\fR, then \fBfastrm\fR will change to that directory before unlinking those files. Otherwise, it will use either the absolute path names or a path name relative to the current directory (whichever is likely more efficient). The \fII\fR parameter is optional; if just \fB\-c\fR is given, \fB\-c1\fR is assumed, which will cause \&\fBfastrm\fR to always chdir before calling \fIunlink\fR\|(2). The default is \&\fB\-c3\fR. Use \fB\-c0\fR to prevent \fBfastrm\fR from ever using \fIchdir\fR\|(2). .IP "\fB\-d\fR" 4 .IX Item "-d" Don't remove any files. Instead, print a list of the files that would be removed to standard output. Each line contains either the current directory of \fBfastrm\fR at the time it would do the unlink and the relative path name it would pass to \fIunlink\fR\|(2) as two fields separated by whitespace and a \f(CW\*(C`/\*(C'\fR, the absolute path name (as a single field) that would be passed to \fIunlink\fR\|(2), or the string \f(CW\*(C`Token\*(C'\fR and the storage \s-1API\s0 token that would be removed. .IP "\fB\-e\fR" 4 .IX Item "-e" Treat an empty input file as an error. This is most useful when \fBfastrm\fR is last in a pipeline after a preceding \fIsort\fR\|(1) command, ensuring that \&\fBfastrm\fR will fail if the sort fails. .IP "\fB\-s\fR[\fIM\fR]" 4 .IX Item "-s[M]" When \fB\-s\fR is given and the number of files to remove in a directory is greater than \fIM\fR, rather than remove files in the order given, \fBfastrm\fR will open the directory and read it, unlinking files in the order that they appear in the directory. On systems with a per-process directory cache or that use a linear search to find files in a directory, this should make directory lookups faster. The \fIM\fR parameter is optional; if just \fB\-s\fR is given, \fB\-s5\fR is assumed. .Sp When this option is in effect, \fBfastrm\fR won't attempt to remove files that it doesn't see in the directory, possibly significantly speeding it up if most of the files to be removed have already been deleted. However, using this option requires \fBfastrm\fR to do more internal work and it also assumes that the order of directory listings is stable in the presence of calls to \fIunlink\fR\|(2) between calls to \fIreaddir\fR\|(3). This may be a dangerous assumption with some sophisticated file systems (and in general this option is only useful with file systems that use unindexed linear searches to find files in directories or when most of the files to be removed have already been deleted). .Sp This optimization is off by default. .IP "\fB\-u\fR[\fIN\fR]" 4 .IX Item "-u[N]" Specifying this option promises that there are no symbolic links in the directory tree from which files are being removed. This allows \fBfastrm\fR to make an additional optimization to its calls to \fIchdir\fR\|(2), constructing a relative path using \f(CW\*(C`../..\*(C'\fR and the like to pass to \fIchdir\fR\|(2) rather than always using absolute paths. Since this reduces the number of directory lookups needed with deeply nested directory structures (such as that typically created by traditional news spool storage), it can be a significant optimization, but it breaks horribly in the presence of symbolic links to directories. .Sp When \fB\-u\fR is given, \fBfastrm\fR will use at most \fIN\fR levels of \f(CW\*(C`..\*(C'\fR segments to construct paths. \fIN\fR is optional; if just \fB\-u\fR is given, \&\fB\-u1\fR is assumed. .Sp This optimization is off by default. .PP \&\fBfastrm\fR also accepts \fB\-a\fR and \fB\-r\fR options, which do nothing at all except allow you to say \f(CW\*(C`fastrm \-usa\*(C'\fR, \f(CW\*(C`fastrm \-usr\*(C'\fR, or \f(CW\*(C`fastrm \&\-user\*(C'\fR. These happen to often be convenient sets of options to use. .SH "EXIT STATUS" .IX Header "EXIT STATUS" \&\fBfastrm\fR exits with a status of zero if there were no problems, and an exit status of 1 if something went wrong. Attempting to remove a file that does not exist is not considered a problem. .SH "EXAMPLES" .IX Header "EXAMPLES" \&\fBfastrm\fR is typically invoked by \s-1INN\s0 via \fIexpirerm\fR\|(8) using a command like: .PP .Vb 1 \& fastrm \-e < expire.list .Ve .PP To enable all optimizations and see the affect on the order of removal caused by \fB\-s\fR, use: .PP .Vb 1 \& fastrm \-d \-s \-e \-u < expire.list .Ve .PP If your file system has indexed directory lookups, but you have a deeply nested directory structure, you may want to use a set of flags like: .PP .Vb 1 \& fastrm \-e \-u3 < expire.list .Ve .PP to strongly prefer relative paths but not to use \fIreaddir\fR\|(2) to order the calls to \fIunlink\fR\|(2). .PP You may want to edit \fIexpirerm\fR\|(8) to change the flags passed to \fBfastrm\fR. .SH "WARNINGS" .IX Header "WARNINGS" \&\fBfastrm\fR cuts corners and does not worry about security, so it does not use \fIchdir\fR\|(2) safely and could be tricked into removing files other than those that were intended if run on a specially constructed file tree or a file tree that is being modified while it is running. It should therefore never be used with world-writable directories or any other directory that might be controlled or modified by an attacker. .SH "NOTES" .IX Header "NOTES" \&\fBfastrm\fR defers opening the storage subsystem or attempting to parse any \&\s-1INN\s0 configuration files until it encounters a token in the list of files to remove. It's therefore possible to use \fBfastrm\fR outside of \s-1INN\s0 as a general fast file removal program. .SH "HISTORY" .IX Header "HISTORY" \&\fBfastrm\fR was originally written by . This manual page was rewritten in \s-1POD\s0 by Russ Allbery for InterNetNews. .PP \&\f(CW$Id:\fR fastrm.pod 9767 2014\-12\-07 21:13:43Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIexpirerm\fR\|(8). inn-2.6.0/doc/man/shlock.10000644000175200017520000001457012575023702014614 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "SHLOCK 1" .TH SHLOCK 1 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" shlock \- Create lock files for use in shell scripts .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fBshlock\fR [\fB\-b\fR|\fB\-c\fR|\fB\-u\fR] \fB\-f\fR \fIname\fR \fB\-p\fR \fIpid\fR .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\fBshlock\fR tries to create a lock file named \fIname\fR and write the process \s-1ID\s0 \fIpid\fR into it. If the file already exists, \fBshlock\fR will read the process \s-1ID\s0 from the file and test to see whether the process is currently running. If the process exists, then the file will not be created. \fIshlock\fR exits with a zero status if it could create the lock file, or non-zero if the file refers to a currently active process. .PP A Perl wrapper around \fBshlock\fR can be used via the \f(CW\*(C`INN::Utils::Shlock\*(C'\fR module. .SH "OPTIONS" .IX Header "OPTIONS" .IP "\fB\-b\fR" 4 .IX Item "-b" Process IDs are normally read and written in \s-1ASCII\s0. If the \fB\-b\fR flag is used, then they will be written as a binary int. .IP "\fB\-c\fR" 4 .IX Item "-c" If the \fB\-c\fR flag is used, then \fBshlock\fR will not create a lock file, but will instead use the file to see if the lock is held by another program. If the lock is valid, the program will exit with a non-zero status; if the lock is not valid (i.e. invoking \fBshlock\fR without the flag would have succeeded), then the program will exit with a zero status. .IP "\fB\-f\fR \fIname\fR" 4 .IX Item "-f name" \&\fIname\fR is the name of the lock file \fBshlock\fR attempts to create. If the file already exists, it will read the process \s-1ID\s0 from the file and exit with a non-zero status if this process is currently active. .IP "\fB\-p\fR \fIpid\fR" 4 .IX Item "-p pid" \&\fIpid\fR is the process \s-1ID\s0 to write into the file \fIname\fR. .IP "\fB\-u\fR" 4 .IX Item "-u" For compatibility with other systems, the \fB\-u\fR flag is accepted as a synonym for \fB\-b\fR since binary locks are used by many \s-1UUCP\s0 packages. .SH "EXAMPLES" .IX Header "EXAMPLES" The following example shows how \fBshlock\fR would be used within a shell script: .PP .Vb 7 \& LOCK=/LOCK.send \& trap \*(Aqrm \-f ${LOCK} ; exit 1\*(Aq 1 2 3 15 \& if shlock \-p $$ \-f ${LOCK} ; then \& # Do appropriate work. \& else \& echo "Locked by \`cat ${LOCK}\`" \& fi .Ve .SH "HISTORY" .IX Header "HISTORY" Written by Rich \f(CW$alz\fR for InterNetNews after a description of \s-1HDB\s0 \s-1UUCP\s0 locking given by Peter Honeyman, and improved by Berend Reitsma to solve a race condition. Converted to \s-1POD\s0 by Julien Elie. .PP \&\f(CW$Id:\fR shlock.pod 9303 2011\-08\-04 22:09:57Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIINN::Utils::Shlock\fR\|(3pm). inn-2.6.0/doc/man/subscriptions.50000644000175200017520000001306312575023702016240 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "SUBSCRIPTIONS 5" .TH SUBSCRIPTIONS 5 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" subscriptions \- Default recommended subscriptions .SH "DESCRIPTION" .IX Header "DESCRIPTION" The \fIpathetc\fR/subscriptions file contains a list of newsgroups that is returned by the \s-1NNTP\s0 command \s-1LIST\s0 \s-1SUBSCRIPTIONS\s0. .PP Clients that support this command usually send it the first time they connect to a new news server. They use the returned list to initialize the list of subscribed newsgroups. The \fIsubscriptions\fR file therefore should contain groups intended for new users, for testing, or that contain FAQs and other useful information for first-time Usenet users. .PP The syntax of the \fIsubscriptions\fR file is trivial; it is a simple list of newsgroup names, one per line. The order of newsgroups may be significant; the news reading client may present the groups in that order to the user. .PP Be aware that use of the \s-1LIST\s0 \s-1SUBSCRIPTIONS\s0 command is not widespread (though documented in \s-1RFC\s0\ 6048) and most news clients will never ask for this file. .SH "EXAMPLE" .IX Header "EXAMPLE" A typical \fIsubscriptions\fR file may look like: .PP .Vb 9 \& news.announce.newusers \& news.newusers.questions \& local.test \& local.general \& local.talk \& misc.test \& misc.test.moderated \& news.answers \& news.announce.newgroups .Ve .PP This gives the client the FAQs and question newsgroup for new users first, then a local newsgroup for testing and various commonly-read local discussion groups, followed by the world-wide test groups, all the FAQs, and announcements of new world-wide newsgroups. If there is a local new users group, one might want to list it first. .SH "HISTORY" .IX Header "HISTORY" Written by Bettina Fink for InterNetNews. .PP \&\f(CW$Id:\fR subscriptions.pod 9137 2010\-10\-29 18:09:12Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fInnrpd\fR\|(8). inn-2.6.0/doc/man/innstat.80000644000175200017520000000103612575023702015011 0ustar iuliusiulius.TH INNSTAT 8 .SH NAME innstat \- print snapshot of INN system .SH SYNOPSIS .B innstat .SH DESCRIPTION The .I innstat script prints a snapshot of the INN system. It displays the operating mode of the server, as well as disk usage and the status of all log and lock files. .SH HISTORY Written by Landon Curt Noll and Rich $alz for InterNetNews. .de R$ This is revision \\$3, dated \\$4. .. .R$ $Id: innstat.8 584 1998-04-09 15:16:17Z mibsoft $ .SH "SEE ALSO" innd(8), news.daily(8), newslog(5), nnrpd(8) inn-2.6.0/doc/man/sendinpaths.80000644000175200017520000001522012575023702015651 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "SENDINPATHS 8" .TH SENDINPATHS 8 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" sendinpaths \- Send Usenet Path: statistics via e\-mail .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fBsendinpaths\fR [\fB\-cdhn\fR] [\fB\-k\fR \fIkeep-days\fR] [\fB\-r\fR \fIreport-days\fR] [\fIaddress\fR [\fIaddress\fR ...]] .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\fBsendinpaths\fR checks \fIpathlog\fR/path for \fBninpaths\fR dump files, finds dump files generated in the past \fIreport-days\fR days, makes sure they are valid by running \fBninpaths\fR on each one and making sure the exit status is zero, and passes them to \fBninpaths\fR to generate a cumulative report. By default, that report is mailed to the e\-mail addresses configured at the beginning of this script (by default, only one address is configured: ) in order to supply the \s-1TOP1000\s0 project with useful statistics. See for more information. .PP When finished, \fBsendinpaths\fR deletes all dump files in \fIpathlog\fR/path that are older than \fIkeep-days\fR days. .PP For more information on how to set up \fBninpaths\fR, see \fIninpaths\fR\|(8). .SH "OPTIONS" .IX Header "OPTIONS" .IP "\fB\-c\fR" 4 .IX Item "-c" When this flag is used, the report is also e\-mailed, besides the default submission addresses or those given as command-line arguments, to the newsmaster's address set at configure time. .IP "\fB\-d\fR" 4 .IX Item "-d" Enables debug messages. .IP "\fB\-h\fR" 4 .IX Item "-h" Gives usage information. .IP "\fB\-k\fR \fIkeep-days\fR" 4 .IX Item "-k keep-days" After having processed dump files, \fBsendinpaths\fR removes those that are older than \fIkeep-days\fR days. The default is \f(CW0\fR, that is to say to remove all dump files. .Sp Setting \fIkeep-days\fR to another value can be useful for debugging purpose because it permits to keep a few dump files. .IP "\fB\-n\fR" 4 .IX Item "-n" Don't e\-mail the report; instead, just print it to standard output. Don't delete old dump files. .IP "\fB\-r\fR \fIreport-days\fR" 4 .IX Item "-r report-days" Process dump files generated during the last \fIreport-days\fR days. The default is \f(CW32\fR, that is to say to process all the dump files that have been generated during the last 32 days (if, of course, they have not been deleted yet by a previous run of \fBsendinpaths\fR according to the value set by the \fB\-k\fR flag). .IP "\fIaddress\fR ..." 4 .IX Item "address ..." E\-mail the report to the mentioned address or addresses, instead of the default one. Several addresses can be used, separated by whitespace (sending the e\-mail to your own e\-mail address can be useful for debugging purpose, to check that everything works fine). For instance, for two addresses: .Sp .Vb 1 \& sendinpaths pathsurvey@example.org top1000@anthologeek.net .Ve .SH "HISTORY" .IX Header "HISTORY" \&\fBsendinpaths\fR was written by Olaf Titz . .PP \&\f(CW$Id:\fR sendinpaths.pod 9624 2014\-03\-16 13:23:46Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIninpaths\fR\|(8). inn-2.6.0/doc/man/tst.30000644000175200017520000001555112575023702014145 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "tst 3" .TH tst 3 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" tst \- ternary search trie functions .SH "SYNOPSIS" .IX Header "SYNOPSIS" .Vb 1 \& #include \& \& struct tst; \& \& struct tst *tst_init(int node_line_width); \& \& void tst_cleanup(struct tst *tst); \& \& int tst_insert(struct tst *tst, const unsigned char *key, void *data, int option, void **exist_ptr); \& \& void *tst_search(struct tst *tst, const unsigned char *key); \& \& void *tst_delete(struct tst *tst, const unsigned char *key); .Ve .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\fBtst_init\fR allocates memory for members of \fIstruct tst\fR, and allocates the first \fInode_line_width\fR nodes. A \s-1NULL\s0 pointer is returned by \fBtst_init\fR if any part of the memory allocation fails. On success, a pointer to a \fIstruct tst\fR is returned. .PP The value for \fInode_line_width\fR must be chosen very carefully. One node is required for every character in the tree. If you choose a value that is too small, your application will spend too much time calling \fImalloc\fR\|(3) and your node space will be too spread out. Too large a value is just a waste of space. .PP \&\fBtst_cleanup\fR frees all memory allocated to nodes, internal structures, as well as \fItst\fR itself. .PP \&\fBtst_insert\fR inserts the string \fIkey\fR into the tree. Behavior when a duplicate key is inserted is controlled by \fIoption\fR. If \fIkey\fR is already in the tree then \fB\s-1TST_DUPLICATE_KEY\s0\fR is returned, and the data pointer for the existing key is placed in \fIexist_ptr\fR. If \&\fIoption\fR is set to \fB\s-1TST_REPLACE\s0\fR then the existing data pointer for the existing key is replaced by \fIdata\fR. Note that the old data pointer will still be placed in \fIexist_ptr\fR. .PP If a duplicate key is encountered and \fIoption\fR is not set to \&\fB\s-1TST_REPLACE\s0\fR then \fB\s-1TST_DUPLICATE_KEY\s0\fR is returned. If \fIkey\fR is zero length then \fB\s-1TST_NULL_KEY\s0\fR is returned. A successful insert or replace returns \fB\s-1TST_OK\s0\fR. A return value of \fB\s-1TST_ERROR\s0\fR indicates that a memory allocation error occurred while trying to grow the node free. .PP Note that the \fIdata\fR argument must never be \fB\s-1NULL\s0\fR. If it is, then calls to \fBtst_search\fR will fail for a key that exists because the data value was set to \fB\s-1NULL\s0\fR, which is what \fBtst_search\fR returns. If you just want a simple existence tree, use the \fBtst\fR pointer as the data pointer. .PP \&\fBtst_search\fR finds the string \fIkey\fR in the tree if it exists and returns the data pointer associated with that key. .PP If \fIkey\fR is not found then \fB\s-1NULL\s0\fR is returned, otherwise the data pointer associated with \fIkey\fR is returned. .PP \&\fBtst_delete\fR deletes the string \fIkey\fR from the tree if it exists and returns the data pointer assocaited with that key. .PP If \fIkey\fR is not found then \fB\s-1NULL\s0\fR is returned, otherwise the data pointer associated with \fIkey\fR is returned. .SH "HISTORY" .IX Header "HISTORY" Converted to \s-1POD\s0 from Peter A.\ Friend's ternary search trie documentation by Alex Kiernan for InterNetNews\ 2.4.0. .PP \&\f(CW$Id:\fR tst.pod 9073 2010\-05\-31 19:00:23Z iulius $ inn-2.6.0/doc/man/inn.conf.50000644000175200017520000022572312575023702015051 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "INN.CONF 5" .TH INN.CONF 5 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" inn.conf \- Configuration data for InterNetNews programs .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\fIinn.conf\fR in \fIpathetc\fR is the primary general configuration file for all InterNetNews programs. Settings which control the general operation of various programs, as well as the paths to all portions of the news installation, are found here. The \s-1INNCONF\s0 environment variable, if set, specifies an alternate path to \fIinn.conf\fR. .PP This file is intended to be fairly static. Any changes made to it will generally not affect any running programs until they restart. Unlike nearly every other configuration file, \fIinn.conf\fR cannot be reloaded dynamically using \fIctlinnd\fR\|(8); \fIinnd\fR\|(8) must be stopped and restarted for relevant changes to \fIinn.conf\fR to take effect (\f(CW\*(C`ctlinnd xexec innd\*(C'\fR is the fastest way to do this.) .PP Blank lines and lines starting with a number sign (\f(CW\*(C`#\*(C'\fR) are ignored. All other lines specify parameters, and should be of the following form: .PP .Vb 1 \& : .Ve .PP (Any amount of whitespace can be put after the colon and is optional.) If the value contains embedded whitespace or any of the characters \f(CW\*(C`[]<\*(C'\fR\*(L"\e:>, it must be enclosed in double quotes (\*(R""). A backslash (\f(CW\*(C`\e\*(C'\fR) can be used to escape quotes and backslashes inside double quotes. is case-sensitive; \f(CW\*(C`server\*(C'\fR is not the same as \f(CW\*(C`Server\*(C'\fR or \f(CW\*(C`SERVER\*(C'\fR. (\fIinn.conf\fR parameters are generally all in lowercase.) .PP If occurs more than once in the file, the first value is used. Some parameters specified in the file may be overridden by environment variables. Most parameters have default values if not specified in \&\fIinn.conf\fR; those defaults are noted in the description of each parameter. .PP Many parameters take a boolean value. For all such parameters, the value may be specified as \f(CW\*(C`true\*(C'\fR, \f(CW\*(C`yes\*(C'\fR, or \f(CW\*(C`on\*(C'\fR to turn it on and may be any of \f(CW\*(C`false\*(C'\fR, \f(CW\*(C`no\*(C'\fR, or \f(CW\*(C`off\*(C'\fR to turn it off. The case of these values is significant. .PP This documentation is extremely long and organized as a reference manual rather than as a tutorial. If this is your first exposure to \s-1INN\s0 and these parameters, it would be better to start by reading other man pages and referring to this one only when an \fIinn.conf\fR parameter is explicitly mentioned. Those parameters which need to be changed when setting up a new server are discussed in \fI\s-1INSTALL\s0\fR. .SH "PARAMETERS" .IX Header "PARAMETERS" .SS "General Settings" .IX Subsection "General Settings" These parameters are used by a wide variety of different components of \&\s-1INN\s0. .IP "\fIdomain\fR" 4 .IX Item "domain" This should be the domain name of the local host. It should not have a leading period, and it should not be a full host address. It is used only if the \fIGetFQDN()\fR routine in \fIlibinn\fR\|(3) cannot get the fully-qualified domain name by using either the \fIgethostname\fR\|(3) or \fIgethostbyname\fR\|(3) calls. The check is very simple; if either routine returns a name with a period in it, then it is assumed to have the full domain name. As this parameter is rarely used, do not use it to affect the righthand side of autogenerated Message-IDs; see instead \fIvirtualhost\fR and \fIdomain\fR in \&\fIreaders.conf\fR\|(5). The default value is unset. .IP "\fIinnflags\fR" 4 .IX Item "innflags" The flags to pass to \fBinnd\fR on startup. See \fIinnd\fR\|(8) for details on the possible flags. The default value is unset. .Sp Note that these flags are only used when \fBinnd\fR is started from \fBrc.news\fR or \fBnntpsend\fR. .IP "\fImailcmd\fR" 4 .IX Item "mailcmd" The path to the program to be used for mailing reports and control messages. The default is \fIpathbin\fR/innmail. This should not normally need to be changed. .IP "\fImta\fR" 4 .IX Item "mta" The command to use when mailing postings to moderators and for the use of \&\fIinnmail\fR\|(1). The message, with headers and an added To: header, will be piped into this program. The string \f(CW%s\fR, if present, will be replaced by the e\-mail address of the moderator. It's strongly recommended for this command to include \f(CW%s\fR on the command line rather than use the addresses in the To: and Cc: headers of the message, since the latter approach allows the news server to be abused as a mechanism to send mail to arbitrary addresses and will result in unexpected behavior. There is no default value for this parameter; it must be set in \fIinn.conf\fR or a fatal error message will be logged via syslog. .Sp For most systems, \f(CW\*(C`/usr/lib/sendmail \-oi \-oem %s\*(C'\fR (adjusted for the correct path to \fBsendmail\fR, and between double quotes) is a good choice. .IP "\fIpathhost\fR" 4 .IX Item "pathhost" What to put into the Path: header to represent the local site. This is added to the Path: header of all articles that pass through the system, including locally posted articles, and is also used when processing some control messages and when naming the server in status reports. There is no default value; this parameter must be set in \fIinn.conf\fR or \s-1INN\s0 will not start. A good value to use is the fully-qualified hostname of the system. .IP "\fIrunasgroup\fR" 4 .IX Item "runasgroup" The group under which the news server will run. The default is \f(CW\*(C`news\*(C'\fR (or the group specified at configure time) and should not normally need to be changed. .IP "\fIrunasuser\fR" 4 .IX Item "runasuser" The user under which the news server will run. The default is \f(CW\*(C`news\*(C'\fR (or the user specified at configure time) and should not normally need to be changed. .IP "\fIserver\fR" 4 .IX Item "server" The name of the default \s-1NNTP\s0 server. If \fInnrpdposthost\fR is not set and \&\s-1UNIX\s0 domain sockets are not supported, \fInnrpd\fR\|(8) tries to hand off locally-posted articles through an \s-1INET\s0 domain socket to this server. \&\fIactsync\fR\|(8), \fInntpget\fR\|(8), and \fIgetlist\fR\|(8) also use this value as the default server to connect to. In the latter cases, the value of the \s-1NNTPSERVER\s0 environment variable, if it exists, overrides this. The default value is unset. .SS "Feed Configuration" .IX Subsection "Feed Configuration" These parameters govern incoming and outgoing feeds: what size of articles are accepted, what filtering and verification is performed on them, whether articles in groups not carried by the server are still stored and propagated, and other similar settings. .IP "\fIartcutoff\fR" 4 .IX Item "artcutoff" Articles older than this number of days are dropped. The default value is \f(CW10\fR, which means that an incoming article will be rejected if its posting date is farther in the past than ten days. .Sp In order to disable that check on date, you can set this parameter to \f(CW0\fR. .Sp The number on the \f(CW\*(C`/remember/\*(C'\fR line in \fIexpire.ctl\fR should probably be one more than that number in order to take into account articles whose posting date is one day into the future. .IP "\fIbindaddress\fR" 4 .IX Item "bindaddress" Which \s-1IP\s0 address \fIinnd\fR\|(8) should bind itself to. This must be in dotted-quad format (nnn.nnn.nnn.nnn). If set to \f(CW\*(C`all\*(C'\fR or not set, innd defaults to listening on all interfaces. The value of the \&\s-1INND_BIND_ADDRESS\s0 environment variable, if set, overrides this setting. The default value is unset. .IP "\fIbindaddress6\fR" 4 .IX Item "bindaddress6" Like \fIbindaddress\fR but for IPv6 sockets. If only one of the \fIbindaddress\fR and \fIbindaddress6\fR parameters is used, then only the socket for the corresponding address family is created. If both parameters are used then two sockets are created. If neither of them is used, the list of sockets to listen on will be determined by the system library \&\fI\fIgetaddrinfo\fI\|(3)\fR function. The value of the \s-1INND_BIND_ADDRESS6\s0, if set, overrides this setting. The default value is unset. .Sp Note that you will generally need to put double quotes ("") around this value if you set it, since IPv6 addresses contain colons. .IP "\fIdontrejectfiltered\fR" 4 .IX Item "dontrejectfiltered" Normally \fIinnd\fR\|(8) rejects incoming articles when directed to do so by any enabled article filters (Perl or Python). However, this parameter causes such articles \fInot\fR to be rejected; instead filtering can be applied on outbound articles. If this parameter is set, all articles will be accepted on the local machine, but articles rejected by the filter will \fInot\fR be fed to any peers specified in \fInewsfeeds\fR with the \f(CW\*(C`Af\*(C'\fR flag. The default value is false. .IP "\fIhiscachesize\fR" 4 .IX Item "hiscachesize" If set to a value other than \f(CW0\fR, a hash of recently received Message-IDs is kept in memory to speed history lookups. The value is the amount of memory to devote to the cache in kilobytes. The cache is only used for incoming feeds and a small cache can hold quite a few Message-IDs, so large values aren't necessarily useful unless you have incoming feeds that are badly delayed. \fBinnreport\fR can provide useful statistics regarding the use of the history cache, especially when it misses. A good value for a system with more than one incoming feed is \f(CW256\fR; systems with only one incoming feed should probably set this to \f(CW0\fR. The default value is \f(CW256\fR. .IP "\fIignorenewsgroups\fR" 4 .IX Item "ignorenewsgroups" Whether newsgroup creation control messages (newgroup and rmgroup) should be fed as if they were posted to the newsgroup they are creating or deleting rather than to the newsgroups listed in the Newsgroups: header. If this parameter is set, the newsgroup affected by the control message will be extracted from the Control: header and the article will be fed as if its Newsgroups: header contained solely that newsgroup. This is useful for routing control messages to peers when they are posted to irrelevant newsgroups that shouldn't be matched against the peer's desired newsgroups in \fInewsfeeds\fR. This is a boolean value and the default is false. .IP "\fIimmediatecancel\fR" 4 .IX Item "immediatecancel" When using the timecaf storage method, article cancels are normally just cached to be cancelled, not cancelled immediately. If this is set to true, they will instead by cancelled as soon as the cancel is processed. This is a boolean value and the default is false. .Sp This setting is ignored unless the timecaf storage method is used. .IP "\fIlinecountfuzz\fR" 4 .IX Item "linecountfuzz" If set to something other than \f(CW0\fR, the line count of the article is checked against the Lines: header of the article (if present) and the article is rejected if the values differ by more than this amount. A reasonable setting is \f(CW5\fR, which is the standard maximum signature length plus one (some injection software calculates the Lines: header before adding the signature). The default value is \f(CW0\fR, which tells \s-1INN\s0 not to check the Lines: header of incoming articles. .IP "\fImaxartsize\fR" 4 .IX Item "maxartsize" The maximum size of article (headers and body) that will be accepted by the server, in bytes. A value of \f(CW0\fR allows any size of article, but note that \fBinnd\fR will crash if system memory is exceeded. The default value is \f(CW1000000\fR (approximately 1\ \s-1MB\s0). This is checked against the article in wire format (\s-1CRLF\s0 at the end of each line, leading periods protected, and with the trailing \*(L"\er\en.\er\en\*(R" at the end). See also \&\fIlocalmaxartsize\fR. .IP "\fImaxconnections\fR" 4 .IX Item "maxconnections" The maximum number of incoming \s-1NNTP\s0 connections \fIinnd\fR\|(8) will accept. The default value is \f(CW50\fR. .IP "\fIpathalias\fR" 4 .IX Item "pathalias" If set, this value is prepended to the Path: header of accepted posts (before \fIpathhost\fR) if it doesn't already appear in the Path: header. The main purpose of this parameter is to configure all news servers within a particular organization to add a common identity string to the Path: header. The default value is unset. .IP "\fIpathcluster\fR" 4 .IX Item "pathcluster" If set, this value is appended to the Path: header of accepted posts (after \fIpathhost\fR) if it isn't already present as the last element of the Path: header. The main purpose of this parameter is to make several news servers appear as one server. The default value is unset. .Sp Note that the Path: header reads right to left, so appended means inserted at the leftmost side of the Path: header. .IP "\fIpgpverify\fR" 4 .IX Item "pgpverify" Whether to enable \s-1PGP\s0 verification of control messages other than cancel. This is a boolean value and the default in the \fIinn.conf\fR sample file is based on whether configure found pgp, pgpv, pgpgpg, gpgv or gpgv2. Note that if the parameter is not present in the configuration file, it defaults to false. .IP "\fIport\fR" 4 .IX Item "port" What \s-1TCP\s0 port \fIinnd\fR\|(8) should listen on. The default value is \f(CW119\fR, the standard \s-1NNTP\s0 port. .IP "\fIrefusecybercancels\fR" 4 .IX Item "refusecybercancels" Whether to refuse all articles whose message IDs start with \&\f(CW\*(C`\*(C'\fR, \f(CW\*(C`|\*(C'\fR, or \f(CW\*(C`:\*(C'\fR. This is a boolean value and the default is false. .IP "\fIcomplaints\fR" 4 .IX Item "complaints" The value of the mail-complaints-to attribute of the Injection-Info: header added to all local posts. The default is the newsmaster's e\-mail address. (If the newsmaster, selected at configure time and defaulting to \f(CW\*(C`usenet\*(C'\fR, doesn't contain \f(CW\*(C`@\*(C'\fR, the address will consist of the newsmaster, a \f(CW\*(C`@\*(C'\fR, and the value of \fIfromhost\fR.) .IP "\fIfromhost\fR" 4 .IX Item "fromhost" Contains a domain used to construct e\-mail addresses. The address of the local news administrator will be given as @\fIfromhost\fR, where is the newsmaster user set at compile time (\f(CW\*(C`usenet\*(C'\fR by default). This setting will also be used by \fImailpost\fR\|(8) to fully qualify addresses and by \&\fIinews\fR\|(1) to generate the Sender: header (and From: header if missing). The value of the \s-1FROMHOST\s0 environment variable, if set, overrides this setting. The default is the fully-qualified domain name of the local host. .IP "\fIlocalmaxartsize\fR" 4 .IX Item "localmaxartsize" The maximum article size (in bytes) for locally posted articles. Articles larger than this will be rejected. A value of \f(CW0\fR allows any size of article, but note that \fBnnrpd\fR and \fBinnd\fR will crash if system memory is exceeded. See also \fImaxartsize\fR, which applies to all articles including those posted locally. The default value is \f(CW1000000\fR (approximately 1\ \s-1MB\s0). .IP "\fImoderatormailer\fR" 4 .IX Item "moderatormailer" The address to which to send submissions for moderated groups. It is only used if the \fImoderators\fR file doesn't exist, or if the moderated group to which an article is posted is not matched by any entry in that file, and takes the same form as an entry in the \fImoderators\fR file. In most cases, \&\f(CW\*(C`%s@moderators.isc.org\*(C'\fR is a good value for this parameter (\f(CW%s\fR is expanded into a form of the newsgroup name). See \fImoderators\fR\|(5) for more details about the syntax. The default is unset. If this parameter isn't set and an article is posted to a moderated group that does not have a matching entry in the \fImoderators\fR file, the posting will be rejected with an error. .IP "\fInnrpdauthsender\fR" 4 .IX Item "nnrpdauthsender" Whether to generate a Sender: header based on reader authentication. If this parameter is set, a Sender: header will be added to local posts containing the identity assigned by \fIreaders.conf\fR. If the assigned identity does not include an \f(CW\*(C`@\*(C'\fR, the reader's hostname is used. If this parameter is set but no identity is assigned, the Sender: header will be removed from all posts even if the poster includes one. This is a boolean value and the default is false. .IP "\fInnrpdposthost\fR" 4 .IX Item "nnrpdposthost" If set, \fInnrpd\fR\|(8) and \fIrnews\fR\|(1) will pass all locally posted articles to the specified host rather than trying to inject them locally. See also \&\fInnrpdpostport\fR. This should always be set if \fIxrefslave\fR is true. The default value is unset. .IP "\fInnrpdpostport\fR" 4 .IX Item "nnrpdpostport" The port on the remote server to connect to to post when \fInnrpdposthost\fR is used. The default value is \f(CW119\fR. .IP "\fIorganization\fR" 4 .IX Item "organization" What to put in the Organization: header if it is left blank by the poster. The value of the \s-1ORGANIZATION\s0 environment variable, if set, overrides this setting. The default is unset, which tells \s-1INN\s0 not to insert an Organization: header. .IP "\fIspoolfirst\fR" 4 .IX Item "spoolfirst" If true, \fInnrpd\fR\|(8) will spool new articles rather than attempting to send them to \fIinnd\fR\|(8). If false, nnrpd will spool articles only if it receives an error trying to send them to innd. Setting this to true can be useful if nnrpd must respond as fast as possible to the client; however, when set, articles will not appear to readers until they are given to innd. nnrpd won't do this; \f(CW\*(C`rnews \-U\*(C'\fR must be run periodically to take the spooled articles and post them. This is a boolean value and the default is false. .IP "\fIstrippostcc\fR" 4 .IX Item "strippostcc" Whether to strip To:, Cc:, and Bcc: headers out of all local posts via \&\fInnrpd\fR\|(8). The primary purpose of this setting is to prevent abuse of the news server by posting to a moderated group and including To: or Cc: headers in the post so that the news server will send the article to arbitrary addresses. \s-1INN\s0 now protects against this abuse in other ways provided \fImta\fR is set to a command that includes \f(CW%s\fR and honors it, so this is generally no longer needed. This is a boolean value and the default is false. .PP \&\fInnrpd\fR\|(8) has support for controlling high-volume posters via an exponential backoff algorithm, as configured by the following parameters. .PP Exponential posting backoff works as follows: news clients are indexed by \s-1IP\s0 address (or username, see \fIbackoffauth\fR below). Each time a post is received from an \s-1IP\s0 address, the time of posting is stored (along with the previous sleep time, see below). After a configurable number of posts in a configurable period of time, \fInnrpd\fR\|(8) will begin to sleep for increasing periods of time before actually posting anything (posting backoff is therefore activated). Posts will still be accepted, but at an increasingly reduced rate. .PP After backoff has been activated, the length of time to sleep is computed based on the difference in time between the last posting and the current posting. If this difference is less than \fIbackoffpostfast\fR, the new sleep time will be 1 + (previous sleep time * \fIbackoffk\fR). If this difference is less than \fIbackoffpostslow\fR but greater than \&\fIbackoffpostfast\fR, then the new sleep time will equal the previous sleep time. If this difference is greater than \fIbackoffpostslow\fR, the new sleep time is zero and posting backoff is deactivated for this poster. (Note that this does not mean posting backoff cannot be reactivated later in the session.) .PP Exponential posting backoff will not be enabled unless \fIbackoffdb\fR is set and \fIbackoffpostfast\fR and \fIbackoffpostslow\fR are set to something other than their default values. .PP Here are the parameters that control exponential posting backoff: .IP "\fIbackoffauth\fR" 4 .IX Item "backoffauth" Whether to index posting backoffs by user rather than by source \s-1IP\s0 address. You must be using authentication in \fInnrpd\fR\|(8) for a value of true to have any meaning. This is a boolean value and the default is false. .IP "\fIbackoffdb\fR" 4 .IX Item "backoffdb" The path to a directory, writeable by the news user, that will contain the backoff database. There is no default for this parameter; you must provide a path to a creatable or writeable directory to enable exponential backoff. .IP "\fIbackoffk\fR" 4 .IX Item "backoffk" The amount to multiply the previous sleep time by if the user is still posting too quickly. A value of \f(CW2\fR will double the sleep time for each excessive post. The default value is \f(CW1\fR. .IP "\fIbackoffpostfast\fR" 4 .IX Item "backoffpostfast" Postings from the same identity that arrive in less than this amount of time (in seconds) will trigger increasing sleep time in the backoff algorithm. The default value is \f(CW0\fR. .IP "\fIbackoffpostslow\fR" 4 .IX Item "backoffpostslow" Postings from the same identity that arrive in greater than this amount of time (in seconds) will reset the backoff algorithm. Another way to look at this constant is to realize that posters will be allowed to generate at most 86400/\fIbackoffpostslow\fR posts per day. The default value is \f(CW1\fR. .IP "\fIbackofftrigger\fR" 4 .IX Item "backofftrigger" This many postings are allowed before the backoff algorithm is triggered. The default value is \f(CW10000\fR. .SS "\s-1TLS/SSL\s0 Support for Reading and Posting" .IX Subsection "TLS/SSL Support for Reading and Posting" Here are the parameters used by \fInnrpd\fR\|(8) to provide \s-1TLS/SSL\s0 support. .PP The parameters related to certificates are: .IP "\fItlscafile\fR" 4 .IX Item "tlscafile" The path to a file containing certificate authority root certificates, used to present a trust chain to a \s-1TLS\s0 client. This parameter is only used if \fBnnrpd\fR is built with \s-1TLS/SSL\s0 support. The default value is an empty string. .IP "\fItlscapath\fR" 4 .IX Item "tlscapath" The path to a directory containing certificate authority root certificates. Each file in the directory should contain one \s-1CA\s0 certificate, and the name of the file should be the \s-1CA\s0 subject name hash value. See the OpenSSL documentation for more information. This parameter is only used if \fBnnrpd\fR is built with \s-1TLS/SSL\s0 support. The default value is \fIpathetc\fR. .IP "\fItlscertfile\fR" 4 .IX Item "tlscertfile" The path to a file containing the server certificate to present to \&\s-1TLS\s0 clients. This parameter is only used if \fBnnrpd\fR is built with \s-1TLS/SSL\s0 support. The default value is \fIpathetc\fR/cert.pem. .IP "\fItlskeyfile\fR" 4 .IX Item "tlskeyfile" The path to a file containing the encryption key for the server certificate named in \fItlscertfile\fR. This may be the same as \&\fItlscertfile\fR if, when you created the certificate, you put the key in the same file (if, for example, you gave the same file name to both the \&\fB\-out\fR and \fB\-keyout\fR options to \f(CW\*(C`openssl req\*(C'\fR). This parameter is only used if \fBnnrpd\fR is built with \s-1TLS/SSL\s0 support. The default value is \&\fIpathetc\fR/key.pem. .Sp This file must only be readable by the news user or \fBnnrpd\fR will refuse to use it. .PP Finally, here are the parameters that can be used to tighten the level of security provided by \s-1TLS/SSL\s0 in case new attacks exploitable in \s-1NNTP\s0 on the \s-1TLS\s0 protocol or some supported cipher suite are discovered: .IP "\fItlsciphers\fR" 4 .IX Item "tlsciphers" The string describing the cipher suites OpenSSL will support. See OpenSSL's \fIciphers\fR\|(1) command documentation for details. The default is unset, which uses OpenSSL's default cipher suite list. .Sp Formally, keeping the \s-1TLS_RSA_WITH_RC4_128_MD5\s0 and \&\s-1TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA\s0 cipher suites is important, as it assures that any two compliant implementations can be configured to interoperate (see \s-1RFC\s0 4642 for more details). .IP "\fItlscompression\fR" 4 .IX Item "tlscompression" Whether to enable or disable \s-1SSL/TLS\s0 compression support. This is a boolean and the default is true, that is to say compression is enabled. .IP "\fItlseccurve\fR" 4 .IX Item "tlseccurve" The name of the elliptic curve to use for ephemeral key exchanges. To see the list of curves supported by OpenSSL, use \f(CW\*(C`openssl ecparam \&\-list_curves\*(C'\fR. .Sp The default is unset, which means an appropriate curve is auto-selected (if your OpenSSL version supports it) or the \s-1NIST\s0 P\-256 curve is used. .Sp This option is only effective if your OpenSSL version has \s-1ECDH\s0 support. .IP "\fItlspreferserverciphers\fR" 4 .IX Item "tlspreferserverciphers" Whether to let the client or the server decide the preferred cipher. This is a boolean and the default is true, that is to say the server decides the preferred cipher. .IP "\fItlsprotocols\fR" 4 .IX Item "tlsprotocols" The list of \s-1SSL/TLS\s0 protocol versions to support. Valid protocols are \&\fBSSLv2\fR, \fBSSLv3\fR, \fBTLSv1\fR, \fBTLSv1.1\fR and \fBTLSv1.2\fR. The default value is to only allow \s-1TLS\s0 protocols: .Sp .Vb 1 \& tlsprotocols: [ TLSv1 TLSv1.1 TLSv1.2 ] .Ve .Sp Note that the listed protocols will be enabled only if the OpenSSL library \s-1INN\s0 has been built with, supports them. In case OpenSSL supports protocols more recent than TLSv1.2, they will be automatically enabled (which anyway is fine regarding security, as newer protocols are supposed to be more secure). .SS "Monitoring" .IX Subsection "Monitoring" These parameters control the behavior of \fIinnwatch\fR\|(8), the program that monitors \s-1INN\s0 and informs the news administrator if anything goes wrong with it. .IP "\fIdoinnwatch\fR" 4 .IX Item "doinnwatch" Whether to start \fIinnwatch\fR\|(8) from rc.news. This is a boolean value, and the default is true. .IP "\fIinnwatchbatchspace\fR" 4 .IX Item "innwatchbatchspace" Free space in \fIpathoutgoing\fR, in \fIinndf\fR\|(8) output units (normally kilobytes), at which \fIinnd\fR\|(8) will be throttled by \fIinnwatch\fR\|(8), assuming a default \fIinnwatch.ctl\fR. The default value is \f(CW4000\fR. .IP "\fIinnwatchlibspace\fR" 4 .IX Item "innwatchlibspace" Free space in \fIpathdb\fR, in \fIinndf\fR\|(8) output units (normally kilobytes), at which \fIinnd\fR\|(8) will be throttled by \fIinnwatch\fR\|(8), assuming a default \&\fIinnwatch.ctl\fR. The default value is \f(CW25000\fR. .IP "\fIinnwatchloload\fR" 4 .IX Item "innwatchloload" Load average times 100 at which \fIinnd\fR\|(8) will be restarted by \fIinnwatch\fR\|(8) (undoing a previous pause or throttle), assuming a default \&\fIinnwatch.ctl\fR. The default value is \f(CW1000\fR (that is, a load average of 10.00). .IP "\fIinnwatchhiload\fR" 4 .IX Item "innwatchhiload" Load average times 100 at which \fIinnd\fR\|(8) will be throttled by \fIinnwatch\fR\|(8), assuming a default \fIinnwatch.ctl\fR. The default value is \f(CW2000\fR (that is, a load average of 20.00). .IP "\fIinnwatchpauseload\fR" 4 .IX Item "innwatchpauseload" Load average times 100 at which \fIinnd\fR\|(8) will be paused by \fIinnwatch\fR\|(8), assuming a default \fIinnwatch.ctl\fR. The default value is \f(CW1500\fR (that is, a load average of 15.00). .IP "\fIinnwatchsleeptime\fR" 4 .IX Item "innwatchsleeptime" How long (in seconds) \fIinnwatch\fR\|(8) will sleep between each check of \s-1INN\s0. The default value is \f(CW600\fR. .IP "\fIinnwatchspoolnodes\fR" 4 .IX Item "innwatchspoolnodes" Free inodes in \fIpatharticles\fR at which \fIinnd\fR\|(8) will be throttled by \&\fIinnwatch\fR\|(8), assuming a default \fIinnwatch.ctl\fR. The default value is \&\f(CW200\fR. .IP "\fIinnwatchspoolspace\fR" 4 .IX Item "innwatchspoolspace" Free space in \fIpatharticles\fR and \fIpathoverview\fR, in \fIinndf\fR\|(8) output units (normally kilobytes), at which \fIinnd\fR\|(8) will be throttled by \&\fIinnwatch\fR\|(8), assuming a default \fIinnwatch.ctl\fR. The default value is \&\f(CW25000\fR. .SS "Logging" .IX Subsection "Logging" These parameters control what information \s-1INN\s0 logs. .IP "\fIdocnfsstat\fR" 4 .IX Item "docnfsstat" Whether to start \fIcnfsstat\fR\|(8) when \fIinnd\fR\|(8) is started. cnfsstat will log the status of all \s-1CNFS\s0 cycbuffs to syslog on a periodic basis (frequency is the default for \f(CW\*(C`cnfsstat \-l\*(C'\fR, currently 600 seconds). This is a boolean value and the default is false. .IP "\fIhtmlstatus\fR" 4 .IX Item "htmlstatus" Whether \fBinnd\fR should write the status report as \s-1HTML\s0 file or in plain text. The \s-1HTML\s0 status file goes to \fIpathhttp\fR/inn_status.html, while the plain text status file is written to \fIpathlog\fR/inn.status. This is a boolean value and the default is true (an \s-1HTML\s0 status file is written). Also see the \fIstatus\fR parameter. .IP "\fIincominglogfrequency\fR" 4 .IX Item "incominglogfrequency" How many articles to process on an incoming channel before logging the activity. The default value is \f(CW200\fR. .IP "\fIlogartsize\fR" 4 .IX Item "logartsize" Whether the size of accepted articles (in bytes) should be written to the article log file. This is useful for flow rate statistics and is recommended. This is a boolean value and the default is true. .IP "\fIlogcancelcomm\fR" 4 .IX Item "logcancelcomm" Set this to true to log \f(CW\*(C`ctlinnd cancel\*(C'\fR commands to syslog. This is a boolean value and the default is false. .IP "\fIlogcycles\fR" 4 .IX Item "logcycles" How many old logs \fIscanlogs\fR\|(8) keeps. \fIscanlogs\fR\|(8) is generally run by \&\fInews.daily\fR\|(8) and will archive compressed copies of this many days worth of old logs. The default value is \f(CW3\fR. .IP "\fIlogipaddr\fR" 4 .IX Item "logipaddr" Whether the verified name of the remote feeding host should be logged to the article log for incoming articles rather than the last entry in the Path: header. The only reason to ever set this to false is due to some interactions with \fInewsfeeds\fR flags; see \fInewsfeeds\fR\|(5) for more information. This is a boolean value and the default is true. .IP "\fIlogsitename\fR" 4 .IX Item "logsitename" Whether the names of the sites to which accepted articles will be sent should be put into the article log file. This is useful for debugging and statistics. This is a boolean value and the default is true. .IP "\fIlogstatus\fR" 4 .IX Item "logstatus" Whether \fBinnd\fR should write a shortened version of its status report to syslog every \fIstatus\fR seconds. This is a boolean value and the default is true. If set to true, see the \fIstatus\fR parameter for more details on how to enable status reporting. .IP "\fIlogtrash\fR" 4 .IX Item "logtrash" Whether \fBinnd\fR should add a line in the \fInews\fR log file to report unwanted newsgroups (that is to say newsgroups not locally carried by the news server). This is a boolean value and the default is true. It may be useful to set it to false when \fIwanttrash\fR is set to true. .IP "\fInnrpdoverstats\fR" 4 .IX Item "nnrpdoverstats" Whether nnrpd overview statistics should be logged via syslog. This can be useful for measuring overview performance. This is a boolean value and the default is true. .IP "\fInntplinklog\fR" 4 .IX Item "nntplinklog" Whether to put the storage \s-1API\s0 token for accepted articles (used by nntplink) in the article log. This is a boolean value and the default is false. .IP "\fIstathist\fR" 4 .IX Item "stathist" Where to write history statistics for analysis with \&\fIcontrib/stathist.pl\fR; this can be modified with \fIctlinnd\fR\|(8) while innd is running. Logging does not occur unless a path is given, and there is no default value. .IP "\fIstatus\fR" 4 .IX Item "status" How frequently (in seconds) \fIinnd\fR\|(8) should write out a status report. The report is written to \fIpathhttp\fR/inn_status.html or \&\fIpathlog\fR/inn.status depending on the value of \fIhtmlstatus\fR. If this is set to \f(CW0\fR or \f(CW\*(C`false\*(C'\fR, status reporting is disabled. The default value is \f(CW600\fR (that is to say reports are written every 10 minutes). .IP "\fItimer\fR" 4 .IX Item "timer" How frequently (in seconds) \fIinnd\fR\|(8) should report performance timings to syslog. If this is set to \f(CW0\fR, performance timing is disabled. Enabling this is highly recommended, and \fIinnreport\fR\|(8) can produce a nice summary of the timings. If set to \f(CW0\fR, performance timings in \fInnrpd\fR\|(8) are also disabled, although \fBnnrpd\fR always reports statistics on exit and therefore any non-zero value is equivalent for it. The default value is \f(CW600\fR (that is to say performance timings are reported every 10 minutes). .SS "System Tuning" .IX Subsection "System Tuning" The following parameters can be modified to tune the low-level operation of \s-1INN\s0. In general, you shouldn't need to modify any of them except possibly \fIrlimitnofile\fR unless the server is having difficulty. .IP "\fIbadiocount\fR" 4 .IX Item "badiocount" How many read or write failures until a channel is put to sleep or closed. The default value is \f(CW5\fR. .IP "\fIblockbackoff\fR" 4 .IX Item "blockbackoff" Each time an attempted write returns \s-1EAGAIN\s0 or \s-1EWOULDBLOCK\s0, \fIinnd\fR\|(8) will wait for an increasing number of seconds before trying it again. This is the multiplier for the sleep time. If you're having trouble with channel feeds not keeping up, it may be good to change this value to \f(CW2\fR or \f(CW3\fR, since then when the channel fills \s-1INN\s0 will try again in a couple of seconds rather than waiting two minutes. The default value is \f(CW120\fR. .IP "\fIchaninacttime\fR" 4 .IX Item "chaninacttime" The time (in seconds) to wait between noticing inactive channels. The default value is \f(CW600\fR. .IP "\fIchanretrytime\fR" 4 .IX Item "chanretrytime" How many seconds to wait before a channel restarts. The default value is \&\f(CW300\fR. .IP "\fIdatamovethreshold\fR" 4 .IX Item "datamovethreshold" The threshold for deciding whether to move already-read data to the top of buffer or extend the buffer. The buffer described here is used for reading \&\s-1NNTP\s0 data. Increasing this value may improve performance, but it should not be increased on Systems with insufficient memory. Permitted values are between \f(CW0\fR and \f(CW1048576\fR (out of range values are treated as \&\f(CW1048576\fR) and the default value is \f(CW16384\fR. .IP "\fIicdsynccount\fR" 4 .IX Item "icdsynccount" How many article writes between updating the active and history files. The default value is \f(CW10\fR. .IP "\fIkeepmmappedthreshold\fR" 4 .IX Item "keepmmappedthreshold" When using buffindexed, retrieving overview data (that is, responding to \&\s-1OVER\s0 or running expireover) causes mmapping of all overview data blocks which include requested overview data for newsgroup. But for high volume newsgroups like control.cancel, this may cause too much mmapping at once leading to system resource problems. To avoid this, if the amount to be mmapped exceeds \fIkeepmmappedthreshold\fR (in \s-1KB\s0), buffindexed mmap's just one overview block (8\ \s-1KB\s0). This parameter is specific to buffindexed overview storage method. The default value is \f(CW1024\fR (1\ \s-1MB\s0). .IP "\fImaxcmdreadsize\fR" 4 .IX Item "maxcmdreadsize" If set to anything other than \f(CW0\fR, maximum buffer size (in bytes) for reading \s-1NNTP\s0 command will have this value. It should not be large on systems which are slow to process and store articles, as that would lead to \fIinnd\fR\|(8) spending a long time on each channel and keeping other channels waiting. The default value is \s-1BUFSIZ\s0 defined in stdio.h (\f(CW1024\fR in most environments, see \fIsetbuf\fR\|(3)). .IP "\fImaxforks\fR" 4 .IX Item "maxforks" How many times to attempt a \fIfork\fR\|(2) before giving up. The default value is \f(CW10\fR. .IP "\fInicekids\fR" 4 .IX Item "nicekids" If set to anything other than \f(CW0\fR, all child processes of \fIinnd\fR\|(8) will have this \fInice\fR\|(2) value. This is usually used to give all child processes of \fIinnd\fR\|(8) a lower priority (higher nice value) so that \fIinnd\fR\|(8) can get the lion's share of the \s-1CPU\s0 when it needs it. The default value is \f(CW4\fR. .IP "\fInicenewnews\fR" 4 .IX Item "nicenewnews" If set to anything greater than \f(CW0\fR, all \fInnrpd\fR\|(8) processes that receive and process a \s-1NEWNEWS\s0 command will \fInice\fR\|(2) themselves to this value (giving other nnrpd processes a higher priority). The default value is \&\f(CW0\fR. Note that this value will be ignored if set to a lower value than \&\fInicennrpd\fR (or \fInicekids\fR if \fInnrpd\fR\|(8) is spawned from \fIinnd\fR\|(8)). .IP "\fInicennrpd\fR" 4 .IX Item "nicennrpd" If set to anything greater than \f(CW0\fR, all \fInnrpd\fR\|(8) processes will \fInice\fR\|(1) themselves to this value. This gives other news processes a higher priority and can help \fIoverchan\fR\|(8) keep up with incoming news (if that's the object, be sure \fIoverchan\fR\|(8) isn't also set to a lower priority via \&\fInicekids\fR). The default value is \f(CW0\fR, which will cause \fInnrpd\fR\|(8) processes spawned from \fIinnd\fR\|(8) to use the value of \fInicekids\fR, while \&\fInnrpd\fR\|(8) run as a daemon will use the system default priority. Note that for \fInnrpd\fR\|(8) processes spawned from \fIinnd\fR\|(8), this value will be ignored if set to a value lower than \fInicekids\fR. .IP "\fIpauseretrytime\fR" 4 .IX Item "pauseretrytime" Wait for this many seconds before noticing inactive channels. Wait for this many seconds before innd processes articles when it's paused or the number of channel write failures exceeds \fIbadiocount\fR. The default value is \f(CW300\fR. .IP "\fIpeertimeout\fR" 4 .IX Item "peertimeout" How long (in seconds) an \fIinnd\fR\|(8) incoming channel may be inactive before innd closes it. The default value is \f(CW3600\fR (an hour). .IP "\fIrlimitnofile\fR" 4 .IX Item "rlimitnofile" The maximum number of file descriptors that \fIinnd\fR\|(8) or \fIinnfeed\fR\|(8) can have open at once. If \fIinnd\fR\|(8) or \fIinnfeed\fR\|(8) attempts to open more file descriptors than this value, it is possible the program may throttle or otherwise suffer reduced functionality. The number of open file descriptors is roughly the maximum number of incoming feeds and outgoing batches for \fIinnd\fR\|(8) and the number of outgoing streams for \fIinnfeed\fR\|(8). If this parameter is set to a negative value, the default limit of the operating system will be used; this will normally be adequate on systems other than Solaris. Nearly all operating systems have some hard maximum limit beyond which this value cannot be raised, usually either 128, 256, or 1024. The default value of this parameter is \f(CW\*(C`\-1\*(C'\fR. Setting it to \&\f(CW256\fR on Solaris systems is highly recommended. .SS "Paths Names" .IX Subsection "Paths Names" .IP "\fIpatharchive\fR" 4 .IX Item "patharchive" Where to store archived news. The default value is \fIpathspool\fR/archive. .IP "\fIpatharticles\fR" 4 .IX Item "patharticles" The path to where the news articles are stored (for storage methods other than \s-1CNFS\s0). The default value is \fIpathspool\fR/articles. .IP "\fIpathbin\fR" 4 .IX Item "pathbin" The path to the news binaries. The default value is \fIpathnews\fR/bin. .IP "\fIpathcontrol\fR" 4 .IX Item "pathcontrol" The path to the files that handle control messages. The code for handling each separate type of control message is located here. Be very careful what you put in this directory with a name ending in \f(CW\*(C`.pl\*(C'\fR, as it can potentially be a severe security risk. The default value is \&\fIpathbin\fR/control. .IP "\fIpathdb\fR" 4 .IX Item "pathdb" The path to the database files used and updated by the server (currently, \&\fIactive\fR, \fIactive.times\fR, \fIhistory\fR and its indices, and \&\fInewsgroups\fR). The default value is \fIpathnews\fR/db. .IP "\fIpathetc\fR" 4 .IX Item "pathetc" The path to the news configuration files. The default value is \&\fIpathnews\fR/etc. .IP "\fIpathfilter\fR" 4 .IX Item "pathfilter" The path to the Perl and Python filters. The default value is \&\fIpathbin\fR/filter. .IP "\fIpathhttp\fR" 4 .IX Item "pathhttp" Where any \s-1HTML\s0 files (such as periodic status reports) are placed. If the news reports should be available in real-time on the web, the files in this directory should be served by a web server. The default value is the value of \fIpathnews\fR/http. .IP "\fIpathincoming\fR" 4 .IX Item "pathincoming" Location where incoming batched news is stored. The default value is \&\fIpathspool\fR/incoming. .IP "\fIpathlog\fR" 4 .IX Item "pathlog" Where the news log files are written. The default value is \&\fIpathnews\fR/log. .IP "\fIpathnews\fR" 4 .IX Item "pathnews" The home directory of the news user and usually the root of the news hierarchy. There is no default; this parameter must be set in \fIinn.conf\fR or \s-1INN\s0 will refuse to start. .IP "\fIpathoutgoing\fR" 4 .IX Item "pathoutgoing" Default location for outgoing feed files. The default value is \&\fIpathspool\fR/outgoing. .IP "\fIpathoverview\fR" 4 .IX Item "pathoverview" The path to news overview files. The default value is \&\fIpathspool\fR/overview. .IP "\fIpathrun\fR" 4 .IX Item "pathrun" The path to files required while the server is running and run-time state information. This includes lock files and the sockets for communicating with \fIinnd\fR\|(8). This directory and the control sockets in it should be protected from unprivileged users other than the news user. The default value is \fIpathnews\fR/run. .IP "\fIpathspool\fR" 4 .IX Item "pathspool" The root of the news spool hierarchy. This used mostly to set the defaults for other parameters, and to determine the path to the backlog directory for \fIinnfeed\fR\|(8). The default value is \fIpathnews\fR/spool. .IP "\fIpathtmp\fR" 4 .IX Item "pathtmp" Where \s-1INN\s0 puts temporary files. For security reasons, this is not the same as the system temporary files directory (\s-1INN\s0 creates a lot of temporary files with predictable names and does not go to particularly great lengths to protect against symlink attacks and the like; this is safe provided that normal users can't write into its temporary directory). The default value is set at configure time and defaults to \&\fIpathnews\fR/tmp. .SH "EXAMPLE" .IX Header "EXAMPLE" Here is a very minimalist example that only sets those parameters that are required. .PP .Vb 5 \& mta: "/usr/lib/sendmail \-oi \-oem %s" \& ovmethod: tradindexed \& pathhost: news.example.com \& pathnews: /usr/local/news \& hismethod: hisv6 .Ve .PP For a more comprehensive example, see the sample \fIinn.conf\fR distributed with \s-1INN\s0 and installed as a starting point; it contains all of the default values for reference. .SH "HISTORY" .IX Header "HISTORY" Written by Rich \f(CW$alz\fR for InterNetNews and since modified, updated, and reorganized by innumerable other people. .PP \&\f(CW$Id:\fR inn.conf.pod 9922 2015\-07\-14 16:43:55Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIinews\fR\|(1), \fIinnd\fR\|(8), \fIinnwatch\fR\|(8), \fImakehistory\fR\|(8), \fInnrpd\fR\|(8), \fIrnews\fR\|(1). .PP Nearly every program in \s-1INN\s0 uses this file to one degree or another. The above are just the major and most frequently mentioned ones. inn-2.6.0/doc/man/getlist.10000644000175200017520000002147012575023702015001 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "GETLIST 1" .TH GETLIST 1 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" getlist \- Get a list from an NNTP server .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fBgetlist\fR [\fB\-AR\fR] [\fB\-h\fR \fIhost\fR] [\fB\-p\fR \fIport\fR] [\fIlist\fR [\fIpattern\fR [\fItypes\fR]]] .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\fBgetlist\fR obtains a list from an \s-1NNTP\s0 server and sends it to standard output. \fIlist\fR may be one of \f(CW\*(C`active\*(C'\fR (which is the default value), \&\f(CW\*(C`active.times\*(C'\fR, \f(CW\*(C`distrib.pats\*(C'\fR, or \f(CW\*(C`newsgroups\*(C'\fR. \f(CW\*(C`active\*(C'\fR contains a list of all newsgroups carried by the server with the high and low article numbers in the group, and the group status field. \f(CW\*(C`active.times\*(C'\fR is a list of newsgroups, their creation times (in seconds since epoch), and the creator of the group. \f(CW\*(C`distrib.pats\*(C'\fR is a list of relevant distributions and the newsgroups they apply to. \f(CW\*(C`newsgroups\*(C'\fR is a list of newsgroups along with their short descriptions. .PP For more information on the formats of these files, see \fIactive\fR\|(5), \&\fIactive.times\fR\|(5), \fIdistrib.pats\fR\|(5) and \fInewsgroups\fR\|(5). .PP The listing files other than the \fIactive\fR file are common extensions to the \&\s-1NNTP\s0 protocol and may not be available on all servers. For instance, \f(CW\*(C`counts\*(C'\fR \&\f(CW\*(C`distributions\*(C'\fR, \f(CW\*(C`headers\*(C'\fR, \f(CW\*(C`moderators\*(C'\fR, \f(CW\*(C`motd\*(C'\fR, \f(CW\*(C`overview.fmt\*(C'\fR and \&\f(CW\*(C`subscriptions\*(C'\fR, amongst other, may be available. Moreover, a \fIuwildmat\fR\|(3) pattern \fIpattern\fR may also be usable for some of these listing files. .PP For more information on the formats of these files, see \fIdistributions\fR\|(5), \&\fImoderators\fR\|(5), \fImotd.news\fR\|(5) and \fIsubscriptions\fR\|(5). The overview fields obtained with \f(CW\*(C`overview.fmt\*(C'\fR are the ones for which the overview database is consistent (see \fIextraoverviewadvertised\fR in \fIinn.conf\fR for more information). The list obtained with \f(CW\*(C`headers\*(C'\fR contains the fields that can be retrieved using the \s-1HDR\s0 command (\fIpattern\fR can then be either \f(CW\*(C`msgid\*(C'\fR or \f(CW\*(C`range\*(C'\fR). The list obtained with \f(CW\*(C`counts\*(C'\fR is like the \f(CW\*(C`active\*(C'\fR one except that the number of articles in a newsgroup is mentioned just before the flag of this newsgroup. .PP The \fIpattern\fR parameter may be used with a \fIlist\fR value of \f(CW\*(C`active\*(C'\fR, \&\f(CW\*(C`active.times\*(C'\fR or \f(CW\*(C`newsgroups\*(C'\fR to limit the output. If given, only entries corresponding to newsgroups that match the \fIuwildmat\fR\|(3) pattern \&\fIpattern\fR will be printed. .PP If the \fIlist\fR parameter is \f(CW\*(C`active\*(C'\fR, a third parameter, \fItypes\fR, may also be used to restrict the list. If given, only entries corresponding to newsgroups with a newsgroup mode found in \fItypes\fR will be printed. For example, a \fItypes\fR value of \f(CW\*(C`y\*(C'\fR would only show unmoderated groups, and a \fItypes\fR value of \f(CW\*(C`ym\*(C'\fR would show all unmoderated and moderated groups but not aliased groups or groups that disallow postings. .SH "OPTIONS" .IX Header "OPTIONS" .IP "\fB\-A\fR" 4 .IX Item "-A" Try to authenticate using the username and password information in \&\fIpasswd.nntp\fR\|(5) before issuing the \s-1LIST\s0 command. .IP "\fB\-h\fR \fIhost\fR" 4 .IX Item "-h host" Connect to the server \fIhost\fR rather than to the value of \fIserver\fR in \&\fIinn.conf\fR or the value of the \s-1NNTPSERVER\s0 environment variable. .IP "\fB\-p\fR \fIport\fR" 4 .IX Item "-p port" Connect to the port \fIport\fR, which is by default \f(CW119\fR. .IP "\fB\-R\fR" 4 .IX Item "-R" Try to switch to \fBnnrpd\fR with a \s-1MODE\s0 \s-1READER\s0 command before issuing the \&\s-1LIST\s0 request. It can be useful in case a feeding access exists for the news server: more information can be retrieved from \fBnnrpd\fR, compared with \fBinnd\fR. .SH "EXAMPLES" .IX Header "EXAMPLES" The following command will obtain the one-line descriptions of all newsgroups found on \s-1UUNET:\s0 .PP .Vb 1 \& getlist \-h news.uu.net newsgroups .Ve .PP The following command will show the active file entries for all moderated comp.* groups on the default server: .PP .Vb 1 \& getlist active \*(Aqcomp.*\*(Aq m .Ve .PP The following command lists all groups where local postings are permitted, are moderated or aliased: .PP .Vb 1 \& getlist active \*(Aq*\*(Aq ym= .Ve .PP Note that the wildcard character \f(CW\*(C`*\*(C'\fR has to be quoted by simple quotes to protect it from the shell. .SH "HISTORY" .IX Header "HISTORY" Written by Landon Curt Noll for InterNetNews. Rewritten in \s-1POD\s0 by Russ Allbery . .PP \&\f(CW$Id:\fR getlist.pod 9767 2014\-12\-07 21:13:43Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIactive\fR\|(5), \fIactive.times\fR\|(5), \fIdistrib.pats\fR\|(5), \fIdistributions\fR\|(5), \fIinn.conf\fR\|(5), \&\fImoderators\fR\|(5), \fImotd.news\fR\|(5), \fInewsgroups\fR\|(5), \fInnrpd\fR\|(8), \fIpasswd.nntp\fR\|(5), \&\fIsubscriptions\fR\|(5), \fIuwildmat\fR\|(3). inn-2.6.0/doc/man/send-nntp.80000644000175200017520000000341512575023702015242 0ustar iuliusiulius.TH SEND-UUCP 8 .SH NAME send-nntp, send-ihave \- send Usenet articles to remote site .SH SYNOPSIS .B send-nntp [ .B \-d ] .B sitename:hostname | sitename [ .B sitename:hostname | sitename .. ] .PP .B send-ihave [ .B \-d ] .B sitename:hostname | sitename [ .B sitename:hostname | sitename .. ] .SH DESCRIPTION The send-* utilities are scripts that process the batch files written by .IR innd (8) to send Usenet articles to a remote NNTP site. .PP The sites to be fed may be specified by giving .I sitename .I hostname pairs on the command line. .PP The .I sitename is the label the site has in the .I newsfeeds file, the .I hostname is the real hostname of the remote site, a FQDN (Fully Qualified Domain Name). Normally, the .I sitename and the .I hostname are the same, and as such don't have to be specified as sitename:hostname pairs but just as a sitename. .PP .I send-nntp starts an innxmit to send the articles to the remote site. .PP .I send-ihave encapsulates the articles in an .I ihave control message and uses .I inews to send the articles to a .I to.sitename pseudo-group. Using .I send-ihave is discouraged, nobody uses it anymore and even the author of this manpage is unsure as to how it actually works or used to work. .PP .I send-* expect that the batchfile for a site is named .IR /sitename . To prevent batchfile corruption, .IR shlock (1) is used to ``lock'' these files. .SH OPTIONS .TP .B "\-d" The ``\-d'' flag causes .I nntpsend to send output to stdout rather than the log file .IR /.log . .SH NOTES You should probably not use send-nntp, but .IR innfeed , or if that is not possible, .IR nntpsend . .PP The usual flags for a batch file for send-nntp are ``\fBTf,Wfm\fP''. .SH "SEE ALSO" newsfeeds(5), nntpsend(8) inn-2.6.0/doc/man/newslog.50000644000175200017520000003574012575023702015015 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "NEWSLOG 5" .TH NEWSLOG 5 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" newslog \- Description of INN log files .SH "DESCRIPTION" .IX Header "DESCRIPTION" Most log files created by Usenet programs reside in the \fIpathlog\fR directory set in \fIinn.conf\fR and have a \f(CW\*(C`.log\*(C'\fR extension. Several versions are usually kept with an additional extension such as \f(CW.1\fR, \f(CW.2\fR, etc. \-\-\ the higher the number, the older the log. These old versions are stored in \fIpathlog\fR/OLD; they may be compressed and thus may have a \f(CW\*(C`.1.gz\*(C'\fR, \f(CW\*(C`.2.gz\*(C'\fR, etc. extension, up to \f(CW\*(C`.nn.gz\*(C'\fR where \f(CW\*(C`nn\*(C'\fR is the number of old logs kept by \fBscanlogs\fR which is set by \fIlogcycles\fR in \fIinn.conf\fR. .PP The \fBscanlogs\fR script and related utilities are responsible for rotating and compressing these files. Some log files always have data (like \&\fInews.notice\fR), others only have data if there is a problem (like \&\fInews.err\fR), and others are only created if a particular program is used (like \fIsend\-uucp.log\fR used by \fBsend-uucp\fR) or if a configuration parameter is set (like \fIinnfeed.log\fR used by \fBinnfeed\fR when \fIdebug-level\fR is set in \fIinnfeed.conf\fR). .PP Besides, the \fBinnstat\fR script monitors the size of all log files. .PP Here are the log files used by \s-1INN:\s0 .IP "\fIcontrol.log\fR" 4 .IX Item "control.log" This file maintains a count of the number of newgroup and rmgroup control messages seen for each newsgroup. The count is of the number of control messages with the indicated arguments, regardless if they were actually processed. All control arguments, including invalid ones, are counted. An example of lines which can be found in that log file is: .Sp .Vb 3 \& 3 Control: newgroup foo.bar moderated \& 3 Control: rmgroup misc.removed \& 1 Control: newgroup misc.created .Ve .Sp This file is updated by \fBtally.control\fR, which is invoked by \fBscanlogs\fR if either one of these two log files exists in \fIpathlog\fR: .Sp .Vb 2 \& newgroup.log \& rmgroup.log .Ve .Sp These two log files contain a summary line describing the control message and the action taken by \fBcontrolchan\fR, followed by the article indented by four spaces, and a blank line. Whereas these files are rotated, \&\fIcontrol.log\fR is not rotated so as to keep the count of seen control messages. .Sp Note that other control log files are also rotated by \fBscanlogs\fR, if they exist, but their contents are not summarized. Here are their names: .Sp .Vb 8 \& checkgroups.log \& default.log \& ihave.log \& miscctl.log \& sendme.log \& sendsys.log \& senduuname.log \& version.log .Ve .Sp In order to create these files, the and fields of relevant \fIcontrol.ctl\fR entries should be correctly set. For instance: .Sp .Vb 7 \& Type Action Meaning \& \-\-\-\- \-\-\-\-\-\- \-\-\-\-\-\-\- \& all log=miscctl Log all messages by default. \& newgroup doit=newgroup Create group and log message. \& newgroup log=newgroup Log message. \& rmgroup verify\-*=rmgroup PGP verify, remove group and log message. \& checkgroups doit=checkgroups Process and log message. .Ve .IP "\fIcontrolbatch.log\fR" 4 .IX Item "controlbatch.log" The \fBcontrolbatch\fR program appends all status messages to this file. It is rotated by \fBscanlogs\fR. .IP "\fIerrlog\fR" 4 .IX Item "errlog" This file contains the standard output and standard error of any program spawned by \fBinnd\fR, such as channel feeds configured in \fInewsfeeds\fR. This file should normally be empty. \fBscanlogs\fR will print the 50 first lines of this log file if it is non-empty so that they appear in daily Usenet reports generated by \fBnews.daily\fR. Then, \fBscanlogs\fR rotates this log file. .IP "\fIexpire.log\fR" 4 .IX Item "expire.log" By default, when \fBnews.daily\fR is going to expire old news articles, it writes the name of the program it invokes, followed by \f(CW\*(C`start\*(C'\fR and the time it has started. Any output from that program is then written, indented by four spaces. Then, the name of the program is written, followed by \f(CW\*(C`end\*(C'\fR and the time it has ended. .Sp Programs called, if needed, are in order: \fBexpireover\fR, \fBctlinnd\fR (with the \f(CW\*(C`lowmark\*(C'\fR option), \fBexpirerm\fR and \fBexpire\fR. Removed articles are listed in \fIexpire.list\fR and low marks for each newsgroup (that is to say the number of the oldest article available in the newsgroup) in \&\fIexpire.lastlowmark\fR. .Sp After the expiry process, \fBscanlogs\fR rotates \fIexpire.log\fR. .IP "\fIinnfeed.log\fR" 4 .IX Item "innfeed.log" When \fIdebug-level\fR is set in \fIinnfeed.conf\fR, \fBinnfeed\fR appends debugging messages to this file. Note that the name of this file can be changed with the \fIlog-file\fR parameter in \fIinnfeed.conf\fR. Be that as it may, the right log file is rotated by \fBscanlogs\fR. .Sp \&\fBinnfeed\fR also logs its status in \fIinnfeed.status\fR (or the name set in \&\fIstatus-file\fR) if \fIgen-html\fR is set to false but this log file is not processed by \fBscanlogs\fR. .IP "\fInews\fR" 4 .IX Item "news" This file logs articles received by \fBinnd\fR. Typical lines are: .Sp .Vb 3 \& Aug 25 13:37:41.839 + news.server.fr 1658 a.peer other.server.org inpaths! \& Aug 25 13:37:41.839 c news.server.fr Cancelling \& Aug 25 13:37:54.638 \- a.peer <23k82@bar.net> 437 Poison newsgroup .Ve .Sp The first one indicates that an article from \f(CW\*(C`news.server.fr\*(C'\fR has been accepted (\f(CW\*(C`+\*(C'\fR). Its Message-ID is \f(CW\*(C`\*(C'\fR and we will send the article to two peers (\f(CW\*(C`a.peer\*(C'\fR and \f(CW\*(C`other.server.org\*(C'\fR, as specified in \fInewsfeeds\fR) and \f(CW\*(C`inpaths!\*(C'\fR (see \fBninpaths\fR for more details about it). The second line mentions that this previous article is a cancel message (\f(CW\*(C`c\*(C'\fR) for \f(CW\*(C`\*(C'\fR and that it has been processed (inndeed, \fBcontrolchan\fR processes all control articles except for cancels which are handled by \fBinnd\fR). The third line indicates that the article whose Message-ID is \f(CW\*(C`<23k82@bar.net>\*(C'\fR has been rejected (\f(CW\*(C`\-\*(C'\fR) because it is posted to a poison newsgroup (a Perl or a Python filter located in \fIpathfilter\fR must have brought that reject along). .Sp See the \*(L"\s-1LOGGING\s0\*(R" section of the \fIinnd\fR\|(8) man page for more information about the format of this log file. .Sp \&\fBinnreport\fR summarizes the rejected articles reported in this file and \fBscanlogs\fR rotates it. .IP "\fInews.crit\fR" 4 .IX Item "news.crit" All critical error messages issued by \fBinnd\fR are appended to this file via syslog. This log file should normally be empty. \fBscanlogs\fR will print the first 50 lines of this log file if it is non-empty so that they appear in daily Usenet reports generated by \fBnews.daily\fR. Then, \fBscanlogs\fR rotates this log file. .Sp You should have the following line in your system \fIsyslog.conf\fR file, using a tab character for the delimiter: .Sp .Vb 1 \& news.crit /news.crit .Ve .Sp (A typical entry is shown; it should agree with \fIpathlog\fR in \fIinn.conf\fR and be tab-delimited.) .IP "\fInews.err\fR" 4 .IX Item "news.err" All major error messages issued by \fBinnd\fR are appended to this file via syslog. This log file should normally be empty. \fBscanlogs\fR will print the first 50 lines of this log file if it is non-empty so that they appear in daily Usenet reports generated by \fBnews.daily\fR. Then, \fBscanlogs\fR rotates this log file. .Sp You should have the following line in your system \fIsyslog.conf\fR file, using a tab character for the delimiter: .Sp .Vb 1 \& news.err /news.err .Ve .Sp (A typical entry is shown; it should agree with \fIpathlog\fR in \fIinn.conf\fR and be tab-delimited.) .IP "\fInews.notice\fR" 4 .IX Item "news.notice" All standard error messages and status messages issued by \fBinnd\fR, \&\fBinnfeed\fR, \fBnnrpd\fR and some other programs are appended to this file via syslog. \fBscanlogs\fR uses the Perl script \fBinnreport\fR to summarize this file. \fBinnreport\fR will also print the first \fImax_unknown\fR unknown lines of this log file if such unrecognized lines are found in \&\fInews.notice\fR so that they appear in daily Usenet reports generated by \fBnews.daily\fR. This parameter can be set in \fIinnreport.conf\fR. Then, \fBscanlogs\fR rotates this log file. .Sp You should have the following line in your system \fIsyslog.conf\fR file, using a tab character for the delimiter: .Sp .Vb 1 \& news.notice /news.notice .Ve .Sp (A typical entry is shown; it should agree with \fIpathlog\fR in \fIinn.conf\fR and be tab-delimited.) .IP "\fInntpsend.log\fR" 4 .IX Item "nntpsend.log" The \fBnntpsend\fR program appends all status messages to this file. It is rotated by \fBscanlogs\fR. .IP "\fIperl\-nocem.log\fR" 4 .IX Item "perl-nocem.log" The \fBperl-nocem\fR program appends all status messages to this file if \&\f(CW\*(C`Sys::Syslog\*(C'\fR is not used (otherwise, such messages are appended to \&\fInews.notice\fR). It is rotated by \fBscanlogs\fR. .IP "\fIsend\-ihave.log\fR" 4 .IX Item "send-ihave.log" The \fBsend-ihave\fR program appends all status messages to this file. It is rotated by \fBscanlogs\fR. .IP "\fIsend\-nntp.log\fR" 4 .IX Item "send-nntp.log" The \fBsend-nntp\fR program appends all status messages to this file. It is rotated by \fBscanlogs\fR. .IP "\fIsend\-uucp.log\fR" 4 .IX Item "send-uucp.log" The \fBsend-uucp\fR program appends all status messages to this file. It is rotated by \fBscanlogs\fR. .IP "\fIunwanted.log\fR" 4 .IX Item "unwanted.log" This log maintains a count of the number of articles that were rejected because they were posted to newsgroups that do not exist at the local site. This file is updated by \fBinnreport\fR while processing the \fInews.notice\fR log file and it is maintained in reverse numeric order (the most popular rejected group first). This file is not rotated so as to keep the count of the articles posted to newsgroups which are absent from the \fIactive\fR file of the news server. .Sp Note that \fIlogtrash\fR has to be set to true in \fIinn.conf\fR for this file to be generated. .PP Finally, these files are also rotated by \fBscanlogs\fR, if they exist, but their contents are not summarized. Here are their names: .PP .Vb 3 \& badcontrol.log \& badpgp.log \& failedpgp.log .Ve .PP They can be used by programs which \s-1PGP\s0 verify articles. .SH "HISTORY" .IX Header "HISTORY" Written by Landon Curt Noll and Rich \f(CW$alz\fR for InterNetNews. Rewritten and converted to \s-1POD\s0 by Julien Elie. .PP \&\f(CW$Id:\fR newslog.pod 9903 2015\-06\-20 17:20:46Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIcontrol.ctl\fR\|(5), \fIctlinnd\fR\|(8), \fIexpire\fR\|(8), \fIexpireover\fR\|(8), \fIexpirerm\fR\|(8), \fIinn.conf\fR\|(5), \&\fIinnd\fR\|(8), \fIinnfeed.conf\fR\|(5), \fIinnreport\fR\|(8), \fIinnreport.conf\fR\|(5), \fInews.daily\fR\|(8), \&\fInnrpd\fR\|(8), \fInntpsend\fR\|(8), \fIscanlogs\fR\|(8), \fIsend\-nntp\fR\|(8), \fIsend\-uucp\fR\|(8), \fIsyslog.conf\fR\|(5), \&\fItally.control\fR\|(8). inn-2.6.0/doc/man/history.50000644000175200017520000000516712575023702015040 0ustar iuliusiulius.\" $Revision: 3782 $ .TH HISTORY 5 .SH NAME history \- record of current and recently expired Usenet articles .SH DESCRIPTION The file .I /history keeps a record of all articles currently stored in the news system, as well as those that have been received but since expired. In a typical production environment, this file will be many megabytes. .PP The file consists of text lines. Each line corresponds to one article. The file is normally kept sorted in the order in which articles are received, although this is not a requirement. .IR Innd (8) appends a new line each time it files an article, and .IR expire (8) builds a new version of the file by removing old articles and purging old entries. .PP Each line consists of two or three fields separated by a tab, shown below as .IR \et : .RS .nf [Hash] \et date [Hash] \et date \et token .fi .RE .PP The .I Hash field is the ASCII representation of the hash of the Message-ID header. This is directly used for the key of the .IR dbz (3). .PP The .I date field consists of three sub-fields separated by a tilde. All sub-fields are the text representation of the number of seconds since the epoch \(em .IR i.e. , a .IR time_t ; see .IR gettimeofday (2). The first sub-field is the article's arrival date. If copies of the article are still present then the second sub-field is either the value of the article's Expires header, or a hyphen if no expiration date was specified. If an article has been expired then the second sub-field will be a hyphen. The third sub-field is the value of the article's Date header, recording when the article was posted. .PP The .I token field is a token of the article. This field is empty if the article has been expired. .PP For example, an article whose Message-ID was <7q2saq$sal$1@isrv4.pa.vix.com>, posted on 26 Aug 1999 08:02:34 GMT and recieved at 26 Aug 1999 08:06:54 GMT, could have a history line (broken into three lines for display) like the following: .RS .nf [E6184A5BC2898A35A3140B149DE91D5C] \et 935678987~-~935678821 \et @030154574F00000000000007CE3B000004BA@ .fi .RE .PP In addition to the text file, there is a .IR dbz (3) database associated with the file that uses the Message-ID field as a key to determine the offset in the text file where the associated line begins. For historical reasons, the key includes the trailing \e0 byte (which is not stored in the text file). .SH HISTORY Written by Rich $alz for InterNetNews. .de R$ This is revision \\$3, dated \\$4. .. .R$ $Id: history.5 3782 2000-08-17 13:30:18Z kondou $ .SH "SEE ALSO" dbz(3), expire(8), inn.conf(5), innd(8), makehistory(8). inn-2.6.0/doc/man/inncheck.80000644000175200017520000002255012575023702015117 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "INNCHECK 8" .TH INNCHECK 8 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" inncheck \- Check INN configuration and database files .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fBinncheck\fR [\fB\-afqv\fR] [\fB\-\-noperm\fR | \fB\-\-perm\fR] [\fB\-\-pedantic\fR] [\fIfile\fR | \fIfile\fR\fB=\fR\fIpath\fR ...] .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\fBinncheck\fR examines various configuration files and databases and verifies things about them. Things verified depend on the file being checked, but generally are things like permissions, ownership, syntax errors in config files, etc. .PP \&\fBinncheck\fR does not make changes to any files \-\-\ it just reports what it thinks may be wrong, and it is up to the operator to fix the problem .PP The set of files checked may be restricted by using \fIfile\fR or \&\fIfile\fR\fB=\fR\fIpath\fR arguments. For example, putting \f(CW\*(C`incoming.conf\*(C'\fR causes only the \fIincoming.conf\fR file to be checked. Using \&\f(CW\*(C`incoming.conf=/tmp/incoming.conf\*(C'\fR on the command line will cause \&\fBinncheck\fR to only verify the \fIincoming.conf\fR file, and it will perform the checks on the \fI/tmp/incoming.conf\fR file instead of the default one. Setting a file path to an empty value like \f(CW\*(C`incoming.conf=\*(C'\fR will skip the check for that file (this only makes sense with \fB\-a\fR, or no files will be checked at all). .PP Valid values for \fIfile\fR are: .PP .Vb 10 \& active \& control.ctl \& control.ctl.local \& expire.ctl \& incoming.conf \& inn.conf \& innfeed.conf \& moderators \& newsfeeds \& nntpsend.ctl \& passwd.nntp \& readers.conf \& storage.conf .Ve .SH "OPTIONS" .IX Header "OPTIONS" .IP "\fB\-a\fR, \fB\-\-all\fR" 4 .IX Item "-a, --all" If any \fIfile\fR value or \fIfile\fR\fB=\fR\fIpath\fR pairs (see below) are given, then normally only the files they refer to are checked. Use the \fB\-a\fR (or \&\fB\-\-all\fR) flag to specify that \fIall\fR files should be checked regardless. In this case the form \fIfile\fR\fB=\fR\fIpath\fR will be the more useful. .IP "\fB\-f\fR, \fB\-\-fix\fR" 4 .IX Item "-f, --fix" Use the \fB\-f\fR flag (or \fB\-\-fix\fR) to have \fBinncheck\fR print the appropriate \&\fBchown\fR/\fBchgrp\fR/\fBchmod\fR command necessary to fix a problem that it reports. Any other output lines will be prefixed with a hash sign (\f(CW\*(C`#\*(C'\fR) to make the output be valid input for a shell. Note that the \fB\-\-perm\fR flag must be used as well when using this flag. .IP "\fB\-\-noperm\fR" 4 .IX Item "--noperm" To avoid doing any checking of file permissions or ownership, use the \&\fB\-\-noperm\fR option. .IP "\fB\-\-pedantic\fR" 4 .IX Item "--pedantic" Use the \fB\-\-pedantic\fR option to get reports on things that are not necessarily wrong, but may indicate a bad configuration, such as \fIinn.conf\fR missing a key. .IP "\fB\-\-perm\fR" 4 .IX Item "--perm" \&\fBinncheck\fR checks all files for permission problems. If the \fB\-\-perm\fR flag is used, then \fIonly\fR the files specified by the \fIfile\fR or \&\fIfile\fR\fB=\fR\fIpath\fR command line arguments will be checked for problems other than permission problems. .IP "\fB\-q\fR, \fB\-\-quiet\fR" 4 .IX Item "-q, --quiet" Use the \fB\-q\fR option (or \fB\-\-quiet\fR) to get less output. .IP "\fB\-v\fR, \fB\-\-verbose\fR" 4 .IX Item "-v, --verbose" Use the \fB\-v\fR option (or \fB\-\-verbose\fR) to get more verbose output. .SH "EXAMPLES" .IX Header "EXAMPLES" To have \fBinncheck\fR check all files for syntax and permission problems simply run: .PP .Vb 1 \& inncheck .Ve .PP It is recommended to run the following command to be aware of all the potential problems \fBinncheck\fR can detect: .PP .Vb 1 \& inncheck \-a \-\-perm \-\-pedantic .Ve .PP To have \fBinncheck\fR check all files for permission problems and to verify the syntax of the \fIactive\fR and \fIincoming.conf\fR files, do: .PP .Vb 1 \& inncheck \-\-perm active incoming.conf .Ve .PP To fix the permissions problems noted in the output of the above command, modify it as follows: .PP .Vb 1 \& inncheck \-f \-\-perm | sh .Ve .PP (Note that it is useless to mention the name of the two files since permission problems are checked on all files.) .PP To have \fBinncheck\fR check the test \fInewsfeeds\fR file in \&\fI/var/tmp/newsfeeds.testing\fR, do: .PP .Vb 1 \& inncheck newsfeeds=/var/tmp/newsfeeds.testing .Ve .PP To have \fBinncheck\fR check all the files as it normally does, but to specify a different location for the \fInewsfeeds\fR file and omit checking of the \&\fIinnfeed.conf\fR file, do: .PP .Vb 1 \& inncheck \-a newsfeeds=/var/tmp/newsfeeds.testing innfeed.conf= .Ve .SH "BUGS" .IX Header "BUGS" If the \fB\-f\fR and \fB\-\-perm\fR options are used together, along with \fB\-a\fR or some \fIfile\fR or \fIfile\fR\fB=\fR\fIpath\fR arguments that refer to a file with a syntax problem, then the output will no longer be valid input for a shell. .PP In most cases, \fBinncheck\fR currently does not correctly understand option values spanning multiple lines. If you get error messages about runaway quotes, you either have to ignore them and check the correctness of the relevant lines yourself, or merge the value into a single line. .SH "HISTORY" .IX Header "HISTORY" Written by Brendan Kehoe and Rich Salz for InterNetNews. Converted to \s-1POD\s0 by Julien Elie. .PP \&\f(CW$Id:\fR inncheck.pod 9428 2012\-06\-15 18:18:45Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIactive\fR\|(5), \fIcontrol.ctl\fR\|(5), \fIexpire.ctl\fR\|(5), \fIhistory\fR\|(5), \fIincoming.conf\fR\|(5), \&\fIinn.conf\fR\|(5), \fIinnfeed.conf\fR\|(5), \fImoderators\fR\|(5), \fInewsfeeds\fR\|(5), \fInntpsend.ctl\fR\|(5), \&\fIpasswd.nntp\fR\|(5), \fIreaders.conf\fR\|(5), \fIstorage.conf\fR\|(5). inn-2.6.0/doc/man/procbatch.80000644000175200017520000002172612575023702015306 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "PROCBATCH 8" .TH PROCBATCH 8 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" procbatch \- Process an INN funnel file or innfeed\-dropped file .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fBprocbatch\fR [\fB\-hquv\fR] [\fB\-c\fR [\fB\-s\fR \fIspooldir\fR]] [\fB\-d\fR \fIoutdir\fR] [\fB\-e\fR \fIpeer\fR] [\fB\-m\fR [\fB\-t\fR \fIbacklogdir\fR]] \fIbatchfile\fR .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\fBprocbatch\fR will take an \s-1INN\s0 funnel or \fIinnfeed-dropped\fR file and split it up by host for direct processing with \fBinnfeed\fR or \fBinnxmit\fR. .PP While funnel files will normally only be of interest after a crash, \&\fBinnfeed\fR may drop articles for a variety of reasons, not all of which indicate a fundamental problem. For example, \fBinnfeed\fR may drop articles when started and stopped in quick succession while \s-1INN\s0 processes a large number of control messages (a temporary indisposition), or when \s-1INN\s0 feeds it articles for a site that isn't in \fIinnfeed.conf\fR (a misconfiguration). .PP Every running \fBinnfeed\fR opens a file named \fIinnfeed\-dropped.\fR, which should normally always be zero length and deleted on exit. If there are non-zero length files, \fBinnfeed\fR has dropped some articles, and those dropped article files have to be processed or those articles will never be sent to peers. Though \fBnews.daily\fR automatically process these files (invoking \fBprocbatch\fR), it is also possible to do that manually. First make sure that the file doesn't correspond to a currently running \&\fBinnfeed\fR, for example by calling \f(CW\*(C`ctlinnd flush innfeed!\*(C'\fR. .PP An \s-1INN\s0 funnel file, or an \fIinnfeed-dropped\fR file, will usually be of the format: .PP .Vb 1 \& pathname message\-id peer1 peer2 peer3 ... .Ve .PP where \fIpathname\fR can alternatively be a storage \s-1API\s0 token. .PP \&\fBprocbatch\fR will break this file up into files named \fIpeer1.tmp\fR, \&\fIpeer2.tmp\fR, \fIpeer3.tmp\fR, etc. of the format: .PP .Vb 1 \& pathname message\-id .Ve .PP These tape files will be sorted and stripped of duplicate entries. Simply renaming them to \fIpeer1\fR, \fIpeer2\fR, \fIpeer3\fR, etc. in a running \fBinnfeed\fR's backlog directory will cause them to be picked up automatically by \fBinnfeed\fR every \fIbacklog-newfile-period\fR seconds (as set in in \fIinnfeed.conf\fR). Use the \fB\-m\fR flag to have \fBprocbatch\fR rename and move them into place automatically. .PP After running \fBprocbatch\fR, you may want to make sure that every generated backlog file has a corresponding \f(CW\*(C`.lock\*(C'\fR file. Otherwise, \fBinnfeed\fR doesn't have that site configured as a peer, meaning your \fIinnfeed.conf\fR may need fixing. .SH "OPTIONS" .IX Header "OPTIONS" .IP "\fB\-c\fR" 4 .IX Item "-c" Check for the existence of an article before writing references to it to the tape files. Currently, this option can only be used with a tradspool article store. Using it with any other spool format will lead to all articles being dropped. .IP "\fB\-d\fR \fIoutdir\fR" 4 .IX Item "-d outdir" Put the output file(s) into the directory \fIoutdir\fR (defaults to \fIpathtmp\fR as set in \fIinn.conf\fR). When also specifying \fB\-m\fR, the temporary output files will still be put here before being moved into their final location. .IP "\fB\-e\fR \fIpeer\fR" 4 .IX Item "-e peer" Only process entries for \fIpeer\fR. .IP "\fB\-h\fR" 4 .IX Item "-h" Display a short help screen. .IP "\fB\-m\fR" 4 .IX Item "-m" Directly append to tape files in \fBinnfeed\fR's backlog directory. .IP "\fB\-q\fR" 4 .IX Item "-q" Quiet mode: only display error messages. .IP "\fB\-s\fR \fIspooldir\fR" 4 .IX Item "-s spooldir" Use \fIspooldir\fR as the location of the article spool (defaults to \&\fIpatharticles\fR as set in \fIinn.conf\fR). This option has an effect only on a tradspool storage system when also specifying \fB\-c\fR. .IP "\fB\-t\fR \fIbacklogdir\fR" 4 .IX Item "-t backlogdir" Use \fIbacklogdir\fR as the location of the \fBinnfeed\fR backlog directory (the directory where the created tape files will be put). Defaults to \&\fIpathspool\fR/innfeed. This option has an effect only when also specifying \&\fB\-m\fR. .IP "\fB\-u\fR" 4 .IX Item "-u" Unlink the input batchfile after processing. .IP "\fB\-v\fR" 4 .IX Item "-v" Add verbosity. .SH "EXAMPLES" .IX Header "EXAMPLES" Take the file \fIinnfeed\-dropped.B012345\fR, process its lines appending to tapefiles in \fBinnfeed\fR's backlog directory, and remove it when done. Be verbose while doing so: .PP .Vb 1 \& procbatch \-umv innfeed\-dropped.B012345 .Ve .PP Go through \fIinnfeed\-dropped.B012345\fR saving entries for \f(CW\*(C`peer1\*(C'\fR in \fI/tmp/peer1.tmp\fR, but only if the articles are still available in the local tradspool: .PP .Vb 1 \& procbatch \-e peer1 \-d /tmp \-c innfeed\-dropped.B012345 .Ve .SH "BUGS" .IX Header "BUGS" \&\fBprocbatch\fR should be able to check for the existence of articles with any kind of article store, possibly using \fBgrephistory\fR. .SH "HISTORY" .IX Header "HISTORY" \&\fBprocbatch\fR was written by James Brister and improved for speed by Clayton O'Neill. .PP This manual page was written by Florian Schlichting, with the help of a memo by Russ Allbery. .PP \&\f(CW$Id:\fR procbatch.pod 9371 2011\-09\-04 09:21:43Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIfilechan\fR\|(8), \fIinnfeed\fR\|(8), \fIinnxmit\fR\|(8), \fInews.daily\fR\|(8). inn-2.6.0/doc/man/clientlib.30000644000175200017520000000452712575023702015301 0ustar iuliusiulius.\" $Revision: 42 $ .TH CLIENTLIB 3 .SH NAME clientlib \- NNTP clientlib part of InterNetNews library .SH SYNOPSIS .nf .ta \w' unsigned long 'u .B "extern FILE *ser_rd_fp;" .B "extern FILE *ser_wr_fp;" .B "extern char ser_line[];" .B "char *" .B "getserverbyfile(file)" .B " char *file;" .B "int" .B "server_init(host)" .B " char *host;" .B "int" .B "handle_server_response(response, host)" .B " int reponse;" .B " char *host;" .B "void" .B "put_server(text)" .B " char *text;" .B "int" .B "get_server(buff, buffsize)" .B " char *buff;" .B " int buffsize;" .B "void" .B "close_server()" .fi .SH DESCRIPTION The routines described in this manual page are part of the InterNetNews library, .IR libinn (3). They are replacements for the ``clientlib'' part of the NNTP distribution, and are intended to be used in building programs like .IR rrn . .PP .I Getserverbyfile calls .I GetConfigValue to get the name of the local NNTP server. It returns a pointer to static space. The .I file parameter is ignored. .PP .I Server_init opens a connect to the NNTP server at the specified .IR host . It returns the server's response code or \-1 on error. If a connection was made, then .I ser_rd_fp and .I ser_wr_fp can be used to read from and write to the server, respectively, and .I ser_line will contain the server's response. .I Ser_line can also be used in other routines. .PP .I Handle_server_response decodes the .IR response , which comes from the server on .IR host. If the client is authorized, it returns 0. A client that is only allowed to read is authorized, but .I handle_server_response will print a message on the standard output. If the client is not authorized to talk to the server, then a message is printed and the routine returns \-1. .PP .I Put_server sends the text in .I buff to the server, adding the necessary NNTP line terminators, and flushing the I/O buffer. .PP .I Get_server reads a line of text from the server into .IR buff , reading at most .I buffsize characters. Any trailing \er\en terminators are stripped off. .I Get_server returns \-1 on error. .PP .I Close_server sends a ``quit'' command to the server and closes the connection. .SH HISTORY Written by Rich $alz for InterNetNews. .de R$ This is revision \\$3, dated \\$4. .. .R$ $Id: clientlib.3 42 1997-08-04 04:03:43Z gpalmer $ .SH "SEE ALSO" libinn(3). inn-2.6.0/doc/man/docheckgroups.80000644000175200017520000002540412575023702016176 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "DOCHECKGROUPS 8" .TH DOCHECKGROUPS 8 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" docheckgroups \- Process checkgroups and output a list of changes .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fBdocheckgroups\fR [\fB\-u\fR] [\fIinclude-pattern\fR [\fIexclude-pattern\fR]] .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\fBdocheckgroups\fR is usually run by \fBcontrolchan\fR in order to process checkgroups control messages. It reads a list of newsgroups along with their descriptions on its standard input. That list should be formatted like the \fInewsgroups\fR\|(5) file: each line contains the name of a newsgroup followed by one or more tabulations and its description. .PP \&\fBdocheckgroups\fR will only check the presence of newsgroups which match \fIinclude-pattern\fR (an \fBegrep\fR expression like \&\f(CW\*(C`^comp\e..*$\*(C'\fR for newsgroups starting with \f(CW\*(C`comp.\*(C'\fR) and which do not match \fIexclude-pattern\fR (also an \fBegrep\fR expression) except for newsgroups mentioned in the \fIpathetc\fR/localgroups file. This file is also formatted like the \fInewsgroups\fR\|(5) file and should contain local newsgroups which would otherwise be mentioned for removal. There is no need to put local newsgroups of hierarchies for which no checkgroups control messages are sent, unless you manually process checkgroups texts for them. Lines beginning with a hash sign (\f(CW\*(C`#\*(C'\fR) are not taken into account in this file. All the newsgroups and descriptions mentioned in \fIpathetc\fR/localgroups are appended to the processed checkgroups. .PP If \fIexclude-pattern\fR is given, \fIinclude-pattern\fR should also be given before (you can use an empty string ("") if you want to include all the newsgroups). Be that as it may, \fBdocheckgroups\fR will only check newsgroups in the top-level hierarchies which are present in the checkgroups. .PP Then, \fBdocheckgroups\fR checks the \fIactive\fR and \fInewsgroups\fR files and displays on its standard output a list of changes, if any. It does not change anything by default; it only points out what should be changed: .IP "\(bu" 2 Newsgroups which should be removed (they are in the \fIactive\fR file but not in the checkgroups) and the relevant \fBctlinnd\fR commands to achieve that; .IP "\(bu" 2 Newsgroups which should be added (they are not in the \fIactive\fR file but in the checkgroups) and the relevant \fBctlinnd\fR commands to achieve that; .IP "\(bu" 2 Newsgroups which are incorrectly marked as moderated or unmoderated (they are both in the \fIactive\fR file and the checkgroups but their status differs) and the relevant \fBctlinnd\fR commands to fix that; .IP "\(bu" 2 Descriptions which should be removed (they are in the \fInewsgroups\fR file but not in the checkgroups); .IP "\(bu" 2 Descriptions which should be added (they are not in the \fInewsgroups\fR file but in the checkgroups). .PP The output of \fBdocheckgroups\fR can be fed into \fBmod-active\fR (it will pause the news server, update the \fIactive\fR file accordingly, reload it and resume the work of the news server) or into the shell (commands for \&\fBctlinnd\fR will be processed one by one). In order to update the \&\fInewsgroups\fR file, the \fB\-u\fR flag must be given to \fBdocheckgroups\fR. .PP When processing a checkgroups manually, it is always advisable to first check the raw output of \fBdocheckgroups\fR. Then, if everything looks fine, use \fBmod-active\fR and the \fB\-u\fR flag. .SH "OPTIONS" .IX Header "OPTIONS" .IP "\fB\-u\fR" 4 .IX Item "-u" If this flag is given, \fBdocheckgroups\fR will update the \fInewsgroups\fR file: it removes obsolete descriptions and adds new ones. It also sorts this file alphabetically and improves its general format (see \fInewsgroups\fR\|(5) for an explanation of the preferred number of tabulations). .SH "EXAMPLES" .IX Header "EXAMPLES" So as to better understand how \fBdocheckgroups\fR works, here are examples with the following \fIactive\fR file: .PP .Vb 6 \& a.first 0000000000 0000000001 y \& a.second.announce 0000000000 0000000001 y \& a.second.group 0000000000 0000000001 y \& b.additional 0000000000 0000000001 y \& b.third 0000000000 0000000001 y \& c.fourth 0000000000 0000000001 y .Ve .PP the following \fInewsgroups\fR file (using tabulations): .PP .Vb 5 \& a.first First group. \& a.second.announce Announce group. \& a.second.group Second group. \& b.third Third group. \& c.fourth Fourth group. .Ve .PP and the following \fIlocalgroups\fR file (using tabulations): .PP .Vb 1 \& b.additional A local newsgroup I want to keep. .Ve .PP The checkgroups we process is in the file \fItest\fR which contains: .PP .Vb 5 \& a.first First group. \& a.second.announce Announce group. (Moderated) \& a.second.group Second group. \& b.third Third group. \& c.fourth Fourth group. .Ve .PP If we run: .PP .Vb 1 \& cat test | docheckgroups .Ve .PP \&\fBdocheckgroups\fR will output that a.second.announce is incorrectly marked as unmoderated and that its description is obsolete. Besides, two new descriptions will be mentioned for addition (the new one for a.second.announce and the missing description for b.additional \-\-\ it should indeed be in the \fInewsgroups\fR file and not only in \fIlocalgroups\fR). Now that we have checked the output of \fBdocheckgroups\fR and that we agree with the changes, we run it with the \fB\-u\fR flag to update the \fInewsgroups\fR file and we redirect the standard output to \fBmod-active\fR to update the \&\fIactive\fR file: .PP .Vb 1 \& cat test | docheckgroups \-u | mod\-active .Ve .PP That's all! .PP Now, suppose we run: .PP .Vb 1 \& cat test | docheckgroups "^c\e..*$" .Ve .PP Nothing is output (indeed, everything is fine for the c.* hierarchy). It would have been similar if the \fItest\fR file had only contained the checkgroups for the c.* hierarchy (\fBdocheckgroups\fR would not have checked a.* and b.*, even if they had been in \fIinclude-pattern\fR). .PP In order to check both a.* and c.*, you can run: .PP .Vb 1 \& cat test | docheckgroups "^a\e..*$|^c\e..*$" .Ve .PP And if you want to check a.* but not a.second.*, you can run: .PP .Vb 1 \& cat test | docheckgroups "^a\e..*$" "^a\e.second\e..*$" .Ve .PP In our example, \fBdocheckgroups\fR will then mention a.second.announce and a.second.group for removal since they are in the \fIactive\fR file (the same goes for their descriptions). Notwithstanding, if you do want to keep a.second.announce, just add this group to \fIlocalgroups\fR and \&\fBdocheckgroups\fR will no longer mention it for removal. .SH "FILES" .IX Header "FILES" .IP "\fIpathbin\fR/docheckgroups" 4 .IX Item "pathbin/docheckgroups" The Shell script itself used to process checkgroups. .IP "\fIpathetc\fR/localgroups" 4 .IX Item "pathetc/localgroups" The list of local newsgroups along with their descriptions. .SH "HISTORY" .IX Header "HISTORY" Documentation written by Julien Elie for InterNetNews. .PP \&\f(CW$Id:\fR docheckgroups.pod 8357 2009\-02\-27 17:56:00Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIactive\fR\|(5), \fIcontrolchan\fR\|(8), \fIctlinnd\fR\|(8), \fImod\-active\fR\|(8), \fInewsgroups\fR\|(5). inn-2.6.0/doc/man/incoming.conf.50000644000175200017520000002560312575023702016063 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "INCOMING.CONF 5" .TH INCOMING.CONF 5 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" incoming.conf \- Configuration of incoming news feeds .SH "DESCRIPTION" .IX Header "DESCRIPTION" The file \fIpathetc\fR/incoming.conf consists of three types of entries: key/value, peer and group. Comments are from the hash character \f(CW\*(C`#\*(C'\fR to the end of the line. Blank lines are ignored. All key/value entries within each type must not be duplicated. Key/value entries are a keyword immediately followed by a colon, at least one blank and a value. For example: .PP .Vb 1 \& max\-connections: 10 .Ve .PP A legal key does not contains blanks, colons, nor \f(CW\*(C`#\*(C'\fR. There are three different types of values: integers, booleans, and strings. Integers are as to be expected. A boolean value is either \f(CW\*(C`true\*(C'\fR or \f(CW\*(C`false\*(C'\fR (case is significant). A string value is any other sequence of characters. If the string needs to contain whitespace, then it must be quoted with double quotes. .PP Peer entries look like: .PP .Vb 3 \& peer { \& # body \& } .Ve .PP The word \f(CW\*(C`peer\*(C'\fR is required. is a label for this peer. It is any string valid as a key. The body of a peer entry contains some number of key/value entries. .PP Group entries look like: .PP .Vb 3 \& group { \& # body \& } .Ve .PP The word \f(CW\*(C`group\*(C'\fR is required. is any string valid as a key. The body of a group entry contains any number of the three types of entries. So key/value pairs can be defined inside a group, and peers can be nested inside a group, and other groups can be nested inside a group. Key/value entries that are defined outside of all peer and group entries are said to be at global scope. Global key/value entries act as defaults for peers. When \fBinnd\fR looks for a specific value in a peer entry (for example, the maximum number of connections to allow), if the value is not defined in the peer entry, then the enclosing groups are examined for the entry (starting at the closest enclosing group). If there are no enclosing groups, or the enclosing groups don't define the key/value, then the value at global scope is used. A small example could be: .PP .Vb 2 \& # Global value applied to all peers that have no value of their own. \& max\-connections: 5 \& \& # A peer definition. \& peer uunet { \& hostname: usenet1.uu.net \& } \& \& peer vixie { \& hostname: gw.home.vix.com \& max\-connections: 10 # Override global value. \& } \& \& # A group of two peers which can open more connections than normal. \& group fast\-sites { \& max\-connections: 15 \& \& # Another peer. The max\-connections: value from the \& # fast\-sites group scope is used. \& peer data.ramona.vix.com { \& hostname: data.ramona.vix.com \& } \& \& peer bb.home.vix.com { \& hostname: bb.home.vix.com \& max\-connections: 20 # He can really cook. \& } \& } .Ve .PP Given the above configuration file, the defined peers would have the following values for the \fImax-connections\fR key. .PP .Vb 4 \& uunet 5 \& vixie 10 \& data.ramona.vix.com 15 \& bb.home.vix.com 20 .Ve .SH "PARAMETERS" .IX Header "PARAMETERS" The following keys are allowed: .IP "\fIcomment\fR" 4 .IX Item "comment" This key requires a string value. Reserved for future use. The default is an empty string. .IP "\fIemail\fR" 4 .IX Item "email" This key requires a string value. Reserved for future use. The default is an empty string. .IP "\fIhold-time\fR" 4 .IX Item "hold-time" This key requires a positive integer value. It defines the hold time before closing, if the connection is over \fImax-connections\fR. A value of zero specifies immediate close. The default is \f(CW0\fR. .IP "\fIhostname\fR" 4 .IX Item "hostname" This key requires a string value. It is a list of hostnames separated by a comma. A hostname is the host's fully qualified domain name, or the dotted-quad \s-1IP\s0 address of the peer for IPv4, or the colon-separated \s-1IP\s0 address of the peer for IPv6. If this key is not present in a peer block, the hostname defaults to the label of the peer. .IP "\fIidentd\fR" 4 .IX Item "identd" This key requires a string value. It is used if you wish to require a peer's user name retrieved through \fBidentd\fR match the specified string. Note that currently \fBinnd\fR does not implement any timeout in \fBidentd\fR callbacks, so enabling this option may cause \fBinnd\fR to hang if the remote peer does not respond to \fBident\fR callbacks in a reasonable timeframe. The default is an empty string, that is to say no \fBidentd\fR. .IP "\fIignore\fR" 4 .IX Item "ignore" This key requires a boolean value. Setting this entry causes \fBinnd\fR to refuse every article sent via \s-1CHECK\s0 or \s-1IHAVE\s0 by this peer. The default is false. .IP "\fImax-connections\fR" 4 .IX Item "max-connections" This key requires a positive integer value. It defines the maximum number of connections allowed. A value of zero specifies an unlimited number of maximum connections (\f(CW\*(C`unlimited\*(C'\fR or \f(CW\*(C`none\*(C'\fR can be used as synonyms). The default is \f(CW0\fR. .IP "\fInolist\fR" 4 .IX Item "nolist" This key requires a boolean value. It defines whether a peer is allowed to issue list command. The default is false, that is to say it can. .IP "\fInoresendid\fR" 4 .IX Item "noresendid" This key requires a boolean value. It defines whether \fBinnd\fR should send \f(CW438\fR (response to \s-1CHECK\s0, in streaming mode) or \f(CW435\fR (response to \s-1IHAVE\s0 in non-streaming mode) responses instead of \f(CW431\fR (response to \s-1CHECK\s0) or \f(CW436\fR (response to \s-1IHAVE\s0) if a message is offered that is already received from another peer. The deferral feature can be useful for peers that resend messages right away, as \fBinnfeed\fR does. The default is false: the deferral feature is used so that the peer receives \f(CW431\fR and \f(CW436\fR codes, and therefore resends the article later. .IP "\fIpassword\fR" 4 .IX Item "password" This key requires a string value. It is used if you wish to require a peer to supply a password via \s-1AUTHINFO\s0 \s-1USER/PASS\s0. The default is an empty string, that it to say no password. .IP "\fIpatterns\fR" 4 .IX Item "patterns" This key requires a string value. It is a list of \fInewsfeeds\fR\|(5)\-style list of newsgroups which are to be accepted from this host. The default is the string \f(CW\*(C`*\*(C'\fR, that is to say all groups are accepted. .IP "\fIskip\fR" 4 .IX Item "skip" This key requires a boolean value. Setting this entry causes this peer to be skipped. The default is false. .IP "\fIstreaming\fR" 4 .IX Item "streaming" This key requires a boolean value. It defines whether streaming commands (\s-1CHECK\s0 and \s-1TAKETHIS\s0) are allowed from this peer. The default is true. .SH "HISTORY" .IX Header "HISTORY" Written by Fabien Tassin for InterNetNews. Converted to \&\s-1POD\s0 by Julien Elie. .PP \&\f(CW$Id:\fR incoming.conf.pod 9589 2013\-12\-19 17:47:33Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIinn.conf\fR\|(5), \fIinnd\fR\|(8), \fInewsfeeds\fR\|(5), \fIuwildmat\fR\|(3). inn-2.6.0/doc/man/motd.news.50000644000175200017520000001244512575023702015252 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "MOTD.NEWS 5" .TH MOTD.NEWS 5 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" motd.news \- Message of the day information for feeders or readers .SH "DESCRIPTION" .IX Header "DESCRIPTION" Two files, found in \fIpathetc\fR, contain local information for news feeders or news readers in a free-form format. The entire files are returned verbatim to any client that issues the \s-1LIST\s0 \s-1MOTD\s0 command. This might be used for new information, notification of upcoming downtime, or similar purposes. .PP These files are named \fImotd.innd\fR (for news feeders) and \fImotd.nnrpd\fR (for news readers); they are used by \fBinnd\fR and \fBnnrpd\fR, respectively. .PP Make sure that these files are encoded in \s-1UTF\-8\s0. They should also be \&\*(L"dot-stuffed\*(R", that is to say that a line beginning with a dot should have that dot doubled. .PP Be aware that use of the \s-1LIST\s0 \s-1MOTD\s0 command is not widespread (though documented in \s-1RFC\s0\ 6048) and most news clients will never ask for this file. .PP If one or both of these files are missing, it is not an error. The server will just send the client the appropriate response for an unmaintained \&\fImotd.innd\fR or \fImotd.nnrpd\fR file. .PP On a fresh \s-1INN\s0 install, samples for these files are installed in \fIpathec\fR; you will therefore find in \fImotd.innd.sample\fR and \&\fImotd.nnrpd.sample\fR a few examples of messages that can be communicated to news clients. .SH "HISTORY" .IX Header "HISTORY" Rewritten in \s-1POD\s0 by Russ Allbery for InterNetNews. .PP \&\f(CW$Id:\fR motd.news.pod 9767 2014\-12\-07 21:13:43Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIinn.conf\fR\|(5). inn-2.6.0/doc/man/filechan.80000644000175200017520000000671212575023702015110 0ustar iuliusiulius.\" $Revision: 5909 $ .TH FILECHAN 8 .SH NAME filechan \- file-writing backend for InterNetNews .SH SYNOPSIS .B filechan [ .BI \-d " directory" ] [ .BI \-f " num_fields" ] [ .BI \-m " mapfile" ] [ .BI \-p " pidfile" ] .SH DESCRIPTION .I Filechan reads lines from standard input and copies certain fields in each line into files named by other fields within the line. .I Filechan is intended to be called by .IR innd (8) as a channel feed. (It is not a full exploder and does not accept commands; see .IR newsfeeds (5) for a description of the difference, and .IR buffchan (8) for an exploder program.) .PP .I Filechan input is interpreted as a sequence of lines. Each line contains a fixed number of initial fields, followed by a variable number of filename fields. All fields in a line are separated by whitespace. The default number of initial fields is one. .PP For each line of input, .I filechan writes the initial fields, separated by whitespace and followed by a newline, to each of the files named in the filename fields. When writing to a file, .I filechan opens it in append mode and tries to lock it and change the ownership to the user and group who owns the directory where the file is being written. .PP Because the time window in which a file is open is very small, complicated flushing and locking protocols are not needed; a .IR mv (1) followed by a .IR sleep (1) for a couple of seconds is sufficient. .SH OPTIONS .TP .B \-f num_fields The ``\fB\-f\fP'' flag may be used to specify a different number of initial fields. .TP .B \-d directory By default, .I filechan writes its output into the directory .IR . The ``\fB\-d\fP'' flag may be used to specify a directory the program should change to before starting. .TP .B \-p pidfile If the ``\fB\-p\fP'' flag is used, the program will write a line containing its process ID (in text) to the specified file. .TP .B \-m mapfile A map file may be specified by using the ``\fB\-m\fP'' flag. Blank lines and lines starting with a number sign (``#'') are ignored. All other lines should have two host names separated by a colon. The first field is the name that may appear in the input stream; the second field names the file to be used when the name in the first field appears. For example, the following map file may be used to map the short names used in the example below to the full domain names: .PP .RS .nf # This is a comment uunet:news.uu.net foo:foo.com munnari:munnari.oz.au .fi .RE .SH EXAMPLES If .I filechan is invoked with ``\fB\-f 2\fP'' and given the following input: .PP .RS .nf news/software/b/132 <1643@munnari.oz.au> foo uunet news/software/b/133 <102060@litchi.foo.com> uunet munnari comp/sources/unix/2002 <999@news.foo.com> foo uunet munnari .fi .RE .PP Then the file .I foo will have these lines: .PP .RS .nf news/software/b/132 <1643@munnari.oz.au> comp/sources/unix/2002 <999@news.foo.com> .fi .RE .sp the file .I munnari will have these lines: .PP .RS .nf news/software/b/133 <102060@litchi.foo.com> comp/sources/unix/2002 <999@news.foo.com> .fi .RE .sp and the file .I uunet will have these lines: .PP .RS .nf news/software/b/132 <1643@munnari.oz.au> news/software/b/133 <102060@litchi.foo.com> comp/sources/unix/2002 <999@news.foo.com> .fi .RE .SH HISTORY Written by Robert Elz , flags added by Rich $alz . .de R$ This is revision \\$3, dated \\$4. .. .R$ $Id: filechan.8 5909 2002-12-03 05:17:18Z vinocur $ .SH "SEE ALSO" buffchan(8), inn.conf(5), innd(8), newsfeeds(5). inn-2.6.0/doc/man/qio.30000644000175200017520000002001412575023702014111 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "qio 3" .TH qio 3 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" qio \- Quick I/O routines for reading files .SH "SYNOPSIS" .IX Header "SYNOPSIS" .Vb 1 \& #include \& \& QIOSTATE *QIOopen(const char *name); \& \& QIOSTATE *QIOfdopen(int> I for InterNetNews. Updated by Russ Allbery . .PP \&\f(CW$Id:\fR qio.pod 9767 2014\-12\-07 21:13:43Z iulius $ inn-2.6.0/doc/man/innfeed.conf.50000644000175200017520000011051112575023702015661 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "INNFEED.CONF 5" .TH INNFEED.CONF 5 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" innfeed.conf \- Configuration file for innfeed .SH "DESCRIPTION" .IX Header "DESCRIPTION" The configuration file \fIinnfeed.conf\fR in \fIpathetc\fR is used to control the \fIinnfeed\fR\|(8) program. It is a fairly free-format file that consists of three types of entries: \fIkey\fR:\fIvalue\fR, \fIpeer\fR and \fIgroup\fR. Comments are from the hash character \f(CW\*(C`#\*(C'\fR to the end of the line. .PP \&\fIkey\fR:\fIvalue\fR entries are a keyword and a value separated by a colon (which can itself be surrounded by whitespace). For example: .PP .Vb 1 \& max\-connections: 10 .Ve .PP A legal \fIkey\fR starts with a letter and contains only letters, digits, and the \f(CW\*(C`_\*(C'\fR and \f(CW\*(C`\-\*(C'\fR characters. There are 5 different types of values: integers, floating-point numbers, characters, booleans, and strings. .PP Integer and floating-point numbers are as to be expected, except that exponents in floating-point numbers are not supported. A boolean value is either \f(CW\*(C`true\*(C'\fR or \f(CW\*(C`false\*(C'\fR (case is not significant). A character value is a single-quoted character as defined by the C\-language. A string value is any other sequence of characters. If the string needs to contain whitespace, then it must be quoted with double quotes, and uses the same format for embedding non-printing characters as normal C\-language string. .PP Peer entries look like: .PP .Vb 3 \& peer { \& # body ... \& } .Ve .PP The word \f(CW\*(C`peer\*(C'\fR is required. The \fI\fR is the same as the site name in \s-1INN\s0's \fInewsfeeds\fR configuration file. The body of a peer entry contains some number (possibly zero) of \fIkey\fR:\fIvalue\fR entries. .PP Group entries look like: .PP .Vb 3 \& group { \& # body ... \& } .Ve .PP The word \f(CW\*(C`group\*(C'\fR is required. The \fI\fR is any string valid as a key. The body of a group entry contains any number of the three types of entries. So \fIkey\fR:\fIvalue\fR pairs can be defined inside a group, and peers can be nested inside a group, and other groups can be nested inside a group. .PP \&\fIkey\fR:\fIvalue\fR entries that are defined outside of all \fIpeer\fR and \fIgroup\fR entries are said to be at \*(L"global scope\*(R". There are global \fIkey\fR:\fIvalue\fR entries that apply to the process as a whole (for example the location of the backlog file directory), and there are global \fIkey\fR:\fIvalue\fR entries that act as defaults for peers. When \fBinnfeed\fR looks for a specific value in a peer entry (for example, the maximum number of connections to set up), if the value is not defined in the peer entry, then the enclosing groups are examined for the entry (starting at the closest enclosing group). If there are no enclosing groups, or the enclosing groups do not define the \fIkey\fR:\fIvalue\fR, then the value at global scope is used. .PP A small example could be: .PP .Vb 3 \& # Global value applied to all peers that have \& # no value of their own. \& max\-connections: 5 \& \& # A peer definition. "uunet" is the name used by innd \& # in the newsfeeds configuration file. \& peer uunet { \& ip\-name: usenet1.uu.net \& } \& \& peer vixie { \& ip\-name: gw.home.vix.com \& max\-connections: 10 # Override global value. \& } \& \& # A group of two peers which can handle more connections \& # than normal. \& group fast\-sites { \& max\-connections: 15 \& \& # Another peer. The "max\-connections" value from the \& # "fast\-sites" group scope is used. The "ip\-name" value \& # defaults to the peer\*(Aqs name. \& peer data.ramona.vix.com { \& } \& \& peer bb.home.vix.com { \& max\-connections: 20 # He can really cook. \& } \& } .Ve .PP Given the above configuration file, the defined peers would have the following values for the \fImax-connections\fR key: .PP .Vb 4 \& uunet 5 \& vixie 10 \& data.ramona.vix.com 15 \& bb.home.vix.com 20 .Ve .PP \&\fBinnfeed\fR ignores \fIkey\fR:\fIvalue\fR pairs it is not interested in. Some configuration file values can be set via a command-line option, in which case that setting overrides the settings in the file. .PP Configuration files can be included in other configuration files via the syntax: .PP .Vb 1 \& $INCLUDE filename .Ve .PP There is a maximum nesting depth of 10. .PP For a fuller example configuration file, see the supplied \fIinnfeed.conf\fR. .SH "GLOBAL VALUES" .IX Header "GLOBAL VALUES" The following listing show all the keys that apply to the process as whole. These are not required (compiled-in defaults are used where needed). .IP "\fInews-spool\fR" 4 .IX Item "news-spool" This key requires a pathname value and defaults to \fIpatharticles\fR in \fIinn.conf\fR. It specifies where the top of the article spool is. This corresponds to the \fB\-a\fR command-line option. .IP "\fIinput-file\fR" 4 .IX Item "input-file" This key requires a pathname value. It specifies the pathname (relative to the \fIbacklog-directory\fR value) that should be read in funnel-file mode. This corresponds to giving a filename as an argument on the command-line (i.e. its presence also implies that funnel-file mode should be used). .Sp The default is unset; \fBinnfeed\fR then runs in channel or batch mode. .IP "\fIpid-file\fR" 4 .IX Item "pid-file" This key requires a pathname value and defaults to \fIinnfeed.pid\fR. It specifies the pathname (relative to \fIpathrun\fR in \fIinn.conf\fR) where the pid of the \fBinnfeed\fR process should be stored. This corresponds to the \fB\-p\fR command-line option. .IP "\fIdebug-level\fR" 4 .IX Item "debug-level" This key defines the debug level for the process. Default is \f(CW0\fR. A non-zero number generates a lot of messages to stderr, or to the config-defined \fIlog-file\fR. This corresponds to the \fB\-d\fR command-line option. .Sp If a file named \fIinnfeed.debug\fR exists in the \fIpathlog\fR directory (as set in \fIinn.conf\fR), then \fIdebug-level\fR is automatically set to \f(CW1\fR. This is a cheap way of avoiding continual reloading of the \fInewsfeeds\fR file when debugging. Note that debug messages still go to \fIlog-file\fR. .IP "\fIdebug-shrinking\fR" 4 .IX Item "debug-shrinking" This key requires a boolean value and defaults to false (the debug file is allowed to grow without bound). If set to true, this file is truncated when its size reaches a certain limit. See \fIbacklog-limit\fR for more details. .IP "\fIinitial-sleep\fR" 4 .IX Item "initial-sleep" This key requires a positive integer. The default value is \f(CW2\fR. It defines the number of seconds to wait when \fBinnfeed\fR (or a fork) starts, before beginning to open connections to remote hosts. .IP "\fIfast-exit\fR" 4 .IX Item "fast-exit" This key requires a boolean value and defaults to false. If set to true, when \fBinnfeed\fR receives a \s-1SIGTERM\s0 or \s-1SIGQUIT\s0 signal, it will close its listeners as soon as it can, even if it means dropping articles. .IP "\fIuse-mmap\fR" 4 .IX Item "use-mmap" This key requires a boolean value and defaults to true. When \fBinnfeed\fR is given file names to send (a fairly rare use case) instead of storage \&\s-1API\s0 tokens, it specifies whether mmaping should be used if \fBinnfeed\fR has been built with \fImmap\fR\|(2) support. If article data on disk is not in NNTP-ready format (\s-1CR/LF\s0 at the end of each line), then after mmaping, the article is read into memory and fixed up, so mmaping has no positive effect (and possibly some negative effect depending on your system), and so in such a case this value should be \f(CW\*(C`false\*(C'\fR, which corresponds to the \fB\-M\fR command-line option. .IP "\fIlog-file\fR" 4 .IX Item "log-file" This key requires a pathname value and defaults to \fIinnfeed.log\fR. It specifies where any logging messages that could not be sent via \fIsyslog\fR\|(3) should go (such as those generated when a positive value for \fIdebug-value\fR is used). This corresponds to the \fB\-l\fR command-line option. .Sp This pathname is relative to \fIpathlog\fR in \fIinn.conf\fR. .IP "\fIlog-time-format\fR" 4 .IX Item "log-time-format" This key requires a format string suitable for \fIstrftime\fR\|(3). It is used for messages sent via \fIsyslog\fR\|(3) and to the \fIstatus-file\fR. Default value is \f(CW\*(C`%a %b %d %H:%M:%S %Y\*(C'\fR. .IP "\fIbacklog-directory\fR" 4 .IX Item "backlog-directory" This key requires a pathname value and defaults to \fIinnfeed\fR. It specifies where the current \fBinnfeed\fR process should store backlog files. This corresponds to the \fB\-b\fR command-line option. .Sp This pathname is relative to \fIpathspool\fR in \fIinn.conf\fR. .IP "\fIbacklog-highwater\fR" 4 .IX Item "backlog-highwater" This key requires a positive integer value and defaults to \f(CW5\fR. It specifies how many articles should be kept on the backlog file queue before starting to write new entries to disk. .IP "\fIbacklog-ckpt-period\fR" 4 .IX Item "backlog-ckpt-period" This key requires a positive integer value and defaults to \f(CW30\fR. It specifies how many seconds elapse between checkpoints of the input backlog file. Too small a number will mean frequent disk accesses; too large a number will mean after a crash, \fBinnfeed\fR will re-offer more already-processed articles than necessary. .IP "\fIbacklog-newfile-period\fR" 4 .IX Item "backlog-newfile-period" This key requires a positive integer value and defaults to \f(CW600\fR. It specifies how many seconds elapse before each check for externally generated backlog files that are to be picked up and processed. .IP "\fIbacklog-rotate-period\fR" 4 .IX Item "backlog-rotate-period" This key requires a positive integer value and defaults to \f(CW60\fR. It specifies how many seconds elapse before \fBinnfeed\fR checks for a manually created backlog file and moves the output backlog file to the input backlog file. .IP "\fIdns-retry\fR" 4 .IX Item "dns-retry" This key requires a positive integer value and defaults to \f(CW900\fR. It defines the number of seconds between attempts to re-lookup host information that previously failed to be resolved. .IP "\fIdns-expire\fR" 4 .IX Item "dns-expire" This key requires a positive integer value and defaults to \f(CW86400\fR. It defines the number of seconds between refreshes of name to address \&\s-1DNS\s0 translation. This is so long-running processes do not get stuck with stale data, should peer \s-1IP\s0 addresses change. .IP "\fIgen-html\fR" 4 .IX Item "gen-html" This key requires a boolean value and defaults to false. It specifies whether the \fIstatus-file\fR should be HTML-ified. .IP "\fIstatus-file\fR" 4 .IX Item "status-file" This key requires a pathname value and defaults to \fIinnfeed.status\fR. An absolute pathname can be used. It specifies the pathname (relative to \fIpathhttp\fR when \fIgen-html\fR is true; otherwise, \fIpathlog\fR as set in \&\fIinn.conf\fR) where the periodic status of the \fBinnfeed\fR process should be stored. This corresponds to the \fB\-S\fR command-line option. .IP "\fIconnection-stats\fR" 4 .IX Item "connection-stats" This key requires a boolean value and defaults to false. If the value is true, then whenever the transmission statistics for a peer are logged, each active connection logs its own statistics. This corresponds to the \&\fB\-z\fR command-line option. .IP "\fIhost-queue-highwater\fR" 4 .IX Item "host-queue-highwater" This key requires a positive integer value and defaults to \f(CW10\fR. It defines how many articles will be held internally for a peer before new arrivals cause article information to be spooled to the backlog file. .IP "\fIstats-period\fR" 4 .IX Item "stats-period" This key requires a positive integer value and defaults to \f(CW600\fR. It defines how many seconds \fBinnfeed\fR waits between generating statistics on transfer rates. .IP "\fIstats-reset\fR" 4 .IX Item "stats-reset" This key requires a positive integer value and defaults to \f(CW43200\fR. It defines how many seconds \fBinnfeed\fR waits before resetting all internal transfer counters back to zero (after logging one final time). This is so a \fBinnfeed\fR process running more than a day will generate \*(L"final\*(R" stats that will be picked up by logfile processing scripts. .IP "\fIinitial-reconnect-time\fR" 4 .IX Item "initial-reconnect-time" This key requires a positive integer value and defaults to \f(CW30\fR. It defines how many seconds to first wait before retrying to reconnect after a connection failure. If the next attempt fails too, then the reconnect time is approximately doubled until the connection succeeds, or \fImax-reconnect-time\fR is reached. .IP "\fImax-reconnect-time\fR" 4 .IX Item "max-reconnect-time" This key requires an integer value and defaults to \f(CW3600\fR. It defines the maximum number of seconds to wait between attempt to reconnect to a peer. The initial value for reconnection attempts is defined by \&\fIinitial-reconnect-time\fR, and it is doubled after each failure, up to this value. .IP "\fIstdio-fdmax\fR" 4 .IX Item "stdio-fdmax" This key requires a non-negative integer value and defaults to \f(CW0\fR. If the value is greater than zero, then whenever a network socket file descriptor is created and it has a value \fIless than\fR this, the file descriptor will be dup'ed to bring the value up greater than this. This is to leave lower numbered file descriptors free for stdio. Certain systems, Sun's in particular, require this. SunOS 4.1.x usually requires a value of 128 and Solaris requires a value of 256. The default if this is not specified, is \f(CW0\fR. .SS "Special keys for \fBimapfeed\fP" .IX Subsection "Special keys for imapfeed" The following keys are used with \fBimapfeed\fR to authenticate to a remote host. Several parameters may be included at global scope: .IP "\fIdeliver-authname\fR" 4 .IX Item "deliver-authname" The authname is who you want to authenticate as. .IP "\fIdeliver-password\fR" 4 .IX Item "deliver-password" This is the appropriate password for authname. .IP "\fIdeliver-username\fR" 4 .IX Item "deliver-username" The username is who you want to \*(L"act\*(R" as, that is, who is actually going to be using the server. .IP "\fIdeliver-realm\fR" 4 .IX Item "deliver-realm" In this case, the \*(L"realm\*(R" is the realm in which the specified authname is valid. Currently this is only needed by the \s-1DIGEST\-MD5\s0 \s-1SASL\s0 mechanism. .IP "\fIdeliver-rcpt-to\fR" 4 .IX Item "deliver-rcpt-to" A \fIprintf\fR\|(3)\-style format string for creating the envelope recipient address. The pattern \s-1MUST\s0 include a single string specifier which will be replaced with the newgroup (e.g. \f(CW\*(C`bb+%s\*(C'\fR). The default is \f(CW\*(C`+%s\*(C'\fR. .IP "\fIdeliver-to-header\fR" 4 .IX Item "deliver-to-header" An optional \fIprintf\fR\|(3)\-style format string for creating a To: header field to be prepended to the article. The pattern \s-1MUST\s0 include a single string specifier which will be replaced with the newgroup (e.g. \f(CW\*(C`post+%s@domain\*(C'\fR). If not specified, the To: header field will not be prepended. .SH "GLOBAL PEER DEFAULTS" .IX Header "GLOBAL PEER DEFAULTS" All the \fIkey\fR:\fIvalue\fR pairs mentioned in this section can be specified at global scope. They may also be specified inside a group or peer definition. Note that when peers are added dynamically (i.e. when \fBinnfeed\fR receives an article for an unspecified peer), it will add the peer site using the parameters specified at global scope. .SS "Required keys" .IX Subsection "Required keys" No keys are currently required. They all have a default value, if not present in the configuration file. .SS "Optional keys" .IX Subsection "Optional keys" The following keys are optional: .IP "\fIarticle-timeout\fR" 4 .IX Item "article-timeout" This key requires a non-negative integer value. The default value is \f(CW600\fR. If no articles need to be sent to the peer for this many seconds, then the peer is considered idle and all its active connections are torn down. .IP "\fIresponse-timeout\fR" 4 .IX Item "response-timeout" This key requires a non-negative integer value. The default value is \f(CW300\fR. It defines the maximum amount of time to wait for a response from the peer after issuing a command. .IP "\fIinitial-connections\fR" 4 .IX Item "initial-connections" This key requires a non-negative integer value. The default value is \f(CW1\fR. It defines the number of connections to be opened immediately when setting up a peer binding. A value of \f(CW0\fR means no connections will be created until an article needs to be sent. .IP "\fImax-connections\fR" 4 .IX Item "max-connections" This key requires a positive integer value. The default value is \f(CW2\fR but may be increased if needed or for large feeds. It defines the maximum number of connections to run in parallel to the peer. A value of \f(CW0\fR specifies an unlimited number of maximum connections. In general, use of an unlimited number of maximum connections is not recommended. Do not ever set \fImax-connections\fR to zero with \fIdynamic-method\fR 0 set, as this will saturate peer hosts with connections. .IP "\fIclose-period\fR" 4 .IX Item "close-period" This key requires a positive integer value and defaults to \f(CW86400\fR. It is the maximum number of seconds a connection should be kept open. Some \s-1NNTP\s0 servers do not deal well with connections being held open for long periods. .IP "\fIdynamic-method\fR" 4 .IX Item "dynamic-method" This key requires an integer value between 0 and 3. The default value is \f(CW3\fR. It controls how connections are opened, up to the maximum specified by \&\fImax-connections\fR. In general (and specifically, with \fIdynamic-method\fR 0), a new connection is opened when the current number of connections is below \&\fImax-connections\fR, and an article is to be sent while no current connections are idle. Without further restraint (i.e. using \fIdynamic-method\fR 0), in practice this means that \fImax-connections\fR connections are established while articles are being sent. Use of other \fIdynamic-method\fR settings imposes a further limit on the amount of connections opened below that specified by \fImax-connections\fR. This limit is calculated in different ways, depending of the value of \fIdynamic-method\fR. .Sp Users should note that adding additional connections is not always productive \&\-\-\ just because opening twice as many connections results in a small percentage increase of articles accepted by the remote peer, this may be at considerable resource cost both locally and at the remote site, whereas the remote site might well have received the extra articles sent from another peer a fraction of a second later. Opening large numbers of connections is considered antisocial. .Sp The meanings of the various settings are: .RS 4 .IP "\fB0\fR (no method)" 2 .IX Item "0 (no method)" Increase of connections up to \fImax-connections\fR is unrestrained. .IP "\fB1\fR (maximize articles per second)" 2 .IX Item "1 (maximize articles per second)" Connections are increased (up to \fImax-connections\fR) and decreased so as to maximize the number of articles per second sent, while using the fewest connections to do this. .IP "\fB2\fR (set target queue length)" 2 .IX Item "2 (set target queue length)" Connections are increased (up to \fImax-connections\fR) and decreased so as to keep the queue of articles to be sent within the bounds set by \fIdynamic-backlog-low\fR and \fIdynamic-backlog-high\fR, while using the minimum resources possible. As the queue will tend to fill if the site is not keeping up, this method ensures that the maximum number of articles are offered to the peer while using the minimum number of connections to achieve this. .IP "\fB3\fR (combination)" 2 .IX Item "3 (combination)" This method uses a combination of methods 1 and 2 above. For sites accepting a large percentage of articles, method 2 will be used to ensure these sites are offered as complete a feed as possible. For sites accepting a small percentage of articles, method 1 is used, to minimize remote resource usage. For intermediate sites, an appropriate combination is used. .RE .RS 4 .RE .IP "\fIdynamic-backlog-low\fR" 4 .IX Item "dynamic-backlog-low" This key requires a floating-point value between 0 and 100. It represents (as a percentage) the low water mark for the host queue. If the host queue falls below this level while using \fIdynamic-method\fR 2 or 3, and if 2 or more connections are open, \fBinnfeed\fR will attempt to drop connections to the host. An Infinite Impulse Response (\s-1IIR\s0) filter is applied to the value to prevent connection flap (see \fIdynamic-filter\fR). The default value is \f(CW20.0\fR. This value must be smaller than \fIdynamic-backlog-high\fR. .IP "\fIdynamic-backlog-high\fR" 4 .IX Item "dynamic-backlog-high" This key requires a floating-point value between 0 and 100. It represents (as a percentage) the high water mark for the host queue. If the host queue rises above this level while using \fIdynamic-method\fR 2 or 3, and if less than \fImax-connections\fR are open to the host, \fBinnfeed\fR will attempt to open further connections to the host. An Infinite Impulse Response (\s-1IIR\s0) filter is applied to the value to prevent connection flap (see \fIdynamic-filter\fR). The default value is \f(CW50.0\fR. This value must be larger than \fIdynamic-backlog-low\fR. .IP "\fIdynamic-backlog-filter\fR" 4 .IX Item "dynamic-backlog-filter" This key requires a floating-point value between 0 and 1. It represents the filter coefficient used by the Infinite Impulse Response (\s-1IIR\s0) filter used to implement \fIdynamic-method\fR 2 and 3. The default value of this filter is \f(CW0.7\fR, giving a time constant of 1/(1\-0.7) articles. Higher values will result in slower response to queue fullness changes; lower values in faster response. .IP "\fImax-queue-size\fR" 4 .IX Item "max-queue-size" This key requires a positive integer value. The default value is \f(CW20\fR. It defines the maximum number of articles to process at one time when using streaming to transmit to a peer. Larger numbers mean more memory consumed as articles usually get pulled into memory (see the description of \fIuse-mmap\fR). .IP "\fIstreaming\fR" 4 .IX Item "streaming" This key requires a boolean value. Its default value is true. It defines whether streaming commands are used to transmit articles to the peers. .IP "\fIno-check-high\fR" 4 .IX Item "no-check-high" This key requires a floating-point number which must be in the range [0.0, 100.0]. When running transmitting with the streaming commands, \&\fBinnfeed\fR attempts an optimization called \*(L"no-CHECK mode\*(R". This involves \&\fInot\fR asking the peer if it wants the article, but just sending it. This optimization occurs when the percentage of the articles the peer has accepted gets larger than this number. If this value is set to \f(CW100.0\fR, then this effectively turns off no-CHECK mode, as the percentage can never get above 100.0. If this value is too small, then the number of articles the peer rejects will get bigger (and your bandwidth will be wasted). The default value of \f(CW95.0\fR usually works pretty well. .IP "\fIno-check-low\fR" 4 .IX Item "no-check-low" This key requires a floating-point number which must be in the range [0.0, 100.0], and it must be smaller that the value for \fIno-check-high\fR. When running in no-CHECK mode, as described above, if the percentage of articles the remote server accepts drops below this number, then the no-CHECK optimization is turned off until the percentage gets above the \&\fIno-check-high\fR value again. If there is small difference between this and the \fIno-check-high\fR value (less than about 5.0), then \fBinnfeed\fR may frequently go in and out of no-CHECK mode. If the difference is too big, then it will make it harder to get out of no-CHECK mode when necessary (wasting bandwidth). Keeping this to between 5.0 and 10.0 less than \&\fBno-check-high\fR usually works pretty well. The default value is \f(CW90.0\fR. .IP "\fIno-check-filter\fR" 4 .IX Item "no-check-filter" This is a floating-point value representing the time constant, in articles, over which the CHECK/no\-CHECK calculations are done. The default value is \&\f(CW50.0\fR, which will implement an Infinite Impulse Response (\s-1IIR\s0) filter of time constant 50. This roughly equates to making a decision about the mode over the previous 50 articles. A higher number will result in a slower response to changing percentages of articles accepted; a lower number will result in a faster response. .IP "\fIport-number\fR" 4 .IX Item "port-number" This key requires a positive integer value. It defines the \s-1TCP/IP\s0 port number to use when connecting to the remote. Usually, port number 119 is used, which is the default value. .IP "\fIforce\-ipv4\fR" 4 .IX Item "force-ipv4" This key requires a boolean value. By default, it is set to false. Setting it to true is the same as setting \fIbindaddress6\fR to \f(CW\*(C`none\*(C'\fR and removing \fIbindaddress\fR from \f(CW\*(C`none\*(C'\fR if it was set. .IP "\fIdrop-deferred\fR" 4 .IX Item "drop-deferred" This key requires a boolean value. By default, it is set to false. When set to true, and a peer replies with code 431 or 436 (try again later), \&\fBinnfeed\fR just drops the article and does not try to re-send it. This is useful for some peers that keep on deferring articles for a long time to prevent \fBinnfeed\fR from trying to offer the same article over and over again. .IP "\fImin-queue-connection\fR" 4 .IX Item "min-queue-connection" This key requires a boolean value. By default, it is set to false. When set to true, \fBinnfeed\fR will attempt to use a connection with the least queue size (or the first empty connection). If this key is set to true, it is recommended that \fIdynamic-method\fR be set to \f(CW0\fR. This allows for article propagation with the least delay. .IP "\fIno-backlog\fR" 4 .IX Item "no-backlog" This key requires a boolean value. It specifies whether spooling should be enabled (false, the default) or disabled (true). Note that when \&\fIno-backlog\fR is set, articles reported as spooled are actually silently discarded. .IP "\fIbacklog-limit\fR" 4 .IX Item "backlog-limit" This key requires a non-negative integer value. If the number is \f(CW0\fR (the default), then backlog files are allowed to grow without bound when the peer is unable to keep up with the article flow. If this number is greater than 0, then it specifies the size (in bytes) the backlog file should get truncated to when the backlog file reaches a certain limit. The limit depends on whether \fIbacklog-factor\fR or \fIbacklog-limit-highwater\fR is used. .Sp This parameter also applies to the debug file when \fIdebug-shrinking\fR is set to true, and has the same effect on this file as the one has on backlog files. .IP "\fIbacklog-factor\fR" 4 .IX Item "backlog-factor" This key requires a floating-point value, which must be larger than 1.0. It is used in conjunction with the peer key \fIbacklog-limit\fR. If \fIbacklog-limit\fR has a value greater than zero, then when the backlog file gets larger than the value \fIbacklog-limit\fR * \fIbacklog-factor\fR, then the backlog file will be truncated to the size \fIbacklog-limit\fR. .Sp For example, if \fIbacklog-limit\fR has a value of \f(CW1000000\fR, and \&\fIbacklog-factor\fR has a value of \f(CW2.0\fR, then when the backlog file gets to be larger than 2000000 bytes in size, it will be truncated to 1000000 bytes. The front portion of the file is removed, and the trimming happens on line boundaries, so the final size may be a bit less than this number. If \fIbacklog-limit-highwater\fR is defined too, then \fIbacklog-factor\fR takes precedence. The default value of \fIbacklog-factor\fR is \f(CW1.1\fR. .Sp This parameter also applies to the debug file when \fIdebug-shrinking\fR is set to true, and has the same effect on this file as the one has on backlog files. .IP "\fIbacklog-limit-highwater\fR" 4 .IX Item "backlog-limit-highwater" This key requires a positive integer value that must be larger than the value for \fIbacklog-limit\fR. The default value is \f(CW0\fR. .Sp If the size of the backlog file gets larger than this value (in bytes), then the backlog file will be shrunk down to the size of \fIbacklog-limit\fR. If both \fIbacklog-factor\fR and \fIbacklog-limit-highwater\fR are defined, then the value of \fIbacklog-factor\fR is used. .Sp This parameter also applies to the debug file when \fIdebug-shrinking\fR is set to true, and has the same effect on this file as the one has on backlog files. .IP "\fIbacklog-feed-first\fR" 4 .IX Item "backlog-feed-first" This key requires a boolean value. By default it is set to false. When set to true, the backlog is fed before new files. This is intended to enforce in-order delivery, so setting this to true when \fIinitial-connections\fR or \fImax-connections\fR is more than 1 is inconsistent. .IP "\fIbindaddress\fR" 4 .IX Item "bindaddress" This key requires a string value. It specifies which outgoing IPv4 address \&\fBinnfeed\fR should bind the local end of its connection to. It must be an IPv4 address in dotted-quad format (nnn.nnn.nnn.nnn), \f(CW\*(C`any\*(C'\fR, or \f(CW\*(C`none\*(C'\fR. If not set or set to \f(CW\*(C`any\*(C'\fR, \fBinnfeed\fR defaults to letting the kernel choose this address. If set to \f(CW\*(C`none\*(C'\fR, \fBinnfeed\fR will not use IPv4 for outgoing connections to peers in this scope (i.e. it forces IPv6). .Sp If not set in \fIinnfeed.conf\fR, \fBinnfeed\fR defaults to the value of \&\fIsourceaddress\fR from \fIinn.conf\fR (which by default is unset). .IP "\fIbindaddress6\fR" 4 .IX Item "bindaddress6" This key requires a string value. It behaves like \fIbindaddress\fR except for outgoing IPv6 connections. It must be in numeric IPv6 format (note that a value containing colons must be enclosed in double quotes), \f(CW\*(C`any\*(C'\fR, or \f(CW\*(C`none\*(C'\fR. If set to \f(CW\*(C`none\*(C'\fR, \fBinnfeed\fR will not use IPv6 for outgoing connections to peers in this scope. .Sp If not set in \fIinnfeed.conf\fR, \fBinnfeed\fR defaults to the value of \&\fIsourceaddress6\fR from \fIinn.conf\fR (which by default is unset). .IP "\fIusername\fR" 4 .IX Item "username" This key requires a string value. If the value is defined, then \fBinnfeed\fR tries to authenticate by \s-1AUTHINFO\s0 \s-1USER\s0 and this value used for user name. \&\fIpassword\fR must also be defined, if this key is defined. .IP "\fIpassword\fR" 4 .IX Item "password" This key requires a string value. The value is the password used for \&\s-1AUTHINFO\s0 \s-1PASS\s0. \fIusername\fR must also be defined, if this key is defined. .SH "PEER VALUES" .IX Header "PEER VALUES" As previously explained, the peer definitions can contain redefinitions of any of the \fIkey\fR:\fIvalue\fR pairs described in the section about global peer defaults above. There is one \fIkey\fR:\fIvalue\fR pair that is specific to a peer definition. .IP "\fIip-name\fR" 4 .IX Item "ip-name" This key requires a word value. The word is the host's \s-1FQDN\s0, or the dotted quad IP-address. If this value is not specified, then the name of the peer in the enclosing \fIpeer\fR block is taken to also be its \fIip-name\fR. .SH "RELOADING" .IX Header "RELOADING" If \fBinnfeed\fR gets a \s-1SIGHUP\s0 signal, then it will reread the configuration file. All values at global scope except for \fIbacklog-directory\fR can be changed (although note that \fIbindaddress\fR and \fIbindaddress6\fR changes will only affect new connections). .PP Any new peers are added and any missing peers have their connections closed. .PP The log file is also reopened. .SH "EXAMPLE" .IX Header "EXAMPLE" For a comprehensive example, see the sample \fIinnfeed.conf\fR distributed with \s-1INN\s0 and installed as a starting point. .PP Here are examples of how to format values: .PP .Vb 12 \& eg\-string: "New\etconfig\etfile\en" \& eg\-long\-string: "A long string that goes \& over multiple lines. The \& newline is kept in the \& string except when quoted \& with a backslash \e \& as here." \& eg\-simple\-string: A\-no\-quote\-string \& eg\-integer: 10 \& eg\-boolean: true \& eg\-char: \*(Aqa\*(Aq \& eg\-ctrl\-g: \*(Aq\e007\*(Aq .Ve .SH "HISTORY" .IX Header "HISTORY" Written by James Brister for InterNetNews. Converted to \&\s-1POD\s0 by Julien Elie. .PP Earlier versions of \fBinnfeed\fR (up to 0.10.1) were shipped separately; \&\fBinnfeed\fR is now part of \s-1INN\s0 and shares the same version number. Please note that the \fIinnfeed.conf\fR format has changed dramatically since version\ 0.9.3. .PP \&\f(CW$Id:\fR innfeed.conf.pod 9923 2015\-07\-14 16:48:11Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIinn.conf\fR\|(5), \fIinnfeed\fR\|(8), \fInewsfeeds\fR\|(5). inn-2.6.0/doc/man/nntpget.10000644000175200017520000000415312575023702015004 0ustar iuliusiulius.\" $Revision: 5909 $ .TH NNTPGET 1 .SH NAME nntpget \- get Usenet articles from a remote NNTP server .SH SYNOPSIS .I nntpget [ .BI \-d " dist" ] [ .BI \-f " file" ] [ .BI \-n " newsgroups" ] [ .BI \-t " timestring" ] [ .B \-o ] [ .BI \-u " file" ] [ .B \-v ] .I host .SH DESCRIPTION .I Nntpget connects to the NNTP server at the specified .I host and retrieves articles from it. The Message-ID's of the desired articles are read from standard input. The articles are sent to standard output. .SH OPTIONS .TP .B \-o The ``\-o'' option may be used only if the command is executed on the host where the .IR innd (8) server is running. If this option is used, .I nntpget connects to the specified remote .I host to retrieve articles. Any article not present in the local .I history database is then fetched from the remote site and offered to the local server. .TP .B \-v If the ``\fB\-v\fP'' option is used with the ``\fB\-o\fP'' option then the Message-ID of each article will be sent to standard output as it is processed. .TP .B \-f The list of article Message-ID's is normally read from standard input. If the ``\fB\-f\fP'' option is used, then a ``newnews'' command is used to retrieve all articles newer then the modification date of the specified .IR file . .TP .B \-u The ``\fB\-u\fP'' option is like ``\fB\-f\fP'' except that if the transfer succeedes, the file will be updated with a statistics line, modifying its timestamp so that it can be used in later invocations. .TP .B \-t If the ``\-t'' option is used, then the specified .I timestring is used as the time and date parameter to the ``newnews'' command. .TP .B \-n If either the ``\fB\-u\fP'' or ``\fB\-f\fP'' options are used, then the ``\fB\-n\fP'' option may be used to specify a newsgroup list. The default is ``*''. .TP .B \-d The ``\fB\-d\fP'' option may be used to specify a distribution list when using the ``\fB\-u\fP'' or ``\fB\-f\fP'' options. The default is no distribution list. .SH HISTORY Written by Rich $alz for InterNetNews. .de R$ This is revision \\$3, dated \\$4. .. .R$ $Id: nntpget.1 5909 2002-12-03 05:17:18Z vinocur $ .SH "SEE ALSO" innd(8). inn-2.6.0/doc/man/ovdb_monitor.80000644000175200017520000001140212575023702016030 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "OVDB_MONITOR 8" .TH OVDB_MONITOR 8 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" ovdb_monitor \- Database maintenance .SH "SYNOPSIS" .IX Header "SYNOPSIS" Use \f(CW\*(C`ovdb_init\*(C'\fR to start ovdb_monitor .SH "DESCRIPTION" .IX Header "DESCRIPTION" When started (by \f(CW\*(C`ovdb_init\*(C'\fR), \f(CW\*(C`ovdb_monitor\*(C'\fR forks three processes that perform routine database maintenance tasks. These are: transaction checkpointing, deadlock detection, and transaction log removal. The process \s-1ID\s0 of the parent is written to \&\fI\fIpathrun\fI/ovdb_monitor.pid\fR. This \s-1PID\s0 is used by other \s-1INN\s0 commands to verify that ovdb_monitor is running. .PP To shut down ovdb_monitor, send a \s-1TERM\s0 signal to the process \s-1ID\s0 in \fI\fIpathrun\fI/ovdb_monitor.pid\fR . The parent process will shut down the three children and wait for their exit before exiting itself. .SH "HISTORY" .IX Header "HISTORY" Written by Heath Kehoe for InterNetNews. .PP \&\f(CW$Id:\fR ovdb_monitor.pod 9765 2014\-12\-07 21:07:34Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIovdb\fR\|(5), \fIovdb_init\fR\|(8) inn-2.6.0/doc/man/makedbz.80000644000175200017520000001353612575023702014756 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "MAKEDBZ 8" .TH MAKEDBZ 8 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" makedbz \- Rebuild dbz files .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fBmakedbz\fR [\fB\-io\fR] [\fB\-f\fR \fIfilename\fR] [\fB\-s\fR \fIsize\fR] .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\fBmakedbz\fR rebuilds \fIdbz\fR\|(3) database. The default name of the text file is \fIpathdb\fR/history; to specify a different name, use the \fB\-f\fR flag. .SH "OPTIONS" .IX Header "OPTIONS" .IP "\fB\-f\fR \fIfilename\fR" 4 .IX Item "-f filename" If the \fB\-f\fR flag is used, then the database files are named \f(CW\*(C`\f(CIfilename\f(CW.dir\*(C'\fR, \&\f(CW\*(C`\f(CIfilename\f(CW.index\*(C'\fR, and \f(CW\*(C`\f(CIfilename\f(CW.hash\*(C'\fR. If the \fB\-f\fR flag is not used, then a temporary link to the name \f(CW\*(C`history.n\*(C'\fR is made and the database files are written as \f(CW\*(C`history.n.index\*(C'\fR , \f(CW\*(C`history.n.hash\*(C'\fR and \f(CW\*(C`history.n.dir\*(C'\fR. .IP "\fB\-i\fR" 4 .IX Item "-i" To ignore the old database, use the \fB\-i\fR flag. Using the \fB\-o\fR or \fB\-s\fR flags implies the \fB\-i\fR flag. .IP "\fB\-o\fR" 4 .IX Item "-o" If the \fB\-o\fR flag is used, then the temporary link to \f(CW\*(C`history.n\*(C'\fR (or the name specified by the \fB\-f\fR flag) is not made and any existing history files are overwritten. If the old database exists, \fBmakedbz\fR will use it to determine the size of the new database. .IP "\fB\-s\fR \fIsize\fR" 4 .IX Item "-s size" \&\fBmakedbz\fR will also ignore any old database if the \fB\-s\fR flag is used to specify the approximate number of entries in the new database. Accurately specifying the size is an optimization that will create a more efficient database. Size is measured in key-value pairs (i.e. lines). (The size should be the estimated eventual size of the file, typically the size of the old file.) .Sp For more information, see the discussion of \fBdbzfresh\fR and \fBdbzsize\fR in \fIdbz\fR\|(3). .SH "HISTORY" .IX Header "HISTORY" Written by Katsuhiro Kondou for InterNetNews. Converted to \&\s-1POD\s0 by Julien Elie. .PP \&\f(CW$Id:\fR makedbz.pod 9934 2015\-08\-28 19:28:56Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIdbz\fR\|(3), \fIhistory\fR\|(5). inn-2.6.0/doc/man/inews.10000644000175200017520000002240212575023702014447 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "INEWS 1" .TH INEWS 1 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" inews \- Post a Usenet article to the local news server .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fBinews\fR [\fB\-ADhNORSVW\fR] [\fB\-acdeFfnortwx\fR \fIvalue\fR] [\fB\-p\fR \fIport\fR] [\fIfile\fR] .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\fBinews\fR reads a Usenet news article, perhaps with headers, from \fIfile\fR or standard input if no file is given. It adds some headers and performs some consistency checks. If the article does not meet those checks, the article is rejected. If it passes the checks, \fBinews\fR sends the article to the local news server as specified in \fIinn.conf\fR. .PP By default, if a file named \fI.signature\fR exists in the home directory of the posting user, it is appended to the post, preceded by a line that contains only \f(CW\*(C`\-\- \*(C'\fR. Signatures are not allowed to be more than four lines long. .PP Cancel messages can only be posted with \fBinews\fR if the sender of the cancel message matches the sender of the original message being cancelled. The same check is also applied to Supersedes. Sender in this case means the contents of the Sender header if present, otherwise the From header. .PP Control messages other than cancel messages are only allowed if \fBinews\fR is being run by the news user or by a user in the news group and if the control message is recognized. If the article contains a Distribution header with a distribution that matches one of the bad distribution patterns in \fIinn/options.h\fR (anything containing a period by default), the message will be rejected. The message will also be rejected if \&\fIcheckincludedtext\fR is true in \fIinn.conf\fR, it contains more quoted text than original text, and it is over 40 lines long. .PP If not provided, the Path header of an article is constructed as follows: The basic Path header will be \*(L"not-for-mail\*(R". If \fIpathhost\fR is specified in \fIinn.conf\fR, it will be added to the beginning Path. Otherwise, if \&\fIserver\fR is specified, the full domain of the local host will be added to the beginning of the Path. Then, if \fB\-x\fR was given, its value will be added to the beginning of the Path. .PP If posting fails, a copy of the failed post will be saved in a file named \&\fIdead.article\fR in the home directory of the user running \fBinews\fR. \&\fBinews\fR exits with a non-zero status if posting failed or with a zero status if posting was successful. .SH "OPTIONS" .IX Header "OPTIONS" Most of the options to \fBinews\fR take a single value and set the corresponding header in the message that is posted. If the value is more than one word or contains any shell metacharacters, it must be quoted to protect it from the shell. Here are all the options that set header fields and the corresponding header: .PP .Vb 12 \& \-a Approved \& \-c Control \& \-d Distribution \& \-e Expires \& \-F References \& \-f From \& \-n Newsgroups \& \-o Organization \& \-r Reply\-To \& \-t Subject \& \-w Followup\-To \& \-x Path prefix .Ve .PP The \fB\-x\fR argument will be added to the beginning of the normal Path header; it will not replace it. .IP "\fB\-A\fR, \fB\-V\fR, \fB\-W\fR" 4 .IX Item "-A, -V, -W" Accepted for compatibility with C News. These options have no affect. .IP "\fB\-D\fR, \fB\-N\fR" 4 .IX Item "-D, -N" Perform the consistency checks and add headers where appropriate, but then print the article to standard output rather than sending it to the server. \&\fB\-N\fR is accepted as as synonym for compatibility with C News. .IP "\fB\-h\fR" 4 .IX Item "-h" Normally, this flag should always be given. It indicates that the article consists of headers, a blank line, and then the message body. If it is omitted, the input is taken to be just the body of the message, and any desired headers have to be specified with command-line options as described above. .IP "\fB\-O\fR" 4 .IX Item "-O" By default, an Organization header will be added if none is present in the article. To prevent adding the default (from \fIorganization\fR in \&\fIinn.conf\fR), use this flag. .IP "\fB\-p\fR \fIport\fR" 4 .IX Item "-p port" Connect to the specified port on the server rather than to the default (port 119). .IP "\fB\-R\fR" 4 .IX Item "-R" Reject all control messages. .IP "\fB\-S\fR" 4 .IX Item "-S" Do not attempt to append \fI~/.signature\fR to the message, even if it exists. .SH "NOTES" .IX Header "NOTES" If the \s-1NNTP\s0 server requests authentication, \fBinews\fR will try to read \&\fIpasswd.nntp\fR to get the username and password to use and will therefore need read access to that file. This is typically done by making that file group-readable and adding all users who should be able to use \fBinews\fR to post to that server to the appropriate group. .PP \&\fBinews\fR used to do even more than it does now, and all of the remaining checks that are not dependent on the user running \fBinews\fR should probably be removed in favor of letting the news server handle them. .PP Since \s-1INN\s0's \fBinews\fR uses \fIinn.conf\fR and some other corners of an \s-1INN\s0 installation, it's not very appropriate as a general stand-alone \fBinews\fR program for general use on a system that's not running a news server. Other, more suitable versions of \fBinews\fR are available as part of various Unix news clients or by themselves. .SH "HISTORY" .IX Header "HISTORY" Written by Rich \f(CW$alz\fR for InterNetNews. Rewritten in \&\s-1POD\s0 by Russ Allbery . .PP \&\f(CW$Id:\fR inews.pod 9767 2014\-12\-07 21:13:43Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIinn.conf\fR\|(5), \fIpasswd.nntp\fR\|(5), \fIrnews\fR\|(1). inn-2.6.0/doc/man/send-uucp.80000644000175200017520000002260612575023702015242 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "SEND-UUCP 8" .TH SEND-UUCP 8 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" send\-uucp \- Send Usenet articles via UUCP .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fBsend-uucp\fR [\fIsite\fR ...] .SH "DESCRIPTION" .IX Header "DESCRIPTION" The \fBsend-uucp\fR program processes batch files written by \fIinnd\fR\|(8) to send Usenet articles to \s-1UUCP\s0 sites. It reads a configuration file to control how it behaves with various sites. Normally, it is run periodically out of cron to put together batches and send them to remote \s-1UUCP\s0 sites. .PP It makes it possible to reduce bandwidth usage and to send news to remote \&\s-1UUCP\s0 sites which cannot receive a real-time feed (for instance if they are over dial-up connections). .SH "OPTIONS" .IX Header "OPTIONS" Any arguments provided to the program are interpreted as a list of sites specified in \fIsend\-uucp.cf\fR for which batches should be generated. If no arguments are supplied then batches will be generated for all sites listed in that configuration file. .SH "CONFIGURATION" .IX Header "CONFIGURATION" The sites to which articles are to be sent must be configured in the configuration file \fIsend\-uucp.cf\fR in \fIpathetc\fR as set in \fIinn.conf\fR. Each site is specified with a line of the form: .PP .Vb 1 \& site[:host[:funnel]] [compressor [maxsize [batchtime]]] .Ve .IP "\fIsite\fR" 4 .IX Item "site" The news site name being configured. This must match a site name from \fInewsfeeds\fR\|(5). .IP "\fIhost\fR" 4 .IX Item "host" The \s-1UUCP\s0 host name to which batches should be sent for this site. If omitted, the news site name will be used as the \s-1UUCP\s0 host name. .IP "\fIfunnel\fR" 4 .IX Item "funnel" In the case of a site configured as a funnel, \fBsend-uucp\fR needs to flush the channel (or exploder) being used as the target of the funnel instead of flushing the site. This is the way to tell \fBsend-uucp\fR the name of the channel or exploder to flush for this site. If not specified, default to flushing the site. .IP "\fIcompressor\fR" 4 .IX Item "compressor" The compression method to use for batches. This should be one of \f(CW\*(C`bzip2\*(C'\fR, \&\f(CW\*(C`compress\*(C'\fR, \f(CW\*(C`gzip\*(C'\fR or \f(CW\*(C`none\*(C'\fR. Arguments for the compression command may be specified by using \f(CW\*(C`_\*(C'\fR instead of spaces. For example, \f(CW\*(C`gzip_\-9\*(C'\fR. The default value is \f(CW\*(C`gzip\*(C'\fR. .IP "\fImaxsize\fR" 4 .IX Item "maxsize" The maximum size in bytes of a single batch \fIbefore\fR compression. The default value is \f(CW500000\fR bytes. .IP "\fIbatchtime\fR" 4 .IX Item "batchtime" A comma separated list of hours during which batches should be generated for a given site. When \fBsend-uucp\fR runs, a site will only be processed if the current hour matches one of the hours in \fIbatchtime\fR. The default is no limitation on when to generate batches. .PP Fields are separated by spaces and only the site name needs to be specified, with defaults being used for unspecified values. If the first character on a line is a hash sign (\f(CW\*(C`#\*(C'\fR) then the rest of the line is ignored. .SH "EXAMPLE" .IX Header "EXAMPLE" Here is an example for the \fIsend\-uucp.cf\fR configuration file: .PP .Vb 8 \& zoetermeer gzip 1048576 5,18,22 \& hoofddorp gzip 1048576 5,18,22 \& pa3ebv gzip 1048576 5,18,22 \& drinkel bzip2 1048576 5,6,18,20,22,0,2 \& manhole compress 1048576 5,18,22 \& owl compress 1048576 \& able \& pern::MYFUNNEL! .Ve .PP This defines eight \s-1UUCP\s0 sites. The first three and the last two use \f(CW\*(C`gzip\*(C'\fR compression, the fourth site (\f(CW\*(C`drinkel\*(C'\fR) uses \f(CW\*(C`bzip2\*(C'\fR and the remaining sites (\f(CW\*(C`manhole\*(C'\fR and \f(CW\*(C`owl\*(C'\fR) use \f(CW\*(C`compress\*(C'\fR. The first six use a batch size of 1\ \s-1MB\s0, and the two last sites (\f(CW\*(C`able\*(C'\fR and \f(CW\*(C`pern\*(C'\fR) use the default of 500,000 bytes. The \f(CW\*(C`zoetermeer\*(C'\fR, \f(CW\*(C`hoofddorp\*(C'\fR, \f(CW\*(C`pa3ebv\*(C'\fR, and \f(CW\*(C`manhole\*(C'\fR sites will only have batches generated for them during the hours of 05:00, 18:00, and 22:00, and the \f(CW\*(C`drinkel\*(C'\fR site will only have batches generated during those hours and 06:00, 20:00, 00:00, and 02:00. There are no restrictions on when batches will be generated for \f(CW\*(C`owl\*(C'\fR, \f(CW\*(C`able\*(C'\fR and \f(CW\*(C`pern\*(C'\fR. .PP The \f(CW\*(C`pern\*(C'\fR site is configured as a funnel into \f(CW\*(C`MYFUNNEL!\*(C'\fR. \fBsend-uucp\fR will issue \f(CW\*(C`ctlinnd flush MYFUNNEL!\*(C'\fR instead of \f(CW\*(C`ctlinnd flush pern\*(C'\fR. .PP As for the \fInewsfeeds\fR file, the usual flags used for a \s-1UUCP\s0 feed are \&\f(CW\*(C`Tf,Wnb\*(C'\fR. Here is a typical entry for \f(CW\*(C`zoetermeer\*(C'\fR, where the batching is kept between 4\ \s-1KB\s0 and 1\ \s-1KB:\s0 .PP .Vb 3 \& zoetermeer\e \& :*,!junk,!control,!control.*/!foo\e \& :Tf,Wnb,B4096/1024: .Ve .SH "FILES" .IX Header "FILES" .IP "\fIpathbin\fR/send\-uucp" 4 .IX Item "pathbin/send-uucp" The Perl script itself used to create news batches from the outgoing files. .IP "\fIpathetc\fR/send\-uucp.cf" 4 .IX Item "pathetc/send-uucp.cf" The configuration file which specifies a list of sites to be processed. .SH "HISTORY" .IX Header "HISTORY" This program was originally written by Edvard Tuinder and then maintained and extended by Miquel van Smoorenburg . Marco d'Itri cleaned up the code for inclusion in \s-1INN\s0. This manual page was written by Mark Brown . .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIinnd\fR\|(8), \fInewsfeeds\fR\|(5), \fIuucp\fR\|(8). inn-2.6.0/doc/man/moderators.50000644000175200017520000001673012575023702015514 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "MODERATORS 5" .TH MODERATORS 5 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" moderators \- Submission addresses for moderated groups .SH "DESCRIPTION" .IX Header "DESCRIPTION" When an unapproved article is posted locally to a moderated newsgroup, it is not passed off to \fBinnd\fR for normal handling; instead, it is sent via e\-mail to the submission address for that newsgroup. The submission address is determined using this configuration file. .PP The file \fIpathetc\fR/moderators is a list of associations between \fIuwildmat\fR\|(3) patterns matching newsgroups and the submission address for those newsgroups. Blank lines and lines starting with a number sign (\f(CW\*(C`#\*(C'\fR) are ignored. All other lines should consist of two fields separated by a colon: .PP .Vb 1 \& :
.Ve .PP The first field is a \fIuwildmat\fR\|(3) pattern matching the group or groups to which this line applies. The first matching line is used, so more specific patterns should be listed before general patterns. .PP The second field, the submission address, should be a simple e\-mail address with one exception: at most one \f(CW%s\fR may occur anywhere in the address. If present, it will be replaced by the name of the newsgroup, with all periods in the name changed to dashes (\f(CW\*(C`\-\*(C'\fR). If there is a literal \f(CW\*(C`%\*(C'\fR in the submission address, it must be written as \f(CW\*(C`%%\*(C'\fR, even if not followed by an \f(CW\*(C`s\*(C'\fR. .PP With the \f(CW%s\fR syntax, periods are converted to dashes for historical reasons, from back in the days when periods in the local part of addresses were not always handled correctly. It's probably no longer necessary, but so much now depends on it that it can't be easily changed. .PP It's intended that the sample \fImoderators\fR file included in the \s-1INN\s0 distribution always be sufficient for all world-wide newsgroups. The hosts behind moderators.isc.org have graciously volunteered to handle forwarding tasks for all world-wide newsgroups so that individual sites don't have to keep track of the submission addresses for moderated groups. The forwarding database used by moderators.isc.org is coordinated by ; if you know of a world-wide newsgroup hierarchy that is not correctly handled by moderators.isc.org, please send the details to that address. .PP Given that, the only thing you should have to add to the sample file under normal circumstances are the forwarding addresses for local or limited-distribution moderated groups. .PP If this file doesn't exist, or if a post is made to a moderated group that has no matching entry in this file, \fBnnrpd\fR falls back on the value of \&\fImoderatormailer\fR set in \fIinn.conf\fR and, failing that, rejects the post. .SH "EXAMPLES" .IX Header "EXAMPLES" Here is a sample file: .PP .Vb 3 \& example.important:announce@example.com \& example.*:%s@smtp.example.com \& *:%s@moderators.isc.org .Ve .PP Using the above file, postings to the moderated newsgroup in the left column below will be sent to the address shown in the right column below: .PP .Vb 3 \& example.important announce@example.com \& example.x\-announce example\-x\-announce@smtp.example.com \& alt.dev.null alt\-dev\-null@moderators.isc.org .Ve .PP Note that periods are changed to dashes and dashes are left alone with the \f(CW%s\fR syntax, so two moderated newsgroups whose names differ only by changing a period to a dash would go to the same address. Such newsgroup pairs should therefore not be created. .SH "HISTORY" .IX Header "HISTORY" Written by Rich \f(CW$alz\fR for InterNetNews. Rewritten in \&\s-1POD\s0 by Russ Allbery . .PP \&\f(CW$Id:\fR moderators.pod 9767 2014\-12\-07 21:13:43Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIinn.conf\fR\|(5), \fInnrpd\fR\|(8), \fIuwildmat\fR\|(3). inn-2.6.0/doc/man/buffchan.80000644000175200017520000002643512575023702015117 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "BUFFCHAN 8" .TH BUFFCHAN 8 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" buffchan \- Buffered file\-writing backend for INN .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fBbuffchan\fR [\fB\-bru\fR] [\fB\-c\fR \fIlines\fR] [\fB\-C\fR \fIseconds\fR] [\fB\-d\fR \&\fIdirectory\fR] [\fB\-f\fR \fInum-fields\fR] [\fB\-l\fR \fIlines\fR] [\fB\-L\fR \fIseconds\fR] [\fB\-m\fR \fImap\fR] [\fB\-p\fR \fIpid-file\fR] [\fB\-s\fR \fIformat\fR] .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\fBbuffchan\fR reads lines from standard input and copies the initial fields in each line to the files named by the remaining fields on the line. \&\fBbuffchan\fR is intended to be called by \fBinnd\fR as an exploder feed. .PP The input is interpreted as a sequence of lines. Each line contains a fixed number of initial fields, followed by a variable number of filename fields. All fields in a line are separated by whitespace and do not contain any whitespace. The default number of initial fields is one. .PP For each line of input, \fBbuffchan\fR writes the initial fields, separated by a space and followed by a newline, to each of the files named in the filename fields. The output files are kept open and are only flushed or closed based on the schedule given by the \fB\-c\fR, \fB\-C\fR, \fB\-l\fR, and \fB\-L\fR options. .PP As an exploder feed (see \fInewsfeeds\fR\|(5) for an explanation), \fBbuffchan\fR interprets lines beginning with an exclamation point as commands. Besides \&\f(CW\*(C`!begin\*(C'\fR (which only marks the start of the feed), there are three supported commands: .IP "!flush [\fIsite\fR]" 4 .IX Item "!flush [site]" The flush command closes and reopens all open files. An optional site can be specified, in which case \fBbuffchan\fR flushes only that file. This command is analogous to the \f(CW\*(C`ctlinnd flush\*(C'\fR command. This command can be sent via \fBinnd\fR using \f(CW\*(C`ctlinnd send \f(CIbuffchan\-site\f(CW \*(Aqflush \f(CIsite\f(CW\*(Aq\*(C'\fR. .Sp Applications can tell that flush has completed by renaming the file before issuing the command. When the original file name has reappeared, the flush is complete. If \fIfchmod\fR\|(3) is available, \fBbuffchan\fR also changes the file to read-only while it's actively writing to it and changes it back to read/write once it has been closed. It will change the mode back to read-only only if it reopens the same file. .IP "!drop [\fIsite\fR]" 4 .IX Item "!drop [site]" The drop command is similar to the flush command, except that no files are reopened. If given an argument, only the specified site is dropped; otherwise, all sites are dropped. (Note that a site will be restarted if the input stream mentions the site again.) .Sp When a \f(CW\*(C`ctlinnd drop site\*(C'\fR command is sent, \fBinnd\fR will automatically forward the command to \fBbuffchan\fR if the site is listed as a funnel feeding into the \fBbuffchan\fR exploder. To drop all sites, use \f(CW\*(C`ctlinnd send \f(CIbuffchan\-site\f(CW drop\*(C'\fR. .IP "!readmap" 4 .IX Item "!readmap" The map file specified with the \fB\-m\fR option, if given, will be reloaded. .PP Once \fBbuffchan\fR opens a file, it keeps it open (in the absence of a drop command). The input must therefore never specify more files than the maximum number of files a process may open. .SH "OPTIONS" .IX Header "OPTIONS" .IP "\fB\-b\fR" 4 .IX Item "-b" Force the output to be buffered. (This is generally the default, but it may depend on the operating system.) If \fB\-b\fR is given, a buffer size of \&\s-1BUFSIZ\s0 (a constant of the system standard I/O library) is used. .IP "\fB\-c\fR \fIlines\fR" 4 .IX Item "-c lines" If the \fB\-c\fR flag is given, \fBbuffchan\fR will close and reopen a file after every \fIlines\fR lines are written to the file. .IP "\fB\-C\fR \fIseconds\fR" 4 .IX Item "-C seconds" If the \fB\-C\fR flag is given, \fBbuffchan\fR will close and reopen a file if it has been open for more than \fIseconds\fR seconds. .IP "\fB\-d\fR \fIdirectory\fR" 4 .IX Item "-d directory" This flag may be used to specify a directory the program should change to before starting. If this flag is used, the default for the \fB\-s\fR flag (see below) is changed to be a simple \f(CW%s\fR (in other words, output files are considered to be relative to \fIdirectory\fR). .IP "\fB\-f\fR \fInum-fields\fR" 4 .IX Item "-f num-fields" By default, each line is expected to contain one fixed field followed by some number of filename fields. If this flag is given, \fInum-fields\fR will be used as the number of initial fixed fields. .IP "\fB\-l\fR \fIlines\fR" 4 .IX Item "-l lines" If the \fB\-l\fR flag is given, \fBbuffchan\fR will flush the output after every \&\fIlines\fR lines are written to a file. .IP "\fB\-L\fR \fIseconds\fR" 4 .IX Item "-L seconds" If the \fB\-L\fR flag is given, \fBbuffchan\fR will flush each output file every \&\fIseconds\fR seconds. .IP "\fB\-m\fR \fImap\fR" 4 .IX Item "-m map" Map files translate the names in the filename fields on each line into filenames that should be used instead. It's used primarily when short names are used in \fInewsfeeds\fR, but the output files should use the full domain names of remote peers. .Sp In the map file, blank lines and lines starting with a number sign (\f(CW\*(C`#\*(C'\fR) are ignored. All other lines should have two host names separated by a colon. The first field is the name that may appear in the input stream; the second field names the file to be used when the name in the first field appears. For example: .Sp .Vb 4 \& # This is a comment \& uunet:news.uu.net \& foo:foo.com \& munnari:munnari.oz.au .Ve .IP "\fB\-p\fR \fIpid-file\fR" 4 .IX Item "-p pid-file" If the \fB\-p\fR option is given, \fBbuffchan\fR will write a line containing its process \s-1ID\s0 (in text) to the specified file when it starts. .IP "\fB\-r\fR" 4 .IX Item "-r" By default, \fBbuffchan\fR sends its error messages to \fIpathlog\fR/errlog. To suppress this redirection and send error messages to standard error, use the \fB\-r\fR flag. .IP "\fB\-s\fR" 4 .IX Item "-s" The \fB\-s\fR flag may be used to specify a format that maps a filename from the filename fields at the end of each line to an actual filename. This is a \fIsprintf\fR\|(3) format string that should contain a single instance of \&\f(CW%s\fR, which will be replaced with the value of the filename field (possibly after mapping with the map file from \fB\-m\fR). The default value is \fIpathoutgoing\fR/\f(CW%s\fR. .IP "\fB\-u\fR" 4 .IX Item "-u" If the \fB\-u\fR flag is used, the output will be unbuffered. .SH "EXAMPLES" .IX Header "EXAMPLES" If \fBbuffchan\fR is invoked with \f(CW\*(C`\-f 2\*(C'\fR and given the following input: .PP .Vb 3 \& news/software/b/132 <1643@munnari.oz.au> foo uunet \& news/software/b/133 <102060@litchi.foo.com> uunet munnari \& comp/sources/unix/2002 <999@news.foo.com> foo uunet munnari .Ve .PP Then the file \fIfoo\fR will have these lines: .PP .Vb 2 \& news/software/b/132 <1643@munnari.oz.au> \& comp/sources/unix/2002 <999@news.foo.com> .Ve .PP the file \fImunnari\fR will have these lines: .PP .Vb 2 \& news/software/b/133 <102060@litchi.foo.com> \& comp/sources/unix/2002 <999@news.foo.com> .Ve .PP and the file \fIuunet\fR will have these lines: .PP .Vb 3 \& news/software/b/132 <1643@munnari.oz.au> \& news/software/b/133 <102060@litchi.foo.com> \& comp/sources/unix/2002 <999@news.foo.com> .Ve .SH "HISTORY" .IX Header "HISTORY" Written by Rich \f(CW$alz\fR for InterNetNews. Converted to \&\s-1POD\s0 by Russ Allbery . .PP \&\f(CW$Id:\fR buffchan.pod 9767 2014\-12\-07 21:13:43Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIctlinnd\fR\|(8), \fIfilechan\fR\|(8), \fIinn.conf\fR\|(5), \fIinnd\fR\|(8), \fInewsfeeds\fR\|(5). inn-2.6.0/doc/man/radius.80000644000175200017520000001414212575023702014622 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "RADIUS 8" .TH RADIUS 8 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" radius \- nnrpd RADIUS password authenticator .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fBradius\fR [\fB\-h\fR] [\fB\-f\fR \fIconfig\fR] .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\fBradius\fR is an \fBnnrpd\fR authenticator, accepting a username and password from \fBnnrpd\fR (given to \fBnnrpd\fR by a reader connection) and attempting to authenticate that username and password against a \s-1RADIUS\s0 server. See \fIreaders.conf\fR\|(5) for more information on how to configure an \fBnnrpd\fR authenticator. It is useful for a site that already does user authentication via \s-1RADIUS\s0 and wants to authenticate news reading connections as well. .PP By default, \fBradius\fR reads \fIpathetc\fR/inn\-radius.conf for configuration information, but a different configuration file can be specified with \&\fB\-f\fR. See \fIinn\-radius.conf\fR\|(5) for a description of the configuration file. .SH "OPTIONS" .IX Header "OPTIONS" .IP "\fB\-f\fR \fIconfig\fR" 4 .IX Item "-f config" Read \fIconfig\fR instead of \fIpathetc\fR/inn\-radius.conf for configuration information. .IP "\fB\-h\fR" 4 .IX Item "-h" Print out a usage message and exit. .SH "EXAMPLE" .IX Header "EXAMPLE" The following \fIreaders.conf\fR\|(5) fragment tells \fBnnrpd\fR to authenticate all connections using this authenticator: .PP .Vb 5 \& auth radius { \& auth: radius \& default: \& default\-domain: example.com \& } .Ve .PP \&\f(CW\*(C`@example.com\*(C'\fR will be appended to the user-supplied identity, and if \s-1RADIUS\s0 authentication fails, the user will be assigned an identity of \f(CW\*(C`@example.com\*(C'\fR. .SH "BUGS" .IX Header "BUGS" It has been reported that this authenticator doesn't work with Ascend \&\s-1RADIUS\s0 servers, but does work with Cistron \s-1RADIUS\s0 servers. It's also believed to work with Livingston's \s-1RADIUS\s0 server. Contributions to make it work better with different types of \s-1RADIUS\s0 servers would be gratefully accepted. .PP This code has not been audited against the \s-1RADIUS\s0 protocol and may not implement it correctly. .SH "HISTORY" .IX Header "HISTORY" The \s-1RADIUS\s0 authenticator was originally written by Aidan Cully. This documentation was written by Russ Allbery . .PP \&\f(CW$Id:\fR radius.pod 9940 2015\-09\-04 12:58:15Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIinn\-radius.conf\fR\|(5), \fInnrpd\fR\|(8), \fIreaders.conf\fR\|(5). inn-2.6.0/doc/man/mailpost.80000644000175200017520000002401412575023702015162 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "MAILPOST 8" .TH MAILPOST 8 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" mailpost \- Feed an e\-mail message into a newsgroup .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fBmailpost\fR [\fB\-hn\fR] [\fB\-a\fR \fIaddr\fR] [\fB\-b\fR \fIdatabase\fR] [\fB\-c\fR \fIwait-time\fR] [\fB\-d\fR \fIdistribution\fR] [\fB\-f\fR \fIaddr\fR] [\fB\-m\fR \fImailing-list\fR] [\fB\-o\fR \fIoutput-command\fR] [\fB\-p\fR \fIport\fR] [\fB\-r\fR \fIaddr\fR] [\fB\-t\fR \fItempdir\fR] [\fB\-x\fR \fIheader\fR[\fB:\fR\fIheader\fR...]] \fInewsgroups\fR .SH "DESCRIPTION" .IX Header "DESCRIPTION" The \fBmailpost\fR program reads a properly formatted e\-mail message from stdin and feeds it to \fBinews\fR for posting to a news server. \fInewsgroups\fR is a whitespace-separated list of group names to which to post the article (at least one newsgroup must be specified). .PP Before feeding the article to \fBinews\fR, it checks that the article has not been seen before, and it changes some headers (cleans up some address headers, removes X\-Trace: and X\-Complaints-To:, and puts \f(CW\*(C`X\-\*(C'\fR in front of unknown headers). .PP If the article has been seen before (\fBmailpost\fR records the Message-ID of each article it handles), then the article will be dropped with a non-zero error status. Other errors will cause the article to be mailed to the newsmaster (selected at configure time and defaulting to \f(CW\*(C`usenet\*(C'\fR). .PP Normally, \fBmailpost\fR is run by \fIsendmail\fR\|(8) via an alias entry: .PP .Vb 2 \& local\-mail\-wreck\-bikes: "|/mailpost \& \-b /var/tmp \-t /var/tmp \-d local local.mail.rec.bicycles.racing" .Ve .PP The \fB\-b\fR and \fB\-t\fR flags are useful to change the directories used by \&\fBmailpost\fR by default. As a matter of fact, though it is recommended to run \fBmailpost\fR as the news user, it is as often as not run as another user, for instance the mail user. Therefore, you should make sure to create and set to be writable by the user that \fBmailpost\fR runs as the directories where to put the database and the temporary files. .PP Instead of \fI/var/tmp\fR, the mail spool directory can be specified, or any other directory where the \fBmailpost\fR process has write access. .SH "OPTIONS" .IX Header "OPTIONS" .IP "\fB\-a\fR \fIaddr\fR" 4 .IX Item "-a addr" If the \fB\-a\fR flag is used, the value given is added to the article as an Approved: header. .IP "\fB\-b\fR \fIdatabase\fR" 4 .IX Item "-b database" If the \fB\-b\fR flag is used, then it defines the location of the persistent database used to store the Message-IDs of articles sent on. This is to prevent articles looping around if a news-to-mail gateway sends them back here. This option may be required if the \fBmailpost\fR process does not have write access to the news database directory. The default value is \fIpathdb\fR as set in \fIinn.conf\fR. .IP "\fB\-c\fR \fIwait-time\fR" 4 .IX Item "-c wait-time" The \fB\-c\fR flag indicates a length of time to sleep before posting. If duplicate messages are received in this interval (by any instance of \&\fBmailpost\fR using the same database), the article is only posted once, but with Newsgroups: header modified to crosspost the article to all indicated groups. The units for \fIwait-time\fR are seconds; a reasonable value may be anywhere from tens to hundreds of seconds, or even higher, depending on how long mail can be delayed on its way to your system. .IP "\fB\-d\fR \fIdistribution\fR" 4 .IX Item "-d distribution" If the \fB\-d\fR flag is used, the value given is added to the article as a Distribution: header. .IP "\fB\-f\fR \fIaddr\fR" 4 .IX Item "-f addr" The \fB\-f\fR flag is a synonym for the \fB\-r\fR flag. .IP "\fB\-h\fR" 4 .IX Item "-h" Print usage information and exit. .IP "\fB\-m\fR \fImailing-list\fR" 4 .IX Item "-m mailing-list" If the \fB\-m\fR flag is used, the value given is added to the article in a Mailing-List: header, if such a header doesn't already exist. .IP "\fB\-n\fR" 4 .IX Item "-n" If the \fB\-n\fR flag is used, neither an article is posted nor a mail is sent in case an error occurs. Everything is written to the standard output. .IP "\fB\-o\fR \fIoutput-command\fR" 4 .IX Item "-o output-command" Specifies the program to which the resulting article processed by \fBmailpost\fR should be sent. For debugging purpose, \f(CW\*(C`\-o cat\*(C'\fR can be used. The default value is \f(CW\*(C`inews \-S \-h\*(C'\fR. .IP "\fB\-p\fR \fIport\fR" 4 .IX Item "-p port" Specifies the port on which \fBnnrpd\fR is listening, used for article posting. If given, \fB\-p\fR is passed along to \fBinews\fR. .IP "\fB\-r\fR \fIaddr\fR" 4 .IX Item "-r addr" A heuristic is used to determine a reasonable value for the Path: header. The \fB\-r\fR flag indicates what to use if no other value can be determined. .IP "\fB\-t\fR \fItempdir\fR" 4 .IX Item "-t tempdir" If the \fB\-t\fR flag is used, then it defines the location of the directory to use to temporarily store error messages that are sent to the newsmaster. This option may be required if the default value refers to a path that does not exist or the \fBmailpost\fR process does not have write access to. Two paths are tried by default: \fIpathtmp\fR as set in \fIinn.conf\fR, and then \fI/var/tmp\fR if \fIpathtmp\fR is not writable. .IP "\fB\-x\fR \fIheader\fR[\fB:\fR\fIheader\fR...]" 4 .IX Item "-x header[:header...]" A colon-separated list of additional headers which should be treated as known headers; these headers will be passed through to \fBinews\fR without having \f(CW\*(C`X\-\*(C'\fR prepended. .Sp Known headers are: .Sp .Vb 12 \& Approved \& Content\-* \& Date \& Distribution \& From \& Mailing\-List \& Message\-ID \& MIME\-* \& References \& Return\-Path \& Sender \& Subject .Ve .SH "FILES" .IX Header "FILES" .IP "\fIpathbin\fR/mailpost" 4 .IX Item "pathbin/mailpost" The Perl script itself used to feed an e\-mail message to a newsgroup. .IP "\fIpathdb\fR/mailpost\-msgid.dir and \fIpathdb\fR/mailpost\-msgid.pag" 4 .IX Item "pathdb/mailpost-msgid.dir and pathdb/mailpost-msgid.pag" The default database files which record previously seen Message-IDs. .SH "HISTORY" .IX Header "HISTORY" Written by Paul Vixie long ago and then hacked up by James Brister for \&\s-1INN\s0 integration. .PP \&\f(CW$Id:\fR mailpost.in 9830 2015\-04\-23 18:56:28Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIactive\fR\|(5), \fIinews\fR\|(1), \fIinn.conf\fR\|(5), \fInnrpd\fR\|(8), \fIuwildmat\fR\|(3). inn-2.6.0/doc/man/innmail.10000644000175200017520000001361012575023702014752 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "INNMAIL 1" .TH INNMAIL 1 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" innmail \- Simple mail\-sending program .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fBinnmail\fR [\fB\-h\fR] [\fB\-s\fR \fIsubject\fR] \fIaddress\fR [\fIaddress\fR ...] .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\fBinnmail\fR is a Perl script intended to provide the non-interactive mail-sending functionality of \fImail\fR\|(1) while avoiding nasty security problems. It takes the body of a mail message on standard input and sends it to the specified addresses by invoking the value of \fImta\fR in \&\fIinn.conf\fR. .PP At least one address (formatted for the \s-1MTA\s0 specified in \fIinn.conf\fR, if it matters) is required. \fBinnmail\fR will sanitize the addresses so that they contain only alphanumerics and the symbols \f(CW\*(C`@\*(C'\fR, \f(CW\*(C`.\*(C'\fR, \f(CW\*(C`\-\*(C'\fR, \f(CW\*(C`+\*(C'\fR, \f(CW\*(C`_\*(C'\fR, and \f(CW\*(C`%\*(C'\fR. .PP \&\fBinnmail\fR was written to be suitable for the \fImailcmd\fR setting in \&\fIinn.conf\fR. .SH "OPTIONS" .IX Header "OPTIONS" .IP "\fB\-h\fR" 4 .IX Item "-h" Gives usage information. .IP "\fB\-s\fR \fIsubject\fR" 4 .IX Item "-s subject" Sets the Subject: header of the message. A warning is issued if this option is omitted. .SH "EXAMPLES" .IX Header "EXAMPLES" This sends a one-line message to the local user \f(CW\*(C`joe\*(C'\fR: .PP .Vb 1 \& echo "A one\-line message." | innmail \-s "Simple message" joe .Ve .PP \&\fBinnmail\fR by default is used by \s-1INN\s0 for sending nightly reports and control message reports. .SH "BUGS" .IX Header "BUGS" \&\fBinnmail\fR fails on addresses that begin with \f(CW\*(C`\-\*(C'\fR, although one might hope that the news server will not need to contact any such addresses. .PP There are many \*(L"correct\*(R" addresses that will be silently modified by the sanitization process. A news administrator should be careful to use particularly sane addresses if they may be passed to \fBinnmail\fR. .SH "HISTORY" .IX Header "HISTORY" \&\fBinnmail\fR was written by James Brister for InterNetNews. This manual page was originally written by Jeffrey M. Vinocur. .PP \&\f(CW$Id:\fR innmail.pod 7851 2008\-05\-26 19:33:08Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIinn.conf\fR\|(5), \fImail\fR\|(1). inn-2.6.0/doc/man/distributions.50000644000175200017520000001322012575023702016226 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "DISTRIBUTIONS 5" .TH DISTRIBUTIONS 5 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" distributions \- Recommended values for the Distribution: header .SH "DESCRIPTION" .IX Header "DESCRIPTION" The file \fIpathetc\fR/distributions contains a list of relevant distributions and their descriptions. It provides local information for posters who wish to add a Distribution: header to their articles so as to restrict their propagation, although it does not guarantee that such articles will not leak elsewhere because of a misconfiguration of a news server to which they are fed. See \fInewsfeeds\fR\|(5) for more information about how a news server handles the Distribution: header. .PP Each line of this file consists of a distribution area followed by its description (encoded in \s-1UTF\-8\s0) after at least a whitespace. For instance: .PP .Vb 4 \& fr Local to France. \& local Local to this news server. \& nj Local to New Jersey. \& usa Local to the United States of America. .Ve .PP Blank lines and lines beginning with a number sign (\f(CW\*(C`#\*(C'\fR) are ignored. .PP Any client that issues the \s-1LIST\s0 \s-1DISTRIBUTIONS\s0 command obtain these recommended values, if available. However, be aware that use of the \s-1LIST\s0 \&\s-1DISTRIBUTIONS\s0 command is not widespread (though documented in \s-1RFC\s0\ 6048) and most news clients will never ask for this file. .PP If this file is empty, it is not an error. The server will just send the client an empty response. And if the file is missing, the server will also send the client an appropriate response to indicate that the distributions list is not maintained on the server. .PP The Distribution: header can also be automatically set by \fBnnrpd\fR if \fIdistrib.pats\fR\|(5) is correctly configured. .SH "HISTORY" .IX Header "HISTORY" Written by Julien Elie for InterNetNews. .PP \&\f(CW$Id:\fR distributions.pod 9137 2010\-10\-29 18:09:12Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIdistrib.pats\fR\|(5), \fInewsfeeds\fR\|(5), \fInnrpd\fR\|(8). inn-2.6.0/doc/man/expire.80000644000175200017520000002553612575023702014640 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "EXPIRE 8" .TH EXPIRE 8 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" expire \- Usenet article and history expiration program .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fBexpire\fR [\fB\-iNnptx\fR] [\fB\-d\fR \fIdir\fR] [\fB\-f\fR \fIfile\fR] [\fB\-g\fR \fIfile\fR] [\fB\-h\fR \fIfile\fR] [\fB\-r\fR \fIreason\fR] [\fB\-s\fR \fIsize\fR] [\fB\-v\fR \fIlevel\fR] [\fB\-w\fR \fInumber\fR] [\fB\-z\fR \fIfile\fR] [\fIexpire.ctl\fR] .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\fBexpire\fR scans the \fIhistory\fR\|(5)\-format text file \fIpathdb\fR/history and uses the information recorded in it to purge itself of old news articles. Articles stored using a storage method that has self-expire functionality are by default not affected by \fBexpire\fR's primary behavior (but see the \&\fB\-N\fR flag to disable this). In this case, \fIexpire.ctl\fR is ignored except the \f(CW\*(C`/remember/\*(C'\fR line for that article; \fBexpire\fR does still probe to see if the article still exists and purges the relevant history and overview entries if appropriate. However, if \fIgroupbaseexpiry\fR in \&\fIinn.conf\fR is true, \fBexpire\fR acts on all articles as specified by \&\fIexpire.ctl\fR regardless of whether their storage methods have self-expire functionality. .PP Note that \fBexpire\fR never purges articles which do not match any entry in \fIexpire.ctl\fR. .SH "OPTIONS" .IX Header "OPTIONS" .IP "\fB\-d\fR \fIdir\fR" 4 .IX Item "-d dir" If the \fB\-d\fR flag is used, then the new \fIhistory\fR file and database is created in the specified directory \fIdir\fR. This is useful when the filesystem does not have sufficient space to hold both the old and new history files. When this flag is used, \fBexpire\fR leaves the server paused and creates a zero-length file named after the new history file, with an extension of \f(CW\*(C`.done\*(C'\fR to indicate that it has successfully completed the expiration. The calling script should install the new history file and unpause the server. The \fB\-r\fR flag should be used with this flag. .IP "\fB\-f\fR \fIfile\fR" 4 .IX Item "-f file" To specify an alternate history file, use the \fB\-f\fR flag. This flag is valid when used with the \fB\-d\fR flag, and the output will be written to the specified file. The default without \fB\-f\fR is \f(CW\*(C`history\*(C'\fR. .IP "\fB\-g\fR \fIfile\fR" 4 .IX Item "-g file" If the \fB\-g\fR flag is given, then a one-line summary equivalent to the output of \fB\-v 1\fR, except preceded by the current time, will be appended to the specified \fIfile\fR. .IP "\fB\-h\fR \fIfile\fR" 4 .IX Item "-h file" To specify an alternate input text history file, use the \fB\-h\fR flag. \&\fBexpire\fR uses the old \fIdbz\fR\|(3) database to determine the size of the new one. (If the \fB\-d\fR flag is not used, the output filename will be the same as the input filename with an extension of \f(CW\*(C`.n\*(C'\fR.) .Sp The default without the \fB\-h\fR flag is \fIpathdb\fR/history. .IP "\fB\-i\fR" 4 .IX Item "-i" To ignore the old database, use the \fB\-i\fR flag. .IP "\fB\-N\fR" 4 .IX Item "-N" The control file is normally ignored for articles in storage methods which have self-expire functionality. If the \fB\-N\fR flag is used, \&\fBexpire\fR still uses the control file for these articles. .IP "\fB\-n\fR" 4 .IX Item "-n" If \fBinnd\fR is not running, use the \fB\-n\fR flag and \fBexpire\fR will not send the \f(CW\*(C`pause\*(C'\fR or \f(CW\*(C`go\*(C'\fR commands. (For more details on the commands, see \fIctlinnd\fR\|(8)). Note that \fBexpire\fR only needs exclusive access for a very short time \-\-\ long enough to see if any new articles arrived since it first hit the end of the file, and to rename the new files to the working files. .IP "\fB\-p\fR" 4 .IX Item "-p" \&\fBexpire\fR makes its decisions on the time the article arrived, as found in the \fIhistory\fR file. This means articles are often kept a little longer than with other expiration programs that base their decisions on the article's posting date. To use the article's posting date, use the \fB\-p\fR flag. .IP "\fB\-r\fR \fIreason\fR" 4 .IX Item "-r reason" \&\fBexpire\fR normally sends a \f(CW\*(C`pause\*(C'\fR command to the local \fBinnd\fR daemon when it needs exclusive access to the \fIhistory\fR file, using the string \&\f(CW\*(C`Expiring\*(C'\fR as the reason. To give a different reason, use the \fB\-r\fR flag. The process \s-1ID\s0 will be appended to the reason. When \fBexpire\fR is finished and the new \fIhistory\fR file is ready, it sends a \f(CW\*(C`go\*(C'\fR command. See also the \fB\-n\fR flag. .IP "\fB\-s\fR \fIsize\fR" 4 .IX Item "-s size" Optimize the new history database for approximately \fIsize\fR pairs (lines in \fIhistory\fR). Accurately specifying the size will create a more efficient database. (The size should be the estimated eventual size of the file, typically the size of the old file.) .IP "\fB\-t\fR" 4 .IX Item "-t" If the \fB\-t\fR flag is used, then \fBexpire\fR will generate a list of the tokens that should be removed on its standard output, and the new \fIhistory\fR file will be left in \fIhistory.n\fR, \fIhistory.n.dir\fR, \fIhistory.n.index\fR and \fIhistory.n.hash\fR. This flag is useful for debugging when used with the \fB\-n\fR flag. Note that if the \fB\-f\fR flag is used, then the name specified with that flag will be used instead of \fIhistory\fR. .IP "\fB\-v\fR \fIlevel\fR" 4 .IX Item "-v level" The \fB\-v\fR flag is used to increase the verbosity of the program, generating messages to standard output. The \fIlevel\fR should be a number, where higher numbers result in more output. Level one will print totals of the various actions done (not valid if a new \fIhistory\fR file is not written), level two will print a report on each individual file, while level five results in multiple lines of output for every history line processed. .IP "\fB\-w\fR \fInumber\fR" 4 .IX Item "-w number" Use the \fB\-w\fR flag to \*(L"warp\*(R" time so that \fBexpire\fR thinks it is running at some time other then the current time. The value should be a signed floating point number indicating the number of days to use as the offset. .IP "\fB\-x\fR" 4 .IX Item "-x" If the \fB\-x\fR flag is used, then \fBexpire\fR will not create any new history files. This is most useful when combined with the \fB\-n\fR and \fB\-t\fR flags to see how different expiration policies would change the amount of disk space used. .IP "\fB\-z\fR \fIfile\fR" 4 .IX Item "-z file" If the \fB\-z\fR flag is used, then articles are not removed, but their names are appended to the specified \fIfile\fR. See the description of \fBdelayrm\fR in \fInews.daily\fR\|(8). If a filename is specified, it is taken as the control file and parsed according to the rules in \fIexpire.ctl\fR. A single dash (\f(CW\*(C`\-\*(C'\fR) may be used to read the file from standard input. If no file is specified, the file \fIpathetc\fR/expire.ctl is read. .SH "HISTORY" .IX Header "HISTORY" Written by Rich \f(CW$alz\fR for InterNetNews. Converted to \&\s-1POD\s0 by Julien Elie. .PP \&\f(CW$Id:\fR expire.pod 8573 2009\-08\-18 13:49:54Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIctlinnd\fR\|(8), \fIdbz\fR\|(3), \fIexpire.ctl\fR\|(5), \fIhistory\fR\|(5), \fIinn.conf\fR\|(5), \fIinnd\fR\|(8), \&\fIinndcomm\fR\|(3), \fInews.daily\fR\|(8). inn-2.6.0/doc/man/expireover.80000644000175200017520000002475512575023702015536 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "EXPIREOVER 8" .TH EXPIREOVER 8 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" expireover \- Expire entries from the news overview database .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fBexpireover\fR [\fB\-ekNpqs\fR] [\fB\-f\fR \fIfile\fR] [\fB\-w\fR \fIoffset\fR] [\fB\-z\fR \fIrmfile\fR] [\fB\-Z\fR \fIlowmarkfile\fR] .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\fBexpireover\fR expires old entries from the news overview database. It reads in a list of newsgroups (by default from \fIpathdb\fR/active, but a different file can be specified with the \fB\-f\fR option) and then removes from the overview database mentions of any articles that no longer exist in the news spool. .PP If \fIgroupbaseexpiry\fR in \fIinn.conf\fR is true, \fBexpireover\fR also removes old articles from the news spool according to the expiration rules in \&\fIexpire.ctl\fR. Otherwise it only removes overview entries for articles that have already been removed by some other process, and \fB\-e\fR, \fB\-k\fR, \&\fB\-N\fR, \fB\-p\fR, \fB\-q\fR, \fB\-w\fR, and \fB\-z\fR are all ignored. .PP When \fIgroupbaseexpiry\fR is set, the default behavior of \fBexpireover\fR is to remove the article from the spool once it expires out of all of the newsgroups to which it was crossposted. The article is, however, removed from the overview database of each newsgroup as soon as it expires out of that individual newsgroup. The effect is that an article crossposted to several groups will be removed from the overview database from each group one-by-one as its age passes the expiration threshold for that group as set in \fIexpire.ctl\fR, and then when it expires out of the last newsgroup, it will be deleted from the news spool. .PP Articles that are stored in self-expiring storage backends such as \s-1CNFS\s0 are normally treated differently and not expired until they expire out of the backend regardless of \fIexpire.ctl\fR. See \fB\-N\fR, however. .PP By default, \fBexpireover\fR purges all overview information for newsgroups that have been removed from the server; this behavior is suppressed if \&\fB\-f\fR is given. .SH "OPTIONS" .IX Header "OPTIONS" .IP "\fB\-e\fR" 4 .IX Item "-e" Remove articles from the news spool and all overview databases as soon as they expire out of any newsgroup to which they are posted, rather than retain them until they expire out of all newsgroups. \fB\-e\fR and \fB\-k\fR cannot be used at the same time. This flag is ignored if \&\fIgroupbaseexpiry\fR is false. .IP "\fB\-f\fR \fIfile\fR" 4 .IX Item "-f file" Use \fIfile\fR as the newsgroup list instead of \fIpathdb\fR/active. \fIfile\fR can be \f(CW\*(C`\-\*(C'\fR to indicate standard input. Using this flag suppresses the normal purge of all overview information from newsgroups that have been removed from the server. .IP "\fB\-k\fR" 4 .IX Item "-k" Retain all overview information for an article, as well as the article itself, until it expires out of all newsgroups to which it was posted. This can cause articles to stick around in a newsgroup for longer than the \&\fIexpire.ctl\fR rules indicate, when they're crossposted. \fB\-e\fR and \fB\-k\fR cannot be used at the same time. This flag is ignored if \&\fIgroupbaseexpiry\fR is false. .IP "\fB\-N\fR" 4 .IX Item "-N" Apply \fIexpire.ctl\fR rules to expire articles even from storage methods that have self-expire functionality. This may remove articles from self-expiring storage methods before the articles \*(L"naturally\*(R" expire. This flag is ignored if \fIgroupbaseexpiry\fR is false. .IP "\fB\-p\fR" 4 .IX Item "-p" By default, \fBexpireover\fR bases decisions on whether to remove an article on the arrival time on the server. This means that articles may be kept a little longer than if the decision were based on the article's posting date. If this option is given, expiration decisions are based on the article posting date instead. This flag is ignored if \fIgroupbaseexpiry\fR is false. .IP "\fB\-q\fR" 4 .IX Item "-q" \&\fBexpireover\fR normally prints statistics at the end of the expiration process. \fB\-q\fR suppresses this report. This flag is ignored if \&\fIgroupbaseexpiry\fR is false. .IP "\fB\-s\fR" 4 .IX Item "-s" \&\fBexpireover\fR normally only checks the existence of articles in the news spool if querying the storage method for that article to see if it still exists is considered \*(L"inexpensive\*(R". To always check the existence of all articles regardless of how resource-intensive this may be, use the \fB\-s\fR flag. See \fIstorage.conf\fR\|(5) for more information about this metric. .IP "\fB\-w\fR \fIoffset\fR" 4 .IX Item "-w offset" \&\*(L"Warps\*(R" time so that \fBexpireover\fR thinks that it's running at some time other than the current time. This is occasionally useful to force groups to be expired or not expired without changing \fIexpire.ctl\fR for the expire run. \fIoffset\fR should be a signed floating point number specifying the number of days difference from the current time to use as \*(L"now\*(R". This flag is ignored if \fIgroupbaseexpiry\fR is false. .IP "\fB\-z\fR \fIrmfile\fR" 4 .IX Item "-z rmfile" Don't remove articles immediately but instead write the path to the article or the token of the article to \fIrmfile\fR, which is suitable input for \fIfastrm\fR\|(1). This can substantially speed up deletion of expired articles for those storage methods where each article is a single file (such as tradspool and timehash). See the description of the \fIdelayrm\fR keyword in \fInews.daily\fR\|(8) for more details. This flag is ignored if \&\fIgroupbaseexpiry\fR is false. .IP "\fB\-Z\fR \fIlowmarkfile\fR" 4 .IX Item "-Z lowmarkfile" Write the lowest article numbers for each newsgroup as it's expired to the specified file. This file is then suitable for \f(CW\*(C`ctlinnd lowmark\*(C'\fR. See \&\fIctlinnd\fR\|(8) for more information. .SH "EXAMPLES" .IX Header "EXAMPLES" Normally \fBexpireover\fR is invoked from \fInews.daily\fR\|(8), which handles such things as processing the \fIrmfile\fR and \fIlowmarkfile\fR if necessary. Sometimes it's convenient to manually expire a particular newsgroup, however. This can be done with a command like: .PP .Vb 2 \& echo example.test | expireover \-f \- \-Z /lowmark \& ctlinnd lowmark /lowmark .Ve .PP This can be particularly useful if a lot of articles in a particular group have expired but the overview information is still present, causing some clients to see a lot of \*(L"this article may have been cancelled\*(R" messages when they first enter the newsgroup. .SH "HISTORY" .IX Header "HISTORY" Written by Rob Robertson and Rich \f(CW$alz\fR (with help from Dave Lawrence ) for InterNetNews. .PP \&\f(CW$Id:\fR expireover.pod 8571 2009\-08\-17 19:10:07Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIactive\fR\|(5), \fIctlinnd\fR\|(8), \fIexpire\fR\|(8), \fIexpire.ctl\fR\|(5), \fIinn.conf\fR\|(5), \&\fInews.daily\fR\|(8). inn-2.6.0/doc/man/storage.conf.50000644000175200017520000005115712575023702015727 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "STORAGE.CONF 5" .TH STORAGE.CONF 5 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" storage.conf \- Configuration file for storage manager .SH "DESCRIPTION" .IX Header "DESCRIPTION" The file \fIpathetc\fR/storage.conf contains the rules to be used in assigning articles to different storage methods. These rules determine where incoming articles will be stored. .PP The storage manager is a unified interface between \s-1INN\s0 and a variety of different storage methods, allowing the news administrator to choose between different storage methods with different trade-offs (or even use several at the same time for different newsgroups, or articles of different sizes). The rest of \s-1INN\s0 need not care what type of storage method was used for a given article; the storage manager will figure this out automatically when that article is retrieved via the storage \&\s-1API\s0. Note that you may also want to see the options provided in \&\fIinn.conf\fR\|(5) regarding article storage. .PP The \fIstorage.conf\fR file consists of a series of storage method entries. Blank lines and lines beginning with a number sign (\f(CW\*(C`#\*(C'\fR) are ignored. The maximum number of characters in each line is 255. The order of entries in this file is important, see below. .PP Each entry specifies a storage method and a set of rules. Articles which match all of the rules of a storage method entry will be stored using that storage method; if an article matches multiple storage method entries, the first one will be used. Each entry is formatted as follows: .PP .Vb 8 \& method { \& class: \& newsgroups: \& size: [,] \& expires: [,] \& options: \& exactmatch: \& } .Ve .PP If spaces or tabs are included in a value, that value must be enclosed in double quotes (""). If either a number sign (\f(CW\*(C`#\*(C'\fR) or a double quote are meant to be included verbatim in a value, they should be escaped with \f(CW\*(C`\e\*(C'\fR. .PP is the name of a storage method to use for articles which match the rules of this entry. The currently available storage methods are: .PP .Vb 5 \& cnfs \& timecaf \& timehash \& tradspool \& trash .Ve .PP See the \*(L"\s-1STORAGE\s0 \s-1METHODS\s0\*(R" section below for more details. .PP The meanings of the keys in each storage method entry are as follows: .IP "\fIclass\fR: " 4 .IX Item "class: " An identifier for this storage method entry. should be a number between 0 and 255. It should be unique across all of the entries in this file. It is mainly used for specifying expiration times by storage class as described in \fIexpire.ctl\fR\|(5); \f(CW\*(C`timehash\*(C'\fR and \f(CW\*(C`timecaf\*(C'\fR will also set the top-level directory in which articles accepted by this storage class are stored. The assignment of a particular number to a storage class is arbitrary but permanent (since it is used in storage tokens). Storage classes can be for instance numbered sequentially in \fIstorage.conf\fR. .IP "\fInewsgroups\fR: " 4 .IX Item "newsgroups: " What newsgroups are stored using this storage method. is a \fIuwildmat\fR\|(3) pattern which is matched against the newsgroups an article is posted to. If \fIstoreonxref\fR in \fIinn.conf\fR is true, this pattern will be matched against the newsgroup names in the Xref: header; otherwise, it will be matched against the newsgroup names in the Newsgroups: header (see \fIinn.conf\fR\|(5) for discussion of the differences between these possibilities). Poison wildmat expressions (expressions starting with \f(CW\*(C`@\*(C'\fR) are allowed and can be used to exclude certain group patterns: articles crossposted to poisoned newsgroups will not be stored using this storage method. The pattern is matched in order. .Sp There is no default newsgroups pattern; if an entry should match all newsgroups, use an explicit \f(CW\*(C`newsgroups: *\*(C'\fR. .IP "\fIsize\fR: [,]" 4 .IX Item "size: [,]" A range of article sizes (in bytes) which should be stored using this storage method. If is \f(CW0\fR or not given, the upper size of articles is limited only by \fImaxartsize\fR in \fIinn.conf\fR. The size: field is optional and may be omitted entirely if you want articles of any size to be stored in this storage method (if, of course, these articles fulfill all the other requirements of this storage method entry). By default, is set to \f(CW0\fR. .IP "\fIexpires\fR: [,]" 4 .IX Item "expires: [,]" A range of article expiration times which should be stored using this storage method. Be careful; this is less useful than it may appear at first. This is based \fBonly\fR on the Expires: header of the article, not on any local expiration policies or anything in \fIexpire.ctl\fR! If is non-zero, then this entry \fBwill not match\fR any article without an Expires: header. This key is therefore only really useful for assigning articles with requested longer expire times to a separate storage method. Articles only match if the time until expiration (that is to say, the amount of time into the future that the Expires: header of the article requests that it remain around) falls in the interval specified by and . .Sp The format of these parameters is \f(CW\*(C`0d0h0m0s\*(C'\fR (days, hours, minutes, and seconds into the future). If is \f(CW\*(C`0s\*(C'\fR or is not specified, there is no upper bound on expire times falling into this entry (note that this key has no effect on when the article will actually be expired, but only on whether or not the article will be stored using this storage method). This field is also optional and may be omitted entirely if you do not want to store articles according to their Expires: header, if any. .Sp A value greater than \f(CW\*(C`0s\*(C'\fR implies that this storage method won't match any article without an Expires: header. .IP "\fIoptions\fR: " 4 .IX Item "options: " This key is for passing special options to storage methods that require them (currently only \f(CW\*(C`cnfs\*(C'\fR). See the \*(L"\s-1STORAGE\s0 \s-1METHODS\s0\*(R" section below for a description of its use. .IP "\fIexactmatch\fR: " 4 .IX Item "exactmatch: " If this key is set to true, all the newsgroups in the Newsgroups: header of incoming articles will be examined to see if they match newsgroups patterns. (Normally, any non-zero number of matching newsgroups is sufficient, provided no newsgroup matches a poison wildmat as described above.) This is a boolean value; \f(CW\*(C`true\*(C'\fR, \f(CW\*(C`yes\*(C'\fR and \f(CW\*(C`on\*(C'\fR are usable to enable this key. The case of these values is not significant. The default is false. .PP If an article matches all of the constraints of an entry, it is stored via that storage method and is associated with that . This file is scanned in order and the first matching entry is used to store the article. .PP If an article does not match any entry, either by being posted to a newsgroup which does not match any of the patterns or by being outside the size and expires ranges of all entries whose newsgroups pattern it does match, the article is not stored and is rejected by \fBinnd\fR. When this happens, the error message: .PP .Vb 1 \& cant store article: no matching entry in storage.conf .Ve .PP is logged to syslog. If you want to silently drop articles matching certain newsgroup patterns or size or expires ranges, assign them to the \f(CW\*(C`trash\*(C'\fR storage method rather than having them not match any storage method entry. .SH "STORAGE METHODS" .IX Header "STORAGE METHODS" Currently, there are five storage methods available. Each method has its pros and cons; you can choose any mixture of them as is suitable for your environment. Note that each method has an attribute \s-1EXPENSIVESTAT\s0 which indicates whether checking the existence of an article is expensive or not. This is used to run \fIexpireover\fR\|(8). .IP "\fBcnfs\fR" 4 .IX Item "cnfs" The \f(CW\*(C`cnfs\*(C'\fR storage method stores articles in large cyclic buffers (\s-1CNFS\s0 stands for Cyclic News File System). Articles are stored in \s-1CNFS\s0 buffers in arrival order, and when the buffer fills, it wraps around to the beginning and stores new articles over the top of the oldest articles in the buffer. The expire time of articles stored in \s-1CNFS\s0 buffers is therefore entirely determined by how long it takes the buffer to wrap around, which depends on how quickly data is being stored in it. (This method is therefore said to have self-expire functionality.) \s-1EXPENSIVESTAT\s0 is false for this method. .Sp \&\s-1CNFS\s0 has its own configuration file, \fIcycbuff.conf\fR, which describes some subtleties to the basic description given above. Storage method entries for the \f(CW\*(C`cnfs\*(C'\fR storage method must have an options: field specifying the metacycbuff into which articles matching that entry should be stored; see \fIcycbuff.conf\fR\|(5) for details on metacycbuffs. .Sp Advantages: By far the fastest of all storage methods (except for \f(CW\*(C`trash\*(C'\fR), since it eliminates the overhead of dealing with a file system and creating new files. Unlike all other storage methods, it does not require manual article expiration. With \s-1CNFS\s0, the server will never throttle itself due to a full spool disk, and groups are restricted to just the buffer files given so that they can never use more than the amount of disk space allocated to them. .Sp Disadvantages: Article retention times are more difficult to control because old articles are overwritten automatically. Attacks on Usenet, such as flooding or massive amounts of spam, can result in wanted articles expiring much faster than intended (with no warning). .IP "\fBtimecaf\fR" 4 .IX Item "timecaf" This method stores multiple articles in one file, whose name is based on the article's arrival time and the storage class. The file name will be: .Sp .Vb 1 \& /timecaf\-nn/bb/aacc.CF .Ve .Sp where \f(CW\*(C`nn\*(C'\fR is the hexadecimal value of , \f(CW\*(C`bb\*(C'\fR and \f(CW\*(C`aacc\*(C'\fR are the hexadecimal components of the arrival time, and \f(CW\*(C`CF\*(C'\fR is a hardcoded extension. (The arrival time, in seconds since the epoch, is converted to hexadecimal and interpreted as \f(CW0xaabbccdd\fR, with \&\f(CW\*(C`aa\*(C'\fR, \f(CW\*(C`bb\*(C'\fR, and \f(CW\*(C`cc\*(C'\fR used to build the path.) This method does not have self-expire functionality (meaning \fBexpire\fR has to run periodically to delete old articles). \s-1EXPENSIVESTAT\s0 is false for this method. .Sp Advantages: It is roughly four times faster than \f(CW\*(C`timehash\*(C'\fR for article writes, since much of the file system overhead is bypassed, while still retaining the same fine control over article retention time. .Sp Disadvantages: Using this method means giving up all but the most careful manually fiddling with the article spool; in this aspect, it looks like \&\f(CW\*(C`cnfs\*(C'\fR. As one of the newer and least widely used storage types, \&\f(CW\*(C`timecaf\*(C'\fR has not been as thoroughly tested as the other methods. .IP "\fBtimehash\fR" 4 .IX Item "timehash" This method is very similar to \f(CW\*(C`timecaf\*(C'\fR except that each article is stored in a separate file. The name of the file for a given article will be: .Sp .Vb 1 \& /time\-nn/bb/cc/yyyy\-aadd .Ve .Sp where \f(CW\*(C`nn\*(C'\fR is the hexadecimal value of , \f(CW\*(C`yyyy\*(C'\fR is a hexadecimal sequence number, and \f(CW\*(C`bb\*(C'\fR, \f(CW\*(C`cc\*(C'\fR, and \f(CW\*(C`aadd\*(C'\fR are components of the arrival time in hexadecimal (the arrival time is interpreted as documented above under \f(CW\*(C`timecaf\*(C'\fR). This method does not have self-expire functionality. \s-1EXPENSIVESTAT\s0 is true for this method. .Sp Advantages: Heavy traffic groups do not cause bottlenecks, and a fine control of article retention time is still possible. .Sp Disadvantages: The ability to easily find all articles in a given newsgroup and manually fiddle with the article spool is lost, and \s-1INN\s0 still suffers from speed degradation due to file system overhead (creating and deleting individual files is a slow operation). .IP "\fBtradspool\fR" 4 .IX Item "tradspool" Traditional spool, or \f(CW\*(C`tradspool\*(C'\fR, is the traditional news article storage format. Each article is stored in an individual text file named: .Sp .Vb 1 \& /news/group/name/nnnnn .Ve .Sp where \f(CW\*(C`news/group/name\*(C'\fR is the name of the newsgroup to which the article was posted with each period changed to a slash, and \f(CW\*(C`nnnnn\*(C'\fR is the sequence number of the article in that newsgroup. For crossposted articles, the article is linked into each newsgroup to which it is crossposted (using either hard or symbolic links). This is the way versions of \s-1INN\s0 prior to 2.0 stored all articles, as well as being the article storage format used by C News and earlier news systems. This method does not have self-expire functionality. \s-1EXPENSIVESTAT\s0 is true for this method. .Sp Advantages: It is widely used and well-understood; it can read article spools written by older versions of \s-1INN\s0 and it is compatible with all third-party \s-1INN\s0 add-ons. This storage mechanism provides easy and direct access to the articles stored on the server and makes writing programs that fiddle with the news spool very easy, and gives fine control over article retention times. .Sp Disadvantages: It takes a very fast file system and I/O system to keep up with current Usenet traffic volumes due to file system overhead. Groups with heavy traffic tend to create a bottleneck because of inefficiencies in storing large numbers of article files in a single directory. It requires a nightly expire program to delete old articles out of the news spool, a process that can slow down the server for several hours or more. .IP "\fBtrash\fR" 4 .IX Item "trash" This method silently discards all articles stored in it. Its only real uses are for testing and for silently discarding articles matching a particular storage method entry (for whatever reason). Articles stored in this method take up no disk space and can never be retrieved, so this method has self-expire functionality of a sort. \s-1EXPENSIVESTAT\s0 is false for this method. .SH "EXAMPLES" .IX Header "EXAMPLES" The following sample \fIstorage.conf\fR file would store all articles posted to alt.binaries.* in the \f(CW\*(C`BINARIES\*(C'\fR \s-1CNFS\s0 metacycbuff, all articles over roughly 50\ \s-1KB\s0 in any other hierarchy in the \f(CW\*(C`LARGE\*(C'\fR \s-1CNFS\s0 metacycbuff, all other articles in alt.* in one timehash class, and all other articles in any newsgroups in a second timehash class, except for the internal.* hierarchy which is stored in traditional spool format. .PP .Vb 10 \& method tradspool { \& class: 1 \& newsgroups: internal.* \& } \& method cnfs { \& class: 2 \& newsgroups: alt.binaries.* \& options: BINARIES \& } \& method cnfs { \& class: 3 \& newsgroups: * \& size: 50000 \& options: LARGE \& } \& method timehash { \& class: 4 \& newsgroups: alt.* \& } \& method timehash { \& class: 5 \& newsgroups: * \& } .Ve .PP Notice that the last storage method entry will catch everything. This is a good habit to get into; make sure that you have at least one catch-all entry just in case something you did not expect falls through the cracks. Notice also that the special rule for the internal.* hierarchy is first, so it will catch even articles crossposted to alt.binaries.* or over 50\ \s-1KB\s0 in size. .PP As for poison wildmat expressions, if you have for instance an article crossposted between misc.foo and misc.bar, the pattern: .PP .Vb 1 \& misc.*,!misc.bar .Ve .PP will match that article whereas the pattern: .PP .Vb 1 \& misc.*,@misc.bar .Ve .PP will not match that article. An article posted only to misc.bar will fail to match either pattern. .PP Usually, high-volume groups and groups whose articles do not need to be kept around very long (binaries groups, *.jobs*, news.lists.filters, etc.) are stored in \s-1CNFS\s0 buffers. Use the other methods (or \s-1CNFS\s0 buffers again) for everything else. However, it is as often as not most convenient to keep in \&\f(CW\*(C`tradspool\*(C'\fR special hierarchies like local hierarchies and hierarchies that should never expire or through the spool of which you need to go manually. .SH "HISTORY" .IX Header "HISTORY" Written by Katsuhiro Kondou for InterNetNews. Rewritten into \s-1POD\s0 by Julien Elie. .PP \&\f(CW$Id:\fR storage.conf.pod 8357 2009\-02\-27 17:56:00Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIcycbuff.conf\fR\|(5), \fIexpire.ctl\fR\|(5), \fIexpireover\fR\|(8), \fIinn.conf\fR\|(5), \fIinnd\fR\|(8), \&\fIuwildmat\fR\|(3). inn-2.6.0/doc/man/Makefile0000644000175200017520000000631412575023702014704 0ustar iuliusiulius## $Id: Makefile 9476 2013-05-24 16:00:23Z iulius $ include ../../Makefile.global top = ../.. SEC1 = convdate.1 fastrm.1 getlist.1 grephistory.1 inews.1 innconfval.1 \ innmail.1 nntpget.1 pgpverify.1 pullnews.1 rnews.1 \ shlock.1 shrinkfile.1 simpleftp.1 sm.1 SEC3 = clientlib.3 dbz.3 inndcomm.3 libauth.3 libinn.3 libinnhist.3 \ libstorage.3 list.3 qio.3 tst.3 uwildmat.3 SEC3PM = INN__Config.3pm \ INN__Utils__Shlock.3pm SEC5 = active.5 active.times.5 buffindexed.conf.5 control.ctl.5 \ cycbuff.conf.5 distrib.pats.5 distributions.5 expire.ctl.5 history.5 incoming.conf.5 \ inn.conf.5 innfeed.conf.5 innwatch.ctl.5 moderators.5 motd.news.5 \ newsfeeds.5 newsgroups.5 newslog.5 nnrpd.track.5 nntpsend.ctl.5 ovdb.5 \ passwd.nntp.5 inn-radius.conf.5 readers.conf.5 \ storage.conf.5 subscriptions.5 SEC8 = actsync.8 archive.8 batcher.8 buffchan.8 ckpasswd.8 \ cnfsheadconf.8 cnfsstat.8 controlchan.8 ctlinnd.8 cvtbatch.8 \ docheckgroups.8 domain.8 expire.8 expireover.8 expirerm.8 \ filechan.8 ident.8 \ innbind.8 inncheck.8 innd.8 inndf.8 innfeed.8 innreport.8 innstat.8 \ innupgrade.8 innwatch.8 innxbatch.8 innxmit.8 mailpost.8 makedbz.8 \ makehistory.8 mod-active.8 news.daily.8 news2mail.8 ninpaths.8 \ nnrpd.8 nntpsend.8 ovdb_init.8 ovdb_monitor.8 ovdb_server.8 \ ovdb_stat.8 overchan.8 perl-nocem.8 procbatch.8 prunehistory.8 radius.8 \ rc.news.8 scanlogs.8 scanspool.8 send-nntp.8 send-uucp.8 sendinpaths.8 \ tally.control.8 tdx-util.8 tinyleaf.8 writelog.8 all: clobber clean distclean: profiled: install: install-man1 install-man3 install-man3pm install-man5 install-man8 install-man1: for M in $(SEC1) ; do \ $(CP_MAN) $$M $D$(MAN1)/$$M ; \ done install-man3: for M in $(SEC3) ; do \ $(CP_MAN) $$M $D$(MAN3)/$$M ; \ done install-man3pm: $(CP_MAN) INN__Config.3pm $D$(MAN3PM)/INN\:\:Config.$(MAN3PM_EXT) $(CP_MAN) INN__Utils__Shlock.3pm $D$(MAN3PM)/INN\:\:Utils\:\:Shlock.$(MAN3PM_EXT) # We also create symbolic links between config files and programs. # We try to use relative symbolic links, when possible. As '-ef' is # not required by POSIX, we make absolute symbolic links before this # test, in case it fails. install-man5: for M in $(SEC5) ; do \ $(CP_MAN) $$M $D$(MAN5)/$$M ; \ done rm -f $D$(MAN5)/motd.innd.5 $D$(MAN5)/motd.nnrpd.5 $(LN_S) motd.news.5 $D$(MAN5)/motd.innd.5 $(LN_S) motd.news.5 $D$(MAN5)/motd.nnrpd.5 rm -f $D$(MAN5)/localgroups.5 $D$(MAN5)/nocem.ctl.5 $(LN_S) $(MAN8)/docheckgroups.8 $D$(MAN5)/localgroups.5 $(LN_S) $(MAN8)/perl-nocem.8 $D$(MAN5)/nocem.ctl.5 if [ $D$(MAN5)/../man8 -ef $D$(MAN8) ] ; then \ rm -f $D$(MAN5)/localgroups.5 $D$(MAN5)/nocem.ctl.5 ; \ $(LN_S) ../man8/docheckgroups.8 $D$(MAN5)/localgroups.5 ; \ $(LN_S) ../man8/perl-nocem.8 $D$(MAN5)/nocem.ctl.5 ; \ fi # auth_krb5 is conditionally compiled, so handle it specially. actsync(8) # also covers actsyncd. install-man8: for M in $(SEC8) ; do \ $(CP_MAN) $$M $D$(MAN8)/$$M ; \ done rm -f $D$(MAN8)/actsyncd.8 $(LN_S) actsync.8 $D$(MAN8)/actsyncd.8 rm -f $D$(MAN8)/imapfeed.8 $(LN_S) innfeed.8 $D$(MAN8)/imapfeed.8 rm -f $D$(MAN8)/inpaths.8 $(LN_S) ninpaths.8 $D$(MAN8)/inpaths.8 if [ x"$(KRB5_AUTH)" != x ] ; then \ $(CP_MAN) auth_krb5.8 $D$(MAN8)/auth_krb5.8 ; \ fi inn-2.6.0/doc/man/sm.10000644000175200017520000002040312575023702013740 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "SM 1" .TH SM 1 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" sm \- Command\-line interface to the INN storage manager .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fBsm\fR [\fB\-cdHiqRrSs\fR] [\fItoken\fR ...] .SH "DESCRIPTION" .IX Header "DESCRIPTION" The \s-1INN\s0 storage manager is the subsystem that stores and keeps track of all of the articles and what storage backend they're in. All stored articles are assigned a storage \s-1API\s0 token. \fBsm\fR is a command-line interface to that storage manager, primarily used to retrieve articles by those tokens but also to perform other operations on the storage subsystem. .PP \&\fItoken\fR is the token of an article (the same thing that's returned by \&\fBgrephistory\fR or stored in the \fIhistory\fR file). It looks something like: .PP .Vb 1 \& @0502000005A4000000010000000000000000@ .Ve .PP Any number of tokens can be given on the command-line for any function other than \fB\-s\fR. If none are, \fBsm\fR normally reads tokens from standard input, one per line. The default operation is to retrieve and write to standard output the corresponding article for each token given. .PP If \fB\-s\fR is given, \fBsm\fR instead stores the article given on standard input (in native format, not wire format) using the standard rules of the storage subsystem. If the article is stored successfully, the token of the article is printed to standard output. Please note that this does not make any attempt to write a history entry or any overview data, and is therefore only useful under very specific circumstances. .SH "OPTIONS" .IX Header "OPTIONS" .IP "\fB\-c\fR" 4 .IX Item "-c" Show a clear, decoded form of the storage \s-1API\s0 token. Each part of the token is explained, in a human-readable string. Amongst other elements, this command gives the path to where the corresponding article is supposed to be stored. .IP "\fB\-d\fR, \fB\-r\fR" 4 .IX Item "-d, -r" Rather than retrieving the specified article, remove the article. This will delete the article out of the news spool and it will not subsequently be retrievable by any part of \s-1INN\s0. It's equivalent to \f(CW\*(C`ctlinnd cancel\*(C'\fR except it takes a storage \s-1API\s0 token instead of a message-ID. .IP "\fB\-H\fR" 4 .IX Item "-H" Retrieve only the headers of the article rather than the entire article. This option cannot be used with \fB\-d\fR, \fB\-r\fR, \fB\-i\fR, or \fB\-S\fR. .IP "\fB\-i\fR" 4 .IX Item "-i" Show the newsgroup name and article number associated with the token rather than the article itself. Note that for crossposted articles, only the first newsgroup and article number to which the article is associated will be returned. .IP "\fB\-q\fR" 4 .IX Item "-q" Suppress all error messages except usage errors. .IP "\fB\-R\fR" 4 .IX Item "-R" Display the raw article. This means that line endings won't be converted to native line endings and will be left as \s-1CRLF\s0 sequences; leading periods will still be escaped for sending over \s-1NNTP\s0, and the article will end in a \s-1CRLF\s0.CRLF sequence. .IP "\fB\-S\fR" 4 .IX Item "-S" Write the article to standard output in the format used by \fBrnews\fR spool files. Multiple articles can be written in this format, and the resulting output can be fed to \fBrnews\fR (on another system, for example) to inject those articles into \s-1INN\s0. This option cannot be used with \fB\-d\fR, \fB\-r\fR, \&\fB\-H\fR, \fB\-i\fR, or \fB\-R\fR. .IP "\fB\-s\fR" 4 .IX Item "-s" Store the article given on standard input using the normal storage rules for articles as configured in \fIstorage.conf\fR\|(5). Print the new token for the message to standard output if it is stored successfully. If this option is given, no other options except possibly \fB\-q\fR should be given. .SH "EXIT STATUS" .IX Header "EXIT STATUS" If all operations were successful, \fBsm\fR exits with status 0. If an operation on any of the provided tokens fails, \fBsm\fR will exit with status 1, even if the operations on other tokens were successful. In other words, if twenty tokens are fed to \f(CW\*(C`sm \-r\*(C'\fR on stdin, 19 articles were successfully removed, but the sixth article couldn't be found, \fBsm\fR will still exit with status 1. .PP This means that if you need to be sure whether a particular operation succeeded, you should run \fBsm\fR on one token at a time. .SH "HISTORY" .IX Header "HISTORY" Written by Katsuhiro Kondou for InterNetNews. Rewritten in \s-1POD\s0 by Russ Allbery . .PP \&\f(CW$Id:\fR sm.pod 9767 2014\-12\-07 21:13:43Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIctlinnd\fR\|(8), \fIgrephistory\fR\|(1), \fIhistory\fR\|(5), \fIrnews\fR\|(1), \fIstorage.conf\fR\|(5). inn-2.6.0/doc/man/inndcomm.30000644000175200017520000000662012575023702015134 0ustar iuliusiulius.\" $Revision: 7901 $ .TH INNDCOMM 3 .SH NAME inndcomm \- INND communication part of InterNetNews library .SH SYNOPSIS .nf .ta \w' unsigned long 'u .B #include "inn/inndcomm.h" .B "int" .B "ICCopen()" .B "int" .B "ICCclose()" .B "void" .B "ICCsettimeout(i)" .B " int i;" .B "int" .B "ICCcommand(cmd, argv, replyp)" .B " char cmd;" .B " char *argv[];" .B " char **replyp;" .B "int" .B "ICCcancel(mesgid)" .B " char *mesgid;" .B "int" .B "ICCreserve(why)" .B " char *why;" .B "int" .B "ICCpause(why)" .B " char *why;" .B "int" .B "ICCgo(why)" .B " char *why;" .B "extern char *ICCfailure;" .fi .SH DESCRIPTION The routines described in this manual page are part of the InterNetNews library, .IR libinn (3). They are used to send commands to a running .IR innd (8) daemon on the local host. The letters ``ICC'' stand for .IR I nnd .IR C ontrol .IR C ommand. .PP .I ICCopen creates a Unix-domain datagram socket and binds it to the server's control socket, if .I is defined. Otherwise it creates a named pipe for communicating with the server. It returns \-1 on failure or zero on success. This routine must be called before any other routine. .PP .I ICCclose closes any descriptors that have been created by .IR ICCopen . It returns \-1 on failure or zero on success. .PP .I ICCsettimeout can be called before any of the following routines to determine how long the library should wait before giving up on getting the server's reply. This is done by setting and catching a SIGALRM .IR signal (2). If the timeout is less then zero then no reply will be waited for. The SC_SHUTDOWN, SC_XABORT, and SC_XEXEC commands do not get a reply either. The default, which can be obtained by setting the timeout to zero, is to wait until the server replies. .PP .I ICCcommand sends the command .I cmd with parameters .I argv to the server. It returns \-1 on error. If the server replies, and .I replyp is not NULL, it will be filled in with an allocated buffer that contains the full text of the server's reply. This buffer is a string in the form of ``'' where ``digits'' is the text value of the recommended exit code; zero indicates success. Replies longer then 4000 bytes will be truncated. The possible values of .I cmd are defined in the ``inn/inndcomm.h'' header file. The parameters for each command are described in .IR ctlinnd (8). This routine returns \-1 on communication failure, or the exit status sent by the server which will never be negative. .PP .I ICCcancel sends a ``cancel'' message to the server. .I Mesgid is the Message-ID of the article that should be cancelled. The return value is the same as for .IR ICCcommand . .PP .IR ICCpause , .IR ICCreserve , and .I ICCgo send a ``pause,'' ``reserve,'' or ``go'' command to the server, respectively. If .I ICCreserve is used, then the .I why value used in the .I ICCpause invocation must match; the value used in the .I ICCgo invocation must always match that the one used in the .I ICCpause invocation. The return value for all three routines is the same as for .IR ICCcommand . .PP If any routine described above fails, the .I ICCfailure variable will identify the system call that failed. .SH HISTORY Written by Rich $alz for InterNetNews. .de R$ This is revision \\$3, dated \\$4. .. .R$ $Id: inndcomm.3 7901 2008-06-22 20:34:26Z iulius $ .SH "SEE ALSO" ctlinnd(8), innd(8), libinn(3). inn-2.6.0/doc/man/distrib.pats.50000644000175200017520000001373212575023702015742 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "DISTRIB.PATS 5" .TH DISTRIB.PATS 5 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" distrib.pats \- Default values for the Distribution: header .SH "DESCRIPTION" .IX Header "DESCRIPTION" The file \fIpathetc\fR/distrib.pats is used by \fBnnrpd\fR to determine the default value of the Distribution: header. Blank lines and lines beginning with a number sign (\f(CW\*(C`#\*(C'\fR) are ignored. All other lines consist of three fields separated by a colon: .PP .Vb 1 \& :: .Ve .PP The first field is the weight to assign to this match. If a newsgroup matches multiple lines, the line with the highest weight is used. This should be an arbitrary integer greater than zero. The order of lines in the file is only important if groups have equal weight (in which case, the first matching line will be used). .PP The second field is either the name of a newsgroup or a \fIuwildmat\fR\|(3)\-style pattern to specify a set of newsgroups. .PP The third field is the value that should be used for the Distribution: header of a posted article, if this line was picked as the best match and no Distribution: header was supplied by the user. It can be an empty string, specifying that no Distribution: header should be added. If it is not empty, it is better to only use US-ASCII characters for that field; otherwise, make sure it is encoded in \s-1UTF\-8\s0. .PP When a post is received by \fBnnrpd\fR that does not already contain a Distribution: header, each newsgroup to which an article is posted will be checked against this file in turn, and the matching line with the highest weight will be used as the value of the Distribution: header. If no lines match, or if the matching line has an empty string for its third field, no header will be added. In case a distribution is added by \fBnnrpd\fR, make sure it is not rejected by the \fInewsfeeds\fR \f(CW\*(C`ME\*(C'\fR entry. .PP A list of recommended distributions can be specified and described in \fIdistributions\fR\|(5). .SH "HISTORY" .IX Header "HISTORY" Written by Rich \f(CW$alz\fR for InterNetNews. Converted to \&\s-1POD\s0 by Russ Allbery . .PP \&\f(CW$Id:\fR distrib.pats.pod 9767 2014\-12\-07 21:13:43Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIdistributions\fR\|(5), \fIinn.conf\fR\|(5), \fInewsfeeds\fR\|(5), \fInnrpd\fR\|(8), \fIuwildmat\fR\|(3). inn-2.6.0/doc/man/controlchan.80000644000175200017520000001563112575023702015651 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "CONTROLCHAN 8" .TH CONTROLCHAN 8 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" controlchan \- Channel\-fed control message handler .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fBcontrolchan\fR [\fB\-ch\fR] .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\fBcontrolchan\fR removes the responsibility for handling control messages (except cancels) from \fBinnd\fR and instead processes them from a channel or file feed. .PP The two Perl modules \f(CW\*(C`Encode\*(C'\fR and \f(CW\*(C`MIME::Parser\*(C'\fR are required by \&\fBcontrolchan\fR. .PP To reduce load, \fBcontrolchan\fR keeps a copy of \fIcontrol.ctl\fR and \&\fIcontrol.ctl.local\fR in memory and checks permissions (including any required \&\s-1PGP\s0 headers) before any scripts are called. These two configuration files are automatically reloaded when \fBcontrolchan\fR notices they have been modified. Also, the default case of an unrecognized control article is handled internally. The \f(CW\*(C`drop\*(C'\fR case is handled with far less fuss. .PP Normally, \fBcontrolchan\fR is invoked by \fBinnd\fR as configured in \fInewsfeeds\fR. An example entry is below. Make sure that the newsgroup \f(CW\*(C`control.cancel\*(C'\fR exists so that \fBcontrolchan\fR does not have to scan through cancels, which it will not be processing anyway. .PP .Vb 4 \& controlchan!\e \& :!*,control,control.*,!control.cancel\e \& :AC,Tc,Wnsm\e \& :/controlchan .Ve .PP \&\fBcontrolchan\fR can also be manually invoked with a mere path to a file (containing a complete control article with its headers and its body) or a token on its standard input: .PP .Vb 2 \& echo \*(Aq/path/to/a/control/article\*(Aq | controlchan \& echo \*(Aq@0303465234000000000000235AE000000002@\*(Aq | controlchan .Ve .PP Note that in the (very, very unlikely) event that you need to process ihave/sendme control messages, be sure that \fIlogipaddr\fR is set to false in \fIinn.conf\fR, because in this case \fBcontrolchan\fR needs a site name, not an \s-1IP\s0 address. .PP \&\fBcontrolchan\fR tries to report all log messages through \fIsyslog\fR\|(3), unless connected to an interactive terminal. To enable \fIsyslog\fR\|(3)'ing for versions of Perl prior to 5.6.0, you will need to have run \fBh2ph\fR on your system include files at some point (this is required to make \f(CW\*(C`Sys::Syslog\*(C'\fR work). If you have not done so, do this: .PP .Vb 2 \& cd /usr/include \& h2ph * sys/* .Ve .PP If you run FreeBSD, you will need to run the following in addition: .PP .Vb 1 \& h2ph machine/* .Ve .SH "OPTIONS" .IX Header "OPTIONS" .IP "\fB\-c\fR" 4 .IX Item "-c" By default, \fBcontrolchan\fR does not process articles whose Date: or Injection-Date: header fields are too far in the past (more than \&\fIartcutoff\fR days, as set in \fIinn.conf\fR) or one day in the future. It allows to prevent a malicious replay of old control articles. .Sp Using the \fB\-c\fR flag disables this check on the cutoff date. .IP "\fB\-h\fR" 4 .IX Item "-h" Gives usage information. .SH "HISTORY" .IX Header "HISTORY" Written by Katsuhiro Kondou for InterNetNews. Converted to \&\s-1POD\s0 by Julien Elie. .PP \&\f(CW$Id:\fR controlchan.pod 9240 2011\-07\-12 09:51:28Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIcontrol.ctl\fR\|(5), \fIinn.conf\fR\|(5). inn-2.6.0/doc/man/uwildmat.30000644000175200017520000003113712575023702015157 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "uwildmat 3" .TH uwildmat 3 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" uwildmat, uwildmat_simple, uwildmat_poison \- Perform wildmat matching .SH "SYNOPSIS" .IX Header "SYNOPSIS" .Vb 1 \& #include \& \& bool uwildmat(const char *text, const char *pattern); \& \& bool uwildmat_simple(const char *text, const char *pattern); \& \& enum uwildmat uwildmat_poison(const char *text, const char *pattern); .Ve .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\fBuwildmat\fR compares \fItext\fR against the wildmat expression \fIpattern\fR, returning true if and only if the expression matches the text. \f(CW\*(C`@\*(C'\fR has no special meaning in \fIpattern\fR when passed to \fBuwildmat\fR. Both \fItext\fR and \fIpattern\fR are assumed to be in the \s-1UTF\-8\s0 character encoding, although malformed \s-1UTF\-8\s0 sequences are treated in a way that attempts to be mostly compatible with single-octet character sets like \s-1ISO\s0 8859\-1. (In other words, if you try to match \s-1ISO\s0 8859\-1 text with these routines everything should work as expected unless the \s-1ISO\s0 8859\-1 text contains valid \s-1UTF\-8\s0 sequences, which thankfully is somewhat rare.) .PP \&\fBuwildmat_simple\fR is identical to \fBuwildmat\fR except that neither \f(CW\*(C`!\*(C'\fR nor \f(CW\*(C`,\*(C'\fR have any special meaning and \fIpattern\fR is always treated as a single pattern. This function exists solely to support legacy interfaces like \s-1NNTP\s0's \s-1XPAT\s0 command, and should be avoided when implementing new features. .PP \&\fBuwildmat_poison\fR works similarly to \fBuwildmat\fR, except that \f(CW\*(C`@\*(C'\fR as the first character of one of the patterns in the expression (see below) \&\*(L"poisons\*(R" the match if it matches. \fBuwildmat_poison\fR returns \&\fB\s-1UWILDMAT_MATCH\s0\fR if the expression matches the text, \fB\s-1UWILDMAT_FAIL\s0\fR if it doesn't, and \fB\s-1UWILDMAT_POISON\s0\fR if the expression doesn't match because a poisoned pattern matched the text. These enumeration constants are defined in the \fBinn/libinn.h\fR header. .SH "WILDMAT EXPRESSIONS" .IX Header "WILDMAT EXPRESSIONS" A wildmat expression follows rules similar to those of shell filename wildcards but with some additions and changes. A wildmat \fIexpression\fR is composed of one or more wildmat \fIpatterns\fR separated by commas. Each character in the wildmat pattern matches a literal occurrence of that same character in the text, with the exception of the following metacharacters: .IP "?" 8 Matches any single character (including a single \s-1UTF\-8\s0 multibyte character, so \f(CW\*(C`?\*(C'\fR can match more than one byte). .IP "*" 8 Matches any sequence of zero or more characters. .IP "\e" 8 .IX Item "" Turns off any special meaning of the following character; the following character will match itself in the text. \f(CW\*(C`\e\*(C'\fR will escape any character, including another backslash or a comma that otherwise would separate a pattern from the next pattern in an expression. Note that \f(CW\*(C`\e\*(C'\fR is not special inside a character range (no metacharacters are). .IP "[...]" 8 A character set, which matches any single character that falls within that set. The presence of a character between the brackets adds that character to the set; for example, \f(CW\*(C`[amv]\*(C'\fR specifies the set containing the characters \f(CW\*(C`a\*(C'\fR, \f(CW\*(C`m\*(C'\fR, and \f(CW\*(C`v\*(C'\fR. A range of characters may be specified using \f(CW\*(C`\-\*(C'\fR; for example, \f(CW\*(C`[0\-5abc]\*(C'\fR is equivalent to \f(CW\*(C`[012345abc]\*(C'\fR. The order of characters is as defined in the \s-1UTF\-8\s0 character set, and if the start character of such a range falls after the ending character of the range in that ranking the results of attempting a match with that pattern are undefined. .Sp In order to include a literal \f(CW\*(C`]\*(C'\fR character in the set, it must be the first character of the set (possibly following \f(CW\*(C`^\*(C'\fR); for example, \f(CW\*(C`[]a]\*(C'\fR matches either \f(CW\*(C`]\*(C'\fR or \f(CW\*(C`a\*(C'\fR. To include a literal \f(CW\*(C`\-\*(C'\fR character in the set, it must be either the first or the last character of the set. Backslashes have no special meaning inside a character set, nor do any other of the wildmat metacharacters. .IP "[^...]" 8 A negated character set. Follows the same rules as a character set above, but matches any character \fBnot\fR contained in the set. So, for example, \&\f(CW\*(C`[^]\-]\*(C'\fR matches any character except \f(CW\*(C`]\*(C'\fR and \f(CW\*(C`\-\*(C'\fR. .PP In addition, \f(CW\*(C`!\*(C'\fR (and possibly \f(CW\*(C`@\*(C'\fR) have special meaning as the first character of a pattern; see below. .PP When matching a wildmat expression against some text, each comma-separated pattern is matched in order from left to right. In order to match, the pattern must match the whole text; in regular expression terminology, it's implicitly anchored at both the beginning and the end. For example, the pattern \f(CW\*(C`a\*(C'\fR matches only the text \f(CW\*(C`a\*(C'\fR; it doesn't match \f(CW\*(C`ab\*(C'\fR or \f(CW\*(C`ba\*(C'\fR or even \f(CW\*(C`aa\*(C'\fR. If none of the patterns match, the whole expression doesn't match. Otherwise, whether the expression matches is determined entirely by the rightmost matching pattern; the expression matches the text if and only if the rightmost matching pattern is not negated. .PP For example, consider the text \f(CW\*(C`news.misc\*(C'\fR. The expression \f(CW\*(C`*\*(C'\fR matches this text, of course, as does \f(CW\*(C`comp.*,news.*\*(C'\fR (because the second pattern matches). \f(CW\*(C`news.*,!news.misc\*(C'\fR does not match this text because both patterns match, meaning that the rightmost takes precedence, and the rightmost matching pattern is negated. \f(CW\*(C`news.*,!news.misc,*.misc\*(C'\fR does match this text, since the rightmost matching pattern is not negated. .PP Note that the expression \f(CW\*(C`!news.misc\*(C'\fR can't match anything. Either the pattern doesn't match, in which case no patterns match and the expression doesn't match, or the pattern does match, in which case because it's negated the expression doesn't match. \f(CW\*(C`*,!news.misc\*(C'\fR, on the other hand, is a useful pattern that matches anything except \f(CW\*(C`news.misc\*(C'\fR. .PP \&\f(CW\*(C`!\*(C'\fR has significance only as the first character of a pattern; anywhere else in the pattern, it matches a literal \f(CW\*(C`!\*(C'\fR in the text like any other non-metacharacter. .PP If the \fBuwildmat_poison\fR interface is used, then \f(CW\*(C`@\*(C'\fR behaves the same as \&\f(CW\*(C`!\*(C'\fR except that if an expression fails to match because the rightmost matching pattern began with \f(CW\*(C`@\*(C'\fR, \fB\s-1UWILDMAT_POISON\s0\fR is returned instead of \&\fB\s-1UWILDMAT_FAIL\s0\fR. .PP If the \fBuwildmat_simple\fR interface is used, the matching rules are the same as above except that none of \f(CW\*(C`!\*(C'\fR, \f(CW\*(C`@\*(C'\fR, or \f(CW\*(C`,\*(C'\fR have any special meaning at all and only match those literal characters. .SH "BUGS" .IX Header "BUGS" All of these functions internally convert the passed arguments to const unsigned char pointers. The only reason why they take regular char pointers instead of unsigned char is for the convenience of \s-1INN\s0 and other callers that may not be using unsigned char everywhere they should. In a future revision, the public interface should be changed to just take unsigned char pointers. .SH "HISTORY" .IX Header "HISTORY" Written by Rich \f(CW$alz\fR in 1986, and posted to Usenet several times since then, most notably in comp.sources.misc in March, 1991. .PP Lars Mathiesen enhanced the multi-asterisk failure mode in early 1991. .PP Rich and Lars increased the efficiency of star patterns and reposted it to comp.sources.misc in April, 1991. .PP Robert Elz added minus sign and close bracket handling in June, 1991. .PP Russ Allbery added support for comma-separated patterns and the \f(CW\*(C`!\*(C'\fR and \f(CW\*(C`@\*(C'\fR metacharacters to the core wildmat routines in July, 2000. He also added support for \s-1UTF\-8\s0 characters, changed the default behavior to assume that both the text and the pattern are in \s-1UTF\-8\s0, and largely rewrote this documentation to expand and clarify the description of how a wildmat expression matches. .PP Please note that the interfaces to these functions are named \fBuwildmat\fR and the like rather than \fBwildmat\fR to distinguish them from the \&\fBwildmat\fR function provided by Rich \f(CW$alz\fR's original implementation. While this code is heavily based on Rich's original code, it has substantial differences, including the extension to support \s-1UTF\-8\s0 characters, and has noticable functionality changes. Any bugs present in it aren't Rich's fault. .PP \&\f(CW$Id:\fR uwildmat.pod 9767 2014\-12\-07 21:13:43Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIgrep\fR\|(1), \fIfnmatch\fR\|(3), \fIregex\fR\|(3), \fIregexp\fR\|(3). inn-2.6.0/doc/man/control.ctl.50000644000175200017520000004701012575023702015571 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "CONTROL.CTL 5" .TH CONTROL.CTL 5 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" control.ctl \- Specify handling of Usenet control messages .SH "DESCRIPTION" .IX Header "DESCRIPTION" The file \fIpathetc\fR/control.ctl is used to determine what action is taken when a control message is received. It is read by \fBcontrolchan\fR, which is normally invoked as a channel program by \fBinnd\fR. When \fIcontrol.ctl\fR is modified, \fBcontrolchan\fR notices this automatically and reloads it. .PP If a \fIcontrol.ctl.local\fR file exists in \fIpathetc\fR, it is read by \&\fBcontrolchan\fR after \fIcontrol.ctl\fR (the resulting behaviour is as though the contents of \fIcontrol.ctl.local\fR were at the end of \&\fIcontrol.ctl\fR). This local file is formatted like \fIcontrol.ctl\fR and is intended to contain local customization. It is also automatically reloaded when modified. .PP Blank lines and lines beginning with a number sign (\f(CW\*(C`#\*(C'\fR) are ignored. All other lines should consist of four fields separated by colons: .PP .Vb 1 \& ::: .Ve .PP Lines are matched in order and the last matching line in the file will be used, except for checkgroups messages which are handled differently (every matching line is used). .PP The first field, , is the type of control message for which this line is valid. It should either be the name of a control message or the word \f(CW\*(C`all\*(C'\fR to indicate that it applies to all control messages. Besides, the following special types are understood: .IP "\fB/encoding/\fR" 4 .IX Item "/encoding/" This type specifies the encoding of newgroup and checkgroups control messages so that new descriptions could be decoded the right way. .Sp .Vb 1 \& /encoding/:*:cn.*:gb18030 .Ve .Sp means that a description for a newsgroup in the Chinese cn.* hierarchy will be decoded as though it were encoded in \s-1GB18030\s0, unless a charset is specified in the control message (in such a case, the charset mentioned in the message is used). However, it is possible to override the mentioned charset if \f(CW\*(C`=force\*(C'\fR is appended after the encoding. For instance, .Sp .Vb 1 \& /encoding/:*:scout.forum.chinese:big5=force .Ve .Sp means that the description for scout.forum.chinese will always be decoded as though it were encoded in Big5, no matter the charset of the corresponding control message. .Sp The default value when no encoding is mentioned (or when the specified encoding is unknown) is \f(CW\*(C`CP1252\*(C'\fR. .Sp The last matching line for a given newsgroup name in \fIcontrol.ctl\fR will be used. .IP "\fB/localencoding/\fR" 4 .IX Item "/localencoding/" When this type is used, the line consist of only two fields. The default value when this type does not appear in \fIcontrol.ctl\fR (or when the specified charset is unknown) is equivalent to: .Sp .Vb 1 \& /localencoding/:utf\-8 .Ve .Sp It means that new descriptions in the \fInewsgroups\fR file will be written using \s-1UTF\-8\s0. And \fBcontrolchan\fR will try to read existing descriptions, so as to see whether they should be updated, as though they were encoded in \s-1UTF\-8\s0. .Sp The last matching line in \fIcontrol.ctl\fR will be used. .IP "\fB/maxdocheckgroups/\fR" 4 .IX Item "/maxdocheckgroups/" This type specifies the maximum number of changes that could be made at one time by a checkgroups before bailing and mailing the changes to the admin if no log file was specified. The default value is \f(CW10\fR. .Sp .Vb 2 \& /maxdocheckgroups/:*:*:10 \& /maxdocheckgroups/:*:fr.*:20 .Ve .Sp Such a configuration means that a checkgroups containing 15 changes for the French fr.* hierarchy (newgroups to add, remove or change the status) will be automatically honoured whereas a checkgroups containing 15 changes for france.* will only have the required changes mailed or logged. .Sp The last matching line for a given newsgroup name in \fIcontrol.ctl\fR will be used. .PP The second field, , is a shell-style pattern that matches the e\-mail address of the person posting the message (with the address first converted to lowercase). The matching is done with rules equivalent to those of the shell's \fIcase\fR statement; see \fIsh\fR\|(1) for more details. .PP If the control message is a newgroup or rmgroup, the third field, , is a shell-style pattern matching the newsgroup affected by the control message (especially \f(CW\*(C`?\*(C'\fR matches exactly one character, \&\f(CW\*(C`*\*(C'\fR matches zero or more characters and \f(CW\*(C`|\*(C'\fR permits to match several patterns on the same line \-\-\ for instance \f(CW\*(C`comp.*|humanities.*\*(C'\fR matches every newsgroup whose name begins with \f(CW\*(C`comp.\*(C'\fR or \f(CW\*(C`humanities.\*(C'\fR). If the control message is a checkgroups, the third field is a shell-style pattern matching the newsgroups that should be processed for checking. If the control message is of any other type, the third field is ignored. .PP The fourth field, , specifies what action to take with control messages that match this line. The following actions are understood: .IP "\fBdoit\fR" 4 .IX Item "doit" The action requested by the control message should be performed. It means that the change will be silently performed. For checkgroups messages, depending on the value of \fB/maxdocheckgroups/\fR, the shell commands that should be run may be mailed to the news administrator (the argument to \&\fB\-\-with\-news\-master\fR given at configure time, \f(CW\*(C`usenet\*(C'\fR by default) instead of being performed. .Sp If you always want notification of actions taken, use \f(CW\*(C`doit=mail\*(C'\fR instead (see below). .IP "\fBdoifarg\fR" 4 .IX Item "doifarg" If the control message has an argument, this is equivalent to \fBdoit\fR. If it does not have an argument, this is equivalent to \fBmail\fR. This is only useful for entries for sendsys control messages, allowing a site to request its own \fInewsfeeds\fR entry by posting a \f(CW\*(C`sendsys mysite\*(C'\fR control message, but not allowing the entire \fInewsfeeds\fR file to be sent. This was intended to partially counter so-called \*(L"sendsys bombs\*(R", where forged sendsys control messages were used to mailbomb people. .Sp Processing sendsys control messages is not recommended even with this work-around unless they are authenticated in some fashion. The risk of having news servers turned into anonymous mail bombing services is too high. .IP "\fBdoit\fR=\fIfile\fR" 4 .IX Item "doit=file" The action is performed as in \fBdoit\fR, and additionally a log entry is written to the specified log file \fIfile\fR. If \fIfile\fR is the word \&\f(CW\*(C`mail\*(C'\fR, the log entry is mailed to the news administrator instead. An empty string is equivalent to \fI/dev/null\fR and says to log nothing. .Sp If \fIfile\fR starts with a slash, it is taken as the absolute filename to use for the log file. Otherwise, the filename is formed by prepending \&\fIpathlog\fR and a slash, and appending \f(CW\*(C`.log\*(C'\fR. In other words, an action of \f(CW\*(C`doit=newgroup\*(C'\fR will log to \fIpathlog\fR/newgroup.log. .IP "\fBdrop\fR" 4 .IX Item "drop" No action is taken and the message is ignored. For checkgroups messages, it means that the newsgroups mentioned will be considered as not existent in the checkgroups for its subsequent process. .Sp .Vb 2 \& checkgroups:*:comp.*:doit \& checkgroups:*:*binaries*:drop .Ve .Sp will for instance remove every newsgroup whose name contains \f(CW\*(C`binaries\*(C'\fR in the comp.* hierarchy, even though such groups are mentioned in the checkgroups. (In that example, the removal is performed by the \fBdoit\fR action because \fBdrop\fR does nothing by itself.) .IP "\fBverify\-*\fR" 4 .IX Item "verify-*" If the action starts with the string \f(CW\*(C`verify\-\*(C'\fR, as in: .Sp .Vb 1 \& verify\-news.announce.newgroups .Ve .Sp then \s-1PGP\s0 verification of the control message will be done and the user \s-1ID\s0 of the key of the authenticated signer will be checked against the expected identity defined by the rest of the string (\f(CW\*(C`news.announce.newgroups\*(C'\fR in the above example). This verification is done via \fBpgpverify\fR; see \fIpgpverify\fR\|(8) for more details. .Sp If no logging is specified (with =\fIfile\fR as mentioned below), logging will be done the same as with \fBdoit\fR as described above. .IP "\fBverify\-*\fR=\fBmail\fR" 4 .IX Item "verify-*=mail" \&\s-1PGP\s0 verification is done as for the \fBverify\-*\fR action described above, and notification of successful newgroup and rmgroup control messages and the output of checkgroups messages will be mailed to the news administrator. (In the case of checkgroups messages, this means that the shell script that should be run will be mailed to the administrator. The subject of the mail will contain information on whether the script has already been run, depending on the value of \fB/maxdocheckgroups/\fR.) .IP "\fBverify\-*\fR=\fIfile\fR" 4 .IX Item "verify-*=file" \&\s-1PGP\s0 verification is done as for the \fBverify\-*\fR action described above, and a log entry is written to the specified file as described in \&\fBdoit\fR=\fIfile\fR above. (In the case of checkgroups messages, this means that the shell script output of the checkgroups message will be written to that file. The initial line of the log will contain information on whether the script has already been run, depending on the value of \&\fB/maxdocheckgroups/\fR.) .IP "\fBlog\fR" 4 .IX Item "log" A one-line log message is sent to standard error. \fBinnd\fR normally directs this to \fIpathlog\fR/errlog. .IP "\fBlog\fR=\fIfile\fR" 4 .IX Item "log=file" A log entry is written to the specified log file, which is interpreted as in \fBdoit\fR=\fIfile\fR described above. .IP "\fBmail\fR" 4 .IX Item "mail" A mail message is sent to the news administrator without taking any other action. .PP One of the difference between a \fBdoit\fR or \fBverify\fR action and a \fBmail\fR action for a checkgroups control message lies in what e\-mail is sent; \fBdoit\fR or \fBverify\fR will mail the news administrator a shell script (which may have already been run) to create, delete, or modify newsgroups to match the checkgroups message, whereas \fBmail\fR will just mail relevant lines of the checkgroups for manual processing by the news administrator. .PP Use of the \fBverify\fR action for processing newgroup, rmgroup and checkgroups messages is \s-1STRONGLY\s0 recommended. Abuse of control messages is rampant, and authentication via \s-1PGP\s0 signature is currently the only reliable way to be sure that a control message comes from who it claims to be from. Most major hierarchies are now issuing PGP-authenticated control messages. .PP In order to use \fBverify\fR actions, the \s-1PGP\s0 key ring of the news user must be populated with the \s-1PGP\s0 keys of the hierarchy maintainers whose control messages you want to honour. For more details on PGP-authenticated control messages and the \s-1URL\s0 for downloading the \s-1PGP\s0 keys of major hierarchies, see \fIpgpverify\fR\|(8). .PP Control messages of type cancel are handled internally by \fBinnd\fR and cannot be affected by any of the mechanisms described here. .SH "EXAMPLES" .IX Header "EXAMPLES" With the following three lines in \fIcontrol.ctl\fR: .PP .Vb 3 \& newgroup:*:*:drop \& newgroup:group\-admin@isc.org:comp.*:verify\-news.announce.newgroups \& newgroup:kre@munnari.oz.au:aus.*:mail .Ve .PP a newgroup coming from \f(CW\*(C`group\-admin@isc.org\*(C'\fR will be honoured if it is for a newsgroup in the comp.* hierarchy and if it has a valid signature corresponding to the \s-1PGP\s0 key with a user \s-1ID\s0 of \f(CW\*(C`news.announce.newgroups\*(C'\fR. If any newgroup claiming to be from \f(CW\*(C`kre@munnari.oz.au\*(C'\fR for a newsgroup in the aus.* hierarchy is received, it too will be honoured. All other newgroup messages will be ignored. .PP Besides, if a \fIcontrol.ctl.local\fR file exists and contains: .PP .Vb 1 \& newgroup:*:comp.lang.*:drop .Ve .PP then a newgroup control article for comp.lang.awk will not be honoured even though it comes from \f(CW\*(C`group\-admin@isc.org\*(C'\fR with a valid signature. .PP As for checkgroups, suppose your news server contains these groups for foo.*, all of them being unmoderated (\f(CW\*(C`y\*(C'\fR status in the \fIactive\fR file): .PP .Vb 8 \& foo.bar1 \& foo.bar2.first \& foo.bar2.second \& foo.bar2.third \& foo.bar3 \& foo.bar3.first \& foo.bar3.second \& foo.bar5 .Ve .PP and you receive the following checkgroups by for foo.*: .PP .Vb 5 \& foo.bar1 A valid newsgroup. \& foo.bar3.first Only one newsgroup in foo.bar3.*. \& foo.bar4 A newsgroup you want. \& foo.bar5 A newsgroup you do not want. \& foo.bar5.first Another newsgroup you do not want. .Ve .PP with the following \fIcontrol.ctl\fR entries: .PP .Vb 1 \& /maxdocheckgroups/:*:foo.*:2 \& \& checkgroups:foo@bar.com:foo.*:verify\-key\-foo \& checkgroups:foo@bar.com:foo.bar2.*:doit \& checkgroups:foo@bar.com:foo.bar3.*:mail \& checkgroups:foo@bar.com:foo.bar4|foo.bar4.*:doit \& checkgroups:foo@bar.com:foo.bar5|foo.bar5.*:drop .Ve .PP Then, as \fIcontrol.ctl\fR is processed from bottom, here is what happens: .IP "1." 4 The newsgroups foo.bar5 and foo.bar5.first are marked as unwanted. But nothing is done yet: other \fIcontrol.ctl\fR entries have to be processed with a real action and a set of newsgroups containing foo.bar5 and foo.bar5.first. .IP "2." 4 The newsgroup foo.bar4 is silently created on the news server, with the description \*(L"A newsgroup you want.\*(R" added to the \fInewsgroups\fR file. In the absence of encoding values (either in the checkgroups message or in \fB/encoding/\fR and \fB/localencoding\fR), the default is to decode the sentence as \s-1CP1242\s0 and reencode it as \s-1UTF\-8\s0. .Sp If \f(CW\*(C`doit=mail\*(C'\fR was used, a mail would be sent to the news administrator to inform him that foo.bar4 was successfully created. .IP "3." 4 The newsgroup foo.bar3.second is no longer present. A mail is sent to the news administrator with a shell script to execute. When it is manually executed, foo.bar3.second will be removed. .Sp Note that the descriptions are handled differently and have already been updated without any manual intervention (foo.bar3.first now has the description \*(L"Only one newsgroup in foo.bar3.*.\*(R" and foo.bar3.second no longer has a description). .IP "4." 4 The newsgroups foo.bar2.first, foo.bar2.second and foo.bar2.third are no longer present. However, as the maximum number of changes that could be made at one time by a checkgroups before bailing and mailing the changes to the news administrator is 2, these newsgroups are not removed. A mail is sent with a shell script to manually execute in order to remove these groups from the news server. .Sp Note that their descriptions are removed from the \fInewsgroups\fR file, as well as any other possible descriptions for obsolete newsgroups in foo.bar2.*. .IP "5." 4 The remaining entry is executed if the \s-1PGP\s0 verification of the checkgroups message is successful. Otherwise, nothing is done (especially, foo.bar5 remains on the news server). .Sp In case the \s-1PGP\s0 signature is verified, foo.bar3 and foo.bar5 are removed from the news server. This entry acts upon newsgroups marked as dropped in its scope and newsgroups not already dealt with by previous \fIcontrol.ctl\fR entries (like foo.bar3 because only foo.bar3.* was previously checked). .Sp Note that if you had wanted to keep foo.bar3 or foo.bar5, you could have added them to the \fIlocalgroups\fR file in \fIpathetc\fR. .SH "HISTORY" .IX Header "HISTORY" Written by Rich \f(CW$alz\fR for InterNetNews. Rewritten in \&\s-1POD\s0 by Russ Allbery . .PP \&\f(CW$Id:\fR control.ctl.pod 9767 2014\-12\-07 21:13:43Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIcontrolchan\fR\|(8), \fIinn.conf\fR\|(5), \fIinnd\fR\|(8), \fInewsfeeds\fR\|(5), \fInewsgroups\fR\|(5), \&\fIpgpverify\fR\|(8), \fIsh\fR\|(1). inn-2.6.0/doc/man/rnews.10000644000175200017520000002323012575023702014460 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "RNEWS 1" .TH RNEWS 1 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" rnews \- Inject individual articles and UUCP batches into INN .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fBrnews\fR [\fB\-NUv\fR] [\fB\-h\fR \fIhost\fR] [\fB\-P\fR \fIport\fR] [\fB\-rS\fR \fIserver\fR] [\fIfile\fR] .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\fBrnews\fR injects either individual articles or UUCP-style article batches into an \s-1INN\s0 server. It submits articles via \s-1IHAVE\s0 and is suitable for injecting articles received from other sources; local postings should generally use \fIinews\fR\|(1) instead. It is also used to process spooled messages created by, for example, \fBnnrpd\fR while \fBinnd\fR is not available. .PP The message is read from \fIfile\fR if given, or standard input if no file is given. Articles are sent to the server given in the \fB\-r\fR or \fB\-S\fR command line options if given, otherwise to the server set via \&\fInnrpdposthost\fR in \fIinn.conf\fR, otherwise to the local server. .PP When sent over \s-1UUCP\s0, Usenet articles are typically collected in a single batch to reduce the \s-1UUCP\s0 overhead. Batches can also be compressed to reduce communication time. If the input to \fBrnews\fR does not begin with the characters \f(CW\*(C`#!\*(C'\fR, it is taken to be a single news article; otherwise, the first line of the input is interpreted as a batch command. .PP If the batch command is: .PP .Vb 1 \& #! rnews .Ve .PP then the next bytes (starting with the next line) are read as a news article. After that article is processed, the next line is again treated as a batch command. .PP If the command is: .PP .Vb 1 \& #! cunbatch .Ve .PP then the rest of the input is fed to \f(CW\*(C`gzip \-d\*(C'\fR to uncompress it, and then the resulting uncompressed output is re-read as if it were the original input to \fBrnews\fR. A compressed batch should therefore start with this line and contain a batch of articles separated by \f(CW\*(C`#!\ rnews\*(C'\fR lines and then compressed with \fIcompress\fR\|(1). (Batches compressed with \fIgzip\fR\|(1) should instead use \f(CW\*(C`gunbatch\*(C'\fR as the batch command; \s-1INN\s0 just uses \fBgzip\fR rather than \fBcompress\fR because it can handle \fBcompress\fR\-style compression but is more widely available, due to old patent issues, than \fBcompress\fR.) .PP Otherwise, if the command is any other word, then \fBrnews\fR will try to execute a program with that name, looking for it in the directory \&\fIpathbin\fR/rnews.libexec. The rest of the batch will be fed to that program's standard input, and the standard output from the program will be treated as if it were the original input to \fBrnews\fR. \s-1INN\s0 comes with three such standard batch processors: .IP "\fBbunbatch\fR" 2 .IX Item "bunbatch" It invokes \fBbzip2\fR and should be used for batches compressed with \fBbzip2\fR. .IP "\fBc7unbatch\fR" 2 .IX Item "c7unbatch" It undoes an \s-1ASCII\s0 encoding to recover the original binary compressed stream and then decompresses it as explained above. .IP "\fBgunbatch\fR" 2 .IX Item "gunbatch" It invokes \fBgzip\fR and should be used for batches compressed with \fBgzip\fR. .PP By default, \fBrnews\fR will log and discard any articles that are rejected by the server or cannot be parsed by \fBrnews\fR for some reason (such as a missing header). This default can be changed when compiling \s-1INN\s0 by setting \s-1DO_RNEWS_SAVE_BAD\s0 in \fIinclude/inn/options.h\fR. There is no way to change it at runtime, unfortunately. .SH "OPTIONS" .IX Header "OPTIONS" .IP "\fB\-h\fR \fIhost\fR" 4 .IX Item "-h host" If \fB\-h\fR is given, \fBrnews\fR will log the message \s-1ID\s0 and \fIhost\fR via syslog for each article that it offers to the server. This is used in conjunction with a \s-1UUCP\s0 feed to get a log of the messages received via that feed. This will also be done if the environment variable \s-1UU_MACHINE\s0 is set, but will only be done if \fIhost\fR is not an empty string. (You can therefore turn off logging even if \s-1UU_MACHINE\s0 will be set by passing the flag \f(CW\*(C`\-h \*(Aq\*(Aq\*(C'\fR to \fBrnews\fR.) .IP "\fB\-N\fR" 4 .IX Item "-N" Normally, if unpacking the input batch fails, it is re-spooled to \&\fIpathincoming\fR for another attempt later. If the \fB\-N\fR flag is given, no such re-spooling is done and \fBrnews\fR will instead exit with status 9 if unpacking fails. .IP "\fB\-P\fR \fIport\fR" 4 .IX Item "-P port" Use \fIport\fR as the server port to connect to rather than \fInnrpdpostport\fR (as set in \fIinn.conf\fR). Note that this value is only used if \fBrnews\fR does not connect to the local server (in other words, when \fB\-r\fR or \fB\-S\fR is given or \fInnrpdposthost\fR is set). .IP "\fB\-r\fR \fIserver\fR, \fB\-S\fR \fIserver\fR" 4 .IX Item "-r server, -S server" \&\fB\-r\fR and \fB\-S\fR are synonymous. If either is given, articles will be sent to \fIserver\fR rather than using the local server, overriding also the setting of \fInnrpdposthost\fR in \fIinn.conf\fR. .IP "\fB\-U\fR" 4 .IX Item "-U" If the server is not available, both \fBrnews\fR and \fBnnrpd\fR will spool posts to new files in the \fIpathincoming\fR directory (as specified in \&\fIinn.conf\fR). When \fBrnews\fR is invoked with the \fB\-U\fR option, it scans that directory and processes all spooled messages found there whose filenames do not begin with \f(CW\*(C`.\*(C'\fR, attempting to send them to the server again. It's a good idea to run this periodically out of cron to pick up any articles spooled due to temporary server unavailability. .SH "BUGS" .IX Header "BUGS" \&\fBrnews\fR cannot process articles that have embedded nul characters in them. (Neither can the rest of \s-1INN\s0 at present.) .SH "HISTORY" .IX Header "HISTORY" Written by Rich \f(CW$alz\fR for InterNetNews. Rewritten in \&\s-1POD\s0 by Russ Allbery . .PP \&\f(CW$Id:\fR rnews.pod 9767 2014\-12\-07 21:13:43Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIbzip2\fR\|(1), \fIcompress\fR\|(1), \fIgzip\fR\|(1), \fIinn.conf\fR\|(5), \fIinnd\fR\|(8), \fInnrpd\fR\|(8). inn-2.6.0/doc/man/tinyleaf.80000644000175200017520000002062712575023702015153 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "TINYLEAF 8" .TH TINYLEAF 8 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" tinyleaf \- Very simple IHAVE\-only NNTP server .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fBtinyleaf\fR \fIspool\fR [\fIprocessor\fR] .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\fBtinyleaf\fR is intended to be the simplest possible transit news server that still does something useful. It must be run under \fIinetd\fR\|(8) or some equivalent, and only implements three commands (\s-1HELP\s0, \s-1IHAVE\s0, and \s-1QUIT\s0). When it receives an article, it saves it into the directory \fIspool\fR and, if \fIprocessor\fR is given, passes information about the article to \&\fIprocessor\fR via a pipe. The file name of the article will be the \s-1MD5\s0 hash of its message-ID, and if a file by that name already exists, \&\fBtinyleaf\fR will refuse the article, reporting it as a duplicate. .PP If \fIprocessor\fR is given, it should specify the path to a program. That program is started when \fBtinyleaf\fR starts, and its current working directory will be \fIspool\fR. For each article received by \fBtinyleaf\fR, a single line will be sent to standard input of \fIprocessor\fR. That line will consist of the file name of the received article (relative to \&\fIspool\fR), a single space, and the message-ID of the received article. Note that the message-ID will be taken from the argument to the \s-1IHAVE\s0 command and may not match the Message-ID: header in the article. When \&\fBtinyleaf\fR shuts down, standard input to \fIprocessor\fR will be closed. .PP \&\fBtinyleaf\fR does no syntax verification of received articles whatsoever; it just stores them and optionally passes them off to \fIprocessor\fR. It also never deletes articles; normally, \fIprocessor\fR should do that when it's finished doing whatever it needs to with the article. .PP \&\fBtinyleaf\fR expects \s-1NNTP\s0 commands on standard input and replies on standard output. Status information and any error messages are sent to standard error. It does no authentication; any authentication must be done by \fIinetd\fR\|(8) or by a wrapper program. (One simple authentication mechanism is to invoke \fBtinyleaf\fR via \fItcpd\fR\|(8) from \s-1TCP\s0 wrappers and use \&\fI/etc/hosts.allow\fR and \fI/etc/hosts.deny\fR to restrict who can talk to the server.) .PP \&\fBtinyleaf\fR has a (currently hard-coded) maximum message size of 1\ \s-1MB\s0 and a (similarly hard-coded) timeout of ten minutes for each command or chunk of article data. .SH "EXAMPLE" .IX Header "EXAMPLE" Suppose that you want to archive news articles on a particular host (like the \s-1FTP\s0 server for a newsgroup archive) where you don't want the overhead of running a full-blown news server. Write a program that reads one line at a time from standard input and treats everything before the first space as the filename of a news article to archive. Each time the program reads a line, it should archive that file and then delete it, and it should exit when it gets end of file on standard input. .PP Then, add a line like: .PP .Vb 2 \& nntp stream tcp nowait archive /usr/sbin/tcpd \e \& /tinyleaf /tinyleaf /archive .Ve .PP (all on one line \-\-\ the backslash and split in this line is just for readability) where \f(CW\*(C`archive\*(C'\fR is the user that owns the archive, \&\fI/usr/sbin/tcpd\fR is the path to \fItcpd\fR\|(8), \fIpathbin\fR/tinyleaf is the path to this program, \fIpathspool\fR/tinyleaf is some scratch directory that the user \f(CW\*(C`archive\*(C'\fR has write access to, and \&\fIpathbin\fR/archive is the path to your \fBarchive\fR script. .PP You can now restrict access to \fBtinyleaf\fR to just your local news server with \f(CW\*(C`/etc/hosts.allow\*(C'\fR and \f(CW\*(C`/etc/hosts.deny\*(C'\fR and set up an ordinary feed from the server to the archive host, just like you would to any other news server, of only the newsgroup that you want to archive. .PP Note that the archiving script should probably perform basic syntax and validity checks on the input, since \fBtinyleaf\fR doesn't. .PP This is the application that motivated the original development of this program. .SH "BUGS" .IX Header "BUGS" The timeout and maximum message size should really be configurable. \&\fBtinyleaf\fR should also probably not just respond 500 to every command other than \s-1HELP\s0, \s-1IHAVE\s0, and \s-1QUIT\s0; there are more useful (and more expected) error codes that could be returned. .PP An option to scan the spool directory for any left-over files and pass them to the processor when starting up would be useful. .SH "HISTORY" .IX Header "HISTORY" Written by Russ Allbery for InterNetNews. .PP \&\f(CW$Id:\fR tinyleaf.pod 9767 2014\-12\-07 21:13:43Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIhosts_access\fR\|(5), \fIinetd\fR\|(8), \fItcpd\fR\|(8). inn-2.6.0/doc/man/innconfval.10000644000175200017520000001775312575023702015474 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "INNCONFVAL 1" .TH INNCONFVAL 1 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" innconfval \- Get configuration parameters from inn.conf .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fBinnconfval\fR [\fB\-pstv\fR] [\fB\-i\fR \fIfile\fR] [\fIparameter\fR ...] .PP \&\fBinnconfval\fR \fB\-C\fR [\fB\-i\fR \fIfile\fR] .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\fBinnconfval\fR normally prints the values of the parameters specified on the command line. By default, it just prints the parameter values, but if \&\fB\-p\fR, \fB\-s\fR, or \fB\-t\fR are given, it instead prints the parameter and value in the form of a variable assignment in Perl, Bourne shell, or Tcl respectively. If no parameters are specifically requested, \fBinnconfval\fR prints out all parameter values (this isn't particularly useful unless one of \fB\-p\fR, \fB\-s\fR, or \fB\-t\fR were specified). .PP All parameters are taken from \fIinn.conf\fR except for \fIversion\fR, which is always the version string of \s-1INN\s0. .PP If given the \fB\-C\fR option, \fBinnconfval\fR instead checks \fIinn.conf\fR, reporting any problems found to standard error. \fBinnconfval\fR will exit with status 0 if no problems are found and with status 1 otherwise. .SH "OPTIONS" .IX Header "OPTIONS" .IP "\fB\-C\fR" 4 .IX Item "-C" Check \fIinn.conf\fR rather than printing out the values of parameters. .IP "\fB\-i\fR \fIfile\fR" 4 .IX Item "-i file" Use \fIfile\fR as the source configuration file rather than \fIinn.conf\fR. \&\fIfile\fR must be a valid \fIinn.conf\fR file and will be parsed the same as \&\fIinn.conf\fR would be. .IP "\fB\-p\fR" 4 .IX Item "-p" Print out parameters as Perl assignment statements. The variable name will be the same as the \fIinn.conf\fR parameter, and string values will be enclosed in single quotes with appropriate escaping. Boolean values will be mapped to the strings \f(CW\*(C`true\*(C'\fR or \f(CW\*(C`false\*(C'\fR. List values will be mapped to an array of strings. \s-1NULL\s0 values are printed out with the \f(CW\*(C`undef\*(C'\fR value. .Sp Here is an example: .Sp .Vb 5 \& $enableoverview = \*(Aqtrue\*(Aq; \& @extraoverviewadvertised = ( \*(AqNewsgroups\*(Aq, \*(AqInjection\-Info\*(Aq ); \& @extraoverviewhidden = undef; \& $organization = \*(AqLet\e\*(Aqs try nasty "quotes"\*(Aq; \& $maxforks = 10; .Ve .Sp If \fBinnconfval\fR is called via the Perl \f(CW\*(C`INN::Config\*(C'\fR module, all these variables are properly exported. .IP "\fB\-s\fR" 4 .IX Item "-s" Print out parameters as Bourne shell assignment statements. The variable name will be the \fIinn.conf\fR parameter name in all capitals, and all variables will be exported, if not \s-1NULL\s0. String values will be enclosed in single quotes with appropriate escaping, and boolean values will be mapped to \f(CW\*(C`true\*(C'\fR or \f(CW\*(C`false\*(C'\fR. List values will be mapped to a space-separated string representing an array of strings (as Bourne shell does not recognize arrays, contrary to several other shells, an array cannot be returned for interoperability reasons). .Sp Here is an example: .Sp .Vb 4 \& ENABLEOVERVIEW=true; export ENABLEOVERVIEW; \& EXTRAOVERVIEWADVERTISED=\*(Aq"Newsgroups" "Injection\-Info"\*(Aq; export EXTRAOVERVIEWADVERTISED; \& ORGANIZATION=\*(AqLet\*(Aq\e\*(Aq\*(Aqs try nasty "quotes"\*(Aq; export ORGANIZATION; \& MAXFORKS=10; export MAXFORKS; .Ve .IP "\fB\-t\fR" 4 .IX Item "-t" Print out parameters as Tcl assignment statements. The variable name will be the same as the \fIinn.conf\fR parameter name but with \f(CW\*(C`inn_\*(C'\fR prepended, and string variables will be escaped appropriately. Boolean values will be mapped to the strings \f(CW\*(C`true\*(C'\fR or \f(CW\*(C`false\*(C'\fR. List values will be mapped to an array of strings. \s-1NULL\s0 values are not printed out. .Sp Here is an example: .Sp .Vb 4 \& set inn_enableoverview "true" \& set inn_extraoverviewadvertised { "Newsgroups" "Injection\-Info" } \& set inn_organization "Let\*(Aqs try nasty \e"quotes\e"" \& set inn_maxforks 10 .Ve .IP "\fB\-v\fR" 4 .IX Item "-v" Print \s-1INN\s0's version. This is equivalent to \f(CW\*(C`innconfval version\*(C'\fR. .SH "HISTORY" .IX Header "HISTORY" Written by Rich \f(CW$alz\fR for InterNetNews. .PP \&\f(CW$Id:\fR innconfval.pod 9288 2011\-07\-22 23:08:57Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIinn.conf\fR\|(5), \fIINN::Config\fR\|(3pm). inn-2.6.0/doc/man/innwatch.80000644000175200017520000001353112575023702015147 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "INNWATCH 8" .TH INNWATCH 8 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" innwatch \- Monitor the state of INN and the system .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fBinnwatch\fR [\fB\-f\fR \fIctlfile\fR] [\fB\-i\fR \fIseconds\fR] [\fB\-l\fR \fIlogfile\fR] [\fB\-t\fR \fIseconds\fR] .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\fBinnwatch\fR is normally started by \fBrc.news\fR. Every \&\fIinnwatchsleeptime\fR seconds, as set in \fIinn.conf\fR, it examines the load average, and the number of free blocks and inodes on the spool partition, as described by its control file, \fIinnwatch.ctl\fR in \fIpathetc\fR. .PP If the load gets too high, or the disk gets too full, it throttles the server. When the condition restores, it unblocks the server. In addition, on each pass through the loop, it will check the logfile \&\fIpathlog\fR/news.crit to see if it has been modified, and send mail to the news administrator if so. .PP Upon receipt of an interrupt signal (\s-1SIGINT\s0), \fBinnwatch\fR will report its status in the file \fIinnwatch.status\fR in \fIpathrun\fR. .SH "OPTIONS" .IX Header "OPTIONS" .IP "\fB\-f\fR \fIfile\fR" 4 .IX Item "-f file" Specify the control file to use, other than the default of \&\fIinnwatch.ctl\fR in \fIpathetc\fR. .IP "\fB\-i\fR \fIseconds\fR" 4 .IX Item "-i seconds" With this option, \fBinnwatch\fR has an initial sleep of \fIseconds\fR seconds at startup. This is useful when \fBinnwatch\fR is started at the same time as \s-1INN\s0, so that it can wait a little before beginning performing its checks. .IP "\fB\-l\fR \fIlogfile\fR" 4 .IX Item "-l logfile" Specify a log file to watch, other than the default of \fInews.crit\fR. .IP "\fB\-t\fR \fIseconds\fR" 4 .IX Item "-t seconds" Specify the period in seconds between checks, to override the value set in \fIinn.conf\fR. .SH "HISTORY" .IX Header "HISTORY" Written by Mike Cooper , with modifications by , Steve Groom and Christophe Wolfhugel . Converted to \s-1POD\s0 by Julien Elie. .PP \&\f(CW$Id:\fR innwatch.pod 9722 2014\-09\-24 17:47:12Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIinn.conf\fR\|(5), \fIinnwatch.ctl\fR\|(5), \fInewslog\fR\|(5), \fIrc.news\fR\|(8). inn-2.6.0/doc/man/cycbuff.conf.50000644000175200017520000003530612575023702015702 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "CYCBUFF.CONF 5" .TH CYCBUFF.CONF 5 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" cycbuff.conf \- Configuration file for INN CNFS storage method .SH "DESCRIPTION" .IX Header "DESCRIPTION" This file defines the cyclical buffers that make up the storage pools for \&\s-1CNFS\s0 (Cyclic News File System). Some options controlling the behavior of the \s-1CNFS\s0 storage system can also be set here. \fIcycbuff.conf\fR is required if the \s-1CNFS\s0 (Cyclic News File System) storage method is used. \s-1INN\s0 will look for it in \fIpathetc\fR (as set in \fIinn.conf\fR). .PP \&\s-1CNFS\s0 stores articles in logical objects called \fImetacycbuffs\fR. Each metacycbuff is in turn composed of one or more physical buffers called \fIcycbuffs\fR. As articles are written to the metacycbuff, each article is written to the next cycbuff in the list in a round-robin fashion (unless \f(CW\*(C`sequential\*(C'\fR mode is specified, in which case each cycbuff is filled before moving on to the next). This is so that you can distribute the individual cycbuffs across multiple physical disks and balance the load between them. Note that in order to use any cycbuff larger than 2\ \s-1GB\s0 on 32\-bit platforms (and some very rare 64\-bit platforms that aren't Linux), you need to build \s-1INN\s0 with the \&\fB\-\-enable\-largefiles\fR option. .PP For information about how to configure \s-1INN\s0 to use \s-1CNFS\s0, see \&\fIstorage.conf\fR\|(5). .PP Blank lines and lines beginning with a hash sign (\f(CW\*(C`#\*(C'\fR) are ignored. All other lines must be of one of the following forms: .PP .Vb 4 \& cycbuffupdate: \& refreshinterval: \& cycbuff::: \& metacycbuff::[,,...][:] .Ve .PP (where items enclosed in [] are optional). Order is mostly not significant, but all \fIcycbuff\fR lines must occur before all \fImetacycbuff\fR lines. Long lines can be continued on the next line by ending the line with a backslash (\f(CW\*(C`\e\*(C'\fR). .IP "\fIcycbuffupdate\fR:" 4 .IX Item "cycbuffupdate:" Sets the number of articles written before the cycbuff header is written back to disk to . Under most operating systems, the header doesn't have to be written to disk for the updated data to be available to other processes on the same system that are reading articles out of \s-1CNFS\s0, but any accesses to the \s-1CNFS\s0 cycbuffs over \s-1NFS\s0 will only see the data present at the last write of the header. After a system crash, all updates since the last write of the \s-1CNFS\s0 header may be lost. The default value, if this line is omitted, is \f(CW25\fR, meaning that the header is written to disk after every 25 articles stored in that cycbuff. .IP "\fIrefreshinterval\fR:" 4 .IX Item "refreshinterval:" Sets the interval (in seconds) between re-reads of the cycbuff header to . This primarily affects \fBnnrpd\fR and controls the frequency with which it updates its knowledge of the current contents of the \s-1CNFS\s0 cycbuffs. The default value, if this line is omitted, is \f(CW30\fR. .IP "\fIcycbuff\fR:::" 4 .IX Item "cycbuff:::" Configures a particular \s-1CNFS\s0 cycbuff. is a symbolic name for the buffer, to be used later in a metacycbuff line. It must be no longer than seven characters. is the full path to the buffer file or block device, and must be no longer than 63 characters. is the length of the buffer in kilobytes (1\ \s-1KB\s0 is 1024 bytes). If is not a block device, it should be \ *\ 1024\ bytes long. .Sp If you're trying to stay under 2\ \s-1GB\s0, keep your sizes below \f(CW2097152\fR. .IP "\fImetacycbuff\fR::[,,...][:]" 4 .IX Item "metacycbuff::[,,...][:]" Specifies a collection of \s-1CNFS\s0 buffers that make up a single logical storage location from the perspective of \s-1INN\s0. Metacycbuffs are referred to in \fIstorage.conf\fR as storage locations for articles, so in order to actually put articles in a cycbuff, it has to be listed as part of some metacycbuff which is then referenced in \fIstorage.conf\fR. .Sp is the symbolic name of the metacycbuff, referred to in the options: field of \f(CW\*(C`cnfs\*(C'\fR entries in \fIstorage.conf\fR. It must be no longer than eight characters. is the name of a cycbuff (the part of a cycbuff line), and any number of cycbuffs may be specified, separated by commas. .Sp If there is more than one cycbuff in a metacycbuff, there are two ways that \s-1INN\s0 can distribute articles between the cycbuffs. The default mode, \&\f(CW\*(C`INTERLEAVE\*(C'\fR, stores the articles in each cycbuff in a round-robin fashion, one article per cycbuff in the order listed. If the cycbuffs are of wildly different sizes, this can cause some of them to roll over much faster than others, and it may not give the best performance depending on your disk layout. The other storage mode, \f(CW\*(C`SEQUENTIAL\*(C'\fR, instead writes to each cycbuff in turn until that cycbuff is full and then moves on to the next one, returning to the first and starting a new cycle when the last one is full. To specify a mode rather than leaving it at the default, add a colon and the mode (\f(CW\*(C`INTERLEAVE\*(C'\fR or \f(CW\*(C`SEQUENTIAL\*(C'\fR) at the end of the metacycbuff line. .PP \&\fBinnd\fR only reads \fIcycbuff.conf\fR on startup, so if you change anything in this file and want \fBinnd\fR to pick up the changes, you have to use \&\f(CW\*(C`ctlinnd xexec innd\*(C'\fR; \f(CW\*(C`ctlinnd reload all \*(Aq\*(Aq\*(C'\fR is not sufficient. .PP When articles are stored, the cycbuff into which they're stored is saved as part of the article token. In order for \s-1INN\s0 to retrieve articles from a cycbuff, that cycbuff must be listed in \fIcycbuff.conf\fR. However, if \&\s-1INN\s0 should not write to a cycbuff, it doesn't need to be (and shouldn't be) listed in a metacycbuff. .PP This provides an easy way to retire a cycbuff. Just remove it from its metacycbuff, leaving in the cycbuff line, and restart \fBinnd\fR (with, for example, \f(CW\*(C`ctlinnd xexec innd\*(C'\fR). No new articles will be put into the cycbuff, but neither will any articles expire from it. After you no longer need the articles in the cycbuff, just remove it entirely from \&\fIcycbuff.conf\fR. Then all of the articles will appear to have been deleted to \s-1INN\s0, and the next nightly expire run will clean up any remaining references to them. .PP Adding a new cycbuff just requires creating it (see below), adding a cycbuff line, adding it to a metacycbuff, and then restarting \fBinnd\fR. .SH "CREATING CYCBUFFS" .IX Header "CREATING CYCBUFFS" When creating a new cycbuff, there are two different methods for creating the buffers in which the articles will be stored. .IP "1." 4 Create a large file on top of a regular file system. The easiest way to do this is probably with \fIdd\fR\|(1), using a command like: .Sp .Vb 1 \& dd if=/dev/zero of=/path/to/cycbuff bs=1024 count= .Ve .Sp where is the size from the cycbuff line in \fIcycbuff.conf\fR. \&\fI\s-1INSTALL\s0\fR contains a script that will generate these commands for you from your \fIcycbuff.conf\fR file. .Sp This is the simplest method, but has the disadvantage that very large files on regular file systems can be fairly slow to access, particularly at the end of the file, and \s-1INN\s0 incurs unnecessary file system overhead when accessing the cycbuff. .IP "2." 4 Use block devices directly. If your operating system allows you to call \&\fImmap()\fR on block devices (Solaris and recent versions of Linux do, FreeBSD at last report does not), this is the recommended method since you can avoid all of the native file system overhead. .Sp Note that some OSes do not support files larger than 2\ \s-1GB\s0, which will limit the size you can make a single cycbuff, but you can still combine many cycbuffs into each metacycbuff. Very old versions of Linux (before 2.4 kernels, that raised the limit to 2\ \s-1TB\s0) are known to have this limitation; FreeBSD does not. Some OSes that support large files don't support direct access to block devices for large partitions (Solaris prior to Solaris\ 7, or not running in 64\-bit mode, is in this category); on those OSes, if you want cycbuffs over 2\ \s-1GB\s0, you'll have to use regular files. If in doubt, keep your cycbuffs smaller than 2\ \s-1GB\s0. .Sp Partition the disk to make each partition equal to or smaller than 2\ \s-1GB\s0. If you're using Solaris, set up your partitions to avoid the first cylinder of the disk (or otherwise the cycbuff header will overwrite the disk partition table and render the cycbuffs inaccessible). Then, create device files for each block device you're going to use. .Sp It's not recommended to use the block device files in \fI/dev\fR, since the news system doesn't have permission to write to them and changing the permissions of the system device files may affect something else. Instead, use \fImknod\fR\|(1) to create a new set of block devices (in somewhere like \fIpathspool\fR/cycbuffs that's only writable by the news user). To do this, run \f(CW\*(C`ls \-Ll\*(C'\fR on the devices in \fI/dev\fR that correspond to the block devices that you want to use. The major and minor device numbers are in the fifth and sixth columns (right before the date), respectively. Then run mknod like: .Sp .Vb 1 \& mknod b .Ve .Sp where is the path to the device to create (matching the part of the cycbuff line) and and are the major and minor device numbers as discovered above. .Sp Here's a short script to do this when given the path to the system device file as an argument: .Sp .Vb 8 \& #!/bin/sh \& base=\`echo "$1" | sed \*(Aqs%.*/%%\*(Aq\` \& major=\`ls \-Ll "$1" | awk \*(Aq{print $5}\*(Aq | tr \-d ,\` \& minor=\`ls \-Ll "$1" | awk \*(Aq{print $6}\` \& mkdir \-p /cycbuffs \& mknod /cycbuffs/"$base" b "$major" "$minor" \& chown news:news /cycbuffs/"$base" \& chmod 644 /cycbuffs/"$base" .Ve .Sp Make sure that the created files are owned by the news user and news group, as specified at configure time (the default being \f(CW\*(C`news\*(C'\fR for both). Also make sure that the permissions on the devices allow the news user to read and write, and if you want other users on the system to be able to use \fBsm\fR to retrieve articles, make sure they're world-readable. .PP Once you have everything configured properly and you start \fBinnd\fR, you should see messages in \fInews.notice\fR that look like: .PP .Vb 1 \& innd: CNFS: no magic cookie found for cycbuff ONE, initializing .Ve .PP where \f(CW\*(C`ONE\*(C'\fR will be whatever you called your cycbuff. .SH "HISTORY" .IX Header "HISTORY" Written by Katsuhiro Kondou for InterNetNews. Rewritten into \s-1POD\s0 by Russ Allbery . .PP \&\f(CW$Id:\fR cycbuff.conf.pod 9925 2015\-08\-08 17:05:43Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIctlinnd\fR\|(8), \fIinnd\fR\|(8), \fInnrpd\fR\|(8), \fIsm\fR\|(1), \fIstorage.conf\fR\|(5). inn-2.6.0/doc/man/mod-active.80000644000175200017520000001464112575023702015367 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "MOD-ACTIVE 8" .TH MOD-ACTIVE 8 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" mod\-active \- Batch processing of newsgroups creation and removal commands .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fBmod-active\fR [\fIctlinnd-command-file\fR ...] .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\fBmod-active\fR is a Perl script that updates the \fIactive\fR file based on its input lines of \fBctlinnd\fR \f(CW\*(C`newgroup\*(C'\fR, \f(CW\*(C`rmgroup\*(C'\fR and \f(CW\*(C`changegroup\*(C'\fR commands. It pauses the server briefly while the existing \fIactive\fR file is read and rewritten, which not only keeps \fBinnd\fR from updating the \&\fIactive\fR file but also locks against other instances of \fBmod-active\fR. .PP The script must be run as the news user. .PP The input to \fBmod-active\fR can come either from one or more \fIctlinnd-command-file\fR files named on the command line, or from the standard input. Typically its input is the output from the \fBdocheckgroups\fR or \fBactsync\fR commands. Every line which contains the string \f(CW\*(C`ctlinnd newgroup\*(C'\fR, \f(CW\*(C`ctlinnd rmgroup\*(C'\fR, or \f(CW\*(C`ctlinnd changegroup\*(C'\fR, optionally preceded by whitespace and/or the path to \fBctlinnd\fR, is noted for the update. Redundant commands, such as a newgroup directive for a group that already exists, are silently ignored. All other lines in the input are also silently ignored. After the new \fIactive\fR file has been generated, the existing one is renamed to \fIactive.old\fR and the new one is moved into place. The script then displays the differences between the two files. Any groups that were added to the \fIactive\fR file are also added to the \fIactive.times\fR file with the string \f(CW\*(C`checkgroups\-update\*(C'\fR. .PP Please note that no syntax checking is performed on group names by \fBmod-active\fR. .SH "BUGS" .IX Header "BUGS" Though \fBinnd\fR is paused while \fBmod-active\fR works, it is not inconceivable that there could be a conflict if something else tries to update the \fIactive\fR file during the relatively short time that \fBmod-active\fR is working. The two most realistic ways for this to happen are either by an administrator concurrently doing a manual \fBctlinnd\fR command, or by \fBinnd\fR receiving a control message, then \fBmod-active\fR pausing the server, then the control message handler script that \fBinnd\fR forked running its own \fBctlinnd\fR command while \fBmod-active\fR is working. Note that such scenarios are \fIvery\fR unlikely to happen. .SH "HISTORY" .IX Header "HISTORY" Written by David C Lawrence for InterNetNews. Converted to \s-1POD\s0 by Julien Elie. .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIactive\fR\|(5), \fIactive.times\fR\|(5), \fIactsync\fR\|(8), \fIctlinnd\fR\|(8), \fIdocheckgroups\fR\|(8), \fIinnd\fR\|(8). inn-2.6.0/doc/man/scanspool.80000644000175200017520000002044712575023702015341 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "SCANSPOOL 8" .TH SCANSPOOL 8 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" scanspool \- Perform a sanity scan over all articles in news spool .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fBscanspool\fR [\fB\-cnv\fR] [\fB\-a\fR \fIactive-file\fR] [\fB\-s\fR \fIspool-dir\fR] .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\fBscanspool\fR is a Perl script for use with a \fItradspool\fR article spool. It will scan the \fIactive\fR file as well as all articles in the spool and report on the errors it encounters. As this may take a while, using the \&\fB\-v\fR switch is recommended to see how far the program has progressed. .PP First, \fBscanspool\fR scans the \fIactive\fR file, noting problems such as: .IP "\(bu" 4 malformed lines; .IP "\(bu" 4 newsgroups aliased to a non-existent newsgroup; .IP "\(bu" 4 newsgroups aliased to a newsgroup that is also aliased. .PP Then it will examine all articles under your news spool directory, complaining about articles that: .IP "\(bu" 4 have a basename that starts with a leading 0; .IP "\(bu" 4 have a basename that is out of range according to the \fIactive\fR file; .IP "\(bu" 4 do not contain a Newsgroups: header line; .IP "\(bu" 4 are all header and no text; .IP "\(bu" 4 are in a directory for which there is no newsgroup in the \fIactive\fR file; .IP "\(bu" 4 are in a newsgroup to which they do not belong. .PP \&\fBscanspool\fR understands aliased newsgroups. Thus, if an article is posted to foo.old.name that is aliased to foo.bar, it will be expected to be found under foo.bar and not foo.old.name. .PP Articles posted to a newsgroup of status \f(CW\*(C`j\*(C'\fR or \f(CW\*(C`x\*(C'\fR (the fourth field of the \fIactive\fR file) will be expected to show up under the \fIjunk\fR group. .PP \&\fBscanspool\fR assumes that the path of a valid newsgroup's directory from the root of the spool tree will not contain any \f(CW\*(C`.\*(C'\fR character. Thus, directories such as \fIout.going\fR, \fItmp.dir\fR, \fIin.coming\fR and \&\fInews.archive\fR will not be searched. This program also assumes that article basenames contain only decimal digits. Last, files under the top level directory \fIlost+found\fR are not scanned. .SH "OPTIONS" .IX Header "OPTIONS" .IP "\fB\-a\fR \fIactive-file\fR" 4 .IX Item "-a active-file" The \fIactive\fR file to use; \fIpathdb\fR/active is the default. .IP "\fB\-c\fR" 4 .IX Item "-c" Only check article filenames. \fBscanspool\fR will therefore not actually scan the Newsgroups: header field of the articles. .IP "\fB\-n\fR" 4 .IX Item "-n" Don't throttle \fBinnd\fR while scanning. .IP "\fB\-s\fR \fIspool-dir\fR" 4 .IX Item "-s spool-dir" The root of the spool tree; \fIpatharticles\fR is the default. .IP "\fB\-v\fR" 4 .IX Item "-v" Setting this flag enables a verbose mode. \fBscanspool\fR will then print which newsgroup is currently worked upon. It will list all the articles found in non\-\fIactive\fR directories (that is to say articles sorted into groups no longer present in the \fIactive\fR file), rather than just printing the group once. .SH "DIAGNOSTICS" .IX Header "DIAGNOSTICS" The output of \fBscanspool\fR will start with one of the following forms: .ie n .IP """FATAL:"" (to stderr)" 4 .el .IP "\f(CWFATAL:\fR (to stderr)" 4 .IX Item "FATAL: (to stderr)" Fatal or internal error. .ie n .IP """WARN:"" (to stderr)" 4 .el .IP "\f(CWWARN:\fR (to stderr)" 4 .IX Item "WARN: (to stderr)" \&\fIactive\fR or article format problem, newsgroup alias problem, \fIfind\fR\|(1) error, article open error. .ie n .IP """path/123:"" (to stdout)" 4 .el .IP "\f(CWpath/123:\fR (to stdout)" 4 .IX Item "path/123: (to stdout)" Issues such as: .RS 4 .IP "\(bu" 4 basename starts with 0; .IP "\(bu" 4 article number out of range; .IP "\(bu" 4 article in the wrong directory; .IP "\(bu" 4 article in a directory not related to an \fIactive\fR non-aliased newsgroup. .RE .RS 4 .RE .ie n .IP """\et ..."" (to stdout)" 4 .el .IP "\f(CW\et ...\fR (to stdout)" 4 .IX Item "t ... (to stdout)" Verbose messages start with a tab. .SH "BUGS" .IX Header "BUGS" \&\fBscanspool\fR is unable to detect and properly deal with spool formats other than tradspool. However, if the files that store your articles are named other than just all-digits, they will simply be skipped (and your \fIactive\fR is still checked). .PP \&\fBscanspool\fR only considers the first line of the Newsgroups: header field. Continuation lines are not taken into account. .SH "HISTORY" .IX Header "HISTORY" \&\fBscanspool\fR was written by Landon Curt Noll (chongo was here /\e../\e). .PP This manual page was written by Florian Schlichting, largely based on comments in the script. .PP \&\f(CW$Id:\fR scanspool.pod 9301 2011\-08\-04 21:09:11Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIactive\fR\|(5). inn-2.6.0/doc/man/newsgroups.50000644000175200017520000002355512575023702015554 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "NEWSGROUPS 5" .TH NEWSGROUPS 5 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" newsgroups \- List of newsgroups and their short descriptions .SH "DESCRIPTION" .IX Header "DESCRIPTION" The file \fIpathdb\fR/newsgroups contains a list of newsgroups for which a short description is available. This file is generally updated by \&\fIcontrolchan\fR\|(8) whenever a control message is received; it is used by \&\fBnnrpd\fR in response to \s-1LIST\s0 \s-1NEWSGROUPS\s0 and is only meant to provide information to users. News readers often show the list of carried newsgroups along with these descriptions. .PP It is not necessary that all the groups carried by the news server (that is to say all the groups listed in the \fIactive\fR file) be listed in the \fInewsgroups\fR file. And it is also not necessary that all the groups listed in the \fInewsgroups\fR file be carried by the news server. Nonetheless, it is of course better if the \fIactive\fR and \fInewsgroups\fR files have exactly the same newsgroups. .PP If you use \f(CW\*(C`ctlinnd newgroup\*(C'\fR to manually create a group, only the \&\fIactive\fR file is updated. You should then edit the \fInewsgroups\fR file in order to add a short description for the created group. The same goes for manually removing or changing the status of a newsgroup. .PP Each line of the \fInewsgroups\fR file consists of two fields separated by at least one tabulation: .PP .Vb 1 \& \et .Ve .PP The first field is the name of the newsgroup. The second field is its description. .PP You can get the \fInewsgroups\fR file of another \s-1NNTP\s0 server with \fIgetlist\fR\|(1). .SH "PREFERRED FORMAT FOR A ONE-LINE NEWSGROUP DESCRIPTION" .IX Header "PREFERRED FORMAT FOR A ONE-LINE NEWSGROUP DESCRIPTION" As far as the format of the \fInewsgroups\fR file is concerned, there is a preferred format for each line. Since news administrators do not generally have the time to fix up the lines that are being automatically included from newgroup or checkgroups messages, this information is provided so that control message senders can craft better control messages. It will also be useful for news administrators to know how to format the description of their local newsgroups. .PP There should be at least one hard tab (8 column tab stops) between the group name and the description. If the group name is at least 16 characters, it should be followed with one tab. If the group name is at least 8 characters, it should be followed with two tabs. And in the unlikely event the group name is less than 8 characters, it should be followed with three tabs. For instance: .PP .Vb 3 \& misc.transport.rail.europe Railroads & railways in all of Europe. \& news.admin.nocem NoCeM protocol policy issues and information. \& news.groups Discussions and lists of newsgroups. .Ve .PP The total line length should be at most 79 columns. The description should start with a capital and not be more than 55 characters (79 \- 24) long. If the group name is longer than 24 characters, the description should be correspondingly shorter. If the group is moderated, it should have \f(CW\*(C` (Moderated)\*(C'\fR (note the space before the opening parenthesis) at the very end of the description, not counted as part of the length of the description. This text must be exactly that, with no variations, as it is used by news software to find moderated groups. .PP Here is an example of moderated newsgroup: .PP .Vb 1 \& news.lists.misc News\-related statistics and lists. (Moderated) .Ve .PP Traditionally, all newsgroup descriptions ended with a period, but this is not necessary and steals away one character that is occasionally useful for forming a better description. .PP Some over-long descriptions could be made to easily fit the length by dropping useless wordings like \f(CW\*(C`Discussion of\*(C'\fR which do not meaningfully contribute to the description. Others are usually pretty easy to get to no more than column eighty, except when the group names start getting really long. Hopefully then the group name itself contains quite a bit of description. .PP In some cases, a longer description really will be necessary; they can of course be used within the \fInewsgroups\fR file. However, they will probably be less readable and less useful for some Usenet users. .PP Descriptions must not contain any control characters (octets between 0x00 and 0x1F). .SH "ENCODING OF THE DESCRIPTIONS" .IX Header "ENCODING OF THE DESCRIPTIONS" There is, at present, no good mechanism for managing the character set of the newsgroup descriptions. Many non-English hierarchies include newsgroup descriptions in their native languages, since this is more useful for their users, and those are included verbatim in the \&\fInewsgroups\fR file. This unfortunately means that different lines of the file will require different character set settings to read properly, and those character sets are not documented in the file. Hopefully some future standard will provide a way to address this; in the meantime, using \s-1UTF\-8\s0 for non-ASCII characters is recommended. .SH "MINIMAL NEWSGROUPS FILE" .IX Header "MINIMAL NEWSGROUPS FILE" The minimal \fInewsgroups\fR file shipped with \s-1INN\s0 is: .PP .Vb 6 \& control Various control messages (no posting). \& control.cancel Cancel messages (no posting). \& control.checkgroups Hierarchy check control messages (no posting). \& control.newgroup Newsgroup creation control messages (no posting). \& control.rmgroup Newsgroup removal control messages (no posting). \& junk Unfiled articles (no posting). .Ve .PP These lines \fImust\fR be tab-delimited, so please be careful in case you copy and paste them from above. .SH "HISTORY" .IX Header "HISTORY" Written by Julien Elie for InterNetNews. The preferred format for a one-line newsgroup description is based on the policies by which the \fInewsgroups\fR file in is maintained; they were originally written by David Lawrence and updated by Russ Allbery . .PP \&\f(CW$Id:\fR newsgroups.pod 9767 2014\-12\-07 21:13:43Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIactive\fR\|(5), \fIcontrolchan\fR\|(8), \fIctlinnd\fR\|(8), \fIgetlist\fR\|(1), \fInnrpd\fR\|(8). inn-2.6.0/doc/man/innreport.80000644000175200017520000000144412575023702015354 0ustar iuliusiulius.TH innreport 8 .SH NAME innreport \- summarize INN log files. .SH SYNOPSIS innreport -f innreport.conf [ -[no]options ] [ logfiles ] .SH DESCRIPTION .I Innreport is a .IR perl (1) script that summarizes INN log files. It is normally invoked by .IR scanlogs (8). Supported programs are .IR innd (8), .IR innfeed (1), .IR innxmit (8), .I nntplink, .IR nnrpd (8), .IR batcher (8), .IR rnews (1) and a few others. .SH OPTIONS There are lots of 'em. Run innreport with ``\-h'' or ``\-help'' to get full details. .SH HISTORY Written by Fabien Tassin for InterNetNews. .de R$ This is revision \\$3, dated \\$4. .. .R$ $Id: innreport.8 8242 2008-12-21 10:29:55Z iulius $ .SH "SEE ALSO" innd(8), innfeed(8), innxmit(8), news.daily(8), newslog(5), nnrpd(8), scanlogs(8), writelog(8). inn-2.6.0/doc/man/cvtbatch.80000644000175200017520000001300612575023702015127 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "CVTBATCH 8" .TH CVTBATCH 8 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" cvtbatch \- Convert Usenet batch files to INN format .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fBcvtbatch\fR [\fB\-w\fR \fIitems\fR] .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\fBcvtbatch\fR reads standard input as a sequence of lines, converts each line, and writes it to standard output. It is used to convert simple batch files that contain just the storage \s-1API\s0 token of an article to \s-1INN\s0 batch files that contain additional information about each article. .PP Each line is taken as a storage \s-1API\s0 token indicating a Usenet article. Only the first word of each line is parsed; anything following whitespace is ignored. Lines not starting with a valid token are also silently ignored. .PP If the input file consists of a series of message-IDs, then use \&\fBgrephistory\fR with the \fB\-s\fR flag piped into \fBcvtbatch\fR. .SH "OPTIONS" .IX Header "OPTIONS" .IP "\fB\-w\fR \fIitems\fR" 4 .IX Item "-w items" The \fB\-w\fR flag specifies how each output line should be written. The items for this flag should be chosen from the \f(CW\*(C`W\*(C'\fR flag items as specified in \fInewsfeeds\fR\|(5). They may be chosen from the following set: .Sp .Vb 5 \& b Size of the article in bytes. \& f Storage API token of the article (same as "n"). \& m Article message\-ID. \& n Storage API token of the article. \& t Arrival time of the article as seconds since epoch. .Ve .Sp The default is \f(CW\*(C`nm\*(C'\fR, that is to say the storage \s-1API\s0 token followed by the message-ID of the article. .SH "HISTORY" .IX Header "HISTORY" Written by Rich \f(CW$alz\fR for InterNetNews. Converted to \s-1POD\s0 by Julien Elie. .PP \&\f(CW$Id:\fR cvtbatch.pod 8704 2009\-11\-03 18:51:44Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIgrephistory\fR\|(1), \fInewsfeeds\fR\|(5). inn-2.6.0/doc/man/rc.news.80000644000175200017520000001555512575023702014723 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "RC.NEWS 8" .TH RC.NEWS 8 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" rc.news \- Start or stop INN daemons .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fBrc.news\fR [\f(CW\*(C`start\*(C'\fR | \f(CW\*(C`stop\*(C'\fR] .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\fBrc.news\fR can be used to start or stop \fBinnd\fR and supporting programs. It checks to make sure \s-1INN\s0 is not already running, handles cases of unclean shutdown, finishes up tasks which might have been interrupted by the preceding shutdown, e\-mails certain boot-time warnings to \&\fInewsmaster\fR (as set in \fIinn.conf\fR), and is generally safer and easier than starting and stopping everything directly. It needs to be run as the news user so that files in \fIpathrun\fR are created with the right ownership (though this is less important for \f(CW\*(C`rc.news stop\*(C'\fR). .PP Programs run and stopped by this script include: .IP "\(bu" 4 Always: \fBinnd\fR is started or stopped. .IP "\(bu" 4 If \fIdoinnwatch\fR is true in \fIinn.conf\fR: \fBinnwatch\fR is started and stopped. .IP "\(bu" 4 If \fIdocnfsstat\fR is true in \fIinn.conf\fR: \fBcnfsstat\fR is started and stopped. .IP "\(bu" 4 If \fIovmethod\fR is set to \f(CW\*(C`ovdb\*(C'\fR in \fIinn.conf\fR: \fBovdb_init\fR is run; \&\fBovdb_server\fR and \fBovdb_monitor\fR are stopped. .IP "\(bu" 4 If \fIrc.news.local\fR exists in \fIpathbin\fR: \fBrc.news.local\fR is run with argument \f(CW\*(C`start\*(C'\fR or \f(CW\*(C`stop\*(C'\fR (to perform site-specific startup or shutdown tasks). .IP "\(bu" 4 When started, if \s-1INN\s0 appears to have previously been shut down during its expiry process, run \fBexpirerm\fR if there are articles to unlink. .IP "\(bu" 4 When started, if overview data appears to have just been rebuilt and \&\fIactive\fR needs to be renumbered, then actually renumber it. .SH "OPTIONS" .IX Header "OPTIONS" .ie n .IP """start""" 4 .el .IP "\f(CWstart\fR" 4 .IX Item "start" If the first argument is \f(CW\*(C`start\*(C'\fR, or no first argument is given, \&\fBrc.news\fR initiates \s-1INN\s0 startup. .ie n .IP """stop""" 4 .el .IP "\f(CWstop\fR" 4 .IX Item "stop" If the first argument is \f(CW\*(C`stop\*(C'\fR, \fBrc.news\fR initiates \s-1INN\s0 shutdown. .SH "EXAMPLES" .IX Header "EXAMPLES" To start \s-1INN\s0 and leave certain error messages going to the terminal: .PP .Vb 1 \& su news \-s /bin/sh \-c /rc.news .Ve .PP To run \s-1INN\s0 at startup time from appropriate system boot scripts: .PP .Vb 1 \& su news \-s /bin/sh \-c /rc.news >> /rc.news 2>&1 .Ve .PP To stop \s-1INN:\s0 .PP .Vb 1 \& su news \-s /bin/sh \-c \*(Aq/rc.news stop\*(Aq .Ve .SH "BUGS" .IX Header "BUGS" Running \f(CW\*(C`rc.news start\*(C'\fR as root is never the right thing to do, so we should at minimum check for this and error, or perhaps change effective user \s-1ID\s0. .SH "HISTORY" .IX Header "HISTORY" // \s-1FIXME:\s0 any attribution for \fIrc.news\fR itself? .PP This manual page written by Jeffrey M.\ Vinocur for InterNetNews. .PP \&\f(CW$Id:\fR rc.news.pod 9723 2014\-09\-24 17:54:24Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIctlinnd\fR\|(8), \fIcnfsstat\fR\|(8), \fIexpirerm\fR\|(8), \fIinn.conf\fR\|(5), \fIinnwatch\fR\|(8), \fIovdb\fR\|(5). inn-2.6.0/doc/man/overchan.80000644000175200017520000001317712575023702015147 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "OVERCHAN 8" .TH OVERCHAN 8 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" overchan \- Batch update the INN overview database .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fBoverchan\fR [\fIfile\fR ...] .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\fBoverchan\fR reads overview data from the specified files or from standard input if no files are specified, and writes that data into the \s-1INN\s0 overview database. (The file \f(CW\*(C`\-\*(C'\fR means to read data from standard input as well.) .PP Normally, overview data is stored by \fBinnd\fR for articles as they're accepted. For performance, however, it's sometimes useful to have overview data written by a separate process. To do this, set \&\fIuseoverchan\fR to true in \fIinn.conf\fR to tell \fBinnd\fR to not write overview data directly and then add an \fBoverchan\fR channel in \&\fInewsfeeds\fR: .PP .Vb 1 \& overview!:*:Tc,WnteO:/overchan .Ve .PP where is \fIpathbin\fR in \fIinn.conf\fR. Additionally, \fBoverchan\fR can be used to bulk-load overview data from appropriately formatted batch files (such as overflow files from an \fBoverchan\fR channel). .PP Each line of input should have the following format: .PP .Vb 1 \& .Ve .PP where is the storage \s-1API\s0 token of the article in textual form (surrounded by \f(CW\*(C`@\*(C'\fR characters), is the arrival timestamp of the article in seconds since epoch, is the expiration time of the article in seconds since epoch or 0 if there is none, and is the tab-separated overview data. Each of these fields must be separated by a single space. .SH "HISTORY" .IX Header "HISTORY" Written by Rob Robertson and Rich \f(CW$alz\fR for InterNetNews. Man page rewritten in \s-1POD\s0 by Russ Allbery . .PP \&\f(CW$Id:\fR overchan.pod 9767 2014\-12\-07 21:13:43Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIinn.conf\fR\|(5), \fIinnd\fR\|(8), \fInewsfeeds\fR\|(5) inn-2.6.0/doc/man/nnrpd.80000644000175200017520000004360512575023702014462 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "NNRPD 8" .TH NNRPD 8 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" nnrpd \- NNTP server for reader clients .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fBnnrpd\fR [\fB\-DfnoSt\fR] [\fB\-4\fR \fIaddress\fR] [\fB\-6\fR \fIaddress\fR] [\fB\-b\fR \fIaddress\fR] [\fB\-c\fR \fIconfigfile\fR] [\fB\-i\fR \fIinitial\fR] [\fB\-I\fR \fIinstance\fR] [\fB\-p\fR \fIport\fR] [\fB\-P\fR \fIprefork\fR] [\fB\-r\fR \fIreason\fR] [\fB\-s\fR \fIpadding\fR] .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\fBnnrpd\fR is an \s-1NNTP\s0 server for newsreaders. It accepts commands on its standard input and responds on its standard output. It is normally invoked by \fIinnd\fR\|(8) with those descriptors attached to a remote client connection. \fBnnrpd\fR also supports running as a standalone daemon. .PP Unlike \fIinnd\fR\|(8), \fBnnrpd\fR supports all \s-1NNTP\s0 commands for user-oriented reading and posting. \fBnnrpd\fR uses the \fIreaders.conf\fR file to control who is authorized to access the Usenet database. .PP On exit, \fBnnrpd\fR will report usage statistics through \fIsyslog\fR\|(3). .PP \&\fBnnrpd\fR only reads config files (both \fIreaders.conf\fR and \fIinn.conf\fR) when it is spawned. You can therefore never change the behavior of a client that's already connected. If \fBnnrpd\fR is run from \fBinnd\fR (the default) or from \fIinetd\fR\|(8), \fIxinetd\fR\|(8), or some equivalent, a new \fBnnrpd\fR process is spawned for every connection and therefore any changes to configuration files will be immediately effective for all new connections. If you are instead running \fBnnrpd\fR with the \fB\-D\fR option, any configuration changes won't take effect until \fBnnrpd\fR is restarted. .PP The \fIinn.conf\fR setting \fInnrpdflags\fR can be used to pass any of the options below to instances of \fBnnrpd\fR that are spawned directly from \&\fBinnd\fR. Many options only make sense when \fB\-D\fR is used, so these options should not be used with \fInnrpdflags\fR. See also the discussion of \fInnrpdflags\fR in \fIinn.conf\fR\|(5). .PP When \fInnrpdloadlimit\fR in \fIinn.conf\fR is not \f(CW0\fR, it will also reject connections if the load average is greater than that value (typically \&\f(CW16\fR). \fBnnrpd\fR can also prevent high-volume posters from abusing your resources. See the discussion of exponential backoff in \fIinn.conf\fR\|(5). .SH "OPTIONS" .IX Header "OPTIONS" .IP "\fB\-4\fR \fIaddress\fR" 4 .IX Item "-4 address" The \fB\-4\fR parameter instructs \fBnnrpd\fR to bind to the specified IPv4 address when started as a standalone daemon using the \fB\-D\fR flag. This has to be a valid IPv4 address belonging to an interface of the local host. It can also be \f(CW0.0.0.0\fR, saying to bind to all addresses (this is the default). .IP "\fB\-6\fR \fIaddress\fR" 4 .IX Item "-6 address" The \fB\-6\fR parameter instructs \fBnnrpd\fR to bind to the specified IPv6 address when started as a standalone daemon using the \fB\-D\fR flag. This has to be a valid IPv6 address belonging to an interface of the local host. It can also be \f(CW\*(C`::0\*(C'\fR, saying to bind to all IPv6 addresses. .Sp By default, \fBnnrpd\fR in daemon mode listens to both IPv4 and IPv6 addresses. With this option, it will listen only to the specified IPv6 addresses. On some systems however, a value of \f(CW\*(C`::0\*(C'\fR will cause it to listen to all IPv4 addresses as well. .IP "\fB\-b\fR \fIaddress\fR" 4 .IX Item "-b address" Similar to the \fB\-4\fR flag. \fB\-b\fR is kept for backwards compatibility. .IP "\fB\-c\fR \fIconfigfile\fR" 4 .IX Item "-c configfile" By default, \fBnnrpd\fR reads the \fIreaders.conf\fR to determine how to authenticate connections. The \fB\-c\fR flag specifies an alternate file for this purpose. If the file name isn't fully qualified, it is taken to be relative to \fIpathetc\fR in \fIinn.conf\fR. (This is useful to have several instances of \fBnnrpd\fR running on different ports or \s-1IP\s0 addresses with different settings.) .IP "\fB\-D\fR" 4 .IX Item "-D" If specified, this parameter causes \fBnnrpd\fR to operate as a daemon. That is, it detaches itself and runs in the background, forking a process for every connection. By default, \fBnnrpd\fR listens on the \s-1NNTP\s0 port (119), so either \fIinnd\fR\|(8) has to be started on another port or the \fB\-p\fR parameter used. Note that with this parameter, \fBnnrpd\fR continues running until killed. This means that it reads \fIinn.conf\fR once on startup and never again until restarted. \fBnnrpd\fR should therefore be restarted if \&\fIinn.conf\fR is changed. .Sp When started in daemon mode, \fBnnrpd\fR will write its \s-1PID\s0 into a file in the \fIpathrun\fR directory. The file will be named \fInnrpd.pid\fR if \fBnnrpd\fR listens on port 119 (default), or \fInnrpd\-%d.pid\fR, where \f(CW%d\fR is replaced with the port that \fBnnrpd\fR is configured to listen on (\fB\-p\fR option is given and its argument is not \f(CW119\fR). .IP "\fB\-f\fR" 4 .IX Item "-f" If specified, \fBnnrpd\fR does not detach itself and runs in the foreground when started as a standalone daemon using the \fB\-D\fR flag. .IP "\fB\-i\fR \fIinitial\fR" 4 .IX Item "-i initial" Specify an initial command to \fBnnrpd\fR. When used, \fIinitial\fR is taken as if it were the first command received by \fBnnrpd\fR. After having responded, \fBnnrpd\fR will close the connection. .IP "\fB\-I\fR \fIinstance\fR" 4 .IX Item "-I instance" If specified, \fIinstance\fR is used as an additional static portion within message-IDs generated by \fBnnrpd\fR; typically this option would be used where a cluster of machines exist with the same virtual hostname and must be disambiguated during posts. .IP "\fB\-n\fR" 4 .IX Item "-n" The \fB\-n\fR flag turns off resolution of \s-1IP\s0 addresses to names. If you only use IP-based restrictions in \fIreaders.conf\fR and can handle \s-1IP\s0 addresses in your logs, using this flag may result in some additional speed. .IP "\fB\-o\fR" 4 .IX Item "-o" The \fB\-o\fR flag causes all articles to be spooled instead of sending them to \fIinnd\fR\|(8). \fBrnews\fR with the \fB\-U\fR flag should be invoked from cron on a regular basis to take care of these articles. This flag is useful if \fIinnd\fR\|(8) is accepting articles and \fBnnrpd\fR is started standalone or using \fIinetd\fR\|(8). .IP "\fB\-p\fR \fIport\fR" 4 .IX Item "-p port" The \fB\-p\fR parameter instructs \fBnnrpd\fR to listen on \fIport\fR when started as a standalone daemon using the \fB\-D\fR flag. .IP "\fB\-P\fR \fIprefork\fR" 4 .IX Item "-P prefork" The \fB\-P\fR parameter instructs \fBnnrpd\fR to prefork \fIprefork\fR children awaiting connections when started as a standalone daemon using the \&\fB\-D\fR flag. .IP "\fB\-r\fR \fIreason\fR" 4 .IX Item "-r reason" If the \fB\-r\fR flag is used, then \fBnnrpd\fR will reject the incoming connection giving \fIreason\fR as the text. This flag is used by \fIinnd\fR\|(8) when it is paused or throttled. \fIreason\fR should be encoded in \s-1UTF\-8\s0. .IP "\fB\-s\fR \fIpadding\fR" 4 .IX Item "-s padding" As each command is received, \fBnnrpd\fR tries to change its \f(CW\*(C`argv\*(C'\fR array so that \fIps\fR\|(1) will print out the command being executed. To get a full display, the \fB\-s\fR flag may be used with a long string as its argument, which will be overwritten when the program changes its title. .IP "\fB\-S\fR" 4 .IX Item "-S" If specified, \fBnnrpd\fR will start a negotiation for a \s-1TLS\s0 session as soon as connected. To use this flag, the OpenSSL \s-1SSL\s0 and crypto libraries must have been found at configure time, or \fB\-\-with\-openssl\fR specified at configure time. For more information on running \fBnnrpd\fR with \s-1TLS\s0 support, see \*(L"\s-1TLS\s0 \s-1SUPPORT\s0\*(R". .IP "\fB\-t\fR" 4 .IX Item "-t" If the \fB\-t\fR flag is used, then all client commands and initial responses will be traced by reporting them in syslog. This flag is set by \fIinnd\fR\|(8) under the control of the \fIctlinnd\fR\|(8) \f(CW\*(C`trace\*(C'\fR command, and is toggled upon receipt of a \s-1SIGHUP\s0; see \fIsignal\fR\|(2). .SH "TLS SUPPORT" .IX Header "TLS SUPPORT" If \s-1INN\s0 is built with \fB\-\-with\-openssl\fR or if the OpenSSL \s-1SSL\s0 and crypto libraries are found at configure time, \fBnnrpd\fR will support news reading over \s-1TLS\s0 (also known as \s-1SSL\s0). For clients that use the \s-1STARTTLS\s0 command, no special configuration is needed beyond creating a \s-1TLS/SSL\s0 certificate for the server. You should do this in exactly the same way that you would generate a certificate for a web server. .PP If you're happy with a self-signed certificate (which will generate warnings with some news reader clients), you can create and install one in the default path by running \f(CW\*(C`make cert\*(C'\fR after \f(CW\*(C`make install\*(C'\fR when installing \s-1INN\s0, or by running the following commands: .PP .Vb 7 \& umask 077 \& openssl req \-new \-x509 \-nodes \-out /cert.pem \e \& \-days 366 \-keyout /key.pem \& chown news:news /cert.pem \& chmod 640 /cert.pem \& chown news:news /key.pem \& chmod 600 /key.pem .Ve .PP Replace the paths with something appropriate to your \s-1INN\s0 installation. This will create a self-signed certificate that will expire in a year. The \fBopenssl\fR program will ask you a variety of questions about your organization. Enter the fully qualified domain name of the server as the name the certificate is for. .PP You then have to set these \fIinn.conf\fR parameters with the right paths: .PP .Vb 3 \& tlscapath: \& tlscertfile: /cert.pem \& tlskeyfile: /key.pem .Ve .PP In case you have a certificate authority root certificate, you can also set \fItlscafile\fR to its path. .PP There are two common ways for a news client to negotiate a \s-1TLS\s0 connection: either via the use of the \s-1STARTTLS\s0 command on the usual \s-1NNTP\s0 port (119) or via the now discouraged way (per \s-1RFC\s0 4642) to immediately negotiate an encrypted session upon connection on a dedicated port (usually 563). As most news clients currently do not use the \s-1STARTTLS\s0 command, and instead expect to connect to a separate port (563) and start a \s-1TLS\s0 negotiation immediately, it is still useful to provide a legacy way for these news clients to encrypt the \s-1NNTP\s0 session. \fBinnd\fR does not, however, know how to listen for connections to that separate port. You will therefore need to arrange for \fBnnrpd\fR to listen on that port through some other means. This can be done with the \fB\-D\fR flag along with \f(CW\*(C`\-p 563\*(C'\fR and put into your init scripts: .PP .Vb 1 \& su news \-s /bin/sh \-c \*(Aq/nnrpd \-D \-p 563 \-S\*(Aq .Ve .PP but the easiest way is probably to add a line like: .PP .Vb 1 \& nntps stream tcp nowait news /nnrpd nnrpd \-S .Ve .PP to \fI/etc/inetd.conf\fR or the equivalent on your system and let \fBinetd\fR run \fBnnrpd\fR. (Change the path to \fBnnrpd\fR to match your installation.) You may need to replace \f(CW\*(C`nntps\*(C'\fR with \f(CW563\fR if \f(CW\*(C`nntps\*(C'\fR isn't defined in \fI/etc/services\fR on your system. .PP Optionally, you may set the \fItlsciphers\fR, \fItlscompression\fR, \&\fItlseccurve\fR, \fItlspreferserverciphers\fR, and \fItlsprotocols\fR parameters in \fIinn.conf\fR to fine-tune the behaviour of the \s-1SSL/TLS\s0 negotiation whenever a new attack on the \s-1TLS\s0 protocol or some supported cipher suite is discovered. .SH "PROTOCOL DIFFERENCES" .IX Header "PROTOCOL DIFFERENCES" \&\fBnnrpd\fR implements the \s-1NNTP\s0 commands defined in \s-1RFC\s0\ 3977 (\s-1NNTP\s0), \&\s-1RFC\s0\ 4642 (\s-1TLS/NNTP\s0), \s-1RFC\s0\ 4643 (\s-1NNTP\s0 authentication) and \s-1RFC\s0\ 6048 (\s-1NNTP\s0 \s-1LIST\s0 additions) with the following differences: .IP "1." 4 The \s-1XGTITLE\s0 [\fIwildmat\fR] command is provided. This extension is used by ANU-News and documented in \s-1RFC\s0\ 2980. It returns a \f(CW282\fR reply code, followed by a one-line description of all newsgroups that match the pattern. The default is the current group. .Sp Note that \s-1LIST\s0 \s-1NEWSGROUPS\s0 should be used instead of \s-1XGTITLE\s0. .IP "2." 4 The \s-1XHDR\s0 \fIheader\fR [\fImessage-ID\fR|\fIrange\fR] command is implemented. It returns a \f(CW221\fR reply code, followed by specific headers for the specified range; the default is to return the data for the current article. See \s-1RFC\s0\ 2980. .Sp Note that \s-1HDR\s0 should be used instead of \s-1XHDR\s0. .IP "3." 4 The \s-1XOVER\s0 [\fIrange\fR] command is provided. It returns a \f(CW224\fR reply code, followed by the overview data for the specified range; the default is to return the data for the current article. See \s-1RFC\s0\ 2980. .Sp Note that \s-1OVER\s0 should be used instead of \s-1XOVER\s0. .IP "4." 4 A new command, \s-1XPAT\s0 \fIheader\fR \fImessage-ID\fR|\fIrange\fR \fIpattern\fR [\fIpattern\fR ...], is provided. The first argument is the case-insensitive name of the header to be searched. The second argument is either an article range or a single message-ID, as specified in \s-1RFC\s0\ 2980. The third argument is a \fIuwildmat\fR\|(3)\-style pattern; if there are additional arguments, they are joined together separated by a single space to form the complete pattern. This command is similar to the \s-1XHDR\s0 command. It returns a \f(CW221\fR response code, followed by the text response of all article numbers that match the pattern. .IP "5." 4 A newsgroup name is case-sensitive for \fBnnrpd\fR. .IP "6." 4 If \s-1IHAVE\s0 has been advertised, it will not necessarily be advertised for the entire session (contrary to section 3.4.1 of \s-1RFC\s0\ 3977). \fBnnrpd\fR only advertises the \s-1IHAVE\s0 capability when it is really available. .IP "7." 4 \&\fBnnrpd\fR allows a wider syntax for wildmats and ranges (especially \f(CW\*(C`\-\*(C'\fR and \f(CW\*(C`\-\f(CIarticle\-number\f(CW\*(C'\fR). .SH "HISTORY" .IX Header "HISTORY" Written by Rich \f(CW$alz\fR for InterNetNews. Overview support added by Rob Robertston and Rich in January, 1993. Exponential backoff (for posting) added by Dave Hayes in Febuary 1998. .PP \&\f(CW$Id:\fR nnrpd.pod 9936 2015\-09\-02 12:35:17Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIctlinnd\fR\|(8), \fIinnd\fR\|(8), \fIinn.conf\fR\|(5), \fIreaders.conf\fR\|(5), \fIsignal\fR\|(2), \fIuwildmat\fR\|(3). inn-2.6.0/doc/man/innupgrade.80000644000175200017520000001735212575023702015475 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "INNUPGRADE 8" .TH INNUPGRADE 8 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" innupgrade \- Upgrade INN configuration files .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fBinnupgrade\fR \fIdirectory\fR .PP \&\fBinnupgrade\fR [\fB\-t\fR \fItype\fR] \fB\-f\fR \fIfile\fR .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\fBinnupgrade\fR is intended to be run during a major upgrade of \s-1INN\s0 to fix the configuration files with any required changes. If given a directory, it will scan that directory for any files that it has updates defined for, try to perform those updates, and replace the files with updated versions if applying the updates resulted in any changes. The old versions of the files will be saved with a \f(CW\*(C`.OLD\*(C'\fR extension. .PP If the \fB\-f\fR flag is used, only that file will be updated. If the file name doesn't match the standard file name of an \s-1INN\s0 configuration file, the optional \fB\-t\fR flag may be given to specify the type. See \&\*(L"\s-1EXAMPLES\s0\*(R" for an example of this. .PP Currently, \fBinnupgrade\fR knows how to apply the following updates: .IP "\fIinn.conf\fR" 2 .IX Item "inn.conf" .RS 2 .PD 0 .IP "\(bu" 2 .PD Quote values with whitespace and comment out keys with no values, required for the change in configuration parsers introduced in \s-1INN\s0\ 2.4. The new format is not backward compatible with the previous parser, since the previous parser will include the double-quotes in the value of the parameter. .IP "\(bu" 2 Add the \fIhismethod\fR parameter if not found (introduced in \s-1INN\s0\ 2.4, with the default value \f(CW\*(C`hisv6\*(C'\fR). Rename \fInntpactsync\fR to \&\fIincominglogfrequency\fR (since \s-1INN\s0\ 2.5). Rename \fIaddnntppostingdate\fR and \fIaddnntppostinghost\fR to respectively \fIaddinjectiondate\fR and \&\fIaddinjectionpostinghost\fR (since \s-1INN\s0\ 2.6). .IP "\(bu" 2 If the \fIoverview.fmt\fR file exists, its content is merged in the \fIextraoverviewadvertised\fR and \fIextraoverviewhidden\fR parameters introduced in \s-1INN\s0\ 2.5. The file is then renamed to \fIoverview.fmt.OLD\fR. .IP "\(bu" 2 If the \fIsasl.conf\fR file exists, its content is merged in the \fItlscapath\fR, \&\fItlscafile\fR, \fItlscertfile\fR and \fItlskeyfile\fR parameters introduced in \&\s-1INN\s0\ 2.5. The file is then renamed to \fIsasl.conf.OLD\fR. .RE .RS 2 .RE .IP "\fInewsfeeds\fR" 2 .IX Item "newsfeeds" .RS 2 .PD 0 .IP "\(bu" 2 .PD Replace the use of \fBstartinnfeed\fR with the appropriate direct invocation of \fBinnfeed\fR or \fBimapfeed\fR. .RE .RS 2 .RE .PP A few obsolete programs or configuration files are renamed with a \f(CW\*(C`.OLD\*(C'\fR extension by \fBinnupgrade\fR. Obsolete man pages are directly removed. .PP Normally, \fBinnupgrade\fR should be run at least on the \fIpathetc\fR directory after any upgrade of \s-1INN\s0 other than a patch release (any upgrade that changes the first or second version numbers). This may occur automatically during the upgrade process. .SH "OPTIONS" .IX Header "OPTIONS" .IP "\fB\-f\fR \fIfile\fR" 4 .IX Item "-f file" Only act on \fIfile\fR rather than working on an entire directory. .IP "\fB\-t\fR \fItype\fR" 4 .IX Item "-t type" For a file specified with \fB\-f\fR, parse it and upgrade it as if it were named \fItype\fR. Used for upgrading files with the same syntax as normal \&\s-1INN\s0 configuration files but with different names. Only makes sense in combination with \fB\-f\fR. .SH "EXAMPLES" .IX Header "EXAMPLES" Upgrade any configuration files found in \fIpathetc\fR and append a \f(CW\*(C`.OLD\*(C'\fR extension to obsolete files in \fIpathetc\fR: .PP .Vb 1 \& innupgrade .Ve .PP Upgrade only \fI/news/etc/inn.conf\fR: .PP .Vb 1 \& innupgrade \-f /news/etc/inn.conf .Ve .PP Upgrade a file named \fIinn\-special.conf\fR that should have the same syntax as \fIinn.conf\fR: .PP .Vb 1 \& innupgrade \-t inn.conf \-f inn\-special.conf .Ve .PP Any upgrade rules that apply to \fIinn.conf\fR will be applied to the alternate file. .SH "HISTORY" .IX Header "HISTORY" Written by Russ Allbery for InterNetNews. .PP \&\f(CW$Id:\fR innupgrade.pod 9767 2014\-12\-07 21:13:43Z iulius $ inn-2.6.0/doc/man/libstorage.30000644000175200017520000004523712575023702015472 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "libstorage 3" .TH libstorage 3 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" libstorage \- routines for managing INN storage .SH "SYNOPSIS" .IX Header "SYNOPSIS" .Vb 1 \& #include \& \& typedef enum { \& RETR_ALL, \& RETR_HEAD, \& RETR_BODY, \& RETR_STAT \& } RETRTYPE; \& \& typedef enum { \& SM_RDWR, \& SM_PREOPEN \& } SMSETUP; \& \& typedef unsigned char STORAGECLASS; \& typedef unsigned char STORAGETYPE; \& \& typedef struct token { \& STORAGETYPE type; \& STORAGECLASS class; \& char token[STORAGE_TOKEN_LENGTH]; \& } TOKEN; \& \& typedef struct { \& unsigned char type; \& const char *data; \& struct iovec *iov; \& int iovcnt; \& size_t len; \& unsigned char nextmethod; \& void *private; \& time_t arrived; \& time_t expires; \& char *groups; \& int groupslen; \& TOKEN *token; \& } ARTHANDLE; \& \& typedef enum { \& SELFEXPIRE, \& SMARTNGNUM, \& EXPENSIVESTAT \& } PROBETYPE; \& \& typedef enum { \& SM_ALL, \& SM_HEAD, \& SM_CANCELLEDART \& } FLUSHTYPE; \& \& struct artngnum { \& char *groupname; \& ARTNUM artnum; \& }; \& \& bool IsToken(const char *text); \& \& char *TokenToText(const TOKEN token); \& \& TOKEN TextToToken(const char *text); \& \& bool SMsetup(SMSETUP type, void *value); \& \& bool SMinit(void); \& \& TOKEN SMstore(const ARTHANDLE article); \& \& ARTHANDLE *SMretrieve(const TOKEN token, const RETRTYPE amount); \& \& ARTHANDLE *SMnext(const ARTHANDLE *article, const RETRTYPE amount); \& \& void SMfreearticle(ARTHANDLE *article); \& \& bool SMcancel(TOKEN token); \& \& bool SMprobe(PROBETYPE type, TOKEN *token, void *value); \& \& void SMprintfiles(FILE *file, TOKEN token, char **xref, int ngroups); \& \& bool SMflushcacheddata(FLUSHTYPE type); \& \& char *SMexplaintoken(const TOKEN token); \& \& void SMshutdown(void); \& \& int SMerrno; \& \& char *SMerrorstr; \& \& #include \& \& #define OV_NOSPACE ... \& \& typedef enum { \& OVSPACE, \& OVSORT, \& OVCUTOFFLOW, \& OVGROUPBASEDEXPIRE, \& OVSTATICSEARCH, \& OVSTATALL, \& OVCACHEKEEP, \& OVCACHEFREE \& } OVCTLTYPE; \& \& typedef enum { \& OVNEWSGROUP, \& OVARRIVED, \& OVNOSORT \& } OVSORTTYPE; \& \& typedef enum { \& OVADDCOMPLETED, \& OVADDFAILED, \& OVADDGROUPNOMATCH \& } OVADDRESULT; \& \& bool OVopen(int mode); \& \& bool OVctl(OVCTLTYPE type, void *val); \& \& bool OVgroupstats(char *group, int *lo, int *hi, int *count, int *flag); \& \& bool OVgroupadd(char *group, ARTNUM lo, ARTNUM hi, char *flag); \& \& bool OVgroupdel(char *group); \& \& OVADDRESULT OVadd(TOKEN token, char *data, int len, time_t arrived, time_t expires); \& \& bool OVcancel(TOKEN token); \& \& void *OVopensearch(char *group, int low, int high); \& \& bool OVsearch(void *handle, ARTNUM *artnum, char **data, int *len, TOKEN *token, time_t *arrived); \& \& void OVclosesearch(void *handle); \& \& bool OVgetartinfo(char *group, ARTNUM artnum, TOKEN *token); \& \& bool OVexpiregroup(char *group, int *lo, struct history *h); \& \& typedef struct _OVGE { \& bool delayrm; \& bool usepost; \& bool quiet; \& bool keep; \& bool earliest; \& bool ignoreselfexpire; \& char *filename; \& time_t now; \& float timewarp; \& } OVGE; \& \& void OVclose(void); .Ve .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\fBlibstorage\fR is a library of common utility (the storage manager) routines for accessing Usenet articles and related data independent of particular storage method; this is known as the storage \s-1API\s0. .PP The storage manager's function is to isolate the applications from the individual methods and make the policy decisions as to which storage method should be called to store an article. One of the core concepts in the storage \s-1API\s0 is that articles are not manipulated using the message-ID or article number, but rather a token that uniquely identifies the article to the method that stored it. This may seem to be redundant since the message-ID already is a unique identifier for the article; however, since the storage method generates the token, it can encode all the information it needs to locate the article in the token. .PP \&\fB\s-1OV\s0\fR is a common utility routines for accessing newsgroups and overview data independent of particular overview method. .PP The \fB\s-1OV\s0\fR function is to isolate the applications from the individual methods. All articles passed through the storage \s-1API\s0 are assumed to be in wire format. Wire format means \f(CW\*(C`\er\en\*(C'\fR at the end of lines, dot-stuffed lines (that is to say \f(CW\*(C`.\*(C'\fR at the beginning of lines which already start with \f(CW\*(C`.\*(C'\fR), and \f(CW\*(C`.\er\en\*(C'\fR at the end of article on \s-1NNTP\s0 stream are not stripped. This has a performance win when transferring articles. Note that for the tradspool method, wire format can be disabled. This is just for compatibility which is needed by some old tools written for traditional spool. .PP The \fBIsToken\fR function checks to see if the text is formatted as a text token string. It returns true if formatted correctly or returns false if not. .PP The \fBTokenToText\fR function converts a token into a text string for output. .PP The \fBTextToToken\fR function converts a text string into a token. .PP The \fBSMsetup\fR function configures some parameters for use by the storage manager. \fItype\fR is one of following: .ie n .IP """SM_RDWR""" 4 .el .IP "\f(CWSM_RDWR\fR" 4 .IX Item "SM_RDWR" Allow read/write open for storage files (default is false). .ie n .IP """SM_PREOPEN""" 4 .el .IP "\f(CWSM_PREOPEN\fR" 4 .IX Item "SM_PREOPEN" Open all storage files at startup time and keep them (default is false). .PP \&\fIvalue\fR is the pointer which tells each type's value. It returns true if setup is successful, or false if not. .PP The \fBSMinit\fR function calls the setup function for all of the configured methods based on \fBSMsetup\fR. This function should be called prior to all other storage \s-1API\s0 functions which begin with \f(CW\*(C`SM\*(C'\fR except \fBSMsetup\fR. It returns true if initialization is successful or returns false if not. \&\fBSMinit\fR returns true, unless all storage methods fail initialization. .PP The \fBSMstore\fR function stores an article specified with \fIarticle\fR. The headers and body of the article are supplied to \fBSMstore\fR using the \&\fIiov\fR and \fIiovcnt\fR members of \fB\s-1ARTHANDLE\s0\fR. (\fIdata\fR and \fIprivate\fR are ignored by \fBSMstore\fR.) If \fIarrived\fR is specified, \fBSMstore\fR uses its value as article's arrival time; otherwise \fBSMstore\fR uses the current time for it. \fBSMstore\fR returns the token type or returns \fB\s-1TOKEN_EMPTY\s0\fR if the article is not stored because some error occurs or simply does not match any \fIuwildmat\fR\|(3) expression in \fIstorage.conf\fR. \fBSMstore\fR fails if \&\fB\s-1SM_RDWR\s0\fR has not been set to true with \fBSMsetup\fR. .PP The \fBSMretrieve\fR function retrieves an article specified with \fItoken\fR. \&\fIamount\fR is the one of following which specifies retrieving type: .ie n .IP """RETR_ALL""" 4 .el .IP "\f(CWRETR_ALL\fR" 4 .IX Item "RETR_ALL" Retrieve the whole article. .ie n .IP """RETR_HEAD""" 4 .el .IP "\f(CWRETR_HEAD\fR" 4 .IX Item "RETR_HEAD" Retrieve the headers of the article. .ie n .IP """RETR_BODY""" 4 .el .IP "\f(CWRETR_BODY\fR" 4 .IX Item "RETR_BODY" Retrieve the body of the article. .ie n .IP """RETR_STAT""" 4 .el .IP "\f(CWRETR_STAT\fR" 4 .IX Item "RETR_STAT" Just check to see if the article exists. .PP \&\fBSMretrieve\fR provides the article data via the \fIdata\fR and \fIlen\fR members of \fB\s-1ARTHANDLE\s0\fR. (\fIiov\fR is not set by \fBSMretrieve\fR.) The data area indicated by \fB\s-1ARTHANDLE\s0\fR should not be modified. .PP The \fBSMnext\fR function is similar in function to \fBSMretrieve\fR except that it is intended for traversing the method's article store sequentially. To start a query, \fBSMnext\fR should be called with a \s-1NULL\s0 pointer \fB\s-1ARTHANDLE\s0\fR. Then \fBSMnext\fR returns \fB\s-1ARTHANDLE\s0\fR which should be used for the next query. If a \s-1NULL\s0 pointer \fB\s-1ARTHANDLE\s0\fR is returned, no articles are left to be queried. If \fIdata\fR of \fB\s-1ARTHANDLE\s0\fR is \s-1NULL\s0 pointer or \fIlen\fR of \&\fB\s-1ARTHANDLE\s0\fR is \f(CW0\fR, it indicates the article may be corrupted and should be cancelled by \fISMcancel\fR. The data area indicated by \fB\s-1ARTHANDLE\s0\fR should not be modified. .PP The \fBSMfreearticle\fR function frees all allocated memory used by \&\fBSMretrieve\fR and \fBSMnext\fR. If \fBSMnext\fR will be called with previously returned \fB\s-1ARTHANDLE\s0\fR, \fBSMfreearticle\fR should not be called as \fBSMnext\fR frees allocated memory internally. .PP The \fBSMcancel\fR function removes the article specified with \fItoken\fR. It returns true if cancellation is successful or returns false if not. \&\fBSMcancel\fR fails if \fB\s-1SM_RDWR\s0\fR has not been set to true with \fBSMsetup\fR. .PP The \fBSMprobe\fR function checks the token on \fB\s-1PROBETYPE\s0\fR. \fItype\fR is one of following: .ie n .IP """SELFEXPIRE""" 4 .el .IP "\f(CWSELFEXPIRE\fR" 4 .IX Item "SELFEXPIRE" Check to see if the method of the token has self expire functionality. .ie n .IP """SMARTNGNUM""" 4 .el .IP "\f(CWSMARTNGNUM\fR" 4 .IX Item "SMARTNGNUM" Get the newsgroup name and the article number of the token. .ie n .IP """EXPENSIVESTAT""" 4 .el .IP "\f(CWEXPENSIVESTAT\fR" 4 .IX Item "EXPENSIVESTAT" Check to see whether checking the existence of an article is expensive or not. .PP The \fBSMprintfiles\fR function shows file name or token usable by \fIfastrm\fR\|(8). .PP The \fBSMflushcacheddata\fR function flushes cached data on each storage method. \fItype\fR is one of following: .ie n .IP """SM_HEAD""" 4 .el .IP "\f(CWSM_HEAD\fR" 4 .IX Item "SM_HEAD" Flush cached header. .ie n .IP """SM_CANCELLEDART""" 4 .el .IP "\f(CWSM_CANCELLEDART\fR" 4 .IX Item "SM_CANCELLEDART" Flush the articles which should be cancelled. .ie n .IP """SM_ALL""" 4 .el .IP "\f(CWSM_ALL\fR" 4 .IX Item "SM_ALL" Flush all cached data. .PP The \fBSMexplaintoken\fR function returns a human-readable text string with a clear, decoded form of the storage \s-1API\s0 token. .PP The \fBSMshutdown\fR function calls the shutdown for each configured storage method and then frees any resources it has allocated for itself. .PP \&\fBSMerrno\fR and \fBSMerrorstr\fR indicate the reason of the last error concerning storage manager. .PP \&\fBOVopen\fR calls the setup function for configured method which is specified as \fIovmethod\fR in \fIinn.conf\fR. \fImode\fR is constructed from following: .ie n .IP """OV_READ""" 4 .el .IP "\f(CWOV_READ\fR" 4 .IX Item "OV_READ" Allow read open for the overview method. .ie n .IP """OV_WRITE""" 4 .el .IP "\f(CWOV_WRITE\fR" 4 .IX Item "OV_WRITE" Allow write open for the overview method. .PP This function should be called prior to all other \s-1OV\s0 functions which begin with \f(CW\*(C`OV\*(C'\fR. .PP The \fBOVctl\fR function probes or sets some parameters for the overview method. \&\fItype\fR is one of following: .ie n .IP """OVGROUPBASEDEXPIRE""" 4 .el .IP "\f(CWOVGROUPBASEDEXPIRE\fR" 4 .IX Item "OVGROUPBASEDEXPIRE" Setup how group-based expiry is done. .ie n .IP """OVCUTOFFLOW""" 4 .el .IP "\f(CWOVCUTOFFLOW\fR" 4 .IX Item "OVCUTOFFLOW" Do not add overview data if the data is under the lowest article. .ie n .IP """OVSORT""" 4 .el .IP "\f(CWOVSORT\fR" 4 .IX Item "OVSORT" Probe which key is suitable for the overview method. .ie n .IP """OVSPACE""" 4 .el .IP "\f(CWOVSPACE\fR" 4 .IX Item "OVSPACE" Probe the overview space usage. .ie n .IP """OVSTATALL""" 4 .el .IP "\f(CWOVSTATALL\fR" 4 .IX Item "OVSTATALL" Stat all the articles when \fBOVexpiregroup\fR is called. .ie n .IP """OVSTATICSEARCH""" 4 .el .IP "\f(CWOVSTATICSEARCH\fR" 4 .IX Item "OVSTATICSEARCH" Setup if results of \fBOVsearch\fR are stored in a static buffer and must be copied before the next call to \fBOVsearch\fR. .ie n .IP """OVCACHEKEEP""" 4 .el .IP "\f(CWOVCACHEKEEP\fR" 4 .IX Item "OVCACHEKEEP" Setup whether the cache should be kept. .ie n .IP """OVCACHEFREE""" 4 .el .IP "\f(CWOVCACHEFREE\fR" 4 .IX Item "OVCACHEFREE" Free the cache. .PP The \fBOVgroupstats\fR function retrieves the specified newsgroup information from the overview method. .PP The \fBOVgroupadd\fR function informs the overview method that the specified newsgroup is being added. .PP The \fBOVgroupdel\fR function informs the overview method that the specified newsgroup is being removed. .PP The \fBOVadd\fR function stores an overview data. .PP The \fBOVcancel\fR function requests the overview method delete overview data specified with token. .PP The \fBOVopensearch\fR function requests the overview method prepare overview data retrieval. The request range is determined by \fIlow\fR and \fIhigh\fR. The setting of \fB\s-1OVSTATICSEARCH\s0\fR determines how search result data must be handled. (Note that with some storage methods, each call to \&\fBOVopensearch\fR may cause internal storage to be remapped. Therefore, multiple simultaneous searches may require data to be copied in between \&\fBOVsearch\fR calls even if \fB\s-1OVSTATICSEARCH\s0\fR is false.) .PP The \fBOVsearch\fR function retrieves information, article number, overview data, or arrival time. It should be called with \s-1NULL\s0 handle when it is the first time; subsequent \fBOVsearch\fR calls should use the handle returned by the previous call to \fBOVsearch\fR. \fBOVsearch\fR returns true, unless it reaches \fIhigh\fR, which is specified by \fBOVopensearch\fR. Retrieved overview data are sorted by article number, and \fIlen\fR is \f(CW0\fR if there is no overview data for the article. Note that the retrieved data is not necessarily null-terminated; you should only rely on \fIlen\fR octets of overview data being present. .PP The \fBOVclosesearch\fR function frees all resources which have been allocated by \fBOVopensearch\fR. .PP The \fBOVgetartinfo\fR function retrieves the overview data and the token specified with \fIartnum\fR. .PP The \fBOVexpiregroup\fR function expires the overview data for the newsgroup. It checks the existence of the article and purges the overview data if the article no longer exists. If \fIgroupbaseexpiry\fR in \fIinn.conf\fR is true, \&\fBOVexpiregroup\fR also expires articles. .PP The \fBOVclose\fR function frees all resources which are used by the overview method. .SH "HISTORY" .IX Header "HISTORY" Written by Katsuhiro Kondou for InterNetNews. Converted to \s-1POD\s0 by Julien Elie. .PP \&\f(CW$Id:\fR libstorage.pod 9073 2010\-05\-31 19:00:23Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIexpire\fR\|(8), \fIfastrm\fR\|(8), \fIinn.conf\fR\|(5), \fIstorage.conf\fR\|(5). inn-2.6.0/doc/man/innbind.80000644000175200017520000003477112575023702014766 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "INNBIND 8" .TH INNBIND 8 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" innbind \- Helper program to bind sockets to privileged ports .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fBinnbind\fR [\fB\-p\fR] \fIfd\fR,\fIfamily\fR,\fIaddress\fR,\fIport\fR [...] .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\fBinnbind\fR is a helper program that's not meant to be run directly. Instead, \fBinnd\fR and \fBnnrpd\fR use it internally to bind to ports that require root privileges to bind to. .PP This program must be installed setuid root in order for \fBinnd\fR or \&\fBnnrpd\fR to bind to ports under 1024. The only functions that it's willing to perform are to bind an open file descriptor to a given address and port or to create a new socket, bind it, and return the bound socket to its caller. It can only be run as the news user (as specified at configure time), and will only bind to ports 119, 433, 563, an additional port specified with the \fB\-\-with\-innd\-port\fR argument to configure, or ports numbered 1024 or higher. .PP Each argument to \fBinnbind\fR must be a comma-separated list of four elements. The first is the file descriptor number that should be bound, the second is the numeric family of the socket (\s-1AF_INET\s0 or \s-1AF_INET6\s0), the third is the local address to bind to (in dotted-quad format for IPv4 and in colon-separated address format for IPv6), and the fourth is the port number. To bind to all addresses with IPv4, use \f(CW0.0.0.0\fR as the address. To bind to all addresses with IPv6, use \f(CW\*(C`::\*(C'\fR as the address. .PP Multiple arguments can be specified to tell \fBinnbind\fR to bind multiple sockets at the same time. Any errors (other than permission denied \-\-\ see below) encountered will cause \fBinnbind\fR to abort, and error messages will be sent both to syslog and to standard error. .PP By default, \fBinnbind\fR attempts to just bind the already open file descriptor that it inherits from its caller. For each successfully bound file descriptor (in the order given on the command line), \fBinnbind\fR prints \f(CW\*(C`ok\*(C'\fR and a newline to its standard output. .PP On some systems (apparently just STREAMS-based systems), however, even a setuid root program cannot bind a socket to a privileged port that was created by a process that didn't have permissions. If \fBinnbind\fR gets permission denied when trying to bind a socket, it will print \f(CW\*(C`no\*(C'\fR and a newline to its standard output. It will then create a new socket, bind it as specified, and then attempt to pass that socket back to its caller using the I_SENDFD \s-1STREAMS\s0 ioctl. The caller should receive that file descriptor with I_RECVFD and use it instead of the one that it created. .PP Note that file descriptor passing is only supported on STREAMS-based systems since it is done with ioctls over a pipe. However, it is believed that those systems are exactly the systems that can't simply bind the inherited file descriptor. If this assumption proves to be incorrect, traditional \s-1BSD\s0 file descriptor passing over a Unix domain socket will have to be added. .SH "OPTIONS" .IX Header "OPTIONS" .IP "\fB\-p\fR" 4 .IX Item "-p" If given as the first command-line argument, no attempt will be made to bind the inherited file descriptor and \fBinnbind\fR will only try creation of a new file descriptor and passing it back via standard output. This option is primarily useful for testing. .SH "SECURITY" .IX Header "SECURITY" As \fBinnbind\fR is normally installed setuid root, security is even more of an issue for it than for other parts of \s-1INN\s0. It is a fairly short program, and if you understand C, you are encouraged to audit it yourself to be certain that it does only what it is supposed to do. The only \s-1INN\s0 library functions it uses are the vector functions, the message functions for error reporting, and xstrdup. .PP The ports that will be bound are restricted to prevent potential attacks made possible by the ability to bind low-numbered ports, such as exploits of the \fIrsh\fR\|(1) family of commands on some systems. If \fBinnbind\fR is installed setuid root, it can only be executed by the news user to prevent other users on the system from being able to bind to even those few privileged ports that it allows. .PP \&\fBinnbind\fR uses no external configuration files; the only files it might open are through the system \fIgetpwnam\fR\|(3) service to get the \s-1UID\s0 of the news user. The only user input that it accepts are its command-line arguments. .SH "DIAGNOSTICS" .IX Header "DIAGNOSTICS" \&\fBinnbind\fR may log the following messages to syslog and print them to standard error. .ie n .IP "cannot create socket for %s: %s" 4 .el .IP "cannot create socket for \f(CW%s:\fR \f(CW%s\fR" 4 .IX Item "cannot create socket for %s: %s" (Fatal) \fBinnbind\fR fell back on attempting to create a new socket to bind for the given argument, and the socket creation failed. .ie n .IP "cannot bind socket for %s: %s" 4 .el .IP "cannot bind socket for \f(CW%s:\fR \f(CW%s\fR" 4 .IX Item "cannot bind socket for %s: %s" (Fatal) Calling bind for the socket corresponding to the given argument failed with a system error. If the error indicates permission denied, make sure that \fBinnbind\fR is setuid root. This can also be caused by trying to use IPv6 on a system whose kernel does not support it. .ie n .IP "cannot bind to restricted port %hu in %s" 4 .el .IP "cannot bind to restricted port \f(CW%hu\fR in \f(CW%s\fR" 4 .IX Item "cannot bind to restricted port %hu in %s" (Fatal) The port number portion of the given command-line argument is for a port below 1024 which is not 119, 433, 563, or a port given to \&\fB\-\-with\-innd\-port\fR at configure time. Other ports are not allowed for security reasons. .ie n .IP "cannot get socket options for file descriptor %d: %s" 4 .el .IP "cannot get socket options for file descriptor \f(CW%d:\fR \f(CW%s\fR" 4 .IX Item "cannot get socket options for file descriptor %d: %s" (Fatal) \fBinnbind\fR was unable to get the socket options for that file descriptor. The most likely cause of this error is passing the wrong file descriptor number to \fBinnbind\fR (a file descriptor that isn't open, or that corresponds to a regular file rather than a network socket). .ie n .IP "cannot get \s-1UID\s0 for %s" 4 .el .IP "cannot get \s-1UID\s0 for \f(CW%s\fR" 4 .IX Item "cannot get UID for %s" (Fatal) \fBinnbind\fR was unable to get the \s-1UID\s0 for the news user specified during configure (and defaulting to \f(CW\*(C`news\*(C'\fR). This normally means that user isn't in the system \fIpasswd\fR file. .ie n .IP "cannot mark socket reusable for %s: %s" 4 .el .IP "cannot mark socket reusable for \f(CW%s:\fR \f(CW%s\fR" 4 .IX Item "cannot mark socket reusable for %s: %s" (Fatal) \fBinnbind\fR created a new socket for the given argument but was unable to mark its bind address reusable (the \s-1SO_REUSEADDR\s0 socket option). .ie n .IP "cannot pass file descriptor: %s" 4 .el .IP "cannot pass file descriptor: \f(CW%s\fR" 4 .IX Item "cannot pass file descriptor: %s" (Fatal) \fBinnbind\fR created and bound a new file descriptor but was unable to pass it back to its caller via its standard output, using the I_SENDFD \&\s-1STREAMS\s0 ioctl. .ie n .IP "invalid file descriptor %d: not \s-1SOCK_STREAM\s0" 4 .el .IP "invalid file descriptor \f(CW%d:\fR not \s-1SOCK_STREAM\s0" 4 .IX Item "invalid file descriptor %d: not SOCK_STREAM" (Fatal) The given file descriptor is not a \s-1SOCK_STREAM\s0 socket. \fBinnbind\fR can only bind \s-1SOCK_STREAM\s0 sockets. .ie n .IP "invalid IPv4 address %s in %s" 4 .el .IP "invalid IPv4 address \f(CW%s\fR in \f(CW%s\fR" 4 .IX Item "invalid IPv4 address %s in %s" (Fatal) The IPv4 address specified in the given command-line option could not be parsed by \fIinet_aton\fR\|(3). IPv4 addresses should be specified in the standard dotted-quad format (10.2.3.4). .ie n .IP "invalid IPv6 address %s in %s" 4 .el .IP "invalid IPv6 address \f(CW%s\fR in \f(CW%s\fR" 4 .IX Item "invalid IPv6 address %s in %s" (Fatal) The IPv6 address specified in the given command-line option could not be parsed by \fIinet_pton\fR\|(3). IPv6 addresses should be specified in \&\s-1RFC\s0\ 4291 format (1080:0:0:0:8:800:200C:417A or 1080::8:800:200C:417A). .ie n .IP "invalid command-line argument %s" 4 .el .IP "invalid command-line argument \f(CW%s\fR" 4 .IX Item "invalid command-line argument %s" (Fatal) The specified command-line argument could not be parsed or was not in the correct format. .ie n .IP "invalid file descriptor %s in %s" 4 .el .IP "invalid file descriptor \f(CW%s\fR in \f(CW%s\fR" 4 .IX Item "invalid file descriptor %s in %s" (Fatal) The file descriptor portion of the given command-line argument is not a non-negative integer. .ie n .IP "invalid port number %s in %s" 4 .el .IP "invalid port number \f(CW%s\fR in \f(CW%s\fR" 4 .IX Item "invalid port number %s in %s" (Fatal) The port number portion of the given command-line argument is not a non-negative integer. .ie n .IP "invalid protocol family %s in %s" 4 .el .IP "invalid protocol family \f(CW%s\fR in \f(CW%s\fR" 4 .IX Item "invalid protocol family %s in %s" (Fatal) The protocol family portion of the given command-line argument is not a non-negative integer. It should be equal to either \s-1AF_INET\s0 or \&\s-1AF_INET6\s0 on the system where \fBinnbind\fR is run. .ie n .IP "must be run by user %s (%lu), not %lu" 4 .el .IP "must be run by user \f(CW%s\fR (%lu), not \f(CW%lu\fR" 4 .IX Item "must be run by user %s (%lu), not %lu" (Fatal) When setuid root, \fBinnbind\fR may only be run by the news user as specified at configure time (\f(CW\*(C`news\*(C'\fR by default), for security reasons. .IP "no addresses specified" 4 .IX Item "no addresses specified" (Fatal) No arguments were given on the command line (except maybe \fB\-p\fR). .ie n .IP "port may not be zero in %s" 4 .el .IP "port may not be zero in \f(CW%s\fR" 4 .IX Item "port may not be zero in %s" (Fatal) The port number portion of the given command-line argument was zero. .ie n .IP "unknown protocol family %s in %s" 4 .el .IP "unknown protocol family \f(CW%s\fR in \f(CW%s\fR" 4 .IX Item "unknown protocol family %s in %s" (Fatal) The protocol number portion of the given command-line argument is neither \s-1AF_INET\s0 nor \s-1AF_INET6\s0. .SH "EXAMPLES" .IX Header "EXAMPLES" As mentioned above, \fBinnbind\fR is never run directly, only by \fBinnd\fR and other programs that need to bind to and listen to network ports. Sample invocations by \fBinnd\fR would be: .PP .Vb 1 \& innbind 3,10,::,119 .Ve .PP to bind the IPv6 socket on file descriptor 3 to port 119, all addresses, or: .PP .Vb 1 \& innbind 6,2,10.0.0.3,433 .Ve .PP to bind the IPv4 socket on file descriptor 6 to port 433 in the address 10.0.0.3. .SH "HISTORY" .IX Header "HISTORY" Written by Russ Allbery for InterNetNews. .PP \&\f(CW$Id:\fR innbind.pod 9767 2014\-12\-07 21:13:43Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIinet_aton\fR\|(3), \fIinet_pton\fR\|(3), \fIinnd\fR\|(8), \fInnrpd\fR\|(8). inn-2.6.0/doc/man/ident.80000644000175200017520000001345712575023702014446 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "IDENT 8" .TH IDENT 8 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" ident \- nnrpd ident resolver .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fBident\fR [\fB\-p\fR \fIport\fR] [\fB\-t\fR] .SH "DESCRIPTION" .IX Header "DESCRIPTION" This program attempts to resolve usernames for \fBnnrpd\fR by using the ident protocol to query the remote host. It contacts the remote host using either IPv4 or IPv6 depending on which protocol was used for the incoming \s-1NNTP\s0 connection. .SH "OPTIONS" .IX Header "OPTIONS" .IP "\fB\-p\fR \fIport\fR" 4 .IX Item "-p port" If this option is given, attempt to contact identd on the specified remote port (which can be a numeric or symbolic specification). Non-numeric values will be looked up using \fIgetservbyname\fR\|(3). The default value is the result of \f(CW\*(C`getservbyname("ident")\*(C'\fR if available, or port 113 otherwise. .IP "\fB\-t\fR" 4 .IX Item "-t" If this option is given, the identity returned will never have a domain part. That is, if the remote server returns a result containing an \f(CW\*(C`@\*(C'\fR character, \fBident\fR truncates the response at the \f(CW\*(C`@\*(C'\fR. This is useful to allow the \fIdefault-domain\fR parameter in \fIreaers.conf\fR to override the domain supplied by the remote host (particularly if the supplied domain part is an unqualified local machine name rather than a full domain name). .SH "EXAMPLE" .IX Header "EXAMPLE" The following \fIreaders.conf\fR\|(5) fragment tells nnrpd to trust ident information for hosts on a local network, but to replace the domain returned from the ident query: .PP .Vb 5 \& auth LAN { \& hosts: "192.168/16" \& res: "ident \-t" \& default\-domain: "internal.example.com" \& } \& \& access LAN { \& users: "*@internal.example.com" \& newsgroups: example.* \& } .Ve .PP Access is granted to the example.* groups for all users on the local network whose machines respond to ident queries. .SH "HISTORY" .IX Header "HISTORY" This documentation was written by Jeffrey M.\ Vinocur . .PP \&\f(CW$Id:\fR ident.pod 8200 2008\-11\-30 13:31:30Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fInnrpd\fR\|(8), \fIreaders.conf\fR\|(5) inn-2.6.0/doc/man/libauth.30000644000175200017520000001454412575023702014764 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "libauth 3" .TH libauth 3 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" libauth \- routines for writing nnrpd resolvers and authenticators .SH "SYNOPSIS" .IX Header "SYNOPSIS" .Vb 1 \& #include "libauth.h" \& \& struct res_info { \& struct sockaddr *client; \& struct sockaddr *local; \& char *clienthostname; \& }; \& \& struct auth_info { \& char *username; \& char *password; \& }; \& \& struct auth_info *get_auth_info(FILE *); \& struct res_info *get_res_info (FILE *); \& \& void free_auth_info(struct auth_info*); \& void free_res_info (struct res_info*); .Ve .SH "DESCRIPTION" .IX Header "DESCRIPTION" These functions provide a convenient C frontend to the nnrpd external authentication interface documented in \fIdoc/external\-auth\fR. Use of this library is \fBnot\fR required; in particular, external resolvers and authenticators written in languages other than C will need to implement the necessary functionality themselves. .PP The \fIget_auth_info()\fR and \fIget_res_info()\fR functions allocate sufficient memory for a \fBstruct auth_info\fR or \fBstruct res_info\fR and any necessary fields, and return a pointer to the struct with the fields filled in from information supplied by nnrpd (the \fBFILE*\fR parameter generally should be \f(CW\*(C`stdin\*(C'\fR). Both functions return \s-1NULL\s0 on error. The caller is responsible for deallocating the memory by using the functions below. .PP The string fields of both structs are straightforward. The \fBclient\fR and \fBlocal\fR fields of \fBstruct res_info\fR actually point to instances of \&\fBstruct sockaddr_in\fR (or \fBstruct sockaddr_in6\fR if IPv6 support is compiled in). .PP The \fIfree_auth_info()\fR and \fIfree_res_info()\fR functions free the struct passed in as argument and all necessary fields. .SH "BUGS" .IX Header "BUGS" In many cases, nnrpd provides more information than is normally useful (for example, even when calling an authenticator, the resolver information is often provided.) On the other hand, in certain cases it provides less information than might be expected (for example, if nnrpd is reading from stdin rather than a socket). The implementation is capable of handling at least the first of these issues, but that functionality is not exposed in the interface. .PP At present, \fIlibauth.h\fR and its implementation are located in \&\fIauthprogs/\fR; perhaps they should be moved to \fIinclude/\fR and \fIlib/\fR, respectively? .SH "HISTORY" .IX Header "HISTORY" Written by Jeffrey M.\ Vinocur for InterNetNews. .PP \&\f(CW$Id:\fR libauth.pod 8200 2008\-11\-30 13:31:30Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fInnrpd\fR\|(8), \fIreaders.conf\fR\|(5), \fIdoc/external\-auth\fR inn-2.6.0/doc/man/prunehistory.80000644000175200017520000001305312575023702016106 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "PRUNEHISTORY 8" .TH PRUNEHISTORY 8 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" prunehistory \- Remove tokens from Usenet history file .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fBprunehistory\fR [\fB\-p\fR] [\fB\-f\fR \fIfilename\fR] .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\fBprunehistory\fR modifies a \fIhistory\fR\|(5)\-format text file to \*(L"remove\*(R" a set of tokens from it. The tokens are removed by overwriting them with spaces, so that the size and position of any following entries does not change. This has an effect similar to expiring the article, in that it is still mentioned in the history database but cannot be retrieved. .PP \&\fBprunehistory\fR reads the standard input. The input is taken as a set of lines. Blank lines and lines starting with a number sign (\f(CW\*(C`#\*(C'\fR) are ignored. All other lines should consist of a message-ID followed by zero or more other fields (which are ignored). The message-ID is used as the \fIdbz\fR\|(3) key to get an offset into the text file. Since \fBinnd\fR only appends to the text file, \&\fBprunehistory\fR does not need to have any interaction with it. .SH "OPTIONS" .IX Header "OPTIONS" .IP "\fB\-p\fR" 4 .IX Item "-p" \&\fBprunehistory\fR will normally complain about lines that do not follow the correct format. If the \fB\-p\fR flag is used, then the program will silently print any invalid lines on its standard output. (Blank lines and comment lines are also passed through.) .IP "\fB\-f\fR \fIfilename\fR" 4 .IX Item "-f filename" The default name of the \fIhistory\fR file is \fIpathdb\fR/history; to specify a different name, use the \fB\-f\fR flag. .SH "HISTORY" .IX Header "HISTORY" Written by Rich \f(CW$alz\fR for InterNetNews. Converted to \&\s-1POD\s0 by Julien Elie. .PP \&\f(CW$Id:\fR prunehistory.pod 9447 2012\-12\-07 19:01:45Z eagle $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIdbz\fR\|(3), \fIhistory\fR\|(5), \fIinnd\fR\|(8). inn-2.6.0/doc/man/cnfsstat.80000644000175200017520000001441512575023702015163 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "CNFSSTAT 8" .TH CNFSSTAT 8 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" cnfsstat \- Show usage of CNFS buffers .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fBcnfsstat\fR [\fB\-ahpPsv\fR] [\fB\-c\fR \fIclass\fR] [\fB\-i\fR \fIseconds\fR] [\fB\-l\fR [\fIseconds\fR]] [\fB\-m\fR \fIbuffer\fR] .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\fBcnfsstat\fR reads \fIpathetc\fR/cycbuff.conf and \fIpathetc\fR/storage.conf to determine which \s-1CNFS\s0 buffers are available. It then reads the specified cyclic buffers and shows their usage status. \fBcnfsstat\fR can be invoked from \&\fBrc.news\fR if \fIdocnfsstat\fR is set to true in \fIinn.conf\fR, and the result is written to \fIsyslog\fR\|(3). .SH "OPTIONS" .IX Header "OPTIONS" .IP "\fB\-a\fR" 4 .IX Item "-a" Besides its usual output, \fBcnfsstat\fR prints the age of the oldest article in the cycbuff. You may also want to use the \fB\-v\fR flag to see extended consistency checks. .IP "\fB\-c\fR \fIclass\fR" 4 .IX Item "-c class" Only information for the specified class is printed. .IP "\fB\-h\fR" 4 .IX Item "-h" Print usage information and exit. .IP "\fB\-i\fR \fIseconds\fR" 4 .IX Item "-i seconds" With this option, \fBcnfsstat\fR has an initial sleep of \fIseconds\fR seconds at startup. This is useful when \fBcnfsstat\fR is started at the same time as \s-1INN\s0, so that it can wait a little before beginning performing its checks. .IP "\fB\-l\fR [\fIseconds\fR]" 4 .IX Item "-l [seconds]" With this option, \fBcnfsstat\fR prints a status snapshot every \fIseconds\fR seconds, and only exits if an error occurs. When unspecified, the default interval is \f(CW600\fR seconds. .Sp At each iteration, \fBcnfsstat\fR checks whether the \fIcycbuff.conf\fR and \&\fIstorage.conf\fR files have been modified, and loads the new configuration if needed. .IP "\fB\-m\fR \fIbuffer\fR" 4 .IX Item "-m buffer" Print information about the specified buffer in a format suitable for \s-1MRTG\s0. .IP "\fB\-p\fR" 4 .IX Item "-p" Print an \s-1MRTG\s0 config file. .IP "\fB\-P\fR" 4 .IX Item "-P" Write \s-1PID\s0 into \fIpathrun\fR/cnfsstat.pid. .IP "\fB\-s\fR" 4 .IX Item "-s" Write output to \fIsyslog\fR\|(3) instead of standard output. .IP "\fB\-v\fR" 4 .IX Item "-v" Write additional information, especially about consistency checks for article storage and the \fIhistory\fR file. .SH "HISTORY" .IX Header "HISTORY" Written by Katsuhiro Kondou for InterNetNews. Converted to \s-1POD\s0 by Julien Elie. .PP \&\f(CW$Id:\fR cnfsstat.pod 9721 2014\-09\-24 17:35:46Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIcycbuff.conf\fR\|(5), \fIhistory\fR\|(5), \fIinn.conf\fR\|(5), \fIrc.news\fR\|(8), \fIstorage.conf\fR\|(5). inn-2.6.0/doc/man/news.daily.80000644000175200017520000003364312575023702015417 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "NEWS.DAILY 8" .TH NEWS.DAILY 8 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" news.daily \- Perform daily Usenet maintenance tasks .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fBnews.daily\fR [\fBdelayrm\fR] [\fBexpctl\fR=\fIpath\fR] [\fBexpdir\fR=\fIpath\fR] [\fBexpireover\fR] [\fBexpireoverflags\fR=\fIargs\fR] [\fBflags\fR=\fIargs\fR] [\fBlowmark\fR] [\fBmailto\fR=\fIaddress\fR] [\fBnoexpire\fR] [\fBnoexpireover\fR] [\fBnoexplog\fR] [\fBnologs\fR] [\fBnomail\fR] [\fBnoprocbatch\fR] [\fBnorenumber\fR] [\fBnorm\fR] [\fBnorotate\fR] [\fBnostat\fR] [\fBnotdaily\fR] [\fBpostexec\fR=\fIprogram\fR] [\fBprocbatchdir\fR=\fIpath\fR] [\fBtmpdir\fR=\fIpath\fR] [\fI/path/to/a/program\fR] .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\fBnews.daily\fR performs a number of important Usenet administrative functions. This includes: .IP "\(bu" 2 producing a status report with \fBinnstat\fR; .IP "\(bu" 2 removing old news articles (with \fBexpirerm\fR if the \fBdelayrm\fR keyword is specified); .IP "\(bu" 2 purging the overview database with \fBexpireover\fR if the corresponding eponym keyword is specified; .IP "\(bu" 2 processing log files and rotating the archived log files with \fBscanlogs\fR; .IP "\(bu" 2 processing \fBinnfeed\fR dropped files with \fBprocbatch\fR; .IP "\(bu" 2 renumbering the \fIactive\fR file with \fBctlinnd\fR; .IP "\(bu" 2 rebuilding the \fIhistory\fR file with \fBexpire\fR; .IP "\(bu" 2 removing any old socket files found in the \fIpathrun\fR directory; .IP "\(bu" 2 collecting all the output and mailing it. .PP Please note that this program should be run under the news administrator's account (usually \f(CW\*(C`news\*(C'\fR), not as root. By default, \fBnews.daily\fR performs all of its functions and mails the output to the news administrator, which is the user specified with \fB\-\-with\-news\-master\fR at configure time (it is \f(CW\*(C`usenet\*(C'\fR by default). You can also change this behaviour with the \fBmailto\fR keyword. .PP By specifying keywords on the command line, it is possible to modify the functions performed, as well as change the arguments given to \fIexpire\fR\|(8) and \&\fIexpireover\fR\|(8). \fBnews.daily\fR should be run once a day, typically out of \fIcron\fR\|(8). Since it will slow the server down while it is running, it should be run during periods of low server usage, such as in the middle of the night. To run it at 3am, for example, add the following entry to the news user's crontab file: .PP .Vb 1 \& 0 3 * * * /news.daily expireover lowmark .Ve .PP If you're using any non-CNFS storage methods, add the \fBdelayrm\fR keyword to the above option list for \fBnews.daily\fR. .PP It may be run more often, but such invocations should at least use the \&\fBnorotate\fR keyword (or perhaps the \fBnotdaily\fR keyword) to prevent the log files from being processed and rotated too fast. The \fIshlock\fR\|(1) program is used to prevent simultaneous executions. .PP The program specified by the given path \fI/path/to/a/program\fR is executed just before any expiration is done. A typical use is to specify an alternate expiration program and use the \fBnoexpire\fR keyword. Multiple programs may be specified; they will be invoked in order. .SH "KEYWORDS" .IX Header "KEYWORDS" The following keywords may be used: .IP "\fBdelayrm\fR" 4 .IX Item "delayrm" This uses the \fB\-z\fR flag when invoking \fBexpire\fR and \fBexpireover\fR. The names of articles to be removed are written to a temporary file, and then renamed after expiration by calling \fBexpirerm\fR which in turn calls \fBfastrm\fR. .IP "\fBexpctl\fR=\fIpath\fR" 4 .IX Item "expctl=path" Specify the file to use as the \fIexpire.ctl\fR file for \fBexpire\fR. .IP "\fBexpdir\fR=\fIpath\fR" 4 .IX Item "expdir=path" By default, \fBexpire\fR builds the new \fIhistory\fR file and database in the same directory as the current files. Using this keyword specifies a different location to build the new files (by passing the \fB\-d\fR flag to \fBexpire\fR), which will then be moved to the right location when finished. .IP "\fBexpireover\fR" 4 .IX Item "expireover" The \fBexpireover\fR program is called after expiration to purge the overview database. If no overview data is created, the \fBexpireover\fR keyword is not needed. This is the case when the server runs only for feeders (no reader). By default, \fBexpireover\fR is not called by \fBnews.daily\fR. .IP "\fBexpireoverflags\fR=\fIargs\fR" 4 .IX Item "expireoverflags=args" If the \fBexpireover\fR keyword is used, this keyword may be used to specify the flags to be passed to \fBexpireover\fR. If the \fBdelayrm\fR keyword is used, then the default value is \fB\-z\fR and the list of deleted files; otherwise, the default value is \fB\-s\fR. .IP "\fBflags\fR=\fIargs\fR" 4 .IX Item "flags=args" By default, \fBexpire\fR is invoked with argument \fB\-v1\fR. Using this keyword changes the arguments to those specified. Be careful to use quotes if multiple arguments are needed. This keyword has no effect if the \fBnoexpire\fR keyword is used. .IP "\fBlowmark\fR" 4 .IX Item "lowmark" If the \fBlowmark\fR keyword is used, \f(CW\*(C`ctlinnd lowmark\*(C'\fR is used for renumbering \fIactive\fR. Normal \f(CW\*(C`ctlinnd renumber\*(C'\fR operation will take long time. With the \fBlowmark\fR keyword, this will take less time. If the \fBlowmark\fR keyword is used, the \fBnorenumber\fR keyword is not needed since \fBnews.daily\fR specifies it implicitly. .Sp If the \fBlowmark\fR keyword is given to \fBnews.daily\fR, then the \&\fBexpireover\fR keyword must also be given; otherwise, that kind of renumbering will not be taken into account. .IP "\fBmailto\fR=\fIaddress\fR" 4 .IX Item "mailto=address" By default, \fBnews.daily\fR mails the report to the newsmaster address specified with \fB\-\-with\-news\-master\fR at configure time. The \fBmailto\fR keyword can specify a different address to which to mail the report. Note that using this keyword has no effect if the \fBnomail\fR keyword is also specified. .IP "\fBnoexpire\fR" 4 .IX Item "noexpire" By default, \fBexpire\fR is invoked to remove old news articles. Using this keyword disables this function. .IP "\fBnoexpireover\fR" 4 .IX Item "noexpireover" By default, \fBexpireover\fR is invoked to remove old overview database if \fIenableoverview\fR is set in \fIinn.conf\fR. Using this keyword disables this function. .IP "\fBnoexplog\fR" 4 .IX Item "noexplog" \&\fBexpire\fR normally appends information to \fIpathlog\fR/expire.log (see \fInewslog\fR\|(5)). Using this keyword causes the \fBexpire\fR output to be handled as part of \&\fBnews.daily\fR's output. It has no effect if the \fBnoexpire\fR keyword is used. .IP "\fBnologs\fR" 4 .IX Item "nologs" After expiration, \fBscanlogs\fR is invoked to process the log files. Using this keyword disables all log processing functions. .IP "\fBnomail\fR" 4 .IX Item "nomail" \&\fBnews.daily\fR normally sends a mail message containing the results to the administrator. Using this keyword causes this message to be sent to stdout and stderr instead. Normally, all utilities invoked by the script have their stdout and stderr redirected into a file. If the file is empty, no message is sent. .IP "\fBnoprocbatch\fR" 4 .IX Item "noprocbatch" This keyword disables the run of \fBprocbatch\fR on \fBinnfeed\fR dropped files generated in its spool directory. These files contain a list of articles which were not correctly sent to peers. By default, they will be processed and removed afterwards. .IP "\fBnorenumber\fR" 4 .IX Item "norenumber" This keyword disables the \f(CW\*(C`ctlinnd renumber\*(C'\fR operation. Normally, the low water marks for all newsgroups are reset in the \fIactive\fR file. .IP "\fBnorm\fR" 4 .IX Item "norm" By default, any \fBctlinnd\fR socket that has not been modified for two days will be removed. Using this keyword disables this function. .IP "\fBnorotate\fR" 4 .IX Item "norotate" By default, log processing includes rotating and cleaning out log files. Using this keyword disables the rotating and cleaning aspect of the log processing: the logs files are only scanned for information and no contents are altered. This keyword has no effect if the \fBnologs\fR keyword is used. The \fBnorotate\fR keyword is passed on to \fBscanlogs\fR if it is invoked. .IP "\fBnostat\fR" 4 .IX Item "nostat" This keyword disables the status report generated by \fBinnstat\fR. Without this keyword, the status report is the first function performed, just prior to obtaining the \fBnews.daily\fR lock. .IP "\fBnotdaily\fR" 4 .IX Item "notdaily" By default, \fBnews.daily\fR expects to be run only once a day, and it does various things (like rotating logs) that normally should only be done on daily basis. Use this keyword any extra times \fBnews.daily\fR is run in the day and the normal log files processing (and rotation) will not be done. This keyword implies \fBnologs\fR. .IP "\fBpostexec\fR=\fIprogram\fR" 4 .IX Item "postexec=program" The program specified by the given path \fIprogram\fR is executed just after all expiration is done. Multiple programs may be specified; they will be invoked in order. .IP "\fBprocbatchdir\fR=\fIpath\fR" 4 .IX Item "procbatchdir=path" \&\fBnews.daily\fR tries to find the backlog directory of \fBinnfeed\fR in \&\fIinnfeed.conf\fR. In case several instances of \fBinnfeed\fR are running or when its configuration file is not the default one, the \fBprocbatchdir\fR keyword can be used to specify the path of the backlog directory. This keyword can be used more than once in the command-line. .IP "\fBtmpdir\fR=\fIpath\fR" 4 .IX Item "tmpdir=path" Overrides the \fIpathtmp\fR setting in \fIinn.conf\fR by setting the environment variable \&\f(CW$TMPDIR\fR to the specified path. Various parts of the expire process, such as sort, will then use this path as the directory for temporary files. .SH "HISTORY" .IX Header "HISTORY" \&\fBnews.daily\fR and this manual page were written by Landon Curt Noll and Rich \f(CW$alz\fR for InterNetNews. It was converted to \s-1POD\s0 by Julien Elie. .PP \&\f(CW$Id:\fR news.daily.pod 9628 2014\-05\-14 17:22:01Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIactive\fR\|(5), \fIctlinnd\fR\|(8), \fIexpire\fR\|(8), \fIexpire.ctl\fR\|(5), \fIexpireover\fR\|(8), \fIexpirerm\fR\|(8), \fIfastrm\fR\|(8), \&\fIinn.conf\fR\|(5), \fIinnstat\fR\|(8), \fInewslog\fR\|(5), \fIprocbatch\fR\|(8), \fIscanlogs\fR\|(8), \fIshlock\fR\|(1). inn-2.6.0/doc/man/list.30000644000175200017520000001440112575023702014277 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "list 3" .TH list 3 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" list \- list routines .SH "SYNOPSIS" .IX Header "SYNOPSIS" .Vb 1 \& #include \& \& struct node { \& struct node *succ; \& struct node *pred; \& }; \& \& struct list { \& struct node *head; \& struct node *tail; \& struct node *tailpred; \& }; \& \& void list_new(struct list *list); \& \& struct node *list_addhead(struct list *list, struct node *node); \& \& struct node *list_addtail(struct list *list, struct node *node); \& \& struct node *list_head(struct list *list); \& \& struct node *list_tail(struct list *list); \& \& struct node *list_succ(struct node *node); \& \& struct node *list_pred(struct node *node); \& \& struct node *list_remhead(struct list *list); \& \& struct node *list_remtail(struct list *list); \& \& struct node *list_remove(struct node *node); \& \& struct node *list_insert(struct list *list, struct node *node, struct node *pred); \& \& bool list_isempty(struct list *list); .Ve .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\fBlist_new\fR initialises the list header \fIlist\fR so as to create an empty list. .PP \&\fBlist_addhead\fR adds \fInode\fR to the head of \fIlist\fR, returning the node just added. .PP \&\fBlist_addtail\fR adds \fInode\fR to the tail of \fIlist\fR, returning the node just added. .PP \&\fBlist_head\fR returns a pointer to the the node at the head of \fIlist\fR or \fB\s-1NULL\s0\fR if the list is empty. .PP \&\fBlist_tail\fR returns a pointer to the the node at the tail of \fIlist\fR or \fB\s-1NULL\s0\fR if the list is empty. .PP \&\fBlist_succ\fR returns the next (successor) node on the list after \&\fInode\fR or \fB\s-1NULL\s0\fR if \fInode\fR was the final node. .PP \&\fBlist_pred\fR returns the previous (predecessor) node on the list before \&\fInode\fR or \fB\s-1NULL\s0\fR if \fInode\fR was the first node. .PP \&\fBlist_remhead\fR removes the first node from \fIlist\fR and returns it to the caller. If the list is empty \fB\s-1NULL\s0\fR is returned. .PP \&\fBlist_remtail\fR removes the last node from \fIlist\fR and returns it to the caller. If the list is empty \fB\s-1NULL\s0\fR is returned. .PP \&\fBlist_remove\fR removes \fInode\fR from the list it is on and returns it to the caller. .PP \&\fBlist_insert\fR inserts \fInode\fR onto \fIlist\fR after the node \fIpred\fR. If \&\fIpred\fR is \fB\s-1NULL\s0\fR then \fInode\fR is added to the head of \fIlist\fR. .SH "HISTORY" .IX Header "HISTORY" Written by Alex Kiernan for InterNetNews\ 2.4.0. .PP \&\f(CW$Id:\fR list.pod 9073 2010\-05\-31 19:00:23Z iulius $ inn-2.6.0/doc/man/INN__Config.3pm0000644000175200017520000001516412575023702015740 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "INN::Config 3pm" .TH INN::Config 3pm "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" INN::Config \- Export all the variables an INN Perl script might need .SH "DESCRIPTION" .IX Header "DESCRIPTION" This Perl module sets up any and all the variables that an \s-1INN\s0 Perl script might need. More particularly, it allows to use \fIinn.conf\fR variables: they are all provided by \fBinnconfval\fR, as well as the version of \s-1INN\s0 (in the variable \f(CW$INN::Config::VERSION\fR for its short number form, on in \&\f(CW$INN::Config::version\fR for its complete form). Other useful variables are also provided (directories, files, programs, masks, parameters). The complete list can be obtained with the following script that prints them out: .PP .Vb 3 \& use lib \*(Aq/lib/perl\*(Aq; \& use INN::Config; \& use Data::Dumper; \& \& my ($varname, $value); \& foreach my $var (@INN::Config::EXPORT_OK) { \& if ($var =~ /^\e$(.*)$/) { \& $varname = "INN::Config::$1"; \& $value = Dumper($$varname); \& $value =~ s/^\e$VAR1 = //; \& print "\e$$varname = $value"; \& } elsif ($var =~ /^\e@(.*)$/) { \& $varname = "INN::Config::$1"; \& $value = Dumper(\e@$varname); \& $value =~ s/^\e$VAR1 = //; \& print "\e@$varname = $value"; \& } \& } .Ve .PP A local Perl script named \fIinnshellvars.pl.local\fR in \fIpathetc\fR will be loaded, if present and executable, at the end of the run of this module. A typical use is to add or override variables. .PP You only have to declare the module at the beginning of them: .PP .Vb 2 \& use lib \*(Aq/lib/perl\*(Aq; \& use INN::Config; .Ve .PP Then, you can for instance use: .PP .Vb 1 \& print $INN::Config::localmaxartsize; .Ve .PP to print the value of \fIlocalmaxartsize\fR as it is set in \fIinn.conf\fR. .PP You can also specify a version when you import the module. If you write: .PP .Vb 1 \& use INN::Config 2.5.0; .Ve .PP only versions of \s-1INN\s0 superior to 2.5.0 will be able to run the Perl script. .PP It is also possible to import the variables directly in your namespace if you specify what you want to import: .PP .Vb 1 \& use INN::Config qw($localmaxartsize $pathbin); .Ve .PP Note that a legacy \fIinnshellvars.pl\fR is also provided in \fIpathnews\fR/lib for compatibility reasons with old Perl scripts not shipped with \s-1INN\s0. It was used by versions of \s-1INN\s0 anterior to 2.5.0. The corresponding scripts for Shell and Tcl are, however, still in use: \fIinnshellvars\fR and \fIinnshellvars.tcl\fR. They offer the same capabilities as this module. .SH "HISTORY" .IX Header "HISTORY" \&\fIinnshellvars.pl\fR was written by James Brister for InterNetNews in 1996. It was converted to the INN::Config Perl module by Julien Elie in 2007. .PP \&\f(CW$Id:\fR Config.pm.in 9567 2013\-11\-17 20:24:35Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIinn.conf\fR\|(5), \fIinnconfval\fR\|(1), \fIperl\fR\|(1). inn-2.6.0/doc/man/makehistory.80000644000175200017520000003043412575023702015674 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "MAKEHISTORY 8" .TH MAKEHISTORY 8 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" makehistory \- Initialize or rebuild INN history database .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fBmakehistory\fR [\fB\-abFIOSx\fR] [\fB\-f\fR \fIfilename\fR] [\fB\-l\fR \fIcount\fR] [\fB\-L\fR \fIload-average\fR] [\fB\-s\fR \fIsize\fR] [\fB\-T\fR \fItmpdir\fR] .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\fBmakehistory\fR rebuilds the \fIhistory\fR\|(5) text file, which contains a list of message-IDs of articles already seen by the server. It can also be used to rebuild the overview database. Note that even though the \fIdbz\fR\|(3) indices for the \fIhistory\fR file are also rebuilt by \fBmakehistory\fR, it is useful to run \fImakedbz\fR\|(8) after \fImakehistory\fR\|(8) in order to improve the efficiency of the indices (\fBmakehistory\fR does not know how large to make the hash table at first run, unless the size is given by the \fB\-s\fR flag). .PP The default location of the \fIhistory\fR text file is \fIpathdb\fR/history; to specify an alternate location, use the \fB\-f\fR flag. .PP By default, \fBmakehistory\fR will scan the entire spool, using the storage manager, and write a history line for every article. To also generate overview information, use the \fB\-O\fR flag. .PP \&\s-1WARNING:\s0 If you're trying to rebuild the overview database, be sure to stop \fIinnd\fR\|(8) and delete or zero out the existing database before you start for the best results. An overview rebuild should not be done while the server is running. Unless the existing overview is deleted, you may end up with problems like out-of-order overview entries, excessively large overview buffers, and the like. .PP If \fIovmethod\fR in \fIinn.conf\fR is \f(CW\*(C`ovdb\*(C'\fR, you must have the ovdb processes running while rebuilding overview. ovdb needs them available while writing overview entries. You can start them by hand separate from the rest of the server by running \fBovdb_init\fR; see \fIovdb_init\fR\|(8) for more details. .SH "OPTIONS" .IX Header "OPTIONS" .IP "\fB\-a\fR" 4 .IX Item "-a" Append to the \fIhistory\fR file rather than generating a new one. If you append to the main \fIhistory\fR file, make sure \fIinnd\fR\|(8) is throttled or not running, or you can corrupt the history. .IP "\fB\-b\fR" 4 .IX Item "-b" Delete any messages found in the spool that do not have valid Message-ID: headers in them. .IP "\fB\-F\fR" 4 .IX Item "-F" Fork a separate process to flush overview data to disk rather than doing it directly. The advantage of this is that it allows \fBmakehistory\fR to continue to collect more data from the spool while the first batch of data is being written to the overview database. The disadvantage is that up to twice as much temporary disk space will be used for the generated overview data. This option only makes sense in combination with \fB\-O\fR. With buffindexed, the \fBoverchan\fR program is invoked to write overview. .IP "\fB\-f\fR \fIfilename\fR" 4 .IX Item "-f filename" Rather than writing directly to \fIpathdb\fR/history, instead write to \&\fIfilename\fR, also in \fIpathdb\fR. .IP "\fB\-I\fR" 4 .IX Item "-I" Don't store overview data for articles numbered lower than the lowest article number in \fIactive\fR. This is useful if there are for whatever reason old articles on disk that shouldn't be available to readers or put into the overview database. .IP "\fB\-l\fR \fIcount\fR" 4 .IX Item "-l count" This option specifies how many articles to process before writing the accumulated overview information out to the overview database. The default is \f(CW10000\fR. Since overview write performance is faster with sorted data, each \*(L"batch\*(R" gets sorted. Increasing the batch size with this option may further improve write performance, at the cost of longer sort times. Also, temporary space will be needed to store the overview batches. At a rough estimate, about 300 * \fIcount\fR bytes of temporary space will be required (not counting temp files created by \fIsort\fR\|(1)). See the description of the \fB\-T\fR option for how to specify the temporary storage location. This option has no effect with buffindexed, because buffindexed does not need sorted overview and no batching is done. .IP "\fB\-L\fR \fIload-average\fR" 4 .IX Item "-L load-average" Temporarily pause activities if the system load average exceeds the specified level \fIload-average\fR. This allows \fBmakehistory\fR to run on a system being used for other purposes without monopolizing system resources and thus making the response time for other applications unacceptably slow. Using \fInice\fR\|(1) does not help much for that because the problem comes from disk I/O usage, and \fIionice\fR\|(1) is not always available or efficient. .IP "\fB\-O\fR" 4 .IX Item "-O" Create the overview database as well as the \fIhistory\fR file. Overview information is only required if the server supports readers; it is not needed for a transit-only server (see \fIenableoverview\fR in \fIinn.conf\fR\|(5)). If you are using the buffindexed overview storage method, erase all of your overview buffers before running \fBmakehistory\fR with \fB\-O\fR. .IP "\fB\-S\fR" 4 .IX Item "-S" Rather than storing the overview data into the overview database, just write it to standard output in a form suitable for feeding to \fBoverchan\fR later if wished. When this option is used, \fB\-F\fR, \fB\-I\fR, \fB\-l\fR, and \fB\-T\fR are ignored. This option only makes sense in combination with \fB\-O\fR. .IP "\fB\-s\fR \fIsize\fR" 4 .IX Item "-s size" Size the history database for approximately \fIsize\fR pairs. Accurately specifying the size is an optimization that will create a more efficient database. (The size should be the estimated eventual size of the \fIhistory\fR file, typically the size of the old file, in lines.) .IP "\fB\-T\fR \fItmpdir\fR" 4 .IX Item "-T tmpdir" If \fB\-O\fR is given, \fBmakehistory\fR needs a location to write temporary overview data. By default, it uses \fIpathtmp\fR, set in \fIinn.conf\fR, but if this option is given, the provided \fItmpdir\fR is used instead. This is also used for temporary files created by \fIsort\fR\|(1) (which is invoked in the process of writing overview information since sorted overview information writes faster). By default, \fBsort\fR usually uses your system temporary directory; see the \fIsort\fR\|(1) man page on your system to be sure. .IP "\fB\-x\fR" 4 .IX Item "-x" If this option is given, \fBmakehistory\fR won't write out \fIhistory\fR file entries. This is useful mostly for building overview without generating a new \fIhistory\fR file. .SH "EXAMPLES" .IX Header "EXAMPLES" Here's a typical example of rebuilding the entire history and overview database, removing broken articles in the news spool. This uses the default temporary file locations and should be done while \fBinnd\fR isn't running (or is throttled). .PP .Vb 1 \& makehistory \-b \-f history.n \-O \-l 30000 \-I .Ve .PP This will rebuild the overview (if using buffindexed, erase the existing overview buffers before running this command) and leave a new \&\fIhistory\fR file as \f(CW\*(C`history.n\*(C'\fR in \fIpathdb\fR. To preserve all of the history entries from the old \fIhistory\fR file that correspond to rejected articles or expired articles, follow the above command with: .PP .Vb 2 \& cd \& awk \*(AqNF == 2 { print }\*(Aq < history >> history.n .Ve .PP (replacing the path with your \fIpathdb\fR, if it isn't the default). Then look over the new \fIhistory\fR file for problems and run: .PP .Vb 1 \& makedbz \-s \`wc \-l < history.n\` \-f history.n .Ve .PP Then rename all of the files matching \f(CW\*(C`history.n.*\*(C'\fR to \f(CW\*(C`history.*\*(C'\fR, replacing the current history database and indices. After that, it's safe to unthrottle \fBinnd\fR. .PP For a simpler example: .PP .Vb 1 \& makehistory \-b \-f history.n \-I \-O .Ve .PP will scan the spool, removing broken articles and generating history and overview entries for articles missing from history. .PP To just rebuild overview: .PP .Vb 1 \& makehistory \-O \-x \-F .Ve .SH "FILES" .IX Header "FILES" .IP "\fIpathdb\fR/history" 4 .IX Item "pathdb/history" This is the default output file for \fBmakehistory\fR. .IP "\fIpathtmp\fR" 4 .IX Item "pathtmp" Where temporary files are written unless \fB\-T\fR is given. .SH "HISTORY" .IX Header "HISTORY" Originally written by Rich \f(CW$alz\fR for InterNetNews and updated by various other people since. .PP \&\f(CW$Id:\fR makehistory.pod 8534 2009\-06\-23 18:08:14Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIactive\fR\|(5), \fIctlinnd\fR\|(8), \fIdbz\fR\|(3), \fIhistory\fR\|(5), \fIinn.conf\fR\|(5), \fIinnd\fR\|(8), \&\fImakedbz\fR\|(8), \fIovdb_init\fR\|(8), \fIoverchan\fR\|(8). inn-2.6.0/doc/man/nntpsend.80000644000175200017520000002202112575023702015157 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "NNTPSEND 8" .TH NNTPSEND 8 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" nntpsend \- Send Usenet articles to remote sites .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fBnntpsend\fR [\fB\-acDdlNnpr\fR] [\fB\-P\fR \fIportnum\fR] [\fB\-s\fR \fIsize\fR] [\fB\-T\fR \fItimelimit\fR] [\fB\-t\fR \fItimeout\fR] [\fB\-w\fR \fIdelay\fR] [\fIsitename\fR \fIfqdn\fR] ... .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\fBnntpsend\fR is a front-end that invokes \fBinnxmit\fR to send Usenet articles to a remote \s-1NNTP\s0 site. The sites to be fed may be specified by giving \&\fIsitename\fR \fIfqdn\fR pairs on the command line. If no such pairs are given, \&\fBnntpsend\fR defaults to the information given in the \fInntpsend.ctl\fR config file. The \fIsitename\fR should be the name of the site as specified in the \fInewsfeeds\fR file. The \fIfqdn\fR should be the hostname or \s-1IP\s0 address of the remote site. An \fBinnxmit\fR is launched for sites with queued news. All \fBinnxmit\fR processes are spawned in the background and the script waits for them all to finish before returning. Output is sent to the file \&\fInntpsend.log\fR in \fIpathlog\fR. In order to keep from overwhelming the local system, \fBnntpsend\fR waits five seconds before spawning each child. .PP \&\fBnntpsend\fR expects that the batch file for a site is named \fIsitename\fR in \fIpathoutgoing\fR. To prevent batch files corruption, \fBshlock\fR is used to \&\*(L"lock\*(R" these files. When \fIsitename\fR \fIfqdn\fR pairs are given on the command line, any flags given on the command completely describe how \fBinnxmit\fR and \fBshrinkfile\fR operate. When no such pairs are given on the command line, then the information found in \fInntpsend.ctl\fR becomes the default flags for that site. Any flags given on the command line override the default flags for the site. .PP An alternative to \fBnntpsend\fR can be \fBinnduct\fR, mentioned in the \&\fIinnfeed\fR\|(8) man page. .SH "OPTIONS" .IX Header "OPTIONS" .IP "\fB\-D\fR, \fB\-d\fR" 2 .IX Item "-D, -d" The \fB\-D\fR flag causes \fBnntpsend\fR to send output to stdout rather than the log file \fInntpsend.log\fR in \fIpathlog\fR. The \fB\-d\fR flag does the same and it passes \fB\-d\fR to all \fBinnxmit\fR invocations, which in turn causes \&\fBinnxmit\fR to go into debug mode. .IP "\fB\-n\fR" 2 .IX Item "-n" If the \fB\-n\fR flag is used, then \fBnntpsend\fR does not use \fBshlock\fR to lock the use of \fBnntpsend\fR. Batch files will still be locked. .IP "\fB\-s\fR \fIsize\fR" 2 .IX Item "-s size" If the \fB\-s\fR flag is used, then \fBshrinkfile\fR will be invoked to perform a head truncation of \fIsize\fR bytes on the batch file and the flag will be passed to it. .IP "\fB\-w\fR \fIdelay\fR" 2 .IX Item "-w delay" If the \fB\-w\fR flag is used, then \fBnntpsend\fR waits for \fIdelay\fR seconds after flushing the site before launching \fBinnxmit\fR. .IP "\fB\-a\fR, \fB\-c\fR, \fB\-l\fR, \fB\-N\fR, \fB\-P\fR \fIportnum\fR, \fB\-p\fR, \fB\-r\fR, \fB\-T\fR \fItimelimit\fR, \fB\-t\fR \fItimeout\fR" 2 .IX Item "-a, -c, -l, -N, -P portnum, -p, -r, -T timelimit, -t timeout" The \fB\-a\fR, \fB\-c\fR, \fB\-l\fR, \fB\-P\fR \fIportnum\fR, \fB\-p\fR, \fB\-r\fR, \fB\-T\fR \fItimelimit\fR and \fB\-t\fR \fItimeout\fR flags are passed on to the child \fBinnxmit\fR program. The \fB\-N\fR flag is passed as \fB\-s\fR flag to the child \fBinnxmit\fR program. See \fIinnxmit\fR\|(8) for more details. .Sp Note that if the \fB\-p\fR flag is used, then no connection is made and no articles are fed to the remote site. It is useful to have \fIcron\fR\|(8) invoke \&\fBnntpsend\fR with this flag in case a site cannot be reached for an extended period of time. .SH "EXAMPLES" .IX Header "EXAMPLES" With the following \fInntpsend.ctl\fR config file: .PP .Vb 4 \& nsavax:erehwon.nsavax.gov::\-t60 \& group70:group70.org:: \& walldrug:walldrug.com:4m\-1m:\-T1800 \-t300 \& kremvax:kremvax.cis:2m: .Ve .PP the command \f(CW\*(C`nntpsend\*(C'\fR will result in the following: .PP .Vb 5 \& Sitename Truncation innxmit flags \& nsavax (none) \-a \-t60 \& group70 (none) \-a \-t180 \& walldrug 1m if >4m \-T1800 \-t300 \& kremvax 2m \-t180 .Ve .PP The command \f(CW\*(C`nntpsend \-d \-T1200\*(C'\fR will result in the following: .PP .Vb 5 \& Sitename Truncation innxmit flags \& nsavax (none) \-a \-d \-T1200 \-t60 \& group70 (none) \-a \-d \-T1200 \-t180 \& walldrug 1m if >4m \-d \-T1200 \-t300 \& kremvax 2m \-d \-T1200 \-t180 .Ve .PP The command \f(CW\*(C`nntpsend \-s 5m \-T1200 nsavax erehwon.nsavax.gov group70 group70.org\*(C'\fR will result in the following: .PP .Vb 3 \& Sitename Truncation innxmit flags \& nsavax 5m \-T1200 \-t180 \& group70 5m \-T1200 \-t180 .Ve .PP Remember that \fB\-a\fR is always given when there is no size limit, and \fB\-t\fR defaults to \f(CW180\fR. .SH "HISTORY" .IX Header "HISTORY" Written by Landon Curt Noll and Rich \f(CW$alz\fR for InterNetNews. Converted to \s-1POD\s0 by Julien Elie. .PP \&\f(CW$Id:\fR nntpsend.pod 9588 2013\-12\-19 17:46:41Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIinn.conf\fR\|(5), \fIinnxmit\fR\|(1), \fInewsfeeds\fR\|(5), \fInntpsend.ctl\fR\|(5), \fIshlock\fR\|(1), \&\fIshrinkfile\fR\|(1). inn-2.6.0/doc/man/nntpsend.ctl.50000644000175200017520000001272712575023702015751 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "NNTPSEND.CTL 5" .TH NNTPSEND.CTL 5 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" nntpsend.ctl \- List of sites to feed via nntpsend .SH "DESCRIPTION" .IX Header "DESCRIPTION" The file \fIpathetc\fR/nntpsend.ctl specifies the default list of sites to be fed by \fBnntpsend\fR. Comments begin with a number sign (\f(CW\*(C`#\*(C'\fR) and continue through the end of the line. Blank lines and comments are ignored. All other lines should consist of four fields separated by a colon. .PP The first field is the name of the site as specified in the \fInewsfeeds\fR file. .PP The second field should be the hostname or \s-1IP\s0 address of the remote site. .PP The third field, if non-empty, specifies the default head truncation size of the batch file. If this field is empty, no truncation is performed. This field may be of the form \f(CW\*(C`\f(CImaxsize\f(CW\-\f(CItruncsize\f(CW\*(C'\fR, in which case it is passed to \fBshrinkfile\fR as \f(CW\*(C`\-m \f(CImaxsize\f(CW \-s \f(CItruncsize\f(CW\*(C'\fR; otherwise it is of the form \f(CW\*(C`\f(CItruncsize\f(CW\*(C'\fR, in which case it is passed as \f(CW\*(C`\-s \f(CItruncsize\f(CW\*(C'\fR. .PP The fourth field specifies some default flags passed to \fBinnxmit\fR. Note that the flag \fB\-a\fR is always given to \fBinnxmit\fR and need not appear here. If no \fB\-t\fR flag is given in this field or on the \fBnntpsend\fR command line, \&\f(CW\*(C`\-t 180\*(C'\fR will be given to \fBinnxmit\fR. .PP See \fInntpsend\fR\|(8) for an example of \fInntpsend.ctl\fR config file. .SH "HISTORY" .IX Header "HISTORY" Written by Landon Curt Noll for InterNetNews. Converted to \&\s-1POD\s0 by Julien Elie. .PP \&\f(CW$Id:\fR nntpsend.ctl.pod 9071 2010\-05\-31 17:41:32Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIinnxmit\fR\|(1), \fInewsfeeds\fR\|(5), \fInntpsend\fR\|(8), \fIshrinkfile\fR\|(1). inn-2.6.0/doc/man/perl-nocem.80000644000175200017520000002124012575023702015371 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "PERL-NOCEM 8" .TH PERL-NOCEM 8 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" perl\-nocem \- A NoCeM\-on\-spool implementation for INN\ 2.x .SH "SYNOPSIS" .IX Header "SYNOPSIS" perl-nocem .SH "DESCRIPTION" .IX Header "DESCRIPTION" NoCeM, which is pronounced \fINo See 'Em\fR, is a protocol enabling authenticated third-parties to issue notices which can be used to cancel unwanted articles (like spam and articles in moderated newsgroups which were not approved by their moderators). It can also be used by readers as a \fIthird-party killfile\fR. It is intended to eventually replace the protocol for third-party cancel messages. .PP \&\fBperl-nocem\fR processes third-party, PGP-signed article cancellation notices. It is possible not to honour all NoCeM notices but only those which are sent by people whom you trust (that is to say if you trust the \s-1PGP\s0 key they use to sign their NoCeM notices). Indeed, it is up to you to decide whether you wish to honour their notices, depending on the criteria they use. .PP Processing NoCeM notices is easy to set up: .IP "1." 4 Import the keys of the NoCeM issuers you trust in order to check the authenticity of their notices. You can do: .Sp .Vb 2 \& gpg \-\-no\-default\-keyring \-\-primary\-keyring /pgp/ncmring.gpg \-\-import \& chmod 644 /pgp/ncmring.gpg .Ve .Sp where is the value of the \fIpathetc\fR parameter set in \&\fIinn.conf\fR and the file containing the key(s) to import. The keyring must be located in /pgp/ncmring.gpg; you only have to create the directory /pgp before using \fBgpg\fR (it will automatically generate the \fIncmring.gpg\fR file) and make sure the news user can read this file, once generated. .Sp For old PGP-generated keys, you may have to use \&\fB\-\-allow\-non\-selfsigned\-uid\fR if they are not properly self-signed, but anyone creating a key really should self-sign the key. Current \s-1PGP\s0 implementations do this automatically. .Sp The keys of NoCeM issuers can be found in the web site of \fIThe NoCeM Registry\fR: . You can even download there a unique file which contains all the keys. .IP "2." 4 Create a \fInocem.ctl\fR config file in \fIpathetc\fR indicating the NoCeM issuers and notices you want to follow. This permission file contains lines like: .Sp .Vb 2 \& bleachbot@httrack.com:spam,site \& pgpmoose@killfile.org:pgpmoose\-forged\-moderation .Ve .Sp This will remove all articles for which the issuer (first part of the line, before the colon \f(CW\*(C`:\*(C'\fR) has issued NoCeM notices corresponding to the criteria specified after the colon. .Sp You will also find information about that on the web site of \&\fIThe NoCeM Registry\fR. .IP "3." 4 Add to the \fInewsfeeds\fR file an entry like this one in order to feed \&\fBperl-nocem\fR the NoCeM notices posted to alt.nocem.misc and news.lists.filters: .Sp .Vb 3 \& nocem!\e \& :!*,alt.nocem.misc,news.lists.filters\e \& :Tc,Wf,Ap:/perl\-nocem .Ve .Sp with the correct path to \fBperl-nocem\fR, located in . Then, reload the \fInewsfeeds\fR file (\f(CW\*(C`ctlinnd reload newsfeeds \*(AqNoCeM channel feed\*(Aq\*(C'\fR). .Sp Note that you should at least carry news.lists.filters on your news server (or other newsgroups where NoCeM notices are sent) if you wish to process them. .IP "4." 4 Everything should now work. However, do not hesitate to manually test \&\fBperl-nocem\fR with a NoCeM notice, using: .Sp .Vb 1 \& grephistory \*(Aq\*(Aq | perl\-nocem .Ve .Sp Indeed, \fBperl-nocem\fR expects tokens on its standard input, and \&\fBgrephistory\fR can easily give it the token of a known article, thanks to its Message-ID. .PP When you have verified that everything works, you can eventually turn off regular spam cancels, if you want, not processing any longer cancels containing \f(CW\*(C`cyberspam\*(C'\fR in the Path: header (see the \&\fIrefusecybercancels\fR parameter in \fIinn.conf\fR). .SH "FILES" .IX Header "FILES" .IP "\fIpathbin\fR/perl\-nocem" 4 .IX Item "pathbin/perl-nocem" The Perl script itself used to process NoCeM notices. .IP "\fIpathetc\fR/nocem.ctl" 4 .IX Item "pathetc/nocem.ctl" The configuration file which specifies the NoCeM notices to be processed. .IP "\fIpathetc\fR/pgp/ncmring.gpg" 4 .IX Item "pathetc/pgp/ncmring.gpg" The keyring which contains the public keys of trusted NoCeM issuers. .SH "BUGS" .IX Header "BUGS" The Subject: header is not checked for the @@NCM string and there is no check for the presence of the References: header. .PP The Newsgroups: pseudo header is not checked, but this can be done in \&\fIlocal_want_cancel_id()\fR. .PP The Hierarchies: header is ignored. .SH "HISTORY" .IX Header "HISTORY" Copyright 2000 by Miquel van Smoorenburg . .PP Copyright 2001 by Marco d'Itri . .PP \&\f(CW$Id:\fR perl\-nocem.in 9639 2014\-05\-17 06:24:44Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIgpgv\fR\|(1), \fIgrephistory\fR\|(1), \fIinn.conf\fR\|(5), \fInewsfeeds\fR\|(5), \fIpgp\fR\|(1). inn-2.6.0/doc/man/nnrpd.track.50000644000175200017520000000214612575023702015555 0ustar iuliusiulius.\" $Revision: 5909 $ .TH NNRPD.TRACK 5 .SH NAME nnrpd.track \- file to specify hosts to be tracked by nnrpd. .SH DESCRIPTION This file, which is located in .I , specifies which hosts are to have their activities recorded during an .I nnrpd session. The .I nnrpd server reads it when first spawned by .IR innd , provided .I readertrack in .I inn.conf is true; otherwise this file is not used. .PP Entries consist of one host specification per line, each line having two fields, separated by a colon: .RS .nf host:identity .fi .RE .PP The first field is either the FQDN of a host, or a domain name (in the form *.domain.com). .PP The second field is simply a segment of text which may be used to more easily identify the client, typically an e-mail address or other identifying mark. .PP For example: .RS .nf nasty.foo.com:nasty@foo.com *.bar.com:VeryNastyClient .fi .RE .PP Written by Steve Carrie for InterNetNews. .de R$ This is revision \\$3, dated \\$4. .. .R$ $Id: nnrpd.track.5 5909 2002-12-03 05:17:18Z vinocur $ .SH "SEE ALSO" inn.conf(5), innd(8), newsfeeds(5), nnrpd(8), inn-2.6.0/doc/man/newsfeeds.50000644000175200017520000012326212575023702015317 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "NEWSFEEDS 5" .TH NEWSFEEDS 5 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" newsfeeds \- Determine where Usenet articles are sent .SH "DESCRIPTION" .IX Header "DESCRIPTION" The file \fIpathetc\fR/newsfeeds specifies how incoming articles should be distributed to other programs and files on the server. It is parsed by the InterNetNews server \fIinnd\fR\|(8) when it starts up, or when directed to by \&\fIctlinnd\fR\|(8). \fBinnd\fR doesn't send articles to remote sites itself, so \&\fInewsfeeds\fR doesn't directly determine which remote news servers articles are sent to. Instead, it specifies what batch files should be created or which programs should be run (and what information should be sent to them), and then this information is used by programs like \fIinnxmit\fR\|(8) and \&\fIinnfeed\fR\|(8) to feed articles to remote sites. .PP The \fInewsfeeds\fR file isn't used solely to set up feeding accepted articles to remote sites but also to pass them (or bits of information about them) to any local programs or files that want that data. For example, \fIcontrolchan\fR\|(8), a daemon that processes incoming control messages, runs out of \fInewsfeeds\fR, as could a news to mail gateway. .PP The file is interpreted as a set of lines, parsed according to the following rules: If a line ends with a backslash, the backslash, the newline, and any whitespace at the start of the next line is deleted. This is repeated until the entire \*(L"logical\*(R" line is collected. If the logical line is blank or starts with a number sign (\f(CW\*(C`#\*(C'\fR), it is ignored. .PP All other lines are interpreted as feed entries. An entry should consist of four colon-separated fields; two of the fields may have optional sub-fields, marked off by a slash. Fields or sub-fields that take multiple parameters should be separated by a comma. Extra whitespace can cause problems and should be avoided. Except for the site names, case is significant. The format of an entry is: .PP .Vb 4 \& sitename[/exclude,exclude,...]\e \& :pattern,pattern...[/distribution,distribution...]\e \& :flag,flag...\e \& :parameter .Ve .PP Each field is described below. .PP The \fIsitename\fR is the name of the site to which a news article can be sent. It is used for writing log entries and for determining if an article should be forwarded to a site. (A \*(L"site\*(R" is the generic term for some destination of newsfeed data; it often corresponds to a remote news peer, but doesn't have to. For example, a local archiving program run from \fInewsfeeds\fR is also a \*(L"site\*(R".) If \fIsitename\fR already appears in the article's Path: header, then the article will not be sent to the site. The name is usually whatever the remote site uses to identify itself in the Path: header, but can be almost any word. .PP Be careful, though, to avoid having the \fIsitename\fR accidentally match a Path: header entry unintentionally. For this reason, special local entries (such as archivers or gateways) should probably end with an exclamation point to make sure that they do not have the same name as any real site. For example, \f(CW\*(C`gateway\*(C'\fR is an obvious name for the local entry that forwards articles out to a mailing list. If a site with the name \&\f(CW\*(C`gateway\*(C'\fR posts an article, when the local site receives the article it will see the name in the Path and not send the article to its own \&\f(CW\*(C`gateway\*(C'\fR entry. Since \f(CW\*(C`gateway!\*(C'\fR can't appear as an individual Path: entry since \f(CW\*(C`!\*(C'\fR is a delimiter in the Path: header, that would be a better thing to use for \fIsitename\fR. .PP (Another way to avoid this problem is with the \f(CW\*(C`Ap\*(C'\fR flag; see the description below.) .PP If an entry has an exclusion sub-field, the article will not be sent to that site if any of \fIexclude\fR appear in the Path: header. (It's sometimes convenient to have the \fIsitename\fR be an abbreviated form of the name of the remote site, since all the \fIsitename\fRs to which an article is sent are written to the log and using shorter \fIsitename\fRs can therefore improve performance for large servers. In this case, the Path: header entries of those sites should be given as \fIexclude\fR entries and the \f(CW\*(C`Ap\*(C'\fR flag used so that the abbreviated \fIsitename\fR doesn't accidentally match some other Path: header entry.) .PP The same \fIsitename\fR can be used more than once and the appropriate action will be taken for each entry that should receive the article, but this is recommended only for program feeds to avoid confusion. Case is not significant in site names. .PP The comma-separated \fIpattern\fR specifies which groups to send to the site; it is interpreted to build a \*(L"subscription list\*(R" for the site. The default subscription is to get all groups carried by the server. It is a \&\fIuwildmat\fR\|(3) pattern supporting poison (\f(CW\*(C`@\*(C'\fR) wildcards; see the \fIuwildmat\fR\|(3) man page for full details on the pattern matching language. \fIpattern\fR will be matched against every newsgroup carried by the server and all newsgroups that match will be added to the subscription list for the site. .PP Normally, a given article (or information about it) is sent to a site if any of the newsgroups to which the article was posted are in that site's subscription list. If a newsgroup matches a \f(CW\*(C`@\*(C'\fR pattern in \fIpattern\fR, then not only is it not added to the subscription list, but any articles crossposted to that newsgroup also will not be sent to that site even if other newsgroups to which it was crossposted are in that site's subscription list. This is called a poison pattern (because matching groups are \*(L"poisoned\*(R"). .PP For example, to receive all comp.* groups, but only comp.sources.unix within the sources newsgroups, the following \fIpattern\fR can be used: .PP .Vb 1 \& comp.*,!comp.sources.*,comp.sources.unix .Ve .PP Note that the trailing \f(CW\*(C`.*\*(C'\fR is required; the pattern has to match the whole newsgroup name. \f(CW\*(C`comp.sources.*\*(C'\fR could be written \f(CW\*(C`comp.sources*\*(C'\fR and would exclude the newsgroup comp.sources (if it exists) as well as the groups in the comp.sources.* hierarchy, but note that this would also exclude a newsgroup named comp.sources\-only (whereas the above pattern would add that group to the site subscription list since it matches \&\f(CW\*(C`comp.*\*(C'\fR and none of the other patterns). .PP For another example, to feed alt.* and misc.* to a given site but not any articles posted to alt.binaries.warez (even if they're also crossposted to other alt.* or misc.* groups), the following pattern can be used: .PP .Vb 1 \& alt.*,@alt.binaries.warez,misc.* .Ve .PP Note, however, that if you reversed the \f(CW\*(C`alt.*\*(C'\fR and \f(CW\*(C`@alt.binaries.warez\*(C'\fR entries, this pattern would be equivalent to \f(CW\*(C`alt.*,misc.*\*(C'\fR, since the last matching pattern determines whether a given newsgroup matches and the poison logic only applies if the poison entry is the last matching entry. .PP Control messages follow slightly different propagation rules than normal articles; see \fIinnd\fR\|(8) for the details. Note that most subscriptions should have \f(CW\*(C`!junk,!control,!control.*\*(C'\fR in their pattern list due to those propagation rules (and since \f(CW\*(C`junk\*(C'\fR is a special internal newsgroup; see \&\fIwanttrash\fR in \fIinn.conf\fR\|(5) for more details on what it's used for) and that the best way to keep control messages local to a site is with a distribution. .PP A subscription can be further modified by specifying distributions that the site should or should not receive. The default is to send all articles to all sites that subscribe to any of the groups where it has been posted, but if an article has a Distribution: header and any \&\fIdistribution\fRs are specified, then they are checked according to the following rules: .IP "1." 4 If the Distribution: header matches any of the values in the sub-field, the article is sent. .IP "2." 4 If a \fIdistribution\fR starts with an exclamation point, and it matches the Distribution: header, the article is not sent. .IP "3." 4 If the Distribution: header does not match any \fIdistribution\fR in the site's entry and no negations were used, the article is not sent. .IP "4." 4 If the Distribution: header does not match any \fIdistribution\fR in the site's entry and any \fIdistribution\fR started with an exclamation point, the article is sent. .PP If an article has more than one distribution specified, then each one is handled according according to the above rules. If any of the specified distributions indicate that the article should be sent, it is; if none do, it is not sent. In other words, the rules are used as a logical or. .PP It is almost definitely a mistake to have a single feed that specifies distributions that start with an exclamation point along with some that don't. .PP Distributions are text words, not patterns; entries like \f(CW\*(C`*\*(C'\fR or \f(CW\*(C`all\*(C'\fR have no special meaning. .PP The \fIflag\fR field is described in \*(L"\s-1FLAG\s0 \s-1VALUES\s0\*(R". The interpretation of the \fIparameter\fR field depends on the type of feed and is explained in more detail in \*(L"\s-1FEED\s0 \s-1TYPES\s0\*(R". It can be omitted for some types of feeds. .PP The site named \f(CW\*(C`ME\*(C'\fR is special. There must be exactly one such entry, and it should be the first entry in the file. If the \f(CW\*(C`ME\*(C'\fR entry has an exclusion sub-field, incoming articles are rejected completely if any of the names specified in that exclusion sub-field appear in their Path: headers. If the \f(CW\*(C`ME\*(C'\fR entry has a subscription list, that list is prepended to the subscription list of all other entries. For example, \&\f(CW\*(C`*,!control,!control.*,!junk,!foo.*\*(C'\fR could be used to set the default subscription list for all other feeds so that local postings are not propagated unless \&\f(CW\*(C`foo.*\*(C'\fR explicitly appears in the site's subscription list. This feature tends to be somewhat confusing since the default subscription is prepended and can be overridden by other patterns. .PP If the \f(CW\*(C`ME\*(C'\fR entry has a distribution sub-field, only articles that match that distribution list are accepted and all other articles with a distribution are rejected. A common use for this is to put something like \f(CW\*(C`/!local\*(C'\fR in the \f(CW\*(C`ME\*(C'\fR entry to reject local postings from other misconfigured sites. The distribution sub-field of \f(CW\*(C`ME\*(C'\fR has no effect on the acceptance or rejection of articles that do not have a Distribution header field. .PP An empty \f(CW\*(C`ME\*(C'\fR entry is possible, in which case no exclusion patterns will be defined. .PP Finally, it is also possible to set variables in \fInewsfeeds\fR and use them later in the file. A line starting with \f(CW\*(C`$\*(C'\fR sets a variable. For example: .PP .Vb 1 \& $LOCALGROUPS=local.*,example.* .Ve .PP This sets the variable \f(CW\*(C`LOCALGROUPS\*(C'\fR to \f(CW\*(C`local.*,example.*\*(C'\fR. This variable can later be used elsewhere in the file, such as in a site entry like: .PP .Vb 1 \& news.example.com:$LOCALGROUPS:Tf,Wnm: .Ve .PP which is then completely equivalent to: .PP .Vb 1 \& news.example.com:local.*,example.*:Tf,Wnm: .Ve .PP Variables aren't solely simple substitution. If either \f(CW\*(C`!\*(C'\fR or \f(CW\*(C`@\*(C'\fR immediately preceds the variable and the value of the variable contains commas, that character will be duplicated before each comma. This somewhat odd-sounding behavior is designed to make it easier to use variables to construct feed patterns. The utility becomes more obvious when you observe that the line: .PP .Vb 1 \& news.example.net:*,@$LOCALGROUPS:Tf,Wnm: .Ve .PP is therefore equivalent to: .PP .Vb 1 \& news.example.net:*,@local.*,@example.*:Tf,Wnm: .Ve .PP which (as explained below) excludes all of the groups in \f(CW$LOCALGROUPS\fR from the feed to that site. .SH "FLAG VALUES" .IX Header "FLAG VALUES" The \fIflags\fR parameter specifies miscellaneous parameters, including the type of feed, what information should be sent to it, and various limitations on what articles should be sent to a site. They may be specified in any order and should be separated by commas. Flags that take values should have the value immediately after the flag letter with no whitespace. The valid flags are: .IP "\fB<\fR \fIsize\fR" 4 .IX Item "< size" An article will only be sent to this site if it is less than \fIsize\fR bytes long. The default is no limit. .IP "\fB>\fR \fIsize\fR" 4 .IX Item "> size" An article will only be sent to this site if it is greater than \fIsize\fR bytes long. The default is no limit. .IP "\fBA\fR \fIchecks\fR" 4 .IX Item "A checks" An article will only be sent to this site if it meets the requirements specified in \fIchecks\fR, which should be chosen from the following set. \&\fIchecks\fR can be multiple letters if appropriate. Note that this flag is not effective on funnel targets; it has to be used on every funnel entry (for instance, \fBAf\fR is not effective on the \fIinnfeed!\fR funnel target and therefore has to be specified on every funnelled news site). .RS 4 .IP "c" 3 .IX Item "c" Exclude all kinds of control messages. .IP "C" 3 .IX Item "C" Only send control messages, not regular articles. .IP "d" 3 .IX Item "d" Only send articles with a Distribution: header. Combined with a particular distribution value in the \fIdistribution\fR part of the site entry, this can be used to limit articles sent to a site to just those with a particuliar distribution. .IP "e" 3 .IX Item "e" Only send articles where every newsgroup listed in the Newsgroups: header exists in the \fIactive\fR file. .IP "f" 3 .IX Item "f" Don't send articles rejected by filters. This is only useful when \&\fIdontrejectfiltered\fR is set to true in \fIinn.conf\fR. With that variable set, this lets one accept all articles but not propagate filtered ones to some sites. .IP "j" 3 .IX Item "j" Propagate articles according to their Newsgroups: header. This is only useful when \fIwanttrash\fR is set to true in \fIinn.conf\fR. With that variable set, articles accepted and filed in \f(CW\*(C`junk\*(C'\fR (due to \fIwanttrash\fR) are fed to peers based on their subscription pattern applied to the Newsgroups: header as though they were accepted and all those groups were locally carried. Otherwise, they are propagated to sites that receive the \f(CW\*(C`junk\*(C'\fR newsgroup. .Sp This variable is useful if you want to run \s-1INN\s0 with a minimal \fIactive\fR file and propagate all posts. .IP "o" 3 .IX Item "o" Only send articles for which overview data was stored. .IP "O" 3 .IX Item "O" Send articles to this site that don't have an Injection-Info: or X\-Trace: header, even if the \f(CW\*(C`O\*(C'\fR flag is also given. .IP "p" 3 .IX Item "p" Only check the exclusions against the Path: header of articles; don't check the site name. This is useful if your site names aren't the same as the Path: entries added by those remote sites, or for program feeds where the site name is arbitrary and unrelated to the Path: header. .RE .RS 4 .Sp If both \f(CW\*(C`c\*(C'\fR and \f(CW\*(C`C\*(C'\fR are given, the last specified one takes precedence. .RE .IP "\fBB\fR \fIhigh\fR/\fIlow\fR" 4 .IX Item "B high/low" If a site is being fed by a file, channel, or exploder (see below), the server will normally start trying to write the information as soon as possible. Providing a buffer may give better system performance and help smooth out overall load if a large batch of news comes in. The value of the this flag should be two numbers separated by a slash. \fIhigh\fR specifies the point at which the server can start draining the feed's I/O buffer, and \fIlow\fR specifies when to stop writing and begin buffering again; the units are bytes. The default is to do no buffering, sending output as soon as it is possible to do so. .IP "\fBC\fR \fIcount\fR" 4 .IX Item "C count" If this flag is specified, an article will only be sent to this site if the number of groups it is posted to, plus the square of the number of groups followups would appear in, is no more than \fIcount\fR. \f(CW30\fR is a good value for this flag, allowing crossposts to up to 29 groups when followups are set to a single group or poster and only allowing crossposts to 5 groups when followups aren't set. .IP "\fBF\fR \fIname\fR" 4 .IX Item "F name" Specifies the name of the file that should be used if it's necessary to begin spooling for the site (see below). If \fIname\fR is not an absolute path, it is taken to be relative to \fIpathoutgoing\fR in \fIinn.conf\fR. If \&\fIname\fR is a directory, the file \fItogo\fR in that directory will be used as the file name. .IP "\fBG\fR \fIcount\fR" 4 .IX Item "G count" If this flag is specified, an article will only be sent to this site if it is posted to no more than \fIcount\fR newsgroups. This has the problem of filtering out many FAQs, as well as newsgroup creation postings and similar administrative announcements. Either the \fBC\fR flag or the \fBU\fR flag is a better solution. .IP "\fBH\fR \fIcount\fR" 4 .IX Item "H count" If this flag is specified, an article will only be sent to this site if it has \fIcount\fR or fewer sites in its Path: line. This flag should only be used as a rough guide because of the loose interpretation of the Path: header; some sites put the poster's name in the header, and some sites that might logically be considered to be one hop become two because they put the posting workstation's name in the header. The default value for \&\fIcount\fR if not specified is one. (Also see the \fBO\fR flag, which is sometimes more appropriate for some uses of this flag.) .IP "\fBI\fR \fIsize\fR" 4 .IX Item "I size" The flag specifies the size of the internal buffer for a file feed. If there are more file feeds than allowed by the system, they will be buffered internally in least-recently-used order. If the internal buffer grows bigger then \fIsize\fR bytes, however, the data will be written out to the appropriate file. The default value is \f(CW16\fR\ \s-1KB\s0. .IP "\fBN\fR \fIstatus\fR" 4 .IX Item "N status" Restricts the articles sent to this site to those in newsgroups with the moderation status given by \fIstatus\fR. If \fIstatus\fR is \f(CW\*(C`m\*(C'\fR, only articles in moderated groups are sent; if \fIstatus\fR is \f(CW\*(C`u\*(C'\fR, only articles in unmoderated groups are sent. .IP "\fBO\fR \fIoriginator\fR" 4 .IX Item "O originator" If this flag is specified, an article will only be sent to this site if it contains an Injection-Info: header (or an X\-Trace: header if no Injection-Info: header is found) and the first field of this header matches \&\fIoriginator\fR. \fIoriginator\fR is a \fIuwildmat\fR\|(3) expression without commas or a list of such expressions, separated by \f(CW\*(C`/\*(C'\fR. The article is never sent if the first character of the pattern begins with \f(CW\*(C`@\*(C'\fR and the rest of the pattern matches. One use of this flag is to restrict the feed to locally generated posts by using an \fIoriginator\fR pattern that matches the Injection-Info: header added by the local server. .IP "\fBP\fR \fIpriority\fR" 4 .IX Item "P priority" The nice priority that this channel or program feed should receive. This should be a positive number between 0 and 20 and is the priority that the new process will run with. This flag can be used to raise the priority to normal if you're using the \fInicekids\fR parameter in \fIinn.conf\fR. .IP "\fBQ\fR \fIhashfeed\fR" 4 .IX Item "Q hashfeed" Specifies the \fIhashfeed\fR match expression for this site. It must be in the form \f(CW\*(C`value/mod\*(C'\fR or \f(CW\*(C`start\-end/mod\*(C'\fR. The Message-ID of the article is hashed using \s-1MD5\s0, which results in a 128\-bit hash. The lowest 32\ bits are then taken by default as the hashfeed value (which is an integer). If the hashfeed value modulus \f(CW\*(C`mod\*(C'\fR plus one equals \f(CW\*(C`value\*(C'\fR or is between \f(CW\*(C`start\*(C'\fR and \f(CW\*(C`end\*(C'\fR, the article will be fed to this site. All these numbers must be integers. .Sp It is a deterministic way to control the flow of articles and to split a feed. For instance: .Sp .Vb 2 \& Q1/2 Feeds about 50% of all articles to this site. \& Q2/2 Feeds the other 50% of all articles. .Ve .Sp Another example with three sites: .Sp .Vb 3 \& Q1\-3/10 Feeds about 30% of all articles. \& Q4\-5/10 Feeds about 20% of all articles. \& Q6\-10/10 Feeds about 50% of all articles. .Ve .Sp If this flag is specified multiple times, the contents will be logically \f(CW\*(C`OR\*(C'\fRed together (just one match is needed). .Sp You can use an extended syntax of the form \f(CW\*(C`value/mod_offset\*(C'\fR or \&\f(CW\*(C`start\-end/mod_offset\*(C'\fR. As \s-1MD5\s0 generates a 128\-bit return value, it is possible to specify from which byte-offset the 32\-bit integer used by hashfeed starts. The default value for \f(CW\*(C`offset\*(C'\fR is \f(CW\*(C`_0\*(C'\fR and thirteen overlapping values from \f(CW\*(C`_0\*(C'\fR to \f(CW\*(C`_12\*(C'\fR can be used. Only up to four totally independent values exist: \f(CW\*(C`_0\*(C'\fR, \f(CW\*(C`_4\*(C'\fR, \&\f(CW\*(C`_8\*(C'\fR and \f(CW\*(C`_12\*(C'\fR. .Sp Therefore, it allows to a generate a second level of deterministic distribution. Indeed, if a news server is fed \f(CW\*(C`Q1/2\*(C'\fR, it can go on splitting thanks to \f(CW\*(C`Q1\-3/9_4\*(C'\fR for instance. Up to four levels of deterministic distribution can be used. .Sp The algorithm is compatible with the one used by Diablo\ 5.1 and up. If you want to use the legacy quickhashing method used by Diablo before 5.1, you can put an \f(CW\*(C`@\*(C'\fR sign just after the \fBQ\fR flag (for instance \f(CW\*(C`Q@1\-3/10\*(C'\fR, but the distribution of the messages is not perfect with this legacy method whose use is discouraged and for which offsets cannot be used). .IP "\fBS\fR \fIsize\fR" 4 .IX Item "S size" If the amount of data queued for the site gets to be larger than \fIsize\fR bytes, the server will switch to spooling, appending to a file specified by the \fBF\fR flag, or \fIpathoutgoing\fR/\fIsitename\fR if \fBF\fR is not specified. Spooling usually happens only for channel or exploder feeds, when the spawned program isn't keeping up with its input. .IP "\fBT\fR \fItype\fR" 4 .IX Item "T type" This flag specifies the type of feed for this site. \fItype\fR should be a letter chosen from the following set: .Sp .Vb 6 \& c Channel \& f File \& l Log entry only \& m Funnel (multiple entries feed into one) \& p Program \& x Exploder .Ve .Sp Each feed is described below in \*(L"\s-1FEED\s0 \s-1TYPES\s0\*(R". The default is \fBTf\fR, for a file feed. .IP "\fBU\fR \fIcount\fR" 4 .IX Item "U count" If this flag is specified, an article will only be sent to this site if followups to this article would be posted to no more than \fIcount\fR newsgroups. (Also see \fBC\fR for a more complex way of handling this.) .IP "\fBW\fR \fIitems\fR" 4 .IX Item "W items" For a file, channel, or exploder feed, this flag controls what information will be sent to this site. For a program feed, only the asterisk (\f(CW\*(C`*\*(C'\fR) has any effect. \fIitems\fR should be chosen from the following set: .RS 4 .IP "b" 3 .IX Item "b" Size of the article (in wire format, meaning with \s-1CRLF\s0 at the end of each line, periods doubled at the beginning of lines, and ending in a line with a single period) in bytes. .IP "e" 3 .IX Item "e" The time the article will expire as seconds since epoch if it has an Expires: header, \f(CW0\fR otherwise. .IP "f" 3 .IX Item "f" The storage \s-1API\s0 token of the article (the same as \f(CW\*(C`n\*(C'\fR). The article can be retrieved given the storage \s-1API\s0 token by using \fIsm\fR\|(8). .IP "g" 3 .IX Item "g" The newsgroup the article is in; if cross-posted, then the first of the groups to which the article was posted that this site gets. (The difference from \f(CW\*(C`G\*(C'\fR is that this sends the newsgroup to which the article was posted even if it is a control message.) .IP "h" 3 .IX Item "h" The history hash key of the article (derived from the message \s-1ID\s0). .IP "m" 3 .IX Item "m" The message \s-1ID\s0 of the article. .IP "n" 3 .IX Item "n" The storage \s-1API\s0 token of the article. The article can be retrieved given the storage \s-1API\s0 token by using \fIsm\fR\|(8). .IP "p" 3 .IX Item "p" The time the article was posted a seconds since epoch. .IP "s" 3 .IX Item "s" The site that fed the article to the server. This is taken from either the Path: header or the \s-1IP\s0 address of the sending site depending on the value of \fIlogipaddr\fR in \fIinn.conf\fR. If \fIlogipaddr\fR is true and the \s-1IP\s0 address is \f(CW0.0.0.0\fR (meaning that the article was fed from localhost by a program like \fIrnews\fR\|(8)), the Path: header value will be sent instead. .IP "t" 3 .IX Item "t" The time the article was received as seconds since epoch. .IP "*" 3 The names of the appropriate funnel entries, or all sites that get the article (see below for more details). .IP "D" 3 .IX Item "D" The value of the Distribution: header of the article, or \f(CW\*(C`?\*(C'\fR if there is no such header in the article. .IP "G" 3 .IX Item "G" Where the article is stored. If the newsgroup is crossposted, this is generally the first of the groups to which it was posted that this site receives; however, control messages are filed in control or control.* (which is the difference between this item and \f(CW\*(C`g\*(C'\fR). .IP "H" 3 .IX Item "H" All of the headers, followed by a blank line. The Xref header will already be present, and a Bytes header containing the article's size in bytes as in the \f(CW\*(C`b\*(C'\fR item will be added to the headers. If used, this should be the only item in the list. .IP "N" 3 .IX Item "N" The value of the Newsgroups: header. .IP "P" 3 .IX Item "P" The value of the Path: header. .IP "O" 3 .IX Item "O" Overview data for the article. .IP "R" 3 .IX Item "R" Information needed for replication (the Xref header without the site name). .RE .RS 4 .Sp More than one letter can be given. If multiple items are specified, they will be written in the order specified separated by spaces. (\f(CW\*(C`H\*(C'\fR should be the only item if given, but if it's not a newline will be sent before the beginning of the headers.) The default is \fBWn\fR. .Sp The \f(CW\*(C`H\*(C'\fR and \f(CW\*(C`O\*(C'\fR items are intended for use by programs that create news overview databases or require similar information. \fBWnteO\fR is the flag to generate input needed by the \fIoverchan\fR\|(8) program. .Sp The asterisk (\f(CW\*(C`*\*(C'\fR) has special meaning. Normally it expands to a space-separated list of all sites that received the current article. If, however, this site is a target of a funnel feed (in other words, if it is named by other sites which have the \fBTm\fR flag), then the asterisk expands to the names of the funnel feeds that received the article. Similarly, if the site is a program feed, an asterisk in the \fIparameter\fR field will be expanded into the list of funnel feeds that received the article. A program feed cannot get the site list unless it is the target of other \&\fBTm\fR feeds. .RE .SH "FEED TYPES" .IX Header "FEED TYPES" \&\fBinnd\fR provides four basic types of feeds: log, file, program, and channel. An exploder is a special type of channel. In addition, several entries can feed into the same feed; these are funnel feeds, which refer to an entry that is one of the other types. Funnel feeds are partially described above with the description of the \fBW*\fR flag. A funnel feed gets every article that would be sent to any of the feeds that funnel into it and normally include the \fBW*\fR flag in their flags so that the program processing that feed knows which sites received which articles. The most common funnel feed is \fIinnfeed\fR\|(8). .PP Note that the term \*(L"feed\*(R" is technically a misnomer, since the server doesn't transfer articles itself and only writes data to a file, program, or log telling another program to transfer the articles. .PP The simplest feed is a log feed (\fBTl\fR). Other than a mention in the news log file, \fIpathlog\fR/news, no data is written out. This is equivalent to a \fBTf\fR entry writing to \fI/dev/null\fR, except that no file is ever opened. Flushing a log feed does nothing. .PP A file feed (\fBTf\fR) is the next simplest type of feed. When the site should receive an article, the specified data is written out to the file named by the \fIparameter\fR field. If \fIparameter\fR is not an absolute path, it is taken to be relative to \fIpathoutgoing\fR in \fIinn.conf\fR. If \&\fIparameter\fR is not given, it defaults to \fIpathoutgoing\fR/\fIsitename\fR. The file name should be unique (two file feeds should not ever point to the same file). .PP File feeds are designed for use by external programs that periodically process the written data. To cooperate with \fBinnd\fR properly, such external programs should first rename the batch file and then send a flush command for that site to \fBinnd\fR using \fIctlinnd\fR\|(8). \fBinnd\fR will then write out any buffered data, close the file, and reopen it (under the original name), and the program can process the data in the renamed file at its leisure. File feeds are most frequently used in combination with \&\fInntpsend\fR\|(8). .PP A program feed (\fBTp\fR) spawns a given program for every article that the site receives. The \fIparameter\fR field must be the command line to execute, and should contain one instance of \f(CW%s\fR, which will be replaced by the storage \s-1API\s0 token of the article (the actual article can be retrieved by the program using \fIsm\fR\|(8)). The program will not receive anything on standard input (unlike earlier versions of \s-1INN\s0, where the article is sent to the program on stdin), and standard output and error from the program will be set to the error log (\fIpathlog\fR/errlog). \fBinnd\fR will try to avoid spawning a shell if the command has no shell meta-characters; this feature can be defeated if necessary for some reason by appending a semi-colon to the end of the command. The full path name of the program to be run must be specified unless the command will be run by the shell (and it is strongly recommended that the full path name always be specified regardless). .PP If a program feed is the target of a funnel, and if \fBW*\fR appears in the flags of the site, a single asterisk may be present in the \fIparameter\fR and will be replaced by a space-separated list of names of the sites feeding into the funnel which received the relevant article. If the site is not the target of a funnel, or if the \fBW*\fR flag is not used, the asterisk has no special meaning. .PP Flushing a program feed does nothing. .PP For a channel (\fBTc\fR) or exploder (\fBTx\fR) feed, the \fIparameter\fR field again names the process to start. As with program feeds, the full path to the program must be specified. However, rather than spawning the program for every article, it is spawned once and then whenever the site receives an article, the data specified by the site flags is written to the standard input of the spawned program. Standard output and error are set as with program feeds. If the process exits, it will be restarted automatically. If the process cannot be started, the server will spool input to a file named \fIpathoutgoing\fR/\fIsitename\fR and will try to start the process again later. .PP When a channel or exploder feed is flushed, the server closes its end of the pipe to the program's standard input. Any pending data that has not been written will be spooled; see the description of the \fBS\fR flag above. The server will then spawn a new instance of the program. No signal is sent to the program; it is up to the program handling a channel or exploder feed to notice end of file on its standard input and exit appropriately. .PP Exploders are a special type of channel feed. In addition to the channel feed behavior described above, exploders can also be sent command lines. These lines start with an exclamation point and their interpretation is up to the exploder. The following commands are generated automatically by the server: .PP .Vb 4 \& !newgroup group \& !rmgroup group \& !flush \& !flush site .Ve .PP These commands are sent whenever the \fIctlinnd\fR\|(8) command of the same name is received by the server. In addition, the \fIctlinnd\fR\|(8) \f(CW\*(C`send\*(C'\fR command can be used to send an arbitrary command line to an exploder. The primary exploder is \fIbuffchan\fR\|(8). .PP Finally, \fBTm\fR feeds are the input to a funnel. The \fIparameter\fR field of the site should name the site handling articles for all of the funnel inputs. .SH "EXAMPLES" .IX Header "EXAMPLES" The syntax of the \fInewsfeeds\fR file is so complex because you can specify a staggering variety of feeds. \s-1INN\s0 is capable of interacting with a wide variety of programs that do various things with news articles. Far and away the most common two entries in \fInewsfeeds\fR, however, are file feeds for \fInntpsend\fR\|(8) and funnel feeds for \fIinnfeed\fR\|(8). .PP The former look like this: .PP .Vb 1 \& feed.example.com:*,!control,!control.*,!junk:Tf,Wnm: .Ve .PP which generates a file named \fIpathoutgoing\fR/feed.example.com containing one line per article consisting of the storage \s-1API\s0 token, a space, and the message \s-1ID\s0. .PP The latter look like this: .PP .Vb 1 \& feed.example.com:*,!control,!control.*,!junk:Tm:innfeed! .Ve .PP Very similar, except that this is the input to a funnel feed named \&\f(CW\*(C`innfeed!\*(C'\fR. One could also write this as: .PP .Vb 1 \& example/feed.example.com:*,!control,!control.*,!junk:Ap,Tm:innfeed! .Ve .PP (note the \fBAp\fR so that articles that contain just \f(CW\*(C`example\*(C'\fR in the Path: header will still be sent), which is completely equivalent except that this will be logged in \fIpathlog\fR/news as going to the site \f(CW\*(C`example\*(C'\fR rather than \f(CW\*(C`feed.example.com\*(C'\fR. .PP The typical feed entry for \fIinnfeed\fR\|(8) is a good example of a channel feed that's the target of various funnel feeds (make sure the path to \fBinnfeed\fR is properly set): .PP .Vb 1 \& innfeed!:!*:Tc,Wnm*:/innfeed \-y .Ve .PP Note that the \fIpattern\fR for this feed is just \f(CW\*(C`!*\*(C'\fR so that it won't receive any articles directly. The feed should only receive those articles that would go to one of the funnel feeds that are feeding into it. \fIinnfeed\fR\|(8) will receive one line per article on its standard input containing the storage \s-1API\s0 token, the message \s-1ID\s0, and a space-separated list of sites that should receive that article. .PP Here's a more esoteric example of a channel feed: .PP .Vb 2 \& watcher!:*:Tc,Wbnm\e \& :exec awk \*(Aq$1 > 1000000 { print "BIG", $2, $3 }\*(Aq > /dev/console .Ve .PP This receives the byte size of each article along with the storage \s-1API\s0 token and message \s-1ID\s0, and prints to the console a line for every article that's over a million bytes. This is actually rather a strange way to write this since \s-1INN\s0 can do the size check itself; the following is equivalent: .PP .Vb 2 \& watcher!:*:Tc,>1000000,Wbnm\e \& :exec awk \*(Aq{ print "BIG", $2, $3}\*(Aq > /dev/console .Ve .PP Here's a cute, really simple news to mail gateway that also serves as an example of a fairly fancy program feed: .PP .Vb 2 \& mailer!:!*:W*,Tp\e \& :sm %s | innmail \-s "News article" * .Ve .PP Remember that \f(CW%s\fR is replaced by the storage \s-1API\s0 token, so this retrieves the article and pipes it into \fBinnmail\fR (which is safer than programs like \fIMail\fR\|(1) because it doesn't parse the body for tilde commands) with a given subject line. Note the use of \f(CW\*(C`*\*(C'\fR in the command line and \fBW*\fR in the flags; this entry is designed to be used as the target of funnel feeds such as: .PP .Vb 2 \& peter@example.com:news.software.nntp:Tm:mailer! \& sue@example.com:news.admin.misc:Tm:mailer! .Ve .PP Suppose that the server receives an article crossposted between news.admin.misc and news.software.nntp. The server will notice that the article should be sent to the site \f(CW\*(C`peter@example.com\*(C'\fR and the site \&\f(CW\*(C`bob@example.com\*(C'\fR, both of which funnel into \f(CW\*(C`mailer!\*(C'\fR, so it will look at the \f(CW\*(C`mailer!\*(C'\fR site and end up executing the command line: .PP .Vb 1 \& sm @...@ | innmail \-s "News article" peter@example.com sue@example.com .Ve .PP which will mail the article to both Peter and Sue. .PP Finally, another very useful example of a channel feed: the standard entry for \fIcontrolchan\fR\|(8). Make sure its path is properly set. .PP .Vb 3 \& controlchan!\e \& :!*,control,control.*,!control.cancel/!collabra\-internal\e \& :AC,Tc,Wnsm:/controlchan .Ve .PP This program only wants information about articles posted to a control newsgroup other than control.cancel, which due to the sorting of control messages described in \fIinnd\fR\|(8) will send it all control messages except for cancel messages. In this case, we also exclude any article with a distribution of \f(CW\*(C`collabra\-internal\*(C'\fR. \fBcontrolchan\fR gets the storage \&\s-1API\s0 token, the name of the sending site (for processing old-style ihave and sendme control messages, be sure to read about \fIlogipaddr\fR in \&\fIcontrolchan\fR\|(8)), and the message \s-1ID\s0 for each article. .PP For many other examples, including examples of the special \f(CW\*(C`ME\*(C'\fR site entry, see the example \fInewsfeeds\fR file distributed with \s-1INN\s0. Also see the install documentation that comes with \s-1INN\s0 for information about setting up the standard newsfeeds entries used by most sites. .SH "HISTORY" .IX Header "HISTORY" Written by Rich \f(CW$alz\fR for InterNetNews. Reformatted and rewritten in \s-1POD\s0 by Russ Allbery . .PP \&\f(CW$Id:\fR newsfeeds.pod 9767 2014\-12\-07 21:13:43Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIactive\fR\|(5), \fIbuffchan\fR\|(8), \fIcontrolchan\fR\|(8), \fIctlinnd\fR\|(8), \fIinn.conf\fR\|(5), \fIinnd\fR\|(8), \&\fIinnfeed\fR\|(8), \fIinnxmit\fR\|(8), \fInntpsend\fR\|(8), \fIuwildmat\fR\|(3). inn-2.6.0/doc/man/tally.control.80000644000175200017520000001331412575023702016137 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "TALLY.CONTROL 8" .TH TALLY.CONTROL 8 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" tally.control \- Keep track of newsgroup creations and deletions .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fBtally.control\fR < \fIlogfile\fR .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\fBtally.control\fR is normally daily invoked by \fBscanlogs\fR. It reads its standard input, which should be the \fInewgroup.log\fR and \fIrmgroup.log\fR control log files. They contain a summary line describing the control message and the action taken by \fBcontrolchan\fR, followed by the article indented by four spaces, and a blank line. Then, \fBtally.control\fR updates the cumulative list of newsgroup creations and deletions which is kept in \fIcontrol.log\fR. .PP All these log files reside in the \fIpathlog\fR directory set in \fIinn.conf\fR. In order to generate them, you need to enable control articles logging in \fIcontrol.ctl\fR\|(5), as explained in the \fIcontrol.log\fR entry of \fInewslog\fR\|(5). .SH "FILES" .IX Header "FILES" .IP "\fIpathbin\fR/tally.control" 4 .IX Item "pathbin/tally.control" The Shell script itself used to tally newsgroup creations and deletions up. .IP "\fIpathlog\fR/control.log" 4 .IX Item "pathlog/control.log" This file maintains a count of the number of newgroup and rmgroup control messages seen for each newsgroup. The count is of the number of control messages with the indicated arguments, regardless if they were actually processed. All control arguments, including invalid ones, are counted. An example of lines which can be found in that log file is: .Sp .Vb 3 \& 3 Control: newgroup foo.bar moderated \& 3 Control: rmgroup misc.removed \& 1 Control: newgroup misc.created .Ve .SH "HISTORY" .IX Header "HISTORY" Written by Landon Curt Noll and Rich \f(CW$alz\fR for InterNetNews. Rewritten and converted to \s-1POD\s0 by Julien Elie. .PP \&\f(CW$Id:\fR tally.control.pod 8357 2009\-02\-27 17:56:00Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIcontrol.ctl\fR\|(5), \fInews.daily\fR\|(8), \fInewslog\fR\|(5), \fIscanlogs\fR\|(8). inn-2.6.0/doc/man/archive.80000644000175200017520000002115512575023702014756 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "ARCHIVE 8" .TH ARCHIVE 8 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" archive \- Usenet article archiver .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fBarchive\fR [\fB\-cfr\fR] [\fB\-a\fR \fIarchive\fR] [\fB\-i\fR \fIindex\fR] [\fB\-p\fR \fIpattern\fR] [\fIinput\fR] .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\fBarchive\fR makes copies of files specified on its standard input. It is normally run either as a channel feed under \fBinnd\fR or by a script before \fBnews.daily\fR is run. .PP \&\fBarchive\fR reads the named \fIinput\fR file, or standard input if no file is given. The input is taken as a sequence of lines; blank lines and lines starting with a number sign (\f(CW\*(C`#\*(C'\fR) are ignored. All other lines should specify the token of an article to archive. Every article is retrieved from a token, and the Xref: header is used to determine the target file in the archive directory. You can limit the targets taken from the Xref: header with the \fB\-p\fR option. .PP Files are copied to a directory within the archive directory, \&\fIpatharchive\fR in \fIinn.conf\fR (or some other directory given with \fB\-a\fR). The default is to create a hierarchy that mimics a traditional news spool storage of the given articles; intermediate directories will be created as needed. For example, if the input token represents article 2211 in the newsgroup comp.sources.unix, \fBarchive\fR will by default store the article as: .PP .Vb 1 \& comp/sources/unix/2211 .Ve .PP in the archive area. This can be modified with the \fB\-c\fR and \fB\-f\fR options. .SH "OPTIONS" .IX Header "OPTIONS" .IP "\fB\-a\fR \fIarchive\fR" 4 .IX Item "-a archive" If the \fB\-a\fR flag is given, its argument specifies the root of the archive area, instead of \fIpatharchive\fR in \fIinn.conf\fR. .IP "\fB\-c\fR" 4 .IX Item "-c" If the \fB\-c\fR flag is given, directory names will be flattened as described under the \fB\-f\fR option. Then, additionally, all posts will be concatenated into a single file, appending to that file if it already exists. The file name will be \f(CW\*(C`YYYYMM\*(C'\fR, formed from the current time when \&\fBarchive\fR is run. In other words, if given an article in comp.sources.unix on December 14th, 1998, the article would be appended to the file: .Sp .Vb 1 \& comp.sources.unix/199812 .Ve .Sp in the archive area. .Sp Articles will be separated by a line containing only \f(CW\*(C`\-\-\-\-\-\-\-\-\-\-\-\*(C'\fR. .IP "\fB\-f\fR" 4 .IX Item "-f" If the \fB\-f\fR flag is used, directory names will be flattened, replacing the slashes with the periods. In other words, article 2211 in comp.sources.unix will be written to: .Sp .Vb 1 \& comp.sources.unix/2211 .Ve .Sp in the archive area. .IP "\fB\-i\fR \fIindex\fR" 4 .IX Item "-i index" If the \fB\-i\fR flag is used, \fBarchive\fR will append one line to the file \&\fIindex\fR for each article that it archives. This line will contain the destination file name, the Message-ID: header, and the Subject: header of the message, separated by spaces. If either header is missing (normally not possible if the article was accepted by \fBinnd\fR), it will be replaced by \f(CW\*(C`\*(C'\fR. The headers will be transformed using the same rules as are used to generate overview data (unfolded and then with tabs, \s-1CR\s0, and \&\s-1LF\s0 replaced by spaces). .IP "\fB\-p\fR \fIpattern\fR" 4 .IX Item "-p pattern" Limits the targets taken from the Xref: header to the groups specified in \&\fIpattern\fR. \fIpattern\fR is a \fIuwildmat\fR\|(3) pattern matching newsgroups that you wish to have \fBarchive\fR handle. .IP "\fB\-r\fR" 4 .IX Item "-r" By default, \fBarchive\fR sets its standard error to \fIpathlog\fR/errlog. To suppress this redirection, use the \fB\-r\fR flag. .SH "RETURN VALUE" .IX Header "RETURN VALUE" If the input is exhausted, \fBarchive\fR will exit with a zero status. If an I/O error occurs, it will try to spool its input, copying it to a file. If there was no input filename, the standard input will be copied to \&\fIpathoutgoing\fR/archive and the program will exit. If an input filename was given, a temporary file named \fIinput\fR.bch (if \fIinput\fR is an absolute pathname) or \fIpathoutgoing\fR/\fIinput\fR.bch (if the filename does not begin with a slash) is created. Once the input is copied, \fBarchive\fR will try to rename this temporary file to be the name of the input file, and then exit. .SH "EXAMPLES" .IX Header "EXAMPLES" A typical \fInewsfeeds\fR\|(5) entry to archive most source newsgroups is as follows: .PP .Vb 4 \& source\-archive!\e \& :!*,*sources*,!*wanted*,!*.d\e \& :Tc,Wn\e \& :/archive \-f \-i /INDEX .Ve .PP Replace and with the appropriate paths. .SH "HISTORY" .IX Header "HISTORY" Written by Rich \f(CW$alz\fR for InterNetNews. Converted to \&\s-1POD\s0 by Russ Allbery . .PP \&\f(CW$Id:\fR archive.pod 9767 2014\-12\-07 21:13:43Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIinn.conf\fR\|(5), \fInewsfeeds\fR\|(5). inn-2.6.0/doc/man/batcher.80000644000175200017520000002400312575023702014740 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "BATCHER 8" .TH BATCHER 8 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" batcher \- Article batching for InterNetNews .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fBbatcher\fR [\fB\-rv\fR] [\fB\-a\fR \fIarticles\fR] [\fB\-A\fR \fItotal-articles\fR] [\fB\-b\fR \fIsize\fR] [\fB\-B\fR \fItotal-size\fR] [\fB\-i\fR \fIstring\fR] [\fB\-N\fR \fIbatches\fR] [\fB\-p\fR \fIprocess\fR] [\fB\-s\fR \fIseparator\fR] \&\fIhost\fR [\fIinput\fR] .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\fBbatcher\fR reads a list of files and prepares news batches for the specified host. It is generally used to prepare \s-1UUCP\s0 feeds, but the resulting batches can be used by any application that uses \fIrnews\fR\|(8) to inject the articles. It is normally invoked by a script run out of cron that uses \fBshlock\fR to lock the host, followed by \fBctlinnd\fR to flush the batch file. See \fIsend\-uucp\fR\|(8) for a front-end for \fBbatcher\fR. .PP \&\fBbatcher\fR reads the file \fIinput\fR, or standard input if no file is given. If \fIinput\fR is a relative file name, it is assumed to be in \&\fIpathoutgoing\fR as set in \fIinn.conf\fR. Blank lines and lines starting with a number sign (\f(CW\*(C`#\*(C'\fR) are ignored. All other lines in the input should consist of one or two fields separated by a single space. The first field is the storage \s-1API\s0 token of an article. The second field, if present, specifies the size of the article in bytes. .PP By default, batches are written to standard output (which isn't very useful if more than one batch is output), but see the \fB\-p\fR option. .SH "OPTIONS" .IX Header "OPTIONS" .IP "\fB\-a\fR \fIarticles\fR" 4 .IX Item "-a articles" This flag limits the number of articles included in each batch. The default is no limit. A new batch will be started when either the total bytes or the number of articles written exceeds the specified limits. .IP "\fB\-A\fR \fItotal-articles\fR" 4 .IX Item "-A total-articles" Limits the total number of articles written for all batches. As soon as the total number of articles written to batches reaches or exceeds \&\fItotal-articles\fR, all additional articles in the input will be deferred. The default is no limit. .IP "\fB\-b\fR \fIsize\fR" 4 .IX Item "-b size" This flag sets the size limit for each batch; as soon as at least this much data has been written out, a new batch will be started. The default size is 60\ \s-1KB\s0. Using \f(CW\*(C`\-b 0\*(C'\fR will allow unlimited batch sizes. .IP "\fB\-B\fR \fItotal-size\fR" 4 .IX Item "-B total-size" Limits the total number of bytes written for all batches. As soon as the total bytes written to batches reaches or exceeds \fItotal-size\fR, all additional articles in the input will be deferred. The default is no limit. .IP "\fB\-i\fR \fIstring\fR" 4 .IX Item "-i string" A batch starts with an identifying line to specify the unpacking method to be used on the receiving end. When this flag is used, \fIstring\fR, followed by a newline, will be output at the start of each batch. The default is to have no initial string (under the assumption that either the processor specified with the \fB\-p\fR flag or some other later process will add the appropriate line). .IP "\fB\-N\fR \fIbatches\fR" 4 .IX Item "-N batches" Limits the total number of batches written. As soon as the number of batches written reaches or exceeds \fIbatches\fR, all additional articles in the input will be deferred. The default is no limit. .IP "\fB\-p\fR \fIprocess\fR" 4 .IX Item "-p process" By default, batches are written to standard output, which is not useful when more than one output batch is created. If this option is given, each batch will instead be fed via a pipe to the shell command \fIprocess\fR. The \&\fIprocess\fR argument must be an \fIsprintf\fR\|(3) format string, which may have a single \f(CW%s\fR parameter that will be replaced with the host name. .Sp A common value is: .Sp .Vb 1 \& ( echo \*(Aq#! gunbatch\*(Aq ; exec gzip \-c ) | uux \- \-r \-z %s!rnews .Ve .Sp which generates gzip-compressed batches and feeds them to \fBuux\fR. .IP "\fB\-r\fR" 4 .IX Item "-r" By default, \fBbatcher\fR reports errors to \fIpathlog\fR/errlog. To suppress this redirection and report errors to standard error, use the \fB\-r\fR flag. .IP "\fB\-s\fR \fIseparator\fR" 4 .IX Item "-s separator" Each article in a batch starts with a separator line that indicates the size of the article. \fIseparator\fR must be an \fIsprintf\fR\|(3) string, which may have a single \f(CW%ld\fR in the string that will be replaced with the size of the article. If the separator is not empty, a newline will also be appended to it when it is added to the beginning of each article. .Sp The default separator is: .Sp .Vb 1 \& #! rnews %ld .Ve .Sp and this should rarely be changed. .IP "\fB\-v\fR" 4 .IX Item "-v" Upon exit, \fBbatcher\fR reports statistics via syslog. With this flag, the statistics will also be printed to standard output. .SH "EXIT STATUS" .IX Header "EXIT STATUS" If the input is exhausted and all batches are created successfully, \&\fBbatcher\fR will exit with a zero status. .PP If any of the limits specified with \fB\-A\fR, \fB\-B\fR, or \fB\-N\fR flags are reached, or if there is an error in writing a batch, \fBbatcher\fR will try to spool the remaining input by copying it to a file as follows: .IP "\(bu" 2 If there was no input filename, the remaining input will be copied to \&\fIpathoutgoing\fR/\fIhost\fR. .IP "\(bu" 2 If an input filename was given, the remaining input will be copied to a temporary file named by appending \f(CW\*(C`.bch\*(C'\fR to the end of \fIinput\fR (and qualified by adding \fIpathoutgoing\fR if \fIinput\fR was not a fully qualified path). If this happens successfully, \fBbatcher\fR will then try to rename this temporary file to \fIinput\fR (thus replacing \fIinput\fR with a copy of itself with all of lines for the successfully batched articles removed). .PP Upon receipt of an interrupt or termination signal, \fBbatcher\fR will finish batching the current article, close the batch, and then rewrite the batch file as described above. .SH "HISTORY" .IX Header "HISTORY" Written by Rich \f(CW$alz\fR for InterNetNews. Rewritten by Russ Allbery in \s-1POD\s0. .PP \&\f(CW$Id:\fR batcher.pod 9767 2014\-12\-07 21:13:43Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIctlinnd\fR\|(8), \fIinn.conf\fR\|(5), \fInewsfeeds\fR\|(5), \fIrnews\fR\|(8), \fIsend\-uucp\fR\|(8), \fIshlock\fR\|(1). inn-2.6.0/doc/man/ovdb_server.80000644000175200017520000001113712575023702015654 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "OVDB_SERVER 8" .TH OVDB_SERVER 8 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" ovdb_server \- overview 'helper' server .SH "SYNOPSIS" .IX Header "SYNOPSIS" Use \f(CW\*(C`ovdb_init\*(C'\fR to start ovdb_server .SH "DESCRIPTION" .IX Header "DESCRIPTION" If the \f(CW\*(C`readserver\*(C'\fR parameter in \fIovdb.conf\fR is true, \&\f(CW\*(C`ovdb_init\*(C'\fR will start \f(CW\*(C`ovdb_server\*(C'\fR. .PP \&\f(CW\*(C`ovdb_server\*(C'\fR opens the overview database, and accesses it on behalf of the nnrpd reader processes. .PP To shut down ovdb_server, send a \s-1TERM\s0 signal to the process \s-1ID\s0 in \fI\fIpathrun\fI/ovdb_server.pid\fR . The parent process will shut down its children and wait for their exit before exiting itself. .SH "HISTORY" .IX Header "HISTORY" Written by Heath Kehoe for InterNetNews. .PP \&\f(CW$Id:\fR ovdb_server.pod 9765 2014\-12\-07 21:07:34Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIovdb\fR\|(5), \fIovdb_init\fR\|(8) inn-2.6.0/doc/man/active.50000644000175200017520000002536112575023702014610 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "ACTIVE 5" .TH ACTIVE 5 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" active \- List of newsgroups carried by the server .SH "DESCRIPTION" .IX Header "DESCRIPTION" The file \fIpathdb\fR/active lists the newsgroups carried by \s-1INN\s0. This file is generally maintained using \fIctlinnd\fR\|(8) to create and remove groups, or by letting \fIcontrolchan\fR\|(8) do so on the basis of received control messages; this file is then updated and a backup stored in \fIpathdb\fR/active.old. Note that the \fInewsgroups\fR\|(5) file normally contains the descriptions of the newsgroups carried by the news server. .PP The \fIactive\fR file should not be edited directly without throttling \fBinnd\fR, and must be reloaded using \fBctlinnd\fR before \fBinnd\fR is unthrottled. Editing it directly even with those precautions may make it inconsistent with the overview database and won't update \fIactive.times\fR, so \fBctlinnd\fR should be used to make modifications whenever possible. .PP Each newsgroup should be listed only once. Each line specifies one group. The order of groups does not matter. Within each newsgroup, received articles for that group are assigned monotonically increasing numbers as unique names. If an article is posted to newsgroups not mentioned in this file, those newsgroups are ignored. .PP If none of the newsgroups listed in the Newsgroups: header of an article are present in this file, the article is either rejected (if \fIwanttrash\fR is false in \fIinn.conf\fR), or is filed into the newsgroup \f(CW\*(C`junk\*(C'\fR and, when \f(CW\*(C`Aj\*(C'\fR is not set in the \fInewsfeeds\fR feed pattern, only propagated to sites that receive the \f(CW\*(C`junk\*(C'\fR newsgroup (if \fIwanttrash\fR is true). .PP Each line of this file consists of four fields separated by a space: .PP .Vb 1 \& .Ve .PP The first field is the name of the newsgroup. The newsgroup \f(CW\*(C`junk\*(C'\fR is special, as mentioned above. The newsgroup \f(CW\*(C`control\*(C'\fR and any newsgroups beginning with \f(CW\*(C`control.\*(C'\fR are also special; control messages are filed into a control.* newsgroup named after the type of control message if that group exists, and otherwise are filed into the newsgroup \f(CW\*(C`control\*(C'\fR (without regard to what newsgroups are listed in the Newsgroups: header). If \fImergetogroups\fR is set to true in \fIinn.conf\fR, newsgroups that begin with \f(CW\*(C`to.\*(C'\fR are also treated specially; see \fIinnd\fR\|(8). .PP The second field is the highest article number that has been used in that newsgroup. The third field is the lowest article number in the group; this number is not guaranteed to be accurate, and should only be taken to be a hint. It is normally updated nightly as part of the expire process; see \fInews.daily\fR\|(8) and look for \f(CW\*(C`lowmark\*(C'\fR or \f(CW\*(C`renumber\*(C'\fR for more details. Note that because of article cancellations, there may be gaps in the numbering sequence. If the lowest article number is greater than the highest article number, then there are no articles in the newsgroup. In order to make it possible to update an entry in-place without rewriting the entire file, the second and third fields are padded out with leading zeros to make them a fixed width. .PP The fourth field contains one of the following status: .PP .Vb 6 \& y Local postings and articles from peers are allowed. \& m The group is moderated and all postings must be approved. \& n No local postings are allowed, only articles from peers. \& j Articles from peers are filed in the junk group instead. \& x No local postings, and articles from peers are ignored. \& =foo.bar Articles are filed in the group foo.bar instead. .Ve .PP If a newsgroup has the \f(CW\*(C`j\*(C'\fR status, no articles will be filed in that newsgroup. Local postings are not accepted; if an article for that newsgroup is received from a remote site, and if it is not crossposted to some other valid group, it will be filed into the \f(CW\*(C`junk\*(C'\fR newsgroup instead. This is different than simply not listing the group, since the article will still be accepted and can be propagated to other sites, and the \f(CW\*(C`junk\*(C'\fR group can be made available to readers if wished. .PP If the field begins with an equal sign, the newsgroup is an alias. Articles cannot be posted to that newsgroup, but they can be received from other sites. Any articles received from peers for that newsgroup are treated as if they were actually posted to the group named after the equal sign. Note that the Newsgroups: header of the articles is not modified. (Alias groups are typically used during a transition and are typically created manually with \fIctlinnd\fR\|(8).) An alias should not point to another alias. .PP Note that \fIreaders.conf\fR can be configured so that local posts to newsgroups with status \f(CW\*(C`j\*(C'\fR, \f(CW\*(C`n\*(C'\fR or \f(CW\*(C`x\*(C'\fR are accepted. .SH "MINIMAL ACTIVE FILE" .IX Header "MINIMAL ACTIVE FILE" For \fBinnd\fR to be able to start, the three groups \f(CW\*(C`control\*(C'\fR, \&\f(CW\*(C`control.cancel\*(C'\fR and \f(CW\*(C`junk\*(C'\fR need to be in the \fIactive\fR file. Besides, if \fImergetogroups\fR is set to true in \fIinn.conf\fR, the newsgroup \f(CW\*(C`to\*(C'\fR also needs to exist. .PP The minimal \fIactive\fR file shipped with \s-1INN\s0 is: .PP .Vb 6 \& control 0000000000 0000000001 n \& control.cancel 0000000000 0000000001 n \& control.checkgroups 0000000000 0000000001 n \& control.newgroup 0000000000 0000000001 n \& control.rmgroup 0000000000 0000000001 n \& junk 0000000000 0000000001 n .Ve .PP (Note that the second and the third field may differ if the news server has already been in use.) .PP There are more control.* pseudogroups here than needed by \fBinnd\fR to start; the corresponding control messages will be filed into them. The \f(CW\*(C`n\*(C'\fR status is so that users cannot post directly to these groups (control messages should only be posted to the groups that they affect). If you do not want these groups to be visible to clients, do not delete them but simply hide them in \fIreaders.conf\fR\|(5). .PP To create additional groups after the server is running, you can use \&\f(CW\*(C`ctlinnd newgroup\*(C'\fR. You can also synchronize your newsgroup list to that of another server by using \fIactsync\fR\|(8) or get the \fIactive\fR file of another \s-1NNTP\s0 server with \fIgetlist\fR\|(1). And do not forget to update your \fInewsgroups\fR file, which can be automatically done thanks to \&\fBdocheckgroups\fR called with the \fB\-u\fR flag. .SH "HISTORY" .IX Header "HISTORY" Written by Rich \f(CW$alz\fR for InterNetNews. Converted to \&\s-1POD\s0 by Russ Allbery . .PP \&\f(CW$Id:\fR active.pod 9767 2014\-12\-07 21:13:43Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIactive.times\fR\|(5), \fIactsync\fR\|(8), \fIcontrolchan\fR\|(8), \fIctlinnd\fR\|(8), \fIdocheckgroups\fR\|(8), \&\fIgetlist\fR\|(1), \fIinn.conf\fR\|(5), \fIinnd\fR\|(8), \fImod\-active\fR\|(8), \fInews.daily\fR\|(8), \fInewsgroups\fR\|(5), \&\fIreaders.conf\fR\|(5). inn-2.6.0/doc/man/ctlinnd.80000644000175200017520000006724412575023702015001 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "CTLINND 8" .TH CTLINND 8 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" ctlinnd \- Control the main InterNetNews daemon .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fBctlinnd\fR [\fB\-hs\fR] [\fB\-t\fR \fItimeout\fR] [\fIcommand\fR [\fIargument\fR ...]] .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\fBctlinnd\fR sends a message to the control channel of \fIinnd\fR\|(8), the main InterNetNews daemon. .PP In the normal mode of behavior, the message is sent to the server, which then performs the requested action and sends back a reply with a text message and an exit code for \fBctlinnd\fR. If the server successfully performed the command, \fBctlinnd\fR will print the reply on standard output and exit with a status of zero. If the server could not perform the command, it will direct \fBctlinnd\fR to exit with a status of one. By default, \fBctlinnd\fR will wait forever for a response from \fBinnd\fR; this can be changed with the \fB\-t\fR flag. .PP The \f(CW\*(C`shutdown\*(C'\fR, \f(CW\*(C`xabort\*(C'\fR, and \f(CW\*(C`xexec\*(C'\fR commands do not generate a reply, since they cause \fBinnd\fR to exit. After these commands, \fBctlinnd\fR will always exit silently with a status of zero. .SH "OPTIONS" .IX Header "OPTIONS" .IP "\fB\-h\fR" 4 .IX Item "-h" Prints a command summary. If a command is given along with the \fB\-h\fR flag, only the usage for that command will be given. .IP "\fB\-s\fR" 4 .IX Item "-s" If the command was successful, don't print the output from \fBinnd\fR. .IP "\fB\-t\fR \fItimeout\fR" 4 .IX Item "-t timeout" Specifies how long to wait for the reply from the server, for commands other than \f(CW\*(C`shutdown\*(C'\fR, \f(CW\*(C`xabort\*(C'\fR, and \f(CW\*(C`xexec\*(C'\fR. \fItimeout\fR is the number of seconds to wait. A value of zero says to wait forever, and a value less than zero says not to wait at all but just exit immediately with status zero. When waiting for a reply, \fBctlinnd\fR will check every two minutes to be sure the server is still running, to make it less likely that \fBctlinnd\fR will just hang. .Sp The default is zero, indicating that \fBctlinnd\fR should wait forever. .SH "COMMANDS" .IX Header "COMMANDS" Here is the complete list of supported commands. Note that nearly all commands have a fixed number of arguments. If a parameter may be an empty string, it is still necessary to pass the empty string to \fBctlinnd\fR as an argument (specified in the shell as two adjacent quotes, like \f(CW\*(Aq\*(Aq\fR). .IP "addhist \fImessage-id\fR \fIarrival\fR \fIexpires\fR \fIposted\fR \fItoken\fR" 4 .IX Item "addhist message-id arrival expires posted token" Add an entry to the history database for \fImessage-id\fR. The angle brackets around the message-ID are optional. It should normally be protected from the shell with single quotes. .Sp \&\fIarrival\fR, \fIexpires\fR, and \fIposted\fR should be the number of seconds since epoch and indicate when the article arrived, when it expires (via the Expires: header), and when it was posted (given in the Date: header), respectively. \fIexpires\fR should be \f(CW0\fR if the article doesn't have an Expires: header. \fItoken\fR is the storage \s-1API\s0 token for the article, and may be empty. .Sp This command can only be used while the server is running, and will be rejected if the server is paused or throttled. .IP "allow \fIreason\fR" 4 .IX Item "allow reason" Allow remote connections, reversing a prior \f(CW\*(C`reject\*(C'\fR command. \fIreason\fR must be the same text given to the \f(CW\*(C`reject\*(C'\fR command, or the empty string (which matches any reason). .IP "begin \fIsite\fR" 4 .IX Item "begin site" Begin feeding \fIsite\fR. The server will rescan the \fInewsfeeds\fR file to find the specified site and set up a newsfeed for it. If the site already existed, a \f(CW\*(C`drop\*(C'\fR for that site is done first. This command is forwarded; see \s-1NOTES\s0 below. .IP "cancel \fImessage-id\fR" 4 .IX Item "cancel message-id" Remove the article with the specified message-ID from the local system. This does not generate a cancel control message; it only affects the local system. The angle brackets around the message-ID are optional. It should normally be protected from the shell with single quotes (and not double quotes). For instance: .Sp .Vb 1 \& ctlinnd cancel \*(Aqtest@foo.bar\*(Aq .Ve .Sp Note that the history database is updated with the specified message-ID so if an article with the same message-ID is afterwards received, it will be rejected; it is useful for rejecting spam before it arrives. .Sp If the server is throttled manually, this command causes it to briefly open the history database. If the server is paused or throttled for any other reason, this command is rejected. .IP "changegroup \fIgroup\fR \fIstatus\fR" 4 .IX Item "changegroup group status" The newsgroup \fIgroup\fR is changed so that its status (the fourth field in the \fIactive\fR file) becomes \fIstatus\fR. This may be used to make an existing group moderated or unmoderated, for example. .Sp This command, unlike \f(CW\*(C`newgroup\*(C'\fR or \f(CW\*(C`rmgroup\*(C'\fR, can only be used while the server is running, and will be rejected if the server is paused or throttled. .IP "checkfile" 4 .IX Item "checkfile" Check the syntax of the \fInewsfeeds\fR file and display a message if any errors are found. The details of the errors are reported to syslog. .IP "drop \fIsite\fR" 4 .IX Item "drop site" Flush and drop \fIsite\fR from the server's list of active feeds. This command is forwarded; see \s-1NOTES\s0 below. .IP "feedinfo \fIsite\fR" 4 .IX Item "feedinfo site" Print detailed information about the state of the feed to \fIsite\fR, or brief status about all feeds if \fIsite\fR is the empty string. .IP "flush \fIsite\fR" 4 .IX Item "flush site" Flush the buffer for the specified site. The action taken depends on the type of feed the site receives; see \fInewsfeeds\fR\|(5) for more information. This is useful when the site is being fed by a file and batching is about to start, or to cleanly shut down and respawn a channel feed. If \fIsite\fR is an empty string, all sites are flushed and the \fIactive\fR file and history database are also flushed to disk. This command is forwarded; see \&\s-1NOTES\s0 below. .Sp Flushing the \fBinnfeed\fR channel feed is the recommended method of restarting \fBinnfeed\fR to pick up new configuration. \fBinnd\fR will spawn a new \fBinnfeed\fR process while the old process shuts down cleanly. .IP "flushlogs" 4 .IX Item "flushlogs" Close the news and error log files and rename them to add \f(CW\*(C`.old\*(C'\fR to the file name, then open fresh news and error logs. The \fIactive\fR file and history database are also flushed to disk. Exploder and process channels are reopened so that they properly take into account the new log files. .IP "go \fIreason\fR" 4 .IX Item "go reason" Re-open the history database and start accepting articles again, reversing a previous \f(CW\*(C`pause\*(C'\fR or \f(CW\*(C`throttle\*(C'\fR command. \fIreason\fR must be either the empty string or match the text that was given to the earlier \f(CW\*(C`pause\*(C'\fR or \&\f(CW\*(C`throttle\*(C'\fR command. .Sp If a \f(CW\*(C`reject\*(C'\fR command is in effect, this will also reverse it by doing the equivalent of an \f(CW\*(C`allow\*(C'\fR command if the reason matches \fIreason\fR. Likewise, if a \f(CW\*(C`reserve\*(C'\fR command had been given, this will clear the reservation if \fIreason\fR matches the text that was given to \f(CW\*(C`reserve\*(C'\fR. .Sp The history database is automatically closed on \f(CW\*(C`throttle\*(C'\fR or \f(CW\*(C`pause\*(C'\fR and reopened on \f(CW\*(C`go\*(C'\fR, so the history database can be replaced during the pause or throttle without requiring an explicit \f(CW\*(C`reload\*(C'\fR command. If any other configuration files or the \fIactive\fR file were modified, a \f(CW\*(C`reload\*(C'\fR command should be given to force the server to re-read those files. .Sp If the server throttled itself because it accumulated too many I/O errors, this command will reset the error count. .Sp If \fBinnd\fR was not started with the \fB\-n y\fR flag, this command also does the equivalent of a \f(CW\*(C`readers\*(C'\fR command with \f(CW\*(C`yes\*(C'\fR as the flag and \&\fIreason\fR as the text. .IP "hangup \fIchannel\fR" 4 .IX Item "hangup channel" Close the socket for the specified incoming channel. This may be useful when an incoming connection appears to be hung (although \fBinnd\fR will close idle connections automatically after a timeout). .IP "help [\fIcommand\fR]" 4 .IX Item "help [command]" Print a command summary for all commands, or just \fIcommand\fR if one is specified. This is equivalent to the \fB\-h\fR option. .IP "kill \fIsignal\fR \fIsite\fR" 4 .IX Item "kill signal site" Signal \fIsignal\fR is sent to the process underlying the specified site, which must be a channel or exploder feed. \fIsignal\fR may be a numeric signal number or one of \f(CW\*(C`hup\*(C'\fR, \f(CW\*(C`int\*(C'\fR, or \f(CW\*(C`term\*(C'\fR; case is not significant. .IP "logmode" 4 .IX Item "logmode" Cause the server to log its current operating mode to syslog. .IP "lowmark \fIfile\fR" 4 .IX Item "lowmark file" Reset the low water marks in the \fIactive\fR file based on the contents of \&\fIfile\fR. Each line in \fIfile\fR must be of the form: .Sp .Vb 1 \& group low\-value .Ve .Sp For example: .Sp .Vb 1 \& comp.lang.c++ 243 .Ve .Sp This command is mostly used by \fBnews.daily\fR to update the \fIactive\fR file after nightly expiration. .IP "mode" 4 .IX Item "mode" Print the server's operating mode as a multi-line summary of the parameters and the operating state. The parameters in the output correspond to command-line flags to \fBinnd\fR and give the current settings of those parameters that can be overridden by command-line flags. .IP "name \fIchannel\fR" 4 .IX Item "name channel" Print the name and relevant information for the given incoming or outgoing channel, or for all channels if \fIchannel\fR is an empty string. The response is formatted as: .Sp .Vb 1 \& :::: .Ve .Sp where is the name of the channel, is its number (generally the same as the file descriptor assigned to it), is the idle time for an \s-1NNTP\s0 channel or the process \s-1ID\s0 for a process channel, and is the status for \s-1NNTP\s0 channels. .Sp The type is one of the following values: .Sp .Vb 6 \& control Control channel for ctlinnd \& file An outgoing file feed \& localconn Local channel used by nnrpd and rnews for posting \& nntp NNTP channel for remote connections \& proc The process for a process feed \& remconn The channel that accepts new remote connections .Ve .Sp Channel status indicates whether the channel is paused or not. Nothing is shown unless the channel is paused, in which case \f(CW\*(C`paused\*(C'\fR is shown. A channel will be paused automatically if the number of remote connections for that label in \fIincoming.conf\fR is greater than \fImax-connections\fR within \fIhold-time\fR seconds. .IP "newgroup \fIgroup\fR [\fIstatus\fR [\fIcreator\fR]]" 4 .IX Item "newgroup group [status [creator]]" Create the specified newsgroup. The \fIstatus\fR parameter is the fourth field of the \fIactive\fR file entry, as described in \fIactive\fR\|(5). If it is not an equal sign, only the first character is used. \fIcreator\fR should be the identity of the person creating the group. If the newsgroup already exists, this is equivalent to the \f(CW\*(C`changegroup\*(C'\fR command. .Sp \&\fIcreator\fR, encoded in \s-1UTF\-8\s0 if given, may be omitted; if so, it will default to the newsmaster (as specified at configure time, normally \&\f(CW\*(C`usenet\*(C'\fR). \fIstatus\fR may also be omitted; if so, it will default to \f(CW\*(C`y\*(C'\fR (a normal, unmoderated group). The combination of defaults make it possible to use the text of the Control: header directly (although don't do this without checking the syntactic validity of the header first). .Sp This command can only be done while the server is running or throttled manually. It will update its internal state when a \f(CW\*(C`go\*(C'\fR command is sent. This command updates the \fIactive.times\fR file as well. This command is forwarded; see \s-1NOTES\s0 below. .IP "param \fIletter\fR \fIvalue\fR" 4 .IX Item "param letter value" Change the specified server parameter. \fIletter\fR is the \fBinnd\fR command-line option to set and \fIvalue\fR is the new value. For example: .Sp .Vb 1 \& ctlinnd param i 5 .Ve .Sp would direct the server to allow only five incoming connections. To enable or disable the action of the \fB\-n\fR flag, use \f(CW\*(C`n\*(C'\fR for the letter and \f(CW\*(C`y\*(C'\fR or \f(CW\*(C`n\*(C'\fR, respectively, for the value. .Sp The supported values for \fIletter\fR are \f(CW\*(C`a\*(C'\fR, \f(CW\*(C`c\*(C'\fR, \f(CW\*(C`H\*(C'\fR, \f(CW\*(C`i\*(C'\fR, \f(CW\*(C`l\*(C'\fR, \f(CW\*(C`n\*(C'\fR, \&\f(CW\*(C`o\*(C'\fR, \f(CW\*(C`T\*(C'\fR, \f(CW\*(C`t\*(C'\fR, and \f(CW\*(C`X\*(C'\fR. .IP "pause \fIreason\fR" 4 .IX Item "pause reason" Pause the server so that no incoming articles are accepted. No existing connections are closed, but the history database is closed. This should be used for short-term locks, such as when replacing the history database. If the server was not started with the \fB\-n y\fR flag, this command also does the equivalent of a \f(CW\*(C`readers\*(C'\fR command with \f(CW\*(C`no\*(C'\fR as the flag and \fIreason\fR as the text, encoded in \s-1UTF\-8\s0. .IP "perl \fIflag\fR" 4 .IX Item "perl flag" Enable or disable Perl filtering. This command is only available if \s-1INN\s0 was built with Perl filtering support. If \fIflag\fR starts with \f(CW\*(C`y\*(C'\fR, filtering is enabled; if it starts with \f(CW\*(C`n\*(C'\fR, filtering is disabled. .Sp When filtering is disabled, if the \fIfilter_innd.pl\fR Perl filter defined a function \f(CW\*(C`filter_end\*(C'\fR, it will be called prior to the deactivation of the filter. .IP "python \fIflag\fR" 4 .IX Item "python flag" Enable or disable Python filtering. This command is only available if \s-1INN\s0 was built with Python filtering support. If \fIflag\fR starts with \f(CW\*(C`y\*(C'\fR, filtering is enabled; if it starts with \f(CW\*(C`n\*(C'\fR, filtering is disabled. .IP "readers \fIflag\fR \fItext\fR" 4 .IX Item "readers flag text" Allow or disallow readers. If \fIflag\fR starts with the letter \f(CW\*(C`n\*(C'\fR, then reading is disallowed by causing the server to pass \fItext\fR as the value of the \fB\-r\fR flag to \fBnnrpd\fR. If \fIflag\fR starts with the letter \f(CW\*(C`y\*(C'\fR and \&\fItext\fR is either an empty string or the same string, encoded in \s-1UTF\-8\s0, that was used when reading was disabled, reading will be re-enabled. .Sp This command has no effect if \fBnnrpd\fR is being run separately rather than spawned by \fBinnd\fR. .IP "reject \fIreason\fR" 4 .IX Item "reject reason" Remote connections (those that would not be handed off to \fBnnrpd\fR) are rejected with \fIreason\fR given as the explanation, encoded in \s-1UTF\-8\s0. Existing connections are not closed. .IP "reload \fIwhat\fR \fIreason\fR" 4 .IX Item "reload what reason" Update the in-memory copy of server configuration files. \fIwhat\fR identifies what should be reloaded, and \fIreason\fR is reported to syslog in the message noting the reload. .Sp There is no way to reload \fIinn.conf\fR, \fIstorage.conf\fR, or other configuration files for the storage or overview backends. To pick up changes to those files, use \f(CW\*(C`ctlinnd xexec innd\*(C'\fR to restart \fBinnd\fR. .Sp If \fIwhat\fR is the empty string or the word \f(CW\*(C`all\*(C'\fR, everything is reloaded. If it is the word \f(CW\*(C`history\*(C'\fR, the history database is closed and re-opened. If it is the word \f(CW\*(C`incoming.conf\*(C'\fR, the corresponding file is reloaded. If it is the word \f(CW\*(C`active\*(C'\fR or \&\f(CW\*(C`newsfeeds\*(C'\fR, both the \fIactive\fR and \fInewsfeeds\fR files are reloaded, which will also cause all outgoing feeds to be flushed and restarted. .Sp If \fIwhat\fR is the word \f(CW\*(C`filter.perl\*(C'\fR, the \fIfilter_innd.pl\fR file is reloaded. If the Perl filter defined a function \f(CW\*(C`filter_before_reload\*(C'\fR, it will be called prior to re-reading \fIfilter_innd.pl\fR. If the Perl function \f(CW\*(C`filter_after_reload\*(C'\fR is defined, it will be called after \&\fIfilter_innd.pl\fR has been reloaded. Reloading the Perl filter does not enable filtering if it has been disabled; use \f(CW\*(C`perl y\*(C'\fR to do this instead. \fIstartup_innd.pl\fR cannot be reloaded. This file is not available for reloading unless \s-1INN\s0 was compiled with Perl filtering support. .Sp If \fIwhat\fR is the word \f(CW\*(C`filter.python\*(C'\fR, the \fIfilter_innd.py\fR file is reloaded. If a Python method named \f(CW\*(C`filter_before_reload\*(C'\fR exists, it will be called prior to re-reading \fIfilter_innd.py\fR. If a Python method named \&\f(CW\*(C`_\|_init_\|_\*(C'\fR exists, it will be called after \fIfilter_innd.py\fR has been reloaded. Reloading the Python filter does not enable filtering if it has been disabled; use \f(CW\*(C`python y\*(C'\fR to do this. This file is not available for reloading unless \s-1INN\s0 was compiled with Python filtering support. .IP "renumber \fIgroup\fR" 4 .IX Item "renumber group" Update the low water and high water marks for \fIgroup\fR in the \fIactive\fR file based on the information in the overview database. Regardless of the contents of the overview database, the high water mark will not be decreased. (Decreasing it may cause duplicate article numbers to be assigned after a crash, which can cause serious problems with the tradspool storage method.) If \fIgroup\fR is the empty string, all newsgroups are renumbered. Renumber only works if overview data has been created (if \fIenableoverview\fR is set to true in \fIinn.conf\fR). .IP "renumberlow \fIfile\fR" 4 .IX Item "renumberlow file" Identical to the \f(CW\*(C`lowmark\*(C'\fR command. .IP "reserve \fIreason\fR" 4 .IX Item "reserve reason" Require the next \f(CW\*(C`pause\*(C'\fR or \f(CW\*(C`throttle\*(C'\fR command to use \fIreason\fR as its reason, encoded in \s-1UTF\-8\s0. This reservation is cleared by giving an empty string for the reason. This is used by programs like \fBexpire\fR to coordinate pauses and throttles of the server and avoid trampling on other instances of themselves. .IP "rmgroup \fIgroup\fR" 4 .IX Item "rmgroup group" Remove the specified newsgroup. The group is removed from the \fIactive\fR file and its overview information is purged, making it immediately unavailable to readers. Unlike the \f(CW\*(C`newgroup\*(C'\fR command, this command does not update the \fIactive.times\fR file. .Sp This command can only be done while the server is running or throttled manually. This command is forwarded; see \s-1NOTES\s0 below. .IP "send \fIfeed\fR \fItext\fR" 4 .IX Item "send feed text" The specified \fItext\fR is sent as a control line to the exploder \fIfeed\fR. .IP "shutdown \fIreason\fR" 4 .IX Item "shutdown reason" The server is shut down, with the specified reason recorded in the log and sent to all open connections. It is a good idea to send a \f(CW\*(C`throttle\*(C'\fR command first so that feeds can be shut down more gracefully. .Sp If Perl or Python filtering is compiled in and enabled, certain functions are called at \f(CW\*(C`throttle\*(C'\fR or \f(CW\*(C`shutdown\*(C'\fR (to save filter state to disk, for example). Consult the embedded filter documentation for details. .IP "stathist (off | \fIfilename\fR)" 4 .IX Item "stathist (off | filename)" Enable or disable generation of history performance statistics. If the parameter is \f(CW\*(C`off\*(C'\fR, no statistics are gathered. Otherwise, statistics are written to the specified file. A parser for this file is provided in the contrib tree of the \s-1INN\s0 distribution. .IP "status (off | \fIinterval\fR)" 4 .IX Item "status (off | interval)" Adjust the frequency with which \fBinnd\fR reports status information to syslog. Status reporting is turned off if \f(CW\*(C`off\*(C'\fR or \f(CW0\fR is given as the argument. Otherwise, status will be reported every \fIinterval\fR seconds. See \fIstatus\fR in \fIinn.conf\fR\|(5) for information on how to set the default. .IP "throttle \fIreason\fR" 4 .IX Item "throttle reason" Close all existing incoming connections and outgoing feeds and reject new connections. Close the history database. This should be used for long-term locks or for running a large number of \f(CW\*(C`newgroup\*(C'\fR and \&\f(CW\*(C`rmgroup\*(C'\fR commands without restarting all outgoing feeds between each one. (Note that changing the status of existing newsgroups when the server is throttled cannot be done.) .Sp If the server was not started with the \fB\-n y\fR flag, then this command also does the equivalent of a \f(CW\*(C`readers\*(C'\fR command with \f(CW\*(C`no\*(C'\fR as the flag and \fIreason\fR as the text, encoded in \s-1UTF\-8\s0. .IP "timer (off | \fIinterval\fR)" 4 .IX Item "timer (off | interval)" Adjust the frequency with which \fBinnd\fR reports performance information to syslog. Performance monitoring is turned off if \f(CW\*(C`off\*(C'\fR or \f(CW0\fR is given as the argument. Otherwise, statistics will be reported every \fIinterval\fR seconds to syslog. See \fItimer\fR in \fIinn.conf\fR\|(5) for information on how to set the default. .IP "trace \fIitem\fR \fIflag\fR" 4 .IX Item "trace item flag" Turn tracing on or off for the specified \fIitem\fR. \fIflag\fR should start with the letter \f(CW\*(C`y\*(C'\fR or \f(CW\*(C`n\*(C'\fR to turn tracing on or off, respectively. If \&\fIitem\fR starts with a number, tracing is set up for the specified \fBinnd\fR channel, which must be an incoming \s-1NNTP\s0 feed. If it starts with the letter \f(CW\*(C`i\*(C'\fR, general \fBinnd\fR tracing is turned on or off. If it starts with the letter \f(CW\*(C`n\*(C'\fR, future \fBnnrpd\fR processes spawned by \f(CW\*(C`innd\*(C'\fR will or will not be passed the \fB\-t\fR flag, as appropriate. This will not affect any \fBnnrpd\fR processes already running, or \fBnnrpd\fR processes started by some means other than \fBinnd\fR. .IP "xabort \fIreason\fR" 4 .IX Item "xabort reason" Log the specified \fIreason\fR and then abort. On most systems, this will cause \fBinnd\fR to dump a core file. This is only useful for debugging. .IP "xexec \fIpath\fR" 4 .IX Item "xexec path" Shut down the server, but then rather than exiting, exec \fBinnd\fR with all of its original arguments except for \fB\-r\fR. \fIpath\fR may be either \f(CW\*(C`innd\*(C'\fR or an empty string, both of which are equivalent. Any other value is an error. .Sp This is the easiest way to start a new copy of \fBinnd\fR after upgrading or reload configuration files that can't be reloaded via the \f(CW\*(C`reload\*(C'\fR command. .SH "NOTES" .IX Header "NOTES" In addition to being acted on by the server, certain commands can be forwarded to an appropriate child process. If the site receiving the command is an exploder (such as \fBbuffchan\fR) or a funnel that feeds into an exploder, the command can be forwarded. In this case, the server will send a command line to the exploder that consists of the \fBctlinnd\fR command name. If the site funnels into an exploder that has an asterisk (\f(CW\*(C`*\*(C'\fR) in its \f(CW\*(C`W\*(C'\fR flag (see \fInewsfeeds\fR\|(5) for more information on feed specifications), the site name will be appended to the command; otherwise, no argument is appended. .SH "BUGS" .IX Header "BUGS" \&\fBctlinnd\fR uses Unix domain sockets on most systems to communicate with \&\fBinnd\fR and is therefore limited by whatever maximum packet size the operating system imposes on Unix domain datagrams. This may mean that server replies are limited to 4\ \s-1KB\s0 on some systems. .SH "HISTORY" .IX Header "HISTORY" Written by Rich \f(CW$alz\fR for InterNetNews. Rewritten in \&\s-1POD\s0 by Russ Allbery . .PP \&\f(CW$Id:\fR ctlinnd.pod 9767 2014\-12\-07 21:13:43Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIactive\fR\|(5), \fIactive.times\fR\|(5), \fIbuffchan\fR\|(8), \fIincoming.conf\fR\|(5), \fIinnd\fR\|(8), \&\fIinndcomm\fR\|(3), \fIinn.conf\fR\|(5), \fInewsfeeds\fR\|(5), \fInnrpd\fR\|(8). inn-2.6.0/doc/man/convdate.10000644000175200017520000001571712575023702015140 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "CONVDATE 1" .TH CONVDATE 1 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" convdate \- Convert to/from RFC\ 5322 dates and seconds since epoch .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fBconvdate\fR [\fB\-dhl\fR] [\fB\-c\fR | \fB\-n\fR | \fB\-s\fR] [\fIdate\fR ...] .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\fBconvdate\fR translates the date/time strings given on the command line, outputting the results one to a line. The input can either be a date in \&\s-1RFC\s0\ 5322 format (accepting the variations on that format that \fIinnd\fR\|(8) is willing to accept), or the number of seconds since epoch (if \fB\-c\fR is given). The output is either \fIctime\fR\|(3) results, the number of seconds since epoch, or a Usenet Date: header, depending on the options given. .PP If \fIdate\fR is not given, \fBconvdate\fR outputs the current date. .SH "OPTIONS" .IX Header "OPTIONS" .IP "\fB\-c\fR" 4 .IX Item "-c" Each argument is taken to be the number of seconds since epoch (a time_t) rather than a date. .IP "\fB\-d\fR" 4 .IX Item "-d" Output a valid Usenet Date: header instead of the results of \fIctime\fR\|(3) for each date given on the command line. This is useful for testing the algorithm used to generate Date: headers for local posts. Normally, the date will be in \s-1UTC\s0, but see the \fB\-l\fR option. .IP "\fB\-h\fR" 4 .IX Item "-h" Print usage information and exit. .IP "\fB\-l\fR" 4 .IX Item "-l" Only makes sense in combination with \fB\-d\fR. If given, Date: headers generated will use the local time zone instead of \s-1UTC\s0. .IP "\fB\-n\fR" 4 .IX Item "-n" Rather than outputting the results of \fIctime\fR\|(3) or a Date: header, output each date given as the number of seconds since epoch (a time_t). This option doesn't make sense in combination with \fB\-d\fR. .IP "\fB\-s\fR" 4 .IX Item "-s" Pass each given date to the \s-1RFC\s0\ 5322 date parser and print the results of \&\fIctime\fR\|(3) (or a Date: header if \fB\-d\fR is given). This is the default behavior. .SH "EXAMPLES" .IX Header "EXAMPLES" Most of these examples are taken, with modifications from the original man page dating from 1991 and were run in the \s-1EST/EDT\s0 time zone. .PP .Vb 2 \& % convdate \*(Aq10 Feb 1991 10:00:00 \-0500\*(Aq \& Sun Feb 10 10:00:00 1991 \& \& % convdate \*(Aq13 Dec 91 12:00 EST\*(Aq \*(Aq04 May 1990 0:0:0\*(Aq \& Fri Dec 13 12:00:00 1991 \& Fri May 4 00:00:00 1990 \& \& % convdate \-n \*(Aq10 feb 1991 10:00\*(Aq \*(Aq4 May 90 12:00\*(Aq \& 666198000 \& 641880000 \& \& % convdate \-c 666198000 \& Sun Feb 10 10:00:00 1991 .Ve .PP \&\fIctime\fR\|(3) results are in the local time zone. Compare to: .PP .Vb 2 \& % convdate \-dc 666198000 \& Sun, 10 Feb 1991 15:00:00 +0000 (UTC) \& \& % env TZ=PST8PDT convdate \-dlc 666198000 \& Sun, 10 Feb 1991 07:00:00 \-0800 (PST) \& \& % env TZ=EST5EDT convdate \-dlc 666198000 \& Sun, 10 Feb 1991 10:00:00 \-0500 (EST) .Ve .PP The system library functions generally use the environment variable \s-1TZ\s0 to determine (or at least override) the local time zone. .SH "HISTORY" .IX Header "HISTORY" Written by Rich \f(CW$alz\fR , rewritten and updated by Russ Allbery for the \fB\-d\fR and \fB\-l\fR flags. .PP \&\f(CW$Id:\fR convdate.pod 9767 2014\-12\-07 21:13:43Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIactive.times\fR\|(5). inn-2.6.0/doc/man/dbz.30000644000175200017520000001652312575023702014112 0ustar iuliusiulius.TH DBZ 3 "6 Sep 1997" .BY "INN" .SH NAME dbzinit, dbzfresh, dbzagain, dbzclose, dbzexists, dbzfetch, dbzstore, dbzsync, dbzsize, dbzgetoptions, dbzsetoptions, dbzdebug \- database routines .SH SYNOPSIS .nf .B #include .PP .B "bool dbzinit(const char *base)" .PP .B "bool dbzclose(void)" .PP .B "bool dbzfresh(const char *base, long size)" .PP .B "bool dbzagain(const char *base, const char *oldbase)" .PP .B "bool dbzexists(const HASH key)" .PP .B "off_t dbzfetch(const HASH key)" .B "bool dbzfetch(const HASH key, void *ivalue)" .PP .B "DBZSTORE_RESULT dbzstore(const HASH key, off_t offset)" .B "DBZSTORE_RESULT dbzstore(const HASH key, void *ivalue)" .PP .B "bool dbzsync(void)" .PP .B "long dbzsize(long nentries)" .PP .B "void dbzgetoptions(dbzoptions *opt)" .PP .B "void dbzsetoptions(const dbzoptions opt)" .PP .SH DESCRIPTION These functions provide an indexing system for rapid random access to a text file (the .I base .IR file ). .PP .I Dbz stores offsets into the base text file for rapid retrieval. All retrievals are keyed on a hash value that is generated by the .I HashMessageID() function. .PP .I Dbzinit opens a database, an index into the base file .IR base , consisting of files .IB base .dir , .IB base .index , and .IB base .hash which must already exist. (If the database is new, they should be zero-length files.) Subsequent accesses go to that database until .I dbzclose is called to close the database. .PP .I Dbzfetch searches the database for the specified .IR key , returning the corresponding .I value if any, if .I <\-\-enable\-tagged\-hash at configure> is specified. If .I <\-\-enable\-tagged\-hash at configure> is not specified, it returns true and content of .I ivalue is set. .I Dbzstore stores the .I key - value pair in the database, if .I <\-\-enable\-tagged\-hash at configure> is specified. If .I <\-\-enable\-tagged\-hash at configure> is not specified, it stores the content of .IR ivalue . .I Dbzstore will fail unless the database files are writable. .I Dbzexists will verify whether or not the given hash exists or not. Dbz is optimized for this operation and it may be significantly faster than .IR dbzfetch() . .PP .I Dbzfresh is a variant of .I dbzinit for creating a new database with more control over details. .PP .IR Dbzfresh 's .I size parameter specifies the size of the first hash table within the database, in key-value pairs. Performance will be best if the number of key-value pairs stored in the database does not exceed about 2/3 of .IR size . (The .I dbzsize function, given the expected number of key-value pairs, will suggest a database size that meets these criteria.) Assuming that an .I fseek offset is 4 bytes, the .B .index file will be .I 4 * size bytes. The .B .hash file will be .I DBZ_INTERNAL_HASH_SIZE * size bytes (the .B .dir file is tiny and roughly constant in size) until the number of key-value pairs exceeds about 80% of .IR size . (Nothing awful will happen if the database grows beyond 100% of .IR size , but accesses will slow down quite a bit and the .B .index and .B .hash files will grow somewhat.) .PP .I Dbz stores up to .SM DBZ_INTERNAL_HASH_SIZE bytes of the message-id's hash in the .B .hash file to confirm a hit. This eliminates the need to read the base file to handle collisions. This replaces the tagmask feature in previous dbz releases. .PP A .I size of ``0'' given to .I dbzfresh is synonymous with the local default; the normal default is suitable for tables of 5,000,000 key-value pairs. Calling .I dbzinit(name) with the empty name is equivalent to calling .IR dbzfresh(name,\ 0) . .PP When databases are regenerated periodically, as in news, it is simplest to pick the parameters for a new database based on the old one. This also permits some memory of past sizes of the old database, so that a new database size can be chosen to cover expected fluctuations. .I Dbzagain is a variant of .I dbzinit for creating a new database as a new generation of an old database. The database files for .I oldbase must exist. .I Dbzagain is equivalent to calling .I dbzfresh with a .I size equal to the result of applying .I dbzsize to the largest number of entries in the .I oldbase database and its previous 10 generations. .PP When many accesses are being done by the same program, .I dbz is massively faster if its first hash table is in memory. If the ``pag_incore'' flag is set to INCORE_MEM, an attempt is made to read the table in when the database is opened, and .I dbzclose writes it out to disk again (if it was read successfully and has been modified). .I Dbzsetoptions can be used to set the .B pag_incore and .B exists_incore flag to new value which should be ``INCORE_NO'', ``INCORE_MEM'', or \&``INCORE_MMAP'' for the .B .hash and .B .index files separately; this does not affect the status of a database that has already been opened. The default is ``INCORE_NO'' for the .B .index file and ``INCORE_MMAP'' for the .B .hash file. The attempt to read the table in may fail due to memory shortage; in this case .I dbz fails with an error. .IR Store s to an in-memory database are not (in general) written out to the file until .IR dbzclose or .IR dbzsync , so if robustness in the presence of crashes or concurrent accesses is crucial, in-memory databases should probably be avoided or the .B writethrough option should be set to ``true''; .PP If the .B nonblock option is ``true'', then writes to the .B .hash and .B .index files will be done using non-blocking I/O. This can be significantly faster if your platform supports non-blocking I/O with files. .PP .I Dbzsync causes all buffers etc. to be flushed out to the files. It is typically used as a precaution against crashes or concurrent accesses when a .IR dbz -using process will be running for a long time. It is a somewhat expensive operation, especially for an in-memory database. .PP Concurrent reading of databases is fairly safe, but there is no (inter)locking, so concurrent updating is not. .PP An open database occupies three .I stdio streams and two file descriptors; Memory consumption is negligible (except for .I stdio buffers) except for in-memory databases. .SH SEE ALSO dbm(3), history(5), libinn(3) .SH DIAGNOSTICS Functions returning .I bool values return ``true'' for success, ``false'' for failure. Functions returning .I off_t values return a value with .I \-1 for failure. .I Dbzinit attempts to have .I errno set plausibly on return, but otherwise this is not guaranteed. An .I errno of .B EDOM from .I dbzinit indicates that the database did not appear to be in .I dbz format. .PP If .SM DBZTEST is defined at compile-time then a .I main() function will be included. This will do performance tests and integrity test. .SH HISTORY The original .I dbz was written by Jon Zeeff (zeeff@b-tech.ann-arbor.mi.us). Later contributions by David Butler and Mark Moraes. Extensive reworking, including this documentation, by Henry Spencer (henry@zoo.toronto.edu) as part of the C News project. MD5 code borrowed from RSA. Extensive reworking to remove backwards compatibility and to add hashes into dbz files by Clayton O'Neill (coneill@oneill.net) .SH BUGS .PP Unlike .IR dbm , .I dbz will refuse to .I dbzstore with a key already in the database. The user is responsible for avoiding this. .PP The RFC5322 case mapper implements only a first approximation to the hideously-complex RFC5322 case rules. .PP .I Dbz no longer tries to be call-compatible with .I dbm in any way. inn-2.6.0/doc/man/INN__Utils__Shlock.3pm0000644000175200017520000001567312575023702017302 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "INN::Utils::Shlock 3pm" .TH INN::Utils::Shlock 3pm "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" INN::Utils::Shlock \- Wrapper around the shlock program .SH "DESCRIPTION" .IX Header "DESCRIPTION" This Perl module wraps the \fIshlock\fR\|(1) program so that it can easily be used. Calling \fBshlock\fR is more portable than using \fIflock\fR\|(2) and its corresponding Perl function because this function does not work as expected on all existing systems. .PP See the \fIshlock\fR\|(1) documentation for more information. .PP Using INN::Utils::Shlock is straight-forward: .PP .Vb 2 \& use lib \*(Aq/lib/perl\*(Aq; \& use INN::Utils::Shlock; \& \& my $lockfile = "myprogram.LOCK"; \& \& # Acquire a lock. \& INN::Utils::Shlock::lock($lockfile); \& \& # Do whatever you want. The lock prevents concurrent accesses. \& \& # Unlock. \& INN::Utils::Shlock::unlock($lockfile); .Ve .PP These two functions return \f(CW1\fR on success, \f(CW0\fR on failure. For example, the success of (un)locking can be checked as: .PP .Vb 1 \& INN::Utils::Shlock::lock($lockfile) or die "cannot create lock file"; .Ve .PP or: .PP .Vb 3 \& if (! INN::Utils::Shlock::lock($lockfile, 4)) { \& die "giving up after 4 unsuccessful attempts to create lock file"; \& } .Ve .PP Instead of calling \f(CW\*(C`unlock(\f(CIlockfile\f(CW)\*(C'\fR, the \f(CW\*(C`releaselocks()\*(C'\fR function can be called. It removes any leftover locks, which is useful when several different locks are used. Another possible use is to call it in an \s-1END\s0 code block: .PP .Vb 4 \& END { \& # In case we bail out, while holding a lock. \& INN::Utils::Shlock::releaselocks(); \& } .Ve .SH "INTERFACE" .IX Header "INTERFACE" .IP "lock(\fIlockfile\fR)" 4 .IX Item "lock(lockfile)" Tries to create a lock file named \fIlockfile\fR. .Sp This function returns \f(CW1\fR on success, \f(CW0\fR on failure. .IP "lock(\fIlockfile\fR, \fItries\fR)" 4 .IX Item "lock(lockfile, tries)" Tries to create a lock file named \fIlockfile\fR. If it fails, locking attempts are repeated once every 2 seconds for at most \fItries\fR times (including the first unsuccessful attempt). .Sp This function returns \f(CW1\fR on success, \f(CW0\fR on failure. .IP "lock(\fIlockfile\fR, \fItries\fR, \fIdelay\fR)" 4 .IX Item "lock(lockfile, tries, delay)" Tries to create a lock file named \fIlockfile\fR. If it fails, locking attempts are repeated once every \fIdelay\fR seconds for at most \fItries\fR times (including the first unsuccessful attempt). .Sp Note that \f(CW\*(C`lock(\f(CIlockfile\f(CW)\*(C'\fR is equivalent to \f(CW\*(C`lock(\f(CIlockfile\f(CW, 1, 2)\*(C'\fR. .Sp This function returns \f(CW1\fR on success, \f(CW0\fR on failure. .IP "\fIreleaselocks()\fR" 4 .IX Item "releaselocks()" Removes all the lock files previously created by calling the \f(CW\*(C`lock(\f(CIlockfile\f(CW)\*(C'\fR function. .Sp This function returns the number of removed lock files. .IP "unlock(\fIlockfile\fR)" 4 .IX Item "unlock(lockfile)" Removes the file named \fIlockfile\fR. .Sp This function returns \f(CW1\fR on success, \f(CW0\fR on failure. .SH "HISTORY" .IX Header "HISTORY" Documentation written by Julien Elie for InterNetNews. .PP \&\f(CW$Id:\fR Shlock.pm.in 9408 2012\-05\-28 18:42:29Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIperl\fR\|(1), \fIshlock\fR\|(1). inn-2.6.0/doc/man/simpleftp.10000644000175200017520000001250312575023702015326 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "SIMPLEFTP 1" .TH SIMPLEFTP 1 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" simpleftp \- Rudimentary FTP client .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fBsimpleftp\fR \fIurl\fR [...] .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\fBsimpleftp\fR is a Perl script that provides basic support for fetching files with \s-1FTP\s0 in a batch oriented fashion. It takes one or more \&\s-1FTP\s0 URLs on the command line. The file(s) will be retrieved from the remote server and placed in the current directory with the same basename as on the remote; e.g., is stored as \fIactive.gz\fR in the current directory. .PP The script properly understands usernames, passwords and ports specified as follows: .PP .Vb 1 \& ftp://user:password@host:port/path/file .Ve .SH "BUGS" .IX Header "BUGS" \&\fBsimpleftp\fR is an extremely poor substitute for more complete programs like the freely available \fBwget\fR or \fBncftp\fR utilities. It was written only to provide elementary support in \s-1INN\s0 for non-interactive fetching of the files in or without requiring administrators to install yet another package. Its shortcomings as a general purpose program are too numerous to mention, but one that stands out is that downloaded files by \fBsimpleftp\fR override existing files with the same name in the local directory. .SH "HISTORY" .IX Header "HISTORY" Tossed off by David C Lawrence for InterNetNews. Rewritten to use \f(CW\*(C`Net::FTP\*(C'\fR by Julien Elie. .PP \&\f(CW$Id:\fR simpleftp.pod 8357 2009\-02\-27 17:56:00Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIactsync\fR\|(8). inn-2.6.0/doc/man/ninpaths.80000644000175200017520000002476712575023702015175 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "NINPATHS 8" .TH NINPATHS 8 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" ninpaths \- Report Usenet Path: statistics (new inpaths) .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fBninpaths\fR \fB\-p\fR \fB\-d\fR \fIdumpfile\fR .PP \&\fBninpaths\fR \fB\-r\fR \fIsite\fR \fB\-u\fR \fIdumpfile\fR [\fB\-u\fR \fIdumpfile\fR ...] \fB\-v\fR \&\fIlevel\fR .SH "DESCRIPTION" .IX Header "DESCRIPTION" This is an efficient and space-saving \fBinpaths\fR reporting program. It works as follows: you feed it the Path: lines via an \s-1INN\s0 channel feed or some other similar method, and from time to time the program writes all its internal counters accumulated so far to a dump file. Another instance of the program picks up all the dump files, adds them up and formats them into the report. The purpose of the final report is to summarize the frequency of occurrence of sites in the Path: headers of articles. .PP Some central sites accumulate the Path: data from many news servers running this program or one like it, and then report statistics on the most frequently seen news servers in Usenet article Path: lines. The \fBsendinpaths\fR script can be run daily to mail the accumulated statistics to such a site and remove the old dump files. .PP You can get a working setup by doing the following: .IP "1." 4 Create a directory at \fIpathlog\fR/path (replacing \fIpathlog\fR here and in all steps that follow with the full path to your \s-1INN\s0 log directory). Do not change the name of the \f(CW\*(C`path\*(C'\fR subdirectory because it is used by \fBsendinpaths\fR. .IP "2." 4 Set up a channel feed using an entry like: .Sp .Vb 1 \& inpaths!:*:Tc,WP:/ninpaths \-p \-d /path/inpaths.%d .Ve .Sp if your version of \s-1INN\s0 supports \f(CW\*(C`WP\*(C'\fR (2.0 and later all do). Replace with the full path to your \s-1INN\s0 binaries directory, and with the full path to your \s-1INN\s0 log directory. .Sp Note that the naming convention of the generated inpaths dump files should not be changed. \fBsendinpaths\fR explicitly searches files whose name starts with \f(CW\*(C`inpaths.\*(C'\fR in the /path directory. .IP "3." 4 Run the following command to start logging these statistics: .Sp .Vb 1 \& ctlinnd reload newsfeeds \*(Aqinpaths feed setup\*(Aq .Ve .IP "4." 4 Enter into your news user crontab these two lines: .Sp .Vb 2 \& 6 6 * * * /ctlinnd flush inpaths! \& 10 6 * * * /sendinpaths .Ve .Sp (the actual time doesn't matter). This will force \fBninpaths\fR to generate a dump file once a day. Then, a few minutes later, \fBsendinpaths\fR collects the dumps, makes a report, sends the collected statistics, and deletes the old dumps. .Sp Note that you can manually generate a report without mailing it, and without deleting processed dump files, with \f(CW\*(C`sendinpaths \-n\*(C'\fR. Another useful command is \f(CW\*(C`sendinpaths \-c\*(C'\fR so as to receive a copy of the e\-mail sent by \fBsendinpaths\fR and therefore make sure that everything is properly set. .IP "5." 4 In a couple of days, check that your daily statistics properly appear in . .SH "OPTIONS" .IX Header "OPTIONS" .IP "\fB\-d\fR \fIdumpfile\fR" 4 .IX Item "-d dumpfile" Save dumps in \fIdumpfile\fR. Any \f(CW%d\fR in \fIdumpfile\fR will be replaced with the current system time when the dump is made. This option should be used with \fB\-p\fR. .Sp The format of these dump files is described below. .IP "\fB\-p\fR" 4 .IX Item "-p" Read Path: lines from standard input. .IP "\fB\-r\fR \fIsite\fR" 4 .IX Item "-r site" Generate a report for \fIsite\fR. Generally \fIsite\fR should be the value of \&\fIpathhost\fR from \fIinn.conf\fR. .IP "\fB\-u\fR \fIdumpfile\fR" 4 .IX Item "-u dumpfile" Read data from \fIdumpfile\fR. This option can be repeated to read data from multiple dump files. .IP "\fB\-v\fR \fIlevel\fR" 4 .IX Item "-v level" Set the verbosity level of the report. Valid values for \fIlevel\fR are \f(CW0\fR, \&\f(CW1\fR, and \f(CW2\fR, with \f(CW2\fR being the default. .SH "DUMP FILE FORMAT" .IX Header "DUMP FILE FORMAT" The format of the generated dump files is: .PP .Vb 5 \& !!NINP \& ... \& !!NLREC \& :!,:!, ... \& !!NEND .Ve .PP where times are \s-1UNIX\s0 timestamps. Then, \fInb-sites\fR records follow. Each record is separated by a space or a new line, and consists of a host name \fIsite_n\fR followed by a number of appearances \fIcount_n\fR. The number of processed Path: header lines is \fInb-articles\fR. .PP Afterwards, \fInb-relations\fR relations follow. In 3.0.x versions, the relations are separated by a space or a new line, and their syntax is \&\f(CW\*(C`\f(CIsite_a\f(CW!\f(CIsite_b\f(CW!\f(CIcount_ab\f(CW\*(C'\fR where \fIsite_a\fR and \fIsite_b\fR are numbers of the site records starting at 0. .PP In 3.1.x versions, the relations begin with a colon and are separated by either nothing or a new line. Their syntax is \f(CW\*(C`:\f(CIsite_a\f(CW!\f(CIsite_b\f(CW,\f(CIcount_ab\f(CW\*(C'\fR with the same meaning as in previous versions. The count can be omitted when it is \f(CW1\fR. More than two sites can be specified in the relation (\f(CW\*(C`:\f(CIsite_a\f(CW!\f(CIsite_b\f(CW!\f(CIsite_c\f(CW,\f(CIcount_abc\f(CW\*(C'\fR). .PP For instance: .PP .Vb 6 \& !!NINP 3.1.1 1302944821 1302944838 5 2 1302944826 \& newsgate.cistron.nl 1 news.trigofacile.com 2 news.ecp.fr 2 usenet.stanford.edu 1 \& bleachbot 1 \& !!NLREC \& :3!2:2!1,2:4!0:0!2 \& !!NLEND 4 .Ve .PP where the two processed Path: headers are: .PP .Vb 2 \& Path: news.trigofacile.com!news.ecp.fr!usenet.stanford.edu!not\-for\-mail \& Path: news.trigofacile.com!news.ecp.fr!newsgate.cistron.nl!bleachbot!not\-for\-mail .Ve .SH "NOTES" .IX Header "NOTES" If your \s-1INN\s0 doesn't have the \f(CW\*(C`WP\*(C'\fR feed flag (1.5 does not, 1.6 and 1.7 do, 2.0 and later all do), use the following \fInewsfeeds\fR entry: .PP .Vb 1 \& inpaths!:*:Tc,WH:/ginpaths .Ve .PP where \fBginpaths\fR is the following script: .PP .Vb 2 \& #!/bin/sh \& exec egrep \*(Aq^Path: \*(Aq | /ninpaths \-p \-d /path/inpaths.%d .Ve .PP replacing and as above. .SH "HISTORY" .IX Header "HISTORY" This is a slightly modified version of Olaf Titz's original \fBninpaths\fR program, which is posted to alt.sources and kept on his \s-1WWW\s0 archive under . .PP The idea and some implementation details for \fBninpaths\fR come from the original \fBinpaths\fR program, but most of the code has been rewritten for clarity. This program is in the public domain. .PP \&\f(CW$Id:\fR ninpaths.pod 9383 2011\-12\-25 20:56:10Z iulius $ .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fInewsfeeds\fR\|(5), \fIsendinpaths\fR\|(8). inn-2.6.0/doc/man/libinnhist.30000644000175200017520000004416312575023702015477 0ustar iuliusiulius.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "libinnhist 3" .TH libinnhist 3 "2015-09-12" "INN 2.6.0" "InterNetNews Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" his \- routines for managing INN history .SH "SYNOPSIS" .IX Header "SYNOPSIS" .Vb 1 \& #include \& \& struct history; \& struct token; \& \& struct histstats { \& int hitpos; \& int hitneg; \& int misses; \& int dne; \& }; \& \& #define HIS_RDONLY ... \& #define HIS_RDWR ... \& #define HIS_CREAT ... \& #define HIS_ONDISK ... \& #define HIS_INCORE ... \& #define HIS_MMAP ... \& \& enum { \& HISCTLG_PATH, \& HISCTLS_PATH, \& HISCTLS_SYNCCOUNT, \& HISCTLS_NPAIRS, \& HISCTLS_IGNOREOLD, \& HISCTLS_STATINTERVAL \& }; \& \& struct history *HISopen(const char *path, const char *method, int flags); \& \& bool HISclose(struct history *history); \& \& bool HISsync(struct history *history); \& \& void HISsetcache(struct history *history, size_t size); \& \& bool HISlookup(struct history *history, const char *key, time_t *arrived, time_t *posted, time_t *expires, TOKEN *token); \& \& bool HIScheck(struct history *history, const char *key); \& \& bool HISwrite(struct history *history, const char *key, time_t arrived, time_t posted, time_t expires, const TOKEN *token); \& \& bool HISremember(struct history *history, const char *key, time_t arrived, time_t posted); \& \& bool HISreplace(struct history *history, const char *key, time_t arrived, time_t posted, time_t expires, const TOKEN *token); \& \& bool HISexpire(struct history *history, const char *path, const char *reason, bool writing, void *cookie, time_t threshold, bool (*exists)(void *cookie, time_t arrived, time_t posted, time_t expires, const TOKEN *token)); \& \& bool HISwalk(struct history *history, const char *reason, void *cookie, bool (*callback)(void *cookie, time_t arrived, time_t posted, time_t expires, const TOKEN *token)); \& \& struct histstats HISstats(struct history *history); \& \& const char *HISerror(struct history *history); \& \& bool HISctl(struct history *history, int request, void *val); .Ve .SH "DESCRIPTION" .IX Header "DESCRIPTION" These functions provide access to the \s-1INN\s0 history database. They maintain key/value pairs in an opaque database whilst providing for expiry of outdated information. .PP The history structure is an opaque handle returned from HISopen. .PP The \fBHISopen\fR function opens the history file designated by \fIpath\fR using the mode \fIflags\fR using the specified \fImethod\fR. \fIflags\fR may be \&\fB\s-1HIS_RDONLY\s0\fR to indicate that read-only access to the history database is desired, or \fB\s-1HIS_RDWR\s0\fR for read/write access. History methods are defined at build time; the history method currently available is \*(L"hisv6\*(R". On success a newly initialised history handle is returned, or \fB\s-1NULL\s0\fR on failure. .PP \&\fB\s-1HIS_ONDISK\s0\fR, \fB\s-1HIS_INCORE\s0\fR and \fB\s-1HIS_MMAP\s0\fR may be logically ORed into \fIflags\fR to provide a hint to the underlying history manager as to how it should handle its data files; \fB\s-1HIS_ONDISK\s0\fR indicates that the caller would like as much of the data to be kept on disk (and out of memory), \fB\s-1HIS_INCORE\s0\fR indicates that the data files should be kept in main memory where possible and \fB\s-1HIS_MMAP\s0\fR that the files should be \&\fImmap()\fRed into the processes address space. \fB\s-1HIS_INCORE\s0\fR is typically used where a mass rebuild of the history database is being performed; the underlying history manager may assume that the caller will call \&\fBHISsync\fR() to sync the data files to disk. .PP The \fB\s-1HIS_CREAT\s0\fR flag indicates that the history database should be initialised as new; if any options which affect creation of the database need to be set an anonymous history handle should be created by calling \fBHISopen\fR with \fIpath\fR set to \fB\s-1NULL\s0\fR, any options set using \fBHISctl\fR, then the database opened by calling \fBHISctl\fR with \&\fB\s-1HISCTLS_PATH\s0\fR. .PP The \fBHISclose\fR function closes the handle \fIhistory\fR and deallocates any resources associated with it. It returns \fBfalse\fR on failure or \&\fBtrue\fR on success. .PP The \fBHISsync\fR function synchronises any outstanding transactions associated with \fIhistory\fR to disk. .PP \&\fBHISsetcache\fR associates a cache used for speeding up HIScheck with \&\fIhistory\fR. The cache will occupy approximately \fIsize\fR bytes. .PP \&\fBHISlookup\fR retrieves a token from \fIhistory\fR based on the passed \&\fIkey\fR (normally the Message-ID). If no entry with an associated token can be found, \fBHISlookup\fR will return \fBfalse\fR. If a token is found \&\fIarrived\fR, \fIexpires\fR, and \fIposted\fR are filled in with the message arrival, expiry, and posting times respectively (or zero, if the time component is not available), in addition to \fItoken\fR being set to the retrieved token and a function return value of \fBtrue\fR. Any of arrived, expires, posted, or token may be \fB\s-1NULL\s0\fR in which case that component is not returned to the caller, without affecting the return value. .PP \&\fBHIScheck\fR checks the database \fIhistory\fR for \fIkey\fR (normally the Message-ID); if \fIkey\fR has previously been set via \fBHISwrite\fR, \&\fBHIScheck\fR returns \fBtrue\fR, else \fBfalse\fR. .PP \&\fBHISwrite\fR writes a new entry to the database \fIhistory\fR associated with \fIkey\fR. \fIarrived\fR, \fIposted\fR, and \fIexpired\fR specify the arrival, posting, and expiry time respectively; \fIposted\fR and \fIexpired\fR may be specifed as <= 0 in which case that component shall be treated as absent in the database. \fItoken\fR is associated with the specified \&\fIkey\fR. \fBHISwrite\fR returns \fBtrue\fR on success, or \fBfalse\fR on failure. The behaviour when \fIkey\fR is not unique with respect to the existing entries in \fIhistory\fR is unspecified. .PP \&\fBHISremember\fR writes a new entry to the database \fIhistory\fR associated with \fIkey\fR, merely remembering that this \fIkey\fR has been seen, together with its arrival time \fIarrived\fR and also its posting time \fIposted\fR, if known. (Otherwise, its posting time may be specified as <= 0 in case it is absent.) \fBHISremember\fR returns \fBtrue\fR on success, or \fBfalse\fR on failure. The behaviour when \fIkey\fR is not unique with respect to the existing entries in \fIhistory\fR is unspecified. .PP \&\fBHISreplace\fR replaces an existing entry in the database \fIhistory\fR, associated with \fIkey\fR. \fIarrived\fR, \fIposted\fR, \fIexpired\fR specify the arrival, posting and expiry time respectively; \fIposted\fR and \&\fIexpired\fR may be specifed as <= 0 in which case that component shall be treated as absent in the database. \fItoken\fR is associated with the specified \fIkey\fR; if \fB\s-1NULL\s0\fR then the history database merely remembers that this \fIkey\fR has been seen, together with its arrival time. \fBHISreplace\fR returns \fBtrue\fR on success, or \fBfalse\fR on failure. .PP \&\fBHISexpire\fR expires the history database associated with \fIhistory\fR, creating a new, replacement, database in the same location if \fIpath\fR is \fB\s-1NULL\s0\fR, or in \fIpath\fR if not \fB\s-1NULL\s0\fR; if \fIpath\fR is not \fB\s-1NULL\s0\fR then the replacement of the old history database with the new one is assumed to be performed out of band by the caller. The \fIwriting\fR flag is normally passed as \fBtrue\fR, if you wish to inhibit writing of the new database (and so merely see the callbacks), \fIwriting\fR may be set \&\fBfalse\fR. .PP If the underlying history mechanism needs to pause the server, the \&\fIreason\fR string is used as the argument to the `ctlinnd pause' command, and as such the server should be reserved by the caller prior to calling \fBHISexpire\fR; if the caller wishes to inhibit pausing of the server, passing \fB\s-1NULL\s0\fR will achieve this. If \fIreason\fR is not \&\fB\s-1NULL\s0\fR, then on successful return from \fBHISexpire\fR the server will be left paused and the caller should unpause it. .PP The history database is scanned and entries with an associated storage token are passed to the discrimination function \fIexists\fR. .PP If \fIexists\fR() returns \fBfalse\fR it indicates that stored entity associated with token is no longer available (or no longer required), and therefore the associated history entry may be expired once it meets the \fIthreshold\fR constraint. If \fIexists\fR() returns \fBtrue\fR the entry is kept as-is in the newly expired history database. .PP The \fIexists\fR function is passed the arrival, posting and expiry times, in addition to the token associated with the entry. Note that posting and/or expiry may be zero, but that the token will never be \&\fB\s-1NULL\s0\fR (such entries are handled solely via the threshold mechanism). The storage token passed to the discrimination function may be updated if required (for example, as might be needed by a hierachical storage management implementation). .PP Entries in the database with a posting time less than \fIthreshold\fR with no token associated with them are deleted from the database. In case the posting time is unknown, the arrival time is used instead. .PP The parameter \fIcookie\fR is passed to the discrimination function, and may be used for any purpose required by the caller. .PP If the discrimination function attempts to access the underlying database (for read or write) during the callback, the behaviour is unspecified. .PP \&\fBHISwalk\fR provides an iteration function for the specified \fIhistory\fR database. For every entry in the history database, \fIcallback\fR is invoked, passing the \fIcookie\fR, arrival, posting, and expiry times, in addition to the token associated with the entry. If the \fIcallback\fR() returns \fBfalse\fR the iteration is aborted and \fBHISwalk\fR returns \&\fBfalse\fR to the caller. .PP To process the entire database in the presence of a running server, \&\fIreason\fR may be passed; if this argument is not \fB\s-1NULL\s0\fR, it is used as an an argument to the `ctlinnd (reserve|pause|go)' commands. If \&\fIreason\fR is \fB\s-1NULL\s0\fR and the server is running, the behaviour of \&\fBHISwalk\fR is undefined. .PP If the callback function attempts to access the underlying database during the callback, the behaviour is unspecified. .PP \&\fBHISstats\fR returns statistics on the history cache mechanism; given a handle \fIhistory\fR, the return value is a \fIstruct histstats\fR detailing: .ie n .IP """hitpos""" 4 .el .IP "\f(CWhitpos\fR" 4 .IX Item "hitpos" The number of times an item was found directly in the cache and known to exist in the underlying history manager. .ie n .IP """hitneg""" 4 .el .IP "\f(CWhitneg\fR" 4 .IX Item "hitneg" The number of times an item was found directly in the cache and known not to exist in the underlying history manager. .ie n .IP """misses""" 4 .el .IP "\f(CWmisses\fR" 4 .IX Item "misses" The number of times an item was not found directly in the cache, but on retrieval from the underlying history manager was found to exist. .ie n .IP """dne""" 4 .el .IP "\f(CWdne\fR" 4 .IX Item "dne" The number of times an item was not found directly in the cache, but on retrieval from the underlying history manager was found not to exist. .PP Note that the history cache is only checked by \fBHIScheck\fR and only affected by \fBHIScheck\fR, \fBHISwrite\fR, \fBHISremember\fR and \&\fBHISreplace\fR. Following a call to \fBHISstats\fR the history statistics associated with \fIhistory\fR are cleared. .PP \&\fBHISerror\fR returns a string describing the most recent error associated with \fIhistory\fR; the format and content of these strings is history manager dependent. Note that on setting an error, the history \&\s-1API\s0 will call the \fBwarn\fR function from \fIlibinn\fR\|(3). .PP \&\fBHISctl\fR provides a control interface to the underlying history manager. The \fIrequest\fR argument determines the type of the request and the meaning of the \fIval\fR argument. The values for \fIrequest\fR are: .ie n .IP """HISCTLG_PATH"" (const char **)" 4 .el .IP "\f(CWHISCTLG_PATH\fR (const char **)" 4 .IX Item "HISCTLG_PATH (const char **)" Get the base file path which the history handle represents. \fIval\fR should be a pointer to a location of type \fBconst char *\fR. The result must not later be passed to \fIfree\fR\|(3). .ie n .IP """HISCTLS_PATH"" (const char *)" 4 .el .IP "\f(CWHISCTLS_PATH\fR (const char *)" 4 .IX Item "HISCTLS_PATH (const char *)" Set the base file path which this history handle should use; typically this is used after an anonymous handle has been created using \&\fBHISopen(\s-1NULL\s0, ...)\fR. \fIval\fR should be a value of type \fBconst char *\fR and will be copied before being stored internally. .ie n .IP """HISCTLS_SYNCCOUNT"" (size_t *)" 4 .el .IP "\f(CWHISCTLS_SYNCCOUNT\fR (size_t *)" 4 .IX Item "HISCTLS_SYNCCOUNT (size_t *)" Set an upper bound on how many history operations may be pending in core before being synced to permanent storage; \fB0\fR indicates unlimited. \fIval\fR should be a pointer to a value of type \fBsize_t\fR and will not be modified by the call. .ie n .IP """HISCTLS_NPAIRS"" (size_t *)" 4 .el .IP "\f(CWHISCTLS_NPAIRS\fR (size_t *)" 4 .IX Item "HISCTLS_NPAIRS (size_t *)" Set a hint to the to the underlying history manager as to how many entries there are expected to be in the history database; \fB0\fR indicates that an automatic or default sizing should be made. \fIval\fR should be a pointer to a value of type \fBsize_t\fR and will not be modified by the call. .ie n .IP """HISCTLS_IGNOREOLD"" (bool *)" 4 .el .IP "\f(CWHISCTLS_IGNOREOLD\fR (bool *)" 4 .IX Item "HISCTLS_IGNOREOLD (bool *)" Instruct the underlying history manager to ignore existing database when creating new ones; typically this option may be set to \fBtrue\fR if the administrator believes that the existing history database is corrupt and that ignoring it may help. \fIval\fR should be a pointer to a value of type \fBbool\fR and will not be modified by the call. .ie n .IP """HISCTLS_STATINTERVAL"" (time_t *)" 4 .el .IP "\f(CWHISCTLS_STATINTERVAL\fR (time_t *)" 4 .IX Item "HISCTLS_STATINTERVAL (time_t *)" For the history v6 and tagged hash managers, set the interval, in seconds, between \fIstat\fR\|(2)s of the history files checking for replaced files (as happens during expire); this option is typically used by \&\fInnrpd\fR\|(8) like applications. \fIval\fR should be a pointer to a value of type \fBtime_t\fR and will not be modified by the call. .SH "HISTORY" .IX Header "HISTORY" Written by Alex Kiernan for InterNetNews\ 2.4.0. .PP \&\f(CW$Id:\fR libinnhist.pod 9073 2010\-05\-31 19:00:23Z iulius $ inn-2.6.0/doc/IPv6-info0000644000175200017520000000364112575023702014131 0ustar iuliusiuliusNotes about IPv6 support in INN: This is $Revision: 9694 $, dated $Date: 2014-09-17 13:55:11 -0700 (Wed, 17 Sep 2014) $. Note that starting from INN 2.6.0, IPv6 is unconditionally enabled, when available. The --enable-ipv6 configure flag no longer exists. This document contains some notes about the status of IPv6 support in INN (see also the parts of the code marked FIXME): Some comments as of the completion of the original patch: Date: Wed, 13 Feb 2002 00:10:59 -0500 (EST) From: Nathan Lutchansky To: Jeffrey M. Vinocur Subject: IPv6 patch notes The IPv6 patch is based directly on Marco d'Itri's IPv6 patch of 2001-03-01 that was posted last year to the inn-workers list. The patch applied fairly cleanly to a working copy from 2002-02-04, and the resulting tree was used as the basis for my work. Modifications by Marco and myself were made so that if IPv6 support is not explicitly enabled with the --enable-ipv6 flag to the configure script, the old networking code will be used. Hopefully, nobody will notice any problems with the default configuration, although some changes have been made to data structures even when IPv6 is disabled. The original patch added IPv6 support to innd and inndstart, and the auth_pass program. I have added support to nnrpd, innfeed, and the ident auth program. There is no IPv6 support for imapfeed and other auxiliary programs like the radius auth backend. Marco's patch made use of several preprocessor defines for configuration but the defines were hand-coded, so I added the corresponding tests the the configuration script. I make no guarantees that the configure script will catch all possible non-portable behavior; the IPv6 API standardization process has left quite a wake of incompatible API implementations over the years. -Nathan inn-2.6.0/doc/config-syntax0000644000175200017520000002267712575023702015217 0ustar iuliusiulius$Id: config-syntax 8566 2009-08-15 07:02:37Z iulius $ This file documents the standardized syntax for INN configuration files. This is the syntax that the parsing code in libinn will understand and the syntax towards which all configuration files should move. The basic structure of a configuration file is a tree of groups. Each group has a type and an optional tag, and may contain zero or more parameter settings, an association of a name with a value. All parameter names and group types are simple case-sensitive strings composed of printable ASCII characters and not containing whitespace or any of the characters "\:;{}[]<>" or the double-quote. A group may contain another group (and in fact the top level of the file can be thought of as a top-level group that isn't allowed to contain parameter settings). Supported parameter values are booleans, integers, real numbers, strings, and lists of strings. The basic syntax looks like: group-type tag { parameter: value parameter: [ string string ... ] # ... group-type tag { # ... } } Tags are strings, with the same syntax as a string value for a parameter; they are optional and may be omitted. A tag can be thought of as the name of a particular group, whereas the says what that group is intended to specify and there may be many groups with the same type. The second parameter example above has as its value a list. The square brackets are part of the syntax of the configuration file; lists are enclosed in square brackets and the elements are space-separated. As seen above, groups may be nested. Multiple occurrences of the same parameter in the parameter section of a group is an error. In practice, the second parameter will take precedent, but an error will be reported when such a configuration file is parsed. Parameter values inherit. In other words, the structure: first { first-parameter: 1 second { second-parameter: 1 third { third-parameter: 1 } } another "tag" { } } is parsed into a tree that looks like: +-------+ +--------+ +-------+ | first |-+-| second |---| third | +-------+ | +--------+ +-------+ | | +---------+ +-| another | +---------+ where each box is a group. The type of the group is given in the box; none of these groups have tags except for the only group of type "another", which has the tag "tag". The group of type "third" has three parameters set, namely "third-parameter" (set in the group itself), "second-parameter" (inherited from the group of type "second"), and "first-parameter" (inherited from "first" by "second" and then from "second" by "third"). The practical meaning of this is that enclosing groups can be used to set default values for a set of subgroups. For example, consider the following configuration that defines three peers of a news server and newsgroups they're allowed to send: peer news1.example.com { newsgroups: * } peer news2.example.com { newsgroups: * } peer news3.example.com { newsgroups: * } This could instead be written as: group { newsgroups: * peer news1.example.com { } peer news2.example.com { } peer news3.example.com { } } or as: peer news1.example.com { newsgroups: * peer news2.example.com { } peer news3.example.com { } } and for a client program that only cares about the defined list of peers, these three structures would be entirely equivalent; all questions about what parameters are defined in the peer groups would have identical answers either way this configuration was written. Note that the second form above is preferred as a matter of style to the third, since otherwise it's tempting to derive some significance from the nesting structure of the peer groups. Also note that in the second example above, the enclosing group *must* have a type other than "peer"; to see why, consider the program that asks the configuration parser for a list of all defined peer groups and uses the resulting list to build some internal data structures. If the enclosing group in the second example above had been of type peer, there would be four peer groups instead of three and one of them wouldn't have a tag, probably provoking an error message. Boolean values may be given as yes, true, or on, or as no, false, or off. Integers must be between -2,147,483,648 and +2,147,483,647 inclusive (the same as the minimums for a C99 signed long). Floating point numbers must be between 0 and 1e37 in absolute magnitude (the same as the minimums for a C99 double) and can safely expect eight digits of precision. Strings are optionally enclosed in double quotes, and must be quoted if they contain any whitespace, double-quote, or any characters in the set "\:;[]{}<>". Escape sequences in strings (sequences beginning with \) are parsed the same as they are in C. Strings can be continued on multiple lines by ending each line in a backslash, and the newline is not considered part of such a continued string (to embed a literal newline in a string, use \n). Lists of strings are delimited by [] and consist of whitespace-separated strings, which must follow the same quoting rules as all other strings. Group tags are also strings and follow the same quoting rules. There are two more bits of syntax. Normally, parameters must be separated by newlines, but for convenience it's possible to put multiple parameters on the same line separated by semicolons: parameter: value; parameter: value Finally, the body of a group may be defined in a separate file. To do this, rather than writing the body of the group enclosed in {}, instead give the file name in <>: group tag (The filename is also a string and may be double-quoted if necessary, but since file names rarely contain any of the excluded characters it's rarely necessary.) Here is the (almost) complete ABNF for the configuration file syntax. The syntax is per RFC 2234. First the basic syntax elements and possible parameter values: newline = %d13 / %d10 / %d13.10 ; Any of CR, LF, or CRLF are interpreted ; as a newline. comment = *WSP "#" *(WSP / VCHAR / %x8A-FF) newline WHITE = WSP / newline [comment] boolean = "yes" / "on" / "true" / "no" / "off" / "false" integer = ["-"] 1*DIGIT real-number = ["-"] 1*DIGIT "." 1*DIGIT [ "e" ["-"] 1*DIGIT ] non-special = %x21 / %x23-39 / %x3D / %x3F-5A / %x5E-7A / %x7C / %x7E / %x8A-FF ; All VCHAR except "\:;<>[]{} quoted-string = DQUOTE 1*(WSP / VCHAR / %x8A-FF) DQUOTE ; DQUOTE within the quoted string must be ; written as 0x5C.22 (\"), and backslash ; sequences are interpreted as in C ; strings. string = 1*non-special / quoted-string list-body = string *( 1*WHITE string ) list = "[" *WHITE [ list-body ] *WHITE "]" Now the general structure: parameter-name = 1*non-special parameter-value = boolean / integer / real-number / string / list parameter = parameter-name ":" 1*WSP parameter-value parameter-list = parameter [ *WHITE (";" / newline) *WHITE parameter ] group-list = group *( *WHITE group ) group-body = parameter-list [ *WHITE newline *WHITE group-list ] / group-list group-file = string group-contents = "{" *WHITE [ group-body ] *WHITE "}" / "<" group-file ">" group-type = 1*non-special group-tag = string group-name = group-type [ 1*WHITE group-tag ] group = group-name 1*WHITE group-contents file = *WHITE *( group *WHITE ) One implication of this grammar is that any line outside a quoted string that begins with "#", optionally preceded by whitespace, is regarded as a comment and discarded. The line must begin with "#" (and optional whitespace); comments at the end of lines aren't permitted. "#" has no special significance in quoted strings, even if it's at the beginning of a line. Note that comments cannot be continued to the next line in any way; each comment line must begin with "#". It's unclear the best thing to do with high-bit characters (both literal characters with value > 0x7F in a configuration file and characters with such values created in quoted strings with \, \x, \u, or \U). In the long term, INN should move towards assuming UTF-8 everywhere, as this is the direction that all of the news standards are heading, but in the interim various non-Unicode character sets are in widespread use and there must be some way of encoding those values in INN configuration files (so that things like the default Organization header value can be set appropriately). As a compromise, the configuration parser will pass unaltered any literal characters with value > 0x7F to the calling application, and \ and \x escapes will generate eight-bit characters in the strings (and therefore cannot be used to generate UTF-8 strings containing code points greater than U+007F). \u and \U, in contrast, will generate characters encoded in UTF-8. inn-2.6.0/doc/GPL0000644000175200017520000004363412575023702013044 0ustar iuliusiulius[ Please note that the only portions of INN covered by this license are those files explicitly noted as being under the GPL in LICENSE. It is a requirement of the GPL, however, that a copy of it be distributed with software licensed under it, and some stand-alone programs that are distributed with INN are covered under the GPL. ] GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. inn-2.6.0/doc/history-innfeed0000644000175200017520000004162712575023702015531 0ustar iuliusiuliusThis is version 1.0 of the INN feeder program `innfeed.' It is written in ANSI C and tries to be POSIX.1 compliant. This software was originally written and maintained by James Brister and is now part of INN. The features of it are: 1. Handles the STREAM extenstion to NNTP. 2. Will open multiple connections to the remote host to parallel feed. 3. Will handle multiple remote hosts. 4. Will tear down idle connections. 5. Runs as a channel/funnel feed from INN, or by reading a funnel file. 6. Will stop issuing CHECK commands and go straight to TAKETHIS if the remote responds affermatively to enough CHECKs. 7. It will go back to issuing CHECKs if enough TAKETHIS commands fail. Changes for 0.10.1 1. Config file inclusion now works via the syntax: $INCLUDE pathname Config files can be included up to a nesting depth of 10. Line numbers and file names are not properly reported yet on errors when includes are used, though. 2. Signal handling is tidied up a bit. If your compiler doesn't support the ``volatile'' keyword, then see the comment in sysconfig.h. 3. If you have a stdio library that hash limit on open files lower then the process limit for open plain files (all flavours of SunOS), then a new config file variable ``stdio-fdmax'' can be used to give that upper bound. When set, all new network connections will be limited to file descriptors over this value, leaving the lower file descriptors free for stdio. See innfeed.conf(5) for more details. Remember that the config file value overrides any compiled in value. Changes for 0.10 1. A major change has been made to the config file. The new format is quite extensible and will let new data items be added in the future without changing the basic format. There's a new option ``-C'' (for ``check'') that will make innfeed read the config file and report on any errors and then exit. This will let you verify things before locking them into a newsfeeds file entry. A program has been added ``innfeed-convcfg'' that will read your old config off the command line (or stdin), and will write a new version to stdout. The new config file structure permits non-peer-specific items to be declared (like the location of the status file, or whether to wrap the generated status file in HTML). This is part of the included sample: use-mmap: true news-spool: /var/news/spool/articles backlog-directory: /var/news/spool/innfeed pid-file: innfeed.pid status-file: innfeed.status gen-html: false log-file: innfeed.log backlog-factor: 1.10 connection-stats: false max-reconnect-time: 3600 so only option you'll probably need now is the ``-c'' option to locate the config file, and as this is also compiled in, you may not even need that. See the innfeed.conf(5) man page for more details on config file structure. 2. The backlog file handling is changed slightly: - The .output file is always kept open (until rotation time). - The .output file is allowed to grow for at least 30 seconds (or the value defined by the key backlog-rotate-period in the config file). This prevents thrashing of backlog files. - The hand-prepared file is checked for only every 600 seconds maximum (or the value defined by the key backlog-new), not every time the files are rotated. - The stating of the three backlog files is reduced dramatically. 3. Signal handling is changed so that they are more synchronous with other activity. This should stop the frequent core-dumps that occured when running in funnel file mode and sending SIGTERM or SIGALRM. 4. A bug related to zero-length articles was fixed. They will now be logged. 5. More information is in the innfeed.status file, including the reasons return by the remote when it is throttled. 6. SIGEMT is now a trigger for closing and reopening all the backlog files. If you have scripts that need to fool with the backlogs, then have the scripts move the backlogs out of the way and then send the SIGEMT. Changes for 0.9.3 1. If your system supports mmap() *and* if you have your articles stored on disk in NNTP-ready format (rare), then you can have innfeed mmap article data to save on memory (thanks to Dave Lawrence). There is an important issue with this: if you try to have innfeed handle too many articles (by running many connections and/or high max-check values in innfeed.conf) at once, then your system (not your process) may run out of free vnodes (global file descriptors), as a vnode is used as long as the file is mmaped. So be careful. If your articles are not in NNTP format then this will be noticed and the article will be pulled into memory for fixing up (and then immediately munmap'd). You can disable use of MMAP if you've built it in by using the '-M' flag. I tried mixing mmap'ing and articles not in NNTP format and it was a real performance loss. I'll be trying it differently later. 2. If innfeed is asked to send an article to a host it knows nothing about, or which it cannot acquire the required lock for (which causes the "ME locked cannot setup peer ..." and "ME unconfigured peer" syslog messages), then innfeed will deposit the article information into a file matching the pattern innfeed-dropped.* in the backlog directory (TAPE_DIRECTORY in config.h). This file will not be processed in any manner -- it's up to you to decide what to do with it (wait for innfeed to exit before doing anything with it, or send innfeed a SIGHUP to get it to reread its config file, which will roll this file). 4. The output backlog files will now be kept below a certain byte limit. This happens via the ``-e'' option. If, after writing to an output file, the new length is bigger than the given limit (multiplied by a fudge factor defined in config.h -- default of 1.10) then the file will be shrunk down to this size (or slightly smaller to find the end of line boundary). The front of the file will be removed to do this. This means lost articles for the entries removed. 3. A SIGHUP will make the config be reloaded. 4. The .checkpoint files have been dropped in favour of scribbling the offset into the input file itself. 5. When the process exits normally a final syslog entry covering all of the peers over the life of the process is written. It looks like: Jan 12 15:51:53 data innfeed.tester[24189]: ME global seconds 2472 offered 43820 accepted 10506 refused 31168 rejected 1773 missing 39 6. SIGALARM now rolls the input file, rather than the log file. This is useful in funnel file mode when you move the input file and tell innd to flush it, then send innfeed the signal. 7. The location of the pid file, config file and status file, can now be relative, in which case they're relative to the backlog directory. 8. stdin stdout and stderr are initialized properly when innfeed is started by a process that has closed them. 9. Various values in config.h have changed (paths to look more like values used in inn 1.5 and others to support point #7 above more easily.) 10. procbatch.pl can now 'require' innshellvars.pl that comes with 1.5. The default is not to. You nead to do a one line tweak if you want it to. The defaults in procbatch.pl match the new defaults of innfeed. 11. Core files that get generated on purpose will be done so in CORE_DIRECTORY (as defined in config.h), if that is defined to a pathname. If CORE_DIRECTORY is defined to be NULL (the default now), then the core will be generated in the backlog directory (as possibly modified by the '-b' option). Changes for 0.9.2 1. Now includes David Lawrence's patches to handle funnel files. 2. EAGAIN errors on read and writes are caught and dealt with (of interest to Solaris `victims'). 3. It is now much faster at servicing the file descriptor attached to innd. This means it is faster at recognising it has been flushed and at dropping connections. This means fewer conflicts with new innfeeds starting before the old one has finished up. It is still a good net-citizen and it finishes the commands already started, so the fast response is only as fast as your slowest peer, but it no longer tries to send everything it had queued internally, and locks get released much quicker. 4. Includes Michael Hucka's patch to make the innfeed.status output neater. 5. Includes Andy Vasilyev's HTML-in-innfeed.status patch (but you have to enable it in config.h). 6. Added a '-a' (top of news spool) and a '-p' (pid file path) option. Changes for 0.9 1. Format of innfeed.conf file changed slightly (for per-peer streaming specs). 2. Including Greg Patten's innlog.pl (taken from ftp://loose.apana.org.au/pub/local/innlog/innlog.pl) 3. Added Christophe Wolfhugel's patch to permit a per-peer restriction on using streaming. 4. More robust handling of peers that return bad responses (no long just calling abort). Changes for 0.8.5 1. Massive syslog messages cleanup courtesy of kre. 2. The innlog.awk-patch hash been dropped from the distribution until the new syslog messages are dealt with. 3. State machine more robust in the face of unexpected responses from remote. Connection gets torn down and bad response's logged. 4. The fixed timers (article reception timeout, read timeout, and flush timeout) are all adjusted by up to +/-10% so that things aren't quite so synchronised. 5. The innfeed.status file has been expanded and reformatted to include more information. Changes for 0.8.4 1. A change in the handing off of articles to connections in order to encourage connections that were opened due to activity spikes, to close down sooner. 2. The backlog files are no longer concatenated together at process startup, but the .input is simply used if it exists, and if not then the hand-dropped file is used first and the .output file second. 3. The innfeed.status is no longer updated by a innfeed that is in its death-throws. 4. Specifically catch the 480 response code from NNRPD when we try to give it an IHAVE. 5. The connection reestablishment time gets properly increased when the connection fails to go through (up to and including the reading of the banner message). 6. Bug fix that occasionally had articles sit in a connection and never get processed. 7. Bug fix in the counter of number of sleeping connections. 8. Bug fix in config file parsing. 9. Procbatch.pl included. Changes for version 0.8.1 1. various bug fixes. 2. core files generated by ASSERT are (possibly) put in a seperate directory to ease debugging are Changes for version 0.8 1. The implicit state machine in the Connection objects has been made explicit. 2. Various bug fixes. Changes for version 0.7.1 1. Pulled the source to inet_addr.c from the bind distribution. (Solaris and some others don't have it). Changes for version 0.7 1. The backlog file mechanism has been completely reworked. There are now only two backlog files: one for output and on for input. The output file becomes the input file when the input file is exhausted. 2. Much less strenuous use of writev. Solaris and other sv4r machines have an amazingly low value for the maximum number of iovecs that can be passed into writev. 3. Proper connection cleanup (QUIT issued) at shutdown. 4. A lock is taken out in the backlog directory for each peer. To feed the same peer from two different instances of innfeed (with a batch file for example), then you must use another directory. 5. Creating a file in the backlog directory with the same name as the peer, the that file will be used next time backlog files are processed. Its format must be: pathname msgid where pathname is absolute, or relative to the top of the news spool. 6. More command line options. 7. Dynamic peer creation. If the proper command line option is used (-y) and innfeed is to told to feed a peer that it doesn't have in its config file, then it will create a new binding to the new peer. The ip name must be the same as the peername, i.e. if innd tells innfeed about a peer fooBarBat, then gethostbyname("fooBarBat") better work. 8. Connections will be periodically torn down (1 hour is the default), even if they're active, so that non-innd peers don't have problems with their history files being kept open for too long. 9. The input backlog files are checkpointed every 30 seconds so that a crash while processing a large backlog doesn't require starting over from the beginning. Changes for version 0.6 1. Logging of spooling of backlog only happens once per stats-logging period. Bugs/Problems/Notes etc: 1. There is no graceful handling of file descriptor exhaustion. 2. If the following situation occurs: - articles on disk are NOT in NNTP-ready format. - innfeed was built with HAVE_MMAP defined. - memory usage is higher than expected try running innfeed with the '-M' flag (or recompiling with HAVE_MMAP undefined). Solaris, and possibly other SVR4 machines, waste a lot of swap space. 3. On the stats logging the 'offered' may not equal the sum of the other fields. This is because the stats at that moment were generated while waiting for a response to a command to come back. Innfeed considers an article ``offered'' when it sends the command, not when it gets a response back. Perhaps this should change. 4. If all the Connections for a peer are idle and a new backlog file is dropped in by hand, then it will not be picked up until the next time it gets an article from innd for that peer. This will be fixed in a later version, but for now, if the peer is likely to be idle for a long time, then flush the process. 5. Adding a backlog file by hand does not cause extra Connections to be automatically created, only the existing Connections will use the file. If the extra load requires new Connections to be built when innd delivers new articles for tranmission, then they too will use the file, but this a side effect and not a direct consequence. This means if you want to run in '-x' mode, then make sure your config file entry states the correct number of initial connections, as they're all the Connections that will be created. 6. If '-x' is used and the config file has an entry for a peer that has no batch file to process, then innfeed will not exit after all batch files have been finished--it will just site there idle. 7. If the remote is running inn and only has you in the nnrp.access file, then innfeed will end up talking to nnrpd. Innfeed will try every 30 seconds to reconnect to a server that will accept IHAVE commands. i.e. there is no exponential back of retry attempt. This is because the connection is considered good once the MODE STREAM command has been accepted or rejected (and nnrpd rejects it). Futures: 1. Innfeed will eventually take exploder commands. 2. The config file will be revamped to allow for more global options etc and run-time configuration. Too much is compile-time dependant at the moment. 3. The connection retry time will get more sophisticated to catch problems like the nnrpd issue mentioned above. 4. Include the number of takesthis/check/ihave commands issued in the log entries. 5. Heaps more stuff requested that's buried in my mail folders. Any compliments, complaints, requests, porting issues etc. should go to . Many thanks to the following people for extra help (above and beyond the call of duty) with pateches, beta testing and/or suggestions: Christophe Wolfhugel Robert Elz Russell Vincent Paul Vixie Stephen Stuart John T. Stapleton Alan Barrett Lee McLoughlin Dan Ellis Katsuhiro Kondou Marc G. Fournier Steven Bauer Richard Perini Per Hedeland Clayton O'Neill Dave Pascoe Michael Handler Petr Lampa David Lawrence Don Lewis Landon Curt Noll If I've forgotten anybody, please let me know. Thanks also to ISC for sponsoring this work. inn-2.6.0/doc/Makefile0000644000175200017520000000200012575023702014115 0ustar iuliusiulius## $Id: Makefile 9081 2010-07-03 17:16:54Z eagle $ ## ## The only target that this Makefile need support is install. Everything ## else is a null target (and the top level Makefile shouldn't even attempt ## them in this directory). include ../Makefile.global top = .. TOPDOCS = CONTRIBUTORS HACKING INSTALL LICENSE NEWS README TODO DOCS = GPL config-design config-semantics config-syntax \ external-auth history history-innfeed hook-perl hook-python \ sample-control DIRS = pod man all: cd pod && $(MAKE) all || exit 1 ; cd .. profiled: depend: clean clobber distclean maintclean: cd pod && $(MAKE) $@ || exit 1 ; cd .. install: for F in $(TOPDOCS) ; do \ $(CP_RPUB) $(top)/$$F $D$(PATHDOC)/$$F ; \ done for F in $(DOCS) ; do \ $(CP_RPUB) $$F $D$(PATHDOC)/$$F ; \ done if [ -r $(top)/README.snapshot ] ; then \ $(CP_RPUB) $(top)/README.snapshot $D$(PATHDOC)/README.snapshot ; \ fi cd man && $(MAKE) install || exit 1 ; cd .. bootstrap: cd pod && $(MAKE) bootstrap || exit 1 ; cd .. inn-2.6.0/doc/hook-perl0000644000175200017520000010611412575023702014313 0ustar iuliusiuliusINN Perl Filtering and Authentication Support This file documents INN's built-in support for Perl filtering and reader authentication. The code is based very heavily on work by Christophe Wolfhugel , and his work was in turn inspired by the existing TCL support. Please send any bug reports to , not to Christophe, as the code has been modified heavily since he originally wrote it. The Perl filtering support is described in more detail below. Basically, it allows you to supply a Perl function that is invoked on every article received by innd from a peer (the innd filter) or by nnrpd from a reader (the nnrpd filter). This function can decide whether to accept or reject the article, and can optionally do other, more complicated processing (such as add history entries, cancel articles, spool local posts into a holding area, or even modify the headers of locally submitted posts). The Perl authentication hooks allow you to replace or supplement the readers.conf mechanism used by nnrpd. For Perl filtering support, you need to have Perl version 5.004 or newer. Earlier versions of Perl will fail with a link error at compilation time. should have the latest Perl version. To enable Perl support, you have to specify --with-perl when you run configure. See INSTALL for more information. The innd Perl Filter When innd starts, it first loads the file INN_PATH_PERL_STARTUP_INND (defined in include/inn/paths.h, by default startup_innd.pl) and then loads the file INN_PATH_PERL_FILTER_INND (also defined in include/inn/paths.h, by default filter_innd.pl). Both of these files must be located in the directory specified by *pathfilter* in inn.conf. The default directory for filter code can be specified at configure time by giving the flag --with-filter-dir to configure. INN doesn't care what Perl functions you define in which files. The only thing that's different about the two files is when they're loaded. startup_innd.pl is loaded only once, when innd first starts, and is never reloaded as long as innd is running. Any modifications to that file won't be noticed by innd; only stopping and restarting innd can cause it to be reloaded. filter_innd.pl, on the other hand, can be reloaded on command (with "ctlinnd reload filter.perl 'reason'"). Whenever filter_innd.pl is loaded, including the first time at innd startup, the Perl function filter_before_reload() is called before it's reloaded and the function filter_after_reload() is called after it's reloaded (if the functions exist). Additionally, any code in either startup_innd.pl or filter_innd.pl at the top level (in other words, not inside a sub { }) is automatically executed by Perl when the files are loaded. This allows one to do things like write out filter statistics whenever the filter is reloaded, load a cache into memory, flush cached data to disk, or other similar operations that should only happen at particular times or with manual intervention. Remember, any code not inside functions in startup_innd.pl is executed when that file is loaded, and it's loaded only once when innd first starts. That makes it the ideal place to put initialization code that should only run once, or code to load data that was preserved on disk across a stop and restart of innd (perhaps using filter_mode() -- see below). As mentioned above, "ctlinnd reload filter.perl 'reason'" (or "ctlinnd reload all 'reason'") will cause filter_innd.pl to be reloaded. If the function filter_art() is defined after the file has been reloaded, filtering is turned on. Otherwise, filtering is turned off. (Note that due to the way Perl stores functions, once you've defined filter_art(), you can't undefine it just by deleting it from the file and reloading the filter. You'll need to replace it with an empty sub.) The Perl function filter_art() is the heart of a Perl filter. Whenever an article is received from a peer, via either IHAVE or TAKETHIS, filter_art() is called if Perl filtering is turned on. It receives no arguments, and should return a single scalar value. That value should be the empty string to indicate that INN should accept the article, or some rejection message to indicate that the article should be rejected (make sure that such a message is properly encoded in UTF-8 so as to comply with the NNTP protocol). filter_art() has access to a global hash named %hdr, which contains all of the standard headers present in the article and their values. The standard headers are: Also-Control, Approved, Archive, Archived-At, Bytes, Cancel-Key, Cancel-Lock, Comments, Content-Base, Content-Disposition, Content-Transfer-Encoding, Content-Type, Control, Date, Date-Received, Distribution, Expires, Face, Followup-To, From, In-Reply-To, Injection-Date, Injection-Info, Keywords, Lines, List-ID, Message-ID, MIME-Version, Newsgroups, NNTP-Posting-Date, NNTP-Posting-Host, NNTP-Posting-Path, Organization, Original-Sender, Originator, Path, Posted, Posting-Version, Received, References, Relay-Version, Reply-To, Sender, Subject, Summary, Supersedes, User-Agent, X-Auth, X-Auth-Sender, X-Canceled-By, X-Cancelled-By, X-Complaints-To, X-Face, X-HTTP-UserAgent, X-HTTP-Via, X-Mailer, X-Modbot, X-Modtrace, X-Newsposter, X-Newsreader, X-No-Archive, X-Original-Message-ID, X-Original-NNTP-Posting-Host, X-Original-Trace, X-Originating-IP, X-PGP-Key, X-PGP-Sig, X-Poster-Trace, X-Postfilter, X-Proxy-User, X-Submissions-To, X-Trace, X-Usenet-Provider, X-User-ID, Xref. Note that all the above headers are as they arrived, not modified by your INN (especially, the Xref: header, if present, is the one of the remote site which sent you the article, and not yours). For example, the Newsgroups: header of the article is accessible inside the Perl filter as $hdr{'Newsgroups'}. In addition, $hdr{'__BODY__'} will contain the full body of the article and $hdr{'__LINES__'} will contain the number of lines in the body of the article. The contents of the %hdr hash for a typical article may therefore look something like this: %hdr = (Subject => 'MAKE MONEY FAST!!', From => 'Joe Spamer ', Date => '10 Sep 1996 15:32:28 UTC', Newsgroups => 'alt.test', Path => 'news.example.com!not-for-mail', Organization => 'Spammers Anonymous', Lines => '5', Distribution => 'usa', 'Message-ID' => '<6.20232.842369548@example.com>', __BODY__ => 'Send five dollars to ISC, c/o ...', __LINES__ => 5 ); Note that the value of $hdr{Lines} is the contents of the Lines: header of the article and may bear no resemblence to the actual length of the article. $hdr{__LINES__} is the line count calculated by INN, and is guaranteed to be accurate. The %hdr hash should not be modified inside filter_art(). Instead, if any of the contents need to be modified temporarily during filtering (smashing case, for example), copy them into a separate variable first and perform the modifications on the copy. Currently, $hdr{__BODY__} is the only data that will cause your filter to die if you modify it, but in the future other keys may also contain live data. Modifying live INN data in Perl will hopefully only cause a fatal exception in your Perl code that disables Perl filtering until you fix it, but it's possible for it to cause article munging or even core dumps in INN. So always, always make a copy first. As mentioned above, if filter_art() returns the empty string (''), the article is accepted. Note that this must be the empty string, not 0 or undef. Otherwise, the article is rejected, and whatever scalar filter_art() returns (typically a string) will be taken as the reason why the article was rejected (make sure that such a message is properly encoded in UTF-8 so as to comply with the NNTP protocol). This reason will be returned to the remote peer as well as logged to the news logs. (innreport, in its nightly report, will summarize the number of articles rejected by the Perl filter and include a count of how many articles were rejected with each reason string.) One other type of filtering is also supported. If Perl filtering is turned on and the Perl function filter_messageid() is defined, that function will be called for each message-ID received from a peer (via either CHECK, IHAVE or TAKETHIS). The function receives a single argument, the message-ID, and like filter_art() should return an empty string to accept the article or an error string to refuse the article (make sure that such a message is properly encoded in UTF-8 so as to comply with the NNTP protocol). This function is called before any history lookups and for every article offered to innd with CHECK or IHAVE (before the actual article is sent), or with TAKETHIS (after the actual article is sent). Accordingly, the message-ID is the only information it has about the article (the %hdr hash will be empty). This code would sit in a performance-critical hot path in a typical server, and therefore should be as fast as possible, but it can do things like refuse articles from certain hosts or cancels for already rejected articles (if they follow the $alz convention) without having to take the network bandwidth hit of accepting the entire article first. Finally, whenever ctlinnd throttle, ctlinnd pause, or ctlinnd go is run, the Perl function filter_mode() is called if it exists. It receives no arguments and returns no value, but it has access to a global hash %mode that contains three values: Mode The current server mode (throttled, paused, or running) NewMode The new mode the server is going to reason The reason that was given to ctlinnd Mode and NewMode will be set to one of running, paused, throttled, or shutdown, except that Mode can never be shutdown. If NewMode is shutdown, innd is being shut down immediately after the filter hooks return. One possible use for this function is to save filter state across a restart of innd by dumping state to disk when NewMode is shutdown and then reloading it when innd restarts (possibly by startup_innd.pl). The state of the Perl interpreter in which all of these Perl functions run is preserved over the lifetime of innd. In other words, it's permissible for the Perl code to create its own global Perl variables, data structures, saved state, and the like, and all of that will be available to filter_art() and filter_messageid() each time they're called. The only variable INN fiddles with (or pays any attention to at all) is %hdr, which is cleared after each call to filter_art(). Perl filtering can be turned off with "ctlinnd perl n" and back on again with "ctlinnd perl y". Perl filtering is turned off automatically if loading of the filter fails or if the filter code returns any sort of a fatal error (either due to Perl itself or due to a "die" in the Perl code). When filtering is disabled and a "filter_end()" function is available, it will be called prior to the deactivation of the filter. Supported innd Callbacks innd makes seven functions available to any of its embedded Perl code. Those are: INN::addhist(*messageid*, *arrival*, *articledate*, *expire*, *paths*) Adds *messageid* to the history database. All of the arguments except the first one are optional; the times default to the current time and the paths field defaults to the empty string. (For those unfamiliar with the fields of a history(5) database entry, the *arrival* is normally the time at which the server accepts the article, the *articledate* is from the Date header of the article, the *expire* is from the Expires header of the article, and the *paths* field is the storage API token. All three times as measured as a time_t since the epoch.) Returns true on success, false otherwise. INN::article(*messageid*) Returns the full article (as a simple string) identified by *messageid*, or undef if it isn't found. Each line will end with a simple \n, but leading periods may still be doubled if the article is stored in wire format. INN::cancel(*messageid*) Cancels *messageid*. (This is equivalent to "ctlinnd cancel"; it cancels the message on the local server, but doesn't post a cancel message or do anything else that affects anything other than the local server.) Returns true on success, false otherwise. INN::filesfor(*messageid*) Returns the *paths* field of the history entry for the given *messageid*. This will be the storage API token for the message. If *messageid* isn't found in the history database, returns undef. INN::havehist(*messageid*) Looks up *messageid* in the history database and returns true if it's found, false otherwise. INN::head(*messageid*) Returns the header (as a simple string) of the article identified by *messageid*, or undef if it isn't found. Each line will end with a simple \n (in other words, regardless of the format of article storage, the returned string won't be in wire format). INN::newsgroup(*newsgroup*) Returns the status of *newsgroup* (the last field of the active file entry for that newsgroup). See active(5) for a description of the possible values and their meanings (the most common are "y" for an unmoderated group and "m" for a moderated group). If *newsgroup* isn't in the active file, returns "undef". These functions can only be used from inside the innd Perl filter; they're not available in the nnrpd filter. Common Callbacks The following additional function is available from inside filters embedded in innd, and is also available from filters embedded in nnrpd (see below): INN::syslog(level, message) Logs a message via syslog(3). This is quite a bit more reliable and portable than trying to use "Sys::Syslog" from inside the Perl filter. Only the first character of the level argument matters; the valid letters are the first letters of ALERT, CRIT, ERR, WARNING, NOTICE, INFO, and DEBUG (case-insensitive) and specify the priority at which the message is logged. If a level that doesn't match any of those levels is given, the default priority level is LOG_NOTICE. The second argument is the message to log; it will be prefixed by "filter: " and logged to syslog with facility LOG_NEWS. The nnrpd Posting Filter Whenever Perl support is needed in nnrpd, it first loads the file INN_PATH_PERL_FILTER_NNRPD (defined in include/inn/paths.h, by default filter_nnrpd.pl). This file must be located in the directory specified by *pathfilter* in inn.conf. The default directory for filter code can be specified at configure time by giving the flag --with-filter-dir to configure. If filter_nnrpd.pl loads successfully and defines the Perl function filter_post(), Perl filtering is turned on. Otherwise, it's turned off. If filter_post() ever returns a fatal error (either from Perl or from a "die" in the Perl code), Perl filtering is turned off for the life of that nnrpd process and any further posts made during that session won't go through the filter. Besides, if filter_nnrpd.pl defines a "filter_end()" function, it will be called prior to the deactivation of the filter. While Perl filtering is on, every article received by nnrpd via the POST command is passed to the filter_post() Perl function before it is passed to INN (or mailed to the moderator of a moderated newsgroup). If filter_post() returns an empty string (''), the article is accepted and normal processing of it continues. Otherwise, the article is rejected and the string returned by filter_post() is returned to the client as the error message (with some exceptions; see below). filter_post() has access to a global hash %attributes which contains information about the connection as follows: $attributes{'hostname'} will contain the hostname (or the IP address if it does not resolve) of the client machine, $attributes{'ipaddress'} will contain its IP address (as a string), $attributes{'port'} will contain the client port (as an integer), $attributes{'interface'} contains the hostname of the interface the client connected on, $attributes{'intipaddr'} contains the IP address (as a string) of the interface the client connected on, and $attributes{'intport'} contains the port (as an integer) on the interface the client connected on. filter_post() also has access to a global hash %hdr, which contains all of the headers of the article. (Unlike the innd Perl filter, %hdr for the nnrpd Perl filter contains *all* of the headers, not just the standard ones. If any of the headers are duplicated, though, %hdr will contain only the value of the last occurrence of the header, whereas it is the first occurrence with the innd Perl filter. nnrpd will reject the article before the filter runs if any of the standard headers are duplicated.) It also has access to the full body of the article in the variable $body, and if the poster authenticated via AUTHINFO (or if either Perl authentication or a readers.conf authentication method is used and produces user information), it has access to the authenticated username of the poster in the variable $user. Unlike the innd Perl filter, the nnrpd Perl filter can modify the %hdr hash. In fact, if the Perl variable $modify_headers is set to true after filter_post() returns, the contents of the %hdr hash will be written back to the article replacing the original headers. filter_post() can therefore make any modifications it wishes to the headers and those modifications will be reflected in the article as it's finally posted. The article body cannot be modified in this way; any changes to $body will just be ignored. Be careful when using the ability to modify headers. filter_post() runs after all the normal consistency checks on the headers and after server supplied headers (like Message-ID: and Date:) are filled in. Deleting required headers or modifying headers that need to follow a strict format can result in nnrpd trying to post nonsense articles (which will probably then be rejected by innd). If $modify_headers is set, *everything* in the %hdr hash is taken to be article headers and added to the article. To modify the contents of the Organization: header, you can use: $hdr{'Organization'} = 'My new organization'; $modify_headers = 1; To delete the Organization: header, you should not use "delete $hdr{'Organization'}" (this syntax may work with some headers, and not with other headers). Instead, you are encouraged to use: $hdr{'Organization'} = undef; $modify_headers = 1; or: $hdr{'Organization'} = ''; $modify_headers = 1; If filter_post() returns something other than the empty string, this message is normally returned to the client as an error (make sure that such a message is properly encoded in UTF-8 so as to comply with the NNTP protocol). There are three exceptions: * If the string returned begins with "CLOSE", the post will be discarded and the connection closed with a 400 response code. * If the string returned begins with "DROP", the post will be silently discarded and success returned to the client. * If the string begins with "SPOOL", success is returned to the client, but the post is saved in a directory named spam under the directory specified by *pathincoming* in inn.conf (in a directory named spam/mod if the post is to a moderated group). This is intended to allow manual inspection of the suspect messages; if they should be posted, they can be manually moved out of the subdirectory to the directory specified by *pathincoming* in inn.conf, where they can be posted by running "rnews -U". If you use this functionality, make sure those directories exist. Changes to Perl Authentication Support for nnrpd The old authentication functionality has been combined with the new readers.conf mechanism by Erik Klavon ; bug reports should however go to , not Erik. The remainder of this section is an introduction to the new mechanism (which uses the perl_auth: and perl_access: readers.conf parameters) with porting/migration suggestions for people familiar with the old mechanism (identifiable by the nnrpperlauth: parameter in inn.conf). Other people should skip this section. The perl_auth parameter allows the use of Perl to authenticate a user. Scripts (like those from the old mechanism) are listed in readers.conf using perl_auth in the same manner other authenticators are using auth: perl_auth: "/path/to/script/auth1.pl" The file given as argument to perl_auth should contain the same procedures as before. The global hash %attributes remains the same, except for the removal of the "type" entry which is no longer needed in this modification and the addition of several new entries (port, intipaddr, intport) described below. The return array now only contains either two or three elements, the first of which is the NNTP return code. The second is an error string which is passed to the client if the error code indicates that the authentication attempt has failed. This allows a specific error message to be generated by the perl script in place of "Authentication failed". An optional third return element if present will be used to match the connection with the users: parameter in access groups and will also be the username logged. If this element is absent, the username supplied by the client during authentication will be used as was the previous behavior. The perl_access parameter (described below) is also new; it allows the dynamic generation of an access group for an incoming connection using a Perl script. If a connection matches an auth group which has a perl_access parameter, all access groups in readers.conf are ignored; instead the procedure described below is used to generate an access group. This concept is due to Jeffrey M. Vinocur. The new functionality should provide all of the existing capabilities of the Perl hook, in combination with the flexibility of readers.conf and the use of other authentication and resolving programs. To use Perl authentication code that predates the readers.conf mechanism, you would need to modify the code slightly (see below for the new specification) and supply a simple readers.conf file. If you don't want to modify your code, the samples directory has nnrpd_auth_wrapper.pl and nnrpd_access_wrapper.pl which should allow you to use your old code without needing to change it. However, before trying to use your old Perl code, you may want to consider replacing it entirely with non-Perl authentication. (With readers.conf and the regular authenticator and resolver programs, much of what once required Perl can be done directly.) Even if the functionality is not available directly, you may wish to write a new authenticator or resolver (which can be done in whatever language you prefer to work in). Perl Authentication Support for nnrpd Support for authentication via Perl is provided in nnrpd by the inclusion of a perl_auth: parameter in a readers.conf auth group. perl_auth: works exactly like the auth: parameter in readers.conf, except that it calls the script given as argument using the Perl hook rather then treating it as an external program. If the processing of readers.conf requires that a perl_auth: statement be used for authentication, Perl is loaded (if it has yet to be) and the file given as argument to the perl_auth: parameter is loaded as well. If a Perl function auth_init() is defined by that file, it is called immediately after the file is loaded. It takes no arguments and returns nothing. Provided the file loads without errors, auth_init() (if present) runs without fatal errors, and a Perl function authenticate() is defined, authenticate() will then be called. authenticate() takes no arguments, but it has access to a global hash %attributes which contains information about the connection as follows: $attributes{hostname} will contain the hostname (or the IP address if it doesn't resolve) of the client machine, $attributes{ipaddress} will contain its IP address (as a string), $attributes{port} will contain the client port (as an integer), $attributes{interface} contains the hostname of the interface the client connected on, $attributes{intipaddr} contains the IP address (as a string) of the interface the client connected on, $attributes{intport} contains the port (as an integer) on the interface the client connected on, $attributes{username} will contain the provided username and $attributes{password} the password. authenticate() should return a two or three element array. The first element is the NNTP response code to return to the client, the second element is an error string which is passed to the client if the response code indicates that the authentication attempt has failed (make sure that such a message is properly encoded in UTF-8 so as to comply with the NNTP protocol). An optional third return element if present will be used to match the connection with the users: parameter in access groups and will also be the username logged. If this element is absent, the username supplied by the client during authentication will be used for matching and logging. The NNTP response code should be 281 (authentication successful), 481 (authentication unsuccessful), or 403 (server failure). If the code returned is anything other than these three values, nnrpd will use 403. If authenticate() dies (either due to a Perl error or due to calling die), or if it returns anything other than the two or three element array described above, an internal error will be reported to the client, the exact error will be logged to syslog, and nnrpd will drop the connection and exit with a 400 response code. Dynamic Generation of Access Groups A Perl script may be used to dynamically generate an access group which is then used to determine the access rights of the client. This occurs whenever the perl_access: is specified in an auth group which has successfully matched the client. Only one perl_access: statement is allowed in an auth group. This parameter should not be mixed with a python_access: statement in the same auth group. When a perl_access: parameter is encountered, Perl is loaded (if it has yet to be) and the file given as argument is loaded as well. Provided the file loads without errors, and a Perl function access() is defined, access() will then be called. access() takes no arguments, but it has access to a global hash %attributes which contains information about the connection as follows: $attributes{hostname} will contain the hostname (or the IP address if it doesn't resolve) of the client machine, $attributes{ipaddress} will contain its IP address (as a string), $attributes{port} will contain the client port (as an integer), $attributes{interface} contains the hostname of the interface the client connected on, $attributes{intipaddr} contains the IP address (as a string) of the interface the client connected on, $attributes{intport} contains the port (as an integer) on the interface the client connected on, $attributes{username} will contain the provided username and domain (in username@domain form). access() returns a hash, containing the desired access parameters and values. Here is an untested example showing how to dynamically generate a list of newsgroups based on the client's username and domain. my %hosts = ( "example.com" => "example.*", "isc.org" => "isc.*" ); sub access { %return_hash = ( "max_rate" => "10000", "addinjectionpostinghost" => "true", # ... ); if( defined $attributes{username} && $attributes{username} =~ /.*@(.*)/ ) { $return_hash{"virtualhost"} = "true"; $return_hash{"path"} = $1; $return_hash{"newsgroups"} = $hosts{$1}; } else { $return_hash{"read"} = "*"; $return_hash{"post"} = "local.*" } return %return_hash; } Note that both the keys and values are quoted strings. These values are to be returned to a C program and must be quoted strings. For values containing one or more spaces, it is not necessary to include extra quotes inside the string. While you may include the users: parameter in a dynamically generated access group, some care should be taken (unless your pattern is just * which is equivalent to leaving the parameter out). The group created with the values returned from the Perl script is the only one considered when nnrpd attempts to find an access group matching the connection. If a users: parameter is included and it doesn't match the connection, then the client will be denied access since there are no other access groups which could match the connection. If access() dies (either due to a Perl error or due to calling die), or if it returns anything other than a hash as described above, an internal error will be reported to the client, the exact error will be logged to syslog, and nnrpd will drop the connection and exit. Notes on Writing Embedded Perl All Perl evaluation is done inside an implicit eval block, so calling die in Perl code will not kill the innd or nnrpd process. Neither will Perl errors (such as syntax errors). However, such errors will have negative effects (fatal errors in the innd or nnrpd filter will cause filtering to be disabled, and fatal errors in the nnrpd authentication code will cause the client connection to be terminated). Calling exit directly, however, *will* kill the innd or nnrpd process, so don't do that. Similarly, you probably don't want to call fork (or any other function that results in a fork such as system, "IPC::Open3::open3()", or any use of backticks) since there are possibly unflushed buffers that could get flushed twice, lots of open state that may not get closed properly, and innumerable other potential problems. In general, be aware that all Perl code is running inside a large and complicated C program, and Perl code that impacts the process as a whole is best avoided. You can use print and warn inside Perl code to send output to STDOUT or STDERR, but you probably shouldn't. Instead, open a log file and print to it instead (or, in the innd filter, use "INN::syslog()" to write messages via syslog like the rest of INN). If you write to STDOUT or STDERR, where that data will go depends on where the filter is running; inside innd, it will go to the news log or the errlog, and inside nnrpd it will probably go nowhere but could go to the client. The nnrpd filter takes some steps to try to keep output from going across the network connection to the client (which would probably result in a very confused client), but best not to take the chance. For similar reasons, try to make your Perl code -w clean, since Perl warnings are written to STDERR. (INN won't run your code under -w, but better safe than sorry, and some versions of Perl have some mandatory warnings you can't turn off.) You *can* use modules in your Perl code, just like you would in an ordinary Perl script. You can even use modules that dynamically load C code. Just make sure that none of the modules you use go off behind your back to do any of the things above that are best avoided. Whenever you make any modifications to the Perl code, and particularly before starting INN or reloading filter.perl with new code, you should run perl -wc on the file. This will at least make sure you don't have any glaring syntax errors. Remember, if there are errors in your code, filtering will be disabled, which could mean that posts you really wanted to reject will leak through and authentication of readers may be totally broken. The samples directory has example startup_innd.pl, filter_innd.pl, filter_nnrpd.pl, and nnrpd_auth.pl files that contain some simplistic examples. Look them over as a starting point when writing your own. Available Packages This is an unofficial list of known filtering packages at the time of publication. This is not an endorsement of these filters by ISC or the INN developers, but is included as assistance in locating packages which make use of this filter mechanism. Cleanfeed URL: (maintained by Steve Crook) Cleanfeed is an extremely powerful spam filter, probably the most widely used spam filter on Usenet currently. It catches excessive multiposting and a host of other things, and is highly configurable. It uses filter_innd.pl exclusively and requires the MD5 Perl module. Cleanfeed was originally developed by Jeremy Nixon who maintained it until 1998. Then Marco d'Itri until 2002. Steve Crook has been maintaining it since 2007. Postfilter URL: (by Paolo Amoroso) Postfilter is an nnrpd Perl filter (filter_nnrpd.pl) which scans all messages sent by local users in order to block abuses. Postfilter can limit the number of messages per user, IP or domain, provides a set of powerful ban lists, supports RBLs and URIBLs, can handle connections that come from TOR and is able to deeply modify the headers. It's designed for large sites. News Shogun URL: (by Aidan Cully) A posting filter for helping a site enforce Usenet-II soundness, and for quotaing the number of messages any user can post to Usenet daily. It uses filter_nnrpd.pl. $Id: hook-perl.pod 9917 2015-07-07 16:37:51Z iulius $ inn-2.6.0/doc/sample-control0000644000175200017520000000174612575023702015357 0ustar iuliusiuliusPath: bounce-back From: group-admin@isc.org (David C Lawrence) Newsgroups: news.announce.newusers Subject: cmsg newgroup news.announce.newusers moderated Control: newgroup news.announce.newusers moderated Approved: newgroups-request@isc.org Message-ID: <868485430.2655@isc.org> Date: Wed, 09 Jul 1997 21:57:10 GMT Lines: 8 X-Info: ftp://ftp.isc.org/pub/pgpcontrol/README.html ftp://ftp.isc.org/pub/pgpcontrol/README X-PGP-Sig: 2.6.2 Subject,Control,Message-ID,Date,From,Sender iQCVAwUBM8QJNsJdOtO4janBAQGkUAP6AlzO065jDQFrG20/b3/SaOm4WGQBly5D pXlVJdYBqPAG3HvxVqAdKM7y6ixM7Mml4OdfK0JeVCH03nqeGuBc51sTDIZ6kyAx +YHlNSnp/JJnpDuJCfXZjwNl4kWImucGgwI5BxrQco8re949Cg5m5TFXiwYMiR/+ AjKZCTtmV1Y= =uSbd news.announce.newusers is a moderated newsgroup which has existed since the mid-1980s. Group submission address: netannounce@deshaw.com Moderator contact address: netannounce@deshaw.com (Mark Moraes) For your newsgroups file: news.announce.newusers Explanatory postings for new users. (Moderated) inn-2.6.0/doc/config-design0000644000175200017520000001556112575023702015134 0ustar iuliusiulius$Id: config-design 9428 2012-06-15 18:18:45Z iulius $ This file is documentation of the design principles that went into INN's configuration file syntax, and some rationale for why those principles were chosen. 1. All configuration files used by INN should have the same syntax. This was the root reason why the project was taken on in the first place; INN developed a proliferation of configuration files, all of which had a slightly (or greatly) different syntax, forcing the administrator to learn several different syntaxes and resulting in a proliferation of parsers, all with their own little quirks. 2. Adding a new configuration file or a new set of configuration options should not require writing a single line of code for syntax parsing. Code that analyzes the semantics of the configuration will of course be necessary, but absolutely no additional code to read files, parse files, build configuration trees, or the like should be required. Ideally, INN should have a single configuration parser that everything uses. 3. The syntax should look basically like the syntax of readers.conf, incoming.conf, and innfeed.conf in INN 2.3. After extensive discussion on the inn-workers mailing list, this seemed to be the most generally popular syntax of the ones already used in INN, and inventing a completely new syntax didn't appear likely to have gains outweighing the effort involved. This syntax seemed sufficiently general to represent all of the configuration information that INN needed. 4. The parsing layer should *not* attempt to do semantic analysis of the configuration; it should concern itself solely with syntax (or very low-level semantics that are standard across all conceivable INN configuration files). In particular, the parsing layer should not know what parameters are valid, what groups are permitted, what types the values for parameters should have, or what default values parameters have. This principle requires some additional explanation, since it is very tempting to not do things this way. However, the more semantic information the parser is aware of, the less general the parser is, and it's very easy to paint oneself into a corner. In particular, it's *not* a valid assumption that all clients of the parsing code will want to reduce the configuration to a bunch of structs; this happens to be true for most clients of inn.conf, for example, but inndstart doesn't want the code needed to reduce everything to a struct and set default values to necessarily be executed in a security-critical context. (The example is now obsolete with the removal of inndstart, but the basic idea is still sound.) Additionally, making the parser know more semantic information either complicates (significantly) the parser interface or means that the parser has to be modified when the semantics change. The latter is not acceptable, and the parser interface should be as straightforward as possible (to encourage all parts of INN to use it). 5. The result of a parse of the configuration file may be represented as a tree of dictionaries, where each dictionary corresponds to a group and each key corresponds to a parameter setting. (Note that this does not assume that the underlying data structure is a hash table, just that it has dictionary semantics, namely a collection of key/value pairs with the keys presumed unique.) 6. Parameter values inherit via group nesting. In other words, if a group is nested inside another group, all parameters defined in the enclosing group are inherited by the nested group unless they're explicitly overridden within the nested group. (This point and point 5 are to some degree just corollaries of point 3.) 7. The parsing library must permit writing as well as reading. It must be possible for a program to read in a configuration file, modify parameters, add and delete groups, and otherwise change the configuration, and then write back out to disk a configuration file that preserves those changes and still remains as faithful to the original (possibly human-written) configuration file as possible. (Ideally, this would extend to preserving comments, but that may be too difficult to do and therefore isn't required.) 8. The parser must not limit the configuration arbitrarily. In particular, unlimited length strings (within available memory) must be supported for string values, and if allowable line length is limited, line continuation must be supported everywhere that there's any reasonable expectation that it might be necessary. One common configuration parameter is a list of hosts or host wildmats that can be almost arbitrarily long, and the syntax and parser must support that. 9. The parser should be reasonably efficient, enough so as to not cause an annoying wait for command-line tools like sm and grephistory to start. In general, though, efficiency in either time or memory is not as high of a priority as readable, straightforward code; it's safe to assume that configuration parsing is only done on startup and at rare intervals and is not on any critical speed paths. 10. Error reporting is a must. It must be possible to clearly report errors in the configuration files, including at minimum the file name and line number where the error occurred. 11. The configuration parser should not trust its input, syntax-wise. It must not segfault, infinitely loop, or otherwise explode on malformed or broken input. And, as a related point, it's better to be aggressively picky about syntax than to be lax and attempt to accept minor violations. The intended configuration syntax is simple and unambiguous, so it should be unnecessary to accept violations. 12. It must be possible to do comprehensive semantic checks of a configuration file, including verifying that all provided parameters are known ones, all parameter values have the correct type, group types that are not expected to be repeated are not, and only expected group types are used. This must *not* be done by the parser, but the parser must provide sufficient hooks that the client program can do this if it chooses. 13. The parser must be re-entrant and thread-safe. 14. The grammar shouldn't require any lookahead to parse. This is in order to keep the parser extremely simple and therefore maintainable. (It's worth noting that this design principle leads to the requirement that parameter keys end in a colon; the presence of the colon allows parameter keys to be distinguished from other syntactic elements allowed in the same scope, like the beginning of a nested group.) inn-2.6.0/doc/pod/0000755000175200017520000000000012575023700013245 5ustar iuliusiuliusinn-2.6.0/doc/pod/news.pod0000644000175200017520000031720412575023702014736 0ustar iuliusiulius=head1 Upgrading from 2.5 to 2.6 The following changes require your full attention because a manual intervention may be needed: =over 2 =item * The name and location of the B configuration file have changed. It is now F, located in I when B is run as the news user, or otherwise in the running user's home directory. This file was previously stored in F<.pullnews> in the running user's home directory (even for the news user). If you use B, you need to manually move and rename the configuration file; otherwise, it will no longer work. Note that the B<-c> flag passed to B allows to specify another configuration file, if need be. =item * The default location of the B database directory has changed from I to I. If you use B without an explicitly specified database directory (using the B<-b> flag), then you should manually move your current database files F and F from I to I. =item * If you have been using SSL/TLS with B before, be aware that the default value of a few F parameters have changed: the server now decides the preferred cipher (instead of the client), and only TLS protocols are allowed (using the flawed SSLv2 and SSLv3 protocols is now disabled). If you want to change these settings, the respective I and I parameters can be tuned to your needs. =item * The B<--with-kerberos> configure flag used to add S support has been renamed to B<--with-krb5>. =item * The B<--with-berkeleydb> configure flag used to add S support has been renamed to B<--with-bdb>. =item * The B<--enable-ipv6> configure flag no longer exists. IPv6 is now unconditionally enabled, if available. =item * $HOME is no longer exported as an environment variable by B, B and the Perl module C. It was previously overriding the default user home directory with I. If you use these scripts in your own scripts, you will have to take care of that change. =item * Owing to the implementation of S (AUTHINFO USER/PASS) in B, if remote peers have to authenticate in order to feed articles, they now have to send a username (which was previously wrongly optional), before sending their password. The mandatory username, though currently unused by B, can be whatever the remote peer wishes. In previous versions of INN, B was already complaining when F contained an empty username associated with a password. A manual review of authenticated feeds should then be done so as to ensure that they are properly working. =item * The Injection-Date: and Injection-Info: headers are now generated by B at injection time instead of the NNTP-Posting-Date:, NNTP-Posting-Host:, X-Complaints-To: and X-Trace: headers. Local scripts that were using (for authentication, privacy, etc.) these now deprecated headers should be updated. Also note that the Path: header of locally posted articles can also contain the contents of the deprecated NNTP-Posting-Host: field. =item * The two I and I parameters in F have been respectively renamed to I and I. B takes care of the modification only for F; a manual change will therefore be needed for F, if these parameters are overridden in this file. =item * The default values of a few F parameters have changed to make use of the vastly expanded storage and RAM commonly available today: datamovethreshold (from 8192 to 16384), msgidcachesize (from 16000 to 64000), overcachesize (from 64 to 128), and wireformat (now enabled by default). The generation of status reports and performance timings are now also enabled by default: logstatus and nnrpdoverstats parameters, with a frequency of 10 minutes (status and timer parameters). =item * The default value of max-queue-size has changed from 5 to 20, and use-mmap now defaults to true for F. =back If you are upgrading from a version prior to S, see also L. =head1 Changes in 2.6.0 =over 2 =item * The NNTP protocol requires a username to be sent before a password when authentication is used. B was wrongly allowing only a password to be sent by authenticated peers. See the note above for more details. =item * The Lines: header is no longer generated by B at injection time. =item * The Injection-Date: header is now generated by B at injection time instead of the deprecated NNTP-Posting-Date: header, when I is set to true. Note that I has been renamed to I in F. =item * The Injection-Info: header is now generated by B at injection time instead of the deprecated NNTP-Posting-Host: (when I is set to true), X-Complaints-To: and X-Trace: headers. Note that I has been renamed to I in F. The Path: header of locally posted articles now also contains the contents of the NNTP-Posting-Host: header. =item * A new I parameter has been added in F. When set to true, the Injection-Info: header field contains an additional posting-account attribute that mentions the username assigned to the user at connection time or after authentication. The default value for this parameter is false. =item * A few headers are now considered as obsolete by B at injection time: NNTP-Posting-Date:, NNTP-Posting-Host:, X-Complaints-To:, X-Trace:, Also-Control:, Article-Names:, Article-Updates:, and See-Also: headers. Besides, B will similarly reject obsolete sendsys, senduuname and version control messages. =item * The presence of a Subject: header field beginning with C no longer causes an article to be interpreted as a control message by B at injection time. =item * B no longer differentiates IHAVE from POST. Articles injected with IHAVE are now treated as though they were injected with POST. It means that if the previous behaviour of IHAVE was expected, B should handle itself the connection instead of B. =item * The name of the B configuration file is now F located in I when B is run as the news user, or otherwise in the running user's home directory. It was previously stored in F<.pullnews> in the running user's home directory (even for the news user). =item * Fixed a leak of semaphores when using buffindexed. Thanks to Richard Kettlewell for having fixed the issue. =item * Building with Libtool is no longer optional. The B<--enable-libtool> option to B has been removed. =item * DESTDIR and non-root installs are now properly supported and documented in INSTALL. The C, C and C steps properly obey DESTDIR. Besides, it is no longer a requirement that the installation step be done by the superuser, as long as the user executing the install has supplied a DESTDIR value that points to a writable directory, I the person or process performing the install corrects the file ownerships when INN is installed on the system on which it's going to run. Thanks to James Ralston for this support. =item * When building INN with S, Cyrus SASL, S, OpenSSL, or zlib support, no longer add standard locations to compiler and linker include flags. Such default paths are now added only if explicitly given to one or more of the B<--with-bdb>, B<--with-bdb-include>, B<--with-bdb-lib>, B<--with-sasl>, B<--with-sasl-include>, B<--with-sasl-lib>, B<--with-krb5>, B<--with-krb5-include>, B<--with-krb5-lib>, B<--with-openssl>, B<--with-openssl-include>, B<--with-openssl-lib>, B<--with-zlib>, B<--with-zlib-include>, or B<--with-zlib-lib> configure flags (the flags ending with C<-include> and C<-lib> are new in S). =item * If the S, Cyrus SASL, S, or OpenSSL SSL and crypto libraries are found at configure time, INN will now be built with support for them unless respectively the B<--without-bdb>, B<--without-sasl>, B<--without-krb5>, or B<--without-openssl> flags are explicitly passed to configure. Note that it was already the default behaviour for zlib support when S support was also enabled. =item * The configure flag B<--enable-reduced-depends> has been added to request that library probes assume shared libraries are in use and dependencies of libraries should not be probed. It therefore tries to minimize the shared library dependencies of the resulting binaries on platforms with proper shared library dependencies. This is not enabled by default, and is of interest primarily to people building packages for distributions. =item * Building INN with Python support now requires the use of S or later as the distutils.sysconfig module used was introduced with S. =item * The INN test suite driver is now fully synchronized with the upstream version of the C TAP Harness package maintained by Russ Allbery. Keeping the INN test suite driver up-to-date will be possible thanks to a new B script in the F directory that automatically fetches the latest upstream changes. Similarly, the new B script permits to keep most of the utility and portability functions synchronized with the upstream version of the rra-c-util package maintained by Russ Allbery. =item * Other minor bug fixes and documentation improvements. =back =head1 Changes in 2.5.5 =over 2 =item * New F parameters used by B to fine-tune the SSL/TLS configuration have been added: I, I, I, I, and I. Many thanks to Christian Mock for his contribution that permits to tighten the level of security provided by TLS/SSL. =item * B no longer creates a child process only for sleeping and then waits on that process. The forked-off process only died after it had done sleeping, which caused the INN service to drop into maintenance state when for instance running under SMF on illumos/Solaris (since not all processes die within timeout). Thanks to Lauri Tirkkonen for the patch. =item * B no longer crashes if a channel is supposed to sleep but does not define a waker callback function. Also, the highest file descriptor of sleeping channels is now properly updated. Thanks to Petr Novopashenniy for the bug report. =item * Add new B<-i> flag to both B and B to specify how many seconds they should sleep at startup. It will especially be useful in B so that these scripts are actually started and then sleep by themselves, instead of being started a minute after B and therefore not being properly stopped if C is invoked during that minute. =item * Add new B<-f> flag to B to specify the configuration file to use, in case it is not the default F file. =item * Add new B<-t> flag to B to change, if needed, the default directory to use to temporarily store error messages that are sent to the newsmaster. Two paths are now tried by default: I as set in F, and then F if I is not writable. =item * When the creation of a newsgroup needed expanding the tradindexed group index, an already-running B was not automatically noticing newly created newsgroups. Richard Kettlewell fixed that issue. =item * Fixed flushing of CNFS buffers when using NFS storage. =item * Fixed how B is executed during an update of an INN installation; on a few systems like AIX, it fails to run because its taint mode was unproperly declared. =item * Several improvements have been contributed to B by Geraint Edwards: the new B<-a> flag adds the Diablo-compatible hashfeed ability, the new B<-B> flag triggers header-only feeding, the B<-m> flag now permits to remove headers matching (or not) a given regexp, and B reporting is improved. =item * B now properly takes into account the time B spends writing when using SASL. =item * B now only shows the first 50 lines from error log files. Otherwise, all of them were present verbatim in the daily report, and the resulting e-mail could bounce owing to its length. Thanks to Jeffrey S for the bug report. =item * Fixed the use of the legacy AUTHINFO GENERIC command, that has been broken since S (therefore proving readers probably no longer use that method to authenticate). Thanks to Richard Kettlewell for having noticed, though, and contributed to tighten the security of the replies of this command. =item * Add the B contribution script written by Jeffrey S to convert old-style F file to F. =back =head1 Changes in 2.5.4 =over 2 =item * An up-to-date F file is provided with this release. You should manually update your F file with the new information recorded about Usenet hierarchies. =item * A test has been improved in F so that B no longer throttles B when no overview directory exists. You should manually update your F file to get this improvement. =item * Fixed a long-standing limitation on how B and B were checking the signer of control messages. They now properly handle the case of several UIDs being defined on a single PGP key, as well as the presence of spaces into UIDs. In previous versions of INN, a few valid control messages got ignored because of that limitation (fido.ger.* and grisbi.* were for instance impacted). =item * As the name of the F configuration file shipped with INN for the B authenticator against a RADIUS server conflicts with the libradius package, this file is renamed to F (B takes care of the rename during the update). =item * The attributes hash is now accessible to B Perl posting filter. As a result, F can make use of it. Only authentication and access Perl hooks could previously use the attributes hash. Thanks to Steve Crook for this addition. =item * INN now properly builds fine with S<< B 2.5.36 >> (this version introduced a change of type for a variable used by INN). =item * When using funnel feeds, B log files were open forever, which resulted in empty log files, once rotated by B. B now reopens its log files upon receiving a HUP signal; this signal is in particular sent by B during log rotation. Thanks to Florian Schlichting for the patch. =item * Exploder and process channels are now reopened when C is used. Otherwise, they could hold open an already deleted F file. The issue affected in particular B or B, running as such channels. =item * Fixed a buffer overflow when using B with more than a million commands during the same IMAP session. Thanks to David Binderman for the bug report. =item * Fixed a segfault occurring in B on systems where time_t is a 64-bit integer. Thanks to S for the patch. =item * Fixed a segfault occurring in B when a res block was used in F without the program: key. =item * Fixed an issue where users were denied posting because of an overlapping buffer copy in a check B was doing. Thanks to Florian Schlichting for the patch. =item * Fixed a regression that occurred in S regarding the path used by default by B for its configuration file. Instead of looking in the running user's home directory, it was looking in the I directory set in F. Thanks to Tony Evans for the bug report. =item * When neither B nor B nor B was found at configure time, the path to the B substitution program shipped with INN was not properly set in B, B, and the C Perl module. Thanks to Christian Garbs for the bug report. =item * B no longer tries to use the ndbm compatibility layer provided by S if S has been built without ndbm support. Also add support for gdbm libraries in B. =item * Fixed a Perl warning in B; using C has been deprecated since S. =item * Fixed the occurrence of an unexpected C error generated by B. Thanks to Paul Tomblin for having caught that long-standing issue. =item * When building INN with S support, no longer add F<-L/usr/lib> to the linker include flags; unconditionally adding it may break the build on systems using lib32 and lib64 directories. =item * On a fresh INN install, F and F are no longer installed by default. Instead, samples for these files are provided in I, named differently so that their default contents are not displayed to news clients before they get customised. =item * Other minor bug fixes and documentation improvements (like the addition in the F man page of the log: and program: parameters in res blocks, and the include directive). =back =head1 Changes in 2.5.3 Please note that the HTML_STATUS compile-time option has been replaced with the I parameter in F. If you used HTML_STATUS, you should set I accordingly. A confusion in the name of a key in F existed in the source code. Make sure that the misspelled, undocumented I key is *not* used in your F file; its real name is I. You should rename the key in case it is present in your configuration file. Otherwise, it will not be taken into account. You can run B to verify that the syntax of this file is correct. It is generally recommended to run B after any changes done to configuration files, especially with the new improved version of this script shipped with S, thanks to the hard work of Florian Schlichting who added support for the syntax of F, F, F and F. An up-to-date F file is provided with this release. You should manually update your F file with the new information recorded about Usenet hierarchies. =over 2 =item * When HDR/XHDR/XPAT were used on a new article coming into a newsgroup, requesting a header not present in the overview database, the first subsequent OVER/XOVER command did not show that article. A remap of the overview data file was missing in B. Thanks to Sam Varshavchik for the bug report. =item * When a header field appeared more than once in an article, it was missing from the overview data. OVER/XOVER, as well as HDR/XHDR/XPAT using the overview, were therefore returning an empty field. The content of the first occurrence is now returned, in accordance with S. Perl and Python filters for B now also properly initialize their header variables with the first occurrence of header fields. (It is still the last occurrence for the Perl filter for B.) =item * Fixed a possible plaintext command injection during the negotiation of a TLS layer. The vulnerability detailed in CVE-2011-0411 (and CVE-2012-3523, specifically for INN) affects the STARTTLS and AUTHINFO SASL commands. B now resets its read buffer upon a successful negotiation of a TLS layer. It prevents malicious commands, sent unencrypted, from being executed in the new encrypted state of the session. =item * Fixed a regression that occurred in S when leading whitespace characters have been made significant in header field bodies. It could lead INN to drop articles and throttle itself when running as a slave because Xref: header fields generated by other news servers, or even S, could contain (valid) leading whitespace. Thanks to Matija Nalis for having caught this bug. =item * Fixed an invalid C<431> response to CHECK commands when B is paused: the message-ID of the article to defer was missing. Also fixed another issue in the messages B replied; when an error occurred during a write on a channel, a trailing extra junk byte was added to the reply. Thanks to River Tarnell for these bug reports. =item * It is now possible to properly generate daily statistics with B thanks to the new B<-k> and B<-r> flags that permit to control the interval of days for processing dump files. The new B<-c> flag permits to send a copy of the generated e-mail to the newsmaster. Also fixed an issue with statistics that could be missing or duplicated for a couple of days when monthly sent. The documentation has been updated and mentions a preferred daily run of B. This script is a complete rewrite in Perl, and is based on Mohan Kokal's initial work. =item * B now properly recognizes continuation lines in F, that is to say lines ending with a backslash (C<\>). Thanks to John S for the bug report. =item * The order of CNFS buffers in a metacycbuff is now properly read and written by B. There previously was a confusion between hexadecimal and decimal values. Thanks again to John S. =item * When the B<-l> flag is given to B, the F and F files are now reloaded if they have been modified since the previous output of B. =item * A single header field line is limited to 998 bytes, per S. B was previously accepting, and also generating Xref: header field lines, up to 1022 bytes. Now, B (acting as an injecting agent) rejects articles which contain header field lines whose length exceeds 998 bytes. And B (acting as a relaying or serving agent) no longer checks that. =item * B advertises the COUNTS, DISTRIBUTIONS, MODERATORS, MOTD and SUBSCRIPTIONS variants of the LIST command in response to CAPABILITIES. These commands already existed in B but S had not yet been published. =item * Add support for LIST MOTD in B. Consequently, the F configuration file which was previously used only by B is renamed to F (B takes care of the rename). B uses the new F file in I for its message of the day. =item * Fixed an issue at configure time that made INN wrongly assume that OpenBSD (4.6) didn't support Unix-domain sockets. Thanks to Wim Lewis for the patch. =item * Fixed an issue on systems which do not have a working flock(2) function (Solaris, for instance). B and B are reported not to be usable on such systems. Many thanks to Dennis Davis for the bug report. A wrapper around B is now called in Perl scripts. The INN::Utils::Shlock module has been added for that use. =item * Fixed an issue in the Python access hook for B: it has not been working since S on 64-bit platforms, owing to a change to Python's C API, using a new Py_ssize_t type definition instead of int. Thanks to Raphael Barrois for the patch. =item * Improve the stability of the Perl filters for B and B: properly save and restore the stack pointer when needed. =item * The Injection-Date: header, when present, is now used by B and B to determine the posting date of an article. Otherwise, the Date: header is used. =item * B now imposes a date cutoff on processing control articles. The I parameter set in F is used. Otherwise, without that cutoff, old control articles could be maliciously reinjected into Usenet, and replayed. (An unsigned Injection-Date: header field could be added to an article that only had a Date: header field.) A new B<-c> flag has been added to B to disable the cutoff check, if needed (usually when manually invoking the program). =item * B no longer adds or updates the Path: header field when an article is forwarded to a moderator. It could otherwise lead to rejects at injection time when the article was approved by the moderator. =item * The X-Trace: header field was not properly generated when an article was locally posted. The field mentioning the IP address was skipped, resulting in a wrong syntax for this header. The local "127.0.0.1" IP address is now used. Besides, C is now mentioned instead of an obscure C in injection header fields. =item * Fixed a bug in the frequency B logs its status: too many useless lines were written to F. Thanks to Florian Schlichting for the fix. =item * When unset in F, the I parameter now properly defaults to C<3> (instead of C<0>) and I to false (instead of true). These two values were already the recommended ones in the documentation and the sample file. Note that I is only used when B is given file names to send instead of storage API tokens, which is a fairly rare use case. =item * B no longer generates an error message (logged in F) when a parameter is not defined in F. All the parameters have a default value, so there is no need to warn the user if they are not present in F. Thanks to Dieter Stussy for having reported this problem. =item * Implement an upper limit to the number of file descriptors B can handle. At most (FD_SETSIZE-1) file descriptors can be used. This upper limit now overrides any superior number set with I in F. Thanks to Steve Crook for the bug report. =item * A default timeout on outgoing sockets (using NNTPconnect) has been added by Florian Schlichting. For a long time, there have been occasional problems with B (and probably other programs) that would hang until manually killed or restarted. =item * The flag B<-S> has been added to B by Florian Schlichting. When used, B reports the errors found in F and exits. =item * B no longer stops processing newsgroups when an error occur during its run (for instance when a newsgroup mentioned in the configuration file is removed from an upstream server). Besides, it can now use authentication when posting to the downstream server. A few other minor bugs have been fixed as for the way B counts the articles. =item * Fixed the way B handles leap years. It now properly generates HTML reports; dates were assumed to be relative to the current year, which may break their computation during for instance the whole 2012 leap year. Please note that no HTML reports have been lost, and that they will appear when INN is updated to this new version. =item * A new parameter has been added to F to determine whether the status file that B can write out (depending on the value of the I parameter) is plain text or wrapped in HTML. It previously only was a compile-time option, set to true by default. Florian Schlichting added the I parameter to provide a configurable behaviour. =item * It is now possible to run a script at the end of the execution of B scripts. If a file named F, F or F is present and executable in I, then it will be executed by the corresponding B script (respectively shell, INN::Config Perl module, and Tcl). A typical use is to add or override variables. =item * Add support for wire-formatted articles in B. =item * A lot of work on cleaning old perl4-style code has been done by Florian Schlichting. =item * B now generates a proper non-zero exit value when errors are found, and allows quiet mode with the B<-q> flag. Florian Schlichting has greatly improved this script in many regards, especially with a config-syntax parser for F, F, F and F. =item * B now properly finds the boundaries of substituted variables in F thanks to Alexander Bartolich. =item * B no longer uses awk. On a few systems, the script was failing because of the presence of an old version of awk that has a limit in the size of the input it can handle. Processing large newsgroups files was consequently impossible. B now uses Perl instead of awk, which solves the issue reported by S. =item * Other minor bug fixes and documentation improvements. In particular, the I, I and I keys in F are now documented. The function C, called when Perl filtering is turned off, is also documented for the B and B Perl filters. =back =head1 Changes in 2.5.2 The way checkpoints are handled by B for B and B has totally changed to provide more accurate daily statistics. The first Usenet report after an upgrade to S will probably contain incorrect statistics for incoming and outgoing articles because the beginning of the log files that will be used was generated by a previous version of INN. A new version of F is shipped with S but, in order to preserve any local changes, will not be automatically installed with make update. The changes are minor and not mandatory for the upgrade. =over 2 =item * Julien Elie has implemented in B the new version of the NNTP protocol described in S, S and S, and B now recognizes the CAPABILITIES command. Despite these standards, three commands (IHAVE, CHECK and TAKETHIS) will continue, for interoperability reasons, to return a reject code (respectively C<435>, C<438>, and C<439>) when the command contains a syntax error instead of C<501>. The mandatory username argument for authenticated peers is not enforced in S but will be be enforced by S when it is released. Major improvements are: =over 2 =item * B now has a decent parser for NNTP commands. The parser is more correct (commands like C<< IHAVEZ<> >>, without a space between the command and its argument, are no longer valid) and allows leading and trailing whitespaces in commands. B also now checks the length of the NNTP command sent by the client. If the command contains more than 512 bytes (or 497 bytes for an argument), an error is returned and the command is discarded. After ten unrecognized commands, B closes the connection with the appropriate code (C<400> instead of C<500>). =item * The output of the HELP command specifies the arguments expected by NNTP commands, similar to B's HELP command. =item * LIST ACTIVE, LIST ACTIVE.TIMES and LIST NEWSGROUPS now allow an optional wildmat argument to restrict the results of those commands to specific newsgroups. =item * When using HEAD or STAT with an article number or a range, C<412> (no group selected) is now returned instead of C<501> (syntax error). =back =item * Jeffrey S has implemented support in both B and B for whitespace in usernames/passwords provided with AUTHINFO USER/PASS. They were previously treated as invalid arguments or incorrectly parsed. B and B now treat everything after the first whitespace character following AUTHINFO USER/PASS, up to, but not including, the final CRLF, as the username/password, in conformity with S. =item * The syntax of message-IDs is now based on S (USEFOR) instead of S. The major change is that quoted-pairs have been removed from the syntax. =item * The Perl and Python filters for B now check the message-ID of articles arriving through TAKETHIS. Only CHECK and IHAVE commands previously used them. =item * Case-insensitive matches are now used for distributions, path identities, IMAP commands, header names, and control commands. (Newsgroups are still matched case-sensitively.) Message-IDs are case-sensitively matched, except for history hashes. =item * The new Archive:, Archive-At:, Comments:, and Summary: header fields defined in S and S can be used in B filters. B now checks at injection time that an article does not contain an Injection-Info: header, that an Injection-Date: header (if provided) is valid, and that the Path: header does not contain C<.POSTED>. Note that INN does not yet generate these two injection fields or include the new Path: header field C<.POSTED> keyword. These new features will be in the next major release of INN. =item * LIST SUBSCRIPTIONS now accepts an optional wildmat argument to restrict the results of this command to specific newsgroups. =item * B now supports a new LIST variant named COUNTS. LIST COUNTS is a combination of LIST ACTIVE and GROUP. It returns the same result as LIST ACTIVE except that the number of articles in a newsgroup is inserted before its status. =item * A new flag has been added to F entries: C, when present, says to feed articles accepted and filed in C (due to I) to peers based on their F feed patterns applied to the Newsgroups: header as though the article were accepted and all those groups were locally carried. This is useful if you want to run INN with a minimal F file and propagate all posts. Thanks to Andrew Gierth for the patch. =item * A new parameter has been added to F: I defines whether a line for articles posted to groups not locally carried by the news server should be added in the F log file to report unwanted newsgroups. The default is true but it can be useful to set it to false (especially when I is also used). =item * The B keyword has been added to B to specify the backlog directory of B. This is useful when several instances of B are running or when its configuration file is not the default one. =item * B now supports a new flag, B<-c>, which shows a decoded form of the storage API token. This was previously done by the contrib B script developed by Olaf Titz and Marco d'Itri. =item * The B flag in F now relies on the contents of the Injection-Info: header field if it is present to determine the origin of an article. It falls back on X-Trace: if there is no Injection-Info: header field. =item * A new "unsigned long" type bas been added to the configuration parser. It will properly warn the news administrator when a variable supposed to be positive contains a negative integer. It will prevent INN from crashing due to misconfiguration at several places where it did not expect negative values. =item * B and B now recognize the new C<403> code introduced by S for a problem preventing the requested action from being taken. =item * HDR and OVER commands now return the correct C<423> code (instead of C<420>) when the current article number is used but the article no longer exists. =item * B, B, B, B, B and B can now authenticate to news servers which only expect a username, without password, conforming to S. =item * The keyword generation code now generates a Keywords: header only if the original article does not already have one. The generated Keywords: header no longer begins with a comma. If keyword generation is set to true in F but the Keywords: header is not stored in the overview, the news administrator is warned and keyword generation deactivated, since it exists only to populate the overview data. =item * Two segfaults in keyword generation were fixed. The first occurred when an article already had a Keywords: header longer than the I parameter. The second was caused by a possible invalid pointer beyond the newly allocated Keywords: header. =item * Fixed B handling of empty lines. B was not properly discarding an empty command and was closing the connection when it received only whitespace in a command. =item * Fixed a bug in how B responded to reader commands when readers were not allowed. A superfluous blank line was sent in its response. =item * Fixed a bug in B's response to TAKETHIS when authentication is required. Previously, C<480> code was returned immediately without accepting the multi-line data block first, which broke synchronization in the NNTP protocol. =item * Fixed a bug in recognizing the article terminator when empty articles were fed to B via IHAVE or TAKETHIS, leading to treating subsequent NNTP commands as part of the article. =item * When B could not provide information for LIST ACTIVE.TIMES and LIST NEWSGROUPS, it was returning an invalid error message without a response code. The proper C<503> answer code is now returned. =item * When an unauthenticated user tried to post an article, B replied C<440> (posting not allowed) instead of the correct C<480> (authentication required) response if the user might be able to post after authentication. Thanks to Daniel Weber for the bug report. =item * Fixed a bug in both B and B answers to LIST commands where the output was not checked for valid dot stuffing. =item * Fixed a bug leading to junked non-control articles being sent to control-only feeds, and also fixed handling of poisoned control groups. Thanks to Andrew Gierth for the patch. =item * Fixed a bug in B leading to incorrect summing of B stats when I was set to an IPv6 address instead of a fully-qualified domain name. Thanks to Petr Novopashenniy for the bug report. =item * Changed how B uses B and B checkpoint messages. Previously, connections held open for multiple days led to skewed and incorrect statistics on how many articles had been received or sent. The count is now more accurate and, for each connection of a feed, only depends on I in F and I in F. =item * Fixed a bug in B Perl filter: a header field whose name begins with the name of a standardized header field was not properly handled. =item * Fixed a bug in how B was parsing Message-ID: and Supersedes: headers which contained trailing whitespace. The article was corrupted by an unexpected "\r" in the middle of the header. B now checks the syntax of the Message-ID: header field, if present. =item * Fixed various bugs in how leading whitespace was treated in headers. The HDR, XHDR and XPAT commands were not properly showing leading whitespace in header values. Lone "\n" and "\r" characters are now changed into spaces and "\r\n" is just removed. B, B, and B now keep leading whitespace in headers when generating overview data, and B now changes "\n" (when not preceded by "\r") into a space when generating overview data. =item * Fixed a bug in the generation of overview data which may corrupt previously generated overview data when a pseudo Xref: header field is injected in an extra overview field. =item * Fixed a bug in the parsing of the I wildmat in F that prevented overview data from being generated when poisoned groups were specified but a latter sub-pattern matched the group. A uwildmat expression is now correctly handled, and a potential segfault has been fixed. Thanks to Dieter Stussy for the bug report. =item * Fixed a bug when HDR, XHDR and XPAT were used when I was set to true in F. The Xref: header of articles posted to only one newsgroup appeared empty. =item * Fixed a bug in B in parsing empty overview fields when called with B<-A> or B<-F>. =item * Fixed a bug in B, which was returning only the size of the headers of an article when the C parameter was used with the B<-w> flag. It now correctly returns the size of the whole article, which is what C was documented to do. B also has a new C parameter, which can be used with the B<-w> flag to retrieve the arrival time of an article. =item * Fixed a bug in how B handles cross-posting feature. It was not properly detaching from B. Thanks to Harald Dunkel for the patch. =item * Fixed a bug in the F B flag: the count of followup groups was one less than the real number. When the value of the Followup-To: header field is C, it is no longer considered to be a followup. Thanks to Dieter Stussy for the patch. =item * When using tradindexed, the overview data for a cancelled article is now immediately removed from the overview. Thanks to Lars Magne Ingebrigtsen for the patch. =item * B has not supported the retrieval of an article with its file name for a long time. The B<-S> flag has therefore been removed. =item * B no longer rejects articles that contain more than 50 header fields. Thanks to Torsten Jerzembeck for the bug report. =item * B no longer sends superfluous mails when the B keyword is given. Mail is only sent when there is real output. Previously, there would always be headings and empty lines left over from the structuring of the full report, which are now omitted. Also, the output of programs executed with B is now included in the regular mail. Thanks to Florian Schlichting for the patch. =item * B no longer maps NULL string or list values to an empty string or list and instead maps them to undefined values. This fixes an issue reported by Kamil Jonca: B was inserting an empty Organization: header when the I parameter in F was unset. =item * Other minor bug fixes and documentation improvements. =back =head1 Changes in 2.5.1 =over 2 =item * Fixed a segfault in B which could occur when SASL was used. =item * Fixed a segfault in the keyword generation code which was assuming that an article was nul-terminated. Fixed another segfault in the keyword generation code when an article already contained a Keywords: header. Thanks to Nix for the bug reports. =item * Owing to the US-CERT vulnerability note VU#238019, Cyrus SASL library has slightly changed. B and B now handle that change. Otherwise, some answers are too long to be properly computed during SASL exchanges. =item * Fixed a memory allocation problem which caused B to die when retrieving via HDR/XHDR/XPAT the contents of an extra overview field absent from the headers of an article. The NEWNEWS command was also affected on very rare cases. Thanks to Tim Woodall for the bug report. =item * HDR/XHDR/XPAT answers are now robust when the overview database is inconsistent. When the overview schema was modified without the overview database being rebuilt, wrong results could be returned for extra fields (especially a random portion of some other header). The desired header name is now explicitly searched for in the overview information. =item * Fixed the source which is logged to the F log file for local postings when the local server is not listed in F. A wrong name was used, taken amongst known peers. The source is now logged as C. =item * Fixed a bug in the timecaf storage method: only the first 65535 articles could be retrievable in a CAF, though everything was properly stored. (A Crunched Article File contains all the articles that arrive to the news server during 256 seconds.) The storage token now uses 4 bytes to store the article sequence number for timecaf, instead of only 2 bytes. Thanks to Kamil Jonca for the bug report and also the patch. =item * Fixed a bug in both timecaf and timehash which prevented them from working on systems where short ints were not 16-bit integers. =item * When there is not enough space to write an entire CAF header, the timecaf storage manager now uses a larger blocksize. On 32-bit systems, the CAF header is about 300 bytes, leaving about 200 bytes for the free bitmap index (the remaining of a 512-byte blocksize). On 64-bit systems, the size of the CAF header could exceed 512 bytes, thus leaving no room for the free bitmap index. A S<1 KB> blocksize is then used, or a larger size if need be. =item * A new CNFS version has been introduced by Miquel van Smoorenburg in the CNFS header. CNFSv4 uses S<4 KB> blocks instead of S<512 bytes>, which more particularly makes writes faster. CNFSv4 supports files/partitions up to S<16 TB> with a S<4 KB> blocksize. Existing CNFS buffers are kept unchanged; only new CNFS buffers are initialized with that new version. =item * B now returns the contents of the expires history field as well as the hash of the message-ID. Besides, when the storage API token does not exist, B now also returns the hash of the requested message-ID. =item * The check on cancel messages when I is set to true in F has been changed to verify that at least one newsgroup in the cancel message can be found in the article to be cancelled. This new feature is from Christopher Biedl. The previous behaviour was to check whether the cancel message is from the same person as the original post, which is extremely easy to spoof; besides, S (USEPRO) mentions that "cancel control messages are not required to contain From: and Sender: header fields matching the target message. This requirement only encouraged cancel issuers to conceal their identity and provided no security". =item * The way the C line in F works has changed. History retention for an article was done according to its original arrival time; it is now according to its original posting date. Otherwise, unnecessary data may be kept too long in the F file. To achieve that, the HISremember() function in history API now expects a fourth parameter: the article posting time. Note that article expiration has not changed and is still based on arrival time, unless the B<-p> flag is passed to B or B, in which case posting time is used. =item * The default value for C has changed from C<10> to C<11> because it should be one more than the I parameter in F, so that articles posted one day into the future are properly retained in history. =item * B has been rewritten by Russ Allbery to use modern Kerberos APIs. Note that using B with PAM support and a Kerberos PAM module instead of this authenticator is still recommended. =item * A new B<-L> flag has been added by Jonathan Kamens to B so as to specify a load average limit. If the system load average exceeds the specified limit, B sleeps until it goes below the limit. =item * As UTF-8 is the default character set in S, C, C, C, C, C and C commands now require the given reason to be encoded in UTF-8, so that it can be properly sent to news readers. The creator's name given to C is also expected to be encoded in UTF-8. =item * The output of consistency checks for article storage and the F file no longer appears by default when C is used. A new B<-v> flag has been added to B so as to see it. =item * The default path for TLS certificates has changed from I/lib to I. It only affects new INN installations or generations of certificates with C. Besides, a default value has been added to I because it is required by B when TLS is used. =item * gzip(1) is now the default UUCP batcher in B instead of compress(1) because B is more widely available than B, due to old patent issues. Note that there is no impact on decompression as it is handled by B. =item * B now uses the Perl core module C rather than the deprecated F library. When used without specifying a CNFS buffer, it now properly displays the status of all CNFS buffers. =back =head1 Upgrading from 2.4 to 2.5 The following changes require your full attention because a manual intervention may be needed: =over 2 =item * In order to process control messages, B now needs the C module. Packages are available from most distributions, or you can install the module directly from CPAN (C in F, for instance on ftp.perl.org). S or later is recommended for INN. If you are using an earlier version, you will also need the C module for correct processing of control messages. (It is included with Perl itself in 5.8.0 and later.) =item * Checkgroups control messages are now differently handled by B: all matching lines in F will be used for a given checkgroups and a B action will really be executed (adding, removing and changing the status of newsgroups). You should make sure that your local configuration does not rely on the previous behaviour of only mailing changes, without actually performing them. =item * You should use the new F file shipped with INN in I and, at the same time, update your F and F files. Also make sure that your F, F and F files are properly encoded in UTF-8, as it is strongly recommended by S. =item * The F file is no longer used by INN. Two new parameters have been added to F: I and I. Although B takes care of the change during C, you should make sure that your overview database is consistent with all the fields declared in F because they will all be advertised, and C forced as the eighth overview field. See the inn.conf(5) man page for more information about these parameters. =item * The B configuration file has slightly changed. The new F file shipped with INN should be used and your possible changes backported to this new version. =item * The $SPOOLBASE variable has been renamed to $SPOOLDIR in B in order to be more consistent. It impacts shell scripts only. If you import B and use that variable in your scripts, you will have to rename it. =item * B is no longer included in INN, B now has better support for GnuPG and should be used instead. =item * The B authenticator program to check passwords with an SMB authentication is no longer included in INN. It was a stripped-down version of pam_smbpass, wasn't maintained, and likely had security problems. To authenticate to an SMB server such as Samba, use PAM and B's PAM support instead. =back The parameters used by B to provide TLS support are now I, I, I and I in F. The F file used for that in previous versions of INN is obsolete. B takes care of the change during C. The I parameter has been renamed to I in F; B handles this renaming during the update. In F, B should be run directly rather than through B. B will attempt to take care of this modification during C. When starting B by hand, B can just be run directly rather than using B. If you get error messages about resetting the file descriptor limits, you may need to increase the file descriptor limits. See the sample init script in F for an example of how to do this. If you are upgrading from a version prior to S, see also L. =head1 Changes in 2.5.0 =over 2 =item * Ken Murchison has contributed SASL authentication support for B, implementing the AUTHINFO SASL section of S. If the B<--with-sasl> option is given to C, B will be able to authenticate clients via secure SASL mechanisms. =item * Julien Elie has implemented in B the new version of the NNTP protocol described in S, S and S. Consequently, B now recognizes the CAPABILITIES command, the HDR and LIST HEADERS commands, the second optional argument to specify a range of articles to LISTGROUP, the OVER command, as well as the C<:bytes> and C<:lines> metadata items. =item * Heath Kehoe has added the ability to compress overview data before it is stored in ovdb. It significantly improves the performance of this storage method and reduces the time spent by B. See the new B<--with-zlib> option to C and the ovdb(5) man page. =item * Alexander Bartolich has greatly improved B and especially its XHTML output (a XSL transformation is also provided, if needed, in F, in the F directory). =item * B and B are no longer part of INN and are no longer used. Instead, a separate setuid root helper program written by Russ Allbery is used to bind to the news ports (and does only that), and is run by B and B when necessary. This means that INN may not be able to increase file descriptor limits for itself the way that it could before. If you get error messages about resetting the file descriptor limits, you may need to increase the file descriptor limits as root before running B as the news user. See the sample init script in F for an example of how to do this. More information on file descriptor limits can be found in F. =item * INN's IPv6 support was largely rewritten by Russ Allbery. IPv4 and IPv6 are now handled through the same code wherever possible, the new IPv6-aware APIs are used everywhere possible, and replacement functions are provided for systems that don't have them yet. The network code is now much more centralized, eliminating lots of duplicate code and adding better IPv6 support to some utilities. =item * INN now uses S or later for configuration. As a result, some C options have changed slightly and more of the standard B<--*dir> options should be supported in lieu of the old INN-specific options. See C for the available options. =item * Thanks to Kirill Berezin, the buffindexed overview method now supports buffers larger than S<2 GB>. It is not necessary to compile INN with large file support to use such large buffers with buffindexed. Buffindexed is now also more robust with mmaped files and uses more optimized data placement. =item * B, a miniature IHAVE-only leaf server written by Russ Allbery, is now included. See the tinyleaf(8) man page for more information. =item * B recognizes the new application/news-groupinfo entity described in USEPRO and can handle character set conversions of newsgroup descriptions. The C and C modules are used. Processing control messages has been greatly improved, especially checkgroups: the F and F files are now properly updated when they are processed, and all matching lines in F for a given checkgroups are honoured (which for instance allows to use both B and B actions for the same checkgroups message). A new F file has also been added in I. Rules set in that file override rules in F, allowing administrators to specify local rules for some control messages without modifying the F configuration file that comes with INN. It also specifies encodings to use for the F file. By default, UTF-8 will be used for newsgroup descriptions, as strongly recommended by S. =item * The Perl and Python I hooks are now called when B is shutting down via either C or C with a new mode value of C. This will allow the Perl hooks to save filter data across B restarts without requiring that the news administrator throttle the server first. (Python already had a separate close hook that is also called.) =item * The legacy B script has been replaced with a real INN Perl module C for Perl programs. The location of Perl modules can be set with the B<--with-libperl-dir> option to C. All Perl scripts shipped with INN have been converted to use that module. You may want to consider using C in your Perl scripts, though B is still provided with INN. =item * Support for embedded Tcl filters in B has been removed. It hasn't worked for some time and causes B crashes if compiled in (even if not used). If someone wants to step forward and maintain it, we recommend starting from scratch and emulating the Perl and Python filters. =item * If I is set in F, the whole user-supplied Path: header will now be stripped. Previously, the final component of the user-supplied Path: would still be retained. =item * B can now set the envelope-from address of the mails it sends. A third optional part in F entries has been added by S to achieve that. =item * The B<-g> option to B is no longer supported. If you are verifying passwords against the system password database, see the ckpasswd(8) man page, and in particular the B<-s> option. (A much better idea would be to just use PAM, which B supports.) =item * Fixed a bug in C which was resetting the low and high water marks of empty newsgroups in the F file. This command now makes the low water mark one more than the real high water mark. The answers to LIST ACTIVE, GROUP and LISTGROUP have also been fixed to do that. =item * Support for bzip2-compressed batches (with B) has been added. =item * B now processes B dropped files during daily maintenance, running B. =item * Support for I and I parameters in F allows to set the news user and the news group under which the news server runs. Thanks to Ivan Shmakov for this feature. New other options have been added to configuration files: I in F, I, I and I in F, and I in F. The B<--with-http-dir> option has also been added to C to set I in F. The I parameter has been renamed to I in F. =item * The F file has been removed in favour of new parameters in F to deal with TLS support: I, I, I and I. =item * The F file has been removed in favour of new parameters in F to deal with transition periods to accommodate overview reconfigurations. It is now possible to specify on the one hand the fields that should be advertised by B in response to LIST OVERVIEW.FMT and used for HDR, XHDR and XPAT requests (see the new I parameter) and on the other hand the additional fields that should be silently generated (see the new I parameter). =item * Support for S versions prior to 4.3 has been dropped. You will have to use at least S; the recommended version is 4.7. =item * INN now builds entirely free of warnings from GCC with fairly aggressive warning options enabled. This involved lots of cleanup of const strings, signed versus unsigned type handling, correcting printf formats, and other changes that fixed obscure bugs and made INN's code more robust. Russ Allbery has also done considerable cleanup work on some of INN's internals, simplifying, refactoring, and removing duplicate code. =item * INN's test suite is now much more comprehensive and tests some high-level functions as well as more of the portability and utility function layer. =item * A lot of work has been done on documentation: improvements of existing documents, new documentation, and proof-reading. Sample configuration files are also more detailed. =back =head1 Changes in 2.4.6 =over 2 =item * Fixed the segfault of the radius authenticator when none of the radius servers respond. Thanks to Matija Nalis for this patch. =item * Fixed a lost initialization in buffindexed, which resolves a potential segfault, thanks to a patch by Kirill Berezin. =item * INN now properly supports S (and also 5.8.9); Perl filters were causing B to segfault on a few systems like FreeBSD. =item * Fixed a long-standing bug which affected Perl hooks for B: the variable containing the body of an article was not properly created, which caused regular expressions matching new lines to fail. It especially affected filters like Cleanfeed which sometimes failed to detect unwanted articles. To fix that issue, Julien Elie added the use of a shared string, available since S, with a fall back to a slower but reliable copy of such bodies in case the function is not available. Using a Perl version superior to 5.7.2 is therefore recommended. =item * Fixed two bugs which could prevent B from being run as a daemon in FreeBSD. Thanks to Johan van Selst for having identified the problem and to Kai Gallasch for having provided a testing FreeBSD server. The listening address was not initialized to C<::0> or C<0.0.0.0> when the B<-b> flag was not used and an incorrect size was given when IPv6 was enabled and the binding done using IPv4. =item * Some annoying assertion failures occurring in B have been fixed by Russ Allbery and Julien Elie. =item * Fixed a bug in B for aliased newsgroups. Only C<=> was written to the F file. Thanks to S for this patch. =item * Fixed a bug which caused B not to honour the B flag in F. =item * Fixed a bug in the IP address displayed for C in B's status file. It was not correctly initialized. =item * Fixed a permission issue: XHDR and XPAT were not checking the rights the user had to read articles when accessing them by their message-ID. =item * Fixed a bug in the replies of XHDR, XOVER and XPAT when the newsgroup is empty. Two initial replies were sent instead of one: the right C<420> code followed by a wrong C<224> code. =item * When no newsgroup is selected, LISTGROUP now returns the right C<412> code (instead of C<481>). =item * B now uses a range of permissions to see whether the file modes are correctly set. Therefore, different configurations depending on the security the user wants to enforce on his system are possible. =item * A new improved version of B is shipped with INN. The B<-u> flag permits to automatically update the F file (with a proper number of tabulations and an alphabetical sort), removing obsolete descriptions and adding new ones. A second argument on command-line permits to specify which newsgroups should not be checked, so as not to treat them. =item * An I keyword has been added by James Ralston to B in order to supply another mail address than the one set at configure time for Usenet daily reports. =item * An updated F file with information about the aioe.*, perl.* and si.* hierarchies is provided; F is also up to date. =item * INN supports S, which is the recommended version to use owing to various bugs affecting previous versions of S. =item * Other minor bugs have also been fixed. =back =head1 Changes in 2.4.5 =over 2 =item * Fixed the "alarm signal" around C in B: it allows a proper disconnection of news clients which were previously hanging when posting an article through a SSL connection. Moreover, the I parameter now works on SSL connections. Thanks to Matija Nalis for the patch. =item * SO_KEEPALIVE is now implemented for SSL TCP connections on systems which support it, allowing system detection and closing the dead TCP SSL connections automatically after system-specified time. Thanks to Matija Nalis for the patch. =item * Fixed a segmentation fault when an article of a size greater than remaining stack is retrieved via SSL. Thanks to Chris Caputo for this patch. =item * Fixed a few segfaults and bugs which affected both Python B and B hooks. They no longer check the existence of methods not used by the hooked script. An issue with Python exception handling was also fixed, as well as a segfault fixed by Russ Allbery which happened whenever one closes and then reopens Python in the same process. Julien Elie also fixed a bug when reloading Python filters (they were not always correctly reloaded) and a segfault when generating access groups with embedded Python filters for B. Many thanks to David Hlacik for its bug reports. =item * The F stub file in order to test Python B hooks, as mentioned in their documentation, is now installed; only F was previously installed in I. Also fixed a bug in F and add missing methods to it. =item * Fixed a long-standing bug in B which prevented it from correctly reporting B and B log messages. =item * Fixed a hang in Perl hooks on (at least) HP/PA since S. =item * Fixed a compilation problem on some platforms because of AF_INET6 which was not inside a HAVE_INET6 block in B. =item * Fixed a bug in B which contained thrice the same IPs for each peer; it unnecessarily slowed the peer IP rotation for B. Thanks, S, for having seen that. Miquel van Smoorenburg provided the patch. =item * A new I improved version of B is shipped with this INN release. This new version is provided by Geraint Edwards. He added no more than S<16 flags>, fixed some bugs and integrated the B contrib script by Kai Henningsen, adding again S<6 other> flags. A long-standing but very minor bug in the B<-g> option was especially fixed and items from the to-do list implemented. Many thanks again to Geraint Edwards. =item * New headers are accessible through Perl and Python B filtering hooks. You will find the exact list in the INN Python Filtering and Authentication Hooks documentation (F) and in Python samples. Thanks to Matija Nalis for this addition of new useful headers. =item * New samples for Python B hooks are shipped with INN: F for access control and F for dynamic access control. The F script is now only used for authorization control. See the F man page for more information (especially the I, I and I parameters). The documention about INN Python Filtering and Authentication Hooks has also been improved by Julien Elie. =back =head1 Changes in 2.4.4 =over 2 =item * Fixed incomplete checking of packet sizes in the B interface in the no-Unix-domain-sockets case. This is a potential buffer overflow in dead code since basically all systems INN builds on support Unix domain sockets these days. Also track the buffer size more correctly in the client side of this interface for the Unix domain socket case. =item * Group blocks in F are now correctly parsed and no longer cause segfaults when loading this file. =item * Fixed a problem with B continuously segfaulting on amd64 hardware (and possibly on lots of 64-bit platforms). Many thanks to Ollivier Robert for his patch and also to Kai Gallasch for having reported the problem and provided the FreeBSD server to debug it. =item * B now rotates B's log file, which prevents B from silently dying when its log file reaches S<2 GB>. =item * S support has been added to INN thanks to Jakub Bogusz. =item * Some news clients hang when posting an article through a SSL connection: it seems that B's SSL routines make it wrongly wait for data completion. In order to fix the problem, the select() wait is now just bypassed. However, the IDLE timer stat is currently not collected for such connections. Thanks to Kachun Lee for this workaround. =item * Fixed a bug in the display of the used compressor (C was used if arguments were passed to B or B). =item * Fixed a bug in B and B which prevented useful error messages to be seen. Also add the B<-x> flag to B in order to insert Xref: headers in articles which lack one. =item * If compiling with S, use its ndbm compatibility layer for B in preference to searching for a traditional dbm library. INN also supports S, 4.5 and 4.6 thanks to Marco d'Itri. =item * B now properly closes stdin/out/err when it becomes a daemon. The issue was reported by Viktor Pilpenok and fixed by Marco d'Itri. =item * Added support for Diablo quickhash and hashfeed algorithms. It allows to distribute the messages among several peers (new B flag for F). Thanks to Miquel van Smoorenburg for this implementation in INN. =item * B now listen on separate sockets for IPv4 and IPv6 connections if the IPV6_V6ONLY socket option is available. There might also be operating systems that still have separate IPv4 and IPv6 TCP implementations, and advanced features like TCP SACK might not be available on v6 sockets. Thanks to Miquel van Smoorenburg for this patch. =item * The two configuration options I and I can now be set on a per-peer basis for B. Setting I to C tells B to never attempt an IPv6 connection to that host. Thanks to Miquel van Smoorenburg for this patch. =item * Added a I parameter to F (modelled on the concept of I) to permit passing of command line arguments to instances of B spawned from B. =item * A new F parameter called I has been added: it allows to append a common name to the Path: header on all incoming articles. I and I (if set) are still appended to the path as usual, but I is always appended as the last element (e.g. on the leftmost side of the Path: header). Thanks to Miquel van Smoorenburg for this feature. =item * B has been rewritten to use C. Indeed, F is no longer shipped with S and the script did not work. =item * B will now check for a timeout and re-open the socket if required. Additionally, B will switch to cancel_ctlinnd in case cancel_nntp fails after sending the Message-ID. Thanks to Christoph Biedl for the patch. A more detailed documentation has also been written for perl-nocem(8). =item * The RADIUS configuration is now wrapped in a C block in F. =item * Checkgroups when there is nothing to change no longer result in sending a blank mail to administrators. Besides, no mail is sent by B for the creation of a newsgroup when the action is C. =item * Checkgroups are now properly propagated even though the news server does not carry the groups they are posted to. =item * B and B now handle wire format messages so that articles from the spool can be directly fed to them. =item * Newgroup control messages for existing groups now change their description. If a mail is sent to administrators, it reminds them to update their F file. It also warns when there are missing or obsolete descriptions. Furthermore, the F file is now written prettier (from one to three tabulations between the name of the group and its short description) and to.* groups cannot be created. =item * The sample F file has been extensively updated. =item * Fixed empty LISTGROUP replies which were not terminated. Thanks to David Canzi for the patch. =item * In response to a LIST [file] command, if the file does not exist, we assume it is not maintained and return C<503> instead of C<215> and an empty file. Moreover, capability to LIST ACTIVE.TIMES for a wildmat pattern as its third argument has been added in order to select wanted newsgroups. =item * B now tries to authenticate if it does not receive a C<200> return code after MODE READER. Indeed, it might be able to post even with a C<201> return code and also with another codes like C<440> or C<480>. =item * If creating a new F file, set the ownership and mode appropriately. B also expects fewer things to be private to the news user. Most of the configuration files will never contain private information like passwords. =item * Other minor bug fixes and documentation improvements. =back =head1 Changes in 2.4.3 =over 2 =item * Previous versions of INN had an optimization for handling XHDR Newsgroups that used the Xref: header from overview. While this does make the command much faster, it doesn't produce accurate results and breaks the NNTP protocol, so this optimization has been removed. =item * Fixed a bug in B that allowed it to accept articles with duplicated headers if the header occurred an odd number of times. Modified the programs for rebuilding overview to use the last Xref: header if there are multiple ones to avoid problems with spools that contain such invalid articles. =item * Fixed yet another problem with verifying that a user has permissions to approve posts to a moderated group. Thanks, Jens Schlegel. =item * Increase the send and receive buffer on the Unix domain socket used by B. This should allow longer replies (particularly for B) on platforms with very low default Unix domain socket buffer sizes. =item * B's handling of articles with nul characters, NNTP errors, header problems, and deferrals has been significantly improved. =item * Thomas Parmelan added support to B for specifying the funnel or exploder site to flush for feeds managed through one and fixed a problem with picking up old stranded work files. =item * Many other more minor bug fixes, optimization improvements, and documentation fixes. =back =head1 Changes in 2.4.2 =over 2 =item * INN is now licensed under a less restrictive license (about as minimally restrictive as possible shy of public domain), and the clause similar to the old BSD advertising clause has been dropped. =item * C and C now always install the newly built binaries, rather than only installing them if the modification times are newer. This is the behavior that people expect. C now also automatically builds a new (empty) history database if one doesn't already exist. =item * The embedded Tcl filter code has been disabled (and will be removed entirely in the next major release of INN). It hasn't worked for some time and causes B crashes if compiled in (even if not used). If someone wants to step forward and maintain it, I recommend starting from scratch and emulating the Perl and Python filters. =item * B should now successfully handle messages from INN up to the maximum allowable packet size in the protocol, fixing problems sites with many active peers were having with B output. =item * Overview generation has been fixed in both B and B to follow the rules in the latest NNTP draft rather than just replacing special characters with spaces. This means that the unfolding of folded header lines will not introduce additional, incorrect whitespace in the overview data. =item * B now uniformly responds with a C<480> or C<502> status code to attempts to read a newsgroup to which the user does not have access, depending on whether the user has authenticated. Previously, it returned a C<411> status code, claiming the group didn't exist, which confuses the reactive authentication capability of news readers. =item * If a user is not authorized to approve articles (using the C I control in F), articles that include Approved: headers will be rejected even if posted to unmoderated groups. Some other site may consider that group to be moderated. =item * The configuration parser used for F and others now correctly handles C<#> inside quoted strings and is more robust against unmatched double quotes. =item * Messages mailed to moderators had two spaces after the colons in the headers, rather than one. This bug has been fixed. =item * A bug that could cause heap corruption and random crashes in B if INN were compiled with Python support has been fixed. =item * Some problems with B's tracking of article size and enforcement of the configured maximum article size have been fixed. =item * B will now correctly verify signatures generated by GnuPG and better supports GnuPG as the PGP implementation. =item * INN's code should now be more 64-bit clean in its handling of size_t, pointer differences, and casting of pointers, correcting problems that showed up on 64-bit platforms like AMD64. =item * Improved the error reporting in the history database code, in B, in B, and in B. =item * Many other, more minor bugs have also been fixed. =back =head1 Changes in 2.4.1 =over 2 =item * SECURITY: Handle the special filing of control messages into per-type newsgroups more robustly. This closes a potentially exploitable buffer overflow. Thanks to Dan Riley for his excellent bug report. =item * Fixed article handling in B so that articles without a Path: header (arising from peers sending malformatted articles or injecting malformatted articles through rnews) would not cause B to crash. (This was not exploitable.) =item * Fixed a serious bug in XPAT handling, thanks to Tommy van Leeuwen. =item * C now looks for B only in F and F, not on the user's path. This should reduce the need for B<--with-sendmail> if your preferred B is in a standard location. =item * The robustness of the tradindexed overview method has been further increased, handling more edge cases arising from corrupted databases and oddly-named newsgroups. =item * B now never decreases the high water mark of a newsgroup when renumbering, which should help ameliorate overview and F file synchronization problems. =item * Do not close and reopen the F file on B reload when the server is paused or throttled. This was breaking B reload all during a server pause. =item * Various minor portability and compilation issues fixed. Substantial numbers of compiler warnings have been cleaned up, thanks largely to work by Ilya Kovalenko. =item * Multiple other more minor bugs have been fixed. =item * Documentation and man pages have been clarified and updated. =back =head1 Upgrading from 2.3 to 2.4 The F parser has changed between S and 2.4. Due to that change, options in F that contain whitespace or a few other special characters must be quoted with double quotes, and empty parameters (parameters with no value) are not allowed. S comes with a script, B, run automatically during C, that will attempt to fix any problems that it finds with your F file, saving the original as F. This change is the beginning of standardization of parsing and syntax across all of INN's configuration files. The history subsystem now has a standard API that allows other backends to be used. Because of this, you now need to specify the history method in F. Adding: hismethod: hisv6 will tell INN to use the same history backend as was used in previous versions. B should take care of this for you. ovdb is known to have some locking and timing issues related to how B shuts down (or fails to shut down) the overview databases. If you have stability problems with ovdb, try setting I to true in F. This will funnel all ovdb reads through a single process with a cleaner interface to the underlying S database. If you use Perl authentication for B (if I in F is true), there have been major changes. See "Changes to Perl Authentication Support for nnrpd" in F for details. Similarly, if you use Python authentication for B (if I in F is true), there have been major changes. See "Changes to Python Authentication and Access Control Support for nnrpd" in F for details. If you use B, it has been completely rewritten and now takes a configuration file to specify its behavior. See its man page for more information. If you use B, it is no longer included in INN since the new B can handle all of the same functionality. The wildmat API has been renamed (to uwildmat and friends; see uwildmat(3) for the interfaces) to distinguish it from Rich $alz's original version, since it now supports UTF-8. This may require changes in other software packages that link against INN's libraries. If you are upgrading from a version prior to S, see L. =head1 Changes in 2.4.0 =over 2 =item * IPv6 support has been added, disabled by default. If you have IPv6 connectivity, build with B<--enable-ipv6> to try it. There are no known bugs, but please report any problems you find (or even successes, if you use an unusual platform). There are a few changes of interest; further information is available in F. =item * The tradindexed overview method has been completely rewritten and should be considerably more robust in the face of system crashes. A new utility, B, is provided to examine the contents of the overview database, repair inconsistencies, and rebuild the overview for particular groups from a tradspool news spool. See tdx-util(8) for more details. =item * The Perl and Python authentication hooks for readers have been extensively overhauled and integrated better with F. See the Changes sections in F and F for more details. =item * B now optionally supports article injection via IHAVE, see readers.conf(5). Any articles injected this way must have Date, From, Message-ID, Newsgroups, Path, and Subject headers. X-Trace and X-Complaints-To headers will be added if the appropriate options are set in F, but other headers will not be modified/inserted (e.g. NNTP-Posting-Host, NNTP-Posting-Date, Organization, Lines, Cc, Bcc, and To headers). =item * B now handles arbitrarily long lines in POST and IHAVE; administrators who want to limit the length of lines in locally posted articles will need to add this to their local filters instead. =item * B no longer handles the poorly-specified S optional fourth argument to the NEWGROUPS command specifying the "distributions" that the command was supposed to apply to. Clients that use that argument will break. There are not believed to be any such clients, and it's easy enough to just filter the returned list of newsgroups (which is generally fairly short) to achieve the same results. =item * B no longer accepts UTC as a synonym for GMT for NEWGROUPS or NEWNEWS. This usage was never portable, and was rejected by the NNTP working group. It is being removed now in the hope that it will be caught before anyone starts to rely on it. =item * B supports a new peer parameter, I, that if set to true feeds any backlog to a peer before new articles, see innfeed.conf(5). When used in combination with I set to C<1>, this can be used to enforce in-order delivery of messages to a peer that is doing Xref slaving, avoiding cases where a higher-numbered message is received before a lower-numbered message in the same group. =item * Several other, more minor protocol issues have been fixed: connections rejected due to the connection rate limiting in B receive C<400> replies instead of C<504> or C<505>, and ARTICLE without an argument will always either retrieve the current article or return a C<423> error, never advance the current article number to the next valid article. See F for all of the known issues with INN's compliance with the current NNTP draft. =item * All accesses to the F file for all parts of INN now go through a generic API like the storage and overview subsystems do. This will eventually allow new history implementations to be dropped in without affecting the rest of INN, and will significantly improve the encapsulation of the history subsystem. See the libinnhist(3) man page for the details of the interface. =item * INN now uses a new parser for the F file. This means that parameters containing whitespace or other special characters must now be quoted; see inn.conf(5). It fixes the long-standing bug that certain values must be included in F even if using the defaults for the use of shell or Perl scripts, and it will serve as the basis for standardizing and cleaning up the configuration file parsing in other parts of INN. B is run during C and should convert an existing F file for you. =item * B has been replaced by a completely rewritten version from Marco d'Itri, Edvard Tuinder, and Miquel van Smoorenburg, which uses a configuration file that specifies batch sizes, compression methods, and hours during which batches should be generated. The old B script has been retired, since B can now handle everything that it did. =item * Two C options have changed names: B<--with-tmp-path> is now B<--with-tmp-dir>, and B<--with-largefiles> is now B<--enable-largefiles>, to improve consistency and better match the C option guidelines. =item * Variables can now be used in the F file to make it easier to specify many similar feeds or feed patterns. See the newsfeeds(5) man page for details. =item * Local connections to INN support a new special mode, MODE CANCEL, that allows efficient batch cancellation of messages. This is intended to be the preferred interface for external spam and abuse filters like NoCeM. See "CANCEL FEEDS" in innd(8) for details. =item * Two new options, I and I, have been added to F to aid in building NFS based shared reader/writer platforms. On the writer server configure I to true and on all of the readers configure I to true; these options add calls to force data out to the NFS server and force it to be read directly from the NFS server at the appropriate moments. Note that it has only been tested on S, using CNFS as the storage mechanism and tradindexed as the overview method. =item * A new option, I, has been added to F. If set to true (the default), then the tradindexed overview method will use mmap() to access its overview data (in 2.3 you couldn't control this; it always used mmap). =item * Thanks to code contributed by CMU, B can now feed an IMAP server as well as other NNTP servers. See the man page for innfeed(8) for more information. =item * An authenticator, B, that checks a username and password against a remote Samba server is now included. See auth_smb(8) for details. =item * The wildmat functions in INN now support UTF-8, in a way that should allow them to still work with most simple 8-bit character sets in widespread use. As part of this change, some additional wildmat interfaces are now available and the names have changed (to uwildmat, where C is for Unicode). See uwildmat(3) for the details. =item * The interface between external authenticators and B is now properly documented, in F. A library implementing this interface in C is provided, which should make it easier to write additional authenticators resolvers. See libauth(3) for details, and any of the existing programs in F for examples. =item * INN now checks to ensure that the configured temporary directory is not world-writeable. Additionally, most (if not all) of the temporary file creation in INN now uses functions that create temporary files properly and safely. =item * All of the applicable bug fixes from the S STABLE series are also included in S. =back =head1 Changes in 2.3.5 =over 2 =item * Clients using POST are no longer permitted to provide an Injector-Info: header. =item * Fixed a bug causing posts with Followup-To: set to a moderated group to be rejected if the posting user didn't have permission to approve postings. =item * Fixed bugs in B with setuid B or setgid B, in B with F parameters containing shell metacharacters but no spaces, and in F with some versions of B. Fixed a variety of size-related printf format warnings (e.g., C<%d> vs. C<%ld>) thanks to the work of Winfried Szukalski. =back =head1 Changes in 2.3.4 =over 2 =item * LIST ACTIVE no longer returns data when given a single group argument if the client is not authorized to read that group. =item * XHDR and XPAT weren't correctly parsing article headers, resulting in searches for the header "newsgroup" matching the header "newsgroups". =item * Made CNFS more robust against crashes by actually syncing the cycbuff headers to disk as was originally intended. Fixed a memory leak in the tradspool code. =item * Two bugs in B when using GnuPG were fixed: it now correctly checks for B (rather than B) when told to use GnuPG and expects the keyring to be F (not F). =item * Substantial updates to the sample provided F file. =item * Compilation fixes with S, S, current versions of Linux (including with large file support), and Tru64. B fixes for ReiserFS. =item * Various bugs in the header handling in B have been fixed, including hangs when using virtual domains and improper processing of folded headers under certain circumstances. =item * Other minor bug fixes and documentation improvements. =back =head1 Changes in 2.3.3 =over 2 =item * B now supports using GnuPG to check signatures (rather than PGP) without the B wrapper. GnuPG can check both old-style RSA signatures and new OpenPGP signatures and is recommended over S. If you have GnuPG installed, B will use it rather than PGP, which means that you may have to create a new key ring for GnuPG to use to verify signatures if you were previously using PGP. =item * Users can no longer post articles containing Approved: headers to moderated groups by default; they must be specifically given that permission with the I parameter in F. See the man page for more details. =item * Two bugs in repacking overview index files and a reliability bug with writing overview data were all fixed in the tradindexed overview method, hopefully making it somewhat more reliable, particularly for B. =item * If F exists in the INN binary directory, it will be run with the start or stop argument whenever B is run. This is available as a hook for local startup and shutdown code. =item * The default history table hash sizes were increased because a too-small value can cause serious performance problems (whereas a too-large hash just wastes a bit of disk space). =item * The sample F file has been extensively updated. =item * Wildmat exclusions (C<@> and C) should now work properly in F newsgroup patterns. =item * The implementation of the B<-w> flag for B was fixed; previously, the value given to B<-w> to change B's notion of the current time was scaled by too much. =item * Various other more minor bug fixes, standards compliance fixes, and documentation improvements. =back =head1 Changes in 2.3.2 =over 2 =item * B can again handle regular filenames as input as well as storage API tokens (allowing it to be used to import an old traditional spool). =item * Several problems with tagged-hash history files have been fixed thanks to the debugging efforts of Andrew Gierth and Sang-yong Suh. =item * A very long-standing (since S!) NNTP protocol bug in B was fixed. The response to an ARTICLE command retrieving a message by Message-ID should have the Message-ID as the third word of the response, not the fourth. Fixing this is reported to I cause problems with some Netscape browsers, but other news servers correctly follow the protocol. =item * Some serious performance problems with expiration of tradspool should now be at least somewhat alleviated. tradspool and timehash now know how to output file names for removal rather than tokens, and B's ability to remove regular files has been restored. This should bring expiration times for tradspool back to within a factor of two of pre-storage-API expiration times. =item * Added a sample F file and documentation for it and B. =item * Various other bug fixes and documentation updates. =back =head1 Changes in 2.3.1 =over 2 =item * B no longer downloads the F file, no longer tries to send postings to moderated groups to the moderator directly, and in general duplicates less of the functionality of B, instead letting B handle it. This fixes the problem of B not working properly for users other than news without being setgid. =item * Added a man page for B. =item * A serious bug in the embedded Perl authentication hooks was fixed, thanks to Jan Rychter. =item * The annoying compilation problem with embedded Perl filtering on Linux systems without libgdbm installed should be fixed. =item * INN now complains loudly at configure time if the configured path for temporary files is world-writeable, since this configuration can be a security hole. =item * Many other varied bug fixes and documentation fixes of all sorts. =back =head1 Upgrading from 2.2 to 2.3 There may be additional things to watch out for not listed here; if you run across any, please let know about them. Simply doing a C is not sufficient to upgrade; the history and overview information will also have to be regenerated, since the formats of both files have changed between 2.2 and 2.3. Regardless of whether you were using the storage API or traditional spool under 2.2, you'll need to rebuild your overview and history files. You will also need to add a F file, if you weren't using the storage API under S. A good default F file for 2.2 users would be: method tradspool { newsgroups: * class: 0 } Create this F file before rebuilding history or overview. If you want to allow readers, or if you want to expire based on newsgroup name, you need to tell INN to generate overview data and pick an overview method by setting I in F. See F and inn.conf(5) for more details. The code that generates the dbz index files has been split into a separate program, B. B still generates the base F file and the overview information, but some of its options have been changed. To rebuild the history and overview files, use something like: makehistory -b -f history.n -O -T /usr/local/news/tmp -l 600000 (change the F path to some directory that has plenty of temporary space, and leave off B<-O> if you're running a transit-only server and don't intend to expire based on group name, and therefore don't need overview.) Or if your overview is buffindexed, use: makehistory -b -f history.n -O -F Both will generate a new history file as F and rebuild overview at the same time. If you want to preserve a record of expired Message-IDs in the history file, run: awk 'NF==2 { print; }' < history >> history.n to append them to the new history file you created above. Look over the new history file and make sure it looks right, then generate the new index files and move them into place: makedbz -s `wc -l < history.n` -f history.n mv history.n history mv history.n.dir history.dir mv history.n.hash history.hash mv history.n.index history.index (Rather than F<.hash> and F<.index> files, you may have a F<.pag> file if you're using tagged hash.) For reader machines, F has been replaced by F. There currently isn't a program to convert between the old format and the new format (if you'd like to contribute one, it would be welcomed gratefully). The new file is unfortunately considerably more complex as a result of its new capabilities; please carefully read the example F provided and the man page when setting up your initial configuration. The provided commented-out examples cover the most common installation (IP-based authentication for all machines on the local network). INN makes extensive use of mmap(2) for the new overview mechanisms, so at the present time NFS-mounting the spool and overview on multiple reader machines from one central server probably isn't feasible in this version. mmap tends to interact poorly with NFS (at the least, NFS clients won't see updates to the mapped files in situations where they should). (The preferred way to fix this would, rather than backing out the use of mmap or making it optional, to add support for Diablo-style header feeds and pull-on-demand of articles from a master server.) The flags for B have changed, plus you probably don't want to run B at all any more. Letting B write overview data itself results in somewhat slower performance, but is more reliable and has a better failure mode under high loads. Writing overview data directly is the default, so in a normal upgrade from 2.2 to 2.3 you'll want to comment out or remove your B entry in F and set I to false in F. B is no longer installed, and no longer works (even with traditional spool). If you have an entry for B in F, remove it. If you're importing a traditional spool from a pre-storage API INN server, it's strongly recommended that you use NNTP to feed the articles to your new server rather than trying to build overview and history directly from the old spool. It's more reliable and ensures that everything gets put into the right place. The easiest way to do this is to generate, on your old server, a list of all of your existing article files and then feed that list to B. Further details can be found in the FAQ at L. If you are using a version of Cleanfeed that still has a line in it like: $lines = $hdr{'__BODY__'} =~ tr/\n/\n/; you will need to change this line to: $lines = $hdr{'__LINES__'}; to work with S or later. This is due to an internal optimization of the interface to embedded filters that's new in S. =head1 Changes in 2.3.0 =over 2 =item * New F file (replaces F) which allows more flexible specification of access restrictions. Included in the sample implementations is a RADIUS-based authenticator. =item * Unified overview has been replaced with an overview API, and there are now three separate overview implementations to choose from. One (tradindexed) is very like traditional overview but uses an additional index file. The second (buffindexed) uses large buffers rather than separate files for each group and can handle a higher incoming article rate while still being fast for readers. The third (ovdb) uses S to store overview information (so you need to have S installed to use it). The I key in F chooses the overview method to use. Note that ovdb has not been as widely tested as the other overview mechanisms and should be considered experimental. =item * All article storage and retrieval is now done via the storage API. Traditional spool is now available as a storage type under the storage API. (Note that the current traditional spool implementation causes nightly expire to be extremely slow for a large number of articles, so it's not recommended that you use the tradspool storage method for the majority of a large spool.) =item * The timecaf storage method has been added, similar to timehash but storing multiple articles in a single file. See F for details on it. =item * INN now supports embedded Python filters as well as Perl and Tcl filters, and supports Python authentication hooks. =item * There is preliminary support for news reading over SSL, using OpenSSL. =item * To simplify anti-abuse filtering, and to be more compliant with news standards and proposed standards, INN now treats as control messages only articles containing a Control: header. A Subject: line beginning with C is no longer sufficient for a message to be considered a control message, and the Also-Control: header is no longer supported. =item * The INN build system no longer uses subst. (This will be transparent to most users; it's an improvement and modernization of how INN is configured.) =item * The build and installation system has been substantially overhauled. C now updates scripts as well as binaries and documentation, there is better support for parallel builds (C), there is less C recursion, and far more of the system-dependent configuration is handled directly by C. libtool build support (including shared library support) should be better than previous releases. =item * All of the applicable bug fixes from the S STABLE series are also included in S. =back =head1 Changes in 2.2.3 =over 2 =item * INN no longer installs B setgid news or B setuid root by default. If you need the old behavior, B<--enable-uucp-rnews> and/or B<--enable-setgid-inews> must be given to C. See F for more information. =item * A security hole when I is turned on in F (not the default) was fixed. =item * Message-IDs are now limited to 250 octets to prevent interoperability problems with other servers. =item * Various other security paranoia fixes have been made. =item * Embedded Perl filters fixed to work with S. =item * Lots of bug fixes. =back =head1 Changes in 2.2.2 =over 2 =item * Various minor bug fixes and a Y2K bug fix. The Y2K bug is in version version 2.2.1 only and will show up after S, 2000 when a news reader issues a NEWNEWS command for a date prior to the year 2000. =back =head1 Changes in 2.2.1 =over 2 =item * Various bug fixes, mostly notably fixes for potential buffer overflow security vulnerabilities. =back =head1 Changes in 2.2.0 =over 2 =item * New F file (replaces F). =item * New (optional) way of handling non-cancel control messages (B) that serializes them and prevents server overload from control message storms. =item * Support for B to fetch F file with B; configured by default to use if you run B. Be sure to read the manual page for B to configure an F file for your site, and test B if you do not C with B or B. Also see L. =item * Some options to C are now moved to F (I and I, without the hyphen). =item * B, a portable version of df(1), is supplied. =item * New B program to show stats of CNFS buffers. =item * B and B programs for gatewaying news to mail and mail to news are supplied. =item * B program for doing a sucking feed is provided (not meant for large feeds). =item * The B script is obsolete (and lives in the F directory, for now). =back $Id: news.pod 9923 2015-07-14 16:48:11Z iulius $ =cut inn-2.6.0/doc/pod/nntpsend.ctl.pod0000644000175200017520000000273712575023702016376 0ustar iuliusiulius=head1 NAME nntpsend.ctl - List of sites to feed via nntpsend =head1 DESCRIPTION The file I/nntpsend.ctl specifies the default list of sites to be fed by B. Comments begin with a number sign (C<#>) and continue through the end of the line. Blank lines and comments are ignored. All other lines should consist of four fields separated by a colon. The first field is the name of the site as specified in the F file. The second field should be the hostname or IP address of the remote site. The third field, if non-empty, specifies the default head truncation size of the batch file. If this field is empty, no truncation is performed. This field may be of the form C<< I-I >>, in which case it is passed to B as C<< -m I -s I >>; otherwise it is of the form C<< I >>, in which case it is passed as C<< -s I >>. The fourth field specifies some default flags passed to B. Note that the flag B<-a> is always given to B and need not appear here. If no B<-t> flag is given in this field or on the B command line, C<-t 180> will be given to B. See nntpsend(8) for an example of F config file. =head1 HISTORY Written by Landon Curt Noll for InterNetNews. Converted to POD by Julien Elie. $Id: nntpsend.ctl.pod 9071 2010-05-31 17:41:32Z iulius $ =head1 SEE ALSO innxmit(1), newsfeeds(5), nntpsend(8), shrinkfile(1). =cut inn-2.6.0/doc/pod/distributions.pod0000644000175200017520000000335712575023702016665 0ustar iuliusiulius=head1 NAME distributions - Recommended values for the Distribution: header =head1 DESCRIPTION The file I/distributions contains a list of relevant distributions and their descriptions. It provides local information for posters who wish to add a Distribution: header to their articles so as to restrict their propagation, although it does not guarantee that such articles will not leak elsewhere because of a misconfiguration of a news server to which they are fed. See newsfeeds(5) for more information about how a news server handles the Distribution: header. Each line of this file consists of a distribution area followed by its description (encoded in UTF-8) after at least a whitespace. For instance: fr Local to France. local Local to this news server. nj Local to New Jersey. usa Local to the United States of America. Blank lines and lines beginning with a number sign (C<#>) are ignored. Any client that issues the LIST DISTRIBUTIONS command obtain these recommended values, if available. However, be aware that use of the LIST DISTRIBUTIONS command is not widespread (though documented in S) and most news clients will never ask for this file. If this file is empty, it is not an error. The server will just send the client an empty response. And if the file is missing, the server will also send the client an appropriate response to indicate that the distributions list is not maintained on the server. The Distribution: header can also be automatically set by B if distrib.pats(5) is correctly configured. =head1 HISTORY Written by Julien Elie for InterNetNews. $Id: distributions.pod 9137 2010-10-29 18:09:12Z iulius $ =head1 SEE ALSO distrib.pats(5), newsfeeds(5), nnrpd(8). =cut inn-2.6.0/doc/pod/readers.conf.pod0000644000175200017520000010735712575023702016341 0ustar iuliusiulius=head1 NAME readers.conf - Access control and configuration for nnrpd =head1 DESCRIPTION F in I specifies access control for nnrpd(8). It controls who is allowed to connect as a news reader and what they're allowed to do after they connect. nnrpd reads this file when it starts up. This generally means that any changes take effect immediately on all subsequent connections, but B may have to be restarted if you use the B<-D> option. (The location I/readers.conf is only the default; the same format applies to any file specified with C.) There are two types of entries in F: parameter/value pairs and configuration groups. Blank lines and anything after a number sign (C<#>) are ignored, unless the character C<#> is escaped with C<\>. The maximum number of characters on each line is 8,191. Parameter/value pairs consist of a keyword immediately followed by a colon, at least one whitespace character, and a value. The case of the parameter is significant (parameter should generally be in all lowercase), and a parameter may contain any characters except colon, C<#>, and whitespace. An example: hosts: *.example.com Values that contain whitespace should be quoted with double quotes, as in: hosts: "*.example.com, *.example.net" If the parameter does not contain whitespace, such as: hosts: *.example.com,*.example.net it's not necessary to quote it, although you may wish to anyway for clarity. There is no way to continue a line on the next line, and therefore no way to have a single parameter with a value longer than about 8,180 characters. Many parameters take a boolean value. For all such parameters, the value may be specified as C, C, or C to turn it on and may be any of C, C, or C to turn it off. The case of these values is not significant. There are two basic types of configuration groups, auth and access. The auth group provides mechanisms to establish the identity of the user, who they are. The access group determines, given the user's identity, what that user is permitted to do. Writing a F file for your setup is a two-step process: first assigning an identity to each incoming connection using auth groups, and then giving each identity appropriate privileges with access group. We recommend I intermingling auth groups and access groups in the config file; it is often more sensible (in the absence of the I parameter) to put all of the auth groups first, and all of the access groups below. A user identity, as established by an auth group, looks like an e-mail address; in other words, it's in the form "@" (or sometimes just "" if no domain is specified. If I is set in F, the user identity is also put into the Sender: header of posts made by that user. See the documentation of that option in inn.conf(5) for more details. An auth group definition looks like: auth { hosts: auth: res: default: default-domain: # ...possibly other settings } The is used as a label for the group and is only for documentation purposes. (If your syslog configuration records the C facility, the will appear in the debugging output of nnrpd. Examining that output can be very helpful in understanding why your configuration doesn't do what you expect it to.) A given auth group applies only to hosts whose name or IP address matches the wildmat expression given with the hosts: parameter (comma-separated wildmat expressions allowed, but C<@> is not supported). Rather than wildmat expressions, you may also use CIDR notation to match any IP address in a netblock; for example, "10.10.10.0/24" will match any IP address between 10.10.10.0 and 10.10.10.255 inclusive. If compiled against the TLS/SSL libraries, an auth group with the I parameter set to true only applies if the incoming connection is using TLS, either from the beginning if the B<-S> flag was passed to B or after a successful use of STARTTLS. For any connection from a host that matches that wildmat expression or netblock, each (multiple res: lines may be present in a block; they are run in sequence until one succeeds), if any, is run to determine the identity of the user just from the connection information. If all the resolvers fail, or if the res: parameter isn't present, the user is assigned an identity of "@"; in other words, the values of the default: and default-domain: parameters are used. If only returns a username, is used as the domain. If the user later authenticates via the AUTHINFO USER/PASS commands, the provided username and password are passed to each (multiple auth, perl_auth, or python_auth lines may be present in a block; they are run in sequence until one succeeds), if any. If one succeeds and returns a different identity than the one assigned at the time of the connection, it is matched against the available access groups again and the actions the user is authorized to do may change. The most common to use is B, which supports several ways of checking passwords including using PAM. See the ckpasswd(8) man page for more details. When matching auth groups, the last auth group in the file that matches a given connection and username/password combination is used. An access group definition usually looks like: access { users: newsgroups: # ...possibly other settings } Again, is just for documentation purposes. This says that all users whose identity matches can read and post to all newsgroups matching (as before, comma-separated wildmat expressions are allowed, but C<@> is not supported). Alternately, you can use the form: access { users: read: post: } and matching users will be able to read any group that matches and post to any group that matches . You can also set several other things in the access group as well as override various inn.conf(5) parameters for just a particular group of users. Just like with auth groups, when matching access groups the last matching one in the file is used to determine the user's permissions. There is an exception to this rule: if the auth group which matched the client contains a perl_access: or python_access: parameter, then the script given as argument is used to dynamically generate an access group. This new access group is then used to determine the access rights of the client; the access groups in the file are ignored. There is one additional special case to be aware of. When forming particularly complex authentication and authorization rules, it is sometimes useful for the identities provided by a given auth group to only apply to particular access groups; in other words, rather than checking the identity against the users: parameter of every access group, it's checked against the users: parameter of only some specific access groups. This is done with the key: parameter. For example: auth example { key: special hosts: *.example.com default: } access example { key: special users: newsgroups: * } In this case, the two key: parameters bind this auth group with this access group. For any incoming connection matching "*.example.com" (assuming there isn't any later auth group that also matches such hosts), no access group that doesn't have "key: special" will even be considered. Similarly, the above access group will only be checked if the user was authenticated with an auth group containing "key: special". This mechanism normally isn't useful; there is almost always a better way to achieve the same result. Also note in the example that there's no default-domain: parameter, which means that no domain is appended to the default username and the identity for such connections is just "". Note that some additional add-ons to INN may prefer that authenticated identities always return a full e-mail address (including a domain), so you may want to set up your system that way. Configuration files can be included in other configuration files via the syntax: include The file named is then included. This syntax is allowed only at top-level. Below is the full list of allowable parameters for auth groups and access groups, and after that are some examples that may make this somewhat clearer. =head1 AUTH GROUP PARAMETERS An auth group without at least one of the res:, auth:, perl_auth:, python_auth:, or default: parameters makes no sense (and in practice will just be ignored). =over 4 =item B A comma-separated list of remote hosts, wildmat patterns matching either hostnames or IP addresses, or IP netblocks specified in CIDR notation. If a user connects from a host that doesn't match this parameter, this auth group will not match the connection and is ignored. Note that if you have a large number of patterns that can't be merged into broader patterns (such as a large number of individual systems scattered around the net that should have access), the hosts: parameter may exceed the maximum line length of 8,192 characters. In that case, you'll need to break that auth group into multiple auth groups, each with a portion of the hosts listed in its hosts: parameter, and each assigning the same user identity. All hosts match if this parameter is not given. =item B A comma-separated list of local host or address patterns with the same syntax as the same as with the hosts: parameter. If this parameter is specified, its auth group will only match connections made to a matching local interface. (Obviously, this is only useful for servers with multiple interfaces.) All local addresses match if this parameter is not given. =item B A simple command line for a user resolver (shell metacharacters are not supported). If a full path is not given, the program executed must be in the I/auth/resolv directory. A resolver is an authentication program which attempts to figure out the identity of the connecting user using nothing but the connection information (in other words, the user has not provided a username and password). An examples of a resolver would be a program that assigns an identity from an ident callback or from the user's hostname. One auth group can have multiple res: parameters, and they will be tried in the order they're listed. The results of the first successful one will be used. Alternatively, a res block can be used instead of a res: paramater. The recognized parameters in such res blocks are: =over 3 =item B A string to log in I/news.notice (with C prepended) before the resolver is tried. One res group can have multiple log: parameters, and they will be logged in the order they're listed. =item B This parameter is mandatory in a res block. Its meaning is the same as the res: parameter used directly in an auth block. auth { res: } is therefore equivalent to: auth { res { program: } } =back =item B A simple command line for a user authenticator (shell metacharacters are not supported). If a full path is not given, the program executed must be located in the I/auth/passwd directory. An authenticator is a program used to handle a user-supplied username and password, via a mechanism such as AUTHINFO USER/PASS. Like with res:, one auth group can have multiple auth: parameters; they will be tried in order and the results of the first successful one will be used. See also perl_auth: below. The most common authenticator to use is ckpasswd(8); see its man page for more information. =item B A path to a perl script for authentication. The perl_auth: parameter works exactly like auth:, except that it calls the named script using the perl hook rather then an external program. Multiple/mixed use of the auth, perl_auth, and python_auth parameters is permitted within any auth group; each line is tried in the order it appears. perl_auth: has more power than auth: in that it provides the authentication program with additional information about the client and the ability to return an error string and a username. This parameter is only valid if INN is compiled with Perl support (B<--with-perl> passed to configure). More information may be found in F. =item B A Python script for authentication. The I parameter works exactly like I, except that it calls the named script (without its C<.py> extension) using the Python hook rather then an external program. Multiple/mixed use of the I, I, and I parameters is permitted within any auth group; each line is tried in the order it appears. I has more power than I in that it provides the authentication program with additional information about the client and the ability to return an error string and a username. This parameter is only valid if INN is compiled with Python support (B<--with-python> passed to B). More information may be found in F. =item B The default username for connections matching this auth group. This is the username assigned to the user at connection time if all resolvers fail or if there are no res: parameters. Note that it can be either a bare username, in which case default-domain: (if present) is appended after an C<@>, or a full identity string containing an C<@>, in which case it will be used verbatim. =item B The default domain string for this auth group. If a user resolver or authenticator doesn't provide a domain, or if the default username is used and it doesn't contain a C<@>, this domain is used to form the user identity. (Note that for a lot of setups, it's not really necessary for user identities to be qualified with a domain name, in which case there's no need to use this parameter.) =item B If this parameter is present, any connection matching this auth group will have its privileges determined only by the subset of access groups containing a matching key parameter. =item B If set to true, an incoming connection only matches this auth group if it is encrypted using TLS/SSL, either from the beginning if the B<-S> flag was passed to B or after a successful use of STARTTLS. This parameter is only valid if INN is compiled with TLS/SSL support (by default if the OpenSSL SSL and crypto libraries are found at configure time, otherwise see the B<--with-openssl> flag passed to configure). =item B A path to a perl script for dynamically generating an access group. If an auth group matches successfully and contains a perl_access parameter, then the argument perl script will be used to create an access group. This group will then determine the access rights of the client, overriding any access groups in F. If and only if a sucessful auth group contains the perl_access parameter, F access groups are ignored and the client's rights are instead determined dynamically. This parameter is only valid if INN is compiled with Perl support (B<--with-perl> passed to configure). More information may be found in the file F. =item B A Python script for dynamically generating an access group. If an auth group matches successfully and contains a I parameter, then the argument script (without its C<.py> extension) will be used to create an access group. This group will then determine the access rights of the client, overriding any access groups in F. If and only if a successful auth group contains the I parameter, F access groups are ignored and the client's rights are instead determined dynamically. This parameter is only valid if INN is compiled with Python support (B<--with-python> passed to B). More information may be found in the file F. =item B A Python script for applying access control dynamically on a per newsgroup basis. If an auth group matches successfully and contains a I parameter, then the argument script (without its C<.py> extension) will be used to determine the clients rights each time the user attempts to view a newsgroup, or read or post an article. Access rights as determined by I override the values of access group parameters such as I, I and I. This parameter is only valid if INN is compiled with Python support (B<--with-python> passed to B). More information may be found in the file F. =back =head1 ACCESS GROUP PARAMETERS =over 4 =item B The privileges given by this access group apply to any user identity which matches this comma-separated list of wildmat patterns. If this parameter isn't given, the access group applies to all users (and is essentially equivalent to C). =item B Users that match this access group are allowed to read and post to all newsgroups matching this comma-separated list of wildmat patterns. The empty string is equivalent to C; if this parameter is missing, the connection will be rejected (unless read: and/or post: are used instead, see below). =item B Like the newsgroups: parameter, but the client is only given permission to read the matching newsgroups. This parameter is often used with post: (below) to specify some read-only groups; it cannot be used in the same access group with a newsgroups: parameter. (If read: is used and post: is missing, the client will have only read-only access.) =item B Like the newsgroups: parameter, but the client is only given permission to post to the matching newsgroups. This parameter is often used with read: (above) to define the patterns for reading and posting separately (usually to give the user permission to read more newsgroups than they're permitted to post to). It cannot be used in the same access group with a newsgroups: parameter. =item B A set of letters specifying the permissions granted to the client. The letters are chosen from the following set: =over 3 =item R The client may read articles. =item P The client may post articles. =item I The client may inject articles with IHAVE. Note that in order to inject articles with the IHAVE command, the user must also have POST permission (the C

option). Articles injected with IHAVE are treated as though they were injected with POST, that is to say such articles must not have been previously injected (they must not contain headers like Injection-Info:). =item A The client may post articles with Approved: headers (in other words, may approve articles for moderated newsgroups). By default, this is not allowed. =item N The client may use the NEWNEWS command, overriding the global setting. =item L The client may post to newsgroups that are set to disallow local posting (status fields C, C and C in the active(5) file). =back Note that if this parameter is given, I in F is ignored for connections matching this access group and the ability of the client to use NEWNEWS is entirely determined by the presence of C in the access string. If you want to support NEWNEWS, make sure to include C in the access string when you use this parameter. Note that if this parameter is given and C isn't present in the access string, the client cannot read regardless of newsgroups: or read: parameters. Similarly, if this parameter is given and C

isn't present, the client cannot post. This use of access: is deprecated and confusing; it's strongly recommended that if the access: parameter is used, C and C

always be included in the access string and newsgroups:, read:, and post: be used to control access. (To grant read access but no posting access, one can have just a read: parameter and no post: parameter.) =item B If this parameter is present, this access group is only considered when finding privileges for users matching auth groups with this same key: parameter. =item B If this parameter is present, a client matching this block will be disconnected with a "Permission denied" message containing the contents (a "reason" string) of this parameter. Some newsreaders will then display the reason to the user. =item B If this parameter is present (and nonzero), it is used for B's rate-limiting code. The client will only be able to download at this speed (in bytes/second). Note that if TLS/SSL is being used, limiting is applied to the pre-encryption datastream. =item B If a Date: header is not included in a posted article, nnrpd(8) normally adds a new Date: header in UTC. If this is set to true, the Date: header will be formatted in local time instead. This is a boolean value and the default is false. =item B Used as the contact address in the help message returned by nnrpd(8), if the virtualhost: parameter is set to true. =item B If set to true, any Path: header provided by a user in a post is stripped rather than used as the beginning of the Path: header of the article. This is a boolean value and the default is false. =item B If set to false, posts made by these users do not pass through the Perl filter even if it is otherwise enabled. This is a boolean value and the default is true. =item B If set to false, posts made by these users do not pass through the Python filter even if it is otherwise enabled. This is a boolean value and the default is true. =item B Set this parameter to true in order to make B behave as if it is running on a server with a different name than it actually is. If you set this parameter to true, you must also set either pathhost: or domain: in the relevant access group in F to something different than is set in F. All articles displayed to clients will then have their Path: and Xref: headers altered to appear to be from the server named in pathhost: or domain: (whichever is set), and posted articles will use that server name in the Path:, Message-ID:, and Injection-Info: headers. Note that setting this parameter requires the server modify all posts before presenting them to the client and therefore may decrease performance slightly. =back In addition, all of the following parameters are valid in access groups and override the global setting in F. See inn.conf(5) for the descriptions of these parameters: addinjectiondate, addinjectionpostingaccount, addinjectionpostinghost, backoff_auth, backoff_db, backoff_k, backoff_postfast, backoff_postslow, backoff_trigger, checkincludedtext, clienttimeout, complaints, domain, fromhost, localmaxartsize, moderatormailer, nnrpdauthsender, nnrpdcheckart, nnrpdoverstats, nnrpdposthost, nnrpdpostport, organization, pathhost, readertrack, spoolfirst, strippostcc. =head1 SUMMARY Here's a basic summary of what happens when a client connects: =over 2 =item * All auth groups are scanned and the ones that don't match the client (due to I, I, I, etc.) are eliminated. =item * The remaining auth groups are scanned from the last to the first, and an attempt is made to apply it to the current connection. This means running res: programs, if any, and otherwise applying default:. The first auth group (starting from the bottom) to return a valid user is kept as the active auth group. =item * If no auth groups yield a valid user (none have default: parameters or successful res: programs) but some of the auth groups have auth: lines (indicating a possibility that the user can authenticate and then obtain permissions), the connection is considered to have no valid auth group (which means that the access groups are ignored completely) but the connection isn't closed. Instead, 480 is returned for everything until the user authenticates. =item * When the user authenticates, the auth groups are rescanned, and only the matching ones which contain at least one auth, perl_auth, or python_auth line are considered. These auth groups are scanned from the last to the first, running auth: programs and perl_auth: or python_auth: scripts. The first auth group (starting from the bottom) to return a valid user is kept as the active auth group. =item * Regardless of how an auth group is established, as soon as one is, that auth group is used to assign a user identity by taking the result of the successful res, auth, perl_auth, or python_auth line (or the default: if necessary), and appending the default-domain if necessary. (If the perl_access: or python_access: parameter is present, see below.) =item * Finally, an access group is selected by scanning the access groups from bottom up and finding the first match. (If the established auth group contained a perl_access: or python_access line, the dynamically generated access group returned by the script is used instead.) User permissions are granted based on the established access group. =back =head1 EXAMPLES Probably the simplest useful example of a complete F, this gives permissions to read and post to all groups to any connections from the "example.com" domain, and no privileges for anyone connecting elsewhere: auth example.com { hosts: "*.example.com, example.com" default: "" } access full { users: "" newsgroups: * } Note that the above access realm could also be written without the users: key, in which case it applies to any user identity (though in this example, the user identity that will be assigned to all matching connections is C<< >>). It is however recommended to keep an explicit users: key so as to better view to whom the access block applies. As the only available auth realm only matches hosts in the "example.com" domain, any connections from other hosts will be rejected immediately. If you have some systems that should only have read-only access to the server, you can modify the example above slightly by adding an additional auth and access group: auth lab { hosts: "*.lab.example.com" default: } access lab { users: read: * } If those are put in the file after the above example, they'll take precedence (because they're later in the file) for any user coming from a machine in the lab.example.com domain, everyone will only have read access, not posting access. Here's a similar example for a news server that accepts connections from anywhere but requires the user to specify a username and password. The username and password are first checked against an external database of usernames and passwords, and then against the system shadow password file: auth all { auth: "ckpasswd -d /newsusers" auth: "ckpasswd -s" } access full { users: * newsgroups: * } When the user first connects, there are no res: keys and no default, so they don't receive any valid identity and the connection won't match any access groups (even ones with C). Such users receive nothing but authentication-required responses from nnrpd until they authenticate. If they then later authenticate, the username and password are checked first by running B with the B<-d> option for an external dbm file of encrypted passwords, and then with the B<-s> option to check the shadow password database (note that this option may require ckpasswd to be setgid to a shadow group, and there are security considerations; see ckpasswd(8) for details). If both of those fail, the user will continue to have no identity; otherwise, an identity will be assigned (usually the supplied username, perhaps with a domain appended, although an authenticator technically can provide a completely different username for the identity), and the access group will match, giving full access. It may be educational to consider how to combine the above examples; general groups always go first. The order of the auth groups actually doesn't matter, since the "hosts: example.com" one only matches connections before username/password is sent, and the "auth: ckpasswd" one only matches after; order would matter if either group applied to both cases. The order of the access groups in this case does matter, provided the newsgroups: lines differ; the access group with no users: line needs to be first, with the "users: " group after. Here's an example of another common case: a server that only allows connections from a local domain and has an additional hierarchy that's password-restricted. auth "example.com" { hosts: "*.example.com" auth: "ckpasswd -d /newsusers" default: "anonymous" } access regular { newsgroups: "*,!example.restricted.*" } access full { users: "*,!anonymous" newsgroups: * } In this example, unauthenticated users get the identity C, which matches only the first access group and hence doesn't get access to the example.restricted.* hierarchy. Anyone who authenticates using a password in the F file gets full access to all groups. However, note that the only authentication block is limited to hostnames in the example.com domain; connections outside of that domain will never be allowed access or an opportunity to authenticate. Here's a very complicated example. This is for an organization that has an internal hierarchy "example.*" only available to local shell users, who are on machines where identd can be trusted. Dialup users must provide a username and password, which is then checked against RADIUS. Remote users have to use a username and password that's checked against a database on the news server. Finally, the admin staff (users "joe" and "jane") can post anywhere (including the "example.admin.*" groups that are read-only for everyone else), and are exempted from the Perl filter. For an additional twist, posts from dialup users have their Sender: header replaced by their authenticated identity. Since this organization has some internal moderated newsgroups, the admin staff can also post messages with Approved: headers, but other users cannot. auth default { auth: "ckpasswd -f /newsusers" default: default-domain: example.com } auth shell { hosts: *.shell.example.com res: ident auth: "ckpasswd -s" default: default-domain: shell.example.com } auth dialup { hosts: *.dialup.example.com auth: radius default: default-domain: dialup.example.com } access shell { users: *@shell.example.com read: * post: "*, !example.admin.*" } access dialup { users: *@dialup.example.com newsgroups: *,!example.* nnrpdauthsender: true } access other { users: "*@example.com, !@example.com" newsgroups: *,!example.* } access fail { users: "@*" newsgroups: !* } access admin { users: "joe@*,jane@*" newsgroups: * access: "RPA" perlfilter: false } Note the use of different domains to separate dialup from shell users easily. Another way to do that would be with key: parameters, but this way provides slightly more intuitive identity strings. Note also that the fail access group catches not only failing connections from external users but also failed authentication of shell and dialup users and dialup users before they've authenticated. The identity string given for, say, dialup users before RADIUS authentication has been attempted matches both the dialup access group and the fail access group, since it's "@dialup.example.com", but the fail group is last so it takes precedence. The shell auth group has an auth: parameter so that users joe and jane can, if they choose, use username and password authentication to gain their special privileges even if they're logged on as a different user on the shell machines (or if ident isn't working). When they first connect, they'd have the default access for that user, but they could then send AUTHINFO USER and AUTHINFO PASS in order to get their extended access. Also note that if the users joe and jane are using their own accounts, they get their special privileges regardless of how they connect, whether the dialups, the shell machines, or even externally with a username and password. Finally, here's a very simple example of a configuration for a public server for a particular hierarchy. auth default { hosts: * default: } access default { users: newsgroups: example.* } Notice that clients aren't allowed to read any other groups; this keeps them from getting access to administrative groups or reading control messages, just as a precaution. When running a public server like this, be aware that many public hierarchies will later be pulled down and reinjected into the main Usenet, so it's highly recommended that you also run a Perl or Python filter to reject any messages crossposted out of your local hierarchy and any messages containing a Supersedes: header. This will keep messages posted to your public hierarchy from hurting any of the rest of Usenet if they leak out. =head1 SECURITY CONSIDERATIONS In general, separate passwords should be used for NNTP wherever possible; the NNTP protocol itself does not protect passwords from casual interception, and many implementations (including this one) do not "lock out" accounts or otherwise discourage password-guessing attacks. So it is best to ensure that a compromised password has minimal effects. Authentication using the AUTHINFO USER/PASS commands passes unencrypted over the network. Extreme caution should therefore be used especially with system passwords (e.g. C). Passwords can be protected by using NNTP over TLS/SSL or through ssh tunnels, and this usage can be enforced by a well-considered server configuration that only permits certain auth groups to be applied in certain cases. Here are some ideas: =over 4 =item * To restrict connections on the standard NNTP port (119) to use TLS for some (or all) of the auth groups to match, use the I parameter. Note that a client can use STARTTLS to negotiate an encrypted connection. A secure layer can also be negotiated during authentication via AUTHINFO SASL. =item * If you consider your local network (but not the internet) secure, have some auth groups with a restrictive hosts: parameter; they would go above, with ones having global applicability below. =item * Consider running B with the B<-S> flag (either also with B<-D>, or out of "super-server" like B) on the NNTPS port (563) for clients that support TLS/SSL. See nnrpd(8) for more details about how to configure that. You can use the I parameter or the B<-c> flag to specify an alternate F file if you want a substantially different configuration for this case. =item * If you want to restrict an auth group to only match loopback connections (for users running newsreaders on localhost or connecting via an ssh tunnel), use the localaddress: parameter. =back =head1 HISTORY Written by Aidan Cully for InterNetNews. Substantially expanded by Russ Allbery . $Id: readers.conf.pod 9798 2015-03-21 13:30:37Z iulius $ =head1 SEE ALSO auth_krb5(8), ckpasswd(8), inn.conf(5), innd(8), newsfeeds(5), nnrpd(8), uwildmat(3). =cut inn-2.6.0/doc/pod/sendinpaths.pod0000644000175200017520000000506312575023702016277 0ustar iuliusiulius=head1 NAME sendinpaths - Send Usenet Path: statistics via e-mail =head1 SYNOPSIS B [B<-cdhn>] [B<-k> I] [B<-r> I] [I

[I
...]] =head1 DESCRIPTION B checks I/path for B dump files, finds dump files generated in the past I days, makes sure they are valid by running B on each one and making sure the exit status is zero, and passes them to B to generate a cumulative report. By default, that report is mailed to the e-mail addresses configured at the beginning of this script (by default, only one address is configured: ) in order to supply the TOP1000 project with useful statistics. See L for more information. When finished, B deletes all dump files in I/path that are older than I days. For more information on how to set up B, see ninpaths(8). =head1 OPTIONS =over 4 =item B<-c> When this flag is used, the report is also e-mailed, besides the default submission addresses or those given as command-line arguments, to the newsmaster's address set at configure time. =item B<-d> Enables debug messages. =item B<-h> Gives usage information. =item B<-k> I After having processed dump files, B removes those that are older than I days. The default is C<0>, that is to say to remove all dump files. Setting I to another value can be useful for debugging purpose because it permits to keep a few dump files. =item B<-n> Don't e-mail the report; instead, just print it to standard output. Don't delete old dump files. =item B<-r> I Process dump files generated during the last I days. The default is C<32>, that is to say to process all the dump files that have been generated during the last 32 days (if, of course, they have not been deleted yet by a previous run of B according to the value set by the B<-k> flag). =item I
... E-mail the report to the mentioned address or addresses, instead of the default one. Several addresses can be used, separated by whitespace (sending the e-mail to your own e-mail address can be useful for debugging purpose, to check that everything works fine). For instance, for two addresses: sendinpaths pathsurvey@example.org top1000@anthologeek.net =back =head1 HISTORY B was written by Olaf Titz . $Id: sendinpaths.pod 9624 2014-03-16 13:23:46Z iulius $ =head1 SEE ALSO ninpaths(8). =cut inn-2.6.0/doc/pod/batcher.pod0000644000175200017520000001301112575023702015357 0ustar iuliusiulius=head1 NAME batcher - Article batching for InterNetNews =head1 SYNOPSIS B [B<-rv>] [B<-a> I] [B<-A> I] [B<-b> I] [B<-B> I] [B<-i> I] [B<-N> I] [B<-p> I] [B<-s> I] I [I] =head1 DESCRIPTION B reads a list of files and prepares news batches for the specified host. It is generally used to prepare UUCP feeds, but the resulting batches can be used by any application that uses rnews(8) to inject the articles. It is normally invoked by a script run out of cron that uses B to lock the host, followed by B to flush the batch file. See send-uucp(8) for a front-end for B. B reads the file I, or standard input if no file is given. If I is a relative file name, it is assumed to be in I as set in F. Blank lines and lines starting with a number sign (C<#>) are ignored. All other lines in the input should consist of one or two fields separated by a single space. The first field is the storage API token of an article. The second field, if present, specifies the size of the article in bytes. By default, batches are written to standard output (which isn't very useful if more than one batch is output), but see the B<-p> option. =head1 OPTIONS =over 4 =item B<-a> I This flag limits the number of articles included in each batch. The default is no limit. A new batch will be started when either the total bytes or the number of articles written exceeds the specified limits. =item B<-A> I Limits the total number of articles written for all batches. As soon as the total number of articles written to batches reaches or exceeds I, all additional articles in the input will be deferred. The default is no limit. =item B<-b> I This flag sets the size limit for each batch; as soon as at least this much data has been written out, a new batch will be started. The default size is S<60 KB>. Using C<-b 0> will allow unlimited batch sizes. =item B<-B> I Limits the total number of bytes written for all batches. As soon as the total bytes written to batches reaches or exceeds I, all additional articles in the input will be deferred. The default is no limit. =item B<-i> I A batch starts with an identifying line to specify the unpacking method to be used on the receiving end. When this flag is used, I, followed by a newline, will be output at the start of each batch. The default is to have no initial string (under the assumption that either the processor specified with the B<-p> flag or some other later process will add the appropriate line). =item B<-N> I Limits the total number of batches written. As soon as the number of batches written reaches or exceeds I, all additional articles in the input will be deferred. The default is no limit. =item B<-p> I By default, batches are written to standard output, which is not useful when more than one output batch is created. If this option is given, each batch will instead be fed via a pipe to the shell command I. The I argument must be an sprintf(3) format string, which may have a single C<%s> parameter that will be replaced with the host name. A common value is: ( echo '#! gunbatch' ; exec gzip -c ) | uux - -r -z %s!rnews which generates gzip-compressed batches and feeds them to B. =item B<-r> By default, B reports errors to I/errlog. To suppress this redirection and report errors to standard error, use the B<-r> flag. =item B<-s> I Each article in a batch starts with a separator line that indicates the size of the article. I must be an sprintf(3) string, which may have a single C<%ld> in the string that will be replaced with the size of the article. If the separator is not empty, a newline will also be appended to it when it is added to the beginning of each article. The default separator is: #! rnews %ld and this should rarely be changed. =item B<-v> Upon exit, B reports statistics via syslog. With this flag, the statistics will also be printed to standard output. =back =head1 EXIT STATUS If the input is exhausted and all batches are created successfully, B will exit with a zero status. If any of the limits specified with B<-A>, B<-B>, or B<-N> flags are reached, or if there is an error in writing a batch, B will try to spool the remaining input by copying it to a file as follows: =over 2 =item * If there was no input filename, the remaining input will be copied to I/I. =item * If an input filename was given, the remaining input will be copied to a temporary file named by appending C<.bch> to the end of I (and qualified by adding I if I was not a fully qualified path). If this happens successfully, B will then try to rename this temporary file to I (thus replacing I with a copy of itself with all of lines for the successfully batched articles removed). =back Upon receipt of an interrupt or termination signal, B will finish batching the current article, close the batch, and then rewrite the batch file as described above. =head1 HISTORY Written by Rich $alz for InterNetNews. Rewritten by Russ Allbery in POD. $Id: batcher.pod 9767 2014-12-07 21:13:43Z iulius $ =head1 SEE ALSO ctlinnd(8), inn.conf(5), newsfeeds(5), rnews(8), send-uucp(8), shlock(1). =cut inn-2.6.0/doc/pod/news2mail.pod0000644000175200017520000000601712575023702015660 0ustar iuliusiulius=head1 NAME news2mail - Channel script to gateway news into e-mails =head1 SYNOPSIS B =head1 DESCRIPTION B runs as a channel process underneath B. It is set up as channel feed in F, with different mailing-lists as funnel entries pointing to it (see below); B expects the token of an article followed by a sequence of list names. B uses the configuration file I/news2mail.cf to map mailing-list names to e-mail addresses. B causes B to queue the messages for later delivery (to avoid DOS attacks by mass postings). You must run C periodically to get the queue processed. =head1 CONFIGURATION FILE The configuration file format is simple: comments (starting with a hash sign C<#>) and blank lines are ignored. All other lines have two or three fields on them. The first is the list name and is what B uses (i.e. the site field of the entry in the F file). The second field is the actual e-mail address to send the article to. The third field is optional: it sets the envelope-from address (for instance a list member's address; if not set, it defaults to the C user). In e-mail messages, the To: header will have the mailing-list address (i.e. the second field). Besides, B strips most article headers from the article before mailing. In F, the channel feed should look like: news2mail!:!*:Ac,Tc,Wn*:/news2mail and for each mailing-list, you only have to add to F an entry list like: list-big-red-ants/lists.ucsd.edu:!*,rec.pets.red-ants:Ap,Tm:news2mail! Please note the use of C and the exclusion of the list owner domain to protect the list from feeding back new arrivals from the list. The site name used in the F entry for a mailing-list (above C) must be the same as the first field in an entry in F. For instance: # Newsfeeds-name List-to-address [List-sender-address] list-big-red-ants big-red-ants@lists.ucsd.edu news+big-red-ants@local.news.server.org =head1 FILES =over 4 =item I/news2mail The Perl program itself used to gateway news into e-mails. =item I/news2mail.cf The configuration file which specifies the mapping to use for gatewaying. =back =head1 BUGS The B program is set up as a funneled channel in F, implying multiple matches should be handled as one S<- and> multiple matching funneled feeds will result in a single call to the script. Therefore, since only one mail is sent, crossposts are not currently properly handled as for the envelope-from address (which then defaults to the C user). =head1 HISTORY B was written by Brian Kantor in 1998. This man page was written by James Brister and converted to POD by Julien Elie. The third optional field in F was added by S in 2008. $Id: news2mail.pod 8199 2008-11-30 13:30:47Z iulius $ =head1 SEE ALSO innd(8), newsfeeds(5). =cut inn-2.6.0/doc/pod/hook-python.pod0000644000175200017520000007102112575023702016233 0ustar iuliusiulius=head1 INN Python Filtering and Authentication Support This file documents INN's built-in optional support for Python article filtering. It is patterned after the Perl and (now obsolete) TCL hooks previously added by Bob Heiney and Christophe Wolfhugel. For this filter to work successfully, you will need to have at least S installed. You can obtain it from L. Please note that S is currently not supported. The B Python interface and the original Python filtering documentation were written by Greg Andruk (nee Fluffy) . The Python authentication and authorization support for B as well as the original documentation for it were written by Ilya Etingof in December 1999. =head1 Installation Once you have built and installed Python, you can cause INN to use it by adding the B<--with-python> switch to your C command. You will need to have all the headers and libraries required for embedding Python into INN; they can be found in Python development packages, which include header files and static libraries. You will then be able to use Python authentication, dynamic access group generation and dynamic access control support in B along with filtering support in B. See the ctlinnd(8) manual page to learn how to enable, disable and reload Python filters on a running server (especially C, C and C). Also, see the F, F, F and F samples in your filters directory for a demonstration of how to get all this working. =head1 Writing an B Filter =head2 Introduction You need to create a F module in INN's filter directory (see the I setting in F). A heavily-commented sample is provided; you can use it as a template for your own filter. There is also an F module there which is not actually used by INN; it is there so you can test your module interactively. First, define a class containing the methods you want to provide to B. Methods B will use if present are: =over 4 =item __init__(I) Not explicitly called by B, but will run whenever the filter module is (re)loaded. This is a good place to initialize constants or pick up where C or C left off. =item filter_before_reload(I) This will execute any time a C or C command is issued. You can use it to save statistics or reports for use after reloading. =item filter_close(I) This will run when a C command is received. =item filter_art(I, I) I is a dictionary containing an article's headers and body. This method is called every time B receives an article. The following can be defined: Also-Control, Approved, Archive, Archived-At, Bytes, Cancel-Key, Cancel-Lock, Comments, Content-Base, Content-Disposition, Content-Transfer-Encoding, Content-Type, Control, Date, Date-Received, Distribution, Expires, Face, Followup-To, From, In-Reply-To, Injection-Date, Injection-Info, Keywords, Lines, List-ID, Message-ID, MIME-Version, Newsgroups, NNTP-Posting-Date, NNTP-Posting-Host, NNTP-Posting-Path, Organization, Original-Sender, Originator, Path, Posted, Posting-Version, Received, References, Relay-Version, Reply-To, Sender, Subject, Summary, Supersedes, User-Agent, X-Auth, X-Auth-Sender, X-Canceled-By, X-Cancelled-By, X-Complaints-To, X-Face, X-HTTP-UserAgent, X-HTTP-Via, X-Mailer, X-Modbot, X-Modtrace, X-Newsposter, X-Newsreader, X-No-Archive, X-Original-Message-ID, X-Original-NNTP-Posting-Host, X-Original-Trace, X-Originating-IP, X-PGP-Key, X-PGP-Sig, X-Poster-Trace, X-Postfilter, X-Proxy-User, X-Submissions-To, X-Trace, X-Usenet-Provider, X-User-ID, Xref, __BODY__, __LINES__. Note that all the above values are as they arrived, not modified by your INN (especially, the Xref: header, if present, is the one of the remote site which sent you the article, and not yours). These values will be buffer objects holding the contents of the same named article headers, except for the special C<__BODY__> and C<__LINES__> items. Items not present in the article will contain C. C is a buffer object containing the article's entire body, and C is an int holding B's reckoning of the number of lines in the article. All the other elements will be buffers with the contents of the same-named article headers. The Newsgroups: header of the article is accessible inside the Python filter as C. If interned strings are used in the filter, calls to C or C are faster: # Syntax for Python 2.x. Newsgroups = intern("Newsgroups") if art[Newsgroups] == buffer("misc.test"): print("Test group") # Syntax for Python 3.x. import sys Newsgroups = sys.intern("Newsgroups") if art[Newsgroups] == memoryview(b"misc.test"): print("Test group") If you want to accept an article, return C or an empty string. To reject, return a non-empty string. The rejection strings will be shown to local clients and your peers, so keep that in mind when phrasing your rejection responses and make sure that such a message is properly encoded in UTF-8 so as to comply with the NNTP protocol. =item filter_messageid(I, I) I is a buffer object containing the ID of an article being offered by CHECK, IHAVE or TAKETHIS. Like with C, the message will be refused if you return a non-empty string. If you use this feature, keep it light because it is called at a rather busy place in B's main loop. =item filter_mode(I, I, I, I) When the operator issues a B C, C, C, C or C command, this function can be used to do something sensible in accordance with the state change. Stamp a log file, save your state on throttle, etc. I and I will be strings containing one of the values in (C, C, C, C, C). I is the state B was in before B was run, I is the state B will be in after the command finishes. I is the comment string provided on the B command line. =back =head2 How to Use these Methods with B To register your methods with B, you need to create an instance of your class, import the built-in INN module, and pass the instance to C. For example: class Filter: def filter_art(self, art): ... blah blah ... def filter_messageid(self, id): ... yadda yadda ... import INN myfilter = Filter() INN.set_filter_hook(myfilter) When writing and testing your Python filter, don't be afraid to make use of C/C and the provided C function. stdout and stderr will be disabled, so your filter will die silently otherwise. Also, remember to try importing your module interactively before loading it, to ensure there are no obvious errors. One typo can ruin your whole filter. A dummy F module is provided to facilitate testing outside the server. To test, change into your filter directory and use a command like: python -ic 'import INN, filter_innd' You can define as many or few of the methods listed above as you want in your filter class (it is fine to define more methods for your own use; B will not be using them but your filter can). If you I define the above methods, GET THE PARAMETER COUNTS RIGHT. There are checks in B to see whether the methods exist and are callable, but if you define one and get the parameter counts wrong, B WILL DIE. You have been warned. Be careful with your return values, too. The C and C methods have to return strings, or C. If you return something like an int, B will I be happy. =head2 A Note regarding Buffer Objects This section is not applicable to S where buffer objects have been replaced with memory views. Buffer objects are cousins of strings, new in S. Using buffer objects may take some getting used to, but we can create buffers much faster and with less memory than strings. For most of the operations you will perform in filters (like C, C, C) you can treat buffers just like strings, but there are a few important differences you should know about: # Make a string and two buffers. s = "abc" b = buffer("def") bs = buffer("abc") s == bs # - This is false because the types differ... buffer(s) == bs # - ...but this is true, the types now agree. s == str(bs) # - This is also true, but buffer() is faster. s[:2] == bs[:2] # - True. Buffer slices are strings. # While most string methods will take either a buffer or a string, # string.join (in the string module) insists on using only strings. import string string.join([str(b), s], '.') # Returns 'def.abc'. '.'.join([str(b), s]) # Returns 'def.abc' too. '.'.join([b, s]) # This raises a TypeError. e = s + b # This raises a TypeError, but... # ...these two both return the string 'abcdef'. The first one # is faster -- choose buffer() over str() whenever you can. e = buffer(s) + b f = s + str(b) g = b + '>' # This is legal, returns the string 'def>'. =head2 Functions Supplied by the Built-in B Module Besides C which is used to register your methods with B as it has already been explained above, the following functions are available from Python scripts: =over 4 =item addhist(I) =item article(I) =item cancel(I) =item havehist(I) =item hashstring(I) =item head(I) =item newsgroup(I) =item syslog(I, I) =back Therefore, not only can B use Python, but your filter can use some of B's features too. Here is some sample Python code to show what you get with the previously listed functions. import INN # Python's native syslog module isn't compiled in by default, # so the INN module provides a replacement. The first parameter # tells the Unix syslogger what severity to use; you can # abbreviate down to one letter and it's case insensitive. # Available levels are (in increasing levels of seriousness) # Debug, Info, Notice, Warning, Err, Crit, and Alert. (If you # provide any other string, it will be defaulted to Notice.) The # second parameter is the message text. The syslog entries will # go to the same log files innd itself uses, with a 'python:' # prefix. syslog('warning', 'I will not buy this record. It is scratched.') animals = 'eels' vehicle = 'hovercraft' syslog('N', 'My %s is full of %s.' % (vehicle, animals)) # Let's cancel an article! This only deletes the message on the # local server; it doesn't send out a control message or anything # scary like that. Returns 1 if successful, else 0. if INN.cancel(''): cancelled = "yup" else: cancelled = "nope" # Check if a given message is in history. This doesn't # necessarily mean the article is on your spool; cancelled and # expired articles hang around in history for a while, and # rejected articles will be in there if you have enabled # remembertrash in inn.conf. Returns 1 if found, else 0. if INN.havehist(''): comment = "*yawn* I've already seen this article." else: comment = 'Mmm, fresh news.' # Here we are running a local spam filter, so why eat all those # cancels? We can add fake entries to history so they'll get # refused. Returns 1 on success, 0 on failure. cancelled_id = '' if INN.addhist("') artheader = INN.head('') # As we can compute a hash digest for a string, we can obtain one # for artbody. It might be of help to detect spam. digest = INN.hashstring(artbody) # Finally, do you want to see if a given newsgroup is moderated or # whatever? INN.newsgroup returns the last field of a group's # entry in active as a string. groupstatus = INN.newsgroup('alt.fan.karl-malden.nose') if groupstatus == '': moderated = 'no such newsgroup' elif groupstatus == 'y': moderated = "nope" elif groupstatus == 'm': moderated = "yep" else: moderated = "something else" =head1 Writing an B Filter =head2 Changes to Python Authentication and Access Control Support for B The old authentication and access control functionality has been combined with the new F mechanism by Erik Klavon ; bug reports should however go to , not Erik. The remainder of this section is an introduction to the new mechanism (which uses the I, I, and I F parameters) with porting/migration suggestions for people familiar with the old mechanism (identifiable by the now deprecated I parameter in F). Other people should skip this section. The I parameter allows the use of Python to authenticate a user. Authentication scripts (like those from the old mechanism) are listed in F using I in the same manner other authenticators are using I: python_auth: "nnrpd_auth" It uses the script named F (note that C<.py> is not present in the I value). Scripts should be placed as before in the filter directory (see the I setting in F). The new hook method C takes no arguments and its return value is ignored; its purpose is to provide a means for authentication specific initialization. The hook method C is the more specific analogue to the old C method. These two method hooks are not required, contrary to C, the main method. The argument dictionary passed to C remains the same, except for the removal of the I entry which is no longer needed in this modification and the addition of several new entries (I, I, I) described below. The return tuple now only contains either two or three elements, the first of which is the NNTP response code. The second is an error string which is passed to the client if the response code indicates that the authentication attempt has failed (make sure that such a message is properly encoded in UTF-8 so as to comply with the NNTP protocol). This allows a specific error message to be generated by the Python script in place of the generic message C. An optional third return element, if present, will be used to match the connection with the I parameter in access groups and will also be the username logged. If this element is absent, the username supplied by the client during authentication will be used, as was the previous behaviour. The I parameter (described below) is new; it allows the dynamic generation of an access group of an incoming connection using a Python script. If a connection matches an auth group which has a I parameter, all access groups in F are ignored; instead the procedure described below is used to generate an access group. This concept is due to Jeffrey S and you can add this line to F in order to use the F Python script in I: python_access: "nnrpd_access" In the old implementation, the authorization method allowed for access control on a per-group basis. That functionality is preserved in the new implementation by the inclusion of the I parameter in F. The only change is the corresponding method name of C as opposed to C. Additionally, the associated optional housekeeping methods C and C may be implemented if needed. In order to use F in I, you can add this line to F: python_dynamic: "nnrpd_dynamic" This new implementation should provide all of the previous capabilities of the Python hooks, in combination with the flexibility of F and the use of other authentication and resolving programs (including the Perl hooks!). To use Python code that predates the new mechanism, you would need to modify the code slightly (see below for the new specification) and supply a simple F file. If you do not want to modify your code, the sample directory has F, F and F which should allow you to use your old code without needing to change it. However, before trying to use your old Python code, you may want to consider replacing it entirely with non-Python authentication. (With F and the regular authenticator and resolver programs, much of what once required Python can be done directly.) Even if the functionality is not available directly, you may wish to write a new authenticator or resolver (which can be done in whatever language you prefer). =head2 Python Authentication Support for B Support for authentication via Python is provided in B by the inclusion of a I parameter in a F auth group. I works exactly like the I parameter in F, except that it calls the script given as argument using the Python hook rather then treating it as an external program. Multiple, mixed use of I with other I statements including I is permitted. Each I statement will be tried in the order they appear in the auth group until either one succeeds or all are exhausted. If the processing of F requires that a I statement be used for authentication, Python is loaded (if it has yet to be) and the file given as argument to the I parameter is loaded as well (do not include the C<.py> extension of this file in the value of I). If a Python object with a method C is hooked in during the loading of that file, then that method is called immediately after the file is loaded. If no errors have occurred, the method C is called. Depending on the NNTP response code returned by C, the authentication hook either succeeds or fails, after which the processing of the auth group continues as usual. When the connection with the client is closed, the method C is called if it exists. =head2 Dynamic Generation of Access Groups A Python script may be used to dynamically generate an access group which is then used to determine the access rights of the client. This occurs whenever the I parameter is specified in an auth group which has successfully matched the client. Only one I statement is allowed in an auth group. This parameter should not be mixed with a I statement in the same auth group. When a I parameter is encountered, Python is loaded (if it has yet to be) and the file given as argument is loaded as well (do not include the C<.py> extension of this file in the value of I). If a Python object with a method C is hooked in during the loading of that file, then that method is called immediately after the file is loaded. If no errors have occurred, the method C is called. The dictionary returned by C is used to generate an access group that is then used to determine the access rights of the client. When the connection with the client is closed, the method C is called, if it exists. While you may include the I parameter in a dynamically generated access group, some care should be taken (unless your pattern is just C<*> which is equivalent to leaving the parameter out). The group created with the values returned from the Python script is the only one considered when B attempts to find an access group matching the connection. If a I parameter is included and it does not match the connection, then the client will be denied access since there are no other access groups which could match the connection. =head2 Dynamic Access Control If you need to have access control rules applied immediately without having to restart all the B processes, you may apply access control on a per newsgroup basis using the Python dynamic hooks (as opposed to F, which does the same on per user basis). These hooks are activated through the inclusion of the I parameter in a F auth group. Only one I statement is allowed in an auth group. When a I parameter is encountered, Python is loaded (if it has yet to be) and the file given as argument is loaded as well (do not include the C<.py> extension of this file in the value of I). If a Python object with a method C is hooked in during the loading of that file, then that method is called immediately after the file is loaded. Every time a reader asks B to read or post an article, the Python method C is invoked before proceeding with the requested operation. Based on the value returned by C, the operation is either permitted or denied. When the connection with the client is closed, the method C is called if it exists. =head2 Writing a Python B Authentication Module You need to create a F module in INN's filter directory (see the I setting in F) where you should define a class holding certain methods depending on which hooks you want to use. Note that you will have to use different Python scripts for authentication and access: the values of I, I and I have to be distinct for your scripts to work. The following methods are known to B: =over 4 =item __init__(I) Not explicitly called by B, but will run whenever the auth module is loaded. Use this method to initialize any general variables or open a common database connection. This method may be omitted. =item authen_init(I) Initialization function specific to authentication. This method may be omitted. =item authenticate(I, I) Called when a I statement is reached in the processing of F. Connection attributes are passed in the I dictionary. Returns a response code, an error string, and an optional string to be used in place of the client-supplied username (both for logging and for matching the connection with an access group). The NNTP response code should be 281 (authentication successful), 481 (authentication unsuccessful), or 403 (server failure). If the code returned is anything other than these three values, B will use 403. If C dies (either due to a Python error or due to calling die), or if it returns anything other than the two or three element array described above, an internal error will be reported to the client, the exact error will be logged to syslog, and B will drop the connection and exit with a 400 response code. =item authen_close(I) This method is invoked on B termination. You can use it to save state information or close a database connection. This method may be omitted. =item access_init(I) Initialization function specific to generation of an access group. This method may be omitted. =item access(I, I) Called when a I statement is reached in the processing of F. Connection attributes are passed in the I dictionary. Returns a dictionary of values representing statements to be included in an access group. =item access_close(I) This method is invoked on B termination. You can use it to save state information or close a database connection. This method may be omitted. =item dynamic_init(I) Initialization function specific to dynamic access control. This method may be omitted. =item dynamic(I, I) Called when a client requests a newsgroup, an article or attempts to post. Connection attributes are passed in the I dictionary. Returns C to grant access, or a non-empty string (which will be reported back to the client) otherwise. =item dynamic_close(I) This method is invoked on B termination. You can use it to save state information or close a database connection. This method may be omitted. =back =head2 The I Dictionary The keys and associated values of the I dictionary are described below. =over 4 =item I C or C values specify the authentication type; only valid for the C method. =item I It is the resolved hostname (or IP address if resolution fails) of the connected reader. =item I The IP address of the connected reader. =item I The port of the connected reader. =item I The hostname of the local endpoint of the NNTP connection. =item I The IP address of the local endpoint of the NNTP connection. =item I The port of the local endpoint of the NNTP connection. =item I The username as passed with AUTHINFO command, or C if not applicable. =item I The password as passed with AUTHINFO command, or C if not applicable. =item I The name of the newsgroup to which the reader requests read or post access; only valid for the C method. =back All the above values are buffer objects (see the notes above on what buffer objects are). =head2 How to Use these Methods with B To register your methods with B, you need to create an instance of your class, import the built-in B module, and pass the instance to C. For example: class AUTH: def authen_init(self): ... blah blah ... def authenticate(self, attributes): ... yadda yadda ... import nnrpd myauth = AUTH() nnrpd.set_auth_hook(myauth) When writing and testing your Python filter, don't be afraid to make use of C/C and the provided C function. stdout and stderr will be disabled, so your filter will die silently otherwise. Also, remember to try importing your module interactively before loading it, to ensure there are no obvious errors. One typo can ruin your whole filter. A dummy F module is provided to facilitate testing outside the server. It is not actually used by B but provides the same set of functions as built-in B module. This stub module may be used when debugging your own module. To test, change into your filter directory and use a command like: python -ic 'import nnrpd, nnrpd_auth' =head2 Functions Supplied by the Built-in B Module Besides C used to pass a reference to the instance of authentication and authorization class to B, the B built-in module exports the following function: =over 4 =item syslog(I, I) It is intended to be a replacement for a Python native syslog. It works like C, seen above. =back =head1 Available Packages This is an unofficial list of known filtering packages at the time of publication. This is not an endorsement of these filters by ISC or the INN developers, but is included as assistance in locating packages which make use of this filter mechanism. =over 4 =item PyClean URL: L (maintained by Steve Crook) PyClean performs a similar role to the original Perl-based Cleanfeed, an extremely powerful spam filter on Usenet. It uses F. =back $Id: hook-python.pod 9899 2015-06-14 11:44:57Z iulius $ =cut inn-2.6.0/doc/pod/cnfsheadconf.pod0000644000175200017520000000162112575023702016374 0ustar iuliusiulius=head1 NAME cnfsheadconf - Read and write CNFS headers =head1 SYNOPSIS B [B<-hw>] [B<-c> I] =head1 DESCRIPTION I reads I/cycbuff.conf and I/storage.conf to determine which CNFS buffers are available. It then reads all of them or the specified cyclic buffer via the B<-c> flag, and modifies the header as directed by the interactive user if B<-w> is used. =head1 OPTIONS =over 4 =item B<-c> I Print the status of the specified class. It also modifies it in case B<-w> is used. =item B<-h> Print usage information and exit. =item B<-w> Prompt for modifications to make to cycbuff header. =back =head1 HISTORY Written by Katsuhiro Kondou for InterNetNews. Converted to POD by Julien Elie. $Id: cnfsheadconf.pod 8545 2009-07-03 21:56:39Z iulius $ =head1 SEE ALSO cycbuff.conf(5), inn.conf(5), storage.conf(5). =cut inn-2.6.0/doc/pod/subscriptions.pod0000644000175200017520000000324512575023702016666 0ustar iuliusiulius=head1 NAME subscriptions - Default recommended subscriptions =head1 DESCRIPTION The I/subscriptions file contains a list of newsgroups that is returned by the NNTP command LIST SUBSCRIPTIONS. Clients that support this command usually send it the first time they connect to a new news server. They use the returned list to initialize the list of subscribed newsgroups. The F file therefore should contain groups intended for new users, for testing, or that contain FAQs and other useful information for first-time Usenet users. The syntax of the F file is trivial; it is a simple list of newsgroup names, one per line. The order of newsgroups may be significant; the news reading client may present the groups in that order to the user. Be aware that use of the LIST SUBSCRIPTIONS command is not widespread (though documented in S) and most news clients will never ask for this file. =head1 EXAMPLE A typical F file may look like: news.announce.newusers news.newusers.questions local.test local.general local.talk misc.test misc.test.moderated news.answers news.announce.newgroups This gives the client the FAQs and question newsgroup for new users first, then a local newsgroup for testing and various commonly-read local discussion groups, followed by the world-wide test groups, all the FAQs, and announcements of new world-wide newsgroups. If there is a local new users group, one might want to list it first. =head1 HISTORY Written by Bettina Fink for InterNetNews. $Id: subscriptions.pod 9137 2010-10-29 18:09:12Z iulius $ =head1 SEE ALSO nnrpd(8). =cut inn-2.6.0/doc/pod/cnfsstat.pod0000644000175200017520000000407612575023702015607 0ustar iuliusiulius=head1 NAME cnfsstat - Show usage of CNFS buffers =head1 SYNOPSIS B [B<-ahpPsv>] [B<-c> I] [B<-i> I] [B<-l> [I]] [B<-m> I] =head1 DESCRIPTION B reads I/cycbuff.conf and I/storage.conf to determine which CNFS buffers are available. It then reads the specified cyclic buffers and shows their usage status. B can be invoked from B if I is set to true in F, and the result is written to syslog(3). =head1 OPTIONS =over 4 =item B<-a> Besides its usual output, B prints the age of the oldest article in the cycbuff. You may also want to use the B<-v> flag to see extended consistency checks. =item B<-c> I Only information for the specified class is printed. =item B<-h> Print usage information and exit. =item B<-i> I With this option, B has an initial sleep of I seconds at startup. This is useful when B is started at the same time as INN, so that it can wait a little before beginning performing its checks. =item B<-l> [I] With this option, B prints a status snapshot every I seconds, and only exits if an error occurs. When unspecified, the default interval is C<600> seconds. At each iteration, B checks whether the F and F files have been modified, and loads the new configuration if needed. =item B<-m> I Print information about the specified buffer in a format suitable for MRTG. =item B<-p> Print an MRTG config file. =item B<-P> Write PID into I/cnfsstat.pid. =item B<-s> Write output to syslog(3) instead of standard output. =item B<-v> Write additional information, especially about consistency checks for article storage and the F file. =back =head1 HISTORY Written by Katsuhiro Kondou for InterNetNews. Converted to POD by Julien Elie. $Id: cnfsstat.pod 9721 2014-09-24 17:35:46Z iulius $ =head1 SEE ALSO cycbuff.conf(5), history(5), inn.conf(5), rc.news(8), storage.conf(5). =cut inn-2.6.0/doc/pod/tinyleaf.pod0000644000175200017520000001034712575023702015573 0ustar iuliusiulius=head1 NAME tinyleaf - Very simple IHAVE-only NNTP server =head1 SYNOPSIS B I [I] =head1 DESCRIPTION B is intended to be the simplest possible transit news server that still does something useful. It must be run under inetd(8) or some equivalent, and only implements three commands (HELP, IHAVE, and QUIT). When it receives an article, it saves it into the directory I and, if I is given, passes information about the article to I via a pipe. The file name of the article will be the MD5 hash of its message-ID, and if a file by that name already exists, B will refuse the article, reporting it as a duplicate. If I is given, it should specify the path to a program. That program is started when B starts, and its current working directory will be I. For each article received by B, a single line will be sent to standard input of I. That line will consist of the file name of the received article (relative to I), a single space, and the message-ID of the received article. Note that the message-ID will be taken from the argument to the IHAVE command and may not match the Message-ID: header in the article. When B shuts down, standard input to I will be closed. B does no syntax verification of received articles whatsoever; it just stores them and optionally passes them off to I. It also never deletes articles; normally, I should do that when it's finished doing whatever it needs to with the article. B expects NNTP commands on standard input and replies on standard output. Status information and any error messages are sent to standard error. It does no authentication; any authentication must be done by inetd(8) or by a wrapper program. (One simple authentication mechanism is to invoke B via tcpd(8) from TCP wrappers and use F and F to restrict who can talk to the server.) B has a (currently hard-coded) maximum message size of S<1 MB> and a (similarly hard-coded) timeout of ten minutes for each command or chunk of article data. =head1 EXAMPLE Suppose that you want to archive news articles on a particular host (like the FTP server for a newsgroup archive) where you don't want the overhead of running a full-blown news server. Write a program that reads one line at a time from standard input and treats everything before the first space as the filename of a news article to archive. Each time the program reads a line, it should archive that file and then delete it, and it should exit when it gets end of file on standard input. Then, add a line like: nntp stream tcp nowait archive /usr/sbin/tcpd \ /tinyleaf /tinyleaf /archive (all on one line S<-- the> backslash and split in this line is just for readability) where C is the user that owns the archive, F is the path to tcpd(8), I/tinyleaf is the path to this program, I/tinyleaf is some scratch directory that the user C has write access to, and I/archive is the path to your B script. You can now restrict access to B to just your local news server with C and C and set up an ordinary feed from the server to the archive host, just like you would to any other news server, of only the newsgroup that you want to archive. Note that the archiving script should probably perform basic syntax and validity checks on the input, since B doesn't. This is the application that motivated the original development of this program. =head1 BUGS The timeout and maximum message size should really be configurable. B should also probably not just respond 500 to every command other than HELP, IHAVE, and QUIT; there are more useful (and more expected) error codes that could be returned. An option to scan the spool directory for any left-over files and pass them to the processor when starting up would be useful. =head1 HISTORY Written by Russ Allbery for InterNetNews. $Id: tinyleaf.pod 9767 2014-12-07 21:13:43Z iulius $ =head1 SEE ALSO hosts_access(5), inetd(8), tcpd(8). =cut inn-2.6.0/doc/pod/newsfeeds.pod0000644000175200017520000010420412575023702015737 0ustar iuliusiulius=head1 NAME newsfeeds - Determine where Usenet articles are sent =head1 DESCRIPTION The file I/newsfeeds specifies how incoming articles should be distributed to other programs and files on the server. It is parsed by the InterNetNews server innd(8) when it starts up, or when directed to by ctlinnd(8). B doesn't send articles to remote sites itself, so F doesn't directly determine which remote news servers articles are sent to. Instead, it specifies what batch files should be created or which programs should be run (and what information should be sent to them), and then this information is used by programs like innxmit(8) and innfeed(8) to feed articles to remote sites. The F file isn't used solely to set up feeding accepted articles to remote sites but also to pass them (or bits of information about them) to any local programs or files that want that data. For example, controlchan(8), a daemon that processes incoming control messages, runs out of F, as could a news to mail gateway. The file is interpreted as a set of lines, parsed according to the following rules: If a line ends with a backslash, the backslash, the newline, and any whitespace at the start of the next line is deleted. This is repeated until the entire "logical" line is collected. If the logical line is blank or starts with a number sign (C<#>), it is ignored. All other lines are interpreted as feed entries. An entry should consist of four colon-separated fields; two of the fields may have optional sub-fields, marked off by a slash. Fields or sub-fields that take multiple parameters should be separated by a comma. Extra whitespace can cause problems and should be avoided. Except for the site names, case is significant. The format of an entry is: sitename[/exclude,exclude,...]\ :pattern,pattern...[/distribution,distribution...]\ :flag,flag...\ :parameter Each field is described below. The I is the name of the site to which a news article can be sent. It is used for writing log entries and for determining if an article should be forwarded to a site. (A "site" is the generic term for some destination of newsfeed data; it often corresponds to a remote news peer, but doesn't have to. For example, a local archiving program run from F is also a "site".) If I already appears in the article's Path: header, then the article will not be sent to the site. The name is usually whatever the remote site uses to identify itself in the Path: header, but can be almost any word. Be careful, though, to avoid having the I accidentally match a Path: header entry unintentionally. For this reason, special local entries (such as archivers or gateways) should probably end with an exclamation point to make sure that they do not have the same name as any real site. For example, C is an obvious name for the local entry that forwards articles out to a mailing list. If a site with the name C posts an article, when the local site receives the article it will see the name in the Path and not send the article to its own C entry. Since C can't appear as an individual Path: entry since C is a delimiter in the Path: header, that would be a better thing to use for I. (Another way to avoid this problem is with the C flag; see the description below.) If an entry has an exclusion sub-field, the article will not be sent to that site if any of I appear in the Path: header. (It's sometimes convenient to have the I be an abbreviated form of the name of the remote site, since all the Is to which an article is sent are written to the log and using shorter Is can therefore improve performance for large servers. In this case, the Path: header entries of those sites should be given as I entries and the C flag used so that the abbreviated I doesn't accidentally match some other Path: header entry.) The same I can be used more than once and the appropriate action will be taken for each entry that should receive the article, but this is recommended only for program feeds to avoid confusion. Case is not significant in site names. The comma-separated I specifies which groups to send to the site; it is interpreted to build a "subscription list" for the site. The default subscription is to get all groups carried by the server. It is a uwildmat(3) pattern supporting poison (C<@>) wildcards; see the uwildmat(3) man page for full details on the pattern matching language. I will be matched against every newsgroup carried by the server and all newsgroups that match will be added to the subscription list for the site. Normally, a given article (or information about it) is sent to a site if any of the newsgroups to which the article was posted are in that site's subscription list. If a newsgroup matches a C<@> pattern in I, then not only is it not added to the subscription list, but any articles crossposted to that newsgroup also will not be sent to that site even if other newsgroups to which it was crossposted are in that site's subscription list. This is called a poison pattern (because matching groups are "poisoned"). For example, to receive all comp.* groups, but only comp.sources.unix within the sources newsgroups, the following I can be used: comp.*,!comp.sources.*,comp.sources.unix Note that the trailing C<.*> is required; the pattern has to match the whole newsgroup name. C could be written C and would exclude the newsgroup comp.sources (if it exists) as well as the groups in the comp.sources.* hierarchy, but note that this would also exclude a newsgroup named comp.sources-only (whereas the above pattern would add that group to the site subscription list since it matches C and none of the other patterns). For another example, to feed alt.* and misc.* to a given site but not any articles posted to alt.binaries.warez (even if they're also crossposted to other alt.* or misc.* groups), the following pattern can be used: alt.*,@alt.binaries.warez,misc.* Note, however, that if you reversed the C and C<@alt.binaries.warez> entries, this pattern would be equivalent to C, since the last matching pattern determines whether a given newsgroup matches and the poison logic only applies if the poison entry is the last matching entry. Control messages follow slightly different propagation rules than normal articles; see innd(8) for the details. Note that most subscriptions should have C in their pattern list due to those propagation rules (and since C is a special internal newsgroup; see I in inn.conf(5) for more details on what it's used for) and that the best way to keep control messages local to a site is with a distribution. A subscription can be further modified by specifying distributions that the site should or should not receive. The default is to send all articles to all sites that subscribe to any of the groups where it has been posted, but if an article has a Distribution: header and any Is are specified, then they are checked according to the following rules: =over 4 =item 1. If the Distribution: header matches any of the values in the sub-field, the article is sent. =item 2. If a I starts with an exclamation point, and it matches the Distribution: header, the article is not sent. =item 3. If the Distribution: header does not match any I in the site's entry and no negations were used, the article is not sent. =item 4. If the Distribution: header does not match any I in the site's entry and any I started with an exclamation point, the article is sent. =back If an article has more than one distribution specified, then each one is handled according according to the above rules. If any of the specified distributions indicate that the article should be sent, it is; if none do, it is not sent. In other words, the rules are used as a logical or. It is almost definitely a mistake to have a single feed that specifies distributions that start with an exclamation point along with some that don't. Distributions are text words, not patterns; entries like C<*> or C have no special meaning. The I field is described in L<"FLAG VALUES">. The interpretation of the I field depends on the type of feed and is explained in more detail in L<"FEED TYPES">. It can be omitted for some types of feeds. The site named C is special. There must be exactly one such entry, and it should be the first entry in the file. If the C entry has an exclusion sub-field, incoming articles are rejected completely if any of the names specified in that exclusion sub-field appear in their Path: headers. If the C entry has a subscription list, that list is prepended to the subscription list of all other entries. For example, C<*,!control,!control.*,!junk,!foo.*> could be used to set the default subscription list for all other feeds so that local postings are not propagated unless C explicitly appears in the site's subscription list. This feature tends to be somewhat confusing since the default subscription is prepended and can be overridden by other patterns. If the C entry has a distribution sub-field, only articles that match that distribution list are accepted and all other articles with a distribution are rejected. A common use for this is to put something like C in the C entry to reject local postings from other misconfigured sites. The distribution sub-field of C has no effect on the acceptance or rejection of articles that do not have a Distribution header field. An empty C entry is possible, in which case no exclusion patterns will be defined. Finally, it is also possible to set variables in F and use them later in the file. A line starting with C<$> sets a variable. For example: $LOCALGROUPS=local.*,example.* This sets the variable C to C. This variable can later be used elsewhere in the file, such as in a site entry like: news.example.com:$LOCALGROUPS:Tf,Wnm: which is then completely equivalent to: news.example.com:local.*,example.*:Tf,Wnm: Variables aren't solely simple substitution. If either C or C<@> immediately preceds the variable and the value of the variable contains commas, that character will be duplicated before each comma. This somewhat odd-sounding behavior is designed to make it easier to use variables to construct feed patterns. The utility becomes more obvious when you observe that the line: news.example.net:*,@$LOCALGROUPS:Tf,Wnm: is therefore equivalent to: news.example.net:*,@local.*,@example.*:Tf,Wnm: which (as explained below) excludes all of the groups in $LOCALGROUPS from the feed to that site. =head1 FLAG VALUES The I parameter specifies miscellaneous parameters, including the type of feed, what information should be sent to it, and various limitations on what articles should be sent to a site. They may be specified in any order and should be separated by commas. Flags that take values should have the value immediately after the flag letter with no whitespace. The valid flags are: =over 4 =item B> I An article will only be sent to this site if it is less than I bytes long. The default is no limit. =item B> I An article will only be sent to this site if it is greater than I bytes long. The default is no limit. =item B I An article will only be sent to this site if it meets the requirements specified in I, which should be chosen from the following set. I can be multiple letters if appropriate. Note that this flag is not effective on funnel targets; it has to be used on every funnel entry (for instance, B is not effective on the I funnel target and therefore has to be specified on every funnelled news site). =over 3 =item c Exclude all kinds of control messages. =item C Only send control messages, not regular articles. =item d Only send articles with a Distribution: header. Combined with a particular distribution value in the I part of the site entry, this can be used to limit articles sent to a site to just those with a particuliar distribution. =item e Only send articles where every newsgroup listed in the Newsgroups: header exists in the F file. =item f Don't send articles rejected by filters. This is only useful when I is set to true in F. With that variable set, this lets one accept all articles but not propagate filtered ones to some sites. =item j Propagate articles according to their Newsgroups: header. This is only useful when I is set to true in F. With that variable set, articles accepted and filed in C (due to I) are fed to peers based on their subscription pattern applied to the Newsgroups: header as though they were accepted and all those groups were locally carried. Otherwise, they are propagated to sites that receive the C newsgroup. This variable is useful if you want to run INN with a minimal F file and propagate all posts. =item o Only send articles for which overview data was stored. =item O Send articles to this site that don't have an Injection-Info: or X-Trace: header, even if the C flag is also given. =item p Only check the exclusions against the Path: header of articles; don't check the site name. This is useful if your site names aren't the same as the Path: entries added by those remote sites, or for program feeds where the site name is arbitrary and unrelated to the Path: header. =back If both C and C are given, the last specified one takes precedence. =item B I/I If a site is being fed by a file, channel, or exploder (see below), the server will normally start trying to write the information as soon as possible. Providing a buffer may give better system performance and help smooth out overall load if a large batch of news comes in. The value of the this flag should be two numbers separated by a slash. I specifies the point at which the server can start draining the feed's I/O buffer, and I specifies when to stop writing and begin buffering again; the units are bytes. The default is to do no buffering, sending output as soon as it is possible to do so. =item B I If this flag is specified, an article will only be sent to this site if the number of groups it is posted to, plus the square of the number of groups followups would appear in, is no more than I. C<30> is a good value for this flag, allowing crossposts to up to 29 groups when followups are set to a single group or poster and only allowing crossposts to 5 groups when followups aren't set. =item B I Specifies the name of the file that should be used if it's necessary to begin spooling for the site (see below). If I is not an absolute path, it is taken to be relative to I in F. If I is a directory, the file F in that directory will be used as the file name. =item B I If this flag is specified, an article will only be sent to this site if it is posted to no more than I newsgroups. This has the problem of filtering out many FAQs, as well as newsgroup creation postings and similar administrative announcements. Either the B flag or the B flag is a better solution. =item B I If this flag is specified, an article will only be sent to this site if it has I or fewer sites in its Path: line. This flag should only be used as a rough guide because of the loose interpretation of the Path: header; some sites put the poster's name in the header, and some sites that might logically be considered to be one hop become two because they put the posting workstation's name in the header. The default value for I if not specified is one. (Also see the B flag, which is sometimes more appropriate for some uses of this flag.) =item B I The flag specifies the size of the internal buffer for a file feed. If there are more file feeds than allowed by the system, they will be buffered internally in least-recently-used order. If the internal buffer grows bigger then I bytes, however, the data will be written out to the appropriate file. The default value is S KB>. =item B I Restricts the articles sent to this site to those in newsgroups with the moderation status given by I. If I is C, only articles in moderated groups are sent; if I is C, only articles in unmoderated groups are sent. =item B I If this flag is specified, an article will only be sent to this site if it contains an Injection-Info: header (or an X-Trace: header if no Injection-Info: header is found) and the first field of this header matches I. I is a uwildmat(3) expression without commas or a list of such expressions, separated by C. The article is never sent if the first character of the pattern begins with C<@> and the rest of the pattern matches. One use of this flag is to restrict the feed to locally generated posts by using an I pattern that matches the Injection-Info: header added by the local server. =item B
] [B<-f> I] [B<-n> I] [B<-p> I] [B<-R> I] =head1 DESCRIPTION B is an administrative interface to the tradindexed overview method for INN. It only works on tradindexed overview databases, not on any other type of INN overview. It allows the administrator to dump various information about the internal state of the overview, audit it for errors, and rebuild portions of the overview database. The tradindexed overview method should lock properly and therefore it should be safe to run this utility and perform any operation it performs, including full repairs or per-group overview rebuilds, while the server is running. However, note that some of the operations performed by this utility can take an extended period of time and will hold locks in the overview database during that period, which depending on what the server is doing may cause the server to stall until B completes its operation. The dump operations are B<-i>, which dumps the master index for the overview database, B<-g>, which dumps the index for an individual group, and B<-o> and B<-O>, which dump the overview information for a particular group (including the additional metadata stored in the index) in two different formats. For B<-g>, B<-o>, and B<-O>, the B<-n> option must also be given to specify a newsgroup to operate on. To add a new newsgroup to the overview database, use B<-c>. A group must be specified with B<-n>. If the group status is something other than C, it should be specified with B<-f>, and the low and high article numbers may be specified with B<-a>. If only one number is given rather than a range, it will be taken to be the high water mark and the low mark will be set to 1. To audit the entire overview database for problems, use B<-A>. Any problems found will be reported to standard error. Use B<-F> to correct the errors found. To rebuild the database for a particular newsgroup, use B<-R>. The B<-R> option takes a path to a directory which contains all of the articles for that newsgroup, one per file. The names of the files must be the numbers of the articles in that group. (In other words, this directory must be a traditional spool directory for that group.) The B<-n> option must also be given to specify the newsgroup for which the overview is being rebuilt. For all operations performed by B, a different overview database than the one specified in F may be specified using the B<-p> option. =head1 OPTIONS =over 4 =item B<-A> Audit the entire overview database for problems. This runs the internal consistency checks built into the tradindexed overview implementation, checking such things as the validity and reachability of all group index entries, the format of the individual overview entries, the correspondence of index entries to overview data, and similar such things. No changes will be made to the database, but problems will be reported to standard error. =item B<-a> I
The article number or numbers to act on. I
is a valid NNTP range, meaning that it can be either a single article number or a range of article numbers (like C<1-5>). Either the start or the end (or both) of the range may be omitted, in which case they will be set to the first or last article number in the group. Passing C<-> for I
is therefore equivalent to not using the B<-a> option at all. Only useful in combination with the B<-o> option to dump overview information or with B<-c> to specify the low and high article numbers when creating a group. =item B<-c> Create a new group in the overview database. The group must be specified with B<-n>. The newsgroup status defaults to C but may be set with B<-f>. The low and high article numbers default to 1 and 0 respectively, but may be set with B<-a>. If only one number is given to B<-a>, it is taken as the high article number. =item B<-F> Audit the entire overview database for problems, fixing them as they're detected where possible. This runs the internal consistency checks built into the tradindexed overview implementation, checking such things as the validity and reachability of all group index entries, the format of the individual overview entries, the correspondence of index entries to overview data, and similar such things. The strategy used when fixing problems is to throw away data that's unrecoverable, so be warned that using this option may result in inaccessible articles if their overview data has been corrupted. To see what would be changed by B<-F>, run B with B<-A> first. =item B<-f> I When creating a newsgroup with B<-c>, set the status of the newly created group to I instead of C. Only useful with B<-c>. =item B<-g> Dump the index of a particular group. The fields are, in order, the article number, the offset of the data for that article in the overview data file for that group, the length of the overview data, the time (in seconds since epoch) when the article arrived on the server, the time (in seconds since epoch) when the article should expire based on its Expires: header (or 0 if there is no Expires: header), and the storage API token of the article. If this option is given, the B<-n> option must also be given to specify the newsgroup on which to act. =item B<-i> Dump the master index of the overview database. This contains similar information to the server F file, such as high and low water marks and moderation status, and is the information that B hands out to clients. The fields are, in order, the newsgroup name, the high water mark, the low water mark, the base article number (the point at which the group index begins), the count of articles in the group, the group status, the time (in seconds since epoch) when that newsgroup was deleted or 0 if it hasn't been, and the inode of the index file for that group. A particular newsgroup can be specified with the B<-n> option. If B<-n> is not given, the entire master index will be dumped. =item B<-n> I Specify the newsgroup on which to act, required for the B<-i>, B<-o>, and B<-R> options. =item B<-O> Dump the overview information for a newsgroup in the format used by B as input. Each line will start with the storage API token, the arrival timestamp in seconds since epoch, the expires timestamp in the same format (or 0 if there is no Expires: header), and then the overview data. If this option is given, the B<-n> option must also be given to specify the newsgroup on which to act. By default, all of the overview information for that newsgroup is dumped, but the B<-a> option may be given to restrict the dump to the information for a single article. =item B<-o> Dump the overview information for a newsgroup, in the same format as it would be returned to clients but with one modification. Appended to the end of each entry will be four additional pieces of data: the article number according to the index file for that group labelled with C, the storage API token for that article labelled with C, the arrival date for that article on the server in S date format labelled with C, and the expiration date for that article (from the Expires: header) in S date format if there is any, labelled with C. If this option is given, the B<-n> option must also be given to specify the newsgroup on which to act. By default, all of the overview information for that newsgroup is dumped, but the B<-a> option may be given to restrict the dump to the information for a single article. =item B<-p> I Act on the overview database rooted in I, overriding the overview path specified in F. =item B<-R> I Rebuild the overview for a given group from the articles stored in I. The articles must be in the form of a traditional spool directory; in other words, each article must be in a separate file and the name of the file must match the article number of the article. If this option is given, the B<-n> option must also be given to specify the newsgroup on which to act. =back =head1 EXAMPLES Dump the master index for the overview database in F, regardless of the overview path specified in F: tdx-util -i -p /news/overview Dump the group index for example.test: tdx-util -g -n example.test Dump the complete overview information for example.test: tdx-util -o -n example.test Dump the overview information for articles 45 and higher in example.test: tdx-util -o -n example.test -a 45- Create an entry for example.test with mode m and low and high article numbers of 4 and 23, respectively. tdx-util -c -n example.test -f m -a 4-23 Audit the entire overview database for any problems: tdx-util -A Rebuild the overview information for example.test from a traditional spool directory: tdx-util -R /example/test -n example.test The last command may be useful for recovering from a bad crash or corrupted overview information for a particular group, if you are also using the tradspool article storage method. =head1 HISTORY Written by Russ Allbery for InterNetNews. $Id: tdx-util.pod 9767 2014-12-07 21:13:43Z iulius $ =head1 SEE ALSO makehistory(8), nnrpd(8). =cut inn-2.6.0/doc/pod/buffindexed.conf.pod0000644000175200017520000001401712575023702017165 0ustar iuliusiulius=head1 NAME buffindexed.conf - Configuration for the buffindexed overview method =head1 DESCRIPTION F, found in I, specifies the buffers that the buffindexed overview method should use. It is required if the server uses buffindexed (as configured by the I parameter in F). Buffindexed uses pre-built buffer files to store overview data and indexes to that data. The buffers are divided into S<8 KB> internally, and a given block is used either for overview data or for index data. A block is always allocated to a single newsgroup and is never shared among newsgroups. In addition to the buffers, buffindexed also stores information in a file named F in I. (This file should not be mistaken for the one named F in I which is used by the tradindexed overview method.) It contains information about each newsgroup: the pointer to the index block for the newsgroup, the high mark, the low mark, the flag of the group, the number of articles, and so forth. This file is created automatically when all buffers are initialized and should not be manually edited. Buffindexed buffers are of fixed size, so buffindexed will never use more space than what is available in those buffers. If all buffers are full, B will throttle when it attempts to store overview information for any additional articles until space is freed (with B, for instance) or another buffer is added. This is unlike the CNFS storage method. You can see the current usage of the buffers with the B<-o> option to B. In the F file, blank lines and lines beginning with a number sign (C<#>) are ignored. All other lines must be of the format: :: The order of lines is not significant. is the index of this overview buffer and must be unique. Other than that constraint, it can be any number between 0 and 65535. is the path to the buffer. The length of the path should not be longer than 63 characters. is the length of the buffer in kilobytes (S<1 KB = 1024 bytes>). If does not specify a special device, the file size of the buffer must be S<< * 1024 bytes >>. If it does specify a special device, that device must have at least space available. For more information on setting up the buffers, see L. An example of F file can be: 0:/OV1:1536000 1:/OV2:1536000 When you first start B with everything configured properly, you should see messages like this in I/news.notice: Aug 27 00:00:00 kevlar innd: buffindexed: no magic cookie found for ovbuff 0, initializing You MUST recreate overview completely using B if you remove or replace buffers. However, new buffers can be added without any special care (other than restarting B after modifying F). If you need to rebuild overview, you should zero all of the buffers first. =head1 CREATING BUFFERS There are two methods to create a new buffindexed buffer: =over 4 =item 1. Create a large file on top of a regular file system. The easiest way to do this is probably with dd(1), using a command like: dd if=/dev/zero of=/path/to/cycbuff bs=1024 count= where is the size from the relevant line in F. This is the simplest method, but has the disadvantage that very large files on regular file systems can be fairly slow to access, particularly at the end of the file, and INN incurs unnecessary file system overhead when accessing the buffer. =item 2. Use block devices directly. If your operating system allows you to call mmap() on block devices (Solaris and recent versions of Linux do, FreeBSD at last report does not), this method can avoid all of the native file system overhead. Note, however, that Solaris has problems with byte range locking on block devices, and therefore this method should not be used on Solaris. Partition the disk. If you're using Solaris, set up your partitions to avoid the first cylinder of the disk (or otherwise the buffindexed header will overwrite the disk partition table and render the buffers inaccessible). Then, create device files for each block device you're going to use. It's not recommended to use the block device files in F, since the news system doesn't have permission to write to them and changing the permissions of the system device files may affect something else. Instead, use mknod(1) to create a new set of block devices (in somewhere like I/overview that's only writable by the news user). To do this, run C on the devices in F that correspond to the block devices that you want to use. The major and minor device numbers are in the fifth and sixth columns (right before the date), respectively. Then run mknod like: mknod b where is the path to the device to create (matching the part of the buffindexed configuration line) and and are the major and minor device numbers as discovered above. Here's a short script to do this when given the path to the system device file as an argument: #!/bin/sh base=`echo "$1" | sed 's%.*/%%'` major=`ls -Ll "$1" | awk '{print $5}' | tr -d ,` minor=`ls -Ll "$1" | awk '{print $6}` mkdir -p mknod /"$base" b "$major" "$minor" chown news:news /"$base" chmod 644 /"$base" Make sure that the created files are owned by the news user and news group, as specified at configure time (the default being C for both). Also make sure that the permissions on the devices allow the news user to read and write. =back =head1 HISTORY Written by Katsuhiro Kondou for InterNetNews. Converted to POD by Russ Allbery . $Id: buffindexed.conf.pod 9925 2015-08-08 17:05:43Z iulius $ =head1 SEE ALSO expireover(8), inn.conf(5), inndf(8), makehistory(8). =cut inn-2.6.0/doc/pod/innbind.pod0000644000175200017520000002046112575023702015377 0ustar iuliusiulius=head1 NAME innbind - Helper program to bind sockets to privileged ports =head1 SYNOPSIS B [B<-p>] I,I,I
,I [...] =head1 DESCRIPTION B is a helper program that's not meant to be run directly. Instead, B and B use it internally to bind to ports that require root privileges to bind to. This program must be installed setuid root in order for B or B to bind to ports under 1024. The only functions that it's willing to perform are to bind an open file descriptor to a given address and port or to create a new socket, bind it, and return the bound socket to its caller. It can only be run as the news user (as specified at configure time), and will only bind to ports 119, 433, 563, an additional port specified with the B<--with-innd-port> argument to configure, or ports numbered 1024 or higher. Each argument to B must be a comma-separated list of four elements. The first is the file descriptor number that should be bound, the second is the numeric family of the socket (AF_INET or AF_INET6), the third is the local address to bind to (in dotted-quad format for IPv4 and in colon-separated address format for IPv6), and the fourth is the port number. To bind to all addresses with IPv4, use C<0.0.0.0> as the address. To bind to all addresses with IPv6, use C<::> as the address. Multiple arguments can be specified to tell B to bind multiple sockets at the same time. Any errors (other than permission denied S<-- see> below) encountered will cause B to abort, and error messages will be sent both to syslog and to standard error. By default, B attempts to just bind the already open file descriptor that it inherits from its caller. For each successfully bound file descriptor (in the order given on the command line), B prints C and a newline to its standard output. On some systems (apparently just STREAMS-based systems), however, even a setuid root program cannot bind a socket to a privileged port that was created by a process that didn't have permissions. If B gets permission denied when trying to bind a socket, it will print C and a newline to its standard output. It will then create a new socket, bind it as specified, and then attempt to pass that socket back to its caller using the I_SENDFD STREAMS ioctl. The caller should receive that file descriptor with I_RECVFD and use it instead of the one that it created. Note that file descriptor passing is only supported on STREAMS-based systems since it is done with ioctls over a pipe. However, it is believed that those systems are exactly the systems that can't simply bind the inherited file descriptor. If this assumption proves to be incorrect, traditional BSD file descriptor passing over a Unix domain socket will have to be added. =head1 OPTIONS =over 4 =item B<-p> If given as the first command-line argument, no attempt will be made to bind the inherited file descriptor and B will only try creation of a new file descriptor and passing it back via standard output. This option is primarily useful for testing. =back =head1 SECURITY As B is normally installed setuid root, security is even more of an issue for it than for other parts of INN. It is a fairly short program, and if you understand C, you are encouraged to audit it yourself to be certain that it does only what it is supposed to do. The only INN library functions it uses are the vector functions, the message functions for error reporting, and xstrdup. The ports that will be bound are restricted to prevent potential attacks made possible by the ability to bind low-numbered ports, such as exploits of the rsh(1) family of commands on some systems. If B is installed setuid root, it can only be executed by the news user to prevent other users on the system from being able to bind to even those few privileged ports that it allows. B uses no external configuration files; the only files it might open are through the system getpwnam(3) service to get the UID of the news user. The only user input that it accepts are its command-line arguments. =head1 DIAGNOSTICS B may log the following messages to syslog and print them to standard error. =over 4 =item cannot create socket for %s: %s (Fatal) B fell back on attempting to create a new socket to bind for the given argument, and the socket creation failed. =item cannot bind socket for %s: %s (Fatal) Calling bind for the socket corresponding to the given argument failed with a system error. If the error indicates permission denied, make sure that B is setuid root. This can also be caused by trying to use IPv6 on a system whose kernel does not support it. =item cannot bind to restricted port %hu in %s (Fatal) The port number portion of the given command-line argument is for a port below 1024 which is not 119, 433, 563, or a port given to B<--with-innd-port> at configure time. Other ports are not allowed for security reasons. =item cannot get socket options for file descriptor %d: %s (Fatal) B was unable to get the socket options for that file descriptor. The most likely cause of this error is passing the wrong file descriptor number to B (a file descriptor that isn't open, or that corresponds to a regular file rather than a network socket). =item cannot get UID for %s (Fatal) B was unable to get the UID for the news user specified during configure (and defaulting to C). This normally means that user isn't in the system F file. =item cannot mark socket reusable for %s: %s (Fatal) B created a new socket for the given argument but was unable to mark its bind address reusable (the SO_REUSEADDR socket option). =item cannot pass file descriptor: %s (Fatal) B created and bound a new file descriptor but was unable to pass it back to its caller via its standard output, using the I_SENDFD STREAMS ioctl. =item invalid file descriptor %d: not SOCK_STREAM (Fatal) The given file descriptor is not a SOCK_STREAM socket. B can only bind SOCK_STREAM sockets. =item invalid IPv4 address %s in %s (Fatal) The IPv4 address specified in the given command-line option could not be parsed by inet_aton(3). IPv4 addresses should be specified in the standard dotted-quad format (10.2.3.4). =item invalid IPv6 address %s in %s (Fatal) The IPv6 address specified in the given command-line option could not be parsed by inet_pton(3). IPv6 addresses should be specified in S format (1080:0:0:0:8:800:200C:417A or 1080::8:800:200C:417A). =item invalid command-line argument %s (Fatal) The specified command-line argument could not be parsed or was not in the correct format. =item invalid file descriptor %s in %s (Fatal) The file descriptor portion of the given command-line argument is not a non-negative integer. =item invalid port number %s in %s (Fatal) The port number portion of the given command-line argument is not a non-negative integer. =item invalid protocol family %s in %s (Fatal) The protocol family portion of the given command-line argument is not a non-negative integer. It should be equal to either AF_INET or AF_INET6 on the system where B is run. =item must be run by user %s (%lu), not %lu (Fatal) When setuid root, B may only be run by the news user as specified at configure time (C by default), for security reasons. =item no addresses specified (Fatal) No arguments were given on the command line (except maybe B<-p>). =item port may not be zero in %s (Fatal) The port number portion of the given command-line argument was zero. =item unknown protocol family %s in %s (Fatal) The protocol number portion of the given command-line argument is neither AF_INET nor AF_INET6. =back =head1 EXAMPLES As mentioned above, B is never run directly, only by B and other programs that need to bind to and listen to network ports. Sample invocations by B would be: innbind 3,10,::,119 to bind the IPv6 socket on file descriptor 3 to port 119, all addresses, or: innbind 6,2,10.0.0.3,433 to bind the IPv4 socket on file descriptor 6 to port 433 in the address 10.0.0.3. =head1 HISTORY Written by Russ Allbery for InterNetNews. $Id: innbind.pod 9767 2014-12-07 21:13:43Z iulius $ =head1 SEE ALSO inet_aton(3), inet_pton(3), innd(8), nnrpd(8). =cut inn-2.6.0/doc/pod/simpleftp.pod0000644000175200017520000000267112575023702015764 0ustar iuliusiulius=head1 NAME simpleftp - Rudimentary FTP client =head1 SYNOPSIS B I [...] =head1 DESCRIPTION B is a Perl script that provides basic support for fetching files with FTP in a batch oriented fashion. It takes one or more FTP URLs on the command line. The file(s) will be retrieved from the remote server and placed in the current directory with the same basename as on the remote; e.g., L is stored as F in the current directory. The script properly understands usernames, passwords and ports specified as follows: ftp://user:password@host:port/path/file =head1 BUGS B is an extremely poor substitute for more complete programs like the freely available B or B utilities. It was written only to provide elementary support in INN for non-interactive fetching of the files in L or L without requiring administrators to install yet another package. Its shortcomings as a general purpose program are too numerous to mention, but one that stands out is that downloaded files by B override existing files with the same name in the local directory. =head1 HISTORY Tossed off by David C Lawrence for InterNetNews. Rewritten to use C by Julien Elie. $Id: simpleftp.pod 8357 2009-02-27 17:56:00Z iulius $ =head1 SEE ALSO actsync(8). =cut inn-2.6.0/doc/pod/checklist.pod0000644000175200017520000002570412575023702015734 0ustar iuliusiulius=head1 Introduction $Id: checklist.pod 9937 2015-09-02 12:44:39Z iulius $ This is an installation checklist written by Rebecca Ore, intended to be the beginning of a different presentation of the information in F, since getting started with installing INN can be complex. Further clarifications, updates, and expansion are welcome. =head1 Setup =over 4 =item * Make sure there is a C user (and a C group to which the C user belongs). If necessary, you can use: adduser --group --home /usr/local/news news where F is the home directory for the C user. This directory will be passed to B via the B<--prefix> option. It will also be set as I in F. =item * Create a local mail alias for C (editing your F file for instance). =item * Make sure the directory F and its subdirectories are owned by C, group C. You want to be careful that things in that directory stay owned by C S<-- but> you can't just C after the install, because you may have binaries that are SUID root. You can do the build as any user, but you need to be root when doing C so as to set the permissions correctly. After that point, though, you may want to C to avoid creating any files as root. (For routine maintenance once INN is working, you can generally be root. However, it is always better to be accustomed to doing that as the news user.) =item * If necessary, add F<~news/bin> (that is to say I) to the news user's path and F<~news/share/man> (that is to say I/share/man) to the news user's MANPATH in your shell config files. (You may want to do this, especially the second part, on your regular account; the man pages are very useful.) You can do this now or later, but you will certainly want the man pages to help with configuring INN. For bash, try: PATH=:$PATH export PATH MANPATH=/share/man:$MANPATH export MANPATH or csh: setenv PATH :$PATH setenv MANPATH /share/man:$MANPATH although if you don't already have MANPATH set, the above may give an error or override your defaults (making it so you can only read the news man pages); if C does not give some reasonable path, you'll need to look up what the default is for your system (such as F or F). =back =head1 Compile =over 4 =item * Download the INN tarball and unpack. Make sure that you download the last release from L or a snapshot from L. =item * Work out configure options (C<./configure --help> for a list). If you aren't working out of F, or want to put some files on a different partition, you can set the directories now (or later in I in F if you change your mind). By default, B<--prefix=/usr/local/news> is used. You probably want B<--with-perl>. If you're not using NetBSD with cycbuffs or OpenBSD, perhaps B<--with-tagged-hash>. You might want to compile in TLS/SSL and S, if your system supports them. You will need to have the relevant external libraries to compile (depending on whether you use OpenSSL for TLS/SSL access to your news server, GnuPG to verify the authenticity of Usenet control messages, Perl, Python, etc.). ./configure --with-perl ... make su make install (If you do the last step as root, all of the ownerships and permissions will be correct.) Note that if you update a previous installation of INN, you should use C instead of C to keep your configuration files. If you wish to use TLS/SSL, you can use C to generate a certificate and a private key. =back =head1 Configure =over 4 =item * Find F and open a separate window for it. A printout is probably a good idea S<-- it's> long but very helpful. Any time the instructions below ask you to make a decision, you can probably find help in F. =item * Now it's time to work on the files in F<~news/etc> (the default I location set in F). Start with F; you must fill in the default moderators address, your fully qualified domain names and path. Fill in all the blanks. Change the file descriptor limits to something like C<500>. =item * If using cycbuffs (the CNFS storage method), open F in one window and a shell in another to create the cycbuff as described in F. As you create them, record in F the paths and sizes. Save paths and sizes in a separate text file on another machine in case you ever blow away the wrong file. Name the metacycbuff, then configure F. =item * In F, be sure that all sizes of articles can be accomodated. If you want to throw away large articles, do it explicitly by using the C storage method (you can also set a lower I in F). =item * The default options in F work fine if you have cycbuffs; if not, configure to suit (depending on your disk space and your interest in some hierarchies, you can define how long you keep articles in your news spool). =item * Check over F and F. You may want to also configure the process of newsgroup control messages (see the corresponding section in F). =item * Run C<< inncheck -a -v -f --pedantic --perm >> and fix anything noted: B gives a rough check on the appropriateness of the configuration files as you go. (It's the equivalent of C for Perl scripts.) Note that B is very conservative about permissions; there's no reason most of the config files can't be world-readable if you prefer that. =item * You can now import an F file (I/active) and run B again. You may want to look at L and only keep the lines corresponding to the newsgroups you are interested in. Also import a F file which contains the descriptions of these newsgroups (see for instance L). Note that it is not necessary to do that now. INN is shipped with minimal F and F files and you can add newsgroups later with C or B. In case you need to create empty initial database files, import an F file (owned by C) and execute as the news user: cd touch newsgroups touch active.times touch history /makedbz -i mv history.n.hash history.hash mv history.n.index history.index mv history.n.dir history.dir chmod 644 * (However, it is not necessary to do that since INN takes care of these files during the setup.) =item * Create the cron jobs (especially B), the log files, and make the changes to your system's F as noted in F. Also create the cron job for B if you've chosen that over B. =item * For the time being, we can see if everything initially works without worrying about feeds or reader access. =back =head1 Run =over 4 =item * Start B by running I/rc.news B. It is also what you should launch in your init scripts: su news -s /bin/sh -c /rc.news Check I/news.notice to see if everything went well; also use C to see if B is running. C and you should see either a welcome banner or a C message. If not, investigate. =item * C now; you'll use C as you complete your configuration. You can also see whether C reports any problems. =back =head1 Feeds All of this can be done while INN is running. =over 4 =item * To get your incoming feeds working, edit F. When done, C (where C is some text that will show up in the logs S<-- anything> will do). =item * To get your outgoing feeds working, decide whether to use B or B. Edit F and either F or F. In F, if using B, use the option which doesn't require you to do a separate B configuration unless you know more than I do. Then C. =back =head1 Readers =over 4 =item * In F, remember that auth and access blocks can be separated. Begin with auth. Your auth for password users could look like this: auth "foreignokay" { auth: "ckpasswd -d /newsusers" default: "" } There is a Perl script in the ckpasswd(8) man page if you want to do authentications by password and have the appropriate libraries. Copy it to I, name the file something like F and change the internal paths to whatever you're using and wherever you're putting the newsusers database. The standard Apache B tool also works just fine to create INN password files. For instance, a line for the newsusers database corresponding to the user C with the password C would be C as obtained by the following command: % htpasswd -nbd user pass user:LIfOpbjNaEQYE In case B is not installed or if you do not want to depend on it, another command involving Perl does a similar job: % perl -e 'print "user:".crypt("pass", "LI")."\n";' user:LIfOpbjNaEQYE Follow with the access stanzas. Something for people with passwords: access "generalpeople" { users: "*" newsgroups: "*,!junk,!control,!control.*" } And then something like one of the following two, depending on whether unauthenticated users get any access: access "restrictive" { users: "" newsgroups: "!*" } access "readonly" { users: "" read: "local.*" post: "!*" } You don't need to reload anything after modifying F; every time an B launches, it reads its configuration from disk. =item * If you wish to use TLS/SSL for your readers, you can either use the same F file or use two different files (for instance F and F). The syntax is similar for both these files. You then need to start a second B to listen to these connections to NNTPS port 563 and put something like that in your init scripts: su news -s /bin/sh -c '/nnrpd -D -c /readers-ssl.conf -p 563 -S' Note that a news client which supports the STARTTLS command can also use the conventional NNTP port 119 to initiate a TLS connection. However, as such clients are not widespread yet, using the separate port 563 is still common practice (though discouraged). See nnrpd(8) for more information about TLS support. =back =cut inn-2.6.0/doc/pod/external-auth.pod0000644000175200017520000001060412575023702016535 0ustar iuliusiulius=head1 B External Authentication Support A fundamental part of the readers.conf(5)-based authorization mechanism is the interface to external authenticator and resolver programs. This interface is documented below. INN ships with a number of such programs (all written in C, although any language can be used). Code for them can be found in F of the source tree; the authenticators are installed to I/auth/passwd, and the resolvers are installed to I/auth/resolv. =head1 Reading Information from B When B spawns an external auth program, it passes information on standard input as a sequence of C lines. Each line ends with CRLF, and a line consisting of only a dot followed by CRLF (C<.\r\n>) indicates the end of the input. The order of the fields is not significant. Additional fields not mentioned below may be included; this should not be cause for alarm. (For robustness as well as ease of debugging, it is probably wise to accept line endings consisting only of LF, and to treat EOF as indicating the end of the input even if C<.\r\n> has not been received.) Code which reads information in the format discussed below and parses it into convenient structures is available authenticators and resolvers written in C; see libauth(3) for details. Use of the libauth library will make these programs substantially easier to write and more robust. =head2 For Authenticators When B calls an authenticator, the lines it passes are ClientAuthname: user\r\n ClientPassword: pass\r\n .\r\n where I and I are the username and password provided by the client (e.g. using AUTHINFO). In addition, B generally also passes, if available, the fields mentioned as intended for resolvers; in rare instances this data may be useful for authenticators. =head2 For Resolvers When B calls a resolver, the lines it passes are ClientHost: hostname\r\n ClientIP: IP-address\r\n ClientPort: port\r\n LocalIP: IP-address\r\n LocalPort: port\r\n .\r\n where I indicates a string representing the hostname if available, I is a numeric IP address (dotted-quad for IPv4, equivalent for IPv6 if appropriate), and I is a numeric port number. (The I and I parameters may be useful for determining which interface was used for the incoming connection.) If information is not available, B will omit the corresponding fields. In particular, this applies to the unusual situation of B not being connected to a socket; TCP-related information is not available for standard input. =head1 Returning Information to B =head2 Exit Status and Signals The external auth program must exit with a status of C<0> to indicate success; any other exit status indicates failure. (The non-zero exit value will be logged.) If the program dies due to catching a signal (for example, a segmentation fault occurs), this will be logged and treated as a failure. =head2 Returning a Username and Domain If the program succeeds, it must return a username string (optionally with a domain appended) by writing to standard output. The line it should write is exactly User:username\r\n where I is the string that B should use in matching F access blocks. The C parameter is case-insensitive. There should be no extra spaces in lines sent from the hook to B; C is read by B as a different username than C. If a domain is provided, it would for instance be C. Note that B implements a five-second timeout for the receipt of this line. =head1 Error Messages As mentioned above, errors can be indicated by a non-zero exit value, or termination due to an unhandled signal; both cases are logged by B. However, external auth programs may wish to log error messages separately. Although B will syslog() anything an external auth program writes to standard error, it is generally better to use the F functions, such as warn() and die(). Please use the F program as an example for any authenticators you write, and F as an example for any resolvers. =head1 HISTORY Written by Aidan Cully for InterNetNews. This documentation was rewritten in POD by Jeffrey S . $Id: external-auth.pod 8200 2008-11-30 13:31:30Z iulius $ =cut inn-2.6.0/doc/pod/motd.news.pod0000644000175200017520000000263612575023702015700 0ustar iuliusiulius=head1 NAME motd.news - Message of the day information for feeders or readers =head1 DESCRIPTION Two files, found in I, contain local information for news feeders or news readers in a free-form format. The entire files are returned verbatim to any client that issues the LIST MOTD command. This might be used for new information, notification of upcoming downtime, or similar purposes. These files are named F (for news feeders) and F (for news readers); they are used by B and B, respectively. Make sure that these files are encoded in UTF-8. They should also be "dot-stuffed", that is to say that a line beginning with a dot should have that dot doubled. Be aware that use of the LIST MOTD command is not widespread (though documented in S) and most news clients will never ask for this file. If one or both of these files are missing, it is not an error. The server will just send the client the appropriate response for an unmaintained F or F file. On a fresh INN install, samples for these files are installed in I; you will therefore find in F and F a few examples of messages that can be communicated to news clients. =head1 HISTORY Rewritten in POD by Russ Allbery for InterNetNews. $Id: motd.news.pod 9767 2014-12-07 21:13:43Z iulius $ =head1 SEE ALSO inn.conf(5). =cut inn-2.6.0/doc/pod/ovdb_stat.pod0000644000175200017520000000433212575023702015742 0ustar iuliusiulius=head1 NAME ovdb_stat - Display information from the ovdb database =head1 SYNOPSIS B B<-Hgci> [B<-r> I] newsgroup [newsgroup ...] B B<-Hklmtv> [B<-d> I] =head1 DESCRIPTION B displays information from the ovdb database: S statistics, newsgroup data, and overview records; and optionally outputs in HTML format. =head1 OPTIONS =over 4 =item B<-g> Newsgroup himark, lowmark, article count, and flag for the given newsgroups (as stored in the ovdb "groupinfo" database) are displayed. =item B<-c> Similar to B<-g>, except the himark, lowmark, and count are calculated by actually scanning the overview records and counting them. This can be a lengthy operation on groups with lots of articles. =item B<-i> Internal data regarding the given newsgroups are displayed. =item B<-r> I Overview records are retrieved. The I parameter may be a single article number, or a range of articles in the format C. =item B<-H> Output is presented in HTML format. =item B<-k> Displays lock region statistics, as returned by the S lock_stat() call. =item B<-l> Displays log region statistics, as returned by the S log_stat() call. =item B<-m> Displays global memory pool statistics, as returned by the S memp_stat() call. =item B<-M> Same as B<-m>, and also displays memory pool statistics for each database file. =item B<-t> Displays log region statistics, as returned by the S txn_stat() call. =item B<-v> Displays ovdb version, and S version. =item B<-d> I Displays information about the given database, as returned by the S db->stat() call. This operation may take a long time on busy systems (several minutes or more). =back =head1 WARNINGS ovdb_stat may be safely killed with the INT, TERM, or HUP signals. It catches those signals and exits cleanly. Do not kill ovdb_stat with other signals, unless absolutely necessary, because it may leave stale locks in the DB environment. =head1 HISTORY Written by Heath Kehoe Ehakehoe@avalon.netE for InterNetNews. $Id: ovdb_stat.pod 9765 2014-12-07 21:07:34Z iulius $ =head1 SEE ALSO ovdb(5) =cut inn-2.6.0/doc/pod/ovdb_monitor.pod0000644000175200017520000000156312575023702016461 0ustar iuliusiulius=head1 NAME ovdb_monitor - Database maintenance =head1 SYNOPSIS Use C to start ovdb_monitor =head1 DESCRIPTION When started (by C), C forks three processes that perform routine database maintenance tasks. These are: transaction checkpointing, deadlock detection, and transaction log removal. The process ID of the parent is written to F/ovdb_monitor.pid>. This PID is used by other INN commands to verify that ovdb_monitor is running. To shut down ovdb_monitor, send a TERM signal to the process ID in F/ovdb_monitor.pid> . The parent process will shut down the three children and wait for their exit before exiting itself. =head1 HISTORY Written by Heath Kehoe Ehakehoe@avalon.netE for InterNetNews. $Id: ovdb_monitor.pod 9765 2014-12-07 21:07:34Z iulius $ =head1 SEE ALSO ovdb(5), ovdb_init(8) =cut inn-2.6.0/doc/pod/fastrm.pod0000644000175200017520000001773712575023702015266 0ustar iuliusiulius=head1 NAME fastrm - Quickly remove a list of files =head1 SYNOPSIS B [B<-de>] [B<-c>|B<-c>I] [B<-s>|B<-s>I] [B<-u>|B<-u>I] I =head1 DESCRIPTION B reads a list of either file names or storage API tokens, one per line, from its standard input and removes them. Storage API tokens are removed via the SMcancel() interface. B does not delete files safely or with an eye to security, but rather cuts every corner it can to delete files as fast as it can. It should therefore never be run on publically writable directories, or in any other environment where a hostile party may control the directory structure in which it is working. If a file name is not an absolute path name, it is considered to be relative to I as given on the command line. The I parameter must be a simple absolute pathname (it must not contain multiple consecutive slashes or references to the special directories C<.> or C<..>). B is designed to be faster than the typical C<| xargs rm> pipeline when given a sorted list of file names as input. For example, B will usually chdir(2) into a directory before removing files from it, meaning that if its input is sorted, most names passed to unlink(2) will be simple names. This can substantially reduce the operating system overhead from directory lookups. B assumes that its input is valid and that it is safe to call unlink(2) on every file name it is given. As a safety measure, however, B when running as root will check with stat(2) that a file name doesn't specify a directory before removing it. (In some operating systems, root is allowed to unlink directories, even directories which aren't empty, which can cause file system corruption.) The input to B should always be sorted S<-- or> even better be in the order file names are output by S if speed is an issue and the input isn't solely storage API tokens. (It deals fine with unsorted input, but is unlikely to be any faster in that case than a simple C<| xargs rm> command.) Sorting may even slightly speed up the removal of storage API tokens due to caching effects, since sorting will tend to keep all of the tokens from a particular storage method together. Various additional optimizations for removing files can be turned on and/or tuned with options (see below). Which options will be most effective depends heavily on the underlying structure of the file system, the way in which directories are stored and searched, and similar, often underdocumented, operating system implementation details. The more sophisticated the underlying operating system and file system, the more likely that it will already perform the equivalent of these optimizations internally. =head1 OPTIONS =over 4 =item B<-c>[I] Controls when B calls chdir(2). If the number of files to be unlinked from a given directory is at least I, then B will change to that directory before unlinking those files. Otherwise, it will use either the absolute path names or a path name relative to the current directory (whichever is likely more efficient). The I parameter is optional; if just B<-c> is given, B<-c1> is assumed, which will cause B to always chdir before calling unlink(2). The default is B<-c3>. Use B<-c0> to prevent B from ever using chdir(2). =item B<-d> Don't remove any files. Instead, print a list of the files that would be removed to standard output. Each line contains either the current directory of B at the time it would do the unlink and the relative path name it would pass to unlink(2) as two fields separated by whitespace and a C, the absolute path name (as a single field) that would be passed to unlink(2), or the string C and the storage API token that would be removed. =item B<-e> Treat an empty input file as an error. This is most useful when B is last in a pipeline after a preceding sort(1) command, ensuring that B will fail if the sort fails. =item B<-s>[I] When B<-s> is given and the number of files to remove in a directory is greater than I, rather than remove files in the order given, B will open the directory and read it, unlinking files in the order that they appear in the directory. On systems with a per-process directory cache or that use a linear search to find files in a directory, this should make directory lookups faster. The I parameter is optional; if just B<-s> is given, B<-s5> is assumed. When this option is in effect, B won't attempt to remove files that it doesn't see in the directory, possibly significantly speeding it up if most of the files to be removed have already been deleted. However, using this option requires B to do more internal work and it also assumes that the order of directory listings is stable in the presence of calls to unlink(2) between calls to readdir(3). This may be a dangerous assumption with some sophisticated file systems (and in general this option is only useful with file systems that use unindexed linear searches to find files in directories or when most of the files to be removed have already been deleted). This optimization is off by default. =item B<-u>[I] Specifying this option promises that there are no symbolic links in the directory tree from which files are being removed. This allows B to make an additional optimization to its calls to chdir(2), constructing a relative path using C<../..> and the like to pass to chdir(2) rather than always using absolute paths. Since this reduces the number of directory lookups needed with deeply nested directory structures (such as that typically created by traditional news spool storage), it can be a significant optimization, but it breaks horribly in the presence of symbolic links to directories. When B<-u> is given, B will use at most I levels of C<..> segments to construct paths. I is optional; if just B<-u> is given, B<-u1> is assumed. This optimization is off by default. =back B also accepts B<-a> and B<-r> options, which do nothing at all except allow you to say C, C, or C. These happen to often be convenient sets of options to use. =head1 EXIT STATUS B exits with a status of zero if there were no problems, and an exit status of 1 if something went wrong. Attempting to remove a file that does not exist is not considered a problem. =head1 EXAMPLES B is typically invoked by INN via expirerm(8) using a command like: fastrm -e < expire.list To enable all optimizations and see the affect on the order of removal caused by B<-s>, use: fastrm -d -s -e -u < expire.list If your file system has indexed directory lookups, but you have a deeply nested directory structure, you may want to use a set of flags like: fastrm -e -u3 < expire.list to strongly prefer relative paths but not to use readdir(2) to order the calls to unlink(2). You may want to edit expirerm(8) to change the flags passed to B. =head1 WARNINGS B cuts corners and does not worry about security, so it does not use chdir(2) safely and could be tricked into removing files other than those that were intended if run on a specially constructed file tree or a file tree that is being modified while it is running. It should therefore never be used with world-writable directories or any other directory that might be controlled or modified by an attacker. =head1 NOTES B defers opening the storage subsystem or attempting to parse any INN configuration files until it encounters a token in the list of files to remove. It's therefore possible to use B outside of INN as a general fast file removal program. =head1 HISTORY B was originally written by . This manual page was rewritten in POD by Russ Allbery for InterNetNews. $Id: fastrm.pod 9767 2014-12-07 21:13:43Z iulius $ =head1 SEE ALSO expirerm(8). =cut inn-2.6.0/doc/pod/domain.pod0000644000175200017520000000336012575023702015224 0ustar iuliusiulius=head1 NAME domain - nnrpd domain resolver =head1 SYNOPSIS B B =head1 DESCRIPTION This program can be used in F to grant access based on the subdomain part of the remote hostname. In particular, it only returns success if the remote hostname ends in B. (A leading dot on B is optional; even without it, the argument must match on dot-separated boundaries). The "username" returned is whatever initial part of the remote hostname remains after B is removed. It is an error if there is no initial part (that is, if the remote hostname is I the specified B). =head1 EXAMPLE The following readers.conf(5) fragment grants access to hosts with internal domain names: auth internal { res: "domain .internal" default-domain: "example.com" } access internal { users: "*@example.com" newsgroups: example.* } Access is granted to the example.* groups for all connections from hosts that resolve to hostnames ending in C<.internal>; a connection from "foo.internal" would match access groups as "foo@example.com". =head1 BUGS It seems the code does not confirm that the matching part is actually at the end of the remote hostname (e.g., "domain: example.com" would match the remote host "foo.example.com.org" by ignoring the trailing ".org" part). Does this resolver actually provide any useful functionality not available by using wildcards in the readers.conf(5) I parameter? If so, the example above should reflect this functionality. =head1 HISTORY This documentation was written by Jeffrey S . $Id: domain.pod 8200 2008-11-30 13:31:30Z iulius $ =head1 SEE ALSO nnrpd(8), readers.conf(5) =cut inn-2.6.0/doc/pod/ovdb.pod0000644000175200017520000003406012575023702014710 0ustar iuliusiulius=head1 NAME ovdb - Overview storage method for INN =head1 DESCRIPTION Ovdb is a storage method that uses the S library to store overview data. It requires version 4.4 or later of the S library (4.7+ is recommended because older versions suffer from various issues). Ovdb makes use of the full transaction/logging/locking functionality of the S environment. S may be downloaded from L and is needed to build the ovdb backend. =head1 UPGRADING This is version 2 of ovdb. If you have a database created with a previous version of ovdb (such as the one shipped with S) your database will need to be upgraded using ovdb_init(8). See the man page ovdb_init(8) for upgrade instructions. =head1 INSTALLATION To build ovdb support into INN, specify the option B<--with-bdb> when running the configure script. By default, configure will search for S in default search paths; there will be a message in the configure output indicating the pathname that will be used. You can override this pathname by adding a path to the option, for instance B<--with-bdb=/usr/BerkeleyDB.4.4>. This directory is expected to have subdirectories F and F (F and F are also checked), containing respectively F, and the library itself. In case non-standard paths to the S libraries are used, one or both of the options B<--with-bdb-include> and B<--with-bdb-lib> can be given to configure with a path. The ovdb database may take up more disk space for a given spool than the other overview methods. Plan on needing at least S<1.1 KB> for every article in your spool (not counting crossposts). So, if you have 5 million articles, you'll need at least S<5.5 GB> of disk space for ovdb. With compression enabled, this estimate changes to S<0.7 KB> per article. See the L section below. Plus, you'll need additional space for transaction logs: at least S<100 MB>. By default the transaction logs go in the same directory as the database. To improve performance, they can be placed on a different disk S<-- see> the L section. =head1 CONFIGURATION To enable ovdb, set the I parameter in F to C. The ovdb database is stored in the directory specified by the I parameter in F. This is the "DB_HOME" directory. To start out, this directory should be empty (other than an optional F file; see L for details) and B (or B) will create the files as necessary in that directory. Make sure the directory is owned by the news user. Other parameters for configuring ovdb are in the ovdb.conf(5) configuration file. See also the sample F. =over 4 =item cachesize Size of the memory pool cache, in kilobytes. The cache will have a backing store file in the DB directory which will be at least as big. In general, the bigger the cache, the better. Use C to see cache hit percentages. To make a change of this parameter take effect, shut down and restart INN (be sure to kill all of the nnrpds when shutting down). Default is 8000, which is adequate for small to medium sized servers. Large servers will probably need at least 20000. =item compress If INN was compiled with zlib, and this compress parameter is true, OVDB will compress overview records that are longer than 600 bytes. See the L section below. =item numdbfiles Overview data is split between this many files. Currently, B will keep all of the files open, so don't set this too high or B may run out of file descriptors. B only opens one at a time, regardless. May be set to one, or just a few, but only do that if your OS supports large (>2G) files. Changing this parameter has no effect on an already-established database. Default is 32. =item txn_nosync If txn_nosync is set to false, S flushes the log after every transaction. This minimizes the number of transactions that may be lost in the event of a crash, but results in significantly degraded performance. Default is true. =item useshm If useshm is set to true, S will use shared memory instead of mmap for its environment regions (cache, lock, etc). With some platforms, this may improve performance. Default is false. =item shmkey Sets the shared memory key used by S when 'useshm' is true. S will create several (usually 5) shared memory segments, using sequentially numbered keys starting with 'shmkey'. Choose a key that does not conflict with any existing shared memory segments on your system. Default is 6400. =item pagesize Sets the page size for the DB files (in bytes). Must be a power of 2. Best choices are 4096 or 8192. The default is 8192. Changing this parameter has no effect on an already-established database. =item minkey Sets the minimum number of keys per page. See the S documentation for more info. Default is based on page size and whether compression is enabled: default_minkey = MAX(2, pagesize / 2600) if compress is false default_minkey = MAX(2, pagesize / 1500) if compress is true The lowest allowed minkey is 2. Setting minkey higher than the default is not recommended, as it will cause the databases to have a lot of overflow pages. Changing this parameter has no effect on an already-established database. =item maxlocks Sets the S "lk_max" parameter, which is the maximum number of locks that can exist in the database at the same time. Default is 4000. =item nocompact The nocompact parameter affects expireover's behavior. The expireover function in ovdb can do its job in one of two ways: by simply deleting expired records from the database, or by re-writing the overview records into a different location leaving out the expired records. The first method is faster, but it leaves 'holes' that result in space that can not immediately be reused. The second method 'compacts' the records by rewriting them. If this parameter is set to 0, expireover will compact all newsgroups; if set to 1, expireover will not compact any newsgroups; and if set to a value greater than one, expireover will only compact groups that have less than that number of articles. Experience has shown that compacting has minimal effect (other than making expireover take longer) so the default is now 1. This parameter will probably be removed in the future. =item readserver Normally, each nnrpd process directly accesses the S environment. The process of attaching to the database (and detaching when finished) is fairly expensive, and can result in high loads in situations when there are lots of reader connections of relatively short duration. When the I parameter is true, the nnrpds will access overview via a helper server (B S<-- which> is started by B). This can also result in cleaner shutdowns for the database, improving stability and avoiding deadlocks and corrupted databases. If you are experiencing any instability in ovdb, try setting this parameter to true. Default is false. =item numrsprocs This parameter is only used when I is true. It sets the number of ovdb_server processes. As each ovdb_server can process only one transaction at a time, running more servers can improve reader response times. Default is 5. =item maxrsconn This parameter is only used when I is true. It sets a maximum number of readers that a given ovdb_server process will serve at one time. This means the maximum number of readers for all of the ovdb_server processes is (numrsprocs * maxrsconn). This does B limit the actual number of readers, since nnrpd will fall back to opening the database directly if it can't connect to a readserver. Default is 0, which means an umlimited number of connections is allowed. =back =head1 COMPRESSION New in this version of OVDB is the ability to compress overview data before it is stored into the database. In addition to consuming less disk space, compression keeps the average size of the database keys smaller. This in turn increases the average number of keys per page, which can significantly improve performance and also helps keep the database more compact. This feature requires that INN be built with zlib. Only records larger than 600 bytes get compressed, because that is the point at which compression starts to become significant. If compression is not enabled (either from the C option in F or INN was not built from zlib), the database will be backward compatible with older versions of OVDB. However, if compression is enabled, the database is marked with a newer version that will prevent older versions of OVDB from opening the database. You can upgrade an existing database to use compression simply by setting I to true in F. Note that existing records in the database will remain uncompressed; only new records added after enabling compression will be compressed. If you disable compression on a database that previously had it enabled, new records will be stored uncompressed, but the database will still be incompatible with older versions of OVDB (and will also be incompatible with this version of OVDB if it was not built with zlib). So to downgrade to a completely uncompressed database you will have to rebuild the database using makehistory. =head1 DB_CONFIG A file called F may be placed in the database directory to customize where the various database files and transaction logs are written. By default, all of the files are written in the "DB_HOME" directory. One way to improve performance is to put the transaction logs on a different disk. To do this, put: DB_LOG_DIR /path/to/logs in the F file. If the pathname you give starts with a /, it is treated as an absolute path; otherwise, it is relative to the "DB_HOME" directory. Make sure that any directories you specify exist and have proper ownership/mode before starting INN, because they won't be created automatically. Also, don't change the DB_CONFIG file while anything that uses ovdb is running. Another thing that you can do with this file is to split the overview database across multiple disks. In the F file, you can list directories that S will search when it goes to open a database. For example, let's say that you have I set to F and you have four additional file systems created on F. You would create a file "/mnt/overview/DB_CONFIG" containing the following lines: set_data_dir /mnt/overview set_data_dir /mnt/ov1 set_data_dir /mnt/ov2 set_data_dir /mnt/ov3 set_data_dir /mnt/ov4 Distribute your ovNNNNN files into the four filesystems. (say, 8 each). When called upon to open a database file, the db library will look for it in each of the specified directories (in order). If said file is not found, one will be created in the first of those directories. Whenever you change DB_CONFIG or move database files around, make sure all news processes that use the database are shut down first (including nnrpds). The DB_CONFIG functionality is part of S itself, rather than something provided by ovdb. See the S documentation for complete details for the version of S that you're running. =head1 RUNNING When starting the news system, B will invoke B. B must be run before using the database. It performs the following tasks: =over 4 =item * Creates the database environment, if necessary. =item * If the database is idle, it performs a normal recovery. The recovery will remove stale locks, recreate the memory pool cache, and repair any damage caused by a system crash or improper shutdown. =item * Starts the DB housekeeping processes (B) if they're not already running. =back And when stopping INN, B kills the ovdb_monitor processes after the other INN processes have been shut down. =head1 DIAGNOSTICS Problems relating to ovdb are logged to news.err with "OVDB" in the error message. INN programs that use overview will fail to start up if the ovdb_monitor processes aren't running. Be sure to run B before running anything that accesses overview. Also, INN programs that use overview will fail to start up if the user running them is not the "news" user. If a program accessing the database crashes, or otherwise exits uncleanly, it might leave a stale lock in the database. This lock could cause other processes to deadlock on that stale lock. To fix this, shut down all news processes (using C if necessary) and then restart. B should perform a recovery operation which will remove the locks and repair damage caused by killing the deadlocked processes. =head1 FILES =over 4 =item inn.conf The I and I parameters are relevant to ovdb. =item ovdb.conf Optional configuration file for tuning. See L above. =item I Directory where the database goes. S calls it the 'DB_HOME' directory. =item I/DB_CONFIG Optional file to configure the layout of the database files. =item I/ovdb.sem A file that gets locked by every process that is accessing the database. This is used by B to determine whether the database is active or quiescent. =item I/ovdb_monitor.pid Contains the process ID of B. =back =head1 TO DO Implement a way to limit how many databases can be open at once (to reduce file descriptor usage); maybe using something similar to the cache code in ov3.c =head1 HISTORY Written by Heath Kehoe for InterNetNews $Id: ovdb.pod 9593 2013-12-27 21:16:09Z iulius $ =head1 SEE ALSO inn.conf(5), innd(8), nnrpd(8), ovdb_init(8), ovdb_monitor(8), ovdb_stat(8) S documentation: in the F directory of the S source distribution, or on the Oracle S web page (L). =cut inn-2.6.0/doc/pod/moderators.pod0000644000175200017520000000665712575023702016150 0ustar iuliusiulius=head1 NAME moderators - Submission addresses for moderated groups =head1 DESCRIPTION When an unapproved article is posted locally to a moderated newsgroup, it is not passed off to B for normal handling; instead, it is sent via e-mail to the submission address for that newsgroup. The submission address is determined using this configuration file. The file I/moderators is a list of associations between uwildmat(3) patterns matching newsgroups and the submission address for those newsgroups. Blank lines and lines starting with a number sign (C<#>) are ignored. All other lines should consist of two fields separated by a colon: :
The first field is a uwildmat(3) pattern matching the group or groups to which this line applies. The first matching line is used, so more specific patterns should be listed before general patterns. The second field, the submission address, should be a simple e-mail address with one exception: at most one C<%s> may occur anywhere in the address. If present, it will be replaced by the name of the newsgroup, with all periods in the name changed to dashes (C<->). If there is a literal C<%> in the submission address, it must be written as C<%%>, even if not followed by an C. With the C<%s> syntax, periods are converted to dashes for historical reasons, from back in the days when periods in the local part of addresses were not always handled correctly. It's probably no longer necessary, but so much now depends on it that it can't be easily changed. It's intended that the sample F file included in the INN distribution always be sufficient for all world-wide newsgroups. The hosts behind moderators.isc.org have graciously volunteered to handle forwarding tasks for all world-wide newsgroups so that individual sites don't have to keep track of the submission addresses for moderated groups. The forwarding database used by moderators.isc.org is coordinated by ; if you know of a world-wide newsgroup hierarchy that is not correctly handled by moderators.isc.org, please send the details to that address. Given that, the only thing you should have to add to the sample file under normal circumstances are the forwarding addresses for local or limited-distribution moderated groups. If this file doesn't exist, or if a post is made to a moderated group that has no matching entry in this file, B falls back on the value of I set in F and, failing that, rejects the post. =head1 EXAMPLES Here is a sample file: example.important:announce@example.com example.*:%s@smtp.example.com *:%s@moderators.isc.org Using the above file, postings to the moderated newsgroup in the left column below will be sent to the address shown in the right column below: example.important announce@example.com example.x-announce example-x-announce@smtp.example.com alt.dev.null alt-dev-null@moderators.isc.org Note that periods are changed to dashes and dashes are left alone with the C<%s> syntax, so two moderated newsgroups whose names differ only by changing a period to a dash would go to the same address. Such newsgroup pairs should therefore not be created. =head1 HISTORY Written by Rich $alz for InterNetNews. Rewritten in POD by Russ Allbery . $Id: moderators.pod 9767 2014-12-07 21:13:43Z iulius $ =head1 SEE ALSO inn.conf(5), nnrpd(8), uwildmat(3). =cut inn-2.6.0/doc/pod/ctlinnd.pod0000644000175200017520000005037212575023702015415 0ustar iuliusiulius=head1 NAME ctlinnd - Control the main InterNetNews daemon =head1 SYNOPSIS B [B<-hs>] [B<-t> I] [I [I ...]] =head1 DESCRIPTION B sends a message to the control channel of innd(8), the main InterNetNews daemon. In the normal mode of behavior, the message is sent to the server, which then performs the requested action and sends back a reply with a text message and an exit code for B. If the server successfully performed the command, B will print the reply on standard output and exit with a status of zero. If the server could not perform the command, it will direct B to exit with a status of one. By default, B will wait forever for a response from B; this can be changed with the B<-t> flag. The C, C, and C commands do not generate a reply, since they cause B to exit. After these commands, B will always exit silently with a status of zero. =head1 OPTIONS =over 4 =item B<-h> Prints a command summary. If a command is given along with the B<-h> flag, only the usage for that command will be given. =item B<-s> If the command was successful, don't print the output from B. =item B<-t> I Specifies how long to wait for the reply from the server, for commands other than C, C, and C. I is the number of seconds to wait. A value of zero says to wait forever, and a value less than zero says not to wait at all but just exit immediately with status zero. When waiting for a reply, B will check every two minutes to be sure the server is still running, to make it less likely that B will just hang. The default is zero, indicating that B should wait forever. =back =head1 COMMANDS Here is the complete list of supported commands. Note that nearly all commands have a fixed number of arguments. If a parameter may be an empty string, it is still necessary to pass the empty string to B as an argument (specified in the shell as two adjacent quotes, like C<''>). =over 4 =item addhist I I I I I Add an entry to the history database for I. The angle brackets around the message-ID are optional. It should normally be protected from the shell with single quotes. I, I, and I should be the number of seconds since epoch and indicate when the article arrived, when it expires (via the Expires: header), and when it was posted (given in the Date: header), respectively. I should be C<0> if the article doesn't have an Expires: header. I is the storage API token for the article, and may be empty. This command can only be used while the server is running, and will be rejected if the server is paused or throttled. =item allow I Allow remote connections, reversing a prior C command. I must be the same text given to the C command, or the empty string (which matches any reason). =item begin I Begin feeding I. The server will rescan the F file to find the specified site and set up a newsfeed for it. If the site already existed, a C for that site is done first. This command is forwarded; see L below. =item cancel I Remove the article with the specified message-ID from the local system. This does not generate a cancel control message; it only affects the local system. The angle brackets around the message-ID are optional. It should normally be protected from the shell with single quotes (and not double quotes). For instance: ctlinnd cancel 'test@foo.bar' Note that the history database is updated with the specified message-ID so if an article with the same message-ID is afterwards received, it will be rejected; it is useful for rejecting spam before it arrives. If the server is throttled manually, this command causes it to briefly open the history database. If the server is paused or throttled for any other reason, this command is rejected. =item changegroup I I The newsgroup I is changed so that its status (the fourth field in the F file) becomes I. This may be used to make an existing group moderated or unmoderated, for example. This command, unlike C or C, can only be used while the server is running, and will be rejected if the server is paused or throttled. =item checkfile Check the syntax of the F file and display a message if any errors are found. The details of the errors are reported to syslog. =item drop I Flush and drop I from the server's list of active feeds. This command is forwarded; see L below. =item feedinfo I Print detailed information about the state of the feed to I, or brief status about all feeds if I is the empty string. =item flush I Flush the buffer for the specified site. The action taken depends on the type of feed the site receives; see newsfeeds(5) for more information. This is useful when the site is being fed by a file and batching is about to start, or to cleanly shut down and respawn a channel feed. If I is an empty string, all sites are flushed and the F file and history database are also flushed to disk. This command is forwarded; see L below. Flushing the B channel feed is the recommended method of restarting B to pick up new configuration. B will spawn a new B process while the old process shuts down cleanly. =item flushlogs Close the news and error log files and rename them to add C<.old> to the file name, then open fresh news and error logs. The F file and history database are also flushed to disk. Exploder and process channels are reopened so that they properly take into account the new log files. =item go I Re-open the history database and start accepting articles again, reversing a previous C or C command. I must be either the empty string or match the text that was given to the earlier C or C command. If a C command is in effect, this will also reverse it by doing the equivalent of an C command if the reason matches I. Likewise, if a C command had been given, this will clear the reservation if I matches the text that was given to C. The history database is automatically closed on C or C and reopened on C, so the history database can be replaced during the pause or throttle without requiring an explicit C command. If any other configuration files or the F file were modified, a C command should be given to force the server to re-read those files. If the server throttled itself because it accumulated too many I/O errors, this command will reset the error count. If B was not started with the B<-n y> flag, this command also does the equivalent of a C command with C as the flag and I as the text. =item hangup I Close the socket for the specified incoming channel. This may be useful when an incoming connection appears to be hung (although B will close idle connections automatically after a timeout). =item help [I] Print a command summary for all commands, or just I if one is specified. This is equivalent to the B<-h> option. =item kill I I Signal I is sent to the process underlying the specified site, which must be a channel or exploder feed. I may be a numeric signal number or one of C, C, or C; case is not significant. =item logmode Cause the server to log its current operating mode to syslog. =item lowmark I Reset the low water marks in the F file based on the contents of I. Each line in I must be of the form: group low-value For example: comp.lang.c++ 243 This command is mostly used by B to update the F file after nightly expiration. =item mode Print the server's operating mode as a multi-line summary of the parameters and the operating state. The parameters in the output correspond to command-line flags to B and give the current settings of those parameters that can be overridden by command-line flags. =item name I Print the name and relevant information for the given incoming or outgoing channel, or for all channels if I is an empty string. The response is formatted as: :::: where is the name of the channel, is its number (generally the same as the file descriptor assigned to it), is the idle time for an NNTP channel or the process ID for a process channel, and is the status for NNTP channels. The type is one of the following values: control Control channel for ctlinnd file An outgoing file feed localconn Local channel used by nnrpd and rnews for posting nntp NNTP channel for remote connections proc The process for a process feed remconn The channel that accepts new remote connections Channel status indicates whether the channel is paused or not. Nothing is shown unless the channel is paused, in which case C is shown. A channel will be paused automatically if the number of remote connections for that label in F is greater than I within I seconds. =item newgroup I [I [I]] Create the specified newsgroup. The I parameter is the fourth field of the F file entry, as described in active(5). If it is not an equal sign, only the first character is used. I should be the identity of the person creating the group. If the newsgroup already exists, this is equivalent to the C command. I, encoded in UTF-8 if given, may be omitted; if so, it will default to the newsmaster (as specified at configure time, normally C). I may also be omitted; if so, it will default to C (a normal, unmoderated group). The combination of defaults make it possible to use the text of the Control: header directly (although don't do this without checking the syntactic validity of the header first). This command can only be done while the server is running or throttled manually. It will update its internal state when a C command is sent. This command updates the F file as well. This command is forwarded; see L below. =item param I I Change the specified server parameter. I is the B command-line option to set and I is the new value. For example: ctlinnd param i 5 would direct the server to allow only five incoming connections. To enable or disable the action of the B<-n> flag, use C for the letter and C or C, respectively, for the value. The supported values for I are C, C, C, C, C, C, C, C, C, and C. =item pause I Pause the server so that no incoming articles are accepted. No existing connections are closed, but the history database is closed. This should be used for short-term locks, such as when replacing the history database. If the server was not started with the B<-n y> flag, this command also does the equivalent of a C command with C as the flag and I as the text, encoded in UTF-8. =item perl I Enable or disable Perl filtering. This command is only available if INN was built with Perl filtering support. If I starts with C, filtering is enabled; if it starts with C, filtering is disabled. When filtering is disabled, if the F Perl filter defined a function C, it will be called prior to the deactivation of the filter. =item python I Enable or disable Python filtering. This command is only available if INN was built with Python filtering support. If I starts with C, filtering is enabled; if it starts with C, filtering is disabled. =item readers I I Allow or disallow readers. If I starts with the letter C, then reading is disallowed by causing the server to pass I as the value of the B<-r> flag to B. If I starts with the letter C and I is either an empty string or the same string, encoded in UTF-8, that was used when reading was disabled, reading will be re-enabled. This command has no effect if B is being run separately rather than spawned by B. =item reject I Remote connections (those that would not be handed off to B) are rejected with I given as the explanation, encoded in UTF-8. Existing connections are not closed. =item reload I I Update the in-memory copy of server configuration files. I identifies what should be reloaded, and I is reported to syslog in the message noting the reload. There is no way to reload F, F, or other configuration files for the storage or overview backends. To pick up changes to those files, use C to restart B. If I is the empty string or the word C, everything is reloaded. If it is the word C, the history database is closed and re-opened. If it is the word C, the corresponding file is reloaded. If it is the word C or C, both the F and F files are reloaded, which will also cause all outgoing feeds to be flushed and restarted. If I is the word C, the F file is reloaded. If the Perl filter defined a function C, it will be called prior to re-reading F. If the Perl function C is defined, it will be called after F has been reloaded. Reloading the Perl filter does not enable filtering if it has been disabled; use C to do this instead. F cannot be reloaded. This file is not available for reloading unless INN was compiled with Perl filtering support. If I is the word C, the F file is reloaded. If a Python method named C exists, it will be called prior to re-reading F. If a Python method named C<__init__> exists, it will be called after F has been reloaded. Reloading the Python filter does not enable filtering if it has been disabled; use C to do this. This file is not available for reloading unless INN was compiled with Python filtering support. =item renumber I Update the low water and high water marks for I in the F file based on the information in the overview database. Regardless of the contents of the overview database, the high water mark will not be decreased. (Decreasing it may cause duplicate article numbers to be assigned after a crash, which can cause serious problems with the tradspool storage method.) If I is the empty string, all newsgroups are renumbered. Renumber only works if overview data has been created (if I is set to true in F). =item renumberlow I Identical to the C command. =item reserve I Require the next C or C command to use I as its reason, encoded in UTF-8. This reservation is cleared by giving an empty string for the reason. This is used by programs like B to coordinate pauses and throttles of the server and avoid trampling on other instances of themselves. =item rmgroup I Remove the specified newsgroup. The group is removed from the F file and its overview information is purged, making it immediately unavailable to readers. Unlike the C command, this command does not update the F file. This command can only be done while the server is running or throttled manually. This command is forwarded; see L below. =item send I I The specified I is sent as a control line to the exploder I. =item shutdown I The server is shut down, with the specified reason recorded in the log and sent to all open connections. It is a good idea to send a C command first so that feeds can be shut down more gracefully. If Perl or Python filtering is compiled in and enabled, certain functions are called at C or C (to save filter state to disk, for example). Consult the embedded filter documentation for details. =item stathist (off | I) Enable or disable generation of history performance statistics. If the parameter is C, no statistics are gathered. Otherwise, statistics are written to the specified file. A parser for this file is provided in the contrib tree of the INN distribution. =item status (off | I) Adjust the frequency with which B reports status information to syslog. Status reporting is turned off if C or C<0> is given as the argument. Otherwise, status will be reported every I seconds. See I in inn.conf(5) for information on how to set the default. =item throttle I Close all existing incoming connections and outgoing feeds and reject new connections. Close the history database. This should be used for long-term locks or for running a large number of C and C commands without restarting all outgoing feeds between each one. (Note that changing the status of existing newsgroups when the server is throttled cannot be done.) If the server was not started with the B<-n y> flag, then this command also does the equivalent of a C command with C as the flag and I as the text, encoded in UTF-8. =item timer (off | I) Adjust the frequency with which B reports performance information to syslog. Performance monitoring is turned off if C or C<0> is given as the argument. Otherwise, statistics will be reported every I seconds to syslog. See I in inn.conf(5) for information on how to set the default. =item trace I I Turn tracing on or off for the specified I. I should start with the letter C or C to turn tracing on or off, respectively. If I starts with a number, tracing is set up for the specified B channel, which must be an incoming NNTP feed. If it starts with the letter C, general B tracing is turned on or off. If it starts with the letter C, future B processes spawned by C will or will not be passed the B<-t> flag, as appropriate. This will not affect any B processes already running, or B processes started by some means other than B. =item xabort I Log the specified I and then abort. On most systems, this will cause B to dump a core file. This is only useful for debugging. =item xexec I Shut down the server, but then rather than exiting, exec B with all of its original arguments except for B<-r>. I may be either C or an empty string, both of which are equivalent. Any other value is an error. This is the easiest way to start a new copy of B after upgrading or reload configuration files that can't be reloaded via the C command. =back =head1 NOTES In addition to being acted on by the server, certain commands can be forwarded to an appropriate child process. If the site receiving the command is an exploder (such as B) or a funnel that feeds into an exploder, the command can be forwarded. In this case, the server will send a command line to the exploder that consists of the B command name. If the site funnels into an exploder that has an asterisk (C<*>) in its C flag (see newsfeeds(5) for more information on feed specifications), the site name will be appended to the command; otherwise, no argument is appended. =head1 BUGS B uses Unix domain sockets on most systems to communicate with B and is therefore limited by whatever maximum packet size the operating system imposes on Unix domain datagrams. This may mean that server replies are limited to S<4 KB> on some systems. =head1 HISTORY Written by Rich $alz for InterNetNews. Rewritten in POD by Russ Allbery . $Id: ctlinnd.pod 9767 2014-12-07 21:13:43Z iulius $ =head1 SEE ALSO active(5), active.times(5), buffchan(8), incoming.conf(5), innd(8), inndcomm(3), inn.conf(5), newsfeeds(5), nnrpd(8). =cut inn-2.6.0/doc/pod/qio.pod0000644000175200017520000000757412575023702014560 0ustar iuliusiulius=head1 NAME qio - Quick I/O routines for reading files =head1 SYNOPSIS #include QIOSTATE *QIOopen(const char *name); QIOSTATE *QIOfdopen(int> I and B is the analog to stdio's FILE structure and should be treated as a black box by all users of these routines. Only the above API should be used. B opens the given file for reading. For regular files, if your system provides that information and the size is reasonable, QIO will use the block size of the underlying file system as its buffer size; otherwise, it will default to a buffer of S<8 KB>. Returns a pointer to use for subsequent calls, or NULL on error. B performs the same operation except on an already-open file descriptor (I must designate a file open for reading). B closes the open file and releases any resources used by the QIOSTATE structure. The QIOSTATE pointer should not be used again after it has been passed to this function. B reads the next newline-terminated line in the file and returns a pointer to it, with the trailing newline replaced by nul. The returned pointer is a pointer into a buffer in the QIOSTATE object and therefore will remain valid until B is called on that object. If EOF is reached, an error occurs, or if the line is longer than the buffer size, NULL is returned instead. To distinguish between the error cases, use B and B. B returns the descriptor of the open file. B returns the length in bytes of the last line returned by B. Its return value is only defined after a successful call to B. B sets the read pointer back to the beginning of the file and reads the first block of the file in anticipation of future reads. It returns 0 if successful and -1 on error. B returns the current value of the read pointer (the lseek(2) offset at which the next line will start). B returns true if there was an error in the last call to B, false otherwise. B returns true if there was an error and the error was that the line was too long. If B returns NULL, these functions should be called to determine what happened. If B returned NULL and B is false, EOF was reached. Note that if B returns true, the next call to B will try to read the remainder of the line and will likely return a partial line; users of this library should in general treat long lines as fatal errors. =head1 EXAMPLES This block of code opens F and reads it a line at a time, printing out each line preceded by its offset in the file. QIOSTATE *qp; off_t offset; char *p; qp = QIOopen("/etc/motd"); if (qp == NULL) { perror("Open error"); exit(1); } for (p = QIOread(qp); p != NULL; p = QIOread(qp)) printf("%ld: %s\n", (unsigned long) QIOtell(qp), p); if (QIOerror(qp)) { perror("Read error"); exit(1); } QIOclose(qp); =head1 HISTORY Written by Rich $alz for InterNetNews. Updated by Russ Allbery . $Id: qio.pod 9767 2014-12-07 21:13:43Z iulius $ =cut inn-2.6.0/doc/pod/innupgrade.pod0000644000175200017520000000673712575023702016124 0ustar iuliusiulius=head1 NAME innupgrade - Upgrade INN configuration files =head1 SYNOPSIS B I B [B<-t> I] B<-f> I =head1 DESCRIPTION B is intended to be run during a major upgrade of INN to fix the configuration files with any required changes. If given a directory, it will scan that directory for any files that it has updates defined for, try to perform those updates, and replace the files with updated versions if applying the updates resulted in any changes. The old versions of the files will be saved with a C<.OLD> extension. If the B<-f> flag is used, only that file will be updated. If the file name doesn't match the standard file name of an INN configuration file, the optional B<-t> flag may be given to specify the type. See L<"EXAMPLES"> for an example of this. Currently, B knows how to apply the following updates: =over 2 =item F =over 2 =item * Quote values with whitespace and comment out keys with no values, required for the change in configuration parsers introduced in S. The new format is not backward compatible with the previous parser, since the previous parser will include the double-quotes in the value of the parameter. =item * Add the I parameter if not found (introduced in S, with the default value C). Rename I to I (since S). Rename I and I to respectively I and I (since S). =item * If the F file exists, its content is merged in the I and I parameters introduced in S. The file is then renamed to F. =item * If the F file exists, its content is merged in the I, I, I and I parameters introduced in S. The file is then renamed to F. =back =item F =over 2 =item * Replace the use of B with the appropriate direct invocation of B or B. =back =back A few obsolete programs or configuration files are renamed with a C<.OLD> extension by B. Obsolete man pages are directly removed. Normally, B should be run at least on the I directory after any upgrade of INN other than a patch release (any upgrade that changes the first or second version numbers). This may occur automatically during the upgrade process. =head1 OPTIONS =over 4 =item B<-f> I Only act on I rather than working on an entire directory. =item B<-t> I For a file specified with B<-f>, parse it and upgrade it as if it were named I. Used for upgrading files with the same syntax as normal INN configuration files but with different names. Only makes sense in combination with B<-f>. =back =head1 EXAMPLES Upgrade any configuration files found in I and append a C<.OLD> extension to obsolete files in I: innupgrade Upgrade only F: innupgrade -f /news/etc/inn.conf Upgrade a file named F that should have the same syntax as F: innupgrade -t inn.conf -f inn-special.conf Any upgrade rules that apply to F will be applied to the alternate file. =head1 HISTORY Written by Russ Allbery for InterNetNews. $Id: innupgrade.pod 9767 2014-12-07 21:13:43Z iulius $ =cut inn-2.6.0/doc/pod/libstorage.pod0000644000175200017520000003107012575023702016107 0ustar iuliusiulius=head1 NAME libstorage - routines for managing INN storage =head1 SYNOPSIS #include typedef enum { RETR_ALL, RETR_HEAD, RETR_BODY, RETR_STAT } RETRTYPE; typedef enum { SM_RDWR, SM_PREOPEN } SMSETUP; typedef unsigned char STORAGECLASS; typedef unsigned char STORAGETYPE; typedef struct token { STORAGETYPE type; STORAGECLASS class; char token[STORAGE_TOKEN_LENGTH]; } TOKEN; typedef struct { unsigned char type; const char *data; struct iovec *iov; int iovcnt; size_t len; unsigned char nextmethod; void *private; time_t arrived; time_t expires; char *groups; int groupslen; TOKEN *token; } ARTHANDLE; typedef enum { SELFEXPIRE, SMARTNGNUM, EXPENSIVESTAT } PROBETYPE; typedef enum { SM_ALL, SM_HEAD, SM_CANCELLEDART } FLUSHTYPE; struct artngnum { char *groupname; ARTNUM artnum; }; bool IsToken(const char *text); char *TokenToText(const TOKEN token); TOKEN TextToToken(const char *text); bool SMsetup(SMSETUP type, void *value); bool SMinit(void); TOKEN SMstore(const ARTHANDLE article); ARTHANDLE *SMretrieve(const TOKEN token, const RETRTYPE amount); ARTHANDLE *SMnext(const ARTHANDLE *article, const RETRTYPE amount); void SMfreearticle(ARTHANDLE *article); bool SMcancel(TOKEN token); bool SMprobe(PROBETYPE type, TOKEN *token, void *value); void SMprintfiles(FILE *file, TOKEN token, char **xref, int ngroups); bool SMflushcacheddata(FLUSHTYPE type); char *SMexplaintoken(const TOKEN token); void SMshutdown(void); int SMerrno; char *SMerrorstr; #include #define OV_NOSPACE ... typedef enum { OVSPACE, OVSORT, OVCUTOFFLOW, OVGROUPBASEDEXPIRE, OVSTATICSEARCH, OVSTATALL, OVCACHEKEEP, OVCACHEFREE } OVCTLTYPE; typedef enum { OVNEWSGROUP, OVARRIVED, OVNOSORT } OVSORTTYPE; typedef enum { OVADDCOMPLETED, OVADDFAILED, OVADDGROUPNOMATCH } OVADDRESULT; bool OVopen(int mode); bool OVctl(OVCTLTYPE type, void *val); bool OVgroupstats(char *group, int *lo, int *hi, int *count, int *flag); bool OVgroupadd(char *group, ARTNUM lo, ARTNUM hi, char *flag); bool OVgroupdel(char *group); OVADDRESULT OVadd(TOKEN token, char *data, int len, time_t arrived, time_t expires); bool OVcancel(TOKEN token); void *OVopensearch(char *group, int low, int high); bool OVsearch(void *handle, ARTNUM *artnum, char **data, int *len, TOKEN *token, time_t *arrived); void OVclosesearch(void *handle); bool OVgetartinfo(char *group, ARTNUM artnum, TOKEN *token); bool OVexpiregroup(char *group, int *lo, struct history *h); typedef struct _OVGE { bool delayrm; bool usepost; bool quiet; bool keep; bool earliest; bool ignoreselfexpire; char *filename; time_t now; float timewarp; } OVGE; void OVclose(void); =head1 DESCRIPTION B is a library of common utility (the storage manager) routines for accessing Usenet articles and related data independent of particular storage method; this is known as the storage API. The storage manager's function is to isolate the applications from the individual methods and make the policy decisions as to which storage method should be called to store an article. One of the core concepts in the storage API is that articles are not manipulated using the message-ID or article number, but rather a token that uniquely identifies the article to the method that stored it. This may seem to be redundant since the message-ID already is a unique identifier for the article; however, since the storage method generates the token, it can encode all the information it needs to locate the article in the token. B is a common utility routines for accessing newsgroups and overview data independent of particular overview method. The B function is to isolate the applications from the individual methods. All articles passed through the storage API are assumed to be in wire format. Wire format means C<\r\n> at the end of lines, dot-stuffed lines (that is to say C<.> at the beginning of lines which already start with C<.>), and C<.\r\n> at the end of article on NNTP stream are not stripped. This has a performance win when transferring articles. Note that for the tradspool method, wire format can be disabled. This is just for compatibility which is needed by some old tools written for traditional spool. The B function checks to see if the text is formatted as a text token string. It returns true if formatted correctly or returns false if not. The B function converts a token into a text string for output. The B function converts a text string into a token. The B function configures some parameters for use by the storage manager. I is one of following: =over 4 =item C Allow read/write open for storage files (default is false). =item C Open all storage files at startup time and keep them (default is false). =back I is the pointer which tells each type's value. It returns true if setup is successful, or false if not. The B function calls the setup function for all of the configured methods based on B. This function should be called prior to all other storage API functions which begin with C except B. It returns true if initialization is successful or returns false if not. B returns true, unless all storage methods fail initialization. The B function stores an article specified with I
. The headers and body of the article are supplied to B using the I and I members of B. (I and I are ignored by B.) If I is specified, B uses its value as article's arrival time; otherwise B uses the current time for it. B returns the token type or returns B if the article is not stored because some error occurs or simply does not match any uwildmat(3) expression in F. B fails if B has not been set to true with B. The B function retrieves an article specified with I. I is the one of following which specifies retrieving type: =over 4 =item C Retrieve the whole article. =item C Retrieve the headers of the article. =item C Retrieve the body of the article. =item C Just check to see if the article exists. =back B provides the article data via the I and I members of B. (I is not set by B.) The data area indicated by B should not be modified. The B function is similar in function to B except that it is intended for traversing the method's article store sequentially. To start a query, B should be called with a NULL pointer B. Then B returns B which should be used for the next query. If a NULL pointer B is returned, no articles are left to be queried. If I of B is NULL pointer or I of B is C<0>, it indicates the article may be corrupted and should be cancelled by I. The data area indicated by B should not be modified. The B function frees all allocated memory used by B and B. If B will be called with previously returned B, B should not be called as B frees allocated memory internally. The B function removes the article specified with I. It returns true if cancellation is successful or returns false if not. B fails if B has not been set to true with B. The B function checks the token on B. I is one of following: =over 4 =item C Check to see if the method of the token has self expire functionality. =item C Get the newsgroup name and the article number of the token. =item C Check to see whether checking the existence of an article is expensive or not. =back The B function shows file name or token usable by fastrm(8). The B function flushes cached data on each storage method. I is one of following: =over 4 =item C Flush cached header. =item C Flush the articles which should be cancelled. =item C Flush all cached data. =back The B function returns a human-readable text string with a clear, decoded form of the storage API token. The B function calls the shutdown for each configured storage method and then frees any resources it has allocated for itself. B and B indicate the reason of the last error concerning storage manager. B calls the setup function for configured method which is specified as I in F. I is constructed from following: =over 4 =item C Allow read open for the overview method. =item C Allow write open for the overview method. =back This function should be called prior to all other OV functions which begin with C. The B function probes or sets some parameters for the overview method. I is one of following: =over 4 =item C Setup how group-based expiry is done. =item C Do not add overview data if the data is under the lowest article. =item C Probe which key is suitable for the overview method. =item C Probe the overview space usage. =item C Stat all the articles when B is called. =item C Setup if results of B are stored in a static buffer and must be copied before the next call to B. =item C Setup whether the cache should be kept. =item C Free the cache. =back The B function retrieves the specified newsgroup information from the overview method. The B function informs the overview method that the specified newsgroup is being added. The B function informs the overview method that the specified newsgroup is being removed. The B function stores an overview data. The B function requests the overview method delete overview data specified with token. The B function requests the overview method prepare overview data retrieval. The request range is determined by I and I. The setting of B determines how search result data must be handled. (Note that with some storage methods, each call to B may cause internal storage to be remapped. Therefore, multiple simultaneous searches may require data to be copied in between B calls even if B is false.) The B function retrieves information, article number, overview data, or arrival time. It should be called with NULL handle when it is the first time; subsequent B calls should use the handle returned by the previous call to B. B returns true, unless it reaches I, which is specified by B. Retrieved overview data are sorted by article number, and I is C<0> if there is no overview data for the article. Note that the retrieved data is not necessarily null-terminated; you should only rely on I octets of overview data being present. The B function frees all resources which have been allocated by B. The B function retrieves the overview data and the token specified with I. The B function expires the overview data for the newsgroup. It checks the existence of the article and purges the overview data if the article no longer exists. If I in F is true, B also expires articles. The B function frees all resources which are used by the overview method. =head1 HISTORY Written by Katsuhiro Kondou for InterNetNews. Converted to POD by Julien Elie. $Id: libstorage.pod 9073 2010-05-31 19:00:23Z iulius $ =head1 SEE ALSO expire(8), fastrm(8), inn.conf(5), storage.conf(5). =cut inn-2.6.0/doc/pod/hacking.pod0000644000175200017520000011230512575023702015361 0ustar iuliusiulius=head1 Hacking INN This file is for people who are interested in making modifications to INN. Normal users can safely skip reading it. It is intended primarily as a guide, resource, and accumulation of tips for maintainers and contributors, and secondarily as documentation of some of INN's internals. First of all, if you plan on working on INN source, please start from the current development tree. There may be significant changes from the previous full release, so starting from development sources will make it considerably easier to integrate your work. You can check out the current development tree using Subversion from: or view it with Trac source browser at: You will need S or later to use the development tree. After checking out the tree, run C<./autogen> to generate the necessary autoconf files. Nightly snapshots can be found at: =head1 Configuring and Portability All INN code should be written expecting S and POSIX. There is no need to attempt to support pre-ANSI compilers, and ANSI-only features such as F<< >>, string concatenation, C<#elif>, and token pasting may be used freely. So far as possible, INN is written to attempt to be portable to any system new enough that someone is likely to want to run a news server on it, but whenever possible this portability should be provided by checking for standard behavior in F and supplying replacements for standard functions that are missing. When there is a conflict between S and C99, INN code should be written expecting C99 and B used to patch up the differences. Try to avoid using C<#ifdef> and the like in the middle of code as much as possible. Instead, try to isolate the necessary portability bits and include them in B or at least in conditional macros separate from the code. Trying to read code littered with conditional compilation directives is much more difficult. The shell script F at the top level of the source tree is generated by B from F and the additional macros in the F directory, and F is generated by B from F. At configure time, F generates F and several other files based on options it was given and what it discovers about the target system. All modifications to F should instead be made to F. The B manual (available using C if you have B and the GNU B utilities installed on your system) is a valuable reference when making any modifications. To regenerate F, just run C. To regenerate F, run C. Alternately, to regenerate both, you can run C<./autogen>. Please don't include patches to either F or F when sending patches to INN; instead, note in your patch that those files must be regenerated. These (and all other) generated files should not be checked into Subversion. At the time of this writing, S or later is required. The supporting files for B are in the F subdirectory, including the files F and F to determine the system name and F for libtool support. The latter file comes from the Debian package of Libtool, available from L (F is in the F subdirectory created after building the package with for instance B); the canonical version of the former two are available from L (which currently redirects to L). In addition, F and a few others m4 files are just a copy of the corresponding files in the F subdirectory of the Debian package of the libtool distribution. (Using libtool without using B requires a few odd hacks.) New versions should be checked in periodically when available. There are no INN-specific modifications to those files except for F which recognizes the additional B<-B> flag that INN's B script uses. This script should also be updated at the same time from L; it similarly contains local modifications in order to support the additional B<-B> flag, as well as a few other changes mentioned in a comment at the beginning of the file. =head1 Autobuilds Automatic build logs on several platforms are available at L. =head1 Documentation INN's documentation is currently somewhat in a state of flux. Much of the documentation is still in the form of man pages written directly in nroff. Some parts of the documentation have been rewritten in POD; that documentation can be found in F. The canonical source for most of the text documentation, including F, F, F, and this file, is also POD. If you're modifying some part of INN's documentation and see that it has a POD version in F, you should make the modifications to the POD source and then regenerate the derived files. For a quick introduction to POD, see the perlpod(1) man page on your system (it should be installed if you have Perl installed). When writing new documentation, write in whatever format you care to; if necessary, we can always convert it to POD or whatever else we want to use. Having the documentation exist in I form is more important than what language you write it in. If you don't have a preference, however, we prefer new documentation to be in POD. If you use POD or regenerate POD documentation, please install something close to the latest versions of the POD processing utilities to avoid changes to the documentation depending on who generated it last. You can find the latest version on CPAN (ftp.perl.org or another mirror) in F. You'll need PodParser (for versions of Perl before 5.6.1; 5.6.1 and later come with a recent enough version) and the latest version of podlators. For versions of Perl earlier than 5.005, you'll also need C in F. S or later will build INN's documentation without significant changes from the versions that are checked into the repository. There are makefile rules in F to build all of the documentation whose master form is POD; if you add additional documentation, please add a rule there as well. Documentation should be generated by cd'ing to F and typing C where C is the relative path to the documentation file. This will get all of the various flags right for B or B. =head1 Error Handling INN has a set of generic error handling routines that should be used as much as possible so that the same syntax can be used for reporting errors everywhere in INN. The six basic functions are notice, sysnotice, warn, syswarn, die, and sysdie; notice prints or logs an informative message, warn prints or logs a warning, and die does the same and then exits the current program. The sys* versions add a colon, a space, and the value of strerror(errno) to the end of the message, and should be used to report failing system calls. All of the actual error reporting is done via error handlers, and a program can register its own handlers in addition to or instead of the default one. The default error handler (message_log_stderr) prints to stderr, prepending the value of message_program_name if it's set to something other than NULL. Several other error handlers are available, particularly including ones that use syslog. See F for all of the available options. There is a different set of error handlers for notice/sysnotice, warn/syswarn, and die/sysdie. To set them, make calls like: message_handlers_warn(1, message_log_stderr); message_handlers_die(2, message_log_stderr, message_log_syslog_err); The first argument is the number of handlers, and the remaining arguments are pointers to functions taking an int (the length of the formatted message), a const char * (the format), a va_list (the arguments), and an int that's 0 if warn or die was called and equal to the value of errno if syswarn or sysdie was called. The length of the formatted message is obtained by calling vsnprintf with the provided format and arguments, and therefore is reliable to use as the size of a buffer to malloc to hold the result of formatting the message provided that vsnprintf is used to format it (warning: the system vsprintf may produce more output under some circumstances, so always use vsnprintf). The error handler can do anything it wishes; each error handler is called in the sequence given. Error handlers shouldn't call warn or die unless great caution is taken to prevent infinite recursion. Also be aware that sysdie is called if malloc fails in xmalloc, so if the error handler needs to allocate memory, it must not use xmalloc or a related function to do so and it must not call die to report failure. The default syslog handlers report memory allocation failure to stderr and exit. Finally, die and sysdie support an additional handler (the external variable message_fatal_cleanup) that's called immediate before exiting, takes no arguments, and returns an int which is used as the argument for exit. It can do any necessary global cleanup, call abort instead to generate a core dump or the like. The advantage of using this system everywhere in INN is that library code can use notice, warn, and die to report messages and errors, and each calling program can set up the handlers as appropriate to make sure the errors go to the right place. The default handler is fine for interactive programs; for programs that run from interactive scripts, adding something like: message_program_name = "program"; to the beginning of main (where program is the name of the program) will make it easier to figure out which program the script calls is failing. For programs that may also be called non-interactively, like rnews, one may want to set up handlers like: message_handlers_notice(2, message_log_stdout, message_log_syslog_info); message_handlers_warn(2, message_log_stderr, message_log_syslog_warning); message_handlers_die(2, message_log_stderr, message_log_syslog_err); Finally, for daemons and other non-interactive programs, one may want to do: message_handlers_notice(1, message_log_syslog_info); message_handlers_warn(1, message_log_syslog_warning); message_handlers_die(1, message_log_syslog_err); to report errors only via syslog. (Note that if you use syslog error handlers, the program should call openlog first thing to make sure they are logged with the right facility.) For historical reasons, error messages that are fatal to the news subsystem are logged at the LOG_CRIT priority, and therefore die in innd should use message_log_syslog_crit. This seems like priority inflation and may change in the future to message_log_syslog_err. =head1 Test Suite The test suite for INN is located in the F directory and is just getting started. The test suite consists of a set of programs listed in F and the scaffolding in the B program. Adding new tests is very straightforward and very flexible. Just write a program that tests some part of INN, put it in a directory under F named after the part of INN it's testing, and have it output first a line containing the count of test cases in that file, and then for each test a line saying C or C where C is the test case number. (If a test is skipped for some reason, such as a test of an optional feature that wasn't compiled into INN, the test program should output C.) Add any rules necessary to build the test to F (note that for simplicity it doesn't recurse into subdirectories) and make sure it creates an executable ending in F<.t>. Then add the name of the test to F, without the F<.t> ending. For C tests, you probably want to use the functions in F (prototypes in F) to handle all of the output. For shell script tests, F contains helpful shell functions. See the existing tests for some hints about how to write new tests, as well as the F guide. One naming convention: to distinguish more easily between for example F (the implementation) and F (the test suite), we add F<-t> to the end of the test file names. So F is the source that compiles into an executable F which is run by putting a line in F of just C. Note that tests don't have to be written in C; in fact, F is just a shell script (that calls a supporting C program). Tests can be written in shell or Perl (but other languages should be avoided because someone who wants to run the test suite may not have it) and just have to follow the above output conventions. Additions to the test suite, no matter how simple, are very welcome. Running the test suite is done with: make check A single test can be run with verbose ouput via: tests/runtests -o Using B ensures that necessary environment variables are set up. =head1 Makefiles All INN makefiles include F at the top level, and only that makefile is a F substitution target. This has the disadvantage that F's normal support for building in a tree outside of the source tree doesn't work, but it has the significant advantage of making F run much faster and allowing one to run C in any subdirectory and pick up all the definitions and settings from the top level configuration. All INN makefiles should also set C<$(top)> to be the path to the top of the build directory (usually relative). This path is used to find various programs like B and libtool so that the same macros (set in F) can be used all over INN. The format of INN's makefiles is mostly standardized; the best examples of the format are probably F and F, at least for directories with lots of separate programs. The C variable holds all the files that should be generated, C those additional files that were generated by F, and C the C source files for generating tag information. There are a set of standard installation commands defined in make variables by F, and these should be used for all file installations. See the comment blocks in F for information on what commands are available and when they should be used. There are also variables set for each of the installation directories that INN uses, for use in building the list of installed paths to files. Each subdirectory makefile should have the targets C (the default), C, C/C, C, and C. The C target generates a profiling version of the programs (although this hasn't been tested much). These rules should be present and empty in those directories where they don't apply. Be sure to test compiling with both static and dynamic libraries and make sure that all the libtool support works correctly. All linking steps, and the compile steps for all library source, should be done through C<$(LIBCC)> and C<$(LIBLD)> (which will be set as appropriate in F). =head1 Scripts INN comes with and installs a large number of different scripts, both Bourne shell and Perl, and also comes with support for Tcl scripts (although it doesn't come with any). Shell variables containing both F-time information and configuration information from F are set by the B support libraries, so the only system-specific configuration that should have to be done is fixing the right path to the interpreter and adding a line to load the appropriate B. F, built by F, does this. It takes a F<.in> file and generates the final script (removing the F<.in>) by fixing the path to the interpreter on the first line and replacing the second line, whatever it is, with code to load the B appropriate for that interpreter. (If invoked with B<-i>, it just fixes the interpreter path.) Scripts should use B (via B) to get the right path and the right variables whenever possible, rather than having F substitute values in them. Any values needed at run-time should instead be available from all of the different B. As for Perl, the C module has the same features as F (only kept for compatibility reasons with old scripts not shipped with INN); however, it can be safely used with warnings on in Perl scripts. See the existing scripts for examples of how this is done. =head1 Include Files Include files relevant to all of INN, or relevant to the two libraries built as part of INN (the utility B library and the B library that contains all storage and overview functions) are found in the F directory; other include files relevant only to a portion of INN are found in the relevant directory. Practically all INN source files will start with: #include "config.h" #include "clibrary.h" The first picks up all defines generated by B and is necessary for types that may not be present on all systems (I, I, I, I, and the like). It therefore should be included before any other headers that use those types, as well as to get general configuration information. It also includes F and F, which pick up additional support macros and compile-time configuration. The second is portably equivalent to: #include #include #include #include #include #include #include #include except that it doesn't include headers that are missing on a given system, replaces functions not found on the system with the INN equivalents, provides macros that INN assumes are available but which weren't found, and defines some additional portability things. Even if this is more headers than the source file actually needs, it's generally better to just include F rather than trying to duplicate the B-driven hackery that it does to do things portably. The primary exception is for source files in F that only define a single function and are used for portability; those may want to include only F so that they can be easily used in other projects that use B. F is a fairly standard header name for this purpose. F does also include F, but it's somewhat poor form to rely on this; it's better to explicitly list the header dependencies for the benefit of someone else reading the code. There are portable wrappers around several header files that have known portability traps or that need some fixing up on some platforms. Look in F and familiarize yourself with them and use them where appropriate. Another frequently included header file is F, which among other things defines xmalloc(), xrealloc(), xstrdup(), and xcalloc(), which are checked versions of the standard memory allocation routines that terminate the program if the memory allocation fails. These should generally always be used instead of the regular C versions. F also provides various other utility functions that are frequently used. F includes a wide variety of paths determined at configure time, both default paths to various parts of INN and paths to programs. Don't just use the default paths, though, if they're also configurable in F; instead, call innconf_read() and use the global I structure. Other files in F are interfaces to particular bits of INN library functionality or are used for other purposes; see the comments in each file. Eventually, the header files will be separated into installed header files and uninstalled header files; the latter are those headers that are used only for compiling INN and aren't useful for users of INN's libraries (such as F). All of the installed headers will live in F and be installed in a subdirectory named F in the configured F directory. This conversion is still in progress. When writing header files, remember that C reserves all identifiers beginning with two underscores and all identifiers beginning with an underscore and a capital letter for the use of the implementation; don't use any identifiers with names like that. Additionally, any identifier beginning with an underscore and a lower-case letter is reserved in file scope, which means that such identifiers can only be used by INN for the name of structure members or function arguments in function prototypes. Try to pay attention to the impact of a header file on the program namespace, particularly for installed header files in F. All symbols defined by a header file should ideally begin with C, C, or some other unique prefix indicating the subsystem that symbol is part of, to avoid accidental conflicts with symbols defined by the program that uses that header file. =head1 Libraries INN includes three shared libraries, in F, F, and F. Whenever any of the source to those libraries is modified, the library version information must be updated at the top of the F in that directory. Follow the instructions in the Libtool manual under Versioning / Updating version info, with the exception that, contrary to point 2, it's useful to those running snapshots to update the version information more frequently than for each public release. Remember that the version information has to be updated to allow recovery from a C command. =head1 Coding Style INN has quite a variety of coding styles intermixed. As with all programs, it's preferrable when making minor modifications to keep the coding style of the code you're modifying. In INN, that will vary by file. (Over time we're trying to standardize on one coding style, so changing the region you worked on to fit the general coding style is also acceptable). If you're writing a substantial new piece of code, the prevailing "standard" INN coding style is the following: =over 3 =item * Write in regular S whenever possible. Use the normal ANSI and POSIX constructs and use B or portability wrappers to fix things up beforehand so that the code itself can read like regular ANSI or POSIX code. Code should be written so that it works as expected on a modern platform and is fixed up with portability tricks for older platforms, not the other way around. You may assume an S compiler. Try to use const wherever appropriate. Don't use register; modern compilers will do as good of a job as you will in choosing what to put into a register. Don't bother with restrict (at least yet). =item * Use string handling functions that take counts for the size of the buffer whenever possible. This means using snprintf in preference to sprintf and using strlcpy and strlcat in preference to strcpy and strcat. Also, use strlcpy and strlcat instead of strncpy and strncat, as it is much easier to audit uses of the former than the latter. strlcpy is like strncpy except that it always nul-terminates and doesn't fill the rest of the buffer with nuls, making it more efficient. strlcat is like strncat except that it always nul-terminates and it takes the total size of the buffer as its third argument rather than just the amount of space left. All of these functions are guaranteed to be available; there are replacements in F for systems that don't have them. If you have to use a string copying routine that doesn't nul-terminate, use memcpy instead. Avoid introducing any uses of strcpy, strcat, strncpy, or strncat so that we can use grep to find dangerous usages and switch them to better functions. =item * Avoid C<#ifdef> and friends whenever possible. Particularly avoid using them in the middle of code blocks. Try to hide all portability preprocessor magic in header files or in portability code in F. When something just has to be done two completely different ways depending on the platform or compile options or the like, try to abstract that functionality out into a generic function and provide two separate implementations using C<#ifdef>; then the main code can just call that function. If you do have to use preprocessor defines, note that if you always define them to either C<0> or C<1> (never use C<#define> without a second argument), you can use the preprocessor define in a regular if statement rather than using C<#if> or C<#ifdef>. Make use of this instead of C<#ifdef> when possible, since that way the compiler will still syntax-check the other branch for you and it makes it far easier to convert the code to use a run-time check if necessary. (Unfortunately, this trick can't be used if one branch may call functions unavailable on a particular platform.) =item * Avoid uses of fixed-width buffers except in performance-critical code, as it's harder to be sure that such code is correct and it tends to be less flexible later on. If you need a reusable, resizable memory buffer, one is provided in F. =item * Avoid uses of static variables whenever possible, particularly in libraries, because it interferes with making the code re-entrant down the road and makes it harder to follow what's going on. Similarly, avoid using global variables whenever possible, and if they are required, try to wrap them into structures that could later be changed into arguments to the affected functions. =item * Use a roughly BSD indentation style but with four-space indents. This means no space before the parenthesis around function arguments, open brace on the same line as if/while/for, and close and open brace on the same line as else. =item * Introductory comments for functions or files are generally written as: /* ** Introductory comment. */ Other multiline comments in the source are generally written as: /* This is a multiline comment. */ Comments before functions saying what they do are nice to have. In general, the RCS/SVN C tag is on the first line of each source file since it's useful to know when a file was last modified. =item * Checks for NULL pointers are preferrably written out explicitly; in other words, use: if (p != NULL) rather than: if (p) to make it clearer what the code is assuming. =item * It's better to always put the body of an C statement on a separate line, even if it's only a single line. In other words, write: if (p != NULL) return p; and not: if (p != NULL) return p; This is in part for a practical reason: some code coverage analysis tools like B will count the second example above as a single line and won't notice if the condition always evaluates the same way. =item * Plain structs make perfectly reasonable abstract data types; it's not necessary to typedef the struct to something else. Structs are actually very useful for opaque data structures, since you can predeclare them and then manipulate pointers to them without ever having to know what the contents look like. Please try to avoid typedefs except for function pointers or other extremely confusing data types, or for data types where we really gain some significant data abstraction from hiding the underlying data type. Also avoid using the C<_t> suffix for any type; all types ending in C<_t> are reserved by POSIX. For typedefs of function pointer types, a suffix of C<_func> usually works. This style point is currently widely violated inside of INN itself; INN originally made extensive use of typedefs. =item * When noting something that should be improved later, add a comment containing C so that one can easily grep for such comments. =back INN's indentation style roughly corresponds to that produced by GNU S 2.2.6> with the following options: -bad -bap -nsob -fca -lc78 -cd41 -cp1 -br -ce -cdw -cli0 -ss -npcs -ncs -di1 -nbc -psl -brs -i4 -ci4 -lp -ts8 -nut -ip5 -lps -l78 -bbo -hnl Unfortunately, B currently doesn't get everything right (it has problems with spacing around struct pointer arguments in functions, wants to put in a space between a dereference of a function pointer and the arguments to the called function, misidentifies some macro calls as being type declarations, and fouls up long but simple case statements). It would be excellent if someday we could just run all of INN's code through B routinely to enforce a consistant coding style, but B isn't quite ready for that. For users of emacs cc-mode, use the "bsd" style but with: (setq c-basic-offset 4) Finally, if possible, please don't use tabs in source files, since they can expand differently in different environments. In particular, please try not to use the mix of tabs and spaces that is the default in emacs. If you use emacs to edit INN code, you may want to put: ; Use only spaces when indenting or centering, no tabs. (setq-default indent-tabs-mode nil) in your F<~/.emacs> file. Note that this is only a rough guideline and the maintainers aren't style nazis; we're more interested in your code contribution than in how you write it. =head1 Making a Release This is a checklist that INN maintainers should go through when preparing a new release of INN. =over 4 =item 1. Update the files shipped with INN, and that are maintained by external projects. =over 2 =item * Make sure that F, F, F, F and libtool m4 files (F, F, F, F and F) are the latest versions. See the instructions in L<"Configuring and Portability"> for details on how to update these files. =item * Make sure that the latest upstream version of the C TAP Harness package is used for the test suite driver. It is available at L, and can be easily synchronized with the script F; just run it, have a look at the resulting changes in INN source code and, if everything seems all right, commit these changes. Parts specific to INN should be kept during an update (especially sections relative to LIBTEST_NEW_FORMAT because the test suite has not yet been fully updated to use the new format of C TAP Harness). =item * Make sure that the latest upstream version of the files maintained in the rra-c-util package that INN uses are the ones shipped with the release. These files are available at L and can be easily synchronized with the script F; just run it, have a look at the resulting changes in INN source code and, if everything seems all right, commit these changes. =item * Make sure that F and F are in sync with the master version at L and L. =back =item 2. Update copyright years in F. =item 3. Update F and regenerate F. Be more detailed for a minor release than for a major release. For a major release, also add information on how to upgrade from the last major release, including anything special to be aware of. (Minor releases shouldn't require any special care when upgrading.) =item 4. Bump the revision number in F (subject 1.2) so that it could be included in a final release. It should not be changed for a beta or a release candidate. If making a major release, the revision numbers of the STABLE series should also be bumped at the end of subject 1.2. =item 5. Double-check that the version information in F, F, and F has been updated if there has been any change in the library sources since the previous release. =item 6. If making a major release, branch the source tree by creating a new directory under F in Subversion named after the major release. This branch will be used for minor releases based on that major release and can be done a little while before the C<.0> release of that major release. svn copy -r ZZZZ -m "Branch Y.Y.0 release." \ file:///srv/svn/inn/trunk file:///srv/svn/inn/branches/Y.Y Then, in that newly created branch, remove the first paragraph in F which deals with development versions. =item 7. Check out a copy of the release branch. It's currently necessary to run C and C to generate F. Then, run C to generate all necessary files. Afterwards, run C. There shouldn't be any differences; otherwise, fix the F file. =item 8. Run C for a final release, C for the first beta version of a new release, or C for the first release candidate version of a new release. Note that you need to have a copy of B from L to do this; at least version 0.7 is required. Start the F at the time of the previous release. (Eventually, the script will be smart enough to do this for you.) Check that the F file is correct; otherwise, regenerate it or manually edit it. Then run again C or any other command you used. =item 9. Generate an MD5 checksum of the release tarball. md5sum inn-Y.Y.Y.tar.gz > inn-Y.Y.Y.tar.gz.md5 =item 10. Generate a diff between this release and the previous release if feasible (always for minor releases, possibly not a good idea due to the length of the diff for major releases). You will need the tar file of the previous release for the comparison. diff -Nurp inn-X.X.X inn-Y.Y.Y > inn-X.X.X-Y.Y.Y.diff gzip inn-X.X.X-Y.Y.Y.diff =item 11. Make the resulting tar file, along with its MD5 checksum and the possible diff from the previous release, available for testing in the F directory on ftp.isc.org and announce its availability on inn-workers. Install it on at least one system and make sure that system runs fine for at least a few days. This is also a good time to send out a draft of the release announcement to inn-workers for proof-reading. =item 12. Move the release into the public area of the ftp site and update the F link. Also put the diff and the MD5 checksum on the ftp site and update the F link. Sign the release (ASC, SHA-1, SHA-256 and SHA-512) and update the F link. Possibly move older releases off into the F directory. Contact the ISC folks to push the release to ftp.isc.org and to update the ISC web site (the relevant contact is C instead of C). =item 13. After the ISC web site has been updated with links towards the new release, send an announce on inn-announce and in news.software.nntp (with a possible crosspost to news.admin.announce). =item 14. Tag the checked-out tree that was used for generating the release with a release tag by copying it to F in Subversion. svn copy -r ZZZZ -m "Tag Y.Y.Y release." \ file:///srv/svn/inn/branches/Y.Y file:///srv/svn/inn/tags/Y.Y.Y =item 15. Bump revision numbers to reflect the one of the following release, especially in F, F and F for major releases, F and F for both minor and major releases. The release versions in the Trac wiki should also be updated. =item 16. For major releases, update the branch used for the generation of STABLE snapshots. =back =head1 References Some additional references that may be hard to find and may be of use to people working on INN: =over 4 =item L The home page for the IETF NNTP standardization effort, including links to the IETF NNTP working group archives and copies of the latest drafts of the new NNTP standard. The old archived mailing list traffic contains a lot of interesting discussion of why NNTP is the way it is. =item L A collection of documents about the Usenet article format, including most of the relevant RFCs and Internet-Drafts. =item L The archives for the USEFOR IETF working group, the working group for the S replacement (the format of Usenet articles), now published as S and S. =item L Forrest Cavalier provides several tools for following INN development at this page and elsewhere in the Usenet RKT. Under here is a web-accessible checked-out copy of the current INN source tree and pointers to how to use CVSup. However, please note that INN development now uses Subversion. =item L A primer on IPv6 with pointers to the appropriate places for more technical details as needed, useful when working on IPv6 support in INN. =back $Id: hacking.pod 9942 2015-09-08 19:49:55Z iulius $ =cut inn-2.6.0/doc/pod/pullnews.pod0000644000175200017520000003013312575023702015624 0ustar iuliusiulius=head1 NAME pullnews - Pull news from multiple news servers and feed it to another =head1 SYNOPSIS B [B<-BhnOqRx>] [B<-a> I] [B<-b> I] [B<-c> I] [B<-C> I] [B<-d> I] [B<-f> I] [B<-F> I] [B<-g> I] [B<-G> I] [B<-H> I] [B<-k> I] [B<-l> I] [B<-m> I] [B<-M> I] [B<-N> I] [B<-p> I] [B<-P> I] [B<-Q> I] [B<-r> I] [B<-s> I[:I]] [B<-S> I] [B<-t> I] [B<-T> I] [B<-w> I] [B<-z> I] [B<-Z> I] [I ...] =head1 REQUIREMENTS The C module must be installed. This module is available as part of the libnet distribution and comes with recent versions of Perl. For older versions of Perl, you can download it from L. =head1 DESCRIPTION B reads a config file named F, and connects to the upstream servers given there as a reader client. This file is looked for in I when B is run as the user set in I in F (which is by default the C user); otherwise, this file is looked for in the running user's home directory. By default, B connects to all servers listed in the configuration file, but you can limit B to specific servers by listing them on the command line: a whitespace-separated list of server names can be specified, like I for one of them. For each server it connects to, it pulls over articles and feeds them to the destination server via the IHAVE or POST commands. This means that the system B is run on must have feeding access to the destination news server. B is designed for very small sites that do not want to bother setting up traditional peering and is not meant for handling large feeds. =head1 OPTIONS =over 4 =item B<-a> I This option is a deterministic way to control the flow of articles and to split a feed. The I parameter must be in the form C or C. The Message-ID of each article is hashed using MD5, which results in a 128-bit hash. The lowest S<32 bits> are then taken by default as the hashfeed value (which is an integer). If the hashfeed value modulus C plus one equals C or is between C and C, B will feed the article. All these numbers must be integers. For instance: pullnews -a 1/2 Feeds about 50% of all articles. pullnews -a 2/2 Feeds the other 50% of all articles. Another example: pullnews -a 1-3/10 Feeds about 30% of all articles. pullnews -a 4-5/10 Feeds about 20% of all articles. pullnews -a 6-10/10 Feeds about 50% of all articles. You can use an extended syntax of the form C or C (using an underscore C<_> instead of a colon C<:> is also recognized). As MD5 generates a 128-bit return value, it is possible to specify from which byte-offset the 32-bit integer used by hashfeed starts. The default value for C is C<:0> and thirteen overlapping values from C<:0> to C<:12> can be used. Only up to four totally independent values exist: C<:0>, C<:4>, C<:8> and C<:12>. Therefore, it allows to a generate a second level of deterministic distribution. Indeed, if B feeds C<1/2>, it can go on splitting thanks to C<1-3/9:4> for instance. Up to four levels of deterministic distribution can be used. The algorithm is compatible with the one used by S and up. =item B<-b> I Backtrack on server numbering reset. Specify the proportion (C<0.0> to C<1.0>) of a group's articles to pull when the server's article number is less than our high for that group. When I is C<1.0>, pull all the articles on a renumbered server. The default is to do nothing. =item B<-B> Feed is header-only, that is to say B only feeds the headers of the articles, plus one blank line. It adds the Bytes: header field if the article does not already have one, and keeps the body only if the article is a control article. =item B<-c> I Normally, the config file is stored in F in I when B is run as the news user, or otherwise in the running user's home directory. If B<-c> is given, I will be used as the config file instead. This is useful if you're running B as a system user on an automated basis out of cron or as an individual user, rather than the news user. See L below for the format of this file. =item B<-C> I Use I characters per line for the progress table. The default value is C<50>. =item B<-d> I Set the debugging level to the integer I; more debugging output will be logged as this increases. The default value is C<0>. =item B<-f> I This changes the proportion of articles to get from each group to I and should be in the range C<0.0> to C<1.0> (C<1.0> being the default). =item B<-F> I Prepend I as a host to the Path: header of articles fed. =item B<-g> I Specify a collection of groups to get. I is a list of newsgroups separated by commas (only commas, no spaces). Each group must be defined in the config file, and only the remote hosts that carry those groups will be contacted. Note that this is a simple list of groups, not a wildmat expression, and wildcards are not supported. =item B<-G> I Add the comma-separated list of groups I to each server in the configuration file (see also B<-g> and B<-w>). =item B<-h> Print a usage message and exit. =item B<-H> I Remove these named headers (colon-separated list) from fed articles. =item B<-k> I Checkpoint (save) the config file every I articles (default is C<0>, that is to say at the end of the session). =item B<-l> I Log progress/stats to I (default is C). =item B<-m> I Feed an article based on header matching. The argument is a number of whitespace-separated tuples (each tuple being a colon-separated header and regular expression). For instance: -m "Hdr1:regexp1 !Hdr2:regexp2 #Hdr3:regexp3 !#Hdr4:regexp4" specifies that the article will be passed only if the C header matches C and the C header does not match C. Besides, if the C header matches C, that header is removed; and if the C header does not match C, that header is removed. =item B<-M> I Specify the maximum number of articles (per group) to process. The default is to process all new articles. See also B<-f>. =item B<-n> Do nothing but read articles S<-- does> not feed articles downstream, writes no B file, does not update the config file. =item B<-N> I Specify the timeout length, as I seconds, when establishing an NNTP connection. =item B<-O> Use an optimized mode: B checks whether the article already exists on the downstream server, before downloading it. It may help for huge articles or a slow link to upstream hosts. =item B<-p> I Connect to the destination news server on a port other than the default of C<119>. This option does not change the port used to connect to the source news servers. =item B<-P> I Restrict feeding an article based on the number of hops it has already made. Count the hops in the Path: header (I), feeding the article only when I is C<+num> and I is more than I; or I is C<-num> and I is less than I. =item B<-q> Print out less status information while running. =item B<-Q> I Set the quietness level (C<-Q 2> is equivalent to C<-q>). The higher this value, the less gets logged. The default is C<0>. =item B<-r> I Rather than feeding the downloaded articles to a destination server, instead create a batch file that can later be fed to a server using B. See rnews(1) for more information about the batch file format. =item B<-R> Be a reader (use MODE READER and POST commands) to the downstream server. The default is to use the IHAVE command. =item B<-s> I[:I] Normally, B will feed the articles it retrieves to the news server running on localhost. To connect to a different host, specify a server with the B<-s> flag. You can also specify the port with this same flag or use B<-p>. =item B<-S> I Specify the maximum time I in seconds for B to run. =item B<-t> I The maximum number (I) of attempts to connect to a server (see also B<-T>). The default is C<0>. =item B<-T> I Pause I seconds between connection retries (see also B<-t>). The default is C<1>. =item B<-w> I Set each group's high water mark (last received article number) to I. If I is negative, calculate S+I> instead (i.e. get the last I articles). Therefore, a I of C<0> will re-get all articles on the server; whereas a I of C<-0> will get no old articles, setting the water mark to I (the most recent article on the server). =item B<-x> If the B<-x> flag is used, an Xref: header is added to any article that lacks one. It can be useful for instance if articles are fed to a news server which has I set in F. =item B<-z> I Sleep I seconds between articles. The default is C<0>. =item B<-Z> I Sleep I seconds between groups. The default is C<0>. =back =head1 CONFIG FILE The config file for B is divided into blocks, one block for each remote server to connect to. A block begins with the host line (which must have no leading whitespace) and contains just the hostname of the remote server, optionally followed by authentication details (username and password for that server). Note that authentication details can also be provided for the downstream server (a host line could be added for it in the configuration file, with no newsgroup to fetch). Following the host line should be one or more newsgroup lines which start with whitespace followed by the name of a newsgroup to retrieve. Only one newsgroup should be listed on each line. B will update the config file to include the time the group was last checked and the highest numbered article successfully retrieved and transferred to the destination server. It uses this data to avoid doing duplicate work the next time it runs. The full syntax is: [ ] [
] [B<-6> I
] [B<-c> I] [B<-H> I] [B<-i> I] [B<-l> I] [B<-m> I] [B<-n> I] [B<-o> I] [B<-P> I] [B<-t> I] [B<-T> I] [B<-X> I] =head1 DESCRIPTION B, the InterNetNews daemon, handles all incoming NNTP feeds, coordinates the storage, retransmission, and overview generation for all accepted articles, and manages the active(5) and history(5) databases. It handles incoming connections on the NNTP port, and also creates and listens to a local Unix-domain stream socket in order to receive articles from local processes such as nnrpd(8) and rnews(1). As the master daemon, B should generally be started at boot and be always running. It listens to a Unix-domain datagram socket for commands to control its activities, commands that can be sent using ctlinnd(8). The current status of B can be obtained by running C, or for more detailed output, innstat(8). B can be in one of three operating modes: running, paused, or throttled. Running is the normal mode; when the server is throttled, it closes connections and rejects new ones. Paused is like a temporary throttle, suspending B's activities but not causing the server to shut down existing connections. The mode is normally changed via ctlinnd(8), either by various automated processes (such as nightly article expiration) or manually by the news administrator, but B will also throttle itself if it encounters ENOSPC errors in writing data or an excessive number of I/O errors (among other problems). B normally takes care of spawning nnrpd(8) to handle connections from news reading clients, but it can be run on a separate port from nnrpd(8) so that feed connections and news reading connections are handled separately (this can often be faster). Normally, B listens on port 119, the assigned port for NNTP; if it is desirable to run B and nnrpd(8) on separate ports, it's recommended that nnrpd(8) be given port 119 (since many news reading clients connect only to that port) and that port 433 be used for B. The primary configuration files that control B's activities are F, which specifies what remote sites B will accept connections from, F, which specifies what is to be done with incoming articles besides storing them, and F, which sets a wide variety of configuration parameters. Some parameters in inn.conf(5) can also be set with command-line flags; for these, the command-line flags take precedence if used. B must be run as the news user and news group. It will check for this at startup and fail to start if not run properly. Normally it should be started via rc.news(8) as part of the system boot up process. It relies on the setuid root helper program innbind(8) to listen on a privileged port (119, 433 or 563). =head1 OPTIONS For the options below that override F settings, see inn.conf(5) for the default values if neither the F setting nor the command-line option is given. =over 4 =item B<-4> I
Normally, B binds to all local IP addresses (unless I is set in F). If this option is given, it specifies the IP address that INN should bind as. This is only relevant for servers with multiple local IP addresses. The IP address must be in dotted-quad (C) format. If this option is specified, it's the same as setting I in F and may cause changes in whether INN binds to an IPv6 address as well. See inn.conf(5) for more details and also the B<-6> flag for B. =item B<-6> I
Only applies when INN has been built with IPv6 support. Normally B binds to all local IP addresses (unless I is set in F). If this option is given, it specifies the IPv6 address that INN should bind to. The IPv6 address must be in colon-separated S format (C). If this option is specified, it's the same as setting I in F and may cause changes in whether INN binds to an IPv4 address as well. See inn.conf(5) for more details and also the B<-4> flag for B. =item B<-a> By default, if a host connects to B but is not listed in F, the connection is handed off to B (or rejected if I is set in F). If B<-a> is given, F is ignored and any host can connect and transfer articles. This flag should never be used with an accessible server connected to Usenet; it would open the server up for all sorts of abuse. =item B<-c> I B normally rejects any article that is older (in days) than the value of I in F. This option, if given, overrides the value of that setting. If I is 0, this check is suppressed and B will accept articles regardless of how old they are. =item B<-C> This flag tells B to accept and propagate but not actually process cancel or supersedes messages. This is intended for sites concerned about abuse of cancels, or that wish to use another cancel mechanism with stronger authentication. =item B<-d>, B<-f> B normally puts itself into the background, points its standard output and error to log files, and disassociates itself from the terminal. Using B<-d> prevents all of this, resulting in log messages being written to standard output; this is generally useful only for debugging. Using B<-f> prevents the backgrounding and disassociation but still redirects output; it may be useful if you want to monitor B with a program that would be confused by forks. =item B<-H> I, B<-T> I, B<-X> I These flags control the number of connections per I seconds that are allowed. This code is meant to protect your server from newsreader clients that make too many connections per minute (and therefore these flags are probably only useful when B is spawning B). You probably should not use these options unless you're having problems. The table used for this check is fixed at 128 entries and is used as a ring; the size was chosen to make calculating the index easy and to be fairly sure that it won't run out of space. In practice, it is unlikely that even half the table will be used at any given moment. The B<-H> flag limits the number of times a host is allowed to connect to the server per the time interval given by B<-X>. The default is C<2>. The B<-T> flag limits the total number of incoming connections per the time interval given by B<-X>. The maximum value is C<128>, and the default is C<60>. Note that the time interval given by B<-X> is set to C<0> by default, that is to say no control is done on the number of connections. =item B<-i> I B normally allows a maximum number of concurrent NNTP connections given by the value of I in F. This option, if given, overrides the value of that setting. If I is C<0>, this check is suppressed. =item B<-l> I B normally rejects any article larger than the value of I in F. This option, if given, overrides the value of that setting and specifies a maximum article size of I. If I is C<0>, this check is suppressed. =item B<-m> I Normally, B starts in the C mode. If this option is given, it specifies what mode B should start in. I should begin with one of C, C

, or C, and the starting mode will be set to C, C, or C, respectively, based on that initial letter. (C is short for C.) =item B<-N> If this option is given, any filters (Perl or Python) are disabled before B starts (normally, filters default to being enabled). The filters can be enabled after B has started with ctlinnd(8). =item B<-n> I Whether B allows (and hands off to B) reader connections while paused or throttled is normally determined by the value of I in F. This option, if given, overrides that value. If I is C, B will not allow readers if it is paused or throttled. If I is C, readers will be allowed regardless of B's operating mode. =item B<-o> I This flag limits the number of file descriptors that are available for outgoing file feeds. The default is the number of available file descriptors minus some reserved for internal use (which could potentially starve B of descriptors to use for accepting new connections). If B has more file feeds than I, some of them will be buffered and only written out periodically. Normally you never need to use this option, since the number of outgoing feeds is fixed, being the number of file feeds configured in F, and is generally small (particularly given that innfeed(8) is now used for most outgoing feeds at large sites). =item B<-P> I The port B should listen on is normally given by the value of I in F. This option, if given, overrides that value and specifies the port that B should bind to. =item B<-r> Instructs B to renumber the F file after starting, just as if a C command were sent. =item B<-s> Just check the syntax of the F file and exit. B will exit with a non-zero status if any errors are found; the actual errors will be reported via syslog(3). =item B<-S> Report errors found in F via syslog(3) and exit normally. (Yes, this is less useful than it should be.) =item B<-t> I Normally, B will flush any changes to history and the F file after 300 seconds of inactivity. This option changes that timeout to I. =item B<-u> The news log (the trace information for every article accepted by B) is normally buffered. This option changes the log to be unbuffered. =back =head1 CONTROL MESSAGES Arriving articles that have a Control: header are called "control messages". Except for cancel messages, these messages are handled by controlchan(8) via a feed set up in F. (Cancel messages update the history database, so they must be handled internally; the cost of syncing, locking, then unlocking would be too high given the number of cancel messages that are received. Note that if an article is cancelled before it is received by the news server, it will be rejected when it arrives since the history database has been updated; it is useful for rejecting spam before it arrives.) The distribution of control messages is different than that of standard articles. Control messages are normally filed into the pseudo-newsgroup named C regardless of which newsgroup they were actually posted to. If, however, a CI newsgroup exists that matches the control command, the control message will be filed into that group instead. For example, a newgroup control message will be filed in C if that group exists; otherwise, it will be filed in C. If you want to specifically feed all control messages to a given site regardless of whether the control messages would affect the newsgroups you're feeding that site, you can put the appropriate control newsgroup in the subscription list. For example, to feed all cancel messages to a given remote site (normally a bad idea), add C to its subscription list. Normally it's best to exclude the control newsgroups from feeds to keep from sending your peers more control messages than they care about. That's why the F pattern C is as often as not specified (adding this pattern do not prevent control messages which affect the newsgroups fed to a site from being sent to it). checkgroups, newgroup and rmgroup control messages receive additional special treatment. If one of these control messages is approved and posted to the newsgroup being created or removed (or to the admin group to which the checkgroups is posted), the message will be sent to all sites whose subscription patterns would cause them to receive articles posted to that group. For example, if a newgroup control message for a nonexistent newsgroup C is received, it will be sent to any site whose subscription pattern would cause it to receive C if that newsgroup existed (such as a pattern of C). For this reason, it is correct to post newgroup messages to the newsgroup that the control message would create. It is I generally correct to crosspost newgroup messages to some "well-propagated" newsgroup; not only will this not actually improve their propagation to sites that want such control messages, but it will also cause sites that do not want those control messages to receive them. Therefore, assuming that a newgroup control message is sent to the group C (specified in the Newsgroups: header) in order to create the group C, the sites with the following subscription patterns will receive it: *,@news.* news.* news.*,!control,!control.* control,control.* but the sites with the following subscription patterns will not receive it: *,@news.*,!control,!control.* comp.*,@news.* If a control message is posted to a group whose name ends with the four characters C<.ctl>, this suffix is stripped off and the control message is propagated as if it were posted to the base group. For example, a cancel message posted to C will be sent to all sites that subscribe to C (or C if that newsgroup doesn't exist) or C. This behavior is present for historical compatibility reasons and should be considered obsolete; support for the C<.ctl> suffix may be removed in a future version of INN. Finally, articles posted to newsgroups beginning with C are treated specially. Provided that either that newsgroup exists in the F file or I is set in F, the remainder of the newsgroup is taken to be a site name, as configured in F, and the article is sent to that site. If I is set, the article will be filed in the group named C (which must exist in the F file). For example, with I set, an article posted to C will be filed in C and sent to the site C. =head1 PROTOCOL DIFFERENCES B implements the NNTP commands defined in S (NNTP), S (NNTP authentication), S (streaming NNTP feeds) and S (NNTP LIST additions) with the following differences: =over 4 =item 1. A batch transfer command, XBATCH I, is provided. This command will read I bytes and store them for later processing by rnews(1) (which must be run separately, probably from cron). See innxbatch(8) and B for more details on this extension. =item 2. As INN is a mode-switching news server, B implements a limited subset of the protocol useful for transferring news. The remaining commands are mostly only useful for readers and are implemented by nnrpd(8). Use of the MODE READER command will cause B to pass the connection to B. =item 3. B allows a wider syntax for wildmats. =item 4. Three commands (IHAVE, CHECK and TAKETHIS) will continue, for interoperability reasons, to return a reject code (respectively C<435>, C<438> and C<439>) when the command contains a syntax error (which normally leads to C<501>). =back =head1 HEADER MODIFICATIONS B modifies as few article headers as possible, although it could be better in this area. Empty headers and headers that consist of nothing but whitespace are dropped. The local site's name (as set with the I parameter in F) and an exclamation point are prepended to the Path: header, provided the first site name in the Path: header is different from the local one. In addition, I and I may be similarly respectively prepended and appended to the Path: header; see inn.conf(5) for the details. The Xref: header is removed and a new one created. B does not rewrite incorrect headers. For example, it will not replace an incorrect Lines: header, though it may reject such an article depending on the value of I in F. =head1 CANCEL FEEDS In order to efficiently apply a large number of local cancels (such as from processing NoCeMs or from some other external source), INN supports a special feed mode available only to connections to the local Unix-domain socket (not to connections to any network sockets). To enter this mode, connect to the Unix-domain socket (I/nntpin) and send the command MODE CANCEL. The response will have code C<284>. Every subsequent line sent on that connection should consist of a single message-ID. An attempt will be made to cancel that message-ID, and the server will reply C<289> for success or C<484> for failure. (Failure can occur, for example, if the server is paused or throttled, or the message-ID is corrupt. Failure does I occur if the article to be cancelled does not exist.) =head1 LOGGING B reports all incoming articles in its log file (I/news). This is a text file with a variable number of space-separated fields in one of the following formats: mon dd hh:mm:ss.mmm + feed site ... mon dd hh:mm:ss.mmm j feed site ... mon dd hh:mm:ss.mmm c feed Cancelling mon dd hh:mm:ss.mmm - feed reason mon dd hh:mm:ss.mmm ? feed reason There may also be hostname and/or size fields after the message-ID depending on the settings of I and I in F. The first three fields are the date and time to millisecond resolution. The fifth field is the site that sent the article (based on the Path: header) and the sixth field is the article's message-ID; they will be a question mark if the information is not available. The fourth field indicates whether the article was accepted or not. If it is a plus sign, then the article was accepted. If it is the letter C, then the article was accepted, providing all of the newsgroups to which the article was posted were set to status C in the F file (or not listed in the F file and I was set in F), and then the article was filed into the C newsgroup. In both of these cases, the article has been accepted and the C field contains the space-separated list of sites to which the article is being sent. If the fourth field is the letter C, then a cancel message was accepted before the original article arrived, and a history entry for the cancelled message was created so that B will reject that message if it arrives later. If the fourth field is a minus sign, then the article was rejected. The reasons for rejection generated by B include: "%s" header too long Article exceeds local limit of %s bytes Article posted in the future -- "%s" Bad "%s" header Can't write history Duplicate Duplicate "%s" header EOF in headers Linecount %s != %s +- %s Missing %s header No body No colon-space in "%s" header No matching newsgroups in cancel <%s> No space Space before colon in "%s" header Too old -- "%s" Unapproved for "%s" Unwanted newsgroup "%s" Unwanted distribution "%s" Whitespace in "Newsgroups" header -- "%s" where C<%s>, above, is replaced by more specific information. (The Perl and Python filters, if used, may reject articles with other reasons.) If the fourth field is the letter C, the article contains strange strings, such as CR without LF or LF without CR. (These characters should never occur in isolation, only together as CRLF to indicate the end of a line.) This log message is just informational, to give an idea of how widespread such articles are; B does not reject such articles. Note that when I is set to true in F and an article is received that isn't posted to any valid newsgroups, it will be accepted and logged with two lines, a C line and a minus sign line, unless the I parameter is set to false (in which case only the C line is written). B also makes extensive reports through syslog(3). The first word of the log message will be the name of the site if the entry is site-specific (such as a "connected" message). The first word will be C if the message relates to the server itself, such as when a read error occurs. If the second word is the four letters C, then an error is being reported. (The absence of an apostrophe is intentional; it makes it easier to grep from the command line and easier to find error messages in FAQs using a search engine. However, C is also used at a few places.) In this case, the next two words generally name the system call or library routine that failed and the object upon which the action was being performed. The rest of the line may contain other information. In other cases, the second word attempts to summarize what change has been made, while the rest of the line gives more specific information. The word C generally indicates an internal logic error. =head1 SIGNALS B will catch SIGTERM and SIGHUP and shut down. If B<-d> is used, SIGINT will also be caught and will result in an orderly shutdown. B will catch the SIGUSR1 signal and recreate the control channel used by ctlinnd(8). =head1 BUGS B normally attempts to strip IP options from incoming connections, since it uses IP-based authentication and source routing can confuse that. However, this doesn't work on all systems, and it doesn't work at all in the presence of IPv6 support (and is disabled in that case). Hence, if using B with IPv6 support, make sure that your kernel or router disables source routing. =head1 HISTORY Written by Rich $alz for InterNetNews. $Id: innd.pod 9228 2011-07-07 11:12:26Z iulius $ =head1 SEE ALSO active(5), ctlinnd(8), dbz(3), history(5), incoming.conf(5), inn.conf(5), innbind(8), innfeed(8), innstat(8), newsfeeds(5), nnrpd(8), rnews(1), syslog(3). =cut inn-2.6.0/doc/pod/innfeed.conf.pod0000644000175200017520000007155412575023702016323 0ustar iuliusiulius=head1 NAME innfeed.conf - Configuration file for innfeed =head1 DESCRIPTION The configuration file F in I is used to control the innfeed(8) program. It is a fairly free-format file that consists of three types of entries: I:I, I and I. Comments are from the hash character C<#> to the end of the line. I:I entries are a keyword and a value separated by a colon (which can itself be surrounded by whitespace). For example: max-connections: 10 A legal I starts with a letter and contains only letters, digits, and the C<_> and C<-> characters. There are 5 different types of values: integers, floating-point numbers, characters, booleans, and strings. Integer and floating-point numbers are as to be expected, except that exponents in floating-point numbers are not supported. A boolean value is either C or C (case is not significant). A character value is a single-quoted character as defined by the C-language. A string value is any other sequence of characters. If the string needs to contain whitespace, then it must be quoted with double quotes, and uses the same format for embedding non-printing characters as normal C-language string. Peer entries look like: peer { # body ... } The word C is required. The I<< >> is the same as the site name in INN's F configuration file. The body of a peer entry contains some number (possibly zero) of I:I entries. Group entries look like: group { # body ... } The word C is required. The I<< >> is any string valid as a key. The body of a group entry contains any number of the three types of entries. So I:I pairs can be defined inside a group, and peers can be nested inside a group, and other groups can be nested inside a group. I:I entries that are defined outside of all I and I entries are said to be at "global scope". There are global I:I entries that apply to the process as a whole (for example the location of the backlog file directory), and there are global I:I entries that act as defaults for peers. When B looks for a specific value in a peer entry (for example, the maximum number of connections to set up), if the value is not defined in the peer entry, then the enclosing groups are examined for the entry (starting at the closest enclosing group). If there are no enclosing groups, or the enclosing groups do not define the I:I, then the value at global scope is used. A small example could be: # Global value applied to all peers that have # no value of their own. max-connections: 5 # A peer definition. "uunet" is the name used by innd # in the newsfeeds configuration file. peer uunet { ip-name: usenet1.uu.net } peer vixie { ip-name: gw.home.vix.com max-connections: 10 # Override global value. } # A group of two peers which can handle more connections # than normal. group fast-sites { max-connections: 15 # Another peer. The "max-connections" value from the # "fast-sites" group scope is used. The "ip-name" value # defaults to the peer's name. peer data.ramona.vix.com { } peer bb.home.vix.com { max-connections: 20 # He can really cook. } } Given the above configuration file, the defined peers would have the following values for the I key: uunet 5 vixie 10 data.ramona.vix.com 15 bb.home.vix.com 20 B ignores I:I pairs it is not interested in. Some configuration file values can be set via a command-line option, in which case that setting overrides the settings in the file. Configuration files can be included in other configuration files via the syntax: $INCLUDE filename There is a maximum nesting depth of 10. For a fuller example configuration file, see the supplied F. =head1 GLOBAL VALUES The following listing show all the keys that apply to the process as whole. These are not required (compiled-in defaults are used where needed). =over 4 =item I This key requires a pathname value and defaults to I in F. It specifies where the top of the article spool is. This corresponds to the B<-a> command-line option. =item I This key requires a pathname value. It specifies the pathname (relative to the I value) that should be read in funnel-file mode. This corresponds to giving a filename as an argument on the command-line (i.e. its presence also implies that funnel-file mode should be used). The default is unset; B then runs in channel or batch mode. =item I This key requires a pathname value and defaults to F. It specifies the pathname (relative to I in F) where the pid of the B process should be stored. This corresponds to the B<-p> command-line option. =item I This key defines the debug level for the process. Default is C<0>. A non-zero number generates a lot of messages to stderr, or to the config-defined I. This corresponds to the B<-d> command-line option. If a file named F exists in the I directory (as set in F), then I is automatically set to C<1>. This is a cheap way of avoiding continual reloading of the F file when debugging. Note that debug messages still go to I. =item I This key requires a boolean value and defaults to false (the debug file is allowed to grow without bound). If set to true, this file is truncated when its size reaches a certain limit. See I for more details. =item I This key requires a positive integer. The default value is C<2>. It defines the number of seconds to wait when B (or a fork) starts, before beginning to open connections to remote hosts. =item I This key requires a boolean value and defaults to false. If set to true, when B receives a SIGTERM or SIGQUIT signal, it will close its listeners as soon as it can, even if it means dropping articles. =item I This key requires a boolean value and defaults to true. When B is given file names to send (a fairly rare use case) instead of storage API tokens, it specifies whether mmaping should be used if B has been built with mmap(2) support. If article data on disk is not in NNTP-ready format (CR/LF at the end of each line), then after mmaping, the article is read into memory and fixed up, so mmaping has no positive effect (and possibly some negative effect depending on your system), and so in such a case this value should be C, which corresponds to the B<-M> command-line option. =item I This key requires a pathname value and defaults to F. It specifies where any logging messages that could not be sent via syslog(3) should go (such as those generated when a positive value for I is used). This corresponds to the B<-l> command-line option. This pathname is relative to I in F. =item I This key requires a format string suitable for strftime(3). It is used for messages sent via syslog(3) and to the I. Default value is C<%a %b %d %H:%M:%S %Y>. =item I This key requires a pathname value and defaults to F. It specifies where the current B process should store backlog files. This corresponds to the B<-b> command-line option. This pathname is relative to I in F. =item I This key requires a positive integer value and defaults to C<5>. It specifies how many articles should be kept on the backlog file queue before starting to write new entries to disk. =item I This key requires a positive integer value and defaults to C<30>. It specifies how many seconds elapse between checkpoints of the input backlog file. Too small a number will mean frequent disk accesses; too large a number will mean after a crash, B will re-offer more already-processed articles than necessary. =item I This key requires a positive integer value and defaults to C<600>. It specifies how many seconds elapse before each check for externally generated backlog files that are to be picked up and processed. =item I This key requires a positive integer value and defaults to C<60>. It specifies how many seconds elapse before B checks for a manually created backlog file and moves the output backlog file to the input backlog file. =item I This key requires a positive integer value and defaults to C<900>. It defines the number of seconds between attempts to re-lookup host information that previously failed to be resolved. =item I This key requires a positive integer value and defaults to C<86400>. It defines the number of seconds between refreshes of name to address DNS translation. This is so long-running processes do not get stuck with stale data, should peer IP addresses change. =item I This key requires a boolean value and defaults to false. It specifies whether the I should be HTML-ified. =item I This key requires a pathname value and defaults to F. An absolute pathname can be used. It specifies the pathname (relative to I when I is true; otherwise, I as set in F) where the periodic status of the B process should be stored. This corresponds to the B<-S> command-line option. =item I This key requires a boolean value and defaults to false. If the value is true, then whenever the transmission statistics for a peer are logged, each active connection logs its own statistics. This corresponds to the B<-z> command-line option. =item I This key requires a positive integer value and defaults to C<10>. It defines how many articles will be held internally for a peer before new arrivals cause article information to be spooled to the backlog file. =item I This key requires a positive integer value and defaults to C<600>. It defines how many seconds B waits between generating statistics on transfer rates. =item I This key requires a positive integer value and defaults to C<43200>. It defines how many seconds B waits before resetting all internal transfer counters back to zero (after logging one final time). This is so a B process running more than a day will generate "final" stats that will be picked up by logfile processing scripts. =item I This key requires a positive integer value and defaults to C<30>. It defines how many seconds to first wait before retrying to reconnect after a connection failure. If the next attempt fails too, then the reconnect time is approximately doubled until the connection succeeds, or I is reached. =item I This key requires an integer value and defaults to C<3600>. It defines the maximum number of seconds to wait between attempt to reconnect to a peer. The initial value for reconnection attempts is defined by I, and it is doubled after each failure, up to this value. =item I This key requires a non-negative integer value and defaults to C<0>. If the value is greater than zero, then whenever a network socket file descriptor is created and it has a value I this, the file descriptor will be dup'ed to bring the value up greater than this. This is to leave lower numbered file descriptors free for stdio. Certain systems, Sun's in particular, require this. SunOS 4.1.x usually requires a value of 128 and Solaris requires a value of 256. The default if this is not specified, is C<0>. =back =head2 Special keys for B The following keys are used with B to authenticate to a remote host. Several parameters may be included at global scope: =over 4 =item I The authname is who you want to authenticate as. =item I This is the appropriate password for authname. =item I The username is who you want to "act" as, that is, who is actually going to be using the server. =item I In this case, the "realm" is the realm in which the specified authname is valid. Currently this is only needed by the DIGEST-MD5 SASL mechanism. =item I A printf(3)-style format string for creating the envelope recipient address. The pattern MUST include a single string specifier which will be replaced with the newgroup (e.g. C). The default is C<+%s>. =item I An optional printf(3)-style format string for creating a To: header field to be prepended to the article. The pattern MUST include a single string specifier which will be replaced with the newgroup (e.g. C). If not specified, the To: header field will not be prepended. =back =head1 GLOBAL PEER DEFAULTS All the I:I pairs mentioned in this section can be specified at global scope. They may also be specified inside a group or peer definition. Note that when peers are added dynamically (i.e. when B receives an article for an unspecified peer), it will add the peer site using the parameters specified at global scope. =head2 Required keys No keys are currently required. They all have a default value, if not present in the configuration file. =head2 Optional keys The following keys are optional: =over 4 =item I This key requires a non-negative integer value. The default value is C<600>. If no articles need to be sent to the peer for this many seconds, then the peer is considered idle and all its active connections are torn down. =item I This key requires a non-negative integer value. The default value is C<300>. It defines the maximum amount of time to wait for a response from the peer after issuing a command. =item I This key requires a non-negative integer value. The default value is C<1>. It defines the number of connections to be opened immediately when setting up a peer binding. A value of C<0> means no connections will be created until an article needs to be sent. =item I This key requires a positive integer value. The default value is C<2> but may be increased if needed or for large feeds. It defines the maximum number of connections to run in parallel to the peer. A value of C<0> specifies an unlimited number of maximum connections. In general, use of an unlimited number of maximum connections is not recommended. Do not ever set I to zero with I 0 set, as this will saturate peer hosts with connections. =item I This key requires a positive integer value and defaults to C<86400>. It is the maximum number of seconds a connection should be kept open. Some NNTP servers do not deal well with connections being held open for long periods. =item I This key requires an integer value between 0 and 3. The default value is C<3>. It controls how connections are opened, up to the maximum specified by I. In general (and specifically, with I 0), a new connection is opened when the current number of connections is below I, and an article is to be sent while no current connections are idle. Without further restraint (i.e. using I 0), in practice this means that I connections are established while articles are being sent. Use of other I settings imposes a further limit on the amount of connections opened below that specified by I. This limit is calculated in different ways, depending of the value of I. Users should note that adding additional connections is not always productive S<-- just> because opening twice as many connections results in a small percentage increase of articles accepted by the remote peer, this may be at considerable resource cost both locally and at the remote site, whereas the remote site might well have received the extra articles sent from another peer a fraction of a second later. Opening large numbers of connections is considered antisocial. The meanings of the various settings are: =over 2 =item B<0> (no method) Increase of connections up to I is unrestrained. =item B<1> (maximize articles per second) Connections are increased (up to I) and decreased so as to maximize the number of articles per second sent, while using the fewest connections to do this. =item B<2> (set target queue length) Connections are increased (up to I) and decreased so as to keep the queue of articles to be sent within the bounds set by I and I, while using the minimum resources possible. As the queue will tend to fill if the site is not keeping up, this method ensures that the maximum number of articles are offered to the peer while using the minimum number of connections to achieve this. =item B<3> (combination) This method uses a combination of methods 1 and 2 above. For sites accepting a large percentage of articles, method 2 will be used to ensure these sites are offered as complete a feed as possible. For sites accepting a small percentage of articles, method 1 is used, to minimize remote resource usage. For intermediate sites, an appropriate combination is used. =back =item I This key requires a floating-point value between 0 and 100. It represents (as a percentage) the low water mark for the host queue. If the host queue falls below this level while using I 2 or 3, and if 2 or more connections are open, B will attempt to drop connections to the host. An Infinite Impulse Response (IIR) filter is applied to the value to prevent connection flap (see I). The default value is C<20.0>. This value must be smaller than I. =item I This key requires a floating-point value between 0 and 100. It represents (as a percentage) the high water mark for the host queue. If the host queue rises above this level while using I 2 or 3, and if less than I are open to the host, B will attempt to open further connections to the host. An Infinite Impulse Response (IIR) filter is applied to the value to prevent connection flap (see I). The default value is C<50.0>. This value must be larger than I. =item I This key requires a floating-point value between 0 and 1. It represents the filter coefficient used by the Infinite Impulse Response (IIR) filter used to implement I 2 and 3. The default value of this filter is C<0.7>, giving a time constant of 1/(1-0.7) articles. Higher values will result in slower response to queue fullness changes; lower values in faster response. =item I This key requires a positive integer value. The default value is C<20>. It defines the maximum number of articles to process at one time when using streaming to transmit to a peer. Larger numbers mean more memory consumed as articles usually get pulled into memory (see the description of I). =item I This key requires a boolean value. Its default value is true. It defines whether streaming commands are used to transmit articles to the peers. =item I This key requires a floating-point number which must be in the range [0.0, 100.0]. When running transmitting with the streaming commands, B attempts an optimization called "no-CHECK mode". This involves I asking the peer if it wants the article, but just sending it. This optimization occurs when the percentage of the articles the peer has accepted gets larger than this number. If this value is set to C<100.0>, then this effectively turns off no-CHECK mode, as the percentage can never get above 100.0. If this value is too small, then the number of articles the peer rejects will get bigger (and your bandwidth will be wasted). The default value of C<95.0> usually works pretty well. =item I This key requires a floating-point number which must be in the range [0.0, 100.0], and it must be smaller that the value for I. When running in no-CHECK mode, as described above, if the percentage of articles the remote server accepts drops below this number, then the no-CHECK optimization is turned off until the percentage gets above the I value again. If there is small difference between this and the I value (less than about 5.0), then B may frequently go in and out of no-CHECK mode. If the difference is too big, then it will make it harder to get out of no-CHECK mode when necessary (wasting bandwidth). Keeping this to between 5.0 and 10.0 less than B usually works pretty well. The default value is C<90.0>. =item I This is a floating-point value representing the time constant, in articles, over which the CHECK/no-CHECK calculations are done. The default value is C<50.0>, which will implement an Infinite Impulse Response (IIR) filter of time constant 50. This roughly equates to making a decision about the mode over the previous 50 articles. A higher number will result in a slower response to changing percentages of articles accepted; a lower number will result in a faster response. =item I This key requires a positive integer value. It defines the TCP/IP port number to use when connecting to the remote. Usually, port number 119 is used, which is the default value. =item I This key requires a boolean value. By default, it is set to false. Setting it to true is the same as setting I to C and removing I from C if it was set. =item I This key requires a boolean value. By default, it is set to false. When set to true, and a peer replies with code 431 or 436 (try again later), B just drops the article and does not try to re-send it. This is useful for some peers that keep on deferring articles for a long time to prevent B from trying to offer the same article over and over again. =item I This key requires a boolean value. By default, it is set to false. When set to true, B will attempt to use a connection with the least queue size (or the first empty connection). If this key is set to true, it is recommended that I be set to C<0>. This allows for article propagation with the least delay. =item I This key requires a boolean value. It specifies whether spooling should be enabled (false, the default) or disabled (true). Note that when I is set, articles reported as spooled are actually silently discarded. =item I This key requires a non-negative integer value. If the number is C<0> (the default), then backlog files are allowed to grow without bound when the peer is unable to keep up with the article flow. If this number is greater than 0, then it specifies the size (in bytes) the backlog file should get truncated to when the backlog file reaches a certain limit. The limit depends on whether I or I is used. This parameter also applies to the debug file when I is set to true, and has the same effect on this file as the one has on backlog files. =item I This key requires a floating-point value, which must be larger than 1.0. It is used in conjunction with the peer key I. If I has a value greater than zero, then when the backlog file gets larger than the value I * I, then the backlog file will be truncated to the size I. For example, if I has a value of C<1000000>, and I has a value of C<2.0>, then when the backlog file gets to be larger than 2000000 bytes in size, it will be truncated to 1000000 bytes. The front portion of the file is removed, and the trimming happens on line boundaries, so the final size may be a bit less than this number. If I is defined too, then I takes precedence. The default value of I is C<1.1>. This parameter also applies to the debug file when I is set to true, and has the same effect on this file as the one has on backlog files. =item I This key requires a positive integer value that must be larger than the value for I. The default value is C<0>. If the size of the backlog file gets larger than this value (in bytes), then the backlog file will be shrunk down to the size of I. If both I and I are defined, then the value of I is used. This parameter also applies to the debug file when I is set to true, and has the same effect on this file as the one has on backlog files. =item I This key requires a boolean value. By default it is set to false. When set to true, the backlog is fed before new files. This is intended to enforce in-order delivery, so setting this to true when I or I is more than 1 is inconsistent. =item I This key requires a string value. It specifies which outgoing IPv4 address B should bind the local end of its connection to. It must be an IPv4 address in dotted-quad format (nnn.nnn.nnn.nnn), C, or C. If not set or set to C, B defaults to letting the kernel choose this address. If set to C, B will not use IPv4 for outgoing connections to peers in this scope (i.e. it forces IPv6). If not set in F, B defaults to the value of I from F (which by default is unset). =item I This key requires a string value. It behaves like I except for outgoing IPv6 connections. It must be in numeric IPv6 format (note that a value containing colons must be enclosed in double quotes), C, or C. If set to C, B will not use IPv6 for outgoing connections to peers in this scope. If not set in F, B defaults to the value of I from F (which by default is unset). =item I This key requires a string value. If the value is defined, then B tries to authenticate by AUTHINFO USER and this value used for user name. I must also be defined, if this key is defined. =item I This key requires a string value. The value is the password used for AUTHINFO PASS. I must also be defined, if this key is defined. =back =head1 PEER VALUES As previously explained, the peer definitions can contain redefinitions of any of the I:I pairs described in the section about global peer defaults above. There is one I:I pair that is specific to a peer definition. =over 4 =item I This key requires a word value. The word is the host's FQDN, or the dotted quad IP-address. If this value is not specified, then the name of the peer in the enclosing I block is taken to also be its I. =back =head1 RELOADING If B gets a SIGHUP signal, then it will reread the configuration file. All values at global scope except for I can be changed (although note that I and I changes will only affect new connections). Any new peers are added and any missing peers have their connections closed. The log file is also reopened. =head1 EXAMPLE For a comprehensive example, see the sample F distributed with INN and installed as a starting point. Here are examples of how to format values: eg-string: "New\tconfig\tfile\n" eg-long-string: "A long string that goes over multiple lines. The newline is kept in the string except when quoted with a backslash \ as here." eg-simple-string: A-no-quote-string eg-integer: 10 eg-boolean: true eg-char: 'a' eg-ctrl-g: '\007' =head1 HISTORY Written by James Brister for InterNetNews. Converted to POD by Julien Elie. Earlier versions of B (up to 0.10.1) were shipped separately; B is now part of INN and shares the same version number. Please note that the F format has changed dramatically since S. $Id: innfeed.conf.pod 9923 2015-07-14 16:48:11Z iulius $ =head1 SEE ALSO inn.conf(5), innfeed(8), newsfeeds(5). =cut inn-2.6.0/doc/pod/controlchan.pod0000644000175200017520000000540312575023702016267 0ustar iuliusiulius=head1 NAME controlchan - Channel-fed control message handler =head1 SYNOPSIS B [B<-ch>] =head1 DESCRIPTION B removes the responsibility for handling control messages (except cancels) from B and instead processes them from a channel or file feed. The two Perl modules C and C are required by B. To reduce load, B keeps a copy of F and F in memory and checks permissions (including any required PGP headers) before any scripts are called. These two configuration files are automatically reloaded when B notices they have been modified. Also, the default case of an unrecognized control article is handled internally. The C case is handled with far less fuss. Normally, B is invoked by B as configured in F. An example entry is below. Make sure that the newsgroup C exists so that B does not have to scan through cancels, which it will not be processing anyway. controlchan!\ :!*,control,control.*,!control.cancel\ :AC,Tc,Wnsm\ :/controlchan B can also be manually invoked with a mere path to a file (containing a complete control article with its headers and its body) or a token on its standard input: echo '/path/to/a/control/article' | controlchan echo '@0303465234000000000000235AE000000002@' | controlchan Note that in the (very, very unlikely) event that you need to process ihave/sendme control messages, be sure that I is set to false in F, because in this case B needs a site name, not an IP address. B tries to report all log messages through syslog(3), unless connected to an interactive terminal. To enable syslog(3)'ing for versions of Perl prior to 5.6.0, you will need to have run B on your system include files at some point (this is required to make C work). If you have not done so, do this: cd /usr/include h2ph * sys/* If you run FreeBSD, you will need to run the following in addition: h2ph machine/* =head1 OPTIONS =over 4 =item B<-c> By default, B does not process articles whose Date: or Injection-Date: header fields are too far in the past (more than I days, as set in F) or one day in the future. It allows to prevent a malicious replay of old control articles. Using the B<-c> flag disables this check on the cutoff date. =item B<-h> Gives usage information. =back =head1 HISTORY Written by Katsuhiro Kondou for InterNetNews. Converted to POD by Julien Elie. $Id: controlchan.pod 9240 2011-07-12 09:51:28Z iulius $ =head1 SEE ALSO control.ctl(5), inn.conf(5). =cut inn-2.6.0/doc/pod/incoming.conf.pod0000644000175200017520000001447412575023702016514 0ustar iuliusiulius=head1 NAME incoming.conf - Configuration of incoming news feeds =head1 DESCRIPTION The file I/incoming.conf consists of three types of entries: key/value, peer and group. Comments are from the hash character C<#> to the end of the line. Blank lines are ignored. All key/value entries within each type must not be duplicated. Key/value entries are a keyword immediately followed by a colon, at least one blank and a value. For example: max-connections: 10 A legal key does not contains blanks, colons, nor C<#>. There are three different types of values: integers, booleans, and strings. Integers are as to be expected. A boolean value is either C or C (case is significant). A string value is any other sequence of characters. If the string needs to contain whitespace, then it must be quoted with double quotes. Peer entries look like: peer { # body } The word C is required. is a label for this peer. It is any string valid as a key. The body of a peer entry contains some number of key/value entries. Group entries look like: group { # body } The word C is required. is any string valid as a key. The body of a group entry contains any number of the three types of entries. So key/value pairs can be defined inside a group, and peers can be nested inside a group, and other groups can be nested inside a group. Key/value entries that are defined outside of all peer and group entries are said to be at global scope. Global key/value entries act as defaults for peers. When B looks for a specific value in a peer entry (for example, the maximum number of connections to allow), if the value is not defined in the peer entry, then the enclosing groups are examined for the entry (starting at the closest enclosing group). If there are no enclosing groups, or the enclosing groups don't define the key/value, then the value at global scope is used. A small example could be: # Global value applied to all peers that have no value of their own. max-connections: 5 # A peer definition. peer uunet { hostname: usenet1.uu.net } peer vixie { hostname: gw.home.vix.com max-connections: 10 # Override global value. } # A group of two peers which can open more connections than normal. group fast-sites { max-connections: 15 # Another peer. The max-connections: value from the # fast-sites group scope is used. peer data.ramona.vix.com { hostname: data.ramona.vix.com } peer bb.home.vix.com { hostname: bb.home.vix.com max-connections: 20 # He can really cook. } } Given the above configuration file, the defined peers would have the following values for the I key. uunet 5 vixie 10 data.ramona.vix.com 15 bb.home.vix.com 20 =head1 PARAMETERS The following keys are allowed: =over 4 =item I This key requires a string value. Reserved for future use. The default is an empty string. =item I This key requires a string value. Reserved for future use. The default is an empty string. =item I This key requires a positive integer value. It defines the hold time before closing, if the connection is over I. A value of zero specifies immediate close. The default is C<0>. =item I This key requires a string value. It is a list of hostnames separated by a comma. A hostname is the host's fully qualified domain name, or the dotted-quad IP address of the peer for IPv4, or the colon-separated IP address of the peer for IPv6. If this key is not present in a peer block, the hostname defaults to the label of the peer. =item I This key requires a string value. It is used if you wish to require a peer's user name retrieved through B match the specified string. Note that currently B does not implement any timeout in B callbacks, so enabling this option may cause B to hang if the remote peer does not respond to B callbacks in a reasonable timeframe. The default is an empty string, that is to say no B. =item I This key requires a boolean value. Setting this entry causes B to refuse every article sent via CHECK or IHAVE by this peer. The default is false. =item I This key requires a positive integer value. It defines the maximum number of connections allowed. A value of zero specifies an unlimited number of maximum connections (C or C can be used as synonyms). The default is C<0>. =item I This key requires a boolean value. It defines whether a peer is allowed to issue list command. The default is false, that is to say it can. =item I This key requires a boolean value. It defines whether B should send C<438> (response to CHECK, in streaming mode) or C<435> (response to IHAVE in non-streaming mode) responses instead of C<431> (response to CHECK) or C<436> (response to IHAVE) if a message is offered that is already received from another peer. The deferral feature can be useful for peers that resend messages right away, as B does. The default is false: the deferral feature is used so that the peer receives C<431> and C<436> codes, and therefore resends the article later. =item I This key requires a string value. It is used if you wish to require a peer to supply a password via AUTHINFO USER/PASS. The default is an empty string, that it to say no password. =item I This key requires a string value. It is a list of newsfeeds(5)-style list of newsgroups which are to be accepted from this host. The default is the string C<*>, that is to say all groups are accepted. =item I This key requires a boolean value. Setting this entry causes this peer to be skipped. The default is false. =item I This key requires a boolean value. It defines whether streaming commands (CHECK and TAKETHIS) are allowed from this peer. The default is true. =back =head1 HISTORY Written by Fabien Tassin for InterNetNews. Converted to POD by Julien Elie. $Id: incoming.conf.pod 9589 2013-12-19 17:47:33Z iulius $ =head1 SEE ALSO inn.conf(5), innd(8), newsfeeds(5), uwildmat(3). =cut inn-2.6.0/doc/pod/innmail.pod0000644000175200017520000000341212575023702015402 0ustar iuliusiulius=head1 NAME innmail - Simple mail-sending program =head1 SYNOPSIS B [B<-h>] [B<-s> I] I

[I
...] =head1 DESCRIPTION B is a Perl script intended to provide the non-interactive mail-sending functionality of mail(1) while avoiding nasty security problems. It takes the body of a mail message on standard input and sends it to the specified addresses by invoking the value of I in F. At least one address (formatted for the MTA specified in F, if it matters) is required. B will sanitize the addresses so that they contain only alphanumerics and the symbols C<@>, C<.>, C<->, C<+>, C<_>, and C<%>. B was written to be suitable for the I setting in F. =head1 OPTIONS =over 4 =item B<-h> Gives usage information. =item B<-s> I Sets the Subject: header of the message. A warning is issued if this option is omitted. =back =head1 EXAMPLES This sends a one-line message to the local user C: echo "A one-line message." | innmail -s "Simple message" joe B by default is used by INN for sending nightly reports and control message reports. =head1 BUGS B fails on addresses that begin with C<->, although one might hope that the news server will not need to contact any such addresses. There are many "correct" addresses that will be silently modified by the sanitization process. A news administrator should be careful to use particularly sane addresses if they may be passed to B. =head1 HISTORY B was written by James Brister for InterNetNews. This manual page was originally written by Jeffrey M. Vinocur. $Id: innmail.pod 7851 2008-05-26 19:33:08Z iulius $ =head1 SEE ALSO inn.conf(5), mail(1). =cut inn-2.6.0/doc/pod/convdate.pod0000644000175200017520000000541112575023702015557 0ustar iuliusiulius=head1 NAME convdate - Convert to/from S dates and seconds since epoch =head1 SYNOPSIS B [B<-dhl>] [B<-c> | B<-n> | B<-s>] [I ...] =head1 DESCRIPTION B translates the date/time strings given on the command line, outputting the results one to a line. The input can either be a date in S format (accepting the variations on that format that innd(8) is willing to accept), or the number of seconds since epoch (if B<-c> is given). The output is either ctime(3) results, the number of seconds since epoch, or a Usenet Date: header, depending on the options given. If I is not given, B outputs the current date. =head1 OPTIONS =over 4 =item B<-c> Each argument is taken to be the number of seconds since epoch (a time_t) rather than a date. =item B<-d> Output a valid Usenet Date: header instead of the results of ctime(3) for each date given on the command line. This is useful for testing the algorithm used to generate Date: headers for local posts. Normally, the date will be in UTC, but see the B<-l> option. =item B<-h> Print usage information and exit. =item B<-l> Only makes sense in combination with B<-d>. If given, Date: headers generated will use the local time zone instead of UTC. =item B<-n> Rather than outputting the results of ctime(3) or a Date: header, output each date given as the number of seconds since epoch (a time_t). This option doesn't make sense in combination with B<-d>. =item B<-s> Pass each given date to the S date parser and print the results of ctime(3) (or a Date: header if B<-d> is given). This is the default behavior. =back =head1 EXAMPLES Most of these examples are taken, with modifications from the original man page dating from 1991 and were run in the EST/EDT time zone. % convdate '10 Feb 1991 10:00:00 -0500' Sun Feb 10 10:00:00 1991 % convdate '13 Dec 91 12:00 EST' '04 May 1990 0:0:0' Fri Dec 13 12:00:00 1991 Fri May 4 00:00:00 1990 % convdate -n '10 feb 1991 10:00' '4 May 90 12:00' 666198000 641880000 % convdate -c 666198000 Sun Feb 10 10:00:00 1991 ctime(3) results are in the local time zone. Compare to: % convdate -dc 666198000 Sun, 10 Feb 1991 15:00:00 +0000 (UTC) % env TZ=PST8PDT convdate -dlc 666198000 Sun, 10 Feb 1991 07:00:00 -0800 (PST) % env TZ=EST5EDT convdate -dlc 666198000 Sun, 10 Feb 1991 10:00:00 -0500 (EST) The system library functions generally use the environment variable TZ to determine (or at least override) the local time zone. =head1 HISTORY Written by Rich $alz , rewritten and updated by Russ Allbery for the B<-d> and B<-l> flags. $Id: convdate.pod 9767 2014-12-07 21:13:43Z iulius $ =head1 SEE ALSO active.times(5). =cut inn-2.6.0/doc/pod/archive.pod0000644000175200017520000001042312575023702015374 0ustar iuliusiulius=head1 NAME archive - Usenet article archiver =head1 SYNOPSIS B [B<-cfr>] [B<-a> I] [B<-i> I] [B<-p> I] [I] =head1 DESCRIPTION B makes copies of files specified on its standard input. It is normally run either as a channel feed under B or by a script before B is run. B reads the named I file, or standard input if no file is given. The input is taken as a sequence of lines; blank lines and lines starting with a number sign (C<#>) are ignored. All other lines should specify the token of an article to archive. Every article is retrieved from a token, and the Xref: header is used to determine the target file in the archive directory. You can limit the targets taken from the Xref: header with the B<-p> option. Files are copied to a directory within the archive directory, I in F (or some other directory given with B<-a>). The default is to create a hierarchy that mimics a traditional news spool storage of the given articles; intermediate directories will be created as needed. For example, if the input token represents article 2211 in the newsgroup comp.sources.unix, B will by default store the article as: comp/sources/unix/2211 in the archive area. This can be modified with the B<-c> and B<-f> options. =head1 OPTIONS =over 4 =item B<-a> I If the B<-a> flag is given, its argument specifies the root of the archive area, instead of I in F. =item B<-c> If the B<-c> flag is given, directory names will be flattened as described under the B<-f> option. Then, additionally, all posts will be concatenated into a single file, appending to that file if it already exists. The file name will be C, formed from the current time when B is run. In other words, if given an article in comp.sources.unix on December 14th, 1998, the article would be appended to the file: comp.sources.unix/199812 in the archive area. Articles will be separated by a line containing only C<----------->. =item B<-f> If the B<-f> flag is used, directory names will be flattened, replacing the slashes with the periods. In other words, article 2211 in comp.sources.unix will be written to: comp.sources.unix/2211 in the archive area. =item B<-i> I If the B<-i> flag is used, B will append one line to the file I for each article that it archives. This line will contain the destination file name, the Message-ID: header, and the Subject: header of the message, separated by spaces. If either header is missing (normally not possible if the article was accepted by B), it will be replaced by C<< >>. The headers will be transformed using the same rules as are used to generate overview data (unfolded and then with tabs, CR, and LF replaced by spaces). =item B<-p> I Limits the targets taken from the Xref: header to the groups specified in I. I is a uwildmat(3) pattern matching newsgroups that you wish to have B handle. =item B<-r> By default, B sets its standard error to I/errlog. To suppress this redirection, use the B<-r> flag. =back =head1 RETURN VALUE If the input is exhausted, B will exit with a zero status. If an I/O error occurs, it will try to spool its input, copying it to a file. If there was no input filename, the standard input will be copied to I/archive and the program will exit. If an input filename was given, a temporary file named I.bch (if I is an absolute pathname) or I/I.bch (if the filename does not begin with a slash) is created. Once the input is copied, B will try to rename this temporary file to be the name of the input file, and then exit. =head1 EXAMPLES A typical newsfeeds(5) entry to archive most source newsgroups is as follows: source-archive!\ :!*,*sources*,!*wanted*,!*.d\ :Tc,Wn\ :/archive -f -i /INDEX Replace and with the appropriate paths. =head1 HISTORY Written by Rich $alz for InterNetNews. Converted to POD by Russ Allbery . $Id: archive.pod 9767 2014-12-07 21:13:43Z iulius $ =head1 SEE ALSO inn.conf(5), newsfeeds(5). =cut inn-2.6.0/doc/pod/ninpaths.pod0000644000175200017520000001363612575023702015610 0ustar iuliusiulius=head1 NAME ninpaths - Report Usenet Path: statistics (new inpaths) =head1 SYNOPSIS B B<-p> B<-d> I B B<-r> I B<-u> I [B<-u> I ...] B<-v> I =head1 DESCRIPTION This is an efficient and space-saving B reporting program. It works as follows: you feed it the Path: lines via an INN channel feed or some other similar method, and from time to time the program writes all its internal counters accumulated so far to a dump file. Another instance of the program picks up all the dump files, adds them up and formats them into the report. The purpose of the final report is to summarize the frequency of occurrence of sites in the Path: headers of articles. Some central sites accumulate the Path: data from many news servers running this program or one like it, and then report statistics on the most frequently seen news servers in Usenet article Path: lines. The B script can be run daily to mail the accumulated statistics to such a site and remove the old dump files. You can get a working setup by doing the following: =over 4 =item 1. Create a directory at I/path (replacing I here and in all steps that follow with the full path to your INN log directory). Do not change the name of the C subdirectory because it is used by B. =item 2. Set up a channel feed using an entry like: inpaths!:*:Tc,WP:/ninpaths -p -d /path/inpaths.%d if your version of INN supports C (2.0 and later all do). Replace with the full path to your INN binaries directory, and with the full path to your INN log directory. Note that the naming convention of the generated inpaths dump files should not be changed. B explicitly searches files whose name starts with C in the /path directory. =item 3. Run the following command to start logging these statistics: ctlinnd reload newsfeeds 'inpaths feed setup' =item 4. Enter into your news user crontab these two lines: 6 6 * * * /ctlinnd flush inpaths! 10 6 * * * /sendinpaths (the actual time doesn't matter). This will force B to generate a dump file once a day. Then, a few minutes later, B collects the dumps, makes a report, sends the collected statistics, and deletes the old dumps. Note that you can manually generate a report without mailing it, and without deleting processed dump files, with C. Another useful command is C so as to receive a copy of the e-mail sent by B and therefore make sure that everything is properly set. =item 5. In a couple of days, check that your daily statistics properly appear in L. =back =head1 OPTIONS =over 4 =item B<-d> I Save dumps in I. Any C<%d> in I will be replaced with the current system time when the dump is made. This option should be used with B<-p>. The format of these dump files is described below. =item B<-p> Read Path: lines from standard input. =item B<-r> I Generate a report for I. Generally I should be the value of I from F. =item B<-u> I Read data from I. This option can be repeated to read data from multiple dump files. =item B<-v> I Set the verbosity level of the report. Valid values for I are C<0>, C<1>, and C<2>, with C<2> being the default. =back =head1 DUMP FILE FORMAT The format of the generated dump files is: !!NINP ... !!NLREC :!,:!, ... !!NEND where times are UNIX timestamps. Then, I records follow. Each record is separated by a space or a new line, and consists of a host name I followed by a number of appearances I. The number of processed Path: header lines is I. Afterwards, I relations follow. In 3.0.x versions, the relations are separated by a space or a new line, and their syntax is C<< I!I!I >> where I and I are numbers of the site records starting at 0. In 3.1.x versions, the relations begin with a colon and are separated by either nothing or a new line. Their syntax is C<< :I!I,I >> with the same meaning as in previous versions. The count can be omitted when it is C<1>. More than two sites can be specified in the relation (C<< :I!I!I,I >>). For instance: !!NINP 3.1.1 1302944821 1302944838 5 2 1302944826 newsgate.cistron.nl 1 news.trigofacile.com 2 news.ecp.fr 2 usenet.stanford.edu 1 bleachbot 1 !!NLREC :3!2:2!1,2:4!0:0!2 !!NLEND 4 where the two processed Path: headers are: Path: news.trigofacile.com!news.ecp.fr!usenet.stanford.edu!not-for-mail Path: news.trigofacile.com!news.ecp.fr!newsgate.cistron.nl!bleachbot!not-for-mail =head1 NOTES If your INN doesn't have the C feed flag (1.5 does not, 1.6 and 1.7 do, 2.0 and later all do), use the following F entry: inpaths!:*:Tc,WH:/ginpaths where B is the following script: #!/bin/sh exec egrep '^Path: ' | /ninpaths -p -d /path/inpaths.%d replacing and as above. =head1 HISTORY This is a slightly modified version of Olaf Titz's original B program, which is posted to alt.sources and kept on his WWW archive under L. The idea and some implementation details for B come from the original B program, but most of the code has been rewritten for clarity. This program is in the public domain. $Id: ninpaths.pod 9383 2011-12-25 20:56:10Z iulius $ =head1 SEE ALSO newsfeeds(5), sendinpaths(8). =cut inn-2.6.0/doc/pod/Makefile0000644000175200017520000002005012575023702014704 0ustar iuliusiulius## $Id: Makefile 9722 2014-09-24 17:47:12Z iulius $ ## ## This Makefile contains rules to generate the files derived from POD ## source. Normal make commands at the top level of the source tree don't ## recurse into this directory. These targets are only used by ## maintainers. include ../../Makefile.global TEXT = ../../HACKING ../../INSTALL ../../NEWS ../../README ../hook-perl \ ../hook-python ../external-auth ../checklist MAN1 = ../man/convdate.1 ../man/fastrm.1 ../man/getlist.1 \ ../man/grephistory.1 ../man/inews.1 ../man/innconfval.1 \ ../man/innmail.1 ../man/pullnews.1 ../man/rnews.1 \ ../man/shlock.1 ../man/simpleftp.1 ../man/sm.1 MAN3 = ../man/libauth.3 ../man/libinnhist.3 ../man/libstorage.3 \ ../man/list.3 ../man/qio.3 \ ../man/tst.3 ../man/uwildmat.3 MAN5 = ../man/active.5 ../man/active.times.5 ../man/buffindexed.conf.5 \ ../man/control.ctl.5 ../man/cycbuff.conf.5 ../man/distrib.pats.5 \ ../man/distributions.5 ../man/expire.ctl.5 ../man/incoming.conf.5 \ ../man/inn.conf.5 ../man/innfeed.conf.5 ../man/moderators.5 \ ../man/motd.news.5 ../man/newsfeeds.5 ../man/newsgroups.5 \ ../man/newslog.5 ../man/nntpsend.ctl.5 ../man/ovdb.5 \ ../man/passwd.nntp.5 ../man/inn-radius.conf.5 ../man/readers.conf.5 \ ../man/storage.conf.5 ../man/subscriptions.5 MAN8 = ../man/actsync.8 ../man/archive.8 ../man/auth_krb5.8 \ ../man/batcher.8 ../man/buffchan.8 \ ../man/ckpasswd.8 ../man/cnfsheadconf.8 ../man/cnfsstat.8 \ ../man/controlchan.8 ../man/ctlinnd.8 ../man/cvtbatch.8 ../man/docheckgroups.8 \ ../man/domain.8 ../man/expire.8 ../man/expireover.8 \ ../man/expirerm.8 ../man/ident.8 \ ../man/innbind.8 ../man/inncheck.8 ../man/innd.8 ../man/inndf.8 \ ../man/innfeed.8 ../man/innupgrade.8 ../man/innwatch.8 \ ../man/innxmit.8 \ ../man/makedbz.8 ../man/makehistory.8 ../man/mod-active.8 \ ../man/news.daily.8 ../man/news2mail.8 ../man/ninpaths.8 \ ../man/nnrpd.8 ../man/nntpsend.8 \ ../man/ovdb_init.8 ../man/ovdb_monitor.8 ../man/ovdb_server.8 \ ../man/ovdb_stat.8 ../man/overchan.8 \ ../man/procbatch.8 ../man/prunehistory.8 ../man/radius.8 \ ../man/rc.news.8 ../man/scanlogs.8 ../man/scanspool.8 \ ../man/sendinpaths.8 \ ../man/tally.control.8 ../man/tdx-util.8 \ ../man/tinyleaf.8 ALL = $(TEXT) $(MAN1) $(MAN3) $(MAN5) $(MAN8) all bootstrap: $(ALL) clean clobber distclean: maintclean: distclean rm -f $(ALL) ../../HACKING: hacking.pod ; $(POD2TEXT) $? > $@ ../../INSTALL: install.pod ; $(POD2TEXT) $? > $@ ../../NEWS: news.pod ; $(POD2TEXT) $? > $@ ../../README: readme.pod ; $(POD2TEXT) $? > $@ ../checklist: checklist.pod ; $(POD2TEXT) $? > $@ ../external-auth: external-auth.pod ; $(POD2TEXT) $? > $@ ../hook-perl: hook-perl.pod ; $(POD2TEXT) $? > $@ ../hook-python: hook-python.pod ; $(POD2TEXT) $? > $@ ../man/convdate.1: convdate.pod ; $(POD2MAN) -s 1 $? > $@ ../man/fastrm.1: fastrm.pod ; $(POD2MAN) -s 1 $? > $@ ../man/getlist.1: getlist.pod ; $(POD2MAN) -s 1 $? > $@ ../man/grephistory.1: grephistory.pod ; $(POD2MAN) -s 1 $? > $@ ../man/inews.1: inews.pod ; $(POD2MAN) -s 1 $? > $@ ../man/innconfval.1: innconfval.pod ; $(POD2MAN) -s 1 $? > $@ ../man/innmail.1: innmail.pod ; $(POD2MAN) -s 1 $? > $@ ../man/pullnews.1: pullnews.pod ; $(POD2MAN) -s 1 $? > $@ ../man/rnews.1: rnews.pod ; $(POD2MAN) -s 1 $? > $@ ../man/shlock.1: shlock.pod ; $(POD2MAN) -s 1 $? > $@ ../man/simpleftp.1: simpleftp.pod ; $(POD2MAN) -s 1 $? > $@ ../man/sm.1: sm.pod ; $(POD2MAN) -s 1 $? > $@ ../man/libauth.3: libauth.pod ; $(POD2MAN) -s 3 $? > $@ ../man/libinnhist.3: libinnhist.pod ; $(POD2MAN) -s 3 $? > $@ ../man/libstorage.3: libstorage.pod ; $(POD2MAN) -s 3 $? > $@ ../man/list.3: list.pod ; $(POD2MAN) -s 3 $? > $@ ../man/qio.3: qio.pod ; $(POD2MAN) -s 3 $? > $@ ../man/tst.3: tst.pod ; $(POD2MAN) -s 3 $? > $@ ../man/uwildmat.3: uwildmat.pod ; $(POD2MAN) -s 3 $? > $@ ../man/active.5: active.pod ; $(POD2MAN) -s 5 $? > $@ ../man/active.times.5: active.times.pod ; $(POD2MAN) -s 5 $? > $@ ../man/buffindexed.conf.5: buffindexed.conf.pod ; $(POD2MAN) -s 5 $? > $@ ../man/control.ctl.5: control.ctl.pod ; $(POD2MAN) -s 5 $? > $@ ../man/cycbuff.conf.5: cycbuff.conf.pod ; $(POD2MAN) -s 5 $? > $@ ../man/distrib.pats.5: distrib.pats.pod ; $(POD2MAN) -s 5 $? > $@ ../man/distributions.5: distributions.pod ; $(POD2MAN) -s 5 $? > $@ ../man/expire.ctl.5: expire.ctl.pod ; $(POD2MAN) -s 5 $? > $@ ../man/incoming.conf.5: incoming.conf.pod ; $(POD2MAN) -s 5 $? > $@ ../man/inn.conf.5: inn.conf.pod ; $(POD2MAN) -s 5 $? > $@ ../man/innfeed.conf.5: innfeed.conf.pod ; $(POD2MAN) -s 5 $? > $@ ../man/moderators.5: moderators.pod ; $(POD2MAN) -s 5 $? > $@ ../man/motd.news.5: motd.news.pod ; $(POD2MAN) -s 5 $? > $@ ../man/newsfeeds.5: newsfeeds.pod ; $(POD2MAN) -s 5 $? > $@ ../man/newsgroups.5: newsgroups.pod ; $(POD2MAN) -s 5 $? > $@ ../man/newslog.5: newslog.pod ; $(POD2MAN) -s 5 $? > $@ ../man/nntpsend.ctl.5: nntpsend.ctl.pod ; $(POD2MAN) -s 5 $? > $@ ../man/ovdb.5: ovdb.pod ; $(POD2MAN) -s 5 $? > $@ ../man/passwd.nntp.5: passwd.nntp.pod ; $(POD2MAN) -s 5 $? > $@ ../man/inn-radius.conf.5: inn-radius.conf.pod ; $(POD2MAN) -s 5 $? > $@ ../man/readers.conf.5: readers.conf.pod ; $(POD2MAN) -s 5 $? > $@ ../man/storage.conf.5: storage.conf.pod ; $(POD2MAN) -s 5 $? > $@ ../man/subscriptions.5: subscriptions.pod ; $(POD2MAN) -s 5 $? > $@ ../man/actsync.8: actsync.pod ; $(POD2MAN) -s 8 $? > $@ ../man/archive.8: archive.pod ; $(POD2MAN) -s 8 $? > $@ ../man/auth_krb5.8: auth_krb5.pod ; $(POD2MAN) -s 8 $? > $@ ../man/batcher.8: batcher.pod ; $(POD2MAN) -s 8 $? > $@ ../man/buffchan.8: buffchan.pod ; $(POD2MAN) -s 8 $? > $@ ../man/ckpasswd.8: ckpasswd.pod ; $(POD2MAN) -s 8 $? > $@ ../man/cnfsheadconf.8: cnfsheadconf.pod ; $(POD2MAN) -s 8 $? > $@ ../man/cnfsstat.8: cnfsstat.pod ; $(POD2MAN) -s 8 $? > $@ ../man/controlchan.8: controlchan.pod ; $(POD2MAN) -s 8 $? > $@ ../man/ctlinnd.8: ctlinnd.pod ; $(POD2MAN) -s 8 $? > $@ ../man/cvtbatch.8: cvtbatch.pod ; $(POD2MAN) -s 8 $? > $@ ../man/docheckgroups.8: docheckgroups.pod ; $(POD2MAN) -s 8 $? > $@ ../man/domain.8: domain.pod ; $(POD2MAN) -s 8 $? > $@ ../man/expire.8: expire.pod ; $(POD2MAN) -s 8 $? > $@ ../man/expireover.8: expireover.pod ; $(POD2MAN) -s 8 $? > $@ ../man/expirerm.8: expirerm.pod ; $(POD2MAN) -s 8 $? > $@ ../man/ident.8: ident.pod ; $(POD2MAN) -s 8 $? > $@ ../man/innbind.8: innbind.pod ; $(POD2MAN) -s 8 $? > $@ ../man/inncheck.8: inncheck.pod ; $(POD2MAN) -s 8 $? > $@ ../man/innd.8: innd.pod ; $(POD2MAN) -s 8 $? > $@ ../man/inndf.8: inndf.pod ; $(POD2MAN) -s 8 $? > $@ ../man/innfeed.8: innfeed.pod ; $(POD2MAN) -s 8 $? > $@ ../man/innupgrade.8: innupgrade.pod ; $(POD2MAN) -s 8 $? > $@ ../man/innwatch.8: innwatch.pod ; $(POD2MAN) -s 8 $? > $@ ../man/innxmit.8: innxmit.pod ; $(POD2MAN) -s 8 $? > $@ ../man/makedbz.8: makedbz.pod ; $(POD2MAN) -s 8 $? > $@ ../man/makehistory.8: makehistory.pod ; $(POD2MAN) -s 8 $? > $@ ../man/mod-active.8: mod-active.pod ; $(POD2MAN) -s 8 $? > $@ ../man/news.daily.8: news.daily.pod ; $(POD2MAN) -s 8 $? > $@ ../man/news2mail.8: news2mail.pod ; $(POD2MAN) -s 8 $? > $@ ../man/ninpaths.8: ninpaths.pod ; $(POD2MAN) -s 8 $? > $@ ../man/nnrpd.8: nnrpd.pod ; $(POD2MAN) -s 8 $? > $@ ../man/nntpsend.8: nntpsend.pod ; $(POD2MAN) -s 8 $? > $@ ../man/ovdb_init.8: ovdb_init.pod ; $(POD2MAN) -s 8 $? > $@ ../man/ovdb_monitor.8: ovdb_monitor.pod ; $(POD2MAN) -s 8 $? > $@ ../man/ovdb_server.8: ovdb_server.pod ; $(POD2MAN) -s 8 $? > $@ ../man/ovdb_stat.8: ovdb_stat.pod ; $(POD2MAN) -s 8 $? > $@ ../man/overchan.8: overchan.pod ; $(POD2MAN) -s 8 $? > $@ ../man/procbatch.8: procbatch.pod ; $(POD2MAN) -s 8 $? > $@ ../man/prunehistory.8: prunehistory.pod ; $(POD2MAN) -s 8 $? > $@ ../man/radius.8: radius.pod ; $(POD2MAN) -s 8 $? > $@ ../man/rc.news.8: rc.news.pod ; $(POD2MAN) -s 8 $? > $@ ../man/scanlogs.8: scanlogs.pod ; $(POD2MAN) -s 8 $? > $@ ../man/scanspool.8: scanspool.pod ; $(POD2MAN) -s 8 $? > $@ ../man/sendinpaths.8: sendinpaths.pod ; $(POD2MAN) -s 8 $? > $@ ../man/tally.control.8: tally.control.pod ; $(POD2MAN) -s 8 $? > $@ ../man/tdx-util.8: tdx-util.pod ; $(POD2MAN) -s 8 $? > $@ ../man/tinyleaf.8: tinyleaf.pod ; $(POD2MAN) -s 8 $? > $@ inn-2.6.0/doc/pod/shlock.pod0000644000175200017520000000437212575023702015244 0ustar iuliusiulius=head1 NAME shlock - Create lock files for use in shell scripts =head1 SYNOPSIS B [B<-b>|B<-c>|B<-u>] B<-f> I B<-p> I =head1 DESCRIPTION B tries to create a lock file named I and write the process ID I into it. If the file already exists, B will read the process ID from the file and test to see whether the process is currently running. If the process exists, then the file will not be created. I exits with a zero status if it could create the lock file, or non-zero if the file refers to a currently active process. A Perl wrapper around B can be used via the C module. =head1 OPTIONS =over 4 =item B<-b> Process IDs are normally read and written in ASCII. If the B<-b> flag is used, then they will be written as a binary int. =item B<-c> If the B<-c> flag is used, then B will not create a lock file, but will instead use the file to see if the lock is held by another program. If the lock is valid, the program will exit with a non-zero status; if the lock is not valid (i.e. invoking B without the flag would have succeeded), then the program will exit with a zero status. =item B<-f> I I is the name of the lock file B attempts to create. If the file already exists, it will read the process ID from the file and exit with a non-zero status if this process is currently active. =item B<-p> I I is the process ID to write into the file I. =item B<-u> For compatibility with other systems, the B<-u> flag is accepted as a synonym for B<-b> since binary locks are used by many UUCP packages. =back =head1 EXAMPLES The following example shows how B would be used within a shell script: LOCK=/LOCK.send trap 'rm -f ${LOCK} ; exit 1' 1 2 3 15 if shlock -p $$ -f ${LOCK} ; then # Do appropriate work. else echo "Locked by `cat ${LOCK}`" fi =head1 HISTORY Written by Rich $alz for InterNetNews after a description of HDB UUCP locking given by Peter Honeyman, and improved by Berend Reitsma to solve a race condition. Converted to POD by Julien Elie. $Id: shlock.pod 9303 2011-08-04 22:09:57Z iulius $ =head1 SEE ALSO INN::Utils::Shlock(3pm). =cut inn-2.6.0/doc/pod/scanlogs.pod0000644000175200017520000000411412575023702015564 0ustar iuliusiulius=head1 NAME scanlogs - Summarize and rotate INN log files =head1 SYNOPSIS B [B] =head1 DESCRIPTION B summarizes the information recorded in the INN log files which reside in the I directory set in F (see newslog(5) for further details about these log files). It is normally invoked by the news.daily(8) script which performs daily server maintenance tasks. It invokes C to close the news and error log files, rename them to add C<.old> to the file names and open fresh news and error logs; the F file is also flushed to disk, along with the history database. By default, B rotates and cleans out the logs. It keeps up to I old compressed log files in I/OLD (the I parameter can be set in F). B also keeps archives of the F file in this directory. It invokes B if F or F exists in I (see the F entry of newslog(5) for more information about that). B displays the first 50 lines of F, F and F, if non-empty, and runs B to summarize the contents of F and F, and to update the F file amongst other things (see more information about that in innreport(8)). =head1 OPTIONS Only one option is currently accepted: =over 4 =item B Using this option disables the rotating and cleaning aspect of the log processing: the logs files are only scanned for information and no contents are altered. If B is invoked more than once a day, the B option should be used to prevent premature log cleaning. =back =head1 FILES See newslog(5) for the list of log files processed by B. =head1 HISTORY Written by Landon Curt Noll and Rich $alz for InterNetNews. Converted to POD by Julien Elie. $Id: scanlogs.pod 9903 2015-06-20 17:20:46Z iulius $ =head1 SEE ALSO inn.conf(5), innreport(8), news.daily(8), newslog(5), shlock(1), tally.control(8). =cut inn-2.6.0/doc/pod/expireover.pod0000644000175200017520000001403612575023702016147 0ustar iuliusiulius=head1 NAME expireover - Expire entries from the news overview database =head1 SYNOPSIS B [B<-ekNpqs>] [B<-f> I] [B<-w> I] [B<-z> I] [B<-Z> I] =head1 DESCRIPTION B expires old entries from the news overview database. It reads in a list of newsgroups (by default from I/active, but a different file can be specified with the B<-f> option) and then removes from the overview database mentions of any articles that no longer exist in the news spool. If I in F is true, B also removes old articles from the news spool according to the expiration rules in F. Otherwise it only removes overview entries for articles that have already been removed by some other process, and B<-e>, B<-k>, B<-N>, B<-p>, B<-q>, B<-w>, and B<-z> are all ignored. When I is set, the default behavior of B is to remove the article from the spool once it expires out of all of the newsgroups to which it was crossposted. The article is, however, removed from the overview database of each newsgroup as soon as it expires out of that individual newsgroup. The effect is that an article crossposted to several groups will be removed from the overview database from each group one-by-one as its age passes the expiration threshold for that group as set in F, and then when it expires out of the last newsgroup, it will be deleted from the news spool. Articles that are stored in self-expiring storage backends such as CNFS are normally treated differently and not expired until they expire out of the backend regardless of F. See B<-N>, however. By default, B purges all overview information for newsgroups that have been removed from the server; this behavior is suppressed if B<-f> is given. =head1 OPTIONS =over 4 =item B<-e> Remove articles from the news spool and all overview databases as soon as they expire out of any newsgroup to which they are posted, rather than retain them until they expire out of all newsgroups. B<-e> and B<-k> cannot be used at the same time. This flag is ignored if I is false. =item B<-f> I Use I as the newsgroup list instead of I/active. I can be C<-> to indicate standard input. Using this flag suppresses the normal purge of all overview information from newsgroups that have been removed from the server. =item B<-k> Retain all overview information for an article, as well as the article itself, until it expires out of all newsgroups to which it was posted. This can cause articles to stick around in a newsgroup for longer than the F rules indicate, when they're crossposted. B<-e> and B<-k> cannot be used at the same time. This flag is ignored if I is false. =item B<-N> Apply F rules to expire articles even from storage methods that have self-expire functionality. This may remove articles from self-expiring storage methods before the articles "naturally" expire. This flag is ignored if I is false. =item B<-p> By default, B bases decisions on whether to remove an article on the arrival time on the server. This means that articles may be kept a little longer than if the decision were based on the article's posting date. If this option is given, expiration decisions are based on the article posting date instead. This flag is ignored if I is false. =item B<-q> B normally prints statistics at the end of the expiration process. B<-q> suppresses this report. This flag is ignored if I is false. =item B<-s> B normally only checks the existence of articles in the news spool if querying the storage method for that article to see if it still exists is considered "inexpensive". To always check the existence of all articles regardless of how resource-intensive this may be, use the B<-s> flag. See storage.conf(5) for more information about this metric. =item B<-w> I "Warps" time so that B thinks that it's running at some time other than the current time. This is occasionally useful to force groups to be expired or not expired without changing F for the expire run. I should be a signed floating point number specifying the number of days difference from the current time to use as "now". This flag is ignored if I is false. =item B<-z> I Don't remove articles immediately but instead write the path to the article or the token of the article to I, which is suitable input for fastrm(1). This can substantially speed up deletion of expired articles for those storage methods where each article is a single file (such as tradspool and timehash). See the description of the I keyword in news.daily(8) for more details. This flag is ignored if I is false. =item B<-Z> I Write the lowest article numbers for each newsgroup as it's expired to the specified file. This file is then suitable for C. See ctlinnd(8) for more information. =back =head1 EXAMPLES Normally B is invoked from news.daily(8), which handles such things as processing the I and I if necessary. Sometimes it's convenient to manually expire a particular newsgroup, however. This can be done with a command like: echo example.test | expireover -f - -Z /lowmark ctlinnd lowmark /lowmark This can be particularly useful if a lot of articles in a particular group have expired but the overview information is still present, causing some clients to see a lot of "this article may have been cancelled" messages when they first enter the newsgroup. =head1 HISTORY Written by Rob Robertson and Rich $alz (with help from Dave Lawrence ) for InterNetNews. $Id: expireover.pod 8571 2009-08-17 19:10:07Z iulius $ =head1 SEE ALSO active(5), ctlinnd(8), expire(8), expire.ctl(5), inn.conf(5), news.daily(8). =cut inn-2.6.0/doc/pod/auth_krb5.pod0000644000175200017520000000623612575023702015646 0ustar iuliusiulius=head1 NAME auth_krb5 - nnrpd S authenticator =head1 SYNOPSIS B [B<-i> I] =head1 DESCRIPTION This program does authentication for B against a S. This is NOT real Kerberos authentication using service tickets; instead, a username and password is used to attempt to obtain a S to confirm that they are valid. As such, this authenticator assumes that B has been given the user's username and password, and therefore is not as secure as real Kerberos authentication. It generally should only be used with NNTP over TLS to protect the password from sniffing. Normally, you do not want to use this authenticator. Instead, use B with PAM support and configure the B PAM stack to use a Kerberos PAM module. A full Kerberos PAM module is more sophisticated about how it validates passwords and has a much broader array of options than this authenticator. =head1 OPTIONS =over 4 =item B<-i> I If this option is given, I will be used as the instance of the principal received from B and authentication will be done against that principal instead of the base principal. In other words, a principal like C, when passed to B invoked with C<-i nntp>, will be transformed into C before attempting Kerberos authentication. Since giving one's password to B is not as secure as normal Kerberos authentication, this option supports a configuration where all users are given a separate instance just for news authentication with its own password, so their regular account password isn't exposed via NNTP. =back =head1 EXAMPLE The following readers.conf(5) fragment tells nnrpd to authenticate users by attempting to obtain S for them, appending an instance of C to usernames before doing so: auth kerberos { auth: "auth_krb5 -i nntp" } access kerberos { users: "*/nntp" newsgroups: example.* } Access is granted to the example.* groups for all users who successfully authenticate. =head1 BUGS Currently, any username containing realm information (containing C<@>) is rejected. This is to prevent someone from passing in a username corresponding to a principal in another realm that they have access to and gaining access to the news server via it. However, this is also something that people may wish to do under some circumstances, so there should be a better way of handling it (such as, perhaps, a list of acceptable realms or a B<-r> flag specifying the realm in which to attempt authentication). It's not clear the right thing to do when the username passed in contains a C and B<-i> was also given. Right now, B will create a malformed Kerberos principal with multiple instances and attempt to authenticate against it, which will fail but perhaps not with the best error message. =head1 HISTORY Originally written by Christopher S. This documentation was written by Russ Allbery based on Christopher's original F file. $Id: auth_krb5.pod 9774 2015-01-04 21:50:10Z eagle $ =head1 SEE ALSO ckpasswd(8), nnrpd(8), readers.conf(5). =cut inn-2.6.0/doc/pod/ovdb_init.pod0000644000175200017520000000610612575023702015733 0ustar iuliusiulius=head1 NAME ovdb_init - Prepare ovdb database for use =head1 SYNOPSIS ovdb_init [C<-u>|C<-r>] =head1 DESCRIPTION This command must be run before any other process can access the overview database. It performs the following steps: =over 4 =item 1 Creates the database environment, if necessary =item 2 If the database is idle (and if the C<-u> option is not specified), it performs a normal recovery. The recovery will remove stale locks, recreate the memory pool cache, and repair any damage caused by a system crash or improper shutdown. =item 3 If the C<-u> option is specified, it performs any necessary upgrades to the database. See the UPGRADING section below. =item 4 Starts the DB housekeeping processes (ovdb_monitor) if they're not already running. (Unless the C<-r> option is specified). =item 5 Starts the ovdb readserver (ovdb_server) processes if I in F is true, and if they are not already running. (Unless the C<-r> option is specified). =back Returns exit status of 0 if all steps were completed successfully. In the event of an error, messages are written to syslog and/or stderr. If a recovery was attempted but it failed, the database may be damaged beyond repair, requiring a rebuild with makehistory(8). This command is normally invoked automatically by rc.news(8). It is OK to run this command multiple times. =head1 OPTIONS =over 4 =item C<-r> Perform recovery only. C is not started. =item C<-u> Perform any needed upgrades. Recovery is not attempted. C is started if the upgrade succeeded. =back =head1 UPGRADING There are two situations in which the database will need to be upgraded: =over 4 =item * You upgrade the S library to a newer version, for example from 2.7.7 to 3.1.17. In this case, the S db->upgrade() method is used. =item * You upgrade ovdb to a newer major version; i.e., ovdb-1.0 to ovdb-2.0. =back In both of these cases, the database is upgraded in-place; and the upgrade can not be undone. Do not interrupt the upgrade process once it has started, because there is a risk of irrepairable corruption. The upgrade may take several minutes to complete. If an upgrade does get interrupted, try running the upgrade again. Here's an example procedure to upgrade a database created with S to use S: =over 4 =item 1 Build and install the S; =item 2 Run configure in the INN source tree and make sure it picks up the right S directory (e.g., F); =item 3 Do a C; =item 4 Shut down INN (e.g., with C) and be sure to kill all instances of B as well; =item 5 Do a C to install the new binaries; =item 6 Run C as the news user; =item 7 Start INN with C. =back It is OK to specify C<-u> even if no upgrades are needed. =head1 HISTORY Written by Heath Kehoe Ehakehoe@avalon.netE for InterNetNews. $Id: ovdb_init.pod 9765 2014-12-07 21:07:34Z iulius $ =head1 SEE ALSO ovdb(5), makehistory(8) =cut inn-2.6.0/doc/pod/ckpasswd.pod0000644000175200017520000001653312575023702015602 0ustar iuliusiulius=head1 NAME ckpasswd - nnrpd password authenticator =head1 SYNOPSIS B [B<-gs>] [B<-d> I] [B<-f> I] [B<-u> I B<-p> I] =head1 DESCRIPTION B is the basic password authenticator for B, suitable for being run from an auth stanza in F. See readers.conf(5) for more information on how to configure an B authenticator. B accepts a username and password from B and tells nnrpd(8) whether that's the correct password for that username. By default, when given no arguments, it tries to check the password using PAM if support for PAM was found when INN was built. Failing that, it tries to check the password against the password field returned by getpwnam(3). Note that these days most systems no longer make real passwords available via getpwnam(3) (some still do if and only if the program calling getpwnam(3) is running as root). When using PAM, B identifies itself as C, not as C, and the PAM configuration must be set up accordingly. The details of PAM configuration are different on different operating systems (and even different Linux distributions); see L below for help getting started, and look for a pam(7) or pam.conf(4) manual page on your system. When using any method other than PAM, B expects all passwords to be stored encrypted by the system crypt(3) function and calls crypt(3) on the supplied password before comparing it to the expected password. If you're using a different password hash scheme (like MD5), you must use PAM. =head1 OPTIONS =over 4 =item B<-d> I Read passwords from a database (ndbm, gdbm or dbm format depending on what your system has) rather than by using getpwnam(3). B expects I.dir and I.pag to exist and to be a database keyed by username with the encrypted passwords as the values. While INN doesn't come with a program intended specifically to create such databases, on most systems it's fairly easy to write a Perl script to do so. Something like: #!/usr/bin/perl use NDBM_File; use Fcntl; tie (%db, 'NDBM_File', '/path/to/database', O_RDWR|O_CREAT, 0640) or die "Cannot open /path/to/database: $!\n"; $| = 1; print "Username: "; my $user = ; chomp $user; print "Password: "; my $passwd = ; chomp $passwd; my @alphabet = ('.', '/', 0..9, 'A'..'Z', 'a'..'z'); my $salt = join '', @alphabet[rand 64, rand 64]; $db{$user} = crypt ($passwd, $salt); untie %db; Note that this will echo back the password when typed; there are obvious improvements that could be made to this, but it should be a reasonable start. Sometimes a program like this will be available with the name B. This option will not be available on systems without ndbm, gdbm or dbm libraries. =item B<-f> I Read passwords from the given file rather than using getpwnam(3). The file is expected to be formatted like a system password file, at least vaguely. That means each line should look something like: username:pdIh9NCNslkq6 (and each line may have an additional colon after the encrypted password and additional data; that data will be ignored by B). Lines starting with a number sign (C<#>) are ignored. INN does not come with a utility to create the encrypted passwords, but B (which comes with Apache) can do so and it's a quick job with Perl (see the example script under B<-d>, or also below). If using Apache's B program, be sure to give it the B<-d> option so that it will use crypt(3). A line in I for the user C with the password C would be C as obtained by the following command: % htpasswd -nbd user pass user:LIfOpbjNaEQYE In case B is not installed or if you do not want to depend on it, another command involving Perl does a similar job: % perl -e 'print "user:".crypt("pass", "LI")."\n";' user:LIfOpbjNaEQYE =item B<-g> Attempt to look up system group corresponding to username and return a string like C to be matched against in F. This option is incompatible with the B<-d> and B<-f> options. =item B<-p> I Use I as the password for authentication rather than reading a password using the B authenticator protocol. This option is useful only for testing your authentication system (particularly since it involves putting a password on the command line), and does not work when B is run by B. If this option is given, B<-u> must also be given. =item B<-s> Check passwords against the result of getspnam(3) instead of getpwnam(3). This function, on those systems that supports it, reads from F or similar more restricted files. If you want to check passwords supplied to nnrpd(8) against system account passwords, you will probably have to use this option on most systems. Most systems require special privileges to call getspnam(3), so in order to use this option you may need to make B setgid to some group (like group C) or even setuid root. B has not been specifically audited for such uses! It is, however, a very small program that you should be able to check by hand for security. This configuration is not recommended if it can be avoided, for serious security reasons. See "SECURITY CONSIDERATIONS" in readers.conf(5) for discussion. =item B<-u> I Authenticate as I. This option is useful only for testing (so that you can test your authentication system easily) and does not work when B is run by B. If this option is given, B<-p> must also be given. =back =head1 EXAMPLES See readers.conf(5) for examples of nnrpd(8) authentication configuration that uses B to check passwords. An example PAM configuration for F that tells B to check usernames and passwords against system accounts is: nnrpd auth required pam_unix.so nnrpd account required pam_unix.so Your system may want you to instead create a file named F in F with lines like: auth required pam_unix.so account required pam_unix.so This is only the simplest configuration. You may be able to include common shared files, and you may want to stack other modules, either to allow different authentication methods or to apply restrictions like lists of users who can't authenticate using B. The best guide is the documentation for your system and the other PAM configurations you're already using. To test to make sure that B is working correctly, you can run it manually and then give it the username (prefixed with C) and password (prefixed with C) on standard input. For example: (echo 'ClientAuthname: test' ; echo 'ClientPassword: testing') \ | ckpasswd -f /path/to/passwd/file will check a username of C and a password of C against the username and passwords stored in F. On success, B will print C and exit with status C<0>. On failure, it will print some sort of error message and exit a non-zero status. =head1 HISTORY Written by Russ Allbery for InterNetNews. $Id: ckpasswd.pod 9937 2015-09-02 12:44:39Z iulius $ =head1 SEE ALSO crypt(3), nnrpd(8), pam(7), readers.conf(5). =cut inn-2.6.0/doc/pod/libinnhist.pod0000644000175200017520000003123512575023702016122 0ustar iuliusiulius=head1 NAME his - routines for managing INN history =head1 SYNOPSIS #include struct history; struct token; struct histstats { int hitpos; int hitneg; int misses; int dne; }; #define HIS_RDONLY ... #define HIS_RDWR ... #define HIS_CREAT ... #define HIS_ONDISK ... #define HIS_INCORE ... #define HIS_MMAP ... enum { HISCTLG_PATH, HISCTLS_PATH, HISCTLS_SYNCCOUNT, HISCTLS_NPAIRS, HISCTLS_IGNOREOLD, HISCTLS_STATINTERVAL }; struct history *HISopen(const char *path, const char *method, int flags); bool HISclose(struct history *history); bool HISsync(struct history *history); void HISsetcache(struct history *history, size_t size); bool HISlookup(struct history *history, const char *key, time_t *arrived, time_t *posted, time_t *expires, TOKEN *token); bool HIScheck(struct history *history, const char *key); bool HISwrite(struct history *history, const char *key, time_t arrived, time_t posted, time_t expires, const TOKEN *token); bool HISremember(struct history *history, const char *key, time_t arrived, time_t posted); bool HISreplace(struct history *history, const char *key, time_t arrived, time_t posted, time_t expires, const TOKEN *token); bool HISexpire(struct history *history, const char *path, const char *reason, bool writing, void *cookie, time_t threshold, bool (*exists)(void *cookie, time_t arrived, time_t posted, time_t expires, const TOKEN *token)); bool HISwalk(struct history *history, const char *reason, void *cookie, bool (*callback)(void *cookie, time_t arrived, time_t posted, time_t expires, const TOKEN *token)); struct histstats HISstats(struct history *history); const char *HISerror(struct history *history); bool HISctl(struct history *history, int request, void *val); =head1 DESCRIPTION These functions provide access to the INN history database. They maintain key/value pairs in an opaque database whilst providing for expiry of outdated information. The history structure is an opaque handle returned from HISopen. The B function opens the history file designated by I using the mode I using the specified I. I may be B to indicate that read-only access to the history database is desired, or B for read/write access. History methods are defined at build time; the history method currently available is "hisv6". On success a newly initialised history handle is returned, or B on failure. B, B and B may be logically ORed into I to provide a hint to the underlying history manager as to how it should handle its data files; B indicates that the caller would like as much of the data to be kept on disk (and out of memory), B indicates that the data files should be kept in main memory where possible and B that the files should be mmap()ed into the processes address space. B is typically used where a mass rebuild of the history database is being performed; the underlying history manager may assume that the caller will call B() to sync the data files to disk. The B flag indicates that the history database should be initialised as new; if any options which affect creation of the database need to be set an anonymous history handle should be created by calling B with I set to B, any options set using B, then the database opened by calling B with B. The B function closes the handle I and deallocates any resources associated with it. It returns B on failure or B on success. The B function synchronises any outstanding transactions associated with I to disk. B associates a cache used for speeding up HIScheck with I. The cache will occupy approximately I bytes. B retrieves a token from I based on the passed I (normally the Message-ID). If no entry with an associated token can be found, B will return B. If a token is found I, I, and I are filled in with the message arrival, expiry, and posting times respectively (or zero, if the time component is not available), in addition to I being set to the retrieved token and a function return value of B. Any of arrived, expires, posted, or token may be B in which case that component is not returned to the caller, without affecting the return value. B checks the database I for I (normally the Message-ID); if I has previously been set via B, B returns B, else B. B writes a new entry to the database I associated with I. I, I, and I specify the arrival, posting, and expiry time respectively; I and I may be specifed as <= 0 in which case that component shall be treated as absent in the database. I is associated with the specified I. B returns B on success, or B on failure. The behaviour when I is not unique with respect to the existing entries in I is unspecified. B writes a new entry to the database I associated with I, merely remembering that this I has been seen, together with its arrival time I and also its posting time I, if known. (Otherwise, its posting time may be specified as <= 0 in case it is absent.) B returns B on success, or B on failure. The behaviour when I is not unique with respect to the existing entries in I is unspecified. B replaces an existing entry in the database I, associated with I. I, I, I specify the arrival, posting and expiry time respectively; I and I may be specifed as <= 0 in which case that component shall be treated as absent in the database. I is associated with the specified I; if B then the history database merely remembers that this I has been seen, together with its arrival time. B returns B on success, or B on failure. B expires the history database associated with I, creating a new, replacement, database in the same location if I is B, or in I if not B; if I is not B then the replacement of the old history database with the new one is assumed to be performed out of band by the caller. The I flag is normally passed as B, if you wish to inhibit writing of the new database (and so merely see the callbacks), I may be set B. If the underlying history mechanism needs to pause the server, the I string is used as the argument to the `ctlinnd pause' command, and as such the server should be reserved by the caller prior to calling B; if the caller wishes to inhibit pausing of the server, passing B will achieve this. If I is not B, then on successful return from B the server will be left paused and the caller should unpause it. The history database is scanned and entries with an associated storage token are passed to the discrimination function I. If I() returns B it indicates that stored entity associated with token is no longer available (or no longer required), and therefore the associated history entry may be expired once it meets the I constraint. If I() returns B the entry is kept as-is in the newly expired history database. The I function is passed the arrival, posting and expiry times, in addition to the token associated with the entry. Note that posting and/or expiry may be zero, but that the token will never be B (such entries are handled solely via the threshold mechanism). The storage token passed to the discrimination function may be updated if required (for example, as might be needed by a hierachical storage management implementation). Entries in the database with a posting time less than I with no token associated with them are deleted from the database. In case the posting time is unknown, the arrival time is used instead. The parameter I is passed to the discrimination function, and may be used for any purpose required by the caller. If the discrimination function attempts to access the underlying database (for read or write) during the callback, the behaviour is unspecified. B provides an iteration function for the specified I database. For every entry in the history database, I is invoked, passing the I, arrival, posting, and expiry times, in addition to the token associated with the entry. If the I() returns B the iteration is aborted and B returns B to the caller. To process the entire database in the presence of a running server, I may be passed; if this argument is not B, it is used as an an argument to the `ctlinnd (reserve|pause|go)' commands. If I is B and the server is running, the behaviour of B is undefined. If the callback function attempts to access the underlying database during the callback, the behaviour is unspecified. B returns statistics on the history cache mechanism; given a handle I, the return value is a I detailing: =over 4 =item C The number of times an item was found directly in the cache and known to exist in the underlying history manager. =item C The number of times an item was found directly in the cache and known not to exist in the underlying history manager. =item C The number of times an item was not found directly in the cache, but on retrieval from the underlying history manager was found to exist. =item C The number of times an item was not found directly in the cache, but on retrieval from the underlying history manager was found not to exist. =back Note that the history cache is only checked by B and only affected by B, B, B and B. Following a call to B the history statistics associated with I are cleared. B returns a string describing the most recent error associated with I; the format and content of these strings is history manager dependent. Note that on setting an error, the history API will call the B function from libinn(3). B provides a control interface to the underlying history manager. The I argument determines the type of the request and the meaning of the I argument. The values for I are: =over 4 =item C (const char **) Get the base file path which the history handle represents. I should be a pointer to a location of type B. The result must not later be passed to free(3). =item C (const char *) Set the base file path which this history handle should use; typically this is used after an anonymous handle has been created using B. I should be a value of type B and will be copied before being stored internally. =item C (size_t *) Set an upper bound on how many history operations may be pending in core before being synced to permanent storage; B<0> indicates unlimited. I should be a pointer to a value of type B and will not be modified by the call. =item C (size_t *) Set a hint to the to the underlying history manager as to how many entries there are expected to be in the history database; B<0> indicates that an automatic or default sizing should be made. I should be a pointer to a value of type B and will not be modified by the call. =item C (bool *) Instruct the underlying history manager to ignore existing database when creating new ones; typically this option may be set to B if the administrator believes that the existing history database is corrupt and that ignoring it may help. I should be a pointer to a value of type B and will not be modified by the call. =item C (time_t *) For the history v6 and tagged hash managers, set the interval, in seconds, between stat(2)s of the history files checking for replaced files (as happens during expire); this option is typically used by nnrpd(8) like applications. I should be a pointer to a value of type B and will not be modified by the call. =back =head1 HISTORY Written by Alex Kiernan for S. $Id: libinnhist.pod 9073 2010-05-31 19:00:23Z iulius $ =cut inn-2.6.0/doc/pod/radius.pod0000644000175200017520000000402212575023702015240 0ustar iuliusiulius=head1 NAME radius - nnrpd RADIUS password authenticator =head1 SYNOPSIS B [B<-h>] [B<-f> I] =head1 DESCRIPTION B is an B authenticator, accepting a username and password from B (given to B by a reader connection) and attempting to authenticate that username and password against a RADIUS server. See readers.conf(5) for more information on how to configure an B authenticator. It is useful for a site that already does user authentication via RADIUS and wants to authenticate news reading connections as well. By default, B reads I/inn-radius.conf for configuration information, but a different configuration file can be specified with B<-f>. See inn-radius.conf(5) for a description of the configuration file. =head1 OPTIONS =over 4 =item B<-f> I Read I instead of I/inn-radius.conf for configuration information. =item B<-h> Print out a usage message and exit. =back =head1 EXAMPLE The following readers.conf(5) fragment tells B to authenticate all connections using this authenticator: auth radius { auth: radius default: default-domain: example.com } C<@example.com> will be appended to the user-supplied identity, and if RADIUS authentication fails, the user will be assigned an identity of CFAILE@example.com>. =head1 BUGS It has been reported that this authenticator doesn't work with Ascend RADIUS servers, but does work with Cistron RADIUS servers. It's also believed to work with Livingston's RADIUS server. Contributions to make it work better with different types of RADIUS servers would be gratefully accepted. This code has not been audited against the RADIUS protocol and may not implement it correctly. =head1 HISTORY The RADIUS authenticator was originally written by Aidan Cully. This documentation was written by Russ Allbery . $Id: radius.pod 9940 2015-09-04 12:58:15Z iulius $ =head1 SEE ALSO inn-radius.conf(5), nnrpd(8), readers.conf(5). =cut inn-2.6.0/doc/pod/cycbuff.conf.pod0000644000175200017520000002373212575023702016327 0ustar iuliusiulius=head1 NAME cycbuff.conf - Configuration file for INN CNFS storage method =head1 DESCRIPTION This file defines the cyclical buffers that make up the storage pools for CNFS (Cyclic News File System). Some options controlling the behavior of the CNFS storage system can also be set here. F is required if the CNFS (Cyclic News File System) storage method is used. INN will look for it in I (as set in F). CNFS stores articles in logical objects called I. Each metacycbuff is in turn composed of one or more physical buffers called I. As articles are written to the metacycbuff, each article is written to the next cycbuff in the list in a round-robin fashion (unless C mode is specified, in which case each cycbuff is filled before moving on to the next). This is so that you can distribute the individual cycbuffs across multiple physical disks and balance the load between them. Note that in order to use any cycbuff larger than S<2 GB> on 32-bit platforms (and some very rare 64-bit platforms that aren't Linux), you need to build INN with the B<--enable-largefiles> option. For information about how to configure INN to use CNFS, see storage.conf(5). Blank lines and lines beginning with a hash sign (C<#>) are ignored. All other lines must be of one of the following forms: cycbuffupdate: refreshinterval: cycbuff::: metacycbuff::[,,...][:] (where items enclosed in [] are optional). Order is mostly not significant, but all I lines must occur before all I lines. Long lines can be continued on the next line by ending the line with a backslash (C<\>). =over 4 =item I: Sets the number of articles written before the cycbuff header is written back to disk to . Under most operating systems, the header doesn't have to be written to disk for the updated data to be available to other processes on the same system that are reading articles out of CNFS, but any accesses to the CNFS cycbuffs over NFS will only see the data present at the last write of the header. After a system crash, all updates since the last write of the CNFS header may be lost. The default value, if this line is omitted, is C<25>, meaning that the header is written to disk after every 25 articles stored in that cycbuff. =item I: Sets the interval (in seconds) between re-reads of the cycbuff header to . This primarily affects B and controls the frequency with which it updates its knowledge of the current contents of the CNFS cycbuffs. The default value, if this line is omitted, is C<30>. =item I::: Configures a particular CNFS cycbuff. is a symbolic name for the buffer, to be used later in a metacycbuff line. It must be no longer than seven characters. is the full path to the buffer file or block device, and must be no longer than 63 characters. is the length of the buffer in kilobytes (S<1 KB> is 1024 bytes). If is not a block device, it should be S<< * 1024 bytes >> long. If you're trying to stay under S<2 GB>, keep your sizes below C<2097152>. =item I::[,,...][:] Specifies a collection of CNFS buffers that make up a single logical storage location from the perspective of INN. Metacycbuffs are referred to in F as storage locations for articles, so in order to actually put articles in a cycbuff, it has to be listed as part of some metacycbuff which is then referenced in F. is the symbolic name of the metacycbuff, referred to in the options: field of C entries in F. It must be no longer than eight characters. is the name of a cycbuff (the part of a cycbuff line), and any number of cycbuffs may be specified, separated by commas. If there is more than one cycbuff in a metacycbuff, there are two ways that INN can distribute articles between the cycbuffs. The default mode, C, stores the articles in each cycbuff in a round-robin fashion, one article per cycbuff in the order listed. If the cycbuffs are of wildly different sizes, this can cause some of them to roll over much faster than others, and it may not give the best performance depending on your disk layout. The other storage mode, C, instead writes to each cycbuff in turn until that cycbuff is full and then moves on to the next one, returning to the first and starting a new cycle when the last one is full. To specify a mode rather than leaving it at the default, add a colon and the mode (C or C) at the end of the metacycbuff line. =back B only reads F on startup, so if you change anything in this file and want B to pick up the changes, you have to use C; C is not sufficient. When articles are stored, the cycbuff into which they're stored is saved as part of the article token. In order for INN to retrieve articles from a cycbuff, that cycbuff must be listed in F. However, if INN should not write to a cycbuff, it doesn't need to be (and shouldn't be) listed in a metacycbuff. This provides an easy way to retire a cycbuff. Just remove it from its metacycbuff, leaving in the cycbuff line, and restart B (with, for example, C). No new articles will be put into the cycbuff, but neither will any articles expire from it. After you no longer need the articles in the cycbuff, just remove it entirely from F. Then all of the articles will appear to have been deleted to INN, and the next nightly expire run will clean up any remaining references to them. Adding a new cycbuff just requires creating it (see below), adding a cycbuff line, adding it to a metacycbuff, and then restarting B. =head1 CREATING CYCBUFFS When creating a new cycbuff, there are two different methods for creating the buffers in which the articles will be stored. =over 4 =item 1. Create a large file on top of a regular file system. The easiest way to do this is probably with dd(1), using a command like: dd if=/dev/zero of=/path/to/cycbuff bs=1024 count= where is the size from the cycbuff line in F. F contains a script that will generate these commands for you from your F file. This is the simplest method, but has the disadvantage that very large files on regular file systems can be fairly slow to access, particularly at the end of the file, and INN incurs unnecessary file system overhead when accessing the cycbuff. =item 2. Use block devices directly. If your operating system allows you to call mmap() on block devices (Solaris and recent versions of Linux do, FreeBSD at last report does not), this is the recommended method since you can avoid all of the native file system overhead. Note that some OSes do not support files larger than S<2 GB>, which will limit the size you can make a single cycbuff, but you can still combine many cycbuffs into each metacycbuff. Very old versions of Linux (before 2.4 kernels, that raised the limit to S<2 TB>) are known to have this limitation; FreeBSD does not. Some OSes that support large files don't support direct access to block devices for large partitions (Solaris prior to S, or not running in 64-bit mode, is in this category); on those OSes, if you want cycbuffs over S<2 GB>, you'll have to use regular files. If in doubt, keep your cycbuffs smaller than S<2 GB>. Partition the disk to make each partition equal to or smaller than S<2 GB>. If you're using Solaris, set up your partitions to avoid the first cylinder of the disk (or otherwise the cycbuff header will overwrite the disk partition table and render the cycbuffs inaccessible). Then, create device files for each block device you're going to use. It's not recommended to use the block device files in F, since the news system doesn't have permission to write to them and changing the permissions of the system device files may affect something else. Instead, use mknod(1) to create a new set of block devices (in somewhere like I/cycbuffs that's only writable by the news user). To do this, run C on the devices in F that correspond to the block devices that you want to use. The major and minor device numbers are in the fifth and sixth columns (right before the date), respectively. Then run mknod like: mknod b where is the path to the device to create (matching the part of the cycbuff line) and and are the major and minor device numbers as discovered above. Here's a short script to do this when given the path to the system device file as an argument: #!/bin/sh base=`echo "$1" | sed 's%.*/%%'` major=`ls -Ll "$1" | awk '{print $5}' | tr -d ,` minor=`ls -Ll "$1" | awk '{print $6}` mkdir -p /cycbuffs mknod /cycbuffs/"$base" b "$major" "$minor" chown news:news /cycbuffs/"$base" chmod 644 /cycbuffs/"$base" Make sure that the created files are owned by the news user and news group, as specified at configure time (the default being C for both). Also make sure that the permissions on the devices allow the news user to read and write, and if you want other users on the system to be able to use B to retrieve articles, make sure they're world-readable. =back Once you have everything configured properly and you start B, you should see messages in F that look like: innd: CNFS: no magic cookie found for cycbuff ONE, initializing where C will be whatever you called your cycbuff. =head1 HISTORY Written by Katsuhiro Kondou for InterNetNews. Rewritten into POD by Russ Allbery . $Id: cycbuff.conf.pod 9925 2015-08-08 17:05:43Z iulius $ =head1 SEE ALSO ctlinnd(8), innd(8), nnrpd(8), sm(1), storage.conf(5). =cut inn-2.6.0/doc/pod/innconfval.pod0000644000175200017520000000715112575023702016114 0ustar iuliusiulius=head1 NAME innconfval - Get configuration parameters from F =head1 SYNOPSIS B [B<-pstv>] [B<-i> I] [I ...] B B<-C> [B<-i> I] =head1 DESCRIPTION B normally prints the values of the parameters specified on the command line. By default, it just prints the parameter values, but if B<-p>, B<-s>, or B<-t> are given, it instead prints the parameter and value in the form of a variable assignment in Perl, Bourne shell, or Tcl respectively. If no parameters are specifically requested, B prints out all parameter values (this isn't particularly useful unless one of B<-p>, B<-s>, or B<-t> were specified). All parameters are taken from F except for I, which is always the version string of INN. If given the B<-C> option, B instead checks F, reporting any problems found to standard error. B will exit with status 0 if no problems are found and with status 1 otherwise. =head1 OPTIONS =over 4 =item B<-C> Check F rather than printing out the values of parameters. =item B<-i> I Use I as the source configuration file rather than F. I must be a valid F file and will be parsed the same as F would be. =item B<-p> Print out parameters as Perl assignment statements. The variable name will be the same as the F parameter, and string values will be enclosed in single quotes with appropriate escaping. Boolean values will be mapped to the strings C or C. List values will be mapped to an array of strings. NULL values are printed out with the C value. Here is an example: $enableoverview = 'true'; @extraoverviewadvertised = ( 'Newsgroups', 'Injection-Info' ); @extraoverviewhidden = undef; $organization = 'Let\'s try nasty "quotes"'; $maxforks = 10; If B is called via the Perl C module, all these variables are properly exported. =item B<-s> Print out parameters as Bourne shell assignment statements. The variable name will be the F parameter name in all capitals, and all variables will be exported, if not NULL. String values will be enclosed in single quotes with appropriate escaping, and boolean values will be mapped to C or C. List values will be mapped to a space-separated string representing an array of strings (as Bourne shell does not recognize arrays, contrary to several other shells, an array cannot be returned for interoperability reasons). Here is an example: ENABLEOVERVIEW=true; export ENABLEOVERVIEW; EXTRAOVERVIEWADVERTISED='"Newsgroups" "Injection-Info"'; export EXTRAOVERVIEWADVERTISED; ORGANIZATION='Let'\''s try nasty "quotes"'; export ORGANIZATION; MAXFORKS=10; export MAXFORKS; =item B<-t> Print out parameters as Tcl assignment statements. The variable name will be the same as the F parameter name but with C prepended, and string variables will be escaped appropriately. Boolean values will be mapped to the strings C or C. List values will be mapped to an array of strings. NULL values are not printed out. Here is an example: set inn_enableoverview "true" set inn_extraoverviewadvertised { "Newsgroups" "Injection-Info" } set inn_organization "Let's try nasty \"quotes\"" set inn_maxforks 10 =item B<-v> Print INN's version. This is equivalent to C. =back =head1 HISTORY Written by Rich $alz for InterNetNews. $Id: innconfval.pod 9288 2011-07-22 23:08:57Z iulius $ =head1 SEE ALSO inn.conf(5), INN::Config(3pm). =cut inn-2.6.0/doc/pod/rnews.pod0000644000175200017520000001221512575023702015112 0ustar iuliusiulius=head1 NAME rnews - Inject individual articles and UUCP batches into INN =head1 SYNOPSIS B [B<-NUv>] [B<-h> I] [B<-P> I] [B<-rS> I] [I] =head1 DESCRIPTION B injects either individual articles or UUCP-style article batches into an INN server. It submits articles via IHAVE and is suitable for injecting articles received from other sources; local postings should generally use inews(1) instead. It is also used to process spooled messages created by, for example, B while B is not available. The message is read from I if given, or standard input if no file is given. Articles are sent to the server given in the B<-r> or B<-S> command line options if given, otherwise to the server set via I in F, otherwise to the local server. When sent over UUCP, Usenet articles are typically collected in a single batch to reduce the UUCP overhead. Batches can also be compressed to reduce communication time. If the input to B does not begin with the characters C<#!>, it is taken to be a single news article; otherwise, the first line of the input is interpreted as a batch command. If the batch command is: #! rnews then the next bytes (starting with the next line) are read as a news article. After that article is processed, the next line is again treated as a batch command. If the command is: #! cunbatch then the rest of the input is fed to C to uncompress it, and then the resulting uncompressed output is re-read as if it were the original input to B. A compressed batch should therefore start with this line and contain a batch of articles separated by C> lines and then compressed with compress(1). (Batches compressed with gzip(1) should instead use C as the batch command; INN just uses B rather than B because it can handle B-style compression but is more widely available, due to old patent issues, than B.) Otherwise, if the command is any other word, then B will try to execute a program with that name, looking for it in the directory I/rnews.libexec. The rest of the batch will be fed to that program's standard input, and the standard output from the program will be treated as if it were the original input to B. INN comes with three such standard batch processors: =over 2 =item B It invokes B and should be used for batches compressed with B. =item B It undoes an ASCII encoding to recover the original binary compressed stream and then decompresses it as explained above. =item B It invokes B and should be used for batches compressed with B. =back By default, B will log and discard any articles that are rejected by the server or cannot be parsed by B for some reason (such as a missing header). This default can be changed when compiling INN by setting DO_RNEWS_SAVE_BAD in F. There is no way to change it at runtime, unfortunately. =head1 OPTIONS =over 4 =item B<-h> I If B<-h> is given, B will log the message ID and I via syslog for each article that it offers to the server. This is used in conjunction with a UUCP feed to get a log of the messages received via that feed. This will also be done if the environment variable UU_MACHINE is set, but will only be done if I is not an empty string. (You can therefore turn off logging even if UU_MACHINE will be set by passing the flag C<-h ''> to B.) =item B<-N> Normally, if unpacking the input batch fails, it is re-spooled to I for another attempt later. If the B<-N> flag is given, no such re-spooling is done and B will instead exit with status 9 if unpacking fails. =item B<-P> I Use I as the server port to connect to rather than I (as set in F). Note that this value is only used if B does not connect to the local server (in other words, when B<-r> or B<-S> is given or I is set). =item B<-r> I, B<-S> I B<-r> and B<-S> are synonymous. If either is given, articles will be sent to I rather than using the local server, overriding also the setting of I in F. =item B<-U> If the server is not available, both B and B will spool posts to new files in the I directory (as specified in F). When B is invoked with the B<-U> option, it scans that directory and processes all spooled messages found there whose filenames do not begin with C<.>, attempting to send them to the server again. It's a good idea to run this periodically out of cron to pick up any articles spooled due to temporary server unavailability. =back =head1 BUGS B cannot process articles that have embedded nul characters in them. (Neither can the rest of INN at present.) =head1 HISTORY Written by Rich $alz for InterNetNews. Rewritten in POD by Russ Allbery . $Id: rnews.pod 9767 2014-12-07 21:13:43Z iulius $ =head1 SEE ALSO bzip2(1), compress(1), gzip(1), inn.conf(5), innd(8), nnrpd(8). =cut inn-2.6.0/doc/pod/ovdb_server.pod0000644000175200017520000000130712575023702016274 0ustar iuliusiulius=head1 NAME ovdb_server - overview 'helper' server =head1 SYNOPSIS Use C to start ovdb_server =head1 DESCRIPTION If the C parameter in F is true, C will start C. C opens the overview database, and accesses it on behalf of the nnrpd reader processes. To shut down ovdb_server, send a TERM signal to the process ID in F/ovdb_server.pid> . The parent process will shut down its children and wait for their exit before exiting itself. =head1 HISTORY Written by Heath Kehoe Ehakehoe@avalon.netE for InterNetNews. $Id: ovdb_server.pod 9765 2014-12-07 21:07:34Z iulius $ =head1 SEE ALSO ovdb(5), ovdb_init(8) =cut inn-2.6.0/doc/pod/inews.pod0000644000175200017520000001201012575023702015072 0ustar iuliusiulius=head1 NAME inews - Post a Usenet article to the local news server =head1 SYNOPSIS B [B<-ADhNORSVW>] [B<-acdeFfnortwx> I] [B<-p> I] [I] =head1 DESCRIPTION B reads a Usenet news article, perhaps with headers, from I or standard input if no file is given. It adds some headers and performs some consistency checks. If the article does not meet those checks, the article is rejected. If it passes the checks, B sends the article to the local news server as specified in F. By default, if a file named F<.signature> exists in the home directory of the posting user, it is appended to the post, preceded by a line that contains only C<-- >. Signatures are not allowed to be more than four lines long. Cancel messages can only be posted with B if the sender of the cancel message matches the sender of the original message being cancelled. The same check is also applied to Supersedes. Sender in this case means the contents of the Sender header if present, otherwise the From header. Control messages other than cancel messages are only allowed if B is being run by the news user or by a user in the news group and if the control message is recognized. If the article contains a Distribution header with a distribution that matches one of the bad distribution patterns in F (anything containing a period by default), the message will be rejected. The message will also be rejected if I is true in F, it contains more quoted text than original text, and it is over 40 lines long. If not provided, the Path header of an article is constructed as follows: The basic Path header will be "not-for-mail". If I is specified in F, it will be added to the beginning Path. Otherwise, if I is specified, the full domain of the local host will be added to the beginning of the Path. Then, if B<-x> was given, its value will be added to the beginning of the Path. If posting fails, a copy of the failed post will be saved in a file named F in the home directory of the user running B. B exits with a non-zero status if posting failed or with a zero status if posting was successful. =head1 OPTIONS Most of the options to B take a single value and set the corresponding header in the message that is posted. If the value is more than one word or contains any shell metacharacters, it must be quoted to protect it from the shell. Here are all the options that set header fields and the corresponding header: -a Approved -c Control -d Distribution -e Expires -F References -f From -n Newsgroups -o Organization -r Reply-To -t Subject -w Followup-To -x Path prefix The B<-x> argument will be added to the beginning of the normal Path header; it will not replace it. =over 4 =item B<-A>, B<-V>, B<-W> Accepted for compatibility with C News. These options have no affect. =item B<-D>, B<-N> Perform the consistency checks and add headers where appropriate, but then print the article to standard output rather than sending it to the server. B<-N> is accepted as as synonym for compatibility with C News. =item B<-h> Normally, this flag should always be given. It indicates that the article consists of headers, a blank line, and then the message body. If it is omitted, the input is taken to be just the body of the message, and any desired headers have to be specified with command-line options as described above. =item B<-O> By default, an Organization header will be added if none is present in the article. To prevent adding the default (from I in F), use this flag. =item B<-p> I Connect to the specified port on the server rather than to the default (port 119). =item B<-R> Reject all control messages. =item B<-S> Do not attempt to append F<~/.signature> to the message, even if it exists. =back =head1 NOTES If the NNTP server requests authentication, B will try to read F to get the username and password to use and will therefore need read access to that file. This is typically done by making that file group-readable and adding all users who should be able to use B to post to that server to the appropriate group. B used to do even more than it does now, and all of the remaining checks that are not dependent on the user running B should probably be removed in favor of letting the news server handle them. Since INN's B uses F and some other corners of an INN installation, it's not very appropriate as a general stand-alone B program for general use on a system that's not running a news server. Other, more suitable versions of B are available as part of various Unix news clients or by themselves. =head1 HISTORY Written by Rich $alz for InterNetNews. Rewritten in POD by Russ Allbery . $Id: inews.pod 9767 2014-12-07 21:13:43Z iulius $ =head1 SEE ALSO inn.conf(5), passwd.nntp(5), rnews(1). =cut inn-2.6.0/doc/pod/active.times.pod0000644000175200017520000000376412575023702016360 0ustar iuliusiulius=head1 NAME active.times - List of local creation times of newsgroups =head1 DESCRIPTION The file I/active.times provides a chronological record of when newsgroups were created on the local server. This file is normally updated by B and B whenever a newgroup control message is processed or a C command is issued, and is used by B to answer NEWGROUPS requests. Each line consists of three fields:
] [B] [B] [B] [B] [B] [B] [B] [B] [B] [B] [B] [B=I] [B=I] [B=I] [I] =head1 DESCRIPTION B performs a number of important Usenet administrative functions. This includes: =over 2 =item * producing a status report with B; =item * removing old news articles (with B if the B keyword is specified); =item * purging the overview database with B if the corresponding eponym keyword is specified; =item * processing log files and rotating the archived log files with B; =item * processing B dropped files with B; =item * renumbering the F file with B; =item * rebuilding the F file with B; =item * removing any old socket files found in the I directory; =item * collecting all the output and mailing it. =back Please note that this program should be run under the news administrator's account (usually C), not as root. By default, B performs all of its functions and mails the output to the news administrator, which is the user specified with B<--with-news-master> at configure time (it is C by default). You can also change this behaviour with the B keyword. By specifying keywords on the command line, it is possible to modify the functions performed, as well as change the arguments given to expire(8) and expireover(8). B should be run once a day, typically out of cron(8). Since it will slow the server down while it is running, it should be run during periods of low server usage, such as in the middle of the night. To run it at 3am, for example, add the following entry to the news user's crontab file: 0 3 * * * /news.daily expireover lowmark If you're using any non-CNFS storage methods, add the B keyword to the above option list for B. It may be run more often, but such invocations should at least use the B keyword (or perhaps the B keyword) to prevent the log files from being processed and rotated too fast. The shlock(1) program is used to prevent simultaneous executions. The program specified by the given path I is executed just before any expiration is done. A typical use is to specify an alternate expiration program and use the B keyword. Multiple programs may be specified; they will be invoked in order. =head1 KEYWORDS The following keywords may be used: =over 4 =item B This uses the B<-z> flag when invoking B and B. The names of articles to be removed are written to a temporary file, and then renamed after expiration by calling B which in turn calls B. =item B=I Specify the file to use as the F file for B. =item B=I By default, B builds the new F file and database in the same directory as the current files. Using this keyword specifies a different location to build the new files (by passing the B<-d> flag to B), which will then be moved to the right location when finished. =item B The B program is called after expiration to purge the overview database. If no overview data is created, the B keyword is not needed. This is the case when the server runs only for feeders (no reader). By default, B is not called by B. =item B=I If the B keyword is used, this keyword may be used to specify the flags to be passed to B. If the B keyword is used, then the default value is B<-z> and the list of deleted files; otherwise, the default value is B<-s>. =item B=I By default, B is invoked with argument B<-v1>. Using this keyword changes the arguments to those specified. Be careful to use quotes if multiple arguments are needed. This keyword has no effect if the B keyword is used. =item B If the B keyword is used, C is used for renumbering F. Normal C operation will take long time. With the B keyword, this will take less time. If the B keyword is used, the B keyword is not needed since B specifies it implicitly. If the B keyword is given to B, then the B keyword must also be given; otherwise, that kind of renumbering will not be taken into account. =item B=I
By default, B mails the report to the newsmaster address specified with B<--with-news-master> at configure time. The B keyword can specify a different address to which to mail the report. Note that using this keyword has no effect if the B keyword is also specified. =item B By default, B is invoked to remove old news articles. Using this keyword disables this function. =item B By default, B is invoked to remove old overview database if I is set in F. Using this keyword disables this function. =item B B normally appends information to I/expire.log (see newslog(5)). Using this keyword causes the B output to be handled as part of B's output. It has no effect if the B keyword is used. =item B After expiration, B is invoked to process the log files. Using this keyword disables all log processing functions. =item B B normally sends a mail message containing the results to the administrator. Using this keyword causes this message to be sent to stdout and stderr instead. Normally, all utilities invoked by the script have their stdout and stderr redirected into a file. If the file is empty, no message is sent. =item B This keyword disables the run of B on B dropped files generated in its spool directory. These files contain a list of articles which were not correctly sent to peers. By default, they will be processed and removed afterwards. =item B This keyword disables the C operation. Normally, the low water marks for all newsgroups are reset in the F file. =item B By default, any B socket that has not been modified for two days will be removed. Using this keyword disables this function. =item B By default, log processing includes rotating and cleaning out log files. Using this keyword disables the rotating and cleaning aspect of the log processing: the logs files are only scanned for information and no contents are altered. This keyword has no effect if the B keyword is used. The B keyword is passed on to B if it is invoked. =item B This keyword disables the status report generated by B. Without this keyword, the status report is the first function performed, just prior to obtaining the B lock. =item B By default, B expects to be run only once a day, and it does various things (like rotating logs) that normally should only be done on daily basis. Use this keyword any extra times B is run in the day and the normal log files processing (and rotation) will not be done. This keyword implies B. =item B=I The program specified by the given path I is executed just after all expiration is done. Multiple programs may be specified; they will be invoked in order. =item B=I B tries to find the backlog directory of B in F. In case several instances of B are running or when its configuration file is not the default one, the B keyword can be used to specify the path of the backlog directory. This keyword can be used more than once in the command-line. =item B=I Overrides the I setting in F by setting the environment variable C<$TMPDIR> to the specified path. Various parts of the expire process, such as sort, will then use this path as the directory for temporary files. =back =head1 HISTORY B and this manual page were written by Landon Curt Noll and Rich $alz for InterNetNews. It was converted to POD by Julien Elie. $Id: news.daily.pod 9628 2014-05-14 17:22:01Z iulius $ =head1 SEE ALSO active(5), ctlinnd(8), expire(8), expire.ctl(5), expireover(8), expirerm(8), fastrm(8), inn.conf(5), innstat(8), newslog(5), procbatch(8), scanlogs(8), shlock(1). =cut inn-2.6.0/doc/pod/tst.pod0000644000175200017520000000550312575023702014570 0ustar iuliusiulius=head1 NAME tst - ternary search trie functions =head1 SYNOPSIS #include struct tst; struct tst *tst_init(int node_line_width); void tst_cleanup(struct tst *tst); int tst_insert(struct tst *tst, const unsigned char *key, void *data, int option, void **exist_ptr); void *tst_search(struct tst *tst, const unsigned char *key); void *tst_delete(struct tst *tst, const unsigned char *key); =head1 DESCRIPTION B allocates memory for members of I, and allocates the first I nodes. A NULL pointer is returned by B if any part of the memory allocation fails. On success, a pointer to a I is returned. The value for I must be chosen very carefully. One node is required for every character in the tree. If you choose a value that is too small, your application will spend too much time calling malloc(3) and your node space will be too spread out. Too large a value is just a waste of space. B frees all memory allocated to nodes, internal structures, as well as I itself. B inserts the string I into the tree. Behavior when a duplicate key is inserted is controlled by I

I The nice priority that this channel or program feed should receive. This should be a positive number between 0 and 20 and is the priority that the new process will run with. This flag can be used to raise the priority to normal if you're using the I parameter in F. =item B I Specifies the I match expression for this site. It must be in the form C or C. The Message-ID of the article is hashed using MD5, which results in a 128-bit hash. The lowest S<32 bits> are then taken by default as the hashfeed value (which is an integer). If the hashfeed value modulus C plus one equals C or is between C and C, the article will be fed to this site. All these numbers must be integers. It is a deterministic way to control the flow of articles and to split a feed. For instance: Q1/2 Feeds about 50% of all articles to this site. Q2/2 Feeds the other 50% of all articles. Another example with three sites: Q1-3/10 Feeds about 30% of all articles. Q4-5/10 Feeds about 20% of all articles. Q6-10/10 Feeds about 50% of all articles. If this flag is specified multiple times, the contents will be logically Ced together (just one match is needed). You can use an extended syntax of the form C or C. As MD5 generates a 128-bit return value, it is possible to specify from which byte-offset the 32-bit integer used by hashfeed starts. The default value for C is C<_0> and thirteen overlapping values from C<_0> to C<_12> can be used. Only up to four totally independent values exist: C<_0>, C<_4>, C<_8> and C<_12>. Therefore, it allows to a generate a second level of deterministic distribution. Indeed, if a news server is fed C, it can go on splitting thanks to C for instance. Up to four levels of deterministic distribution can be used. The algorithm is compatible with the one used by S and up. If you want to use the legacy quickhashing method used by Diablo before 5.1, you can put an C<@> sign just after the B flag (for instance C, but the distribution of the messages is not perfect with this legacy method whose use is discouraged and for which offsets cannot be used). =item B I If the amount of data queued for the site gets to be larger than I bytes, the server will switch to spooling, appending to a file specified by the B flag, or I/I if B is not specified. Spooling usually happens only for channel or exploder feeds, when the spawned program isn't keeping up with its input. =item B I This flag specifies the type of feed for this site. I should be a letter chosen from the following set: c Channel f File l Log entry only m Funnel (multiple entries feed into one) p Program x Exploder Each feed is described below in L<"FEED TYPES">. The default is B, for a file feed. =item B I If this flag is specified, an article will only be sent to this site if followups to this article would be posted to no more than I newsgroups. (Also see B for a more complex way of handling this.) =item B I For a file, channel, or exploder feed, this flag controls what information will be sent to this site. For a program feed, only the asterisk (C<*>) has any effect. I should be chosen from the following set: =over 3 =item b Size of the article (in wire format, meaning with CRLF at the end of each line, periods doubled at the beginning of lines, and ending in a line with a single period) in bytes. =item e The time the article will expire as seconds since epoch if it has an Expires: header, C<0> otherwise. =item f The storage API token of the article (the same as C). The article can be retrieved given the storage API token by using sm(8). =item g The newsgroup the article is in; if cross-posted, then the first of the groups to which the article was posted that this site gets. (The difference from C is that this sends the newsgroup to which the article was posted even if it is a control message.) =item h The history hash key of the article (derived from the message ID). =item m The message ID of the article. =item n The storage API token of the article. The article can be retrieved given the storage API token by using sm(8). =item p The time the article was posted a seconds since epoch. =item s The site that fed the article to the server. This is taken from either the Path: header or the IP address of the sending site depending on the value of I in F. If I is true and the IP address is C<0.0.0.0> (meaning that the article was fed from localhost by a program like rnews(8)), the Path: header value will be sent instead. =item t The time the article was received as seconds since epoch. =item Z<>* The names of the appropriate funnel entries, or all sites that get the article (see below for more details). =item D The value of the Distribution: header of the article, or C if there is no such header in the article. =item G Where the article is stored. If the newsgroup is crossposted, this is generally the first of the groups to which it was posted that this site receives; however, control messages are filed in control or control.* (which is the difference between this item and C). =item H All of the headers, followed by a blank line. The Xref header will already be present, and a Bytes header containing the article's size in bytes as in the C item will be added to the headers. If used, this should be the only item in the list. =item N The value of the Newsgroups: header. =item P The value of the Path: header. =item O Overview data for the article. =item R Information needed for replication (the Xref header without the site name). =back More than one letter can be given. If multiple items are specified, they will be written in the order specified separated by spaces. (C should be the only item if given, but if it's not a newline will be sent before the beginning of the headers.) The default is B. The C and C items are intended for use by programs that create news overview databases or require similar information. B is the flag to generate input needed by the overchan(8) program. The asterisk (C<*>) has special meaning. Normally it expands to a space-separated list of all sites that received the current article. If, however, this site is a target of a funnel feed (in other words, if it is named by other sites which have the B flag), then the asterisk expands to the names of the funnel feeds that received the article. Similarly, if the site is a program feed, an asterisk in the I field will be expanded into the list of funnel feeds that received the article. A program feed cannot get the site list unless it is the target of other B feeds. =back =head1 FEED TYPES B provides four basic types of feeds: log, file, program, and channel. An exploder is a special type of channel. In addition, several entries can feed into the same feed; these are funnel feeds, which refer to an entry that is one of the other types. Funnel feeds are partially described above with the description of the B flag. A funnel feed gets every article that would be sent to any of the feeds that funnel into it and normally include the B flag in their flags so that the program processing that feed knows which sites received which articles. The most common funnel feed is innfeed(8). Note that the term "feed" is technically a misnomer, since the server doesn't transfer articles itself and only writes data to a file, program, or log telling another program to transfer the articles. The simplest feed is a log feed (B). Other than a mention in the news log file, I/news, no data is written out. This is equivalent to a B entry writing to F, except that no file is ever opened. Flushing a log feed does nothing. A file feed (B) is the next simplest type of feed. When the site should receive an article, the specified data is written out to the file named by the I field. If I is not an absolute path, it is taken to be relative to I in F. If I is not given, it defaults to I/I. The file name should be unique (two file feeds should not ever point to the same file). File feeds are designed for use by external programs that periodically process the written data. To cooperate with B properly, such external programs should first rename the batch file and then send a flush command for that site to B using ctlinnd(8). B will then write out any buffered data, close the file, and reopen it (under the original name), and the program can process the data in the renamed file at its leisure. File feeds are most frequently used in combination with nntpsend(8). A program feed (B) spawns a given program for every article that the site receives. The I field must be the command line to execute, and should contain one instance of C<%s>, which will be replaced by the storage API token of the article (the actual article can be retrieved by the program using sm(8)). The program will not receive anything on standard input (unlike earlier versions of INN, where the article is sent to the program on stdin), and standard output and error from the program will be set to the error log (I/errlog). B will try to avoid spawning a shell if the command has no shell meta-characters; this feature can be defeated if necessary for some reason by appending a semi-colon to the end of the command. The full path name of the program to be run must be specified unless the command will be run by the shell (and it is strongly recommended that the full path name always be specified regardless). If a program feed is the target of a funnel, and if B appears in the flags of the site, a single asterisk may be present in the I and will be replaced by a space-separated list of names of the sites feeding into the funnel which received the relevant article. If the site is not the target of a funnel, or if the B flag is not used, the asterisk has no special meaning. Flushing a program feed does nothing. For a channel (B) or exploder (B) feed, the I field again names the process to start. As with program feeds, the full path to the program must be specified. However, rather than spawning the program for every article, it is spawned once and then whenever the site receives an article, the data specified by the site flags is written to the standard input of the spawned program. Standard output and error are set as with program feeds. If the process exits, it will be restarted automatically. If the process cannot be started, the server will spool input to a file named I/I and will try to start the process again later. When a channel or exploder feed is flushed, the server closes its end of the pipe to the program's standard input. Any pending data that has not been written will be spooled; see the description of the B flag above. The server will then spawn a new instance of the program. No signal is sent to the program; it is up to the program handling a channel or exploder feed to notice end of file on its standard input and exit appropriately. Exploders are a special type of channel feed. In addition to the channel feed behavior described above, exploders can also be sent command lines. These lines start with an exclamation point and their interpretation is up to the exploder. The following commands are generated automatically by the server: !newgroup group !rmgroup group !flush !flush site These commands are sent whenever the ctlinnd(8) command of the same name is received by the server. In addition, the ctlinnd(8) C command can be used to send an arbitrary command line to an exploder. The primary exploder is buffchan(8). Finally, B feeds are the input to a funnel. The I field of the site should name the site handling articles for all of the funnel inputs. =head1 EXAMPLES The syntax of the F file is so complex because you can specify a staggering variety of feeds. INN is capable of interacting with a wide variety of programs that do various things with news articles. Far and away the most common two entries in F, however, are file feeds for nntpsend(8) and funnel feeds for innfeed(8). The former look like this: feed.example.com:*,!control,!control.*,!junk:Tf,Wnm: which generates a file named I/feed.example.com containing one line per article consisting of the storage API token, a space, and the message ID. The latter look like this: feed.example.com:*,!control,!control.*,!junk:Tm:innfeed! Very similar, except that this is the input to a funnel feed named C. One could also write this as: example/feed.example.com:*,!control,!control.*,!junk:Ap,Tm:innfeed! (note the B so that articles that contain just C in the Path: header will still be sent), which is completely equivalent except that this will be logged in I/news as going to the site C rather than C. The typical feed entry for innfeed(8) is a good example of a channel feed that's the target of various funnel feeds (make sure the path to B is properly set): innfeed!:!*:Tc,Wnm*:/innfeed -y Note that the I for this feed is just C so that it won't receive any articles directly. The feed should only receive those articles that would go to one of the funnel feeds that are feeding into it. innfeed(8) will receive one line per article on its standard input containing the storage API token, the message ID, and a space-separated list of sites that should receive that article. Here's a more esoteric example of a channel feed: watcher!:*:Tc,Wbnm\ :exec awk '$1 > 1000000 { print "BIG", $2, $3 }' > /dev/console This receives the byte size of each article along with the storage API token and message ID, and prints to the console a line for every article that's over a million bytes. This is actually rather a strange way to write this since INN can do the size check itself; the following is equivalent: watcher!:*:Tc,>1000000,Wbnm\ :exec awk '{ print "BIG", $2, $3}' > /dev/console Here's a cute, really simple news to mail gateway that also serves as an example of a fairly fancy program feed: mailer!:!*:W*,Tp\ :sm %s | innmail -s "News article" * Remember that C<%s> is replaced by the storage API token, so this retrieves the article and pipes it into B (which is safer than programs like Mail(1) because it doesn't parse the body for tilde commands) with a given subject line. Note the use of C<*> in the command line and B in the flags; this entry is designed to be used as the target of funnel feeds such as: peter@example.com:news.software.nntp:Tm:mailer! sue@example.com:news.admin.misc:Tm:mailer! Suppose that the server receives an article crossposted between news.admin.misc and news.software.nntp. The server will notice that the article should be sent to the site C and the site C, both of which funnel into C, so it will look at the C site and end up executing the command line: sm @...@ | innmail -s "News article" peter@example.com sue@example.com which will mail the article to both Peter and Sue. Finally, another very useful example of a channel feed: the standard entry for controlchan(8). Make sure its path is properly set. controlchan!\ :!*,control,control.*,!control.cancel/!collabra-internal\ :AC,Tc,Wnsm:/controlchan This program only wants information about articles posted to a control newsgroup other than control.cancel, which due to the sorting of control messages described in innd(8) will send it all control messages except for cancel messages. In this case, we also exclude any article with a distribution of C. B gets the storage API token, the name of the sending site (for processing old-style ihave and sendme control messages, be sure to read about I in controlchan(8)), and the message ID for each article. For many other examples, including examples of the special C site entry, see the example F file distributed with INN. Also see the install documentation that comes with INN for information about setting up the standard newsfeeds entries used by most sites. =head1 HISTORY Written by Rich $alz for InterNetNews. Reformatted and rewritten in POD by Russ Allbery . $Id: newsfeeds.pod 9767 2014-12-07 21:13:43Z iulius $ =head1 SEE ALSO active(5), buffchan(8), controlchan(8), ctlinnd(8), inn.conf(5), innd(8), innfeed(8), innxmit(8), nntpsend(8), uwildmat(3). =cut inn-2.6.0/doc/pod/libauth.pod0000644000175200017520000000462012575023702015405 0ustar iuliusiulius=head1 NAME libauth - routines for writing nnrpd resolvers and authenticators =head1 SYNOPSIS #include "libauth.h" struct res_info { struct sockaddr *client; struct sockaddr *local; char *clienthostname; }; struct auth_info { char *username; char *password; }; struct auth_info *get_auth_info(FILE *); struct res_info *get_res_info (FILE *); void free_auth_info(struct auth_info*); void free_res_info (struct res_info*); =head1 DESCRIPTION These functions provide a convenient C frontend to the nnrpd external authentication interface documented in F. Use of this library is B required; in particular, external resolvers and authenticators written in languages other than C will need to implement the necessary functionality themselves. The get_auth_info() and get_res_info() functions allocate sufficient memory for a B or B and any necessary fields, and return a pointer to the struct with the fields filled in from information supplied by nnrpd (the B parameter generally should be C). Both functions return NULL on error. The caller is responsible for deallocating the memory by using the functions below. The string fields of both structs are straightforward. The B and B fields of B actually point to instances of B (or B if IPv6 support is compiled in). The free_auth_info() and free_res_info() functions free the struct passed in as argument and all necessary fields. =head1 BUGS In many cases, nnrpd provides more information than is normally useful (for example, even when calling an authenticator, the resolver information is often provided.) On the other hand, in certain cases it provides less information than might be expected (for example, if nnrpd is reading from stdin rather than a socket). The implementation is capable of handling at least the first of these issues, but that functionality is not exposed in the interface. At present, F and its implementation are located in F; perhaps they should be moved to F and F, respectively? =head1 HISTORY Written by Jeffrey S for InterNetNews. $Id: libauth.pod 8200 2008-11-30 13:31:30Z iulius $ =head1 SEE ALSO nnrpd(8), readers.conf(5), F =cut inn-2.6.0/doc/pod/newslog.pod0000644000175200017520000002341412575023702015435 0ustar iuliusiulius=head1 NAME newslog - Description of INN log files =head1 DESCRIPTION Most log files created by Usenet programs reside in the I directory set in F and have a C<.log> extension. Several versions are usually kept with an additional extension such as C<.1>, C<.2>, etc. S<-- the> higher the number, the older the log. These old versions are stored in I/OLD; they may be compressed and thus may have a C<.1.gz>, C<.2.gz>, etc. extension, up to C<.nn.gz> where C is the number of old logs kept by B which is set by I in F. The B script and related utilities are responsible for rotating and compressing these files. Some log files always have data (like F), others only have data if there is a problem (like F), and others are only created if a particular program is used (like F used by B) or if a configuration parameter is set (like F used by B when I is set in F). Besides, the B script monitors the size of all log files. Here are the log files used by INN: =over 4 =item F This file maintains a count of the number of newgroup and rmgroup control messages seen for each newsgroup. The count is of the number of control messages with the indicated arguments, regardless if they were actually processed. All control arguments, including invalid ones, are counted. An example of lines which can be found in that log file is: 3 Control: newgroup foo.bar moderated 3 Control: rmgroup misc.removed 1 Control: newgroup misc.created This file is updated by B, which is invoked by B if either one of these two log files exists in I: newgroup.log rmgroup.log These two log files contain a summary line describing the control message and the action taken by B, followed by the article indented by four spaces, and a blank line. Whereas these files are rotated, F is not rotated so as to keep the count of seen control messages. Note that other control log files are also rotated by B, if they exist, but their contents are not summarized. Here are their names: checkgroups.log default.log ihave.log miscctl.log sendme.log sendsys.log senduuname.log version.log In order to create these files, the and fields of relevant F entries should be correctly set. For instance: Type Action Meaning ---- ------ ------- all log=miscctl Log all messages by default. newgroup doit=newgroup Create group and log message. newgroup log=newgroup Log message. rmgroup verify-*=rmgroup PGP verify, remove group and log message. checkgroups doit=checkgroups Process and log message. =item F The B program appends all status messages to this file. It is rotated by B. =item F This file contains the standard output and standard error of any program spawned by B, such as channel feeds configured in F. This file should normally be empty. B will print the 50 first lines of this log file if it is non-empty so that they appear in daily Usenet reports generated by B. Then, B rotates this log file. =item F By default, when B is going to expire old news articles, it writes the name of the program it invokes, followed by C and the time it has started. Any output from that program is then written, indented by four spaces. Then, the name of the program is written, followed by C and the time it has ended. Programs called, if needed, are in order: B, B (with the C option), B and B. Removed articles are listed in F and low marks for each newsgroup (that is to say the number of the oldest article available in the newsgroup) in F. After the expiry process, B rotates F. =item F When I is set in F, B appends debugging messages to this file. Note that the name of this file can be changed with the I parameter in F. Be that as it may, the right log file is rotated by B. B also logs its status in F (or the name set in I) if I is set to false but this log file is not processed by B. =item F This file logs articles received by B. Typical lines are: Aug 25 13:37:41.839 + news.server.fr 1658 a.peer other.server.org inpaths! Aug 25 13:37:41.839 c news.server.fr Cancelling Aug 25 13:37:54.638 - a.peer <23k82@bar.net> 437 Poison newsgroup The first one indicates that an article from C has been accepted (C<+>). Its Message-ID is C<< >> and we will send the article to two peers (C and C, as specified in F) and C (see B for more details about it). The second line mentions that this previous article is a cancel message (C) for C<< >> and that it has been processed (inndeed, B processes all control articles except for cancels which are handled by B). The third line indicates that the article whose Message-ID is C<< <23k82@bar.net> >> has been rejected (C<->) because it is posted to a poison newsgroup (a Perl or a Python filter located in I must have brought that reject along). See the "LOGGING" section of the innd(8) man page for more information about the format of this log file. B summarizes the rejected articles reported in this file and B rotates it. =item F All critical error messages issued by B are appended to this file via syslog. This log file should normally be empty. B will print the first 50 lines of this log file if it is non-empty so that they appear in daily Usenet reports generated by B. Then, B rotates this log file. You should have the following line in your system F file, using a tab character for the delimiter: news.crit /news.crit (A typical entry is shown; it should agree with I in F and be tab-delimited.) =item F All major error messages issued by B are appended to this file via syslog. This log file should normally be empty. B will print the first 50 lines of this log file if it is non-empty so that they appear in daily Usenet reports generated by B. Then, B rotates this log file. You should have the following line in your system F file, using a tab character for the delimiter: news.err /news.err (A typical entry is shown; it should agree with I in F and be tab-delimited.) =item F All standard error messages and status messages issued by B, B, B and some other programs are appended to this file via syslog. B uses the Perl script B to summarize this file. B will also print the first I unknown lines of this log file if such unrecognized lines are found in F so that they appear in daily Usenet reports generated by B. This parameter can be set in F. Then, B rotates this log file. You should have the following line in your system F file, using a tab character for the delimiter: news.notice /news.notice (A typical entry is shown; it should agree with I in F and be tab-delimited.) =item F The B program appends all status messages to this file. It is rotated by B. =item F The B program appends all status messages to this file if C is not used (otherwise, such messages are appended to F). It is rotated by B. =item F The B program appends all status messages to this file. It is rotated by B. =item F The B program appends all status messages to this file. It is rotated by B. =item F The B program appends all status messages to this file. It is rotated by B. =item F This log maintains a count of the number of articles that were rejected because they were posted to newsgroups that do not exist at the local site. This file is updated by B while processing the F log file and it is maintained in reverse numeric order (the most popular rejected group first). This file is not rotated so as to keep the count of the articles posted to newsgroups which are absent from the F file of the news server. Note that I has to be set to true in F for this file to be generated. =back Finally, these files are also rotated by B, if they exist, but their contents are not summarized. Here are their names: badcontrol.log badpgp.log failedpgp.log They can be used by programs which PGP verify articles. =head1 HISTORY Written by Landon Curt Noll and Rich $alz for InterNetNews. Rewritten and converted to POD by Julien Elie. $Id: newslog.pod 9903 2015-06-20 17:20:46Z iulius $ =head1 SEE ALSO control.ctl(5), ctlinnd(8), expire(8), expireover(8), expirerm(8), inn.conf(5), innd(8), innfeed.conf(5), innreport(8), innreport.conf(5), news.daily(8), nnrpd(8), nntpsend(8), scanlogs(8), send-nntp(8), send-uucp(8), syslog.conf(5), tally.control(8). =cut inn-2.6.0/doc/pod/getlist.pod0000644000175200017520000001006312575023702015426 0ustar iuliusiulius=head1 NAME getlist - Get a list from an NNTP server =head1 SYNOPSIS B [B<-AR>] [B<-h> I] [B<-p> I] [I [I [I]]] =head1 DESCRIPTION B obtains a list from an NNTP server and sends it to standard output. I may be one of C (which is the default value), C, C, or C. C contains a list of all newsgroups carried by the server with the high and low article numbers in the group, and the group status field. C is a list of newsgroups, their creation times (in seconds since epoch), and the creator of the group. C is a list of relevant distributions and the newsgroups they apply to. C is a list of newsgroups along with their short descriptions. For more information on the formats of these files, see active(5), active.times(5), distrib.pats(5) and newsgroups(5). The listing files other than the F file are common extensions to the NNTP protocol and may not be available on all servers. For instance, C C, C, C, C, C and C, amongst other, may be available. Moreover, a uwildmat(3) pattern I may also be usable for some of these listing files. For more information on the formats of these files, see distributions(5), moderators(5), motd.news(5) and subscriptions(5). The overview fields obtained with C are the ones for which the overview database is consistent (see I in F for more information). The list obtained with C contains the fields that can be retrieved using the HDR command (I can then be either C or C). The list obtained with C is like the C one except that the number of articles in a newsgroup is mentioned just before the flag of this newsgroup. The I parameter may be used with a I value of C, C or C to limit the output. If given, only entries corresponding to newsgroups that match the uwildmat(3) pattern I will be printed. If the I parameter is C, a third parameter, I, may also be used to restrict the list. If given, only entries corresponding to newsgroups with a newsgroup mode found in I will be printed. For example, a I value of C would only show unmoderated groups, and a I value of C would show all unmoderated and moderated groups but not aliased groups or groups that disallow postings. =head1 OPTIONS =over 4 =item B<-A> Try to authenticate using the username and password information in passwd.nntp(5) before issuing the LIST command. =item B<-h> I Connect to the server I rather than to the value of I in F or the value of the NNTPSERVER environment variable. =item B<-p> I Connect to the port I, which is by default C<119>. =item B<-R> Try to switch to B with a MODE READER command before issuing the LIST request. It can be useful in case a feeding access exists for the news server: more information can be retrieved from B, compared with B. =back =head1 EXAMPLES The following command will obtain the one-line descriptions of all newsgroups found on UUNET: getlist -h news.uu.net newsgroups The following command will show the active file entries for all moderated comp.* groups on the default server: getlist active 'comp.*' m The following command lists all groups where local postings are permitted, are moderated or aliased: getlist active '*' ym= Note that the wildcard character C<*> has to be quoted by simple quotes to protect it from the shell. =head1 HISTORY Written by Landon Curt Noll for InterNetNews. Rewritten in POD by Russ Allbery . $Id: getlist.pod 9767 2014-12-07 21:13:43Z iulius $ =head1 SEE ALSO active(5), active.times(5), distrib.pats(5), distributions(5), inn.conf(5), moderators(5), motd.news(5), newsgroups(5), nnrpd(8), passwd.nntp(5), subscriptions(5), uwildmat(3). =cut inn-2.6.0/doc/pod/mod-active.pod0000644000175200017520000000456412575023702016014 0ustar iuliusiulius=head1 NAME mod-active - Batch processing of newsgroups creation and removal commands =head1 SYNOPSIS B [I ...] =head1 DESCRIPTION B is a Perl script that updates the F file based on its input lines of B C, C and C commands. It pauses the server briefly while the existing F file is read and rewritten, which not only keeps B from updating the F file but also locks against other instances of B. The script must be run as the news user. The input to B can come either from one or more I files named on the command line, or from the standard input. Typically its input is the output from the B or B commands. Every line which contains the string C, C, or C, optionally preceded by whitespace and/or the path to B, is noted for the update. Redundant commands, such as a newgroup directive for a group that already exists, are silently ignored. All other lines in the input are also silently ignored. After the new F file has been generated, the existing one is renamed to F and the new one is moved into place. The script then displays the differences between the two files. Any groups that were added to the F file are also added to the F file with the string C. Please note that no syntax checking is performed on group names by B. =head1 BUGS Though B is paused while B works, it is not inconceivable that there could be a conflict if something else tries to update the F file during the relatively short time that B is working. The two most realistic ways for this to happen are either by an administrator concurrently doing a manual B command, or by B receiving a control message, then B pausing the server, then the control message handler script that B forked running its own B command while B is working. Note that such scenarios are I unlikely to happen. =head1 HISTORY Written by David C Lawrence for InterNetNews. Converted to POD by Julien Elie. =head1 SEE ALSO active(5), active.times(5), actsync(8), ctlinnd(8), docheckgroups(8), innd(8). =cut inn-2.6.0/doc/pod/procbatch.pod0000644000175200017520000001077612575023702015733 0ustar iuliusiulius=head1 NAME procbatch - Process an INN funnel file or innfeed-dropped file =head1 SYNOPSIS B [B<-hquv>] [B<-c> [B<-s> I]] [B<-d> I] [B<-e> I] [B<-m> [B<-t> I]] I =head1 DESCRIPTION B will take an INN funnel or F file and split it up by host for direct processing with B or B. While funnel files will normally only be of interest after a crash, B may drop articles for a variety of reasons, not all of which indicate a fundamental problem. For example, B may drop articles when started and stopped in quick succession while INN processes a large number of control messages (a temporary indisposition), or when INN feeds it articles for a site that isn't in F (a misconfiguration). Every running B opens a file named F<< innfeed-dropped. >>, which should normally always be zero length and deleted on exit. If there are non-zero length files, B has dropped some articles, and those dropped article files have to be processed or those articles will never be sent to peers. Though B automatically process these files (invoking B), it is also possible to do that manually. First make sure that the file doesn't correspond to a currently running B, for example by calling C. An INN funnel file, or an F file, will usually be of the format: pathname message-id peer1 peer2 peer3 ... where I can alternatively be a storage API token. B will break this file up into files named F, F, F, etc. of the format: pathname message-id These tape files will be sorted and stripped of duplicate entries. Simply renaming them to F, F, F, etc. in a running B's backlog directory will cause them to be picked up automatically by B every I seconds (as set in in F). Use the B<-m> flag to have B rename and move them into place automatically. After running B, you may want to make sure that every generated backlog file has a corresponding C<.lock> file. Otherwise, B doesn't have that site configured as a peer, meaning your F may need fixing. =head1 OPTIONS =over 4 =item B<-c> Check for the existence of an article before writing references to it to the tape files. Currently, this option can only be used with a tradspool article store. Using it with any other spool format will lead to all articles being dropped. =item B<-d> I Put the output file(s) into the directory I (defaults to I as set in F). When also specifying B<-m>, the temporary output files will still be put here before being moved into their final location. =item B<-e> I Only process entries for I. =item B<-h> Display a short help screen. =item B<-m> Directly append to tape files in B's backlog directory. =item B<-q> Quiet mode: only display error messages. =item B<-s> I Use I as the location of the article spool (defaults to I as set in F). This option has an effect only on a tradspool storage system when also specifying B<-c>. =item B<-t> I Use I as the location of the B backlog directory (the directory where the created tape files will be put). Defaults to I/innfeed. This option has an effect only when also specifying B<-m>. =item B<-u> Unlink the input batchfile after processing. =item B<-v> Add verbosity. =back =head1 EXAMPLES Take the file F, process its lines appending to tapefiles in B's backlog directory, and remove it when done. Be verbose while doing so: procbatch -umv innfeed-dropped.B012345 Go through F saving entries for C in F, but only if the articles are still available in the local tradspool: procbatch -e peer1 -d /tmp -c innfeed-dropped.B012345 =head1 BUGS B should be able to check for the existence of articles with any kind of article store, possibly using B. =head1 HISTORY B was written by James Brister and improved for speed by Clayton O'Neill. This manual page was written by Florian Schlichting, with the help of a memo by Russ Allbery. $Id: procbatch.pod 9371 2011-09-04 09:21:43Z iulius $ =head1 SEE ALSO filechan(8), innfeed(8), innxmit(8), news.daily(8). =cut inn-2.6.0/doc/pod/rc.news.pod0000644000175200017520000000505712575023702015341 0ustar iuliusiulius=head1 NAME rc.news - Start or stop INN daemons =head1 SYNOPSIS B [C | C] =head1 DESCRIPTION B can be used to start or stop B and supporting programs. It checks to make sure INN is not already running, handles cases of unclean shutdown, finishes up tasks which might have been interrupted by the preceding shutdown, e-mails certain boot-time warnings to I (as set in F), and is generally safer and easier than starting and stopping everything directly. It needs to be run as the news user so that files in I are created with the right ownership (though this is less important for C). Programs run and stopped by this script include: =over 4 =item * Always: B is started or stopped. =item * If I is true in F: B is started and stopped. =item * If I is true in F: B is started and stopped. =item * If I is set to C in F: B is run; B and B are stopped. =item * If F exists in I: B is run with argument C or C (to perform site-specific startup or shutdown tasks). =item * When started, if INN appears to have previously been shut down during its expiry process, run B if there are articles to unlink. =item * When started, if overview data appears to have just been rebuilt and F needs to be renumbered, then actually renumber it. =back =head1 OPTIONS =over 4 =item C If the first argument is C, or no first argument is given, B initiates INN startup. =item C If the first argument is C, B initiates INN shutdown. =back =head1 EXAMPLES To start INN and leave certain error messages going to the terminal: su news -s /bin/sh -c /rc.news To run INN at startup time from appropriate system boot scripts: su news -s /bin/sh -c /rc.news >> /rc.news 2>&1 To stop INN: su news -s /bin/sh -c '/rc.news stop' =head1 BUGS Running C as root is never the right thing to do, so we should at minimum check for this and error, or perhaps change effective user ID. =head1 HISTORY // FIXME: any attribution for F itself? This manual page written by Jeffrey S for InterNetNews. $Id: rc.news.pod 9723 2014-09-24 17:54:24Z iulius $ =head1 SEE ALSO ctlinnd(8), cnfsstat(8), expirerm(8), inn.conf(5), innwatch(8), ovdb(5). =cut inn-2.6.0/doc/pod/makehistory.pod0000644000175200017520000001705012575023702016315 0ustar iuliusiulius=head1 NAME makehistory - Initialize or rebuild INN history database =head1 SYNOPSIS B [B<-abFIOSx>] [B<-f> I] [B<-l> I] [B<-L> I] [B<-s> I] [B<-T> I] =head1 DESCRIPTION B rebuilds the history(5) text file, which contains a list of message-IDs of articles already seen by the server. It can also be used to rebuild the overview database. Note that even though the dbz(3) indices for the F file are also rebuilt by B, it is useful to run makedbz(8) after makehistory(8) in order to improve the efficiency of the indices (B does not know how large to make the hash table at first run, unless the size is given by the B<-s> flag). The default location of the F text file is I/history; to specify an alternate location, use the B<-f> flag. By default, B will scan the entire spool, using the storage manager, and write a history line for every article. To also generate overview information, use the B<-O> flag. WARNING: If you're trying to rebuild the overview database, be sure to stop innd(8) and delete or zero out the existing database before you start for the best results. An overview rebuild should not be done while the server is running. Unless the existing overview is deleted, you may end up with problems like out-of-order overview entries, excessively large overview buffers, and the like. If I in F is C, you must have the ovdb processes running while rebuilding overview. ovdb needs them available while writing overview entries. You can start them by hand separate from the rest of the server by running B; see ovdb_init(8) for more details. =head1 OPTIONS =over 4 =item B<-a> Append to the F file rather than generating a new one. If you append to the main F file, make sure innd(8) is throttled or not running, or you can corrupt the history. =item B<-b> Delete any messages found in the spool that do not have valid Message-ID: headers in them. =item B<-F> Fork a separate process to flush overview data to disk rather than doing it directly. The advantage of this is that it allows B to continue to collect more data from the spool while the first batch of data is being written to the overview database. The disadvantage is that up to twice as much temporary disk space will be used for the generated overview data. This option only makes sense in combination with B<-O>. With buffindexed, the B program is invoked to write overview. =item B<-f> I Rather than writing directly to I/history, instead write to I, also in I. =item B<-I> Don't store overview data for articles numbered lower than the lowest article number in F. This is useful if there are for whatever reason old articles on disk that shouldn't be available to readers or put into the overview database. =item B<-l> I This option specifies how many articles to process before writing the accumulated overview information out to the overview database. The default is C<10000>. Since overview write performance is faster with sorted data, each "batch" gets sorted. Increasing the batch size with this option may further improve write performance, at the cost of longer sort times. Also, temporary space will be needed to store the overview batches. At a rough estimate, about 300 * I bytes of temporary space will be required (not counting temp files created by sort(1)). See the description of the B<-T> option for how to specify the temporary storage location. This option has no effect with buffindexed, because buffindexed does not need sorted overview and no batching is done. =item B<-L> I Temporarily pause activities if the system load average exceeds the specified level I. This allows B to run on a system being used for other purposes without monopolizing system resources and thus making the response time for other applications unacceptably slow. Using nice(1) does not help much for that because the problem comes from disk I/O usage, and ionice(1) is not always available or efficient. =item B<-O> Create the overview database as well as the F file. Overview information is only required if the server supports readers; it is not needed for a transit-only server (see I in inn.conf(5)). If you are using the buffindexed overview storage method, erase all of your overview buffers before running B with B<-O>. =item B<-S> Rather than storing the overview data into the overview database, just write it to standard output in a form suitable for feeding to B later if wished. When this option is used, B<-F>, B<-I>, B<-l>, and B<-T> are ignored. This option only makes sense in combination with B<-O>. =item B<-s> I Size the history database for approximately I pairs. Accurately specifying the size is an optimization that will create a more efficient database. (The size should be the estimated eventual size of the F file, typically the size of the old file, in lines.) =item B<-T> I If B<-O> is given, B needs a location to write temporary overview data. By default, it uses I, set in F, but if this option is given, the provided I is used instead. This is also used for temporary files created by sort(1) (which is invoked in the process of writing overview information since sorted overview information writes faster). By default, B usually uses your system temporary directory; see the sort(1) man page on your system to be sure. =item B<-x> If this option is given, B won't write out F file entries. This is useful mostly for building overview without generating a new F file. =back =head1 EXAMPLES Here's a typical example of rebuilding the entire history and overview database, removing broken articles in the news spool. This uses the default temporary file locations and should be done while B isn't running (or is throttled). makehistory -b -f history.n -O -l 30000 -I This will rebuild the overview (if using buffindexed, erase the existing overview buffers before running this command) and leave a new F file as C in I. To preserve all of the history entries from the old F file that correspond to rejected articles or expired articles, follow the above command with: cd awk 'NF == 2 { print }' < history >> history.n (replacing the path with your I, if it isn't the default). Then look over the new F file for problems and run: makedbz -s `wc -l < history.n` -f history.n Then rename all of the files matching C to C, replacing the current history database and indices. After that, it's safe to unthrottle B. For a simpler example: makehistory -b -f history.n -I -O will scan the spool, removing broken articles and generating history and overview entries for articles missing from history. To just rebuild overview: makehistory -O -x -F =head1 FILES =over 4 =item I/history This is the default output file for B. =item I Where temporary files are written unless B<-T> is given. =back =head1 HISTORY Originally written by Rich $alz for InterNetNews and updated by various other people since. $Id: makehistory.pod 8534 2009-06-23 18:08:14Z iulius $ =head1 SEE ALSO active(5), ctlinnd(8), dbz(3), history(5), inn.conf(5), innd(8), makedbz(8), ovdb_init(8), overchan(8). =cut inn-2.6.0/doc/pod/expire.ctl.pod0000644000175200017520000001745212575023702016041 0ustar iuliusiulius=head1 NAME expire.ctl - Configuration file for article expiration =head1 DESCRIPTION The file I/expire.ctl is the default configuration file for B and B, which read it at start-up. It serves two purposes: it defines how long history entries for expired or rejected articles are remembered, and it determines how long articles stored on the server are retained. Normally, if all of the storage methods used by the server are self-expiring (such as CNFS), all lines except the C setting (described below) are ignored. This can be changed with the B<-N> option to B or B. Blank lines and lines beginning with a number sign (C<#>) are ignored. All other lines should be in one of two formats. The order of the file is significant, and the last matching entry will be used. The first format specifies how long to keep history entries for articles that aren't present in the news spool. These are articles that have either already expired, or articles which the server rejected (when I is set to true in F). There should be one and only one line in this format, which looks like: /remember/: where is a decimal number that specifies the minimum number of days a history record for a given message-ID is retained (from its original posting time), regardless of whether the article is present in the spool. (History entries for articles still present in the spool are always retained.) The primary reason to retain a record of old articles is in case a peer offers old articles that were previously accepted but have already expired. Without a history record for such articles, the server would accept the article again and readers would see duplicate articles. Articles older than a certain number of days won't be accepted by the server at all (see I in inn.conf(5) and the B<-c> flag in innd(8)), and this setting should probably match that time period to ensure that the server never accepts duplicates. As the default value for I is C<10> days, it means that C should be set to C<11> days in order to take into account articles whose posting date is one day into the future. Most of the lines in this file will be in the second format, which consists of either four or five colon-separated fields: :::: if I is true in F (the default), and otherwise: ::: All lines must be in the correct format given the current setting of I, and therefore the two formats cannot co-exist in the same file. Normally, a rule matches a newsgroup through the combination of the and fields. is a uwildmat(3)-style pattern, specifying the newsgroups to which the line is applied. Note that the last matching entry will be used, so general patterns (such as defaults for all groups where is C<*>) should appear at the beginning of the file before more specific settings. The field can be used to further limit newsgroups to which the line applies, and should be chosen from the following set: M Only moderated groups U Only unmoderated groups A All groups X Remove the article from all groups it appears in One of C, C, or C must be specified. C should be used in combination with one of the other letters, not by itself. An expiration policy is applied to every article in a newsgroup it matches. There is no way to set an expiration policy for articles crossposted to groups you don't carry that's different than other articles in the same group. Normally, articles are not completely deleted until they expire out of every group to which they were posted, but if an article is expired following a rule where contains C, it is deleted out of all newsgroups to which it was posted immediately. If I is instead set to false, there is no and field and the above does not apply. Instead, there is a single field, which is either a number matching the storage class number specified in F or C<*> to specify a default for all storage classes. All articles stored in a storage class will be expired following the instructions in the line with a matching , and when articles are expired, they're always removed from all groups to which they were posted. The remaining three fields are the same in either format, and are used to determine how long an article should be kept from its original arrival time (unless the B<-p> flag is passed to expire(8) or expireover(8), in which case its original posting time is used). Each field should be either a decimal number of days (fractions like C<8.5> are allowed, but remember that articles are only removed when B or B is run, normally once a day by B) or the word C. The middle field, , will be used as the expiration period for most articles. The other two fields, and , only come into play if the article requests a particular expiration date with an Expires: header. Articles with an Expires: header will be expired at the date given in that header, subject to the constraints that they will be retained at least days and no longer than days. If is set to C, no article matching that line will ever be expired. If is set to C, no article matching that line without an explicit Expires: header will ever be expired. If is set to C, Expires: headers will be honored no matter how far into the future they are. One should think of the fields as a lower bound, the default, and an upper bound. Since most articles do not have an Expires: header, the second field is the most important and most commonly applied. Articles that do not match any expiration rule will not be expired, but this is considered an error and will result in a warning. There should always be a default line (a line with a of C<*> and of C, or a line with a of C<*>), which can explicitly state that articles should never expire by default if that's the desired configuration. The default line should generally be the first line of the file (except for C) so that other expiration rules can override it. It is often useful to honor the Expires: header in articles, especially those in moderated groups. To do this, set to zero, to whatever normal expiration you wish, and to C or some large number, like C<365> days for a maximum article life of a year. To ignore any Expires: header, set all three fields to the same value. =head1 EXAMPLES When I is true (the default): # Keep expired article history for 11 days, matching artcutoff plus one. /remember/:11 # Most articles stay for two weeks, ignoring Expires: headers. *:A:14:14:14 # Accept Expires: headers in moderated groups for up to a year and # retain moderated groups for a bit longer. *:M:1:30:365 # Keep local groups for a long time and local project groups forever. example.*:A:1:90:90 example.project.*:A:never:never:never When I is false, for class-based expiration: # Keep expired article history for 11 days, matching artcutoff plus one. /remember/:11 # Set a default expiration of seven days and honour Expires: headers # within reasonable limits. *:1:7:35 # Class 0 is retained for two weeks and honor Expires: headers # within reasonable limits. 0:1:14:65 =head1 HISTORY Written by Rich $alz for InterNetNews. Converted to POD by Russ Allbery . $Id: expire.ctl.pod 9767 2014-12-07 21:13:43Z iulius $ =head1 SEE ALSO expire(8), expireover(8), inn.conf(5), innd(8), news.daily(8), storage.conf(5), uwildmat(3). =cut inn-2.6.0/doc/pod/tdx-util.pod0000644000175200017520000002243612575023702015534 0ustar iuliusiulius=head1 NAME tdx-util - Tradindexed overview manipulation utility =head1 SYNOPSIS B [B<-AFcgiOo>] [B<-a> I