Compare commits

...

14 commits

Author SHA1 Message Date
2e4c6984e6 Add option to add common headers to all projects.
Signed-off-by: Thomas Hochstein <thh@thh.name>
2026-02-07 19:38:53 +01:00
66435ceda0 Update POD.
Signed-off-by: Thomas Hochstein <thh@thh.name>
2026-02-07 19:21:03 +01:00
cc881f7897 Warn of unencoded 8bit characters in header or body if -d is set.
Signed-off-by: Thomas Hochstein <thh@thh.name>
2026-02-07 19:20:10 +01:00
1e7bccbbec Disabled projects must be posted, if forced.
Signed-off-by: Thomas Hochstein <thh@thh.name>
2026-02-07 18:59:38 +01:00
3801b61d77 Check for illegal headers and stop posting.
Signed-off-by: Thomas Hochstein <thh@thh.name>
2026-02-07 18:53:42 +01:00
41d307a2fe Accept a posting-frequency of "never", too.
Signed-off-by: Thomas Hochstein <thh@thh.name>
2026-02-07 18:48:27 +01:00
67182bc643 Fix ChangeLog format.
Signed-off-by: Thomas Hochstein <thh@thh.name>
2026-01-31 22:41:22 +01:00
c969b7c2c1 Add conversion script for old status files.
Signed-off-by: Thomas Hochstein <thh@thh.name>
2026-01-31 11:19:33 +01:00
de5163c877 Change version suffix to '-pre'(release).
Signed-off-by: Thomas Hochstein <thh@thh.name>
2026-01-24 18:50:36 +01:00
ae4714e30d Imply -o for --testing as long as -n is not set.
Signed-off-by: Thomas Hochstein <thh@thh.name>
2026-01-24 18:42:41 +01:00
6b73bdfdd3 Don't modify headers while --testing if -o is set.
Signed-off-by: Thomas Hochstein <thh@thh.name>
2026-01-24 18:31:48 +01:00
9177618643 Merge branch 'maint'
* maint:
  Release 1.0.1

Signed-off-by: Thomas Hochstein <thh@thh.name>
2026-01-24 18:19:32 +01:00
7e7285d126 Merge branch 'maint'
* maint:
  Remove debugging code.
  Add %t placeholder for Message-ID.
  Fix attribution.

Signed-off-by: Thomas Hochstein <thh@thh.name>
2026-01-24 15:56:28 +01:00
2bfafad4cb Bump version.
Signed-off-by: Thomas Hochstein <thh@thh.name>
2026-01-23 22:32:57 +01:00
3 changed files with 284 additions and 207 deletions

View file

@ -1,3 +1,14 @@
yapfaq 1.1.0 (unreleased)
* --test: Set Supersedes and don't modify Message-ID if -o is set.
* --test: Force -o if -n is not set.
* Add conversion script for old status files.
* Accept a posting-frequency of "never", too.
* Check for illegal headers and stop posting, if found.
* Disabled projects must be posted, if forced.
* Warn of unencoded 8bit characters in header or body if -d is set.
* Update POD.
* Add an option for a common headers file for all projects.
yapfaq 1.0.1 (2025-01-24) yapfaq 1.0.1 (2025-01-24)
* Add %t placeholder for Message-ID (feature parity with 0.9). * Add %t placeholder for Message-ID (feature parity with 0.9).
* Remove debugging code. * Remove debugging code.

View file

@ -19,7 +19,7 @@
# It can be redistributed and/or modified under the same terms under # It can be redistributed and/or modified under the same terms under
# which Perl itself is published. # which Perl itself is published.
my $VERSION = "1.0.1"; my $VERSION = "1.1.0-pre";
(my $NAME = $0) =~ s#^.*/##; (my $NAME = $0) =~ s#^.*/##;
use utf8; use utf8;
@ -45,6 +45,8 @@ $Config{'nntp-pass'} = ''; # password for AUTHINFO
$Config{'force-auth'} = 0; # set to 1 to force authentication $Config{'force-auth'} = 0; # set to 1 to force authentication
$Config{'starttls'} = 0; # set to 1 to use STARTTLS if possible $Config{'starttls'} = 0; # set to 1 to use STARTTLS if possible
$Config{'xtraheaders'} = ''; # path to file with extra headers
$Config{'verbose'} = 0; # set to 1 to get status messages $Config{'verbose'} = 0; # set to 1 to get status messages
$Config{'debug'} = 0; # set to 1 to get some debug output, $Config{'debug'} = 0; # set to 1 to get some debug output,
# set to 2 for NNTP debug output # set to 2 for NNTP debug output
@ -100,6 +102,7 @@ GetOptions ('p|project=s' => \$OptProject,
'nntp-pass=s' => \$Config{'nntp-pass'}, 'nntp-pass=s' => \$Config{'nntp-pass'},
'starttls!' => \$Config{'starttls'}, 'starttls!' => \$Config{'starttls'},
'force-auth!' => \$Config{'force-auth'}, 'force-auth!' => \$Config{'force-auth'},
'xtraheaders=s' => \$Config{'xtraheaders'},
'v|verbose!' => \$Config{'verbose'}, 'v|verbose!' => \$Config{'verbose'},
'd|debug!' => \$Config{'debug'}, 'd|debug!' => \$Config{'debug'},
'c|config' => \&ShowConf, 'c|config' => \&ShowConf,
@ -112,6 +115,9 @@ if ($OptSimulation) {
$Config{'verbose'} = 1; $Config{'verbose'} = 1;
} }
# -t implies -o if -n is not set
$OptOutput = 1 if $OptTest && !$OptNewsgroup;
### create list of @Projects from $Config{'datadir'} unless -p is set ### create list of @Projects from $Config{'datadir'} unless -p is set
my @Projects; my @Projects;
if (!$OptProject) { if (!$OptProject) {
@ -374,6 +380,11 @@ sub BuildPosting {
warn "W: '$BodyFile' not found.\n"; warn "W: '$BodyFile' not found.\n";
return ''; return '';
} }
my $XtraHeaderFile = $Config{'xtraheaders'} if $Config{'xtraheaders'};
if ($Config{'xtraheaders'} && not -r $XtraHeaderFile) {
warn "W: '$XtraHeaderFile' not found.\n";
return '';
}
# today (TD) # today (TD)
my $TD = DateTime->now->set_time_zone('local'); my $TD = DateTime->now->set_time_zone('local');
@ -401,6 +412,10 @@ sub BuildPosting {
print "- Reading headers ($Project.hdr) and body ($Project.txt).\n" if $Config{'debug'}; print "- Reading headers ($Project.hdr) and body ($Project.txt).\n" if $Config{'debug'};
my @Headers = path($HeaderFile)->lines; my @Headers = path($HeaderFile)->lines;
my @Body = path($BodyFile)->lines; my @Body = path($BodyFile)->lines;
if ($Config{'xtraheaders'}) {
print "- Reading extra headers ($XtraHeaderFile).\n" if $Config{'debug'};
push @Headers, path($XtraHeaderFile)->lines ;
}
my %Header = &ParseHeaders(@Headers); my %Header = &ParseHeaders(@Headers);
# check for mandatory headers # check for mandatory headers
@ -409,6 +424,17 @@ sub BuildPosting {
return ''; return '';
} }
# check for illegal headers
my $FoundIllegalHeader = 0;
foreach (qw/Date User-Agent X-Newsreader X-Mailer Injection-Date
Injection-Info NNTP-Posting-Date NNTP-Posting-Host X-Trace/) {
if ($Header{lc($_)}) {
warn "W: $_ header may not be set in '$HeaderFile'.\n";
$FoundIllegalHeader = 1;
}
}
return '' if $FoundIllegalHeader;
# add Date: # add Date:
push @Headers, 'Date: ' . $TD->strftime('%a, %d %b %Y %H:%M:%S %z') . "\n"; push @Headers, 'Date: ' . $TD->strftime('%a, %d %b %Y %H:%M:%S %z') . "\n";
# add missing Message-ID: # add missing Message-ID:
@ -416,6 +442,16 @@ sub BuildPosting {
# add User-Agent # add User-Agent
push @Headers, "User-Agent: $NAME/$VERSION\n"; push @Headers, "User-Agent: $NAME/$VERSION\n";
# check for unencoded 8bit characters in header or body in --debug mode
# taken from tinews.pl
if ($Config{'debug'}) {
print "- Raw 8-bit data in headers.\n" if (grep {/[\x80-\xff]/} @Headers);
# check for MIME headers and warn for 8bit characters in body if missing
if (!defined($Header{'mime-version'}) || !defined($Header{'content-type'})) {
print "- 8bit data in body without MIME-headers.\n" if (grep {/[\x80-\xff]/} @Body);
}
}
# parse pseudo headers from body # parse pseudo headers from body
my ($InRealBody,$LastModified,$PostingFrequency); my ($InRealBody,$LastModified,$PostingFrequency);
foreach (@Body) { foreach (@Body) {
@ -475,7 +511,7 @@ sub BuildPosting {
$_ =~ s/\%p/$$/g; $_ =~ s/\%p/$$/g;
$_ =~ s/\%t/$TimeStamp/g; $_ =~ s/\%t/$TimeStamp/g;
# add random part in test mode # add random part in test mode
if ($OptTest) { if ($OptTest && !$OptOutput) {
my $random = sprintf("%08X", rand(0xFFFFFFFF)); my $random = sprintf("%08X", rand(0xFFFFFFFF));
$_ =~ s/</<test-$random-/; $_ =~ s/</<test-$random-/;
} }
@ -491,7 +527,7 @@ sub BuildPosting {
} }
# add Supersedes: if set # add Supersedes: if set
if (/^Supersedes: /) { if (/^Supersedes: /) {
if ($LastMID && !$OptTest) { if ($LastMID && (!$OptTest or $OptOutput)) {
$_= "Supersedes: $LastMID\n"; $_= "Supersedes: $LastMID\n";
} else { } else {
$_ = ''; $_ = '';
@ -512,8 +548,8 @@ sub BuildPosting {
} }
} }
# not due if Posting-Freqency is "none" # not due if Posting-Freqency is "none" or never
if ($PostingFrequency =~ /none/) { if ($PostingFrequency =~ /none|never/ && !$OptForce) {
print "... is disabled.\n" if $Config{'verbose'} or $Config{'debug'}; print "... is disabled.\n" if $Config{'verbose'} or $Config{'debug'};
return ''; return '';
} }
@ -724,6 +760,14 @@ Use a TLS encrypted connection (via STARTTLS) if available.
You can override this option on the command line by using You can override this option on the command line by using
B<--starttls> or B<--nostarttls> accordingly. B<--starttls> or B<--nostarttls> accordingly.
=item B<xtraheaders> = I<path>
Path to a file with common headers for all project files. Those
headers will be appended to each project.
You can override this option on the command line by using
B<--xtraheaders> = I<path>.
=back =back
=head2 Project files =head2 Project files
@ -737,7 +781,14 @@ files need to be in B<datadir>.
Needs to have at least I<From:>, I<Subject:> and I<Newsgroups:> and Needs to have at least I<From:>, I<Subject:> and I<Newsgroups:> and
can contain all other headers that the posting should have. Headers can contain all other headers that the posting should have. Headers
must conform to RFC 5536 and RFC 5322 and use MIME encoded words for must conform to RFC 5536 and RFC 5322 and use MIME encoded words for
8bit characters. B<yapfaq> won't convert headers. 8bit characters. B<yapfaq> won't convert headers, but will warn of
unencoded 8bit characters in B<--debug> mode. Longer headers should
be folded; B<yapfaq> won't fold headers.
The headers file must not contain any of the following headers:
I<Date:>, I<User-Agent:>, I<X-Newsreader:>, I<X-Mailer:>,
I<Injection-Date:>, I<Injection-Info:>, I<NNTP-Posting-Date:>,
I<NNTP-Posting-Host:> or I<X-Trace:>.
I<Subject:> may contain a I<%LM> placeholder that will be replaced I<Subject:> may contain a I<%LM> placeholder that will be replaced
with the I<Last-modified:> pseudo-header from the text file with the I<Last-modified:> pseudo-header from the text file
@ -799,7 +850,8 @@ I<Last-modified:> and I<Posting-frequency> will be evaluated by
B<yapfaq>. B<yapfaq>.
If your content contains 8bit characters, you'll need suitable MIME If your content contains 8bit characters, you'll need suitable MIME
headers in your headers file. headers in your headers file. B<yapfaq> will warn of unencoded 8bit
characters with missung MIME headers in B<--debug> mode.
B<Example text file with pseudo-headers> B<Example text file with pseudo-headers>
@ -843,7 +895,7 @@ unique I<Message-ID:> (and no I<Supersedes:> header).
Don't post via NNTP, but print to STDOUT. Don't post via NNTP, but print to STDOUT.
Combine with B<--test> to avoid updating project status. Use B<--test> instead to avoid updating project status.
Intended for testing purposes or to pipe in another program like Intended for testing purposes or to pipe in another program like
I<inews> or I<tinews.pl>. If you want to pipe the output to another I<inews> or I<tinews.pl>. If you want to pipe the output to another
@ -861,20 +913,18 @@ Display this man page and exit.
=item B<-s>, B<--simulation> =item B<-s>, B<--simulation>
Simulation mode. Don't post, just show which projects would be due. Simulation mode. Don't post, just show which projects would be due.
Implies B<--test> and B<--verbose>. Implies B<--test> (without B<--output>) and B<--verbose>.
Can be combined with B<--project> to show if just one project is due. Can be combined with B<--project> to show if just one project is due.
=item B<-t>, B<--test> =item B<-t>, B<--test>
Test mode. Don't update project status (time and Message-ID of last Test mode. Don't update project status (time and Message-ID of last
posting), dont' add a I<Supersedes:> header and modify the posting); if project is posted to Usenet, dont' add a I<Supersedes:>
I<Message-ID:> with a random part. header and modify the I<Message-ID:> with a random part.
The text(s) will still be posted if due or forced by B<--force>. Implies B<--output> (to redirect output to STDOUT) as long as
B<--newsgroup> (to override the I<Newsgroups:> header) is not set.
Combine with B<--output> to redirect output to STDOUT or with
B<--newsgroup> to override the I<Newsgroups:> header.
=item B<-V>, B<--version> =item B<-V>, B<--version>
@ -911,16 +961,16 @@ that are not:
Do a test run of your I<example> text and and print it on STDOUT Do a test run of your I<example> text and and print it on STDOUT
(whether ist is due or not): (whether ist is due or not):
yapfaq.pl -t -f -o -p example yapfaq.pl -t -f -p example
(or yapfaq.pl -tfop example) (or yapfaq.pl -tfp example)
The same, with debugging output (add "-d"): The same, with debugging output (add "-d"):
yapfaq.pl -tfdop example yapfaq.pl -tfdp example
Force a test post of your I<example> text to I<alt.test>, even if Force a test post of your I<example> text to I<alt.test>, even if
the text is not due to be posted (same as before, just replace "-o" the text is not due to be posted (same as before, just add
by "-n alt-test"): "-n alt-test"):
yapfaq.pl -t -f -p example -n alt.test yapfaq.pl -t -f -p example -n alt.test

16
contrib/convert.pl Executable file
View file

@ -0,0 +1,16 @@
#! /usr/bin/perl -w
#
# Convert pre-1.0 yapfaq status file to new format
#
# Usage: convert.pl < old.txt.cfg > new.cfg
while (<>) {
if (/Lastpost/) {
$_ =~ /Lastpost:\s(\d\d?\.\d\d?\.\d\d\d\d)/;
print "Last-Posted: $1\n";
}
if (/LastMID/) {
$_ =~ /LastMID:\s(<[^>]+>)/;
print "Last-Message-ID: $1\n";
}
}