#!/usr/bin/perl =for gpg -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 =head1 NAME perlsign - GPG-sign perl programs and modules. =head1 VERSION This documentation describes version 0.04 of C, March 31, 2003. =cut use strict; use IO; use Getopt::Long; use vars qw'$VERSION'; $VERSION = 0.04; # 1. Get configuration parameters. # Defaults: my $gpg_path_dflt = 'gpg'; my $tmp_path_dflt = '.'; my $cfg_file_dflt = "$ENV{HOME}/.perlsignrc"; # Command-line choices: my $gpg_path; my $tmp_path; my $cfg_file; GetOptions ('gpg=s' => \$gpg_path, 'temp=s' => \$tmp_path, 'cfg=s' => \$cfg_file); # Config file options: my $cfg; if (defined $cfg_file) # If user chose a config file { $cfg = new IO::File $cfg_file or die "Cannot read $cfg_file: $!\n"; } elsif (-e $cfg_file_dflt) # If default config file exists { $cfg = new IO::File $cfg_file_dflt or die "Cannot read $cfg_file_dflt: $!\n"; } if (defined $cfg) # If we found a file to open { while (<$cfg>) { s/\#.*//; # Remove comment chars next unless /\S/; # Skip blank lines unless (/=/) { warn "Syntax error in config file line " . $cfg->input_line_number() . ", ignoring.\n"; next; } s/^\s+//; s/\s+$//; my ($opt, $val) = split /\s*=\s*/, $_, 2; $opt =~ s/^--//; if ($opt eq 'gpg') { $gpg_path ||= $val; } elsif ($opt eq 'temp') { $tmp_path ||= $val; } else { warn "Unrecognized option $opt in config file, ignoring.\n"; } } } $gpg_path ||= $gpg_path_dflt; $tmp_path ||= $tmp_path_dflt; # 2. Copy file to 1st temp file, stripping certain lines along the way. my $in_file = shift or die "Usage: $0 script-or-module-name\n"; my $bare = $in_file; $bare =~ s!.*/!!; my $pre_sign = "$tmp_path/$bare.pre"; my $post_sign = "$tmp_path/$bare.post"; my $in = new IO::File $in_file or die "Cannot read $in_file: $!\n"; my $out = new IO::File ">$pre_sign" or die "Cannot create temp file $pre_sign: $!\n"; my $shebang; my $line; my $last_was_blank; # Check for shebang line $line = <$in>; if ($line =~ /^ \s* \# \s* \!/x) { $shebang = $line; $line = <$in>; } # Skip any blank lines while ($line =~ /^\cM?\n/) { $line = <$in>; } # If next line is not a POD line, we'll need to insert a =cut. if ($line !~ /^=/) { print $out "=cut\n\n"; } # Copy rest of input to output, skipping =for gpg and =begin gpg sections my $in_for; my $in_begin; my $skip_blanks; while (defined $line) { if ($in_for) { if ($line =~ /^\cM?\n/) { $in_for = 0; $skip_blanks = 1; } next; } if ($in_begin) { if ($line =~ /^=end \s+ (pgp|gpg)/x) { $in_begin = 0; $skip_blanks = 1; } next; } if ($line =~ /^=for \s+ (pgp|gpg)/x) { $in_for = 1; next; } if ($line =~ /^=begin \s+ (pgp|gpg)/x) { $in_begin = 1; next; } if ($skip_blanks && $line =~ /^\cM?\n/) { next; } $skip_blanks = 0; $last_was_blank = $line =~ /^\cM?\n/; print $out $line; } continue { $line = <$in>; } # Prepare for signature area print $out "\n" unless $last_was_blank; print $out "=begin gpg\n\n"; close $in; close $out; # 3. Remove 2nd temp file, if it exists. unlink $post_sign if -e $post_sign; # Ignore error; GPG'll catch it. # 4. Invoke GPG to sign the 1st temp file (into the 2nd temp file) system $gpg_path, '--output', $post_sign, '--clearsign', $pre_sign; # 5. Remove first temp file unlink $pre_sign or warn "Couldn't remove temp file $pre_sign: $!\n"; # 6. Now copy back to original, replacing stuff that was stripped away. $in = new IO::File $post_sign or die "Cannot read $post_sign: $!\n"; $out = new IO::File ">$in_file" or die "Cannot write $in_file: $!\n"; print $out "$shebang\n" if $shebang; print $out "=for gpg\n"; while (<$in>) { tr/\cM//d; print $out $_; } print $out "\n=end gpg\n"; close $in; close $out; # 7. Remove second temp file unlink $post_sign or warn "Couldn't remove temp file $post_sign: $!\n"; __END__ =head1 SYNOPSIS perlsign [options] file.pl Options: --gpg=/path/to/gpg # default: gpg --temp=/temp/dir # Default: current dir --cfg=config_filename # Default: $HOME/.perlsignrc =head1 DESCRIPTION This program invokes GPG to digitally sign Perl source files. The GPG signature becomes embedded into the source file as POD paragraphs. The functionality of the program or module should not be affected. This may be useful to prove authorship of a program, or to prove that changes have or have not been made to a source file. Note that the file you specify is overwritten with a signed version of the file. The signature can be verified with the following command: gpg --verify file =head1 OPTIONS =over 4 =item --gpg Specifies the path to the gpg executable. The default is just 'gpg'; i.e., it looks for gpg in the user's PATH. =item --temp Specifies the directory in which to place temporary files. C uses two temporary files, named by appending ".pre" and ".post" to the input file's name. The temp directory defaults to '.', ie the current directory. =item --cfg Specifies a config file to use for setting options. The default is C<.perlsignrc> in the user's home directory. =back =head1 CONFIG FILE Instead of specifying command-line options each time you run C, you can place them in a config file. The format of the config file is very simple: --option = value One option per line. You may omit the "--". Whitespace is more or less ignored. Blank lines are ignored. Anything after a "#" character on a line is ignored. =head1 POSSIBLE FUTURE FEATURES =over 1 =item * Support PGP in addition to GPG. The problem with this is that there are so many different, incompatible versions of the PGP command-line utility. =back =begin changelog CHANGE HISTORY v0.01, 2002/07/05 First version. v0.02, 2002/12/10 Changed name from 'sign' to 'perlsign'. v0.03, 2003/03/29 Formatting changes for upload to CPAN. =end changelog =head1 README This program invokes GPG to digitally sign Perl source files. The GPG signature becomes embedded into the source file as POD paragraphs. The functionality of the program or module isn't affected. This may be useful to prove authorship of a program, or to prove that changes have or have not been made to a source file. =head1 SCRIPT CATEGORIES CPAN VersionControl/CVS =head1 PREREQUISITES strict IO Getopt::Long =head1 AUTHOR / COPYRIGHT Eric J. Roode, roode@cpan.org Copyright (c) 2002-3 by Eric J. Roode. All Rights Reserved. This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself. If you have suggestions for improvement, please drop me a line. If you make improvements to this software, I ask that you please send me a copy of your changes. Thanks. =begin gpg -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.2.1 (GNU/Linux) iD8DBQE+iEBQY96i4h5M0egRAvQ1AKCm3u4kztJjcFcmCY6A8U9Z9KU+7QCgm4nV Dl4UKmLVvgoPVdIyNInBpzM= =nbOK -----END PGP SIGNATURE----- =end gpg