File Coverage

blib/lib/Convert/ASCII/Armour.pm
Criterion Covered Total %
statement 71 76 93.4
branch 17 24 70.8
condition 1 2 50.0
subroutine 12 14 85.7
pod 4 9 44.4
total 105 125 84.0


line stmt bran cond sub pod time code
1             #!/usr/bin/perl -sw
2             ##
3             ## Convert::ASCII::Armour
4             ##
5             ## Copyright (c) 2001, Vipul Ved Prakash. All rights reserved.
6             ## This code is free software; you can redistribute it and/or modify
7             ## it under the same terms as Perl itself.
8             ##
9             ## $Id: Armour.pm,v 1.4 2001/03/19 23:15:09 vipul Exp $
10              
11             package Convert::ASCII::Armour;
12 5     5   9315 use strict;
  5         9  
  5         185  
13 5     5   26 use Digest::MD5 qw(md5);
  5         8  
  5         317  
14 5     5   4513 use MIME::Base64;
  5         4596  
  5         379  
15 5     5   5889 use Compress::Zlib qw(compress uncompress);
  5         514558  
  5         557  
16 5     5   75 use vars qw($VERSION);
  5         12  
  5         6407  
17              
18             ($VERSION) = '$Revision: 1.4 $' =~ /\s(\d+\.\d+)\s/;
19              
20              
21             sub new {
22 5     5 1 133 return bless {}, shift;
23             }
24              
25              
26             sub error {
27 0     0 0 0 my ($self, $errstr) = @_;
28 0         0 $$self{errstr} = "$errstr\n";
29 0         0 return;
30             }
31              
32              
33             sub errstr {
34 0     0 1 0 my $self = shift;
35 0         0 return $$self{errstr};
36             }
37              
38              
39             sub armour {
40              
41 4     4 1 76 my ($self, %params) = @_;
42              
43 4 100       20 my $compress = $params{Compress} ? "COMPRESSED " : "";
44 4 50       20 return undef unless $params{Content};
45 4 50       22 $params{Object} = "UNKNOWN $compress DATA" unless $params{Object};
46              
47 4         21 my $head = "-"x5 . "BEGIN $compress$params{Object}" . "-"x5;
48 4         18 my $tail = "-"x5 . "END $compress$params{Object}" . "-"x5;
49              
50 4         9 my $content = $self->encode_content (%{$params{Content}});
  4         30  
51 4 100       45 $content = compress($content) if $compress;
52 4         1164 my $checksum = encode_base64 (md5 ($content));
53 4         17 my $econtent = encode_base64 ($content);
54              
55 4         9 my $eheaders = "";
56 4         10 for my $key (keys %{$params{Headers}}) {
  4         16  
57 6         22 $eheaders .= "$key: $params{Headers}->{$key}\n";
58             }
59              
60 4         24 my $message = "$head\n$eheaders\n$econtent=$checksum$tail\n";
61 4         29 return $message;
62              
63             }
64              
65              
66             sub unarmour {
67 4     4 1 31 my ($self, $message) = @_;
68              
69 4 50       76 my ($head, $object, $headers, $content, $tail) = $message =~
70             m:(-----BEGIN ([^\n\-]+)-----)\n(.*?\n\n)?(.+)(-----END .*?-----)$:s
71             or return $self->error ("Breached Armour.");
72              
73 4         16 my ($compress, $obj) = $object =~ /^(COMPRESSED )(.*)$/;
74 4 100       15 $object = $obj if $obj;
75 4 50       29 $content =~ s:=([^\n]+)$::s or return $self->error ("Breached Armour.");
76 4         30 my $checksum = $1; $content = decode_base64 ($content);
  4         30  
77 4         24 my $ncheck = encode_base64 (md5 ($content)); $ncheck =~ s/\n//;
  4         13  
78 4 50       15 return $self->error ("Checksum Failed.") unless $ncheck eq $checksum;
79 4 100       21 $content = uncompress ($content) if $compress;
80 4   50     187 my $dcontent = $self->decode_content ($content) || return;
81              
82 4         7 my $dheaders;
83 4 100       12 if ($headers) {
84 3         19 my @pairs = split /\n/, $headers;
85 3         8 for (@pairs) {
86 6         17 my ($key, $value) = split /: /, $_, 2;
87 6 50       26 $$dheaders{$key} = $value if $key;
88             }
89             }
90              
91 4         21 my %return = ( Content => $dcontent,
92             Object => $object,
93             Headers => $dheaders );
94              
95 4         15 return \%return;
96              
97             }
98              
99              
100             sub encode_content {
101 5     5 0 37 my ($self, %data) = @_;
102 5         10 my $encoded = "";
103              
104 5         26 for my $key (keys %data) {
105 20         76 $encoded .= length ($key) . chr(0) . length ($data{$key}) .
106             chr(0) . "$key$data{$key}";
107             }
108              
109 5         21 return $encoded;
110             }
111              
112              
113             sub decode_content {
114 5     5 0 20 my ($self, $content) = @_;
115 5         8 my %data;
116              
117 5         18 while ($content) {
118 20 50       87 $content =~ s/^(\d+)\x00(\d+)\x00// ||
119             return $self->error ("Inconsistent content.");
120 20         38 my $keylen = $1; my $valuelen = $2;
  20         31  
121 20         80 my $key = substr $content, 0, $keylen;
122 20         53 my $value = substr $content, $keylen, $valuelen;
123 20         42 substr ($content, 0, $keylen + $valuelen) = "";
124 20         90 $data{$key} = $value;
125             }
126              
127 5         24 return \%data;
128             }
129              
130              
131 1     1 0 28 sub armor { armour (@_) }
132 1     1 0 18 sub unarmor { unarmour (@_) }
133              
134              
135             1;
136              
137              
138             =head1 NAME
139              
140             Convert::ASCII::Armour - Convert binary octets into ASCII armoured messages.
141              
142             =head1 SYNOPSIS
143              
144             my $converter = new Convert::ASCII::Armour;
145              
146             my $message = $converter->armour(
147             Object => "FOO RECORD",
148             Headers => {
149             Table => "FooBar",
150             Version => "1.23",
151             },
152             Content => {
153             Key => "0x8738FA7382",
154             Name => "Zoya Hall",
155             Pic => "....", # gif
156             },
157             Compress => 1,
158             );
159              
160             print $message;
161              
162              
163             -----BEGIN COMPRESSED FOO RECORD-----
164             Version: 1.23
165             Table: FooBar
166              
167             eJwzZzA0Z/BNLS5OTE8NycgsVgCiRIVciIAJg6EJg0tiSaqhsYJvYlFy...
168             XnpOZl5qYlJySmpaekZmVnZObl5+QWFRcUlpWXlFZRWXAk7g6OTs4urm...
169             Fh4VGaWAR5ehkbGJqZm5hSUeNXWKDsoGcWpaGpq68bba0dWxtTVmDOYM...
170             NzuZ
171             =MxpZvjkrv5XyhkVCuXmsBQ==
172             -----END COMPRESSED FOO RECORD-----
173              
174              
175             my $decoded = $converter->unarmour( $message )
176             || die $converter->errstr();
177            
178              
179             =head1 DESCRIPTION
180              
181             This module converts hashes of binary octets into ASCII messages suitable
182             for transfer over 6-bit clean transport channels. The encoded ASCII
183             resembles PGP's armoured messages, but are in no way compatible with PGP.
184              
185             =head1 METHODS
186              
187             =head2 B
188              
189             Constructor.
190              
191             =head2 B
192              
193             Converts a hash of binary octets into an ASCII encoded message. The
194             encoded message has 4 parts: head and tail strings that act as identifiers
195             and delimiters, a cluster of headers at top of the message, Base64 encoded
196             message body and a Base64 encoded MD5 digest of the message body. armour()
197             takes a hash as argument with following keys:
198              
199             =over 4
200              
201             =item B
202              
203             An identification string embedded in head and tail strings.
204              
205             =item B
206              
207             Content is a hashref that contains the binary octets to be encoded. This
208             hash is serialized, compressed (if specified) and encoded into ASCII with
209             MIME::Base64. The result is the body of the encoded message.
210              
211             =item B
212              
213             Headers is a hashref that contains ASCII headers that are placed at top of
214             the encoded message. Headers are encoded as RFC822 headers.
215              
216             =item B
217              
218             A boolean parameter that forces armour() to compress the message body.
219              
220             =back
221              
222             =head2 B
223              
224             Decodes an armoured ASCII message into the hash provided as argument
225             to armour(). The hash contains Content, Object, and Headers.
226             unarmour() performs several consistency checks and returns a non-true
227             value on failure.
228              
229             =head2 B
230              
231             Returns the error message set by unarmour() on failure.
232              
233             =head1 AUTHOR
234              
235             Vipul Ved Prakash, Email@vipul.netE
236              
237             =head1 LICENSE
238              
239             Copyright (c) 2001, Vipul Ved Prakash. All rights reserved. This code is
240             free software; you can redistribute it and/or modify it under the same
241             terms as Perl itself.
242              
243             =head1 SEE ALSO
244              
245             MIME::Base64(3), Compress::Zlib(3), Digest::MD5(3)
246              
247             =cut