blob: 8c1f179ab694df835e9357045181441ea99c637b [file] [log] [blame]
package LinuxStableQueue;
use strict;
use warnings;
use Config::Simple;
use Cwd ();
use File::Path ();
use MIME::Words ();
use POSIX ();
BEGIN {
use Exporter ();
our (@ISA, @EXPORT_OK);
@ISA = qw(Exporter);
@EXPORT_OK = qw($STABLE_QUEUES %STABLE_GIT $UPSTREAM_GIT $MAIL_FROM
$MAIL_ANNOUNCE_TO $MAIL_ANNOUNCE_CC $MAIL_REVIEW_TO
$MAIL_REVIEW_COVER_CC $MAIL_REVIEW_CC $STABLE_RC_URL
&base_version &last_update &next_update &get_queue
&get_git &common_mail_header
&mime_entity_to_mbox &print_signature &parse_mailbox_addr
&mailbox_addr_to_addr_spec &make_mailbox_addr
&parse_mailbox_list &quilt_dir_from_git_dir);
}
our ($STABLE_QUEUES, %STABLE_GIT, $UPSTREAM_GIT, $MAIL_FROM,
$MAIL_ANNOUNCE_TO, $MAIL_ANNOUNCE_CC, $MAIL_REVIEW_TO,
$MAIL_REVIEW_COVER_CC, $MAIL_REVIEW_CC);
# Config::Simple does a reasonably good job of parsing shell variable
# assignments.
my $config = new Config::Simple(
filename => "${ENV{HOME}}/.config/linux-stable-queue",
syntax => 'simple');
my $vars = $config->vars();
# Convert each known config variable to a module-level variable,
# except for STABLE_GIT_* which we convert to a single variable.
for my $key (keys %$vars) {
if ($key =~ /^default\.(STABLE_QUEUES|UPSTREAM_GIT|MAIL_(?:FROM|ANNOUNCE_(?:TO|CC)|REVIEW_(?:TO|COVER_CC|CC))|STABLE_RC_URL)$/) {
no strict 'refs';
${$1} = $vars->{$key};
} elsif ($key =~ /^default\.STABLE_GIT_([\d_]+)$/) {
my $base = $1;
$base =~ s/_/./g;
$STABLE_GIT{$base} = $vars->{$key};
}
}
# Get base version, i.e. the Linus stable release that a version is based on
sub base_version {
my ($ver) = @_;
$ver =~ s/-rc.*$//;
$ver =~ s/^((?:2\.6|[3-9]|..)\..*)\..*/$1/;
return $ver;
}
sub add_update {
my ($ver, $delta) = @_;
my ($base, $update);
$base = base_version($ver);
$update = $ver;
$update =~ s/^\Q$base.\E// or $update = 0;;
$update += $delta;
return ($update == 0) ? $base : "$base.$update";
}
# Get last stable update version (or Linus stable release, for .1)
sub last_update {
my ($ver) = @_;
return add_update($ver, -1);
}
# Get next stable update version
sub next_update {
my ($ver) = @_;
return add_update($ver, 1);
}
# Get patch queue directory for a given base version
sub get_queue {
my ($base) = @_;
my $queue = "$STABLE_QUEUES/queue-$base";
File::Path::make_path $queue or die "$!";
return $queue;
}
# Get git repo directory for a given base version
sub get_git {
my ($base) = @_;
return $STABLE_GIT{$base};
}
# We want messages to display in the right order if sorted by either
# Subject or Date. The Date field only has 1 second resolution, so
# add 1 second for each message.
my $mail_time;
sub next_mail_time {
if ($mail_time) {
++$mail_time;
} else {
$mail_time = time();
}
return $mail_time;
}
# Return common mail header fields
sub common_mail_header {
# XXX Surely there are common utilities for this?
my $date = POSIX::strftime('%a, %d %b %Y %H:%M:%S %z',
localtime(next_mail_time()));
my $domain = $MAIL_FROM;
$domain =~ s/>?\s*$//;
$domain =~ s/^.*\@//;
my $mid = '<lsq.' . time() . '.' . int(rand(1e9)) . "\@$domain>";
return (Date => $date,
'Message-Id' => $mid,
'X-Mailer' => 'LinuxStableQueue (scripts by bwh)',
'X-Patchwork-Hint' => 'ignore');
}
sub mime_entity_to_mbox {
my ($entity, $mbox_fh) = @_;
for my $part ($entity->parts ? $entity->parts : ($entity)) {
if ($part->as_string =~ /^From /m) {
# We have to use QP and escape this later. The as_string
# method will take care of escaping everything else as
# needed for QP.
$part->head->replace('Content-Transfer-Encoding',
'quoted-printable');
}
}
# Escape 'From ' within the message and ensure are at least two '\n'
# before the 'From ' of the next message
my $text = $entity->as_string;
$text =~ s/^From /=46rom /gm;
while ($text !~ /\n\n$/) {
$text .= "\n";
}
# The address here will become the SMTP sender when using
# formail + sendmail. For now, copy the From address. The date
# is irrelevant though.
my $sender = mailbox_addr_to_addr_spec($entity->head->get('From'));
$mbox_fh->print("From $sender Mon Mar 25 00:00:00 2013\n");
$mbox_fh->print($text);
}
sub print_signature {
my ($out_fh) = @_;
my $sig_fh = new FileHandle("${ENV{HOME}}/.signature", 'r');
if (defined($sig_fh)) {
print $out_fh "\n-- \n";
while (<$sig_fh>) {
print $out_fh $_;
}
close($sig_fh);
}
}
# Split mailbox address into name (if present) and addr-spec.
# Don't bother handling comments or rejecting all illegal characters.
sub parse_mailbox_addr {
my ($mailbox) = @_;
my ($name, $addr_spec);
if ($mailbox =~ /^\s*((?:[^<]|\\.)*)\s*<([^<>]+)>\s*$/) {
($name, $addr_spec) = ($1, $2);
$name =~ s/\s*$//;
} else {
$addr_spec = $mailbox;
}
return ($name, $addr_spec);
}
sub mailbox_addr_to_addr_spec {
return (parse_mailbox_addr(@_))[1];
}
# Make mailbox address from name (may be undef) and addr-spec.
sub make_mailbox_addr {
my ($name, $addr_spec) = @_;
if (!defined($name)) {
return $addr_spec;
} else {
# Quote and MIMEify name as necessary
if ($name !~ /^".*"$/) {
$name = "\"$name\"";
}
$name = MIME::Words::encode_mimewords($name, Charset => 'UTF-8');
return "$name <$addr_spec>";
}
}
# Split mailbox list into mailbox addresses.
# List is comma-separated, but addresses may also contain quoted or
# escaped commas.
sub parse_mailbox_list {
my ($list) = @_;
my (@mailboxes);
while ($list =~ /\G((?:[^,\\\"]|\\.|"(?:[^,\\\"]|\\.)*")+),?/g) {
push @mailboxes, $1;
}
return @mailboxes;
}
sub quilt_dir_from_git_dir {
my ($git_dir) = @_;
$git_dir = Cwd::abs_path($git_dir);
for my $base (keys %STABLE_GIT) {
if ($git_dir eq Cwd::abs_path($STABLE_GIT{$base})) {
return "$STABLE_QUEUES/queue-$base";
}
}
return undef;
}
1;