# HG changeset patch # User bears # Date 972595273 -3600 # Node ID 1c7303c71f66b8dce32a0dde6907f37f7ba74dd3 # Parent bfeea2bc09b678499711e28ecfbd21b46a4ed131 [svn] * src/protocol.c: Fix bug in Prt_getLn if we should read a line starting with '\0' - according to the leafnode mailing list, this has been seen in the wild. * docs/inews.1,docs/noffle.1,docs/noffle.conf.5, packages/redhat/noffle.spec,src/configfile.h,src/configfile.c, src/noffle.c,src/post.h,src/post.c: Removed use of getopt_long, and added inews mode - the Noffle executable behaves as inews is invoked as inews. This includes adding From: and Organization: headers if necessary - add configs to override defaults for the From: domain and specify the organization. For all my fellow trn-heads out there, and users of any other ageing newsreader that expects inews. Updated RPM spec to create inews link to noffle on install. diff -r bfeea2bc09b6 -r 1c7303c71f66 ChangeLog --- a/ChangeLog Thu Oct 26 22:13:28 2000 +0100 +++ b/ChangeLog Thu Oct 26 22:21:13 2000 +0100 @@ -1,3 +1,22 @@ +Thu Oct 26 21:42:45 BST 2000 Jim Hague + + * src/protocol.c: Fix bug in Prt_getLn if we should read a line + starting with '\0' - according to the leafnode mailing list, + this has been seen in the wild. + * docs/inews.1,docs/noffle.1,docs/noffle.conf.5, + packages/redhat/noffle.spec,src/configfile.h,src/configfile.c, + src/noffle.c,src/post.h,src/post.c: Removed use of getopt_long, + and added inews mode - the Noffle executable behaves + as inews is invoked as inews. This includes adding From: and + Organization: headers if necessary - add configs to override + defaults for the From: domain and specify the organization. + For all my fellow trn-heads out there, and users of any other + ageing newsreader that expects inews. Updated RPM spec to create + inews link to noffle on install. + * src/server.c: When replying to a command, generate the reply into + a buffer, release the lock and then send the reply, so we don't + hog the lock should the reply stall for some network reason. + Sun Oct 15 2000 Markus Enzenberger * acconfig.h,config.h.in,configure.in,src/Makefile.in,src/fetch.c: @@ -21,6 +40,7 @@ * src/post.c: do no longer always replace invalid message-IDs. Wed Aug 16 00:03:50 BST 2000 Jim Hague + * Permit 'k' and 'm' suffices after numbers in filter rules. Fri Aug 11 2000 Markus Enzenberger @@ -78,6 +98,9 @@ data first and then process. The aim is to ensure the lock isn't held with a network operation is in progress. I may carry on and extend this to all server operations as well (so a slow client can't hog the lock). + * docs/noffle.conf.5,src/configfile.h,src/configfile.c,src/post.c, + src/protocol.h,src/protocol.c: Add Path: header to newly posted + articles. Provide default content - path-header in config overrides. Sat Jul 22 2000 Markus Enzenberger diff -r bfeea2bc09b6 -r 1c7303c71f66 NEWS --- a/NEWS Thu Oct 26 22:13:28 2000 +0100 +++ b/NEWS Thu Oct 26 22:21:13 2000 +0100 @@ -5,6 +5,9 @@ * Article filtering: see noffle.conf(5) for details. * Noffle no longer hangs if the connection breaks down during a fetch. * SENDMAILPROG is used instead of mail for returning failed postings. + * Noffle will act as inews is invoked as inews. + * Fetching and reading news can overlap, and generally blocking on the + database lock reduced to a minimum. 1.0pre7: -------- diff -r bfeea2bc09b6 -r 1c7303c71f66 docs/inews.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/inews.1 Thu Oct 26 22:21:13 2000 +0100 @@ -0,0 +1,122 @@ +.TH inews 1 +.\" $Id: inews.1 227 2000-10-26 21:21:13Z bears $ +.SH NAME +inews \- Send a Usenet article. + +.SH SYNOPSIS + +.B inews +[ +.B \-D +] +[ +.B \-O +] +[ +.B \-S +] +[ +.I input +] + +.SH DESCRIPTION + +.I inews +reads a Usenet news article including headers from +the named file or standard input if no file is given. +It adds some headers and performs some checks. If the article +fails the checks (e.g. the article lacks a body, or has no Newsgroups: +line), the article is rejected. Otherwise the article is posted. + + +.SH OPTIONS + +.TP +.B \-D +Debug mode. +.I inews +adds header lines and performs some checks. If the checks are successful, +the article is printed on the standard output and not posted. +.TP +.B \-O +If no +.I Organization: +header is found, and a value for +.I organization +is specified in +.BR noffle.conf (5), +.I inews +will add a +.I Organization: +header using that value. To suppress this behaviour, use the +.B \-O +flag. +.TP +.B "\-h \-A \-V \-W" +For compatibility with inews programs used with other news servers, +.I inews +accepts, but ignores, the ``\fB\-h\fP'', ``\fB\-A\fP'', ``\fB\-V\fP'' +and ``\fB\-W\fP'' flags. +.TP +.B \-N +For compatability with inews programs used with other news servers, +the ``\fB\-N\fP'' flag is treated as the ``\fB\-D\fP'' flag. +.TP +.B \-S +Normally, if a file named +.I .signature +exists in the user's home directory, +.I inews +will try to append it to the end of the article. If the file cannot be +read, the article will not be posted. +If the +.B \-S +flag is given, .signature will not be added. + +.SH NOTES + +.I inews +exits with a zero status if the article was successfully posted, or a +non-zero status if a check fails or posting fails. + +.PP +This +.I inews +is supplied with the +.B NOFFLE +news server. It is, in fact, the main +.BR noffle (1) +executable, and the +.I inews +program is just a link to the main +.I noffle +program. If the +.I noffle +program sees it is being executed under the name +.I inews +it adjusts its behaviour accordingly. + +.SH FILES + +.B NOFFLE +takes its configuration from a configuration file, by default +.I /etc/noffle.conf. +For a description of this file, see +.BR noffle.conf(5). + +.SH SEE ALSO + +.BR noffle (1), +.BR noffle.conf (5), + +.SH AUTHORS + +Markus Enzenberger +.br +Volker Wysk +.br +Jim Hague +.br +Uwe Hermann + +1998-2000. diff -r bfeea2bc09b6 -r 1c7303c71f66 docs/noffle.1 --- a/docs/noffle.1 Thu Oct 26 22:13:28 2000 +0100 +++ b/docs/noffle.1 Thu Oct 26 22:21:13 2000 +0100 @@ -1,5 +1,5 @@ .TH noffle 1 -.\" $Id: noffle.1 183 2000-07-25 12:14:54Z bears $ +.\" $Id: noffle.1 227 2000-10-26 21:21:13Z bears $ .SH NAME noffle \- Usenet package optimized for dialup connections. @@ -289,6 +289,15 @@ Remove group with name from list of groups that are presently to be fetched. +.SH NOTES + +If the +.I noffle +prgram sees the name under which it is executed is +.IR inews , +it changes its behaviour to that described in +.BR inews (1). + .SH FILES .B NOFFLE @@ -338,6 +347,7 @@ .SH SEE ALSO +.BR inews (1), .BR noffle.conf (5), .BR crond (8), .BR inetd (8), diff -r bfeea2bc09b6 -r 1c7303c71f66 docs/noffle.conf.5 --- a/docs/noffle.conf.5 Thu Oct 26 22:13:28 2000 +0100 +++ b/docs/noffle.conf.5 Thu Oct 26 22:21:13 2000 +0100 @@ -1,5 +1,5 @@ .TH noffle.conf 5 -.\" $Id: noffle.conf.5 199 2000-08-15 23:08:13Z bears $ +.\" $Id: noffle.conf.5 227 2000-10-26 21:21:13Z bears $ .SH NAME noffle.conf \- Configuration file for NOFFLE news server @@ -77,6 +77,30 @@ Default: 300 .TP +.B from-domain +When invoked as +.BR inews (1) +.B NOFFLE +will add a From: line to posts that lack one. The address given +uses the userid of the user who invoked +.B inews +and the specified domain. +.br +Default: The system domain + +.TP +.B organization +When invoked as +.BR inews (1) +.B NOFFLE +will, unless the -O flag is specified, add an Organization: line to +posts that lack one. If no +.B organization +is specified, the line is not added. +.br +Default: + +.TP .B mail-to
Receiver of failed postings. If empty then failed postings are returned to the sender (taking the address from the article's Sender, X-Sender or @@ -177,7 +201,7 @@ has its default value (empty) the header content is "!not-for-mail". Use the -.B default-path +.B path-header setting to provide alternate content for the Path: header. This will very rarely be necessary. .br diff -r bfeea2bc09b6 -r 1c7303c71f66 packages/redhat/noffle.spec --- a/packages/redhat/noffle.spec Thu Oct 26 22:13:28 2000 +0100 +++ b/packages/redhat/noffle.spec Thu Oct 26 22:21:13 2000 +0100 @@ -12,7 +12,6 @@ Requires: gdbm Requires: mailx -Requires: textutils %description NOFFLE is a news server optimized for low speed dialup connection to the @@ -47,6 +46,8 @@ install -m 0755 -o news -g news packages/redhat/noffle-expire $RPM_BUILD_ROOT/etc/cron.daily/noffle-expire %post +# Create inews line +ln -sf $RPM_BUILD_ROOT/%{prefix}/bin/noffle $RPM_BUILD_ROOT/%{prefix}/bin/inews # Update /etc/inetd.conf if ! grep -q '^[# \t]*nntp' /etc/inetd.conf ; then echo >> /etc/inetd.conf @@ -66,6 +67,7 @@ %postun # The script gets 0 on uninstall, 1 on upgrade. Don't remove # hosts.deny/inetd.conf lines on upgrade. +rm $RPM_BUILD_ROOT/%{prefix}/bin/inews if [ "$1" = 0 ] ; then if [ -f /etc/inetd.conf ] && grep noffle /etc/inetd.conf > /dev/null 2>&1 ; then @@ -93,6 +95,7 @@ %{prefix}/bin/noffle %{prefix}/man/man1/noffle.1 %{prefix}/man/man5/noffle.conf.5 +%{prefix}/bin/inews /etc/cron.daily/noffle-expire %dir /var/spool/noffle %dir /var/spool/noffle/data @@ -102,6 +105,9 @@ %dir /var/spool/noffle/overview %changelog +* Thu Oct 26 2000 Jim Hague +- Added inews link. + * Sun Jun 18 2000 Jim Hague - Version 1.0pre6-3 RPM - Changed /etc/noffle.conf mode to 0600 in case server password is required diff -r bfeea2bc09b6 -r 1c7303c71f66 src/configfile.c --- a/src/configfile.c Thu Oct 26 22:13:28 2000 +0100 +++ b/src/configfile.c Thu Oct 26 22:21:13 2000 +0100 @@ -6,7 +6,7 @@ SPOOLDIR VERSION - $Id: configfile.c 199 2000-08-15 23:08:13Z bears $ + $Id: configfile.c 227 2000-10-26 21:21:13Z bears $ */ #if HAVE_CONFIG_H @@ -92,6 +92,8 @@ int maxAutoSubscribeMode; AutoSubscribeModeEntry *autoSubscribeMode; Str pathHeader; + Str fromDomain; + Str organization; } config = { SPOOLDIR, /* spoolDir */ @@ -118,7 +120,9 @@ 0, /* numAutoSubscribeMode */ 0, /* maxAutoSubscribeMode */ NULL, /* autoSubscribeMode */ - "" /* pathHeader */ + "", /* pathHeader */ + "", /* fromDomain */ + "" /* organization */ }; const char * Cfg_spoolDir( void ) { return config.spoolDir; } @@ -138,6 +142,8 @@ const char * Cfg_mailTo( void ) { return config.mailTo; } int Cfg_defaultExpire( void ) { return config.defaultExpire; } const char * Cfg_pathHeader( void ) { return config.pathHeader; } +const char * Cfg_fromDomain( void ) { return config.fromDomain; } +const char * Cfg_organization( void ) { return config.organization; } void Cfg_beginServEnum( void ) @@ -363,6 +369,16 @@ } static void +getText( Str variable, const char *line ) +{ + const char *l; + + /* Skip command */ + l = Utl_restOfLn( line, 1 ); + Utl_cpyStr( variable, l ); +} + +static void getServ( const char *line ) { Str dummy, name, user, pass; @@ -808,11 +824,15 @@ getGroups( p, TRUE ); else if ( strcmp( "omitgroups", name ) == 0 ) getGroups( p, FALSE ); + else if ( strcmp( "path-header", name ) == 0 ) + getStr( config.pathHeader, p ); + else if ( strcmp( "from-domain", name ) == 0 ) + getStr( config.fromDomain, p ); /* The following need line because they may have uppercase data */ + else if ( strcmp( "organization", name ) == 0 ) + getText( config.organization, line ); else if ( strcmp( "server", name ) == 0 ) getServ( line ); - else if ( strcmp( "path-header", name ) == 0 ) - getStr( config.pathHeader, p ); else if ( strcmp( "filter", name ) == 0 ) getFilter( line ); else diff -r bfeea2bc09b6 -r 1c7303c71f66 src/configfile.h --- a/src/configfile.h Thu Oct 26 22:13:28 2000 +0100 +++ b/src/configfile.h Thu Oct 26 22:21:13 2000 +0100 @@ -3,7 +3,7 @@ Common declarations and handling of the configuration file. - $Id: configfile.h 155 2000-06-24 20:28:01Z bears $ + $Id: configfile.h 227 2000-10-26 21:21:13Z bears $ */ #ifndef CONFIGFILE_H @@ -61,6 +61,12 @@ /* Return Path: header contents */ const char * Cfg_pathHeader( void ); +/* Return domain for generated From: lines */ +const char * Cfg_fromDomain( void ); + +/* Return default Organization: contents */ +const char * Cfg_organization( void ); + void Cfg_read( void ); /* Get a new iterator for get group patterns for the given server */ diff -r bfeea2bc09b6 -r 1c7303c71f66 src/noffle.c --- a/src/noffle.c Thu Oct 26 22:13:28 2000 +0100 +++ b/src/noffle.c Thu Oct 26 22:21:13 2000 +0100 @@ -10,7 +10,7 @@ received for some seconds (to allow multiple clients connect at the same time). - $Id: noffle.c 197 2000-08-14 19:49:56Z bears $ + $Id: noffle.c 227 2000-10-26 21:21:13Z bears $ */ #if HAVE_CONFIG_H @@ -19,7 +19,6 @@ #include #include -#include #include #include #include @@ -182,18 +181,18 @@ } static Bool -doPost( void ) +doPost( FILE *f, unsigned flags ) { Str line; DynStr *s; Bool res; s = new_DynStr( 10000 ); - while ( fgets( line, MAXCHAR, stdin ) != NULL ) + while ( fgets( line, MAXCHAR, f ) != NULL ) DynStr_app( s, line ); res = TRUE; - if ( ! Post_open( DynStr_str( s ) ) ) + if ( ! Post_open( DynStr_str( s ), flags ) ) { fprintf( stderr, "Post failed: Malformed article.\n" ); res = FALSE; @@ -640,36 +639,144 @@ raise( sig ); } +static void +printInewsUsage( void ) +{ + static const char *msg = + "Usage: inews [-D] [-O] [-S] [input]\n" + " -D Debug - send article to stdout and don't post\n" + " -O Don't add Organization header\n" + " -S Don't add .signature\n" + " input File containing message, standard input if none specified.\n" + "For compatability, -h, -A, -V, -W are ignored and -N is\n" + "equivalent to -D.\n"; + fprintf( stderr, "%s", msg ); +} + +static int +doInews( int argc, char **argv ) +{ + int result; + int flags; + FILE *f; + + UNUSED( argc ); + + noffle.lockAtStartup = TRUE; + + /* Process options */ + flags = POST_ADD_ORG | POST_ADD_SIG | POST_ADD_FROM; + for ( ; argv[0] != NULL && argv[0][0] == '-' ; argv++ ) + { + if ( argv[0][2] != '\0' ) + { + printInewsUsage(); + return EXIT_FAILURE; + } + + switch( argv[0][1] ) + { + case 'h': + case 'A': + case 'V': + case 'W': + break; + case 'N': + case 'D': + flags |= POST_DEBUG; + break; + case 'O': + flags &= ~POST_ADD_ORG; + break; + case 'S': + flags &= ~POST_ADD_SIG; + break; + default: + printInewsUsage(); + return EXIT_FAILURE; + } + } + + if ( argv[0] == NULL ) + f = stdin; + else + { + f = fopen( argv[0], "r" ); + if ( f == NULL ) + { + Log_err( "Can't access %s (%s).", argv[0], strerror( errno ) ); + return EXIT_FAILURE; + } + } + + if ( ! initNoffle() ) + return EXIT_FAILURE; + result = EXIT_SUCCESS; + + if ( ! doPost( f, flags ) ) + result = EXIT_FAILURE; + + if ( f != stdin ) + fclose( f ); + + closeNoffle(); + return result; +} + +static int +getArgLetter(const char *arg) +{ + int res; + struct option + { + const char *longOpt; + const char *opt; + } options[] = + { + { "--article", "-a" }, + { "--cancel", "-c" }, + { "--create", "-C" }, + { "--database", "-d" }, + { "--delete", "-D" }, + { "--expire", "-e" }, + { "--fetch", "-f" }, + { "--groups", "-g" }, + { "--help", "-h" }, + { "--list", "-l" }, + { "--modify", "-m" }, + { "--offline", "-o" }, + { "--online", "-n" }, + { "--post", "-p" }, + { "--query", "-q" }, + { "--server", "-r" }, + { "--requested", "-R" }, + { "--subscribe-over", "-s" }, + { "--subscribe-full", "-S" }, + { "--subscribe-thread", "-t" }, + { "--unsubscribe", "-u" }, + { "--version", "-v" }, + { NULL, NULL } + + }; + struct option *opt; + + res = -1; + for ( opt = options; opt->opt != NULL; opt++ ) + if ( strcmp( arg, opt->longOpt ) == 0 || + strcmp( arg, opt->opt ) == 0 ) + { + res = opt->opt[1]; + break; + } + + return res; +} + int main ( int argc, char **argv ) { int c, result; - struct option longOptions[] = - { - { "article", required_argument, NULL, 'a' }, - { "cancel", required_argument, NULL, 'c' }, - { "create", required_argument, NULL, 'C' }, - { "database", no_argument, NULL, 'd' }, - { "delete", required_argument, NULL, 'D' }, - { "expire", no_argument, NULL, 'e' }, - { "fetch", no_argument, NULL, 'f' }, - { "groups", no_argument, NULL, 'g' }, - { "help", no_argument, NULL, 'h' }, - { "list", no_argument, NULL, 'l' }, - { "modify", required_argument, NULL, 'm' }, - { "offline", no_argument, NULL, 'o' }, - { "online", no_argument, NULL, 'n' }, - { "post", no_argument, NULL, 'p' }, - { "query", required_argument, NULL, 'q' }, - { "server", no_argument, NULL, 'r' }, - { "requested", no_argument, NULL, 'R' }, - { "subscribe-over", required_argument, NULL, 's' }, - { "subscribe-full", required_argument, NULL, 'S' }, - { "subscribe-thread", required_argument, NULL, 't' }, - { "unsubscribe", required_argument, NULL, 'u' }, - { "version", no_argument, NULL, 'v' }, - { NULL, 0, NULL, 0 } - }; - + const char *cmdname, *p; + signal( SIGSEGV, bugReport ); signal( SIGABRT, logSignal ); signal( SIGFPE, logSignal ); @@ -677,55 +784,72 @@ signal( SIGINT, logSignal ); signal( SIGTERM, logSignal ); signal( SIGPIPE, logSignal ); - c = getopt_long( argc, argv, "a:c:C:dD:efghlm:onpq:rRs:S:t:u:v", - longOptions, NULL ); + + /* Find last component of command name. */ + cmdname = argv[0]; + p = strrchr( cmdname, '/' ); + if ( p != NULL ) + cmdname = ++p; + + argv++; + argc--; + + /* Were we invoked as inews? */ + if ( strcmp( cmdname, "inews" ) == 0 ) + return doInews( argc, argv ); + + c = -1; + if ( *argv != NULL ) + { + c = getArgLetter( *argv ); + argv++; + argc--; + } + noffle.lockAtStartup = ! ( c == 'r' || c == 'h' ); if ( ! initNoffle() ) return EXIT_FAILURE; result = EXIT_SUCCESS; switch ( c ) { - case 0: - /* Options that set a flag. */ - break; case 'a': - if ( ! optarg ) + if ( *argv == NULL ) { fprintf( stderr, "Option -a needs argument.\n" ); result = EXIT_FAILURE; } else - doArt( optarg ); + doArt( *argv ); break; case 'c': - if ( ! optarg ) + if ( *argv == NULL ) { fprintf( stderr, "Option -c needs argument.\n" ); result = EXIT_FAILURE; } else - doCancel( optarg ); + doCancel( *argv ); break; case 'C': - if ( ! optarg ) + if ( *argv == NULL ) { fprintf( stderr, "Option -C needs argument.\n" ); result = EXIT_FAILURE; } else - doCreateLocalGroup( optarg ); + doCreateLocalGroup( *argv ); break; case 'd': doDb(); break; case 'D': - if ( ! optarg ) + if ( *argv == NULL ) { fprintf( stderr, "Option -D needs argument.\n" ); result = EXIT_FAILURE; } else - doDeleteLocalGroup( optarg ); + doDeleteLocalGroup( *argv ); break; case 'e': doExpire(); @@ -744,13 +868,13 @@ doList(); break; case 'm': - if ( ! optarg ) + if ( *argv == NULL ) { fprintf( stderr, "Option -m needs argument.\n" ); result = EXIT_FAILURE; } else - if ( ! doModify( optarg, argc - optind, &argv[ optind ] ) ) + if ( ! doModify( *argv, --argc, ++argv ) ) result = EXIT_FAILURE; break; case 'n': @@ -766,24 +890,24 @@ Online_set( FALSE ); break; case 'p': - if ( ! doPost() ) + if ( ! doPost( stdin, 0 ) ) result = EXIT_FAILURE; break; case 'q': - if ( ! optarg ) + if ( *argv == NULL ) { fprintf( stderr, "Option -q needs argument.\n" ); result = EXIT_FAILURE; } else { - if ( strcmp( optarg, "groups" ) == 0 ) + if ( strcmp( *argv, "groups" ) == 0 ) noffle.queryGrps = TRUE; - else if ( strcmp( optarg, "desc" ) == 0 ) + else if ( strcmp( *argv, "desc" ) == 0 ) noffle.queryDsc = TRUE; else { - fprintf( stderr, "Unknown argument -q %s\n", optarg ); + fprintf( stderr, "Unknown argument -q %s\n", *argv ); result = EXIT_FAILURE; } doQuery(); @@ -794,43 +918,43 @@ Server_run(); break; case 'R': - doRequested( optarg ); + doRequested( *argv ); break; case 's': - if ( ! optarg ) + if ( *argv == NULL ) { fprintf( stderr, "Option -s needs argument.\n" ); result = EXIT_FAILURE; } else - result = doSubscribe( optarg, OVER ); + result = doSubscribe( *argv, OVER ); break; case 'S': - if ( ! optarg ) + if ( *argv == NULL ) { fprintf( stderr, "Option -S needs argument.\n" ); result = EXIT_FAILURE; } else - doSubscribe( optarg, FULL ); + doSubscribe( *argv, FULL ); break; case 't': - if ( ! optarg ) + if ( *argv == NULL ) { fprintf( stderr, "Option -t needs argument.\n" ); result = EXIT_FAILURE; } else - result = doSubscribe( optarg, THREAD ); + result = doSubscribe( *argv, THREAD ); break; case 'u': - if ( ! optarg ) + if ( *argv == NULL ) { fprintf( stderr, "Option -u needs argument.\n" ); result = EXIT_FAILURE; } else - doUnsubscribe( optarg ); + doUnsubscribe( *argv ); break; case '?': /* Error message already printed by getopt_long */ diff -r bfeea2bc09b6 -r 1c7303c71f66 src/post.c --- a/src/post.c Thu Oct 26 22:13:28 2000 +0100 +++ b/src/post.c Thu Oct 26 22:21:13 2000 +0100 @@ -1,14 +1,18 @@ /* post.c - $Id: post.c 202 2000-08-23 09:52:25Z enz $ + $Id: post.c 227 2000-10-26 21:21:13Z bears $ */ #if HAVE_CONFIG_H #include #endif +#include +#include #include +#include +#include #include "post.h" #include #include "common.h" @@ -25,6 +29,9 @@ #include "util.h" #include "portable.h" +#define BEGIN_SIG "-- " +#define SIG_FILE "/.signature" + struct OverInfo { Str subject; @@ -43,10 +50,11 @@ ItemList *control; /* Control message? NULL if not */ Bool approved; /* Has Approved: header? */ Bool posted; /* Has it been put in the article database? */ + int flags; /* Posting flags */ struct OverInfo over; }; -static struct Article article = { NULL, NULL, NULL, FALSE, FALSE, +static struct Article article = { NULL, NULL, NULL, FALSE, FALSE, 0, { "", "", "", "", "", 0, 0 } }; /* Add the article to a group. */ @@ -163,14 +171,15 @@ { DynStr * s; Str line, field, value; - Bool replyToFound, pathFound; + Bool replyToFound, pathFound, orgFound; time_t t; + int sigLines; s = new_DynStr( 10000 ); article.text = s; memset( &article.over, 0, sizeof( article.over ) ); - replyToFound = pathFound = FALSE; + replyToFound = pathFound = orgFound = FALSE; /* Grab header lines first, getting overview info as we go. */ while ( ( p = Utl_getHeaderLn( line, p ) ) != NULL @@ -222,6 +231,11 @@ pathFound = TRUE; DynStr_appLn( s, line ); } + else if ( strcmp ( field, "organization" ) == 0 ) + { + orgFound = TRUE; + DynStr_appLn( s, line ); + } else if ( strcmp ( field, "x-sender" ) == 0 ) { DynStr_app( s, "X-NOFFLE-X-Sender: " ); @@ -236,8 +250,22 @@ /* Now sort header-related issues */ if ( article.over.from[ 0 ] == '\0' ) { - Log_err( "Posted message has no From field" ); - return FALSE; + if ( article.flags & POST_ADD_FROM ) + { + Log_dbg( "Adding From field to posted message." ); + DynStr_app( s, "From: " ); + if ( ! Prt_genFromHdr( article.over.from ) ) + { + Log_err( "Can't generate From field" ); + return FALSE; + } + DynStr_appLn( s, article.over.from ); + } + else + { + Log_err( "Posted message has no From field" ); + return FALSE; + } } if ( article.over.subject[ 0 ] == '\0' ) { @@ -295,6 +323,20 @@ DynStr_appLn( s, article.over.from ); } + /* Ensure Organization header if required */ + if ( ( ! orgFound ) && ( article.flags & POST_ADD_ORG ) ) + { + Str org; + + Utl_cpyStr( org, Cfg_organization() ); + if ( org[ 0 ] != '\0' ) + { + Log_dbg( "Adding Organization field to posted message." ); + DynStr_app( s, "Organization: " ); + DynStr_appLn( s, org ); + } + } + /* OK, header ready to roll. Something to accompany it? */ if ( p == NULL || p[ 0 ] == '\0' ) { @@ -305,9 +347,68 @@ /* Add the empty line separating header and body */ DynStr_appLn( s, "" ); - /* Now pop on the rest of the body and count the lines & bytes */ + /* Now pop on the rest of the body */ DynStr_app( s, p ); - for ( p++, article.over.lines = 0; *p != '\0'; p++ ) + + /* Add a signature if requested to do so and if one found. */ + sigLines = 0; + if ( article.flags & POST_ADD_SIG ) + { + Str sigfile; + struct passwd *pwd; + FILE *f; + + /* Generate sig file path */ + pwd = getpwuid( getuid() ); + Utl_cpyStr( sigfile, pwd->pw_dir ); + Utl_catStr( sigfile, SIG_FILE ); + + f = fopen( sigfile, "r" ); + if ( f == NULL ) + { + /* If err is ENOENT, file doesn't exist. This is OK. */ + if ( errno != ENOENT ) + { + Log_err( "Can't access .signature file (%s), " + "article not posted.", + strerror( errno ) ); + return FALSE; + } + } + else + { + /* OK, try to add it. */ + Str sline; + + Log_dbg( "Adding .signature to posted message." ); + + DynStr_appLn( s, BEGIN_SIG ); + sigLines++; + while ( Prt_getLn( sline, f, 0 ) ) + { + DynStr_appLn( s, sline ); + sigLines++; + } + + if ( ferror( f ) ) + { + Log_err( "Error reading .signature file (%s), " + "article not posted.", + strerror( errno ) ); + fclose( f ); + return FALSE; + } + + fclose( f ); + } + } + + /* + * Count the lines & bytes. This counts the original number of + * lines in the supplied body, so add in the number of signature + * lines added, including the separator. + */ + for ( p++, article.over.lines = sigLines; *p != '\0'; p++ ) if ( *p == '\n' ) article.over.lines++; article.over.bytes = DynStr_len( s ); @@ -428,7 +529,7 @@ /* Register an article for posting. */ Bool -Post_open( const char * text ) +Post_open( const char * text, unsigned flags ) { if ( article.text != NULL ) { @@ -436,6 +537,8 @@ return FALSE; } + article.flags = flags; + if ( ! getArticleText( text ) ) return FALSE; @@ -453,6 +556,12 @@ Bool Post_post( void ) { + if ( article.flags & POST_DEBUG ) + { + fputs( DynStr_str( article.text ), stdout ); + return TRUE; + } + if ( ! checkPostableNewsgroup() ) return FALSE; diff -r bfeea2bc09b6 -r 1c7303c71f66 src/post.h --- a/src/post.h Thu Oct 26 22:13:28 2000 +0100 +++ b/src/post.h Thu Oct 26 22:21:13 2000 +0100 @@ -5,7 +5,7 @@ necessary, and add to the local database and/or queue for external posting as appropriate. - $Id: post.h 159 2000-06-24 20:47:40Z bears $ + $Id: post.h 227 2000-10-26 21:21:13Z bears $ */ #ifndef POST_H @@ -17,9 +17,15 @@ #include "common.h" +/* Flags for Post_open */ +#define POST_ADD_ORG 0001 +#define POST_DEBUG 0002 +#define POST_ADD_SIG 0004 +#define POST_ADD_FROM 0010 + /* Register an article for posting. */ Bool -Post_open( const char * text ); +Post_open( const char * text, unsigned flags ); /* Post the article. */ Bool diff -r bfeea2bc09b6 -r 1c7303c71f66 src/protocol.c --- a/src/protocol.c Thu Oct 26 22:13:28 2000 +0100 +++ b/src/protocol.c Thu Oct 26 22:21:13 2000 +0100 @@ -1,7 +1,7 @@ /* protocol.c - $Id: protocol.c 217 2000-09-23 10:40:35Z enz $ + $Id: protocol.c 227 2000-10-26 21:21:13Z bears $ */ #if HAVE_CONFIG_H @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -79,10 +80,10 @@ if ( ret == NULL ) return FALSE; len = strlen( line ); - if ( line[ len - 1 ] == '\n' ) + if ( len > 0 && line[ len - 1 ] == '\n' ) { line[ len - 1 ] = '\0'; - if ( line[ len - 2 ] == '\r' ) + if ( len > 1 && line[ len - 2 ] == '\r' ) line[ len - 2 ] = '\0'; } Log_dbg( "[R] %s", line ); @@ -344,6 +345,63 @@ Utl_catStr( pathHdr, "!not-for-mail" ); } +Bool +Prt_genFromHdr( Str fromHdr ) +{ + Str name, domain; + const char *nameval; + struct passwd *pwd; + + /* First get the domain to use. If config empty, use FQDN */ + Utl_cpyStr( domain, Cfg_fromDomain() ); + + if ( DynStr_len( domain ) == 0 ) + if ( ! getFQDN( domain ) ) + Utl_catStr( domain, "unknown" ); + + /* Now get pwd for the username */ + pwd = getpwuid( getuid() ); + if ( pwd == NULL ) + return FALSE; + + /* Now for their name - use env NAME if available */ + nameval = getenv( "NAME" ); + if ( nameval != NULL ) + Utl_cpyStr( name, nameval ); + else + { + char *p; + + /* + Extract from GECOS field. Following the lead of the INN inews, + ignore leading stuff like "23-" "stuff]-" or "stuff -" as well + as trailing whitespace, or anything that comes after + a comma or semicolon. + */ + nameval = pwd->pw_gecos; + p = strchr( nameval, '-' ); + if ( p != NULL && p > nameval && + ( p[-1] == ']' || p[-1] == ' ' || isdigit( p[ -1 ] ) ) ) + nameval = p; + p = strrchr( nameval, ',' ); + if ( p != NULL ) + *p = '\0'; + p = strchr( nameval, ';' ); + if ( p != NULL ) + *p = '\0'; + Utl_cpyStr( name, nameval ); + } + + /* OK, build From: contents */ + Utl_cpyStr( fromHdr, pwd->pw_name ); + Utl_catStr( fromHdr, "@" ); + Utl_catStr( fromHdr, domain ); + Utl_catStr( fromHdr, " (" ); + Utl_catStr( fromHdr, name ); + Utl_catStr( fromHdr, ")" ); + + return TRUE; +} diff -r bfeea2bc09b6 -r 1c7303c71f66 src/protocol.h --- a/src/protocol.h Thu Oct 26 22:13:28 2000 +0100 +++ b/src/protocol.h Thu Oct 26 22:21:13 2000 +0100 @@ -4,7 +4,7 @@ Functions related with the NNTP protocol which are useful for both the server and the client. - $Id: protocol.h 217 2000-09-23 10:40:35Z enz $ + $Id: protocol.h 227 2000-10-26 21:21:13Z bears $ */ #ifndef PRT_H @@ -111,4 +111,7 @@ void Prt_genPathHdr( Str pathHdr, const char *from ); +Bool +Prt_genFromHdr( Str fromHdr ); + #endif