1: #!/usr/bin/perl -w
   2: ##############################################################################
   3: ##    -*-perl-*-
   4: ##
   5: ## ydecode - A better, faster yEnc decoder.
   6: ##
   7: ## REVISION HISTORY
   8: ##
   9: ## 1.0 2002/02/27 Initial release.
  10: ## 1.1 2002/03/02 Fixed miscellaneous translation bugs.
  11: ## 1.2 2002/03/12 Code around utf8 badness by working at the byte level.
  12: ## 1.3 2002/11/05 Precompute mapping for a slight speedup (robobunny).
  13: ##############################################################################
  14: 
  15: package Convert::yEnc;
  16: 
  17: use strict;
  18: use String::CRC32;
  19: use Exporter;
  20: use vars qw(@ISA @EXPORT_OK);
  21: @ISA = qw(Exporter);
  22: @EXPORT_OK = qw(decode);
  23: 
  24: sub decode($)
  25: {
  26:     my $fh = shift;
  27: 
  28:     my $size;
  29:     my $name;
  30:     my $part;
  31:     my $offset;
  32:     my $pSize;
  33:     my $decoded;
  34:     my @ymap = map { ($_-42+256)%256 } (0..255);
  35: 
  36:     my $decoding = 0;
  37:     binmode($fh);
  38:     while(<$fh>) {
  39:         chomp;
  40:         if (!$decoding) {
  41:             if (/^=ybegin/) {
  42:                 if (/ size=(\d+)/) {
  43:                     $size = $1;
  44:                 } 
  45:                 else {
  46:                     die "size field in ybegin is mandatory.\n";
  47:                 }
  48: 
  49:                 if (/ part=(\d+)/) {
  50:                     $part = $1;
  51:                 } 
  52:                 else {
  53:                     undef $part;
  54:                 }
  55: 
  56:                 if (/ name=(.*)$/) {
  57:                     $name = $1;
  58:                     $name =~ s/\s+$//g;
  59:                     print "Found attachment $name of size $size.\n";
  60:                 } 
  61:                 else {
  62:                     die "name field in ybegin is mandatory.\n";
  63:                 }
  64: 
  65:                 if (defined $part) {
  66:                     my $line = <$fh>;
  67:                     chomp $line;
  68:                     $line =~ s/\s+$//g;
  69:                     if ($line =~ /^=ypart/) {
  70:                         if ($line =~ / begin=(\d+)/) {
  71:                             $offset = $1 - 1;
  72:                         } 
  73:                         else {
  74:                             print "Part $part has no begin field - ignoring.\n";
  75:                             undef $part;
  76:                         }
  77:                         if ($line =~ / end=(\d+)/) {
  78:                             $pSize = $1 - $offset;
  79:                         } 
  80:                         else {
  81:                             print "Part $part has no end field - ignoring.\n";
  82:                             undef $part;
  83:                         }
  84:                         print "File $name is multipart.\n" if ($part == 1);
  85:                         print "Processing part $part.\n";
  86:                     } 
  87:                     else {
  88:                         print "ybegin with part= field not followed"
  89:                             ." by ypart=.  Treating as a single part.\n";
  90:                         undef $part;
  91:                     }
  92:                 }
  93:                 
  94:                 undef $decoded;    
  95:                 $decoding = 1 if (defined $size);
  96:             }
  97:         } 
  98:         else {
  99:             if (/^=yend/) {
 100:                 $decoding = 0;
 101: 
 102:                 my $endSize;
 103:                 if (/size=(\d+)/) {
 104:                     $endSize = $1;
 105:                 } 
 106:                 else {
 107:                     print "Size is mandatory in yend, ignoring encoded stuff.\n";
 108:                     next;
 109:                 }
 110: 
 111:                 my $crc;
 112:                 if (defined $part) {
 113:                     if (/ pcrc32=([0-9a-f]+)/i) {
 114:                         $crc = $1;
 115:                     }
 116:                 } 
 117:                 else {
 118:                     if (/ crc32=([0-9a-f]+)/i) {
 119:                         $crc = $1;
 120:                     }
 121:                 }
 122: 
 123:                 if (defined $crc) {
 124:                     my $realCRC = crc32($decoded);
 125:                     if (hex($crc) != $realCRC) {
 126:                         print "CRCs mismatch.  Expected ", $crc;
 127:                         print " got ", sprintf("%x", $realCRC), ".\n";
 128:                         next;
 129:                     }
 130:                 }
 131: 
 132:                 my $decodedSize = length($decoded);
 133:                 if (defined $part) {    
 134:                     if ($decodedSize != $pSize) {
 135:                         die "Size mismatch.  Expected $pSize, got $decodedSize.\n";
 136:                     }
 137: 
 138:                     print "Writing part $part to $name...";
 139:                     if ($part == 1) {
 140:                         open(FH,"> $name") or die "Can't write to file $name\n";
 141:                     } 
 142:                     else {
 143:                         open(FH,"+< $name") or die "Can't append to $name\n";
 144:                     }
 145:                     binmode(FH);
 146:                     seek(FH, $offset, 0);
 147:                     print FH $decoded;
 148:                     close(FH);
 149:                     print "done.\n";
 150:                 } 
 151:                 else {
 152:                     if ($endSize != $size) {
 153:                         die "begin/end size mismatch.  Expected $size, got $endSize.\n";
 154:                     }
 155:                     if ($decodedSize != $endSize) {
 156:                         die "Size mismatch.  Expected $endSize, got $decodedSize.\n";
 157:                     }
 158: 
 159:                     print "Writing $name...";
 160:                     open(FH, "> $name") or die "Can't write to file $name\n";
 161:                     binmode(FH);
 162:                     print FH $decoded;
 163:                     close(FH);
 164:                     print "done.\n";
 165:                 }
 166:             } 
 167:             else {
 168:                 my $line = $_;
 169: 
 170:                 # Remove extraneous trailing 0x0d's, if possible.
 171:                 $line =~ s/\x0d$//;
 172: 
 173:                 # Work with bytes, to protect against utf8 hardship.
 174:                 my @bytes = unpack("C*", $line);
 175:                 my @uline;
 176:                 foreach (my $i=0; $i<scalar(@bytes); $i++) {
 177:                     if ($bytes[$i] == ord('=')) {
 178:                         $i++;
 179:                         $bytes[$i] -= 64;
 180:                     }
 181:                     push @uline, $ymap[$bytes[$i]];
 182:                 }
 183:                 $decoded .= pack("C*", @uline);
 184:             }
 185:         }
 186:     }
 187: }
 188: 
 189: 1;
 190: 
 191: package main;
 192: 
 193: use strict;
 194: use Getopt::Std;
 195: 
 196: my %opt;
 197: 
 198: my $error = !getopts('w', \%opt);
 199: if ($error) {
 200:     print << "EOU";
 201: 
 202: Usage: ydecode [-w] < file
 203: 
 204:    where
 205:         -w        Print out warranty information
 206: 
 207: EOU
 208: }
 209: elsif ($opt{'w'}) {
 210: print << "EOW";
 211: ------------------------------------------------------------------------------
 212: BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
 213: FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
 214: OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
 215: PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
 216: OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 217: MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
 218: TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
 219: PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
 220: REPAIR OR CORRECTION.
 221: 
 222: IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
 223: WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
 224: REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
 225: INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
 226: OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
 227: TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
 228: YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
 229: PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
 230: POSSIBILITY OF SUCH DAMAGES.
 231: EOW
 232: }
 233: else {
 234:   Convert::yEnc::decode(\*STDIN);
 235: }
 236: 
 237: __END__
 238: =pod
 239: 
 240: =head1 NAME
 241: 
 242: ydecode - A better, faster yEnc decoder.
 243: 
 244: =head1 SYNOPSIS
 245: 
 246:    ydecode [-w] < file
 247:         -w        Print out warranty information
 248: 
 249: =head1 DESCRIPTION
 250: 
 251: C<ydecode> decodes yEnc data from the standard input and
 252: writes out the embedded file(s) to the current working directory.
 253: 
 254: =head1 EXAMPLES
 255: 
 256:    ydecode < file
 257: 
 258:    cat 00000005.ntx | ydecode
 259: 
 260: 
 261: =head1 INSTALLATION
 262: 
 263: You will need the following module(s), if you don't already have them:
 264: 
 265: String::CRC32
 266: 
 267: Getopt::Std
 268: 
 269: =head1 AUTHOR
 270: 
 271: Gerard Lanois <gerard@lanois.com>
 272: 
 273: Courtesy of Gerard's Perl Page, http://home.san.rr.com/lanois/perl/
 274: 
 275: =head1 CREDITS
 276: 
 277: This is based on yencdecoder.pl, yenc format decoder - v1.0 - 20020224
 278: by Hellstorm a.k.a. Jaume Bacardit Peñarroya) - <helly@he11storm.net>
 279: 
 280: =head1 LICENSE
 281:  
 282: ydecode - Copyright (C) 2002  Gerard Lanois <gerard@lanois.com>
 283: 
 284: This program is free software; you can redistribute it and/or modify
 285: it under the terms of the GNU General Public License as published by
 286: the Free Software Foundation; either version 2 of the License, or
 287: (at your option) any later version.
 288: 
 289: This program is distributed in the hope that it will be useful,
 290: but WITHOUT ANY WARRANTY; without even the implied warranty of
 291: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 292: GNU General Public License for more details.
 293: 
 294: You should have received a copy of the GNU General Public License
 295: along with this program; if not, write to the Free Software
 296: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 297: 
 298: =cut
 299: 

Replies are listed 'Best First'.
Re: ydecode - A better, faster yEnc decoder.
by Kanji (Parson) on Feb 28, 2002 at 03:18 UTC

    It's great to see more yEnc tools, but there already a Convert::yEnc (alt.) in CPAN.

    (Okay, okay, it's only been there for a week, but still! :))

        --k.

      It wasn't in the modules list yesterday. I looked before I started writing this. Still not there today either.

      Not in the recent arrivals either.

      If it was in the recent modules, it slipped off.

      It was never discussed or announced in any of the comp.lang.perl.* newsgroups.

      There were no mentions of yEnc on perlmonks.

      I searched all those resources prior to posting yesterday. I just forgot to search for it directly on that dog-assed slow search.cpan.org. "but still...", as you said.

      I just downloaded the Convert::yEnc from CPAN. It is has some problems.

      I am emailing the author right now....

        Warning to anyone who wants to try to use the Convert::yEnc from CPAN. It does not successfully decode the sample files given at yenc.org.

        Here is a small test program:

        #!/usr/bin/perl -w use strict; use Convert::yEnc qw(ydecode); ydecode(\*STDIN);

        Now feed it the test data files:

        decode < 00000005.ntx cat 0000002* | decode

        My program successfully decodes these data files.

        The small test program given above which uses the CPAN Convert::yEnc does not.

        "It has some problems".

        Yup, that's why I didn't announce it anywhere, nor ask for it to be included in the module list. It's not usable the way it is right now, and it's not tested -- but it's a framework.

        Pre-alpha, and needs quite a bit of UTSL to be useful at all.

Re: ydecode - A better, faster yEnc decoder.
by robobunny (Friar) on Jul 16, 2002 at 12:51 UTC
    very nice. i played around with the same script for a while before discovering your module, but your version is much better. you can get a small speed increase (~20% or so) by generating an array mapping the transformation instead of computing it every time through the loop. before your while loop, add:
    my @ymap = map { ($_-42+256)%256 } (0..255);
    then replace:
    179: push @uline, ($bytes[$i]-42+256)%256;
    with:
    push @uline, $ymap[$bytes[$i]];
      Thanks. Integrated this change into v1.3 (see above).
        hi could you accept variables too and not handles only ? My program have the data on memory, I would not like to put it on a file before lauching the yenc decode thank you :)
Re: ydecode - A better, faster yEnc decoder.
by perldoc (Scribe) on Mar 02, 2002 at 19:37 UTC

    I just fixed some NASTY bugs. Be sure you have the latest version (v1.1).