security/nss/cmd/smimetools/smime

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/security/nss/cmd/smimetools/smime	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,547 @@
     1.4 +#!/usr/local/bin/perl
     1.5 +
     1.6 +# This Source Code Form is subject to the terms of the Mozilla Public
     1.7 +# License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 +# file, You can obtain one at http://mozilla.org/MPL/2.0/.
     1.9 +
    1.10 +#
    1.11 +# smime.pl - frontend for S/MIME message generation and parsing
    1.12 +#
    1.13 +
    1.14 +use Getopt::Std;
    1.15 +
    1.16 +@boundarychars = ( "0" .. "9", "A" .. "F" );
    1.17 +
    1.18 +# path to cmsutil
    1.19 +$cmsutilpath = "cmsutil";
    1.20 +
    1.21 +#
    1.22 +# Thanks to Gisle Aas <gisle@aas.no> for the base64 functions
    1.23 +# originally taken from MIME-Base64-2.11 at www.cpan.org
    1.24 +#
    1.25 +sub encode_base64($)
    1.26 +{
    1.27 +    my $res = "";
    1.28 +    pos($_[0]) = 0;                          # ensure start at the beginning
    1.29 +    while ($_[0] =~ /(.{1,45})/gs) {
    1.30 +	$res .= substr(pack('u', $1), 1);    # get rid of length byte after packing
    1.31 +	chop($res);
    1.32 +    }
    1.33 +    $res =~ tr|` -_|AA-Za-z0-9+/|;
    1.34 +    # fix padding at the end
    1.35 +    my $padding = (3 - length($_[0]) % 3) % 3;
    1.36 +    $res =~ s/.{$padding}$/'=' x $padding/e if $padding;
    1.37 +    # break encoded string into lines of no more than 76 characters each
    1.38 +    $res =~ s/(.{1,76})/$1\n/g;
    1.39 +    $res;
    1.40 +}
    1.41 +
    1.42 +sub decode_base64($)
    1.43 +{
    1.44 +    local($^W) = 0; # unpack("u",...) gives bogus warning in 5.00[123]
    1.45 +
    1.46 +    my $str = shift;
    1.47 +    my $res = "";
    1.48 +
    1.49 +    $str =~ tr|A-Za-z0-9+=/||cd;            # remove non-base64 chars
    1.50 +    if (length($str) % 4) {
    1.51 +	require Carp;
    1.52 +	Carp::carp("Length of base64 data not a multiple of 4")
    1.53 +    }
    1.54 +    $str =~ s/=+$//;                        # remove padding
    1.55 +    $str =~ tr|A-Za-z0-9+/| -_|;            # convert to uuencoded format
    1.56 +    while ($str =~ /(.{1,60})/gs) {
    1.57 +	my $len = chr(32 + length($1)*3/4); # compute length byte
    1.58 +	$res .= unpack("u", $len . $1 );    # uudecode
    1.59 +    }
    1.60 +    $res;
    1.61 +}
    1.62 +
    1.63 +#
    1.64 +# parse headers into a hash
    1.65 +#
    1.66 +# %headers = parseheaders($headertext);
    1.67 +#
    1.68 +sub parseheaders($)
    1.69 +{
    1.70 +    my ($headerdata) = @_;
    1.71 +    my $hdr;
    1.72 +    my %hdrhash;
    1.73 +    my $hdrname;
    1.74 +    my $hdrvalue;
    1.75 +    my @hdrvalues;
    1.76 +    my $subhdrname;
    1.77 +    my $subhdrvalue;
    1.78 +
    1.79 +    # the expression in split() correctly handles continuation lines
    1.80 +    foreach $hdr (split(/\n(?=\S)/, $headerdata)) {
    1.81 +	$hdr =~ s/\r*\n\s+/ /g;	# collapse continuation lines
    1.82 +	($hdrname, $hdrvalue) = $hdr =~ m/^(\S+):\s+(.*)$/;
    1.83 +
    1.84 +	# ignore non-headers (or should we die horribly?)
    1.85 +	next unless (defined($hdrname));
    1.86 +	$hdrname =~ tr/A-Z/a-z/;			# lowercase the header name
    1.87 +	@hdrvalues = split(/\s*;\s*/, $hdrvalue);	# split header values (XXXX quoting)
    1.88 +
    1.89 +	# there is guaranteed to be at least one value
    1.90 +	$hdrvalue = shift @hdrvalues;
    1.91 +	if ($hdrvalue =~ /^\s*\"(.*)\"\s*$/) {		# strip quotes if there
    1.92 +	    $hdrvalue = $1;
    1.93 +	}
    1.94 +
    1.95 +	$hdrhash{$hdrname}{MAIN} = $hdrvalue;
    1.96 +	# print "XXX $hdrname = $hdrvalue\n";
    1.97 +
    1.98 +	# deal with additional name-value pairs
    1.99 +	foreach $hdrvalue (@hdrvalues) {
   1.100 +	    ($subhdrname, $subhdrvalue) = $hdrvalue =~ m/^(\S+)\s*=\s*(.*)$/;
   1.101 +	    # ignore non-name-value pairs (or should we die?)
   1.102 +	    next unless (defined($subhdrname));
   1.103 +	    $subhdrname =~ tr/A-Z/a-z/;
   1.104 +	    if ($subhdrvalue =~ /^\s*\"(.*)\"\s*$/) {	# strip quotes if there
   1.105 +		$subhdrvalue = $1;
   1.106 +	    }
   1.107 +	    $hdrhash{$hdrname}{$subhdrname} = $subhdrvalue;
   1.108 +	}
   1.109 +
   1.110 +    }
   1.111 +    return %hdrhash;
   1.112 +}
   1.113 +
   1.114 +#
   1.115 +# encryptentity($entity, $options) - encrypt an S/MIME entity,
   1.116 +#                                    creating a new application/pkcs7-smime entity
   1.117 +#
   1.118 +# entity  - string containing entire S/MIME entity to encrypt
   1.119 +# options - options for cmsutil
   1.120 +#
   1.121 +# this will generate and return a new application/pkcs7-smime entity containing
   1.122 +# the enveloped input entity.
   1.123 +#
   1.124 +sub encryptentity($$)
   1.125 +{
   1.126 +    my ($entity, $cmsutiloptions) = @_;
   1.127 +    my $out = "";
   1.128 +    my $boundary;
   1.129 +
   1.130 +    $tmpencfile = "/tmp/encryptentity.$$";
   1.131 +
   1.132 +    #
   1.133 +    # generate a random boundary string
   1.134 +    #
   1.135 +    $boundary = "------------ms" . join("", @boundarychars[map{rand @boundarychars }( 1 .. 24 )]);
   1.136 +
   1.137 +    #
   1.138 +    # tell cmsutil to generate a enveloped CMS message using our data
   1.139 +    #
   1.140 +    open(CMS, "|$cmsutilpath -E $cmsutiloptions -o $tmpencfile") or die "ERROR: cannot pipe to cmsutil";
   1.141 +    print CMS $entity;
   1.142 +    unless (close(CMS)) {
   1.143 +	print STDERR "ERROR: encryption failed.\n";
   1.144 +	unlink($tmpsigfile);
   1.145 +	exit 1;
   1.146 +    }
   1.147 +
   1.148 +    $out  = "Content-Type: application/pkcs7-mime; smime-type=enveloped-data; name=smime.p7m\n";
   1.149 +    $out .= "Content-Transfer-Encoding: base64\n";
   1.150 +    $out .= "Content-Disposition: attachment; filename=smime.p7m\n";
   1.151 +    $out .= "\n";			# end of entity header
   1.152 +
   1.153 +    open (ENC, $tmpencfile) or die "ERROR: cannot find newly generated encrypted content";
   1.154 +    local($/) = undef;			# slurp whole file
   1.155 +    $out .= encode_base64(<ENC>), "\n";	# entity body is base64-encoded CMS message
   1.156 +    close(ENC);
   1.157 +
   1.158 +    unlink($tmpencfile);
   1.159 +
   1.160 +    $out;
   1.161 +}
   1.162 +
   1.163 +#
   1.164 +# signentity($entity, $options) - sign an S/MIME entity
   1.165 +#
   1.166 +# entity  - string containing entire S/MIME entity to sign
   1.167 +# options - options for cmsutil
   1.168 +#
   1.169 +# this will generate and return a new multipart/signed entity consisting
   1.170 +# of the canonicalized original content, plus a signature block.
   1.171 +#
   1.172 +sub signentity($$)
   1.173 +{
   1.174 +    my ($entity, $cmsutiloptions) = @_;
   1.175 +    my $out = "";
   1.176 +    my $boundary;
   1.177 +
   1.178 +    $tmpsigfile = "/tmp/signentity.$$";
   1.179 +
   1.180 +    #
   1.181 +    # generate a random boundary string
   1.182 +    #
   1.183 +    $boundary = "------------ms" . join("", @boundarychars[map{rand @boundarychars }( 1 .. 24 )]);
   1.184 +
   1.185 +    #
   1.186 +    # tell cmsutil to generate a signed CMS message using the canonicalized data
   1.187 +    # The signedData has detached content (-T) and includes a signing time attribute (-G)
   1.188 +    #
   1.189 +    # if we do not provide a password on the command line, here's where we would be asked for it
   1.190 +    #
   1.191 +    open(CMS, "|$cmsutilpath -S -T -G $cmsutiloptions -o $tmpsigfile") or die "ERROR: cannot pipe to cmsutil";
   1.192 +    print CMS $entity;
   1.193 +    unless (close(CMS)) {
   1.194 +	print STDERR "ERROR: signature generation failed.\n";
   1.195 +	unlink($tmpsigfile);
   1.196 +	exit 1;
   1.197 +    }
   1.198 +
   1.199 +    open (SIG, $tmpsigfile) or die "ERROR: cannot find newly generated signature";
   1.200 +
   1.201 +    #
   1.202 +    # construct a new multipart/signed MIME entity consisting of the original content and
   1.203 +    # the signature
   1.204 +    #
   1.205 +    # (we assume that cmsutil generates a SHA1 digest)
   1.206 +    $out .= "Content-Type: multipart/signed; protocol=\"application/pkcs7-signature\"; micalg=sha1; boundary=\"${boundary}\"\n";
   1.207 +    $out .= "\n";		# end of entity header
   1.208 +    $out .= "This is a cryptographically signed message in MIME format.\n"; # explanatory comment
   1.209 +    $out .= "\n--${boundary}\n";
   1.210 +    $out .= $entity;
   1.211 +    $out .= "\n--${boundary}\n";
   1.212 +    $out .= "Content-Type: application/pkcs7-signature; name=smime.p7s\n";
   1.213 +    $out .= "Content-Transfer-Encoding: base64\n";
   1.214 +    $out .= "Content-Disposition: attachment; filename=smime.p7s\n";
   1.215 +    $out .= "Content-Description: S/MIME Cryptographic Signature\n";
   1.216 +    $out .= "\n";		# end of signature subentity header
   1.217 +
   1.218 +    local($/) = undef;		# slurp whole file
   1.219 +    $out .= encode_base64(<SIG>);	# append base64-encoded signature
   1.220 +    $out .= "\n--${boundary}--\n";
   1.221 +
   1.222 +    close(SIG);
   1.223 +    unlink($tmpsigfile);
   1.224 +
   1.225 +    $out;
   1.226 +}
   1.227 +
   1.228 +sub usage {
   1.229 +    print STDERR "usage: smime [options]\n";
   1.230 +    print STDERR " options:\n";
   1.231 +    print STDERR " -S nick             generate signed message, use certificate named \"nick\"\n";
   1.232 +    print STDERR "  -p passwd          use \"passwd\" as security module password\n";
   1.233 +    print STDERR " -E rec1[,rec2...]   generate encrypted message for recipients\n";
   1.234 +    print STDERR " -D                  decode a S/MIME message\n";
   1.235 +    print STDERR "  -p passwd          use \"passwd\" as security module password\n";
   1.236 +    print STDERR "                     (required for decrypting only)\n";
   1.237 +    print STDERR " -C pathname         set pathname of \"cmsutil\"\n";
   1.238 +    print STDERR " -d directory        set directory containing certificate db\n";
   1.239 +    print STDERR "                     (default: ~/.netscape)\n";
   1.240 +    print STDERR "\nWith -S or -E, smime will take a regular RFC822 message or MIME entity\n";
   1.241 +    print STDERR "on stdin and generate a signed or encrypted S/MIME message with the same\n";
   1.242 +    print STDERR "headers and content from it. The output can be used as input to a MTA.\n";
   1.243 +    print STDERR "-D causes smime to strip off all S/MIME layers if possible and output\n";
   1.244 +    print STDERR "the \"inner\" message.\n";
   1.245 +}
   1.246 +
   1.247 +#
   1.248 +# start of main procedures
   1.249 +#
   1.250 +
   1.251 +#
   1.252 +# process command line options
   1.253 +#
   1.254 +unless (getopts('S:E:p:d:C:D')) {
   1.255 +    usage();
   1.256 +    exit 1;
   1.257 +}
   1.258 +
   1.259 +unless (defined($opt_S) or defined($opt_E) or defined($opt_D)) {
   1.260 +    print STDERR "ERROR: -S and/or -E, or -D must be specified.\n";
   1.261 +    usage();
   1.262 +    exit 1;
   1.263 +}
   1.264 +
   1.265 +$signopts = "";
   1.266 +$encryptopts = "";
   1.267 +$decodeopts = "";
   1.268 +
   1.269 +# pass -d option along
   1.270 +if (defined($opt_d)) {
   1.271 +    $signopts .= "-d \"$opt_d\" ";
   1.272 +    $encryptopts .= "-d \"$opt_d\" ";
   1.273 +    $decodeopts .= "-d \"$opt_d\" ";
   1.274 +}
   1.275 +
   1.276 +if (defined($opt_S)) {
   1.277 +    $signopts .= "-N \"$opt_S\" ";
   1.278 +}
   1.279 +
   1.280 +if (defined($opt_p)) {
   1.281 +    $signopts .= "-p \"$opt_p\" ";
   1.282 +    $decodeopts .= "-p \"$opt_p\" ";
   1.283 +}
   1.284 +
   1.285 +if (defined($opt_E)) {
   1.286 +    @recipients = split(",", $opt_E);
   1.287 +    $encryptopts .= "-r ";
   1.288 +    $encryptopts .= join (" -r ", @recipients);
   1.289 +}
   1.290 +
   1.291 +if (defined($opt_C)) {
   1.292 +    $cmsutilpath = $opt_C;
   1.293 +}
   1.294 +
   1.295 +#
   1.296 +# split headers into mime entity headers and RFC822 headers
   1.297 +# The RFC822 headers are preserved and stay on the outer layer of the message
   1.298 +#
   1.299 +$rfc822headers = "";
   1.300 +$mimeheaders = "";
   1.301 +$mimebody = "";
   1.302 +$skippedheaders = "";
   1.303 +while (<STDIN>) {
   1.304 +    last if (/^$/);
   1.305 +    if (/^content-\S+: /i) {
   1.306 +	$lastref = \$mimeheaders;
   1.307 +    } elsif (/^mime-version: /i) {
   1.308 +	$lastref = \$skippedheaders;			# skip it
   1.309 +    } elsif (/^\s/) {
   1.310 +	;
   1.311 +    } else {
   1.312 +	$lastref = \$rfc822headers;
   1.313 +    }
   1.314 +    $$lastref .= $_;
   1.315 +}
   1.316 +
   1.317 +#
   1.318 +# if there are no MIME entity headers, generate some default ones
   1.319 +#
   1.320 +if ($mimeheaders eq "") {
   1.321 +    $mimeheaders .= "Content-Type: text/plain; charset=us-ascii\n";
   1.322 +    $mimeheaders .= "Content-Transfer-Encoding: 7bit\n";
   1.323 +}
   1.324 +
   1.325 +#
   1.326 +# slurp in the entity body
   1.327 +#
   1.328 +$saveRS = $/;
   1.329 +$/ = undef;
   1.330 +$mimebody = <STDIN>;
   1.331 +$/ = $saveRS;
   1.332 +chomp($mimebody);
   1.333 +
   1.334 +if (defined $opt_D) {
   1.335 +    #
   1.336 +    # decode
   1.337 +    #
   1.338 +    # possible options would be:
   1.339 +    # - strip off only one layer
   1.340 +    # - strip off outer signature (if present)
   1.341 +    # - just print information about the structure of the message
   1.342 +    # - strip n layers, then dump DER of CMS message
   1.343 +
   1.344 +    $layercounter = 1;
   1.345 +
   1.346 +    while (1) {
   1.347 +	%hdrhash = parseheaders($mimeheaders);
   1.348 +	unless (exists($hdrhash{"content-type"}{MAIN})) {
   1.349 +	    print STDERR "ERROR: no content type header found in MIME entity\n";
   1.350 +	    last;	# no content-type - we're done
   1.351 +	}
   1.352 +
   1.353 +	$contenttype = $hdrhash{"content-type"}{MAIN};
   1.354 +	if ($contenttype eq "application/pkcs7-mime") {
   1.355 +	    #
   1.356 +	    # opaque-signed or enveloped message
   1.357 +	    #
   1.358 +	    unless (exists($hdrhash{"content-type"}{"smime-type"})) {
   1.359 +		print STDERR "ERROR: no smime-type attribute in application/pkcs7-smime entity.\n";
   1.360 +		last;
   1.361 +	    }
   1.362 +	    $smimetype = $hdrhash{"content-type"}{"smime-type"};
   1.363 +	    if ($smimetype eq "signed-data" or $smimetype eq "enveloped-data") {
   1.364 +		# it's verification or decryption time!
   1.365 +
   1.366 +		# can handle only base64 encoding for now
   1.367 +		# all other encodings are treated as binary (8bit)
   1.368 +		if ($hdrhash{"content-transfer-encoding"}{MAIN} eq "base64") {
   1.369 +		    $mimebody = decode_base64($mimebody);
   1.370 +		}
   1.371 +
   1.372 +		# if we need to dump the DER, we would do it right here
   1.373 +
   1.374 +		# now write the DER
   1.375 +		$tmpderfile = "/tmp/der.$$";
   1.376 +		open(TMP, ">$tmpderfile") or die "ERROR: cannot write signature data to temporary file";
   1.377 +		print TMP $mimebody;
   1.378 +		unless (close(TMP)) {
   1.379 +		    print STDERR "ERROR: writing signature data to temporary file.\n";
   1.380 +		    unlink($tmpderfile);
   1.381 +		    exit 1;
   1.382 +		}
   1.383 +
   1.384 +		$mimeheaders = "";
   1.385 +		open(TMP, "$cmsutilpath -D $decodeopts -h $layercounter -i $tmpderfile |") or die "ERROR: cannot open pipe to cmsutil";
   1.386 +		$layercounter++;
   1.387 +		while (<TMP>) {
   1.388 +		    last if (/^\r?$/);			# empty lines mark end of header
   1.389 +		    if (/^SMIME: /) {			# add all SMIME info to the rfc822 hdrs
   1.390 +			$lastref = \$rfc822headers;
   1.391 +		    } elsif (/^\s/) {
   1.392 +			;				# continuation lines go to the last dest
   1.393 +		    } else {
   1.394 +			$lastref = \$mimeheaders;	# all other headers are mime headers
   1.395 +		    }
   1.396 +		    $$lastref .= $_;
   1.397 +		}
   1.398 +		# slurp in rest of the data to $mimebody
   1.399 +		$saveRS = $/; $/ = undef; $mimebody = <TMP>; $/ = $saveRS;
   1.400 +		close(TMP);
   1.401 +
   1.402 +		unlink($tmpderfile);
   1.403 +
   1.404 +	    } else {
   1.405 +		print STDERR "ERROR: unknown smime-type \"$smimetype\" in application/pkcs7-smime entity.\n";
   1.406 +		last;
   1.407 +	    }
   1.408 +	} elsif ($contenttype eq "multipart/signed") {
   1.409 +	    #
   1.410 +	    # clear signed message
   1.411 +	    #
   1.412 +	    unless (exists($hdrhash{"content-type"}{"protocol"})) {
   1.413 +		print STDERR "ERROR: content type has no protocol attribute in multipart/signed entity.\n";
   1.414 +		last;
   1.415 +	    }
   1.416 +	    if ($hdrhash{"content-type"}{"protocol"} ne "application/pkcs7-signature") {
   1.417 +		# we cannot handle this guy
   1.418 +		print STDERR "ERROR: unknown protocol \"", $hdrhash{"content-type"}{"protocol"},
   1.419 +			"\" in multipart/signed entity.\n";
   1.420 +		last;
   1.421 +	    }
   1.422 +	    unless (exists($hdrhash{"content-type"}{"boundary"})) {
   1.423 +		print STDERR "ERROR: no boundary attribute in multipart/signed entity.\n";
   1.424 +		last;
   1.425 +	    }
   1.426 +	    $boundary = $hdrhash{"content-type"}{"boundary"};
   1.427 +
   1.428 +	    # split $mimebody along \n--$boundary\n - gets you four parts
   1.429 +	    # first (0), any comments the sending agent might have put in
   1.430 +	    # second (1), the message itself
   1.431 +	    # third (2), the signature as a mime entity
   1.432 +	    # fourth (3), trailing data (there shouldn't be any)
   1.433 +
   1.434 +	    @multiparts = split(/\r?\n--$boundary(?:--)?\r?\n/, $mimebody);
   1.435 +
   1.436 +	    #
   1.437 +	    # parse the signature headers
   1.438 +	    ($submimeheaders, $submimebody) = split(/^$/m, $multiparts[2]);
   1.439 +	    %sighdrhash = parseheaders($submimeheaders);
   1.440 +	    unless (exists($sighdrhash{"content-type"}{MAIN})) {
   1.441 +		print STDERR "ERROR: signature entity has no content type.\n";
   1.442 +		last;
   1.443 +	    }
   1.444 +	    if ($sighdrhash{"content-type"}{MAIN} ne "application/pkcs7-signature") {
   1.445 +		# we cannot handle this guy
   1.446 +		print STDERR "ERROR: unknown content type \"", $sighdrhash{"content-type"}{MAIN},
   1.447 +			"\" in signature entity.\n";
   1.448 +		last;
   1.449 +	    }
   1.450 +	    if ($sighdrhash{"content-transfer-encoding"}{MAIN} eq "base64") {
   1.451 +		$submimebody = decode_base64($submimebody);
   1.452 +	    }
   1.453 +
   1.454 +	    # we would dump the DER at this point
   1.455 +
   1.456 +	    $tmpsigfile = "/tmp/sig.$$";
   1.457 +	    open(TMP, ">$tmpsigfile") or die "ERROR: cannot write signature data to temporary file";
   1.458 +	    print TMP $submimebody;
   1.459 +	    unless (close(TMP)) {
   1.460 +		print STDERR "ERROR: writing signature data to temporary file.\n";
   1.461 +		unlink($tmpsigfile);
   1.462 +		exit 1;
   1.463 +	    }
   1.464 +
   1.465 +	    $tmpmsgfile = "/tmp/msg.$$";
   1.466 +	    open(TMP, ">$tmpmsgfile") or die "ERROR: cannot write message data to temporary file";
   1.467 +	    print TMP $multiparts[1];
   1.468 +	    unless (close(TMP)) {
   1.469 +		print STDERR "ERROR: writing message data to temporary file.\n";
   1.470 +		unlink($tmpsigfile);
   1.471 +		unlink($tmpmsgfile);
   1.472 +		exit 1;
   1.473 +	    }
   1.474 +
   1.475 +	    $mimeheaders = "";
   1.476 +	    open(TMP, "$cmsutilpath -D $decodeopts -h $layercounter -c $tmpmsgfile -i $tmpsigfile |") or die "ERROR: cannot open pipe to cmsutil";
   1.477 +	    $layercounter++;
   1.478 +	    while (<TMP>) {
   1.479 +		last if (/^\r?$/);
   1.480 +		if (/^SMIME: /) {
   1.481 +		    $lastref = \$rfc822headers;
   1.482 +		} elsif (/^\s/) {
   1.483 +		    ;
   1.484 +		} else {
   1.485 +		    $lastref = \$mimeheaders;
   1.486 +		}
   1.487 +		$$lastref .= $_;
   1.488 +	    }
   1.489 +	    $saveRS = $/; $/ = undef; $mimebody = <TMP>; $/ = $saveRS;
   1.490 +	    close(TMP);
   1.491 +	    unlink($tmpsigfile);
   1.492 +	    unlink($tmpmsgfile);
   1.493 +
   1.494 +	} else {
   1.495 +
   1.496 +	    # not a content type we know - we're done
   1.497 +	    last;
   1.498 +
   1.499 +	}
   1.500 +    }
   1.501 +
   1.502 +    # so now we have the S/MIME parsing information in rfc822headers
   1.503 +    # and the first mime entity we could not handle in mimeheaders and mimebody.
   1.504 +    # dump 'em out and we're done.
   1.505 +    print $rfc822headers;
   1.506 +    print $mimeheaders . "\n" . $mimebody;
   1.507 +
   1.508 +} else {
   1.509 +
   1.510 +    #
   1.511 +    # encode (which is much easier than decode)
   1.512 +    #
   1.513 +
   1.514 +    $mimeentity = $mimeheaders . "\n" . $mimebody;
   1.515 +
   1.516 +    #
   1.517 +    # canonicalize inner entity (rudimentary yet)
   1.518 +    # convert single LFs to CRLF
   1.519 +    # if no Content-Transfer-Encoding header present:
   1.520 +    #  if 8 bit chars present, use Content-Transfer-Encoding: quoted-printable
   1.521 +    #  otherwise, use Content-Transfer-Encoding: 7bit
   1.522 +    #
   1.523 +    $mimeentity =~ s/\r*\n/\r\n/mg;
   1.524 +
   1.525 +    #
   1.526 +    # now do the wrapping
   1.527 +    # we sign first, then encrypt because that's what Communicator needs
   1.528 +    #
   1.529 +    if (defined($opt_S)) {
   1.530 +	$mimeentity = signentity($mimeentity, $signopts);
   1.531 +    }
   1.532 +
   1.533 +    if (defined($opt_E)) {
   1.534 +	$mimeentity = encryptentity($mimeentity, $encryptopts);	
   1.535 +    }
   1.536 +
   1.537 +    #
   1.538 +    # XXX sign again to do triple wrapping (RFC2634)
   1.539 +    #
   1.540 +
   1.541 +    #
   1.542 +    # now write out the RFC822 headers
   1.543 +    # followed by the final $mimeentity
   1.544 +    #
   1.545 +    print $rfc822headers;
   1.546 +    print "MIME-Version: 1.0 (NSS SMIME - http://www.mozilla.org/projects/security)\n";	# set up the flag
   1.547 +    print $mimeentity;
   1.548 +}
   1.549 +
   1.550 +exit 0;

mercurial