App-tpnotify
view release on metacpan or search on metacpan
err("examine $wd for details");
}
exit($code);
}
sub info {
print "$progname: ";
print @_;
print "\n";
}
# download($source_url, dest => 'filename' or ref)
# ------------------------------------------------
# Downloads material from $source. If 'dest' is a reference to a scalar,
# the downloaded material is stored to that ref. If it is a filename,
# the material is stored in the named disk file. If 'dest' is not given,
# the name of the disk file is determined as the basename of the path
# component from the $source_url.
sub download {
my ($source,%opt) = @_;
my $url = new URI($source);
my $dest = delete $opt{dest} || basename($url->path);
my %args;
if (ref($dest) eq '') {
$args{':content_file'} = $dest;
info("downloading $source to $wd/$dest") if $verbose;
} else {
info("downloading $source") if $verbose;
}
my $scheme = $url->scheme;
eval {
require "LWP/Protocol/$scheme.pm";
};
if ($@) {
$@ =~ s/\s+at [^\s]+ line \d+\.$//;
abend(EX_OSERR, "$@");
}
my $ua = LWP::UserAgent->new();
$ua->agent("tpnotify/$VERSION");
my $response = $ua->get($url->as_string, %args);
unless ($response->is_success) {
abend(EX_UNAVAILABLE,
"downloading $source failed: " . $response->status_line);
}
if (ref($dest) eq 'SCALAR') {
$$dest = $response->decoded_content;
}
return $dest;
}
# get_sources($URL)
# -----------------
# Download and extract source archive.
sub get_sources {
my ($source) = @_;
$archive_file = download($source);
info("scanning $archive_file") if $verbose;
open(my $fd, '-|', "tar tf $archive_file")
or abend(EX_NOINPUT, "can't open $archive_file: $!");
while (<$fd>) {
chomp;
unless (m#^(?<dir>.+?)/(?<file>.*)$#) {
abend(EX_DATAERR, "$archive_file content suspicious: member $_");
}
if (defined($topdir)) {
unless ($+{dir} eq $topdir) {
abend(EX_DATAERR,
"$archive_file content suspicious: $+{dir} does not match $topdir");
}
} else {
$topdir = $+{dir};
}
my $f = $+{file};
if ($f eq 'configure.ac' || $f =~ m#po/.*\.pot#) {
$files{$f} = $_;
}
}
close $fd;
info("top level directory: $topdir") if $verbose;
# Verify available files
unless (exists($files{'configure.ac'})) {
abend(EX_DATAERR, "no configure.ac in $archive_file");
}
unless (keys(%files) > 1) {
abend(EX_DATAERR, "no potfile in $archive_file");
}
my $filelist = join(' ', values(%files));
info("extracting from $archive_file") if $verbose;
my $cmd = "tar xf $archive_file $filelist";
system($cmd);
check_command_status($cmd);
}
# check_command_status($STAT)
# ---------------------------
# Handles the result of the system or wait function call.
sub check_command_status {
my $cmd = shift;
my $status = shift || $?;
if ($status == -1) {
abend(EX_OSERR, "failed to run $cmd");
} elsif ($status & 127) {
abend(EX_UNAVAILABLE, "$cmd exited on signal " . ($status & 127));
} elsif (my $e = ($status >> 8)) {
abend(EX_UNAVAILABLE, "$cmd exited with status $e");
}
}
# verify
# ------
# Verifies the tarball. Determines canonical package name, extracts the POT
# file and checks if it lists the correct package name in its
# "Project-Id-Version" header and that its msgids differ from the ones
# already registered on the TP.
sub verify {
my ($in, $out);
my $pid = open2($out, $in, "m4 -P - $files{'configure.ac'}")
or abend(EX_NOINPUT, "can't open $files{'configure.ac'}: $!");
print $in <<'EOT';
m4_divert(-1)
m4_changequote([,])
m4_define([AC_INIT],[m4_divert(0)$1
$2[]m4_divert(-1)])
EOT
close $in;
waitpid($pid, 0);
check_command_status("m4");
chomp(my @lines = <$out>);
abend(EX_DATAERR, "can't parse $files{'configure.ac'}")
unless $#lines == 1;
($package_name, $package_version) = @lines;
$package_tarname = $package_name;
$package_tarname =~ s/GNU\s+//;
$package_tarname = lc $package_tarname; # FIXME: this is not always right,
# perhaps
info("package $package_name, tarname $package_tarname, version $package_version") if $verbose;
$package_base = "$package_tarname-$package_version";
unless (defined($release_type)) {
if ($package_version =~ m/\d+\.\d+\.(\d+)/ && int($1) >= 90) {
$release_type = 'alpha';
} else {
$release_type = 'stable';
}
}
if (substr($archive_file, 0, length($package_base)) ne $package_base) {
abend(EX_DATAERR,
"filename $archive_file does not begin with $package_base");
}
if ($package_base ne $topdir) {
abend(EX_DATAERR,
"toplevel directory $topdir does not begin with $package_base");
}
my $potfile = "po/$package_tarname.pot";
unless ($files{$potfile}) {
abend(EX_DATAERR, "potfile $potfile not found in archive");
}
verify_potfile($files{$potfile});
}
# po_header($FILENAME)
# --------------------
# Extract the PO header from the POT file $FILENAME.
# Returns a reference to a hash: header-name => value.
sub po_header {
my $name = shift;
(my $h = Locale::PO->load_file_asarray($name)->[0]->msgstr)
=~ s/^"(.*)"$/$1/;
my %ret;
foreach my $s (split /\\n/, $h) {
if ($s =~ /^(.+?):\s*(.*)$/) {
$ret{lc $1}=$2;
}
}
\%ret;
}
if ($file eq '') {
err($message);
} else {
err("$file: $message");
}
}
}
}
}
sub set_mailer {
my ($mailer, $locus) = @_;
if ($mailer =~ /sendmail:(.*)/) {
$mailer_args{via} = 'sendmail';
$mailer_args{executable} = $1 if $1;
} elsif ($mailer =~ m#smtp://(?:(?<user>[^:]+)(?::(?<password>.+))?@)?(?<host>[^:]+)(?::(?<port>\d+))?#) {
$mailer_args{via} = 'smtp';
$mailer_args{hostname} = $+{host};
$mailer_args{port} = $+{port} if $+{port};
$mailer_args{username} = $+{user} if $+{user};
$mailer_args{password} = $+{password} if $+{password};
} else {
err("unknown mailer spec", prefix => $locus);
return 0;
}
return 1;
}
sub read_template_file {
my ($file, $locus) = @_;
if (open(my $fd, '<', $file)) {
local $/;
$template = <$fd>;
close($fd);
return 1;
} else {
err("can't open template file $file: $!", prefix => $locus);
return 0;
}
}
my %kw = (
keep => \$keep,
'template-file' => \&read_template_file,
template => \$template,
'signature-file' => \$signature_file,
mailer => \&set_mailer,
from => \$sender,
sender => \$sender,
fullname => \$fullname,
domain => \$localdomain,
to => \$recipient,
add => \@add_headers,
'add-header' => \@add_headers,
'refile-method' => \$refile_method
);
sub read_config {
my $config_file = shift;
open(FILE, "<", $config_file)
or abend(EX_NOINPUT, "cannot open $config_file: $!");
my $line = 0;
my $err;
my $key;
my $val;
my $heredoc;
my $heredoc_line;
while (<FILE>) {
++$line;
if ($heredoc) {
if (/^$heredoc\s*$/) {
$heredoc = undef;
} else {
$val .= $_;
next;
}
} else {
chomp;
s/^\s+//;
s/\s+$//;
s/#.*//;
next if ($_ eq "");
if (/^(.*?)\s*=\s*(.*)/) {
$key = $1;
$val = $2;
if ($val =~ /<<(\w+)\s*$/) {
$heredoc = $1;
$heredoc_line = $line;
$val = '';
next;
}
} else {
err("$config_file:$line: syntax error");
++$err;
}
}
if (exists($kw{$key})) {
my $ref = $kw{$key};
if (ref($ref) eq 'CODE') {
unless (&{$ref}($val, "$config_file:$line")) {
++$err;
}
} elsif (ref($ref) eq 'ARRAY') {
push @{$ref}, $val;
} else {
$$ref = $val;
}
} else {
err("$config_file:$line: unrecognized keyword: '$key'");
++$err;
}
}
close FILE;
abend(EX_CONFIG, "unfinished heredoc, started at line $heredoc_line")
if defined $heredoc;
abend(EX_CONFIG, "errors in config file") if $err;
}
#
my $debug;
my $config_file = "$ENV{HOME}/.tpnotify" if -e "$ENV{HOME}/.tpnotify";
Getopt::Long::Configure(qw(gnu_getopt no_ignore_case pass_through));
GetOptions("help" => sub {
pod2usage(-exitstatus => EX_OK, -verbose => 2);
},
"h" => sub {
pod2usage(-message => "$progname: $progdescr",
-exitstatus => EX_OK);
},
"usage" => sub {
pod2usage(-exitstatus => EX_OK, -verbose => 0);
},
"config|c=s" => \$config_file,
"no-config|N" => sub { $config_file = undef },
);
read_config($config_file) if defined $config_file;
Getopt::Long::Configure(qw(gnu_getopt no_ignore_case no_pass_through));
GetOptions("keep|k" => \$keep,
"alpha|A" => sub { $release_type = 'alpha' },
"stable|S" => sub { $release_type = 'stable' },
"template|t=s" => sub {
exit(EX_NOINPUT) unless read_template_file($_[1])
},
"signature|s=s" => \$signature_file,
"no-signature" => sub { $signature_file = undef },
"verbose|v+" => \$verbose,
"dry-run|n" => \$dry_run,
"debug|d+" => \$debug,
"mailer|m=s" => sub {
exit(EX_USAGE) unless set_mailer($_[1])
},
"from|f=s" => \$sender,
"fullname|F=s" => \$fullname,
"domain|D=s" => \$localdomain,
"to=s" => \$recipient,
"add|a=s@" => \@add_headers,
"refile-method=s" => \$refile_method,
"force" => \$force_option
) or exit(EX_USAGE);
++$verbose if $dry_run;
if ($debug && exists($mailer_args{via})) {
if ($mailer_args{via} eq 'sendmail') {
$mailer_args{sendmail_options} = []
unless exists $mailer_args{sendmail_options};
push @{$mailer_args{sendmail_options}},
'-O', 'LogLevel=99', '-d10.100', '-d13.90', '-d11.100';
} elsif ($mailer_args{via} eq 'smtp') {
$mailer_args{smtp_debug} = 1;
}
}
if ($sender && exists($mailer_args{via})) {
if ($mailer_args{via} eq 'sendmail') {
$mailer_args{sendmail_options} = []
unless exists $mailer_args{sendmail_options};
push @{$mailer_args{sendmail_options}}, '-f', $sender;
} elsif ($mailer_args{via} eq 'smtp') {
$mailer_args{from} = $sender;
}
}
my ($name,undef,undef,undef,undef,$comment,$gecos) = getpwuid($<);
$fullname = $gecos || $comment || $name unless defined $fullname;
$sender = $name . '@' . ($localdomain || hostname()) unless defined $sender;
if ($recipient) {
$mailer_args{to} = $recipient;
}
$archive_url = shift;
abend(EX_USAGE, "not enough arguments") unless defined $archive_url;
abend(EX_USAGE, "too many arguments") unless $#ARGV == -1;
if ($refile_method) {
abend(EX_USAGE, "unknown refiling method")
unless exists($refile_tab{$refile_method});
abend(EX_USAGE, "refiling method not supported")
unless (&{$refile_tab{$refile_method}{supported}});
}
$wd = tempdir()
or abend(EX_CANTCREAT, "can't create temporary directory: $!");
( run in 0.535 second using v1.01-cache-2.11-cpan-39bf76dae61 )