"Fossies" - the Fresh Open Source Software Archive  

Source code changes of the file "tools/tinews.pl" between
tin-2.4.4.tar.xz and tin-2.4.5.tar.xz

About: TIN is a threaded NNTP and spool based UseNet newsreader.

tinews.pl  (tin-2.4.4.tar.xz):tinews.pl  (tin-2.4.5.tar.xz)
#! /usr/bin/perl -w #! /usr/bin/perl -w
# #
# reads an article on STDIN, mails any copies if required, # reads an article on STDIN, mails any copies if required,
# signs the article and posts it. # signs the article and posts it.
# #
# #
# Copyright (c) 2002-2020 Urs Janssen <urs@tin.org>, # Copyright (c) 2002-2021 Urs Janssen <urs@tin.org>,
# Marc Brockschmidt <marc@marcbrockschmidt.de> # Marc Brockschmidt <marc@marcbrockschmidt.de>
# #
# Redistribution and use in source and binary forms, with or without # Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions # modification, are permitted provided that the following conditions
# are met: # are met:
# #
# 1. Redistributions of source code must retain the above copyright notice, # 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer. # this list of conditions and the following disclaimer.
# #
# 2. Redistributions in binary form must reproduce the above copyright # 2. Redistributions in binary form must reproduce the above copyright
skipping to change at line 43 skipping to change at line 43
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # 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 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE. # POSSIBILITY OF SUCH DAMAGE.
# #
# #
# TODO: - extend debug mode to not delete tmp-files and be more verbose # TODO: - extend debug mode to not delete tmp-files and be more verbose
# - add pid to pgptmpf to allow multiple simultaneous instances # - add pid to pgptmpf to allow multiple simultaneous instances
# - check for /etc/nntpserver (and /etc/news/server) # - check for /etc/nntpserver (and /etc/news/server)
# - add $PGPOPTS, $PGPPATH and $GNUPGHOME support # - add $PGPOPTS, $PGPPATH and $GNUPGHOME support
# - cleanup and remove duplicated code # - cleanup and remove duplicated code
# - option to convert CRLF to LF in input
# - use STARTTLS (if Net::NNTP is recent enough and server supports it)?
# - quote inpupt properly before passing to shell # - quote inpupt properly before passing to shell
# - $ENV{'NEWSHOST'} / $ENV{'NNTPSERVER'} and $ENV{'NNTPPORT'}
# do have higher precedence than settings in the script and
# config-file, but config-settig SSL may override $ENV{'NNTPPORT'}
# - if (!defined $ENV{'GPG_TTY'}) {if (open(my $T,'-|','tty')) { # - if (!defined $ENV{'GPG_TTY'}) {if (open(my $T,'-|','tty')) {
# chomp(my $tty=<$T>); close($T); # chomp(my $tty=<$T>); close($T);
# $ENV{'GPG_TTY'}=$tty if($tty =~ m/^\//)}} # $ENV{'GPG_TTY'}=$tty if($tty =~ m/^\//)}}
# for gpg? # for gpg?
# # - option to break long header lines?
# use Text::Wrap; $Text::Wrap::columns=998; wrap("","\t",$_);
# - option to trim References
# ...
use strict; use strict;
use warnings; use warnings;
# version Number # version Number
my $version = "1.1.51"; my $version = "1.1.57";
my %config; my %config;
# configuration, may be overwritten via ~/.tinewsrc # configuration, may be overwritten via ~/.tinewsrc
$config{'NNTPServer'} = 'news'; # your NNTP servers name, may be set via $config{'nntp-server'} = 'news'; # your NNTP servers name, may be set via
$NNTPSERVER $NNTPSERVER
$config{'NNTPPort'} = 119; # NNTP-port, may be set via $NNTPPORT $config{'nntp-port'} = 119; # NNTP-port, may be set via $NNTPPORT
$config{'NNTPUser'} = ''; # username for nntp-auth, may be set via $config{'nntp-user'} = ''; # username for nntp-auth, may be set via ~/.newsa
~/.newsauth or ~/.nntpauth uth or ~/.nntpauth
$config{'NNTPPass'} = ''; # password for nntp-auth, may be set via $config{'nntp-pass'} = ''; # password for nntp-auth, may be set via ~/.newsa
~/.newsauth or ~/.nntpauth uth or ~/.nntpauth
$config{'PGPSigner'} = ''; # sign as who? $config{'ssl'} = 0; # set to 1 to use NNTPS if possible
$config{'PGPPass'} = ''; # pgp2 only
$config{'PathtoPGPPass'}= ''; # pgp2, pgp5, pgp6 and gpg $config{'pgp-signer'} = ''; # sign as who?
$config{'PGPPassFD'} = 9; # file descriptor used for input redirection of P $config{'pgp-pass'} = ''; # pgp2 only
athtoPGPPass; GPG1, GPG2, PGP5 and PGP6 only $config{'path-to-pgp-pass'}= ''; # pgp2, pgp5, pgp6 and gpg
$config{'pgp-pass-fd'} = 9; # file descriptor used for input redirection of p
ath-to-pgp-pass; GPG1, GPG2, PGP5 and PGP6 only
$config{'pgp'} = '/usr/bin/pgp'; # path to pgp $config{'pgp'} = '/usr/bin/pgp'; # path to pgp
$config{'PGPVersion'} = '2'; # Use 2 for 2.X, 5 for PGP5, 6 for PGP6, GPG or G PG1 for GPG1 and GPG2 for GPG2 $config{'pgp-version'} = '2'; # Use 2 for 2.X, 5 for PGP5, 6 for PGP6, GPG or G PG1 for GPG1 and GPG2 for GPG2
$config{'digest-algo'} = 'MD5';# Digest Algorithm for GPG. Must be supported by your installation $config{'digest-algo'} = 'MD5';# Digest Algorithm for GPG. Must be supported by your installation
$config{'Interactive'} = 'yes';# allow interactive usage $config{'interactive'} = 'yes';# allow interactive usage
$config{'verbose'} = 0; # set to 1 to get warning messages
$config{'debug'} = 0; # set to 1 to get some debug output
$config{'sig-path'} = glob('~/.signature'); # path to signature
$config{'add-signature'}= 'yes';# Add $config{'sig-path'} to posting if there is
no sig
$config{'sig-max-lines'}= 4; # max number of signatures lines
$config{'sig_path'} = glob('~/.signature'); # path to signature $config{'max-header-length'} = 998; # RFC 5536
$config{'add_signature'}= 'yes';# Add $config{'sig_path'} to posting if there is
no sig
$config{'sig_max_lines'}= 4; # max number of signatures lines
$config{'sendmail'} = '/usr/sbin/sendmail -i -t'; # set to '' to disa ble mail-actions $config{'sendmail'} = '/usr/sbin/sendmail -i -t'; # set to '' to disa ble mail-actions
$config{'pgptmpf'} = 'pgptmp'; # temporary file for PGP. $config{'pgptmpf'} = 'pgptmp'; # temporary file for PGP.
$config{'pgpheader'} = 'X-PGP-Sig'; $config{'pgpheader'} = 'X-PGP-Sig';
$config{'pgpbegin'} = '-----BEGIN PGP SIGNATURE-----'; # Begin o f PGP-Signature $config{'pgpbegin'} = '-----BEGIN PGP SIGNATURE-----'; # Begin o f PGP-Signature
$config{'pgpend'} = '-----END PGP SIGNATURE-----'; # End of PGP-Signature $config{'pgpend'} = '-----END PGP SIGNATURE-----'; # End of PGP-Signature
$config{'canlock_algorithm'} = 'sha1'; # Digest algorithm used for cance $config{'canlock-algorithm'} = 'sha1'; # Digest algorithm used for cance
l-lock and cancel-key; sha1, sha256 and sha512 are supported l-lock and cancel-key; sha1, sha256 and sha512 are supported
# $config{'canlock_secret'} = '~/.cancelsecret'; # Path to canlock # $config{'canlock-secret'} = '~/.cancelsecret'; # Path to canlock
secret file secret file
# $config{'ignore_headers'} = ''; # headers to be ignored during si gning # $config{'ignore-headers'} = ''; # headers to be ignored during si gning
$config{'PGPSignHeaders'} = ['From', 'Newsgroups', 'Subject', 'Control', $config{'pgp-sign-headers'} = ['From', 'Newsgroups', 'Subject', 'Control',
'Supersedes', 'Followup-To', 'Date', 'Injection-Date', 'Sender', 'Approve d', 'Supersedes', 'Followup-To', 'Date', 'Injection-Date', 'Sender', 'Approve d',
'Message-ID', 'Reply-To', 'Cancel-Key', 'Also-Control', 'Message-ID', 'Reply-To', 'Cancel-Key', 'Also-Control',
'Distribution']; 'Distribution'];
$config{'PGPorderheaders'} = ['from', 'newsgroups', 'subject', 'control', $config{'pgp-order-headers'} = ['from', 'newsgroups', 'subject', 'control',
'supersedes', 'followup-To', 'date', 'injection-date', 'organization', 'supersedes', 'followup-To', 'date', 'injection-date', 'organization',
'lines', 'sender', 'approved', 'distribution', 'message-id', 'lines', 'sender', 'approved', 'distribution', 'message-id',
'references', 'reply-to', 'mime-version', 'content-type', 'references', 'reply-to', 'mime-version', 'content-type',
'content-transfer-encoding', 'summary', 'keywords', 'cancel-lock', 'content-transfer-encoding', 'summary', 'keywords', 'cancel-lock',
'cancel-key', 'also-control', 'x-pgp', 'user-agent']; 'cancel-key', 'also-control', 'x-pgp', 'user-agent'];
################################################################################ ################################################################################
use Getopt::Long qw(GetOptions); use Getopt::Long qw(GetOptions);
use Net::NNTP; use Net::NNTP;
skipping to change at line 124 skipping to change at line 135
# read config file (first match counts) from # read config file (first match counts) from
# $XDG_CONFIG_HOME/tinewsrc ~/.config/tinewsrc ~/.tinewsrc # $XDG_CONFIG_HOME/tinewsrc ~/.config/tinewsrc ~/.tinewsrc
# if present # if present
my $TINEWSRC = undef; my $TINEWSRC = undef;
my (@try, %seen); my (@try, %seen);
if ($ENV{'XDG_CONFIG_HOME'}) { if ($ENV{'XDG_CONFIG_HOME'}) {
push(@try, (glob("$ENV{'XDG_CONFIG_HOME'}/tinewsrc"))[0]); push(@try, (glob("$ENV{'XDG_CONFIG_HOME'}/tinewsrc"))[0]);
} }
push(@try, (glob('~/.config/tinewsrc'))[0], (glob('~/.tinewsrc'))[0]); push(@try, (glob('~/.config/tinewsrc'))[0], (glob('~/.tinewsrc'))[0]);
foreach (grep { ! $seen{$_}++ } @try) { foreach (@try) {
last if (open($TINEWSRC, '<', $_)); last if (open($TINEWSRC, '<', $_));
$TINEWSRC = undef; $TINEWSRC = undef;
} }
if (defined($TINEWSRC)) { if (defined($TINEWSRC)) {
my $changes = 0;
while (defined($_ = <$TINEWSRC>)) { while (defined($_ = <$TINEWSRC>)) {
if (m/^([^#\s=]+)\s*=\s*(\S[^#]+)/io) { if (m/^([^#\s=]+)\s*=\s*(\S[^#]+)/io) {
chomp($config{$1} = $2); # rename pre 1.1.56 tinewsrc-var names
my $key = $1;
my $val = $2;
$key =~ s#^followupto#follow-to# && $changes++;
$key =~ s#^replyto#reply-to# && $changes++;
$key =~ s#^NNTP(?!\-).#NNTP-# && $changes++;
$key =~ s#^PathtoPGPPass#path-to-pgp-pass# && $changes++;
$key =~ s#^PGPorderheaders#pgp-order-headers# && $changes
++;
$key =~ s#^PGPPassFD#pgp-pass-fd# && $changes++;
$key =~ s#^PGPSignHeaders#pgp-sign-headers# && $changes++
;
$key =~ s#^PGP(?!\-).#PGP-# && $changes++;
$key =~ s#_#-# && $changes++;
chomp($config{lc($key)} = $val);
} }
} }
close($TINEWSRC); close($TINEWSRC);
print "Old style tinewsrc option names found, you should adjust them.\n" if ($changes && ($config{'verbose'} || $config{'debug'}));
} }
# as of tinews 1.1.51 we use 3 args open() to pipe to sendmail # as of tinews 1.1.51 we use 3 args open() to pipe to sendmail
# thus we remove any leading '|' to avoid syntax errors; # thus we remove any leading '|' to avoid syntax errors;
# for redirections use cat etc.pp., eg. 'cat > /tmp/foo' # for redirections use cat etc.pp., eg. 'cat > /tmp/foo'
$config{'sendmail'} =~ s/^\s*\|\s*//io; $config{'sendmail'} =~ s/^\s*\|\s*//io;
# digest-algo is case sensitive and should be all uppercase # digest-algo is case sensitive and should be all uppercase
$config{'digest-algo'} = uc($config{'digest-algo'}); $config{'digest-algo'} = uc($config{'digest-algo'});
# these env-vars have higher priority (order is important) # these env-vars have higher priority (order is important)
$config{'NNTPServer'} = $ENV{'NEWSHOST'} if ($ENV{'NEWSHOST'}); $config{'nntp-server'} = $ENV{'NEWSHOST'} if ($ENV{'NEWSHOST'});
$config{'NNTPServer'} = $ENV{'NNTPSERVER'} if ($ENV{'NNTPSERVER'}); $config{'nntp-server'} = $ENV{'NNTPSERVER'} if ($ENV{'NNTPSERVER'});
$config{'NNTPPort'} = $ENV{'NNTPPORT'} if ($ENV{'NNTPPORT'}); $config{'nntp-port'} = $ENV{'NNTPPORT'} if ($ENV{'NNTPPORT'});
# Get options: # Get options:
$Getopt::Long::ignorecase=0; $Getopt::Long::ignorecase=0;
$Getopt::Long::bundling=1; $Getopt::Long::bundling=1;
GetOptions('A|V|W|O|no-organization|h|headers' => [], # do nothing GetOptions('A|V|W|h|headers' => [], # do nothing
'debug|D|N' => \$config{'debug'}, 'debug|D|N' => \$config{'debug'},
'port|p=i' => \$config{'NNTPPort'}, 'port|p=i' => \$config{'nntp-port'},
'no-sign|X' => \$config{'no_sign'}, 'no-sign|X' => \$config{'no-sign'},
'no-control|R' => \$config{'no_control'}, 'no-control|R' => \$config{'no-control'},
'no-signature|S' => \$config{'no_signature'}, 'no-signature|S' => \$config{'no-signature'},
'no-canlock|L' => \$config{'no_canlock'}, 'no-canlock|L' => \$config{'no-canlock'},
'no-injection-date|I' => \$config{'no-injection-date'}, 'no-injection-date|I' => \$config{'no-injection-date'},
'force-auth|Y' => \$config{'force_auth'}, 'no-organization|O' => \$config{'no-organization'},
'force-auth|Y' => \$config{'force-auth'},
'approved|a=s' => \$config{'approved'}, 'approved|a=s' => \$config{'approved'},
'control|c=s' => \$config{'control'}, 'control|c=s' => \$config{'control'},
'canlock-algorithm=s' => \$config{'canlock_algorithm'}, 'canlock-algorithm=s' => \$config{'canlock-algorithm'},
'distribution|d=s' => \$config{'distribution'}, 'distribution|d=s' => \$config{'distribution'},
'expires|e=s' => \$config{'expires'}, 'expires|e=s' => \$config{'expires'},
'from|f=s' => \$config{'from'}, 'from|f=s' => \$config{'from'},
'ignore-headers|i=s' => \$config{'ignore_headers'}, 'ignore-headers|i=s' => \$config{'ignore-headers'},
'followupto|w=s' => \$config{'followup-to'}, 'followup-to|w=s' => \$config{'followup-to'},
'newsgroups|n=s' => \$config{'newsgroups'}, 'newsgroups|n=s' => \$config{'newsgroups'},
'replyto|r=s' => \$config{'reply-to'}, 'reply-to|r=s' => \$config{'reply-to'},
'savedir|s=s' => \$config{'savedir'}, 'savedir|s=s' => \$config{'savedir'},
'ssl|nntps' => \$config{'ssl'},
'subject|t=s' => \$config{'subject'}, 'subject|t=s' => \$config{'subject'},
'references|F=s' => \$config{'references'}, 'references|F=s' => \$config{'references'},
'organization|o=s' => \$config{'organization'}, 'organization|o=s' => \$config{'organization'},
'path|x=s' => \$config{'path'}, 'path|x=s' => \$config{'path'},
'help|H' => \$config{'help'}, 'help|H' => \$config{'help'},
'version|v' => \$config{'version'} 'transform' => \$config{'transform'},
'verbose|v' => \$config{'verbose'},
'version' => \$config{'version'}
); );
foreach (@ARGV) { foreach (@ARGV) {
print STDERR "Unknown argument $_."; print STDERR "Unknown argument $_.";
usage(); usage();
} }
if ($config{'version'}) { if ($config{'version'}) {
version(); version();
exit 0; exit 0;
} }
usage() if ($config{'help'}); usage() if ($config{'help'});
my $sha_mod=undef; # check if SSL support is available
if ($config{'ssl'}) {
eval "Net::NNTP->can_ssl";
if ($@) {
warn "Your Net::NNTP doesn't support SSL.\n" if ($config{'debug'}
|| $config{'verbose'});
$config{'ssl'} = 0;
}
}
# and now adjust default port depending on SSL requested and
# available or not
if ($config{'ssl'}) {
$config{'nntp-port'} = 563 if ($config{'nntp-port'} == 119);
} else {
$config{'nntp-port'} = 119 if ($config{'nntp-port'} == 563);
}
my $sha_mod = undef;
# Cancel-Locks require some more modules # Cancel-Locks require some more modules
if ($config{'canlock_secret'} && !$config{'no_canlock'}) { if ($config{'canlock-secret'} && !$config{'no-canlock'}) {
$config{'canlock_algorithm'} = lc($config{'canlock_algorithm'}); $config{'canlock-algorithm'} = lc($config{'canlock-algorithm'});
# we support sha1, sha256 and sha512, fallback to sha1 if something else is given # we support sha1, sha256 and sha512, fallback to sha1 if something else is given
if (!($config{'canlock_algorithm'} =~ /^sha(1|256|512)$/)) { if (!($config{'canlock-algorithm'} =~ /^sha(1|256|512)$/)) {
warn "Digest algorithm " . $config{'canlock_algorithm'} . " not s warn "Digest algorithm " . $config{'canlock-algorithm'} . " not s
upported. Falling back to sha1.\n" if $config{'debug'}; upported. Falling back to sha1.\n" if ($config{'debug'} || $config{'verbose'});
$config{'canlock_algorithm'} = 'sha1'; $config{'canlock-algorithm'} = 'sha1';
} }
if ($config{'canlock_algorithm'} eq 'sha1') { if ($config{'canlock-algorithm'} eq 'sha1') {
foreach ('Digest::SHA qw(sha1)', 'Digest::SHA1()') { foreach ('Digest::SHA qw(sha1)', 'Digest::SHA1()') {
eval "use $_"; eval "use $_";
if (!$@) { if (!$@) {
($sha_mod = $_) =~ s#( qw\(sha1\)|\(\))##; ($sha_mod = $_) =~ s#( qw\(sha1\)|\(\))##;
last; last;
} }
} }
foreach ('MIME::Base64()', 'Digest::HMAC_SHA1()') { foreach ('MIME::Base64()', 'Digest::HMAC_SHA1()') {
eval "use $_"; eval "use $_";
if ($@ || !defined($sha_mod)) { if ($@ || !defined($sha_mod)) {
$config{'no_canlock'} = 1; $config{'no-canlock'} = 1;
warn "Cancel-Locks disabled: Can't locate ".$_."\ warn "Cancel-Locks disabled: Can't locate ".$_."\
n" if $config{'debug'}; n" if ($config{'debug'} || $config{'verbose'});
last; last;
} }
} }
} elsif ($config{'canlock_algorithm'} eq 'sha256') { } elsif ($config{'canlock-algorithm'} eq 'sha256') {
foreach ('MIME::Base64()', 'Digest::SHA qw(sha256 hmac_sha256)') { foreach ('MIME::Base64()', 'Digest::SHA qw(sha256 hmac_sha256)') {
eval "use $_"; eval "use $_";
if ($@) { if ($@) {
$config{'no_canlock'} = 1; $config{'no-canlock'} = 1;
warn "Cancel-Locks disabled: Can't locate ".$_."\ warn "Cancel-Locks disabled: Can't locate ".$_."\
n" if $config{'debug'}; n" if ($config{'debug'} || $config{'verbose'});
last; last;
} }
} }
} else { } else {
foreach ('MIME::Base64()', 'Digest::SHA qw(sha512 hmac_sha512)') { foreach ('MIME::Base64()', 'Digest::SHA qw(sha512 hmac_sha512)') {
eval "use $_"; eval "use $_";
if ($@) { if ($@) {
$config{'no_canlock'} = 1; $config{'no-canlock'} = 1;
warn "Cancel-Locks disabled: Can't locate ".$_."\ warn "Cancel-Locks disabled: Can't locate ".$_."\
n" if $config{'debug'}; n" if ($config{'debug'} || $config{'verbose'});
last; last;
} }
} }
} }
} }
my $term = Term::ReadLine->new('tinews'); my $term = Term::ReadLine->new('tinews');
my $attribs = $term->Attribs; my $attribs = $term->Attribs;
my $in_header = 1; my $in_header = 1;
my (%Header, @Body, $PGPCommand); my (%Header, @Body, $PGPCommand);
if (! $config{'no_sign'}) { if (! $config{'no-sign'}) {
$config{'PGPSigner'} = $ENV{'SIGNER'} if ($ENV{'SIGNER'}); $config{'pgp-signer'} = $ENV{'SIGNER'} if ($ENV{'SIGNER'});
$config{'PathtoPGPPass'} = $ENV{'PGPPASSFILE'} if ($ENV{'PGPPASSFILE'}); $config{'path-to-pgp-pass'} = $ENV{'PGPPASSFILE'} if ($ENV{'PGPPASSFILE'}
if ($config{'PathtoPGPPass'}) { );
open(my $PGPPass, '<', (glob($config{'PathtoPGPPass'}))[0]) or if ($config{'path-to-pgp-pass'}) {
$config{'Interactive'} && die("$0: Can't open ".$config{' open(my $pgppass, '<', (glob($config{'path-to-pgp-pass'}))[0]) or
PathtoPGPPass'}.": $!"); $config{'interactive'} && die("$0: Can't open ".$config{'
chomp($config{'PGPPass'} = <$PGPPass>); path-to-pgp-pass'}.": $!");
close($PGPPass); chomp($config{'pgp-pass'} = <$pgppass>);
} close($pgppass);
if ($config{'PGPVersion'} eq '2' && $ENV{'PGPPASS'}) { }
$config{'PGPPass'} = $ENV{'PGPPASS'}; if ($config{'pgp-version'} eq '2' && $ENV{'PGPPASS'}) {
$config{'pgp-pass'} = $ENV{'PGPPASS'};
} }
} }
# Remove unwanted headers from PGPSignHeaders # Remove unwanted headers from pgp-sign-headers
if (${config{'ignore_headers'}}) { if (${config{'ignore-headers'}}) {
my @hdr_to_ignore = split(/,/, ${config{'ignore_headers'}}); my @hdr_to_ignore = split(/,/, ${config{'ignore-headers'}});
foreach my $hdr (@hdr_to_ignore) { foreach my $hdr (@hdr_to_ignore) {
@{$config{'PGPSignHeaders'}} = map {lc($_) eq lc($hdr) ? () : $_} @{$config{'PGPSignHeaders'}}; @{$config{'pgp-sign-headers'}} = map {lc($_) eq lc($hdr) ? () : $ _} @{$config{'pgp-sign-headers'}};
} }
} }
# Read the message and split the header # Read the message and split the header
readarticle(\%Header, \@Body); readarticle(\%Header, \@Body);
# Add signature if there is none # Add signature if there is none
if (!$config{'no_signature'}) { if (!$config{'no-signature'}) {
if ($config{'add_signature'} && !grep {/^-- /} @Body) { if ($config{'add-signature'} && !grep {/^-- /} @Body) {
if (-r glob($config{'sig_path'})) { if (-r glob($config{'sig-path'})) {
my $l = 0; my $l = 0;
push @Body, "-- \n"; push @Body, "-- \n";
open(my $SIGNATURE, '<', glob($config{'sig_path'})) or di e("Can't open " . $config{'sig_path'} . ": $!"); open(my $SIGNATURE, '<', glob($config{'sig-path'})) or di e("Can't open " . $config{'sig-path'} . ": $!");
while (<$SIGNATURE>) { while (<$SIGNATURE>) {
die $config{'sig_path'} . " longer than " . $conf ig{'sig_max_lines'}. " lines!" if (++$l > $config{'sig_max_lines'}); die $config{'sig-path'} . " longer than " . $conf ig{'sig-max-lines'}. " lines!" if (++$l > $config{'sig-max-lines'});
push @Body, $_; push @Body, $_;
} }
close($SIGNATURE); close($SIGNATURE);
} else { } else {
if ($config{'debug'}) { warn "Tried to add " . $config{'sig-path'} . ", but it is
warn "Tried to add " . $config{'sig_path'} . ", b unreadable.\n" if ($config{'debug'} || $config{'verbose'});
ut it is unreadable";
}
} }
} }
} }
# import headers set in the environment # import headers set in the environment
if (!defined($Header{'reply-to'})) { if (!defined($Header{'reply-to'})) {
if ($ENV{'REPLYTO'}) { if ($ENV{'REPLYTO'}) {
chomp($Header{'reply-to'} = "Reply-To: " . $ENV{'REPLYTO'}); chomp($Header{'reply-to'} = "Reply-To: " . $ENV{'REPLYTO'});
$Header{'reply-to'} .= "\n"; $Header{'reply-to'} .= "\n";
} }
skipping to change at line 324 skipping to change at line 367
} }
$Header{'date'} = "Date: ".getdate()."\n" if (!defined($Header{'date'}) || $Head er{'date'} !~ m/^[^\s:]+: .+/o); $Header{'date'} = "Date: ".getdate()."\n" if (!defined($Header{'date'}) || $Head er{'date'} !~ m/^[^\s:]+: .+/o);
$Header{'injection-date'} = "Injection-Date: ".getdate()."\n" if (!$config{'no-i njection-date'}); $Header{'injection-date'} = "Injection-Date: ".getdate()."\n" if (!$config{'no-i njection-date'});
if (defined($Header{'user-agent'})) { if (defined($Header{'user-agent'})) {
chomp $Header{'user-agent'}; chomp $Header{'user-agent'};
$Header{'user-agent'} = $Header{'user-agent'}." ".$pname."/".$version."\n "; $Header{'user-agent'} = $Header{'user-agent'}." ".$pname."/".$version."\n ";
} }
delete $Header{'x-pgp-key'} if (!$config{'no_sign'} && defined($Header{'x-pgp-ke delete $Header{'x-pgp-key'} if (!$config{'no-sign'} && defined($Header{'x-pgp-ke
y'})); y'}));
delete $Header{'organization'} if ($config{'no-organization'} && defined($Header
{'organization'}));
# No control messages allowed when using -R|--no-control # No control. No control. You have no control.
if ($config{'no_control'} and $Header{control}) { if ($config{'no-control'} and $Header{control}) {
print STDERR "No control messages allowed.\n"; print STDERR "No control messages allowed.\n";
exit 1; exit 1;
} }
# various checks # various checks
if ($config{'debug'}) { if ($config{'debug'} || $config{'verbose'}) {
foreach (keys %Header) { foreach (keys %Header) {
warn "Raw 8-bit data in the following header:\n$Header{$_}" if ($ Header{$_} =~ m/[\x80-\xff]/o); warn "Raw 8-bit data in the following header:\n$Header{$_}\n" if ($Header{$_} =~ m/[\x80-\xff]/o);
} }
if (!defined($Header{'mime-version'}) || !defined($Header{'content-type'} ) || !defined($Header{'content-transfer-encoding'})) { if (!defined($Header{'mime-version'}) || !defined($Header{'content-type'} ) || !defined($Header{'content-transfer-encoding'})) {
warn "8bit body without MIME-headers\n" if (grep {/[\x80-\xff]/} @Body); warn "8bit body without MIME-headers\n" if (grep {/[\x80-\xff]/} @Body);
} }
} }
# try ~/.newsauth if no $config{'NNTPPass'} was set # try ~/.newsauth if no $config{'nntp-pass'} was set
if (!$config{'NNTPPass'}) { if (!$config{'nntp-pass'}) {
my ($l, $server, $pass, $user); my ($l, $server, $pass, $user);
if (-r (glob("~/.newsauth"))[0]) { if (-r (glob("~/.newsauth"))[0]) {
open (my $NEWSAUTH, '<', (glob("~/.newsauth"))[0]) or die("Can't open ~/.newsauth: $!"); open (my $NEWSAUTH, '<', (glob("~/.newsauth"))[0]) or die("Can't open ~/.newsauth: $!");
while ($l = <$NEWSAUTH>) { while ($l = <$NEWSAUTH>) {
chomp $l; chomp $l;
next if ($l =~ m/^[#\s]/); next if ($l =~ m/^[#\s]/);
($server, $pass, $user) = split(/\s+\b/, $l); ($server, $pass, $user) = split(/\s+\b/, $l);
last if ($server =~ m/\Q$config{'NNTPServer'}\E/); last if ($server =~ m/\Q$config{'nntp-server'}\E/);
} }
close($NEWSAUTH); close($NEWSAUTH);
if ($pass && $server =~ m/\Q$config{'NNTPServer'}\E/) { if ($pass && $server =~ m/\Q$config{'nntp-server'}\E/) {
$config{'NNTPPass'} = $pass; $config{'nntp-pass'} = $pass;
$config{'NNTPUser'} = $user || getlogin || getpwuid($<) | $config{'nntp-user'} = $user || getlogin || getpwuid($<)
| $ENV{USER}; || $ENV{USER};
} else { } else {
$pass = $user = ""; $pass = $user = "";
} }
} }
# try ~/.nntpauth if we still got no password # try ~/.nntpauth if we still got no password
if (!$pass) { if (!$pass) {
if (-r (glob("~/.nntpauth"))[0]) { if (-r (glob("~/.nntpauth"))[0]) {
open (my $NNTPAUTH, '<', (glob("~/.nntpauth"))[0]) or die ("Can't open ~/.nntpauth: $!"); open (my $NNTPAUTH, '<', (glob("~/.nntpauth"))[0]) or die ("Can't open ~/.nntpauth: $!");
while ($l = <$NNTPAUTH>) { while ($l = <$NNTPAUTH>) {
chomp $l; chomp $l;
next if ($l =~ m/^[#\s]/); next if ($l =~ m/^[#\s]/);
($server, $user, $pass) = split(/\s+\b/, $l); ($server, $user, $pass) = split(/\s+\b/, $l);
last if ($server =~ m/\Q$config{'NNTPServer'}\E/) ; last if ($server =~ m/\Q$config{'nntp-server'}\E/ );
} }
close($NNTPAUTH); close($NNTPAUTH);
if ($pass && $server =~ m/\Q$config{'NNTPServer'}\E/) { if ($pass && $server =~ m/\Q$config{'nntp-server'}\E/) {
$config{'NNTPPass'} = $pass; $config{'nntp-pass'} = $pass;
$config{'NNTPUser'} = $user || getlogin || getpwu $config{'nntp-user'} = $user || getlogin || getpw
id($<) || $ENV{USER}; uid($<) || $ENV{USER};
} }
} }
} }
} }
if (! $config{'savedir'} && defined($Header{'newsgroups'}) && !defined($Header{' # instead of abort posting just to prefetch a Messsage-ID we should (try
message-id'})) { # to keep) the the session open instead
my $Server = AuthonNNTP(); if (!($config{'no-sign'} && $config{'no-canlock'})) {
my $ServerMsg = $Server->message(); if (! $config{'savedir'} && defined($Header{'newsgroups'}) && !defined($H
$Server->datasend('.'); eader{'message-id'})) {
$Server->dataend(); my $Server = AuthonNNTP();
$Server->quit(); my $ServerMsg = $Server->message();
$Header{'message-id'} = "Message-ID: $1\n" if ($ServerMsg =~ m/(<\S+\@\S+ $Header{'message-id'} = "Message-ID: $1\n" if ($ServerMsg =~ m/(<
>)/o); \S+\@\S+>)/o);
} #$Server->datasend('.'); # dataend() already sends "."
$Server->dataend();
if (!defined($Header{'message-id'})) { $Server->quit();
my $hname;
eval "use Sys::Hostname";
if ($@) {
chomp($hname = `hostname`);
} else {
$hname = hostname();
} }
my ($hostname,) = gethostbyname($hname);
if (defined($hostname) && $hostname =~ m/\./io) { if (!defined($Header{'message-id'})) {
$Header{'message-id'} = "Message-ID: " . sprintf("<N%xI%xT%x@%s>\ my $hname;
n", $>, timelocal(localtime), $$, $hostname); eval "use Sys::Hostname";
if ($@) {
chomp($hname = `hostname`);
} else {
$hname = hostname();
}
my ($hostname,) = gethostbyname($hname);
if (defined($hostname) && $hostname =~ m/\./io) {
$Header{'message-id'} = "Message-ID: " . sprintf("<N%xI%x
T%x@%s>\n", $>, timelocal(localtime), $$, $hostname);
}
} }
} }
# add Cancel-Lock (and Cancel-Key) header(s) if requested # add Cancel-Lock (and Cancel-Key) header(s) if requested
if ($config{'canlock_secret'} && !$config{'no_canlock'} && defined($Header{'mess if ($config{'canlock-secret'} && !$config{'no-canlock'} && defined($Header{'mess
age-id'})) { age-id'})) {
open(my $CANLock, '<', (glob($config{'canlock_secret'}))[0]) or die("$0: open(my $CANLock, '<', (glob($config{'canlock-secret'}))[0]) or die("$0:
Can't open " . $config{'canlock_secret'} . ": $!"); Can't open " . $config{'canlock-secret'} . ": $!");
chomp(my $key = <$CANLock>); chomp(my $key = <$CANLock>);
close($CANLock); close($CANLock);
(my $data = $Header{'message-id'}) =~ s#^Message-ID: ##i; (my $data = $Header{'message-id'}) =~ s#^Message-ID: ##i;
chomp $data; chomp $data;
my $cancel_key = buildcancelkey($data, $key); my $cancel_key = buildcancelkey($data, $key);
my $cancel_lock = buildcancellock($cancel_key, $sha_mod); my $cancel_lock = buildcancellock($cancel_key, $sha_mod);
if (defined($Header{'cancel-lock'})) { if (defined($Header{'cancel-lock'})) {
chomp $Header{'cancel-lock'}; chomp $Header{'cancel-lock'};
$Header{'cancel-lock'} .= " " . $config{'canlock_algorithm'} . ": " . $cancel_lock . "\n"; $Header{'cancel-lock'} .= " " . $config{'canlock-algorithm'} . ": " . $cancel_lock . "\n";
} else { } else {
$Header{'cancel-lock'} = "Cancel-Lock: " . $config{'canlock_algor ithm'} . ":" . $cancel_lock . "\n"; $Header{'cancel-lock'} = "Cancel-Lock: " . $config{'canlock-algor ithm'} . ":" . $cancel_lock . "\n";
} }
if ((defined($Header{'supersedes'}) && $Header{'supersedes'} =~ m/^Supers edes:\s+<\S+>\s*$/i) || (defined($Header{'control'}) && $Header{'control'} =~ m/ ^Control:\s+cancel\s+<\S+>\s*$/i) ||(defined($Header{'also-control'}) && $Header {'also-control'} =~ m/^Also-Control:\s+cancel\s+<\S+>\s*$/i)) { if ((defined($Header{'supersedes'}) && $Header{'supersedes'} =~ m/^Supers edes:\s+<\S+>\s*$/i) || (defined($Header{'control'}) && $Header{'control'} =~ m/ ^Control:\s+cancel\s+<\S+>\s*$/i) ||(defined($Header{'also-control'}) && $Header {'also-control'} =~ m/^Also-Control:\s+cancel\s+<\S+>\s*$/i)) {
if (defined($Header{'also-control'}) && $Header{'also-control'} = ~ m/^Also-Control:\s+cancel\s+/i) { if (defined($Header{'also-control'}) && $Header{'also-control'} = ~ m/^Also-Control:\s+cancel\s+/i) {
($data = $Header{'also-control'}) =~ s#^Also-Control:\s+c ancel\s+##i; ($data = $Header{'also-control'}) =~ s#^Also-Control:\s+c ancel\s+##i;
chomp $data; chomp $data;
$cancel_key = buildcancelkey($data, $key); $cancel_key = buildcancelkey($data, $key);
} else { } else {
if (defined($Header{'control'}) && $Header{'control'} =~ m/^Control: cancel /i) { if (defined($Header{'control'}) && $Header{'control'} =~ m/^Control: cancel /i) {
($data = $Header{'control'})=~ s#^Control:\s+canc el\s+##i; ($data = $Header{'control'})=~ s#^Control:\s+canc el\s+##i;
skipping to change at line 439 skipping to change at line 488
} else { } else {
if (defined($Header{'supersedes'})) { if (defined($Header{'supersedes'})) {
($data = $Header{'supersedes'}) =~ s#^Sup ersedes: ##i; ($data = $Header{'supersedes'}) =~ s#^Sup ersedes: ##i;
chomp $data; chomp $data;
$cancel_key = buildcancelkey($data, $key) ; $cancel_key = buildcancelkey($data, $key) ;
} }
} }
} }
if (defined($Header{'cancel-key'})) { if (defined($Header{'cancel-key'})) {
chomp $Header{'cancel-key'}; chomp $Header{'cancel-key'};
$Header{'cancel-key'} .= " " . $config{'canlock_algorithm '} . ":" . $cancel_key . "\n"; $Header{'cancel-key'} .= " " . $config{'canlock-algorithm '} . ":" . $cancel_key . "\n";
} else { } else {
$Header{'cancel-key'} = "Cancel-Key: " . $config{'canlock _algorithm'} . ":" . $cancel_key . "\n"; $Header{'cancel-key'} = "Cancel-Key: " . $config{'canlock -algorithm'} . ":" . $cancel_key . "\n";
} }
} }
} }
# set Posted-And-Mailed if we send a mailcopy to someone else # set Posted-And-Mailed if we send a mailcopy to someone else
if ($config{'sendmail'} && defined($Header{'newsgroups'}) && (defined($Header{'t o'}) || defined($Header{'cc'}) || defined($Header{'bcc'}))) { if ($config{'sendmail'} && defined($Header{'newsgroups'}) && (defined($Header{'t o'}) || defined($Header{'cc'}) || defined($Header{'bcc'}))) {
foreach ('to', 'bcc', 'cc') { foreach ('to', 'bcc', 'cc') {
if (defined($Header{$_}) && $Header{$_} ne $Header{'from'}) { if (defined($Header{$_}) && $Header{$_} ne $Header{'from'}) {
$Header{'posted-and-mailed'} = "Posted-And-Mailed: yes\n" ; $Header{'posted-and-mailed'} = "Posted-And-Mailed: yes\n" ;
last; last;
} }
} }
} }
if (! $config{'no_sign'}) { if (! $config{'no-sign'}) {
if (!$config{'PGPSigner'}) { if (!$config{'pgp-signer'}) {
chomp($config{'PGPSigner'} = $Header{'from'}); chomp($config{'pgp-signer'} = $Header{'from'});
$config{'PGPSigner'} =~ s/^[^\s:]+: (.*)/$1/; $config{'pgp-signer'} =~ s/^[^\s:]+: (.*)/$1/;
} }
$PGPCommand = getpgpcommand($config{'PGPVersion'}); $PGPCommand = getpgpcommand($config{'pgp-version'});
} }
# (re)move mail-headers # (re)move mail-headers
my ($To, $Cc, $Bcc, $Newsgroups) = ''; my ($To, $Cc, $Bcc, $Newsgroups) = '';
$To = $Header{'to'} if (defined($Header{'to'})); $To = $Header{'to'} if (defined($Header{'to'}));
$Cc = $Header{'cc'} if (defined($Header{'cc'})); $Cc = $Header{'cc'} if (defined($Header{'cc'}));
$Bcc = $Header{'bcc'} if (defined($Header{'bcc'})); $Bcc = $Header{'bcc'} if (defined($Header{'bcc'}));
delete $Header{$_} foreach ('to', 'cc', 'bcc'); delete $Header{$_} foreach ('to', 'cc', 'bcc');
$Newsgroups = $Header{'newsgroups'} if (defined($Header{'newsgroups'})); $Newsgroups = $Header{'newsgroups'} if (defined($Header{'newsgroups'}));
my $MessageR = []; my $MessageR = [];
if ($config{'no_sign'}) { if ($config{'no-sign'}) {
# don't sign article # don't sign article
push @$MessageR, $Header{$_} for (keys %Header); push @$MessageR, $Header{$_} for (keys %Header);
push @$MessageR, "\n", @Body; push @$MessageR, "\n", @Body;
} else { } else {
# sign article # sign article
$MessageR = signarticle(\%Header, \@Body); $MessageR = signarticle(\%Header, \@Body);
} }
# post or save article # post or save article
if (! $config{'savedir'}) { if (! $config{'savedir'}) {
skipping to change at line 497 skipping to change at line 546
savearticle($MessageR) if ($Newsgroups); savearticle($MessageR) if ($Newsgroups);
} }
# mail article # mail article
if (($To || $Cc || $Bcc) && $config{'sendmail'}) { if (($To || $Cc || $Bcc) && $config{'sendmail'}) {
open(my $MAIL, '|-', $config{'sendmail'}) || die("$!"); open(my $MAIL, '|-', $config{'sendmail'}) || die("$!");
unshift @$MessageR, "$To" if ($To); unshift @$MessageR, "$To" if ($To);
unshift @$MessageR, "$Cc" if ($Cc); unshift @$MessageR, "$Cc" if ($Cc);
unshift @$MessageR, "$Bcc" if ($Bcc); unshift @$MessageR, "$Bcc" if ($Bcc);
print($MAIL @$MessageR); print($MAIL @$MessageR);
close($MAIL); close($MAIL);
} }
# exit with error if neither $Newsgroups nor any of $To, $Cc or $Bcc set
my $required = 0;
foreach ('Newsgroups', 'To,', 'Cc', 'Bcc') {
$required++ if (defined($Header{lc($_)}));
last if $required;
}
die("$0: neither Newsgroups: nor any of To:, Cc:, Bcc or present.\n") if (!$requ
ired);
# Game over. Insert new coin. # Game over. Insert new coin.
exit; exit;
#-------- sub readarticle #-------- sub readarticle
# #
sub readarticle { sub readarticle {
my ($HeaderR, $BodyR) = @_; my ($HeaderR, $BodyR) = @_;
my $currentheader; my $currentheader;
my $l = 0;
while (defined($_ = <>)) { while (defined($_ = <>)) {
s#\r\n$#\n# if ($config{'transform'});
if ($in_header) { if ($in_header) {
use bytes;
if (m/^$/o) { #end of header if (m/^$/o) { #end of header
$in_header = 0; $in_header = 0;
} elsif (m/^([^\s:]+): (.*)$/s) { } elsif (m/^([^\s:]+): (.*)$/s) {
$currentheader = lc($1); $currentheader = lc($1);
$$HeaderR{$currentheader} = "$1: $2"; $$HeaderR{$currentheader} = "$1: $2";
$l = length($_);
print $1 . ":-header exceeds line length limit "
. $l . " > " . $config{'max-header-length'} . " octets.\n" if (($config{'verbose
'} || $config{'debug'}) && length($_) > $config{'max-header-length'});
} elsif (m/^[ \t]/o) { } elsif (m/^[ \t]/o) {
$$HeaderR{$currentheader} .= $_; $$HeaderR{$currentheader} .= $_;
$l = length($_);
print "Part of continued " . ucfirst($currenthead
er) . ":-header exceeds line length limit " . $l . " > " . $config{'max-header-l
ength'} . " octets.\n" if (($config{'verbose'} || $config{'debug'}) && $l > $con
fig{'max-header-length'});
# } elsif (m/^([^\s:]+):$/) { # skip over empty headers # } elsif (m/^([^\s:]+):$/) { # skip over empty headers
# next; # next;
} else { } else {
chomp($_); chomp($_);
# TODO: quote esc. sequences? # TODO: quote esc. sequences?
die("'$_' is not a correct header-line"); die("'$_' is not a correct header-line");
} }
} else { } else {
push @$BodyR, $_; push @$BodyR, $_;
} }
skipping to change at line 562 skipping to change at line 627
my $tz = sprintf ("%s%0.2d%0.2d", $sign, $offseth, $offsetm); my $tz = sprintf ("%s%0.2d%0.2d", $sign, $offseth, $offsetm);
return "$wday, $day $monthN $year $hh:$mm:$ss $tz"; return "$wday, $day $monthN $year $hh:$mm:$ss $tz";
} }
#-------- sub AuthonNNTP #-------- sub AuthonNNTP
# AuthonNNTP opens the connection to a Server and returns a Net::NNTP-Object. # AuthonNNTP opens the connection to a Server and returns a Net::NNTP-Object.
# #
# User, Password and Server are defined before as elements # User, Password and Server are defined before as elements
# of the global hash %config. If no values for user or password # of the global hash %config. If no values for user or password
# are defined, the sub will try to ask the user (only if # are defined, the sub will try to ask the user (only if
# $config{'Interactive'} is != 0). # $config{'interactive'} is != 0).
sub AuthonNNTP { sub AuthonNNTP {
my $Server = Net::NNTP->new($config{'NNTPServer'}, Reader => 1, Debug => my $Server = Net::NNTP->new(
$config{'debug'}, Port => $config{'NNTPPort'}) Host => $config{'nntp-server'},
or die("$0: Can't connect to ".$config{'NNTPServer'}.":".$config{ Reader => 1,
'NNTPPort'}."!\n"); Debug => $config{'debug'},
my $ServerMsg = ""; Port => $config{'nntp-port'},
SSL => $config{'ssl'},
SSL_verify_mode => 0
) or die("$0: Can't connect to ".$config{'nntp-server'}.":".$config{'nntp
-port'}."!\n");
if ($config{'ssl'} && $config{'debug'}) {
printf("SSL_fingerprint: %s %s\n", split(/\$/, $Server->get_finge
rprint));
}
my $ServerMsg = $Server->message();
my $ServerCod = $Server->code(); my $ServerCod = $Server->code();
# no read and/or write access - give up # no read and/or write access - give up
if ($ServerCod < 200 || $ServerCod > 201) { if ($ServerCod < 200 || $ServerCod > 201) {
$ServerMsg = $Server->message();
$Server->quit(); $Server->quit();
die($0.": ".$ServerCod." ".$ServerMsg."\n"); die($0.": ".$ServerCod." ".$ServerMsg."\n");
} }
# read access - try auth # read access - try auth
if ($ServerCod == 201 || $config{'force_auth'}) { if ($ServerCod == 201 || $config{'force-auth'}) {
if ($config{'NNTPPass'} eq "") { if ($config{'nntp-pass'} eq "") {
if ($config{'Interactive'}) { if ($config{'interactive'}) {
$config{'NNTPUser'} = $term->readline("Your Usern $config{'nntp-user'} = $term->readline("Your User
ame at ".$config{'NNTPServer'}.": "); name at ".$config{'nntp-server'}.": ");
$attribs->{redisplay_function} = $attribs->{shado w_redisplay}; $attribs->{redisplay_function} = $attribs->{shado w_redisplay};
$config{'NNTPPass'} = $term->readline("Password f or ".$config{'NNTPUser'}." at ".$config{'NNTPServer'}.": "); $config{'nntp-pass'} = $term->readline("Password for ".$config{'nntp-user'}." at ".$config{'nntp-server'}.": ");
} else { } else {
$ServerMsg = $Server->message();
$Server->quit(); $Server->quit();
die($0.": ".$ServerCod." ".$ServerMsg."\n"); die($0.": ".$ServerCod." ".$ServerMsg."\n");
} }
} }
$Server->authinfo($config{'NNTPUser'}, $config{'NNTPPass'}); $Server->authinfo($config{'nntp-user'}, $config{'nntp-pass'});
$ServerCod = $Server->code(); $ServerCod = $Server->code();
$ServerMsg = $Server->message(); $ServerMsg = $Server->message();
if ($ServerCod != 281) { # auth failed if ($ServerCod != 281) { # auth failed
$Server->quit(); $Server->quit();
die $0.": ".$ServerCod." ".$ServerMsg."\n"; die $0.": ".$ServerCod." ".$ServerMsg."\n";
} }
} }
$Server->post(); $Server->post();
$ServerCod = $Server->code(); $ServerCod = $Server->code();
if ($ServerCod == 480) { if ($ServerCod == 480) {
if ($config{'NNTPPass'} eq "") { if ($config{'nntp-pass'} eq "") {
if ($config{'Interactive'}) { if ($config{'interactive'}) {
$config{'NNTPUser'} = $term->readline("Your Usern $config{'nntp-user'} = $term->readline("Your User
ame at ".$config{'NNTPServer'}.": "); name at ".$config{'nntp-server'}.": ");
$attribs->{redisplay_function} = $attribs->{shado w_redisplay}; $attribs->{redisplay_function} = $attribs->{shado w_redisplay};
$config{'NNTPPass'} = $term->readline("Password f or ".$config{'NNTPUser'}." at ".$config{'NNTPServer'}.": "); $config{'nntp-pass'} = $term->readline("Password for ".$config{'nntp-user'}." at ".$config{'nntp-server'}.": ");
} else { } else {
$ServerMsg = $Server->message(); $ServerMsg = $Server->message();
$Server->quit(); $Server->quit();
die($0.": ".$ServerCod." ".$ServerMsg."\n"); die($0.": ".$ServerCod." ".$ServerMsg."\n");
} }
} }
$Server->authinfo($config{'NNTPUser'}, $config{'NNTPPass'}); $Server->authinfo($config{'nntp-user'}, $config{'nntp-pass'});
$Server->post(); $Server->post();
} }
return $Server; return $Server;
} }
#-------- sub getpgpcommand #-------- sub getpgpcommand
# getpgpcommand generates the command to sign the message and returns it. # getpgpcommand generates the command to sign the message and returns it.
# #
# Receives: # Receives:
# - $PGPVersion: A scalar holding the PGPVersion # - $pgpversion: A scalar holding the pgp-version
sub getpgpcommand { sub getpgpcommand {
my ($PGPVersion) = @_; my ($pgpversion) = @_;
my $found = 0; my $found = 0;
if ($config{'pgp'} !~ /^\//) { if ($config{'pgp'} !~ /^\//) {
foreach(split(/:/, $ENV{'PATH'})) { foreach(split(/:/, $ENV{'PATH'})) {
if (-x $_."/".$config{'pgp'}) { if (-x $_."/".$config{'pgp'}) {
$found++; $found++;
last; last;
} }
} }
} }
if (!-x $config{'pgp'} && ! $found) { if (!-x $config{'pgp'} && ! $found) {
warn "PGP signing disabled: Can't locate executable ".$config{'pg warn "PGP signing disabled: Can't locate executable ".$config{'pg
p'}."\n" if $config{'debug'}; p'}."\n" if ($config{'debug'} || $config{'verbose'});
$config{'no_sign'} = 1; $config{'no-sign'} = 1;
} }
if ($PGPVersion eq '2') { if ($pgpversion eq '2') {
if ($config{'PGPPass'}) { if ($config{'pgp-pass'}) {
$PGPCommand = "PGPPASS=\"".$config{'PGPPass'}."\" ".$conf $PGPCommand = "PGPPASS=\"".$config{'pgp-pass'}."\" ".$con
ig{'pgp'}." -z -u \"".$config{'PGPSigner'}."\" +verbose=0 language='en' -saft <" fig{'pgp'}." -z -u \"".$config{'pgp-signer'}."\" +verbose=0 language='en' -saft
.$config{'pgptmpf'}.".txt >".$config{'pgptmpf'}.".txt.asc"; <".$config{'pgptmpf'}.".txt >".$config{'pgptmpf'}.".txt.asc";
} elsif ($config{'Interactive'}) { } elsif ($config{'interactive'}) {
$PGPCommand = $config{'pgp'}." -z -u \"".$config{'PGPSign $PGPCommand = $config{'pgp'}." -z -u \"".$config{'pgp-sig
er'}."\" +verbose=0 language='en' -saft <".$config{'pgptmpf'}.".txt >".$config{' ner'}."\" +verbose=0 language='en' -saft <".$config{'pgptmpf'}.".txt >".$config{
pgptmpf'}.".txt.asc"; 'pgptmpf'}.".txt.asc";
} else { } else {
die("$0: Passphrase is unknown!\n"); die("$0: Passphrase is unknown!\n");
} }
} elsif ($PGPVersion eq '5') { } elsif ($pgpversion eq '5') {
if ($config{'PathtoPGPPass'}) { if ($config{'path-to-pgp-pass'}) {
$PGPCommand = "PGPPASSFD=".$config{'PGPPassFD'}." ".$conf $PGPCommand = "PGPPASSFD=".$config{'pgp-pass-fd'}." ".$co
ig{'pgp'}."s -u \"".$config{'PGPSigner'}."\" -t --armor -o ".$config{'pgptmpf'}. nfig{'pgp'}."s -u \"".$config{'pgp-signer'}."\" -t --armor -o ".$config{'pgptmpf
".txt.asc -z -f < ".$config{'pgptmpf'}.".txt ".$config{'PGPPassFD'}."<".$config{ '}.".txt.asc -z -f < ".$config{'pgptmpf'}.".txt ".$config{'pgp-pass-fd'}."<".$co
'PathtoPGPPass'}; nfig{'path-to-pgp-pass'};
} elsif ($config{'Interactive'}) { } elsif ($config{'interactive'}) {
$PGPCommand = $config{'pgp'}."s -u \"".$config{'PGPSigner $PGPCommand = $config{'pgp'}."s -u \"".$config{'pgp-signe
'}."\" -t --armor -o ".$config{'pgptmpf'}.".txt.asc -z -f < ".$config{'pgptmpf'} r'}."\" -t --armor -o ".$config{'pgptmpf'}.".txt.asc -z -f < ".$config{'pgptmpf'
.".txt"; }.".txt";
} else { } else {
die("$0: Passphrase is unknown!\n"); die("$0: Passphrase is unknown!\n");
} }
} elsif ($PGPVersion eq '6') { # this is untested } elsif ($pgpversion eq '6') { # this is untested
if ($config{'PathtoPGPPass'}) { if ($config{'path-to-pgp-pass'}) {
$PGPCommand = "PGPPASSFD=".$config{'PGPPassFD'}." ".$conf $PGPCommand = "PGPPASSFD=".$config{'pgp-pass-fd'}." ".$co
ig{'pgp'}." -u \"".$config{'PGPSigner'}."\" -saft -o ".$config{'pgptmpf'}.".txt. nfig{'pgp'}." -u \"".$config{'pgp-signer'}."\" -saft -o ".$config{'pgptmpf'}.".t
asc < ".$config{'pgptmpf'}.".txt ".$config{'PGPPassFD'}."<".$config{'PathtoPGPPa xt.asc < ".$config{'pgptmpf'}.".txt ".$config{'pgp-pass-fd'}."<".$config{'path-t
ss'}; o-pgp-pass'};
} elsif ($config{'Interactive'}) { } elsif ($config{'interactive'}) {
$PGPCommand = $config{'pgp'}." -u \"".$config{'PGPSigner' $PGPCommand = $config{'pgp'}." -u \"".$config{'pgp-signer
}."\" -saft -o ".$config{'pgptmpf'}.".txt.asc < ".$config{'pgptmpf'}.".txt"; '}."\" -saft -o ".$config{'pgptmpf'}.".txt.asc < ".$config{'pgptmpf'}.".txt";
} else { } else {
die("$0: Passphrase is unknown!\n"); die("$0: Passphrase is unknown!\n");
} }
} elsif ($PGPVersion =~ m/GPG1?$/io) { } elsif ($pgpversion =~ m/GPG1?$/io) {
if ($config{'PathtoPGPPass'}) { if ($config{'path-to-pgp-pass'}) {
$PGPCommand = $config{'pgp'}." --emit-version --digest-al $PGPCommand = $config{'pgp'}." --emit-version --digest-al
go $config{'digest-algo'} -a -u \"".$config{'PGPSigner'}."\" -o ".$config{'pgptm go $config{'digest-algo'} -a -u \"".$config{'pgp-signer'}."\" -o ".$config{'pgpt
pf'}.".txt.asc --no-tty --batch --passphrase-fd ".$config{'PGPPassFD'}." ".$conf mpf'}.".txt.asc --no-tty --batch --passphrase-fd ".$config{'pgp-pass-fd'}." ".$c
ig{'PGPPassFD'}."<".$config{'PathtoPGPPass'}." --clearsign ".$config{'pgptmpf'}. onfig{'pgp-pass-fd'}."<".$config{'path-to-pgp-pass'}." --clearsign ".$config{'pg
".txt"; ptmpf'}.".txt";
} elsif ($config{'Interactive'}) { } elsif ($config{'interactive'}) {
$PGPCommand = $config{'pgp'}." --emit-version --digest-al $PGPCommand = $config{'pgp'}." --emit-version --digest-al
go $config{'digest-algo'} -a -u \"".$config{'PGPSigner'}."\" -o ".$config{'pgptm go $config{'digest-algo'} -a -u \"".$config{'pgp-signer'}."\" -o ".$config{'pgpt
pf'}.".txt.asc --no-secmem-warning --no-batch --clearsign ".$config{'pgptmpf'}." mpf'}.".txt.asc --no-secmem-warning --no-batch --clearsign ".$config{'pgptmpf'}.
.txt"; ".txt";
} else { } else {
die("$0: Passphrase is unknown!\n"); die("$0: Passphrase is unknown!\n");
} }
} elsif ($PGPVersion =~ m/GPG2$/io) { } elsif ($pgpversion =~ m/GPG2$/io) {
if ($config{'PathtoPGPPass'}) { if ($config{'path-to-pgp-pass'}) {
$PGPCommand = $config{'pgp'}." --pinentry-mode loopback - $PGPCommand = $config{'pgp'}." --pinentry-mode loopback -
-emit-version --digest-algo $config{'digest-algo'} -a -u \"".$config{'PGPSigner' -emit-version --digest-algo $config{'digest-algo'} -a -u \"".$config{'pgp-signer
}."\" -o ".$config{'pgptmpf'}.".txt.asc --no-tty --batch --passphrase-fd ".$conf '}."\" -o ".$config{'pgptmpf'}.".txt.asc --no-tty --batch --passphrase-fd ".$con
ig{'PGPPassFD'}." ".$config{'PGPPassFD'}."<".$config{'PathtoPGPPass'}." --clears fig{'pgp-pass-fd'}." ".$config{'pgp-pass-fd'}."<".$config{'path-to-pgp-pass'}."
ign ".$config{'pgptmpf'}.".txt"; --clearsign ".$config{'pgptmpf'}.".txt";
} elsif ($config{'Interactive'}) { } elsif ($config{'interactive'}) {
$PGPCommand = $config{'pgp'}." --emit-version --digest-al $PGPCommand = $config{'pgp'}." --emit-version --digest-al
go $config{'digest-algo'} -a -u \"".$config{'PGPSigner'}."\" -o ".$config{'pgptm go $config{'digest-algo'} -a -u \"".$config{'pgp-signer'}."\" -o ".$config{'pgpt
pf'}.".txt.asc --no-secmem-warning --no-batch --clearsign ".$config{'pgptmpf'}." mpf'}.".txt.asc --no-secmem-warning --no-batch --clearsign ".$config{'pgptmpf'}.
.txt"; ".txt";
} else { } else {
die("$0: Passphrase is unknown!\n"); die("$0: Passphrase is unknown!\n");
} }
} else { } else {
die("$0: Unknown PGP-Version $PGPVersion!"); die("$0: Unknown PGP-Version $pgpversion!");
} }
return $PGPCommand; return $PGPCommand;
} }
#-------- sub postarticle #-------- sub postarticle
# postarticle posts your article to your Newsserver. # postarticle posts your article to your Newsserver.
# #
# Receives: # Receives:
# - $ArticleR: A reference to an array containing the article # - $ArticleR: A reference to an array containing the article
sub postarticle { sub postarticle {
my ($ArticleR) = @_; my ($ArticleR) = @_;
my $Server = AuthonNNTP(); my $Server = AuthonNNTP();
my $ServerCod = $Server->code(); my $ServerCod = $Server->code();
my $ServerMsg = $Server->message();
if ($ServerCod == 340) { if ($ServerCod == 340) {
$Server->datasend(@$ArticleR); $Server->datasend(@$ArticleR);
## buggy Net::Cmd < 2.31
$Server->set_status(200, "");
$Server->dataend(); $Server->dataend();
if (!$Server->ok()) { $ServerCod = $Server->code();
my $ServerMsg = $Server->message(); $ServerMsg = $Server->message();
if (! $Server->ok()) {
$Server->quit(); $Server->quit();
die("\n$0: Posting failed! Response from news server:\n", $Server->code(), ' ', $ServerMsg); die("\n$0: Posting failed! Response from news server:\n", $ServerCod, ' ', $ServerMsg);
} }
$Server->quit(); $Server->quit();
} else { } else {
die("\n".$0.": Posting failed!\n"); die("\n$0: Posting failed! Response from news server:\n", $Server Cod, ' ', $ServerMsg);
} }
return; return;
} }
#-------- sub savearticle #-------- sub savearticle
# savearticle saves your article to the directory $config{'savedir'} # savearticle saves your article to the directory $config{'savedir'}
# #
# Receives: # Receives:
# - $ArticleR: A reference to an array containing the article # - $ArticleR: A reference to an array containing the article
sub savearticle { sub savearticle {
skipping to change at line 742 skipping to change at line 818
# Receives: # Receives:
# - $HeaderR: A reference to a hash containing the articles headers. # - $HeaderR: A reference to a hash containing the articles headers.
# - $BodyR: A reference to an array containing the body. # - $BodyR: A reference to an array containing the body.
# #
# Returns: # Returns:
# - $MessageRef: A reference to an array containing the whole message. # - $MessageRef: A reference to an array containing the whole message.
sub signarticle { sub signarticle {
my ($HeaderR, $BodyR) = @_; my ($HeaderR, $BodyR) = @_;
my (@pgphead, @pgpbody, $pgphead, $pgpbody, $signheaders, @signheaders); my (@pgphead, @pgpbody, $pgphead, $pgpbody, $signheaders, @signheaders);
foreach (@{$config{'PGPSignHeaders'}}) { foreach (@{$config{'pgp-sign-headers'}}) {
if (defined($$HeaderR{lc($_)}) && $$HeaderR{lc($_)} =~ m/^[^\s:]+ : .+/o) { if (defined($$HeaderR{lc($_)}) && $$HeaderR{lc($_)} =~ m/^[^\s:]+ : .+/o) {
push @signheaders, $_; push @signheaders, $_;
} }
} }
$pgpbody = join("", @$BodyR); $pgpbody = join("", @$BodyR);
# Delete and create the temporary pgp-Files # Delete and create the temporary pgp-Files
unlink $config{'pgptmpf'}.".txt"; unlink $config{'pgptmpf'}.".txt";
unlink $config{'pgptmpf'}.".txt.asc"; unlink $config{'pgptmpf'}.".txt.asc";
skipping to change at line 765 skipping to change at line 841
$pgphead = "X-Signed-Headers: $signheaders\n"; $pgphead = "X-Signed-Headers: $signheaders\n";
foreach my $header (@signheaders) { foreach my $header (@signheaders) {
if ($$HeaderR{lc($header)} =~ m/^[^\s:]+: (.+?)\n?$/so) { if ($$HeaderR{lc($header)} =~ m/^[^\s:]+: (.+?)\n?$/so) {
$pgphead .= $header.": ".$1."\n"; $pgphead .= $header.": ".$1."\n";
} }
} }
unless (substr($pgpbody,-1,1)=~ /\n/ ) {$pgpbody.="\n"}; unless (substr($pgpbody,-1,1)=~ /\n/ ) {$pgpbody.="\n"};
open(my $FH, '>', $config{'pgptmpf'} . ".txt") or die("$0: can't open ".$ config{'pgptmpf'}.": $!\n"); open(my $FH, '>', $config{'pgptmpf'} . ".txt") or die("$0: can't open ".$ config{'pgptmpf'}.": $!\n");
print $FH $pgphead, "\n", $pgpbody; print $FH $pgphead, "\n", $pgpbody;
print $FH "\n" if ($config{'PGPVersion'} =~ m/GPG/io); # workaround a pgp /gpg incompatibility - should IMHO be fixed in pgpverify print $FH "\n" if ($config{'pgp-version'} =~ m/GPG/io); # workaround a pg p/gpg incompatibility - should IMHO be fixed in pgpverify
close($FH) or warn "$0: Couldn't close TMP: $!\n"; close($FH) or warn "$0: Couldn't close TMP: $!\n";
# Start PGP, then read the signature; # Start PGP, then read the signature;
`$PGPCommand`; `$PGPCommand`;
open($FH, '<', $config{'pgptmpf'} . ".txt.asc") or die("$0: can't open ". $config{'pgptmpf'}.".txt.asc: $!\n"); open($FH, '<', $config{'pgptmpf'} . ".txt.asc") or die("$0: can't open ". $config{'pgptmpf'}.".txt.asc: $!\n");
local $/ = "\n".$config{'pgpbegin'}."\n"; local $/ = "\n".$config{'pgpbegin'}."\n";
$_ = <$FH>; $_ = <$FH>;
unless (m/\Q$config{'pgpbegin'}\E$/o) { unless (m/\Q$config{'pgpbegin'}\E$/o) {
unlink $config{'pgptmpf'} . ".txt"; unlink $config{'pgptmpf'} . ".txt";
skipping to change at line 818 skipping to change at line 894
close($FH); close($FH);
die("$0: unexpected data following ".$config{'pgpend'}."\n"); die("$0: unexpected data following ".$config{'pgpend'}."\n");
} }
close($FH); close($FH);
unlink $config{'pgptmpf'} . ".txt.asc"; unlink $config{'pgptmpf'} . ".txt.asc";
my $tmppgpheader = $config{'pgpheader'} . ": " . $$HeaderR{$config{'pgphe ader'}}; my $tmppgpheader = $config{'pgpheader'} . ": " . $$HeaderR{$config{'pgphe ader'}};
delete $$HeaderR{$config{'pgpheader'}}; delete $$HeaderR{$config{'pgpheader'}};
@pgphead = (); @pgphead = ();
foreach my $header (@{$config{PGPorderheaders}}) { foreach my $header (@{$config{'pgp-order-headers'}}) {
if ($$HeaderR{$header} && $$HeaderR{$header} ne "\n") { if ($$HeaderR{$header} && $$HeaderR{$header} ne "\n") {
push(@pgphead, "$$HeaderR{$header}"); push(@pgphead, "$$HeaderR{$header}");
delete $$HeaderR{$header}; delete $$HeaderR{$header};
} }
} }
foreach my $header (keys %$HeaderR) { foreach my $header (keys %$HeaderR) {
if ($$HeaderR{$header} && $$HeaderR{$header} ne "\n") { if ($$HeaderR{$header} && $$HeaderR{$header} ne "\n") {
push(@pgphead, "$$HeaderR{$header}"); push(@pgphead, "$$HeaderR{$header}");
delete $$HeaderR{$header}; delete $$HeaderR{$header};
} }
} }
push @pgphead, ("X-PGP-Hash: " . $config{'digest-algo'} . "\n") if (defin ed($config{'digest-algo'})); push @pgphead, ("X-PGP-Hash: " . $config{'digest-algo'} . "\n") if (defin ed($config{'digest-algo'}));
push @pgphead, ("X-PGP-Key: " . $config{'PGPSigner'} . "\n"), $tmppgphead er; push @pgphead, ("X-PGP-Key: " . $config{'pgp-signer'} . "\n"), $tmppgphea der;
undef $tmppgpheader; undef $tmppgpheader;
@pgpbody = split(/$/m, $pgpbody); @pgpbody = split(/$/m, $pgpbody);
my @pgpmessage = (@pgphead, "\n", @pgpbody); my @pgpmessage = (@pgphead, "\n", @pgpbody);
return \@pgpmessage; return \@pgpmessage;
} }
#-------- sub buildcancelkey #-------- sub buildcancelkey
# buildcancelkey builds the cancel-key based on the configured HASH algorithm. # buildcancelkey builds the cancel-key based on the configured HASH algorithm.
# #
# Receives: # Receives:
# - $data: The input data. # - $data: The input data.
# - $key: The secret key to be used. # - $key: The secret key to be used.
# #
# Returns: # Returns:
# - $cancel_key: The calculated cancel-key. # - $cancel_key: The calculated cancel-key.
sub buildcancelkey { sub buildcancelkey {
my ($data, $key) = @_; my ($data, $key) = @_;
my $cancel_key; my $cancel_key;
if ($config{'canlock_algorithm'} eq 'sha1') { if ($config{'canlock-algorithm'} eq 'sha1') {
$cancel_key = MIME::Base64::encode(Digest::HMAC_SHA1::hmac_sha1($ data, $key), ''); $cancel_key = MIME::Base64::encode(Digest::HMAC_SHA1::hmac_sha1($ data, $key), '');
} elsif ($config{'canlock_algorithm'} eq 'sha256') { } elsif ($config{'canlock-algorithm'} eq 'sha256') {
$cancel_key = MIME::Base64::encode(Digest::SHA::hmac_sha256($data , $key), ''); $cancel_key = MIME::Base64::encode(Digest::SHA::hmac_sha256($data , $key), '');
} else { } else {
$cancel_key = MIME::Base64::encode(Digest::SHA::hmac_sha512($data , $key), ''); $cancel_key = MIME::Base64::encode(Digest::SHA::hmac_sha512($data , $key), '');
} }
return $cancel_key; return $cancel_key;
} }
#-------- sub buildcancellock #-------- sub buildcancellock
# buildcancellock builds the cancel-lock based on the configured HASH algorithm # buildcancellock builds the cancel-lock based on the configured HASH algorithm
# and the given cancel-key. # and the given cancel-key.
# #
# Receives: # Receives:
# - $sha_mod: A hint which module to be used for sha1. # - $sha_mod: A hint which module to be used for sha1.
# - $cancel_key: The cancel-key for which the lock has to be calculated. # - $cancel_key: The cancel-key for which the lock has to be calculated.
# #
# Returns: # Returns:
# - $cancel_lock: The calculated cancel-lock. # - $cancel_lock: The calculated cancel-lock.
sub buildcancellock { sub buildcancellock {
my ($cancel_key, $sha_mod) = @_; my ($cancel_key, $sha_mod) = @_;
my $cancel_lock; my $cancel_lock;
if ($config{'canlock_algorithm'} eq 'sha1') { if ($config{'canlock-algorithm'} eq 'sha1') {
if ($sha_mod =~ m/SHA1/) { if ($sha_mod =~ m/SHA1/) {
$cancel_lock = MIME::Base64::encode(Digest::SHA1::sha1($c ancel_key, ''), ''); $cancel_lock = MIME::Base64::encode(Digest::SHA1::sha1($c ancel_key, ''), '');
} else { } else {
$cancel_lock = MIME::Base64::encode(Digest::SHA::sha1($ca ncel_key, ''), ''); $cancel_lock = MIME::Base64::encode(Digest::SHA::sha1($ca ncel_key, ''), '');
} }
} elsif ($config{'canlock_algorithm'} eq 'sha256') { } elsif ($config{'canlock-algorithm'} eq 'sha256') {
$cancel_lock = MIME::Base64::encode(Digest::SHA::sha256($cancel_k ey, ''), ''); $cancel_lock = MIME::Base64::encode(Digest::SHA::sha256($cancel_k ey, ''), '');
} else { } else {
$cancel_lock = MIME::Base64::encode(Digest::SHA::sha512($cancel_k ey, ''), ''); $cancel_lock = MIME::Base64::encode(Digest::SHA::sha512($cancel_k ey, ''), '');
} }
return $cancel_lock; return $cancel_lock;
} }
sub version { sub version {
print $pname." ".$version."\n"; print $pname." ".$version."\n";
return; return;
skipping to change at line 906 skipping to change at line 982
version(); version();
print "Usage: ".$pname." [OPTS] < article\n"; print "Usage: ".$pname." [OPTS] < article\n";
print " -a string set Approved:-header to string\n"; print " -a string set Approved:-header to string\n";
print " -c string set Control:-header to string\n"; print " -c string set Control:-header to string\n";
print " -d string set Distribution:-header to string\n"; print " -d string set Distribution:-header to string\n";
print " -e string set Expires:-header to string\n"; print " -e string set Expires:-header to string\n";
print " -f string set From:-header to string\n"; print " -f string set From:-header to string\n";
print " -i string list of headers to be ignored for signing\n"; print " -i string list of headers to be ignored for signing\n";
print " -n string set Newsgroups:-header to string\n"; print " -n string set Newsgroups:-header to string\n";
print " -o string set Organization:-header to string\n"; print " -o string set Organization:-header to string\n";
print " -p port use port as NNTP port [default=".$config{'NNTPPort'}. "]\n"; print " -p port use port as NNTP port [default=".$config{'nntp-port'} ."]\n";
print " -r string set Reply-To:-header to string\n"; print " -r string set Reply-To:-header to string\n";
print " -s string save signed article to directory string instead of po sting\n"; print " -s string save signed article to directory string instead of po sting\n";
print " -t string set Subject:-header to string\n"; print " -t string set Subject:-header to string\n";
print " -v show version\n"; print " -v show warnings about missing/disabled features\n";
print " -w string set Followup-To:-header to string\n"; print " -w string set Followup-To:-header to string\n";
print " -x string set Path:-header to string\n"; print " -x string set Path:-header to string\n";
print " -D enable debugging\n"; print " -D enable debugging\n";
print " -F string set References:-header to string\n"; print " -F string set References:-header to string\n";
print " -H show help\n"; print " -H show help\n";
print " -I do not add Injection-Date: header\n"; print " -I do not add Injection-Date: header\n";
print " -L do not add Cancel-Lock: / Cancel-Key: headers\n"; print " -L do not add Cancel-Lock: / Cancel-Key: headers\n";
print " -O do not add Organization:-header\n";
print " -R disallow control messages\n"; print " -R disallow control messages\n";
print " -S do not append " . $config{'sig_path'} . "\n"; print " -S do not append " . $config{'sig-path'} . "\n";
print " -X do not sign article\n"; print " -X do not sign article\n";
print " -Y force authentication on connect\n"; print " -Y force authentication on connect\n";
print " --canlock-algorithm string\n";
print " digest algorithm for Cancel-Lock (sha1, sha256 or sha
512)\n";
print " --ssl use NNTPS (via port 563) if available\n";
print " --transform convert <CR><LF> to <LF>\n";
print " --version show version\n";
printf ("\nAvailable tinewsrc-vars: %s\n", join(", ",sort keys %config))
if ($config{'verbose'} || $config{'debug'});
exit 0; exit 0;
} }
__END__ __END__
=head1 NAME =head1 NAME
tinews.pl - Post and sign an article via NNTP tinews.pl - Post and sign an article via NNTP
=head1 SYNOPSIS =head1 SYNOPSIS
skipping to change at line 951 skipping to change at line 1034
already have the relevant MIME-headers as B<tinews.pl> will not already have the relevant MIME-headers as B<tinews.pl> will not
add any MIME-headers nor encode its input. add any MIME-headers nor encode its input.
If the article contains To:, Cc: or Bcc: headers and mail-actions are If the article contains To:, Cc: or Bcc: headers and mail-actions are
configured it will automatically add a "Posted-And-Mailed: yes" header configured it will automatically add a "Posted-And-Mailed: yes" header
to the article and send out the mail-copies. to the article and send out the mail-copies.
If a Cancel-Lock secret file is defined it will automatically add a If a Cancel-Lock secret file is defined it will automatically add a
Cancel-Lock: (and Cancel-Key: if required) header. Cancel-Lock: (and Cancel-Key: if required) header.
The input should have unix line endings (<LF>, '\n'). The input should have unix line endings (<LF>, '\n'). Use --B<transform>
to convert from <CR><LF> to just <LF>.
=head1 OPTIONS =head1 OPTIONS
X<tinews, command-line options> X<tinews, command-line options>
=over 4 =over 4
=item -B<a> C<Approved> | --B<approved> C<Approved> =item -B<a> C<Approved> | --B<approved> C<Approved>
X<-a> X<--approved> X<-a> X<--approved>
Set the article header field Approved: to the given value. Set the article header field Approved: to the given value.
skipping to change at line 1012 skipping to change at line 1096
=item -B<o> C<Organization> | --B<organization> C<Organization> =item -B<o> C<Organization> | --B<organization> C<Organization>
X<-o> X<--organization> X<-o> X<--organization>
Set the article header field Organization: to the given value. Set the article header field Organization: to the given value.
=item -B<p> C<port> | --B<port> C<port> =item -B<p> C<port> | --B<port> C<port>
X<-p> X<--port> X<-p> X<--port>
use C<port> as NNTP-port use C<port> as NNTP-port
=item -B<r> C<Reply-To> | --B<replyto> C<Reply-To> =item -B<r> C<Reply-To> | --B<reply-to> C<Reply-To>
X<-r> X<--replyto> X<-r> X<--reply-to>
Set the article header field Reply-To: to the given value. Set the article header field Reply-To: to the given value.
=item -B<s> F<directory> | --B<savedir> F<directory> =item -B<s> F<directory> | --B<savedir> F<directory>
X<-s> X<--savedir> X<-s> X<--savedir>
Save signed article to directory F<directory> instead of posting. Save signed article to directory F<directory> instead of posting.
=item -B<t> C<Subject> | --B<subject> C<Subject> =item -B<t> C<Subject> | --B<subject> C<Subject>
X<-t> X<--subject> X<-t> X<--subject>
Set the article header field Subject: to the given value. Set the article header field Subject: to the given value.
=item -B<v> | --B<version> =item -B<v> | --B<verbose>
X<-v> X<--version> X<-v> X<--verbose>
Show version. Warn about disabled options due to lacking perl-modules or executables and
unreadable files and enable warnings about raw 8-bit data.
=item -B<w> C<Followup-To> | --B<followupto> C<Followup-To> =item -B<w> C<Followup-To> | --B<followup-to> C<Followup-To>
X<-w> X<--followupto> X<-w> X<--followup-to>
Set the article header field Followup-To: to the given value. Set the article header field Followup-To: to the given value.
=item -B<x> C<Path> | --B<path> C<Path> =item -B<x> C<Path> | --B<path> C<Path>
X<-x> X<--path> X<-x> X<--path>
Set the article header field Path: to the given value. Set the article header field Path: to the given value.
=item -B<D> | -B<N> | --B<debug>
X<-D> X<-N> X<--debug>
Set L<Net::NNTP(3pm)> to debug mode, enable warnings about raw 8-bit data,
warn about disabled options due to lacking perl-modules or executables and
unreadable files.
=item -B<H> | --B<help> =item -B<H> | --B<help>
X<-H> X<--help> X<-H> X<--help>
Show help-page. Show help-page.
=item -B<I> | --B<no-injection-date> =item -B<I> | --B<no-injection-date>
X<-I> X<--no-injection-date> X<-I> X<--no-injection-date>
Do not add Injection-Date: header. Do not add Injection-Date: header.
=item -B<L> | --B<no-canlock> =item -B<L> | --B<no-canlock>
X<-L> X<--no-canlock> X<-L> X<--no-canlock>
Do not add Cancel-Lock: / Cancel-Key: headers. Do not add Cancel-Lock: / Cancel-Key: headers.
=item --B<canlock-algorithm> C<Algorithm> =item -B<O> | --B<no-organization>
X<--canlock-algorithm> X<-O> X<--no-organization>
Digest algorithm used for Cancel-Lock: / Cancel-Key: headers. Do not add Organization: header.
Supported algorithms are sha1, sha256 and sha512. Default is sha1.
=item -B<R> | --B<no-control> =item -B<R> | --B<no-control>
X<-R> X<--no-control> X<-R> X<--no-control>
Restricted mode, disallow control-messages. Restricted mode, disallow control-messages.
=item -B<S> | --B<no-signature> =item -B<S> | --B<no-signature>
X<-s> X<--no-signature> X<-s> X<--no-signature>
Do not append F<$HOME/.signature>. Do not append F<$HOME/.signature>.
skipping to change at line 1083 skipping to change at line 1174
=item -B<X> | --B<no-sign> =item -B<X> | --B<no-sign>
X<-X> X<--no-sign> X<-X> X<--no-sign>
Do not sign the article. Do not sign the article.
=item -B<Y> | --B<force-auth> =item -B<Y> | --B<force-auth>
X<-Y> X<--force-auth> X<-Y> X<--force-auth>
Force authentication on connect even if not required by the server. Force authentication on connect even if not required by the server.
=item --B<canlock-algorithm> C<Algorithm>
X<--canlock-algorithm>
Digest algorithm used for Cancel-Lock: / Cancel-Key: headers.
Supported algorithms are sha1, sha256 and sha512. Default is sha1.
=item --B<ssl> | --B<nntps>
X<--ssl> X<--nntps>
Use NNTPS (via port 563) if available. This requires a recent version
of L<Net::NNTP(3pm)> and L<IO::Socket::SSL(3pm)>. Be aware that no SSL
verification will be done.
=item --B<transform>
X<--transform>
Convert network line endings (<CR><LF>) to unix line endings (<LF>).
=item --B<version>
X<--version>
Show version.
=item -B<A> -B<V> -B<W> =item -B<A> -B<V> -B<W>
X<-A> X<-V> X<-W> X<-A> X<-V> X<-W>
These options are accepted for compatibility reasons but ignored. These options are accepted for compatibility reasons but ignored.
=item -B<h> | --B<headers> =item -B<h> | --B<headers>
X<-h> X<--headers> X<-h> X<--headers>
These options are accepted for compatibility reasons but ignored. These options are accepted for compatibility reasons but ignored.
=item -B<O> | --B<no-organization>
X<-O> X<--no-organization>
These options are accepted for compatibility reasons but ignored.
=item -B<D> | -B<N> | --B<debug>
X<-D> X<-N> X<--debug>
Enable warnings about raw 8-bit data and set L<Net::NNTP(3pm)> in debug
mode, enable warnings about raw 8-bit data, warn about disabled options
due to lacking perl-modules or executables and unreadable files.
=back =back
=head1 EXIT STATUS =head1 EXIT STATUS
The following exit values are returned: The following exit values are returned:
=over 4 =over 4
=item S< 0> =item S< 0>
skipping to change at line 1177 skipping to change at line 1279
Set the article header field Reply-To: to the return address specified by Set the article header field Reply-To: to the return address specified by
the variable if there isn't already a Reply-To: header in the article. the variable if there isn't already a Reply-To: header in the article.
The '-B<r>' command-line option overrides B<$REPLYTO>. The '-B<r>' command-line option overrides B<$REPLYTO>.
=item B<$ORGANIZATION> =item B<$ORGANIZATION>
X<$ORGANIZATION> X<ORGANIZATION> X<$ORGANIZATION> X<ORGANIZATION>
Set the article header field Organization: to the contents of the variable Set the article header field Organization: to the contents of the variable
if there isn't already an Organization: header in the article. The '-B<o>' if there isn't already an Organization: header in the article. The '-B<o>'
command-line option overrides B<$ORGANIZATION>. command-line option overrides B<$ORGANIZATION>, The '-B<O>' command-line
option disables it.
=item B<$DISTRIBUTION> =item B<$DISTRIBUTION>
X<$DISTRIBUTION> X<DISTRIBUTION> X<$DISTRIBUTION> X<DISTRIBUTION>
Set the article header field Distribution: to the contents of the variable Set the article header field Distribution: to the contents of the variable
if there isn't already a Distribution: header in the article. The '-B<d>' if there isn't already a Distribution: header in the article. The '-B<d>'
command-line option overrides B<$DISTRIBUTION>. command-line option overrides B<$DISTRIBUTION>.
=back =back
skipping to change at line 1232 skipping to change at line 1335
"nntpserver user password" pairs for NNTP servers that require "nntpserver user password" pairs for NNTP servers that require
authorization. First match counts. Lines starting with "#" are skipped and authorization. First match counts. Lines starting with "#" are skipped and
blank lines are ignored. This file should be readable only for the user as blank lines are ignored. This file should be readable only for the user as
it contains the user's unencrypted password for reading news. it contains the user's unencrypted password for reading news.
F<$HOME/.newsauth> is checked first. F<$HOME/.newsauth> is checked first.
=item F<$XDG_CONFIG_HOME/tinewsrc> F<$HOME/.config/tinewsrc> F<$HOME/.tinewsrc> =item F<$XDG_CONFIG_HOME/tinewsrc> F<$HOME/.config/tinewsrc> F<$HOME/.tinewsrc>
"option=value" configuration pairs. Lines that start with "#" are ignored. "option=value" configuration pairs. Lines that start with "#" are ignored.
If the file contains unencrypted passwords (e.g. NNTPPass or PGPPass), it If the file contains unencrypted passwords (e.g. nntp-pass or pgp-pass), it
should be readable for the user only. should be readable for the user only.
=back =back
=head1 SECURITY =head1 SECURITY
If you've configured or entered a password, even if the variable that If you've configured or entered a password, even if the variable that
contained that password has been erased, it may be possible for someone to contained that password has been erased, it may be possible for someone to
find that password, in plaintext, in a core dump. In short, if serious find that password, in plaintext, in a core dump. In short, if serious
security is an issue, don't use this script. security is an issue, don't use this script.
Be aware that even if NNTPS is used still no SSL verification will be done.
=head1 NOTES =head1 NOTES
B<tinews.pl> is designed to be used with L<pgp(1)>-2.6.3, B<tinews.pl> is designed to be used with L<pgp(1)>-2.6.3,
L<pgp(1)>-5, L<pgp(1)>-6, L<gpg(1)> and L<gpg2(1)>. L<pgp(1)>-5, L<pgp(1)>-6, L<gpg(1)> and L<gpg2(1)>.
B<tinews.pl> requires the following standard modules to be installed: B<tinews.pl> requires the following standard modules to be installed:
L<Getopt::Long(3pm)>, L<Net::NNTP(3pm)>, <Time::Local(3pm)> and L<Getopt::Long(3pm)>, L<Net::NNTP(3pm)>, <Time::Local(3pm)> and
L<Term::Readline(3pm)>. L<Term::Readline(3pm)>.
NNTPS (NNTP with implicit TLS; RFC 4642 and RFC 8143) may be unavailable
if L<Net::NNTP(3pm)> is too old or L<IO::Socket::SSL(3pm)> is missing on
the system. B<tinews.pl> will fallback to unencrypted NNTP in that case.
If the Cancel-Lock feature (RFC 8315) is enabled the following additional If the Cancel-Lock feature (RFC 8315) is enabled the following additional
modules must be installed: L<MIME::Base64(3pm)>, L<Digest::SHA(3pm)> or modules must be installed: L<MIME::Base64(3pm)>, L<Digest::SHA(3pm)> or
L<Digest::SHA1(3pm)> and L<Digest::HMAC_SHA1(3pm)>. sha256 and sha512 as L<Digest::SHA1(3pm)> and L<Digest::HMAC_SHA1(3pm)>. sha256 and sha512 as
algorithms for B<canlock-algorithm> are only available with L<Digest::SHA(3pm)>. algorithms for B<canlock-algorithm> are only available with L<Digest::SHA(3pm)>.
L<gpg2(1)> users may need to set B<$GPG_TTY>, i.e. L<gpg2(1)> users may need to set B<$GPG_TTY>, i.e.
GPG_TTY=$(tty) GPG_TTY=$(tty)
export GPG_TTY export GPG_TTY
skipping to change at line 1277 skipping to change at line 1386
=head1 AUTHOR =head1 AUTHOR
Urs Janssen E<lt>urs@tin.orgE<gt>, Urs Janssen E<lt>urs@tin.orgE<gt>,
Marc Brockschmidt E<lt>marc@marcbrockschmidt.deE<gt> Marc Brockschmidt E<lt>marc@marcbrockschmidt.deE<gt>
=head1 SEE ALSO =head1 SEE ALSO
L<pgp(1)>, L<gpg(1)>, L<gpg2(1)>, L<pgps(1)>, L<Digest::HMAC_SHA1(3pm)>, L<pgp(1)>, L<gpg(1)>, L<gpg2(1)>, L<pgps(1)>, L<Digest::HMAC_SHA1(3pm)>,
L<Digest::SHA(3pm)>, L<Digest::SHA1(3pm)>, L<Getopt::Long(3pm)>, L<Digest::SHA(3pm)>, L<Digest::SHA1(3pm)>, L<Getopt::Long(3pm)>,
L<MIME::Base64(3pm)>, L<Net::NNTP(3pm)>, L<Time::Local(3pm)>, L<IO::Socket::SSL(3pm)>, L<MIME::Base64(3pm)>, L<Net::NNTP(3pm)>,
L<Term::Readline(3pm)> L<Time::Local(3pm)>, L<Term::Readline(3pm)>
=cut =cut
 End of changes. 119 change blocks. 
267 lines changed or deleted 389 lines changed or added

Home  |  About  |  Features  |  All  |  Newest  |  Dox  |  Diffs  |  RSS Feeds  |  Screenshots  |  Comments  |  Imprint  |  Privacy  |  HTTP(S)