C# classes to decode yEnc and UuEncode encoded Usenet binaries

//


// Simple Usenet article decoders; includes classes to decode


// either yEnc or UuEncoded binaries.  In either case you


// instantiate the appropriate class passing it the stream


// you want output written to, and then feed it the input lines


// via the DecodeLine method.


//


// by Steve Tibbett


//


 


using System;


using System.Collections;


using System.Collections.Specialized;


using System.Diagnostics;


using System.Text;


using System.IO;


 


namespace NntpLib


{


      public abstract class ArticleDecoder


      {


            protected Stream outStream  = null;


 


            public ArticleDecoder(Stream OutputStream)


            {


                  outStream = OutputStream;


            }


 


            public abstract bool DecodeLine(string line);


      }


 


      public class YencDecoder : ArticleDecoder


      {


            bool inBody = false;


            bool inCode = false;


            string beginLine;


 


            public YencDecoder(Stream OutputStream, string BeginLine) : base(OutputStream)


            {


                  this.beginLine = BeginLine;


            }


 


            /// <summary>


            /// Returns true when we're done (we've found the


            /// encoded block, and completed decoding it)


            /// </summary>


            public override bool DecodeLine(string line)


            {


                  if (!inBody)


                  {


                        if (line.StartsWith("=ybegin "))


                        {


                              inBody = true;


                              return false;


                        };


                  }


 


                  if (line.StartsWith("=yend "))


                  {


                        // Done


                        return true;


                  }


 


                  if (!inCode)


                  {


                        if (line.StartsWith("=ypart "))


                              inCode = true;


 


                        return false;


                  };


 


 


                  byte[] lineBytes = Encoding.UTF8.GetBytes(line);


                  byte[] outBytes = new byte[lineBytes.Length];


                  int outByte = 0;


                  for (int i=0; i<lineBytes.Length; i++)


                  {


                        lineBytes[i] = (byte)((lineBytes[i] + 42) & 0xff);


                        char ch = (char)lineBytes[i];


                        if (ch == '=')


                        {


                              i++;


                              outBytes[outByte++] = (byte)((lineBytes[i]+64)&0xff);


                        } else


                        {


                              outBytes[outByte++] = lineBytes[i];


                        };


                  };


 


                  outStream.Write(outBytes, 0, outByte);


                  return false;


            }


      }


 


      /// <summary>


      /// Summary description for Article.


      /// </summary>


      public class UuDecoder : ArticleDecoder


      {


            bool inBody = false;


            bool inCode = false;


            string beginLine;


 


            public UuDecoder(Stream OutputStream, string BeginLine) : base(OutputStream)


            {


                  beginLine = BeginLine;


            }


 


            public static byte[] uuDecode(string sBuffer)


            {


                  // Create an output array


                  byte[] outBuffer = new byte[(sBuffer.Length-1)/4*3];


                  int outIdx = 0;


 


                  // Get the string as an array of ASCII bytes


                  byte[] asciiBytes = Encoding.ASCII.GetBytes(sBuffer);


                  for (int i=0; i<asciiBytes.Length; i++)


                        asciiBytes[i] = (byte)((asciiBytes[i]-0x20) & 0x3f);


                 


                  // Convert each block fo 4 input bytes into 3


                  // output bytes


                  for (int i = 1; i <= (asciiBytes.Length-1); i += 4)


                  {


                        outBuffer[outIdx++] = (byte)(asciiBytes[i] << 2 | asciiBytes[i+1] >> 4);


                        outBuffer[outIdx++] = (byte)(asciiBytes[i+1] << 4 | asciiBytes[i+2] >> 2);


                        outBuffer[outIdx++] = (byte)(asciiBytes[i+2] << 6 | asciiBytes[i+3]);


                  }


 


                  return outBuffer;


            }


 


            /// <summary>


            /// Returns true when we're done (we've found the


            /// encoded block, and completed decoding it)


            /// </summary>


            public override bool DecodeLine(string line)


            {


                  if (line.Length == 0 && !inBody)


                  {


                        inBody = true;


                        return false;


                  };


 


                  if (inBody && line.StartsWith("begin"))


                  {


                        inCode = true;


                        return false;


                  };


 


                  if (inCode)


                  {


                        if (line.Length < 5)


                              return true;


 


                        byte[] data = uuDecode(line);


                        outStream.Write(data, 0, data.Length);


                  };


 


                  return false;


            }    


      }


}