File Coverage

blib/lib/Convert/yEnc.pm
Criterion Covered Total %
statement 15 118 12.7
branch 0 60 0.0
condition 0 5 0.0
subroutine 5 8 62.5
pod 2 3 66.6
total 22 194 11.3


line stmt bran cond sub pod time code
1             package Convert::yEnc;
2              
3             require 5.005;
4 1     1   489 use strict;
  1         1  
  1         23  
5 1     1   2 use warnings;
  1         2  
  1         25  
6 1         72 use vars qw(@ISA $VERSION @EXPORT @EXPORT_OK %EXPORT_TAGS
7 1     1   2 $DEBUG $Linelength $Blocksize);
  1         4  
8 1     1   396 use String::CRC32;
  1         344  
  1         64  
9 1     1   5 use Carp;
  1         1  
  1         1469  
10              
11             require Exporter;
12              
13             @ISA = qw(Exporter);
14              
15             # Items to export into callers namespace by default. Note: do not export
16             # names by default without a very good reason. Use EXPORT_OK instead.
17             # Do not simply export all your public functions/methods/constants.
18              
19             # This allows declaration use Convert::yEnc ':all';
20             # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
21             # will save memory.
22             %EXPORT_TAGS = ( 'all' => [ qw(
23             yencode ydecode
24             ) ] );
25              
26             @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
27              
28             @EXPORT = qw(
29              
30             );
31              
32             $VERSION = '0.04_01';
33              
34             $Linelength = 64; # default, can be changed
35             $Blocksize = 200; # input buffer size
36              
37              
38             # Preloaded methods go here.
39              
40              
41             my %esc = ( chr(0) => chr( 0+64), chr(9) => chr(9+64), chr(10) => chr(10+64), chr(13) => chr(13+64),
42             chr(27) => chr(27+64), '=' => chr(ord('=')+64), '.' => chr(ord('.')+64) );
43              
44             my $esc = qr/([@{[join '', keys %esc]}])/;
45              
46             sub yencode {
47 0     0 1   my($dest, $filename, $src, $filelen,
48             $part, $lastpart,
49             $fulllen, $pend, $partcrc) = @_;
50              
51 0 0         if($part == 0) {
52             # Single part
53 0           print $dest "=ybegin line=$Linelength size=$filelen name=$filename\r\n";
54             } else {
55             # Multipart
56 0 0         $pend = 0 unless defined $pend;
57 0           my $pbegin = $pend + 1;
58 0           $pend = $pend + $filelen;
59              
60 0           print $dest "=ybegin part=$part line=$Linelength size=$fulllen name=$filename\r\n";
61 0           print $dest "=ypart begin=$pbegin end=$pend\r\n";
62             }
63              
64 0           my $crc = 0xFFFFFF;
65 0   0       my $fullcrc = $partcrc || 0xFFFFFF;
66              
67             # This will break if '==' is a valid sequence -- Ctrl-ý?
68 0           my $rex = qr/^(.{1,@{[$Linelength - 1]}}(?:[^=]|=.))/s;
  0            
69 0           my $line;
70              
71 0           my $text = '';
72 0           my $newtext;
73 0           my $outtext = '';
74              
75 0           while(read($src, $newtext, $Blocksize)) {
76 0           $crc = crc32($newtext, $crc);
77 0           $fullcrc = crc32($newtext, $fullcrc);
78              
79 0           $newtext =~ tr[\x00-\xd5\xd6-\xff]
80             [\x2a-\xff\x00-\x29];
81              
82 0           $newtext =~ s/$esc/=$esc{$1}/g;
83              
84 0           $text .= $newtext;
85              
86 0           while(length($text) >= $Linelength) {
87 0 0         $text =~ s/$rex//s and print $dest "$1\r\n";
88             }
89              
90             }
91              
92 0 0         print $dest "$text\r\n" if length $text;
93              
94 0 0         if($part == 0) {
    0          
95             # single part
96 0           printf $dest "=yend size=%d crc32=%08x \r\n",
97             $filelen, ($crc ^ 0xFFFFFFFF);
98             } elsif($part == $lastpart) {
99 0           printf $dest "=yend size=%d part=%d pcrc32=%08x crc32=%08x \r\n",
100             $filelen, $part, ($crc ^ 0xFFFFFFFF), ($fullcrc ^ 0xFFFFFFFF);
101             } else {
102             # multi-part
103 0           printf $dest "=yend size=%d part=%d pcrc32=%08x \r\n",
104             $filelen, $part, ($crc ^ 0xFFFFFFFF);
105             }
106              
107 0 0         return wantarray ? ($outtext, $pend, $crc) : ($outtext);
108             }
109              
110              
111             sub ydecode {
112 0     0 1   my($src) = @_;
113              
114 0           my $in_msg = 0;
115 0           my $eof = 0;
116              
117 0           my $numbytes = 0;
118              
119 0           my($part, $pbegin, $pend, $filesize, $filename, $linelen);
120 0           my $crc = 0xFFFFFFFF;
121 0           my $fullcrc = 0xFFFFFFFF;
122              
123             # these are the values read from the =yend line
124 0           my $filecrc;
125             my $filefullcrc;
126 0           my $filelen;
127              
128 0           local $/ = "\x0d\x0a"; # read in by CRLF-terminated lines
129              
130 0           while(<$src>) {
131 0           chomp; # remove CRLF
132              
133 0 0 0       if(/^=ybegin/ && !$in_msg) {
134 0           $in_msg = 1;
135              
136             # extract information from the start line
137 0 0         if(/part=(\d+)/) {
138 0           $part = $1;
139             }
140              
141 0 0         if(/line=(\d+)/) {
142 0           $linelen = $1;
143             } else {
144 0           croak "Line length not found in message";
145             }
146              
147 0 0         if(/size=(\d+)/) {
148 0           $filesize = $1;
149             } else {
150 0           croak "File size not found in message";
151             }
152              
153 0 0         if(/name=(.*)/) {
154 0           $filename = $1;
155              
156 0 0         open DEST, '>$filename' or croak "Can't open '$filename': $!";
157             } else {
158 0           croak "Filename not found in message";
159             }
160              
161              
162             # multipart message?
163 0 0         if(defined $part) {
164 0           local $_ = <$src>;
165 0           chomp;
166              
167 0 0         if(!/^=ypart/) {
168 0           croak "Error: part $part does not start with =ypart";
169             } else {
170 0 0         if(/begin=(\d+)/) {
171 0           $pbegin = $1;
172             } else {
173 0           croak "missing begin= in part $part";
174             }
175              
176 0 0         if(/end=(\d+)/) {
177 0           $pend = $1;
178             } else {
179 0           croak "missing end= in part $part";
180             }
181             }
182             } # multipart message?
183              
184             # skip to next line
185 0           next;
186             } # =ybegin seen
187              
188             # skip to the start of the message.
189 0 0         next unless $in_msg;
190              
191 0 0         if(/^=yend/) {
192 0           $eof = 1; # set eof marker
193              
194 0 0         if(defined $part) {
195             # multipart
196 0           my $thispart;
197              
198 0 0         if(/part=(\d+)/) {
199 0           $thispart = $1;
200             } else {
201 0           croak "part= not found in yend line of part $part";
202             }
203              
204 0 0         if($part != $thispart) {
205 0           croak "part= in yend line ($thispart) disagrees with part in start line ($part)";
206             }
207              
208 0 0         if(/pcrc32=([0-9a-fA-F]{8})/) {
209 0           $filecrc = hex $1;
210              
211 0 0         if($filecrc ^ 0xFFFFFFFF != $crc) {
212 0           croak sprintf "part CRC does not match: %08x calculated vs %08x in file",
213             ($crc ^ 0xFFFFFFFF), $filecrc;
214             }
215             }
216             } # multipart?
217              
218 0 0         if(/\bcrc32=([0-9a-fA-F]{8})/) {
219 0           $filefullcrc = hex $1;
220              
221 0 0         if($filefullcrc ^ 0xFFFFFFFF != $fullcrc) {
222 0           croak sprintf "full CRC does not match: %08x calculated vs %08x in file",
223             ($fullcrc ^ 0xFFFFFFFF), $filefullcrc;
224             }
225             }
226              
227 0 0         if(/size=(\d+)/) {
228 0           $filesize = $1;
229              
230 0 0         if($filesize != $numbytes) {
231             # TODO!
232 0           croak "filesize $filesize does not equal number of bytes in this part $numbytes";
233             }
234              
235             # TODO: handle multipart size calculations; check
236             # whole size at end of file;
237             # search for multipart messages
238             }
239              
240 0           last;
241             } # =yend line seen?
242              
243             # process a normal line
244              
245             # de-escape
246 0           s/=(.)/chr((ord($1)+256-64) % 256)/eg;
  0            
247              
248             # undo the skew
249 0           tr[\x00-\x29\x2a-\xff]
250             [\xd6-\xff\x00-\xd5];
251              
252 0           $crc = crc32($_, $crc);
253 0           $fullcrc = crc32($_, $fullcrc);
254              
255 0           $numbytes += length;
256              
257 0           print DEST;
258             } # while <$src>
259              
260             # did we exit the loop normally?
261 0 0         unless($eof) {
262 0           croak "Error: unexpected EOF in message";
263             }
264              
265 0 0         close DEST or die "Can't close '$filename': $!";
266              
267 0           1;
268             }
269              
270              
271             # for possible future use
272             sub ydecode_part {
273 0     0 0   1;
274             }
275              
276              
277              
278              
279             1;
280             __END__