//

// 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;

                  };

 

                  if (line.EndsWith("="))

                  {

                        Debugger.Break();

                  };

 

                  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;

            }    

      }

}