Exemple #1
0
 public void DecodeMessageLines( )
 {
     if (AcCommon.StringValue(Properties.ContentTransferEncoding)
         == "quoted-printable")
     {
         mRawMessageLines = mMessageLines;
         mMessageLines    = QuotedPrintable.DecodeLines(mRawMessageLines);
     }
 }
        /// <summary>
        /// Is the position in the string the start of an encoded-word
        /// </summary>
        /// <param name="InString">String to examine</param>
        /// <param name="InBx">Position in string</param>
        /// <returns></returns>
        public static bool IsStartOfEncodedWord(string InString, int InBx)
        {
            bool isEncodedWord = true;

            if (AcCommon.PullString(InString, InBx, 2, null) != "=?")
            {
                isEncodedWord = false;
            }
            else
            {
                isEncodedWord = CrackEncodedWord(InString, InBx).a;
            }

            return(isEncodedWord);
        }
Exemple #3
0
 /// <summary>
 /// Add an array of strings as a value to the comma sep value string. ( the array
 /// is its converted to CsvString form and enclosed in parenthesis before being added
 /// to the string.
 /// </summary>
 /// <param name="InValue"></param>
 /// <returns></returns>
 public CsvString Add(string[] InValue)
 {
     if (InValue == null)
     {
         string Value = null;
         AddString(Value);
     }
     else
     {
         StringBuilder sb = new StringBuilder( );
         sb.Append("_sa(");
         sb.Append(AcCommon.ToCsvString(InValue));
         sb.Append(")");
         AddString(sb.ToString( ));
     }
     return(this);
 }
        // --------------------------- Dequote ------------------------------
        public static string Dequote(string InText, QuoteEncapsulation InQem)
        {
            int           Lx        = InText.Length + 2; // 2=quote chars.
            char          QuoteChar = InText[0];
            StringBuilder sb        = new StringBuilder(Lx);
            int           Ix        = 0;
            int           EndIx     = InText.Length - 2;

            while (true)
            {
                ++Ix;
                if (Ix > EndIx)
                {
                    break;
                }
                char ch1 = InText[Ix];

                // using the escape method to enclose quote chars. This means the escape char
                // is used to encapsulate other special characters in the quoted string.
                // todo: dequote using "QuotedStringTraits" rules.
                if ((ch1 == '\\') &&
                    (InQem == QuoteEncapsulation.Escape) &&
                    (Ix < (EndIx - 1)))
                {
                    sb.Append(MaterializeEscapeChar(InText, Ix));
                    ++Ix;
                }

                // quote char enquoted using the "double the char" method.
                else if ((ch1 == QuoteChar) &&
                         (InQem == QuoteEncapsulation.Double) &&
                         (AcCommon.PullChar(InText, Ix + 1) == QuoteChar))
                {
                    sb.Append(ch1);
                    ++Ix;
                }

                // any other character.  append to result string.
                else
                {
                    sb.Append(ch1);
                }
            }
            return(sb.ToString( ));
        }
Exemple #5
0
        // ------------------- CalcMessageShowCodes ---------------------------
        // return the message string in a form that shows the cr, lf, and tab codes
        private string CalcMessageShowCodes( )
        {
            string        msg = Message;
            StringBuilder sb  = new StringBuilder(msg.Length);

            for (int Ix = 0; Ix < msg.Length; ++Ix)
            {
                char ch1 = msg[Ix];
                if (ch1 == '\t')
                {
                    sb.Append("<TAB>");
                }


                // a crlf followed by a space in a message header is a FOLD. mail agents
                // interpret folds as whitespace.
                else if ((ch1 == '\r') &&
                         (AcCommon.PullString(msg, Ix, 3, null) == "\r\n "))
                {
                    sb.Append("<FOLD>");
                    Ix += 2;
                }

                else if ((ch1 == '\r') &&
                         (AcCommon.PullString(msg, Ix, 2, null) == NetworkConstants.CrLf))
                {
                    sb.Append("<CRLF>");
                    ++Ix;
                }
                else if (ch1 == '\r')
                {
                    sb.Append("<CR>");
                }
                else if (ch1 == '\n')
                {
                    sb.Append("<LF>");
                }
                else
                {
                    sb.Append(ch1);
                }
            }
            return(sb.ToString( ));
        }
Exemple #6
0
        // ------------------------ ScanCloseParen -------------------------
        public int ScanCloseParen(int InBx)
        {
            char OpenParenChar = mCmds[InBx];
            char CloseParenChar = AcCommon.CalcCloseBraceChar(mCmds[InBx]);
            int  Ix = InBx, Fx = 0;
            int  Lx         = mCmds.Length;
            int  ParenLevel = 1;

            while (true)
            {
                ++Ix;
                if (Ix >= Lx)
                {
                    throw(new TextScanException(
                              "ScanCloseParen", "Close paren not found."));
                }

                char ch1 = mCmds[Ix];
                if (ch1 == OpenParenChar)
                {
                    ++ParenLevel;
                }
                else if (ch1 == CloseParenChar)
                {
                    --ParenLevel;
                    if (ParenLevel == 0)
                    {
                        break;
                    }
                }
                else if (IsOpenParenChar(Ix) == true)
                {
                    Fx = ScanCloseParen(Ix);
                    Ix = Fx;
                }
                else if (IsOpenQuoteChar(Ix) == true)
                {
                    Fx = ScanCloseQuote(Ix);
                    Ix = Fx;
                }
            }
            return(Ix);
        }
Exemple #7
0
        // ------------------------ ScanCloseBrace -------------------------
        // todo: pass TextTraits to a version of this method. Use recursion for each brace
        //       char found.
        /// <summary>
        /// scan for closing brace char that matches the start at open brace char.
        /// </summary>
        /// <param name="InString"></param>
        /// <param name="InBx">scan start position</param>
        /// <param name="InEx">end position in string</param>
        /// <param name="InQem"></param>
        /// <returns></returns>
        public static int ScanCloseBrace(
            string InString, int InBx, int InEx, QuoteEncapsulation InQem)
        {
            char openBraceChar = InString[InBx];
            char closeBraceChar = AcCommon.CalcCloseBraceChar(openBraceChar);
            int  Ix = InBx, Fx = 0;
            int  ParenLevel = 1;

            if (InEx >= InString.Length)
            {
                throw new ApplicationException("ScanCloseBrace end pos exceeds string length");
            }

            while (true)
            {
                ++Ix;
                if (Ix > InEx)
                {
                    Ix = -1;
                    break;
                }

                char ch1 = InString[Ix];
                if (ch1 == openBraceChar)
                {
                    ++ParenLevel;
                }
                else if (ch1 == closeBraceChar)
                {
                    --ParenLevel;
                    if (ParenLevel == 0)
                    {
                        break;
                    }
                }
                else if (IsOpenQuoteChar(ch1) == true)
                {
                    Fx = ScanCloseQuote(InString, Ix, InQem);
                    Ix = Fx;
                }
            }
            return(Ix);
        }
Exemple #8
0
        // ------------------------ ScanCloseBrace -------------------------
        // todo: pass TextTraits to a version of this method. Use recursion for each brace
        //       char found.
        public static int ScanCloseBrace(
            string InString, int InBx, QuoteEncapsulation InQem)
        {
            char openBraceChar = InString[InBx];
            char closeBraceChar = AcCommon.CalcCloseBraceChar(openBraceChar);
            int  Ix = InBx, Fx = 0;
            int  Lx         = InString.Length;
            int  ParenLevel = 1;

            while (true)
            {
                ++Ix;
                if (Ix >= Lx)
                {
                    throw(new TextException(
                              "ScanCloseBrace", "Close brace not found."));
                }

                char ch1 = InString[Ix];
                if (ch1 == openBraceChar)
                {
                    ++ParenLevel;
                }
                else if (ch1 == closeBraceChar)
                {
                    --ParenLevel;
                    if (ParenLevel == 0)
                    {
                        break;
                    }
                }
                else if (IsOpenQuoteChar(ch1) == true)
                {
                    Fx = ScanCloseQuote(InString, Ix, InQem);
                    Ix = Fx;
                }
            }
            return(Ix);
        }
Exemple #9
0
        public MimeMessagePart AddNewPart(
            string InStream, MimeMessagePart.PartInProgress InPip)
        {
            MimeMessagePart part = null;

            if (InPip.PartCode == MimePartCode.Top)
            {
                part = new MimeTopPart( );
            }
            else
            {
                part = new MimeMessagePart( );
            }

            part.PartCode = InPip.PartCode;
            base.Add(part);

            // store the property lines of the part.
            if (InPip.PropBx != -1)
            {
                part.LoadPropertyLines(InStream, InPip.PropBx, InPip.PropLineCx);
            }

            // store the message lines of the part.
            if (InPip.MessageBx != -1)
            {
                part.LoadMessageLines(InStream, InPip.MessageBx, InPip.MessageLineCx);
            }

            // the message lines are quoted-printable encoded. decode here.
            if (AcCommon.StringValue(part.Properties.ContentTransferEncoding)
                == "quoted-printable")
            {
                part.DecodeMessageLines( );
            }

            return(part);
        }
Exemple #10
0
        // --------------------------- ScanCloseQuote ------------------------------
        public static int ScanCloseQuote(
            string InString, int InBx, QuoteEncapsulation InQem)
        {
            char QuoteChar = InString[InBx];
            int  cloqIx    = -1;

            for (int Ix = InBx + 1; Ix < InString.Length; ++Ix)
            {
                char ch1 = InString[Ix];

                // using the escape method to enclose quote chars. This means the escape char
                // is used to encapsulate other special characters in the quoted string.
                // todo: dequote using "QuotedStringTraits" rules.
                if ((ch1 == '\\') &&
                    (InQem == QuoteEncapsulation.Escape) &&
                    (Ix < (InString.Length - 1)))
                {
                    ++Ix;
                }

                // quote char enquoted using the "double the char" method.
                else if ((ch1 == QuoteChar) &&
                         (InQem == QuoteEncapsulation.Double) &&
                         (AcCommon.PullChar(InString, Ix + 1) == QuoteChar))
                {
                    ++Ix;
                }

                // found the closing quote char.
                else if (ch1 == QuoteChar)
                {
                    cloqIx = Ix;
                    break;
                }
            }

            return(cloqIx);
        }
        // ----------------------- MaterializeEscapeChar ---------------------
        // used by the Dequote method. unpacks the standard escape sequences used in
        // quoted strings.
        // returns an int/char pair holding the length of the escape sequence and the
        // materialized character value.
        private static IntCharPair MaterializeEscapeChar(string InString, int InIx)
        {
            char nx = AcCommon.PullCharArray(InString, InIx, 2)[1];

            if (nx == 't')
            {
                return(new IntCharPair(2, '\t'));
            }
            else if (nx == 'r')
            {
                return(new IntCharPair(2, '\r'));
            }
            else if (nx == 'n')
            {
                return(new IntCharPair(2, '\n'));
            }
            else if (nx == '\'')
            {
                return(new IntCharPair(2, '\''));
            }
            else if (nx == '\\')
            {
                return(new IntCharPair(2, '\\'));
            }
            else if (nx == '"')
            {
                return(new IntCharPair(2, '"'));
            }
            else if (nx == '0')
            {
                return(new IntCharPair(2, '\0'));
            }
            else
            {
                throw(new ApplicationException("Unexpected escape sequence starting at " +
                                               "position " + InIx + " in string: " + InString));
            }
        }
Exemple #12
0
        // -------------------------- CalcEncodedByteBounds ------------------------
        // An encoded byte can either be in form "=xx" or a single literal byte character.
        // The bounds of the encoded byte are either the single char, or run from the
        // "=" character to the 2nd octet external form character.
        // This method is passed and encoded string and a position in that string. It returns,
        // by reference, the bounds of the encoded byte at that position.
        private static void CalcEncodedByteBounds(
            ref int OutBx,
            ref int OutEx,
            string InEncodedChars,
            int InIx)
        {
            // isolate chars prior to InIx.
            char[] chars = AcCommon.PullCharArray(InEncodedChars, InIx - 2, 3);

            // find the begin position of the encoded byte.
            if (chars[2] == '=')
            {
                OutBx = InIx;
            }
            else if (chars[1] == '=')
            {
                OutBx = InIx - 1;
            }
            else if (chars[0] == '=')
            {
                OutBx = InIx - 2;
            }
            else
            {
                OutBx = InIx;
            }

            // calc the end position of the encoded byte.
            if (InEncodedChars[OutBx] == '=')
            {
                OutEx = OutBx + 2;
            }
            else
            {
                OutEx = OutBx;
            }
        }
Exemple #13
0
        /// <summary>
        /// Decode the quoted-printable encoded string.
        /// Note: use DecodeLines to decode lines of text that include the QP line
        /// continuation character ( "=" ).
        /// </summary>
        /// <param name="InString"></param>
        /// <returns></returns>
        public static string DecodeString(string InString)
        {
            StringBuilder sb = new StringBuilder(InString.Length);

            for (int Ix = 0; Ix < InString.Length; ++Ix)
            {
                char ch1 = InString[Ix];
                if (ch1 != '=')
                {
                    sb.Append(ch1);
                }
                else
                {
                    string hex = AcCommon.PullString(InString, Ix + 1, 2, " ");

                    // note: not an error if a lone "=" in a QP encoded string.
                    if (AcCommon.IsHexExternalForm(hex) == false)
                    {
                        sb.Append(ch1);
                    }

                    else
                    {
//						sb.Append( (char) Convert.ToInt32( hex, 16 )) ;
//						Ix += 2 ;

                        byte   singleByte = System.Convert.ToByte(hex, 16);
                        byte[] bytes      = AcCommon.ToByteArray(singleByte);
                        string hexChar    = System.Text.Encoding.ASCII.GetString(bytes);
                        sb.Append(hexChar);
                        Ix += 2;
                    }
                }
            }
            return(sb.ToString( ));
        }
Exemple #14
0
        // -------------------- ScanWord_IsolateDelim ---------------------------
        private static void ScanWord_IsolateDelim(
            string InString,
            int InBx,
            ref WordCursor InOutResults,
            TextTraits InTraits)
        {
            int    Bx, Lx;
            string delim;

            // setup the start of the delim.
            if (InOutResults.WordBx == -1)
            {
                Bx = InBx;
            }
            else
            {
                Bx = InOutResults.WordEx + 1;
            }

            // word went to the end of the string.
            if (Bx >= InString.Length)
            {
                Bx = -1;
            }

            // we have a delimiter of some kind.
            if (Bx != -1)
            {
                InOutResults.DelimIsWhitespace = false;

                // the delim is a hard delim ( not whitespace )
                char ch1 = InString[Bx];
                if (AcCommon.Contains(InTraits.WhitespaceChars, ch1) == false)
                {
                    Lx    = 1;
                    delim = InString.Substring(Bx, Lx);
                    InOutResults.SetDelim(delim, Bx);
                }

                // is a soft delim ( whitespace ). Look for hard delim after the ws.
                else
                {
                    ScanCharResults scanResults =
                        ScanNotEqual(InString, Bx, InTraits.WhitespaceChars);
                    if ((scanResults.ResultPos != -1) &&
                        (AcCommon.Contains(InTraits.DelimChars, scanResults.ResultChar)))
                    {
                        Lx    = 1;
                        delim = AcCommon.CharToString(scanResults.ResultChar);
                        InOutResults.SetDelim(delim, scanResults.ResultPos);
                    }

                    // the whitespace char is the delim of record.
                    else
                    {
                        Lx    = 1;
                        delim = InString.Substring(Bx, Lx);
                        InOutResults.SetDelim(delim, Bx);
                        InOutResults.DelimIsWhitespace = true;
                    }
                }
            }
        }
Exemple #15
0
        // ------------------------ RequiresEncoding -------------------------
        // evaluate if the string requires encoding according to the QuotedPrintableTraits.
        public static bool RequiresEncoding(string InValue, QuotedPrintableTraits InTraits)
        {
            bool requiresEncoding = false;

            char[] newLineChars = Environment.NewLine.ToCharArray( );

            // encode always.
            if (InTraits.EncodeAlways == true)
            {
                requiresEncoding = true;
            }

            // length of string exceeds the "RequiresEncodingTriggerLength"
            else if ((InTraits.RequiresEncodingTriggerLength != -1) &&
                     (InValue.Length > InTraits.RequiresEncodingTriggerLength))
            {
                requiresEncoding = true;
            }

            // loop for each character in the string.  Test each to determine if the
            // string requires Quoted-Printable encoding.
            for (int Ix = 0; Ix < InValue.Length; ++Ix)
            {
                if (requiresEncoding == true)
                {
                    break;
                }
                char ch1 = InValue[Ix];

                // one of the "other" chars to encode.
                if ((InTraits.OtherEncodeChars != null) &&
                    (InTraits.OtherEncodeChars.IndexOf(ch1) != -1))
                {
                    requiresEncoding = true;
                }

                // space or tab.  encoding depends on if followed by crlf or not.
                else if ((ch1 == 9) || (ch1 == 32))
                {
                    char[] nxChars =
                        AcCommon.PullCharArray(InValue, Ix + 1, newLineChars.Length);
                    if ((InTraits.LinebreakTreatment == LinebreakTreatment.Break) &&
                        (AcCommon.CompareEqual(nxChars, newLineChars) == true))
                    {
                        requiresEncoding = true;
                    }
                }

                // LineBreak sequence handled as a line break.
                else if ((ch1 == newLineChars[0]) &&
                         (AcCommon.CompareEqual(newLineChars,
                                                AcCommon.PullCharArray(InValue, Ix, newLineChars.Length)) == true))
                {
                    if (InTraits.LinebreakTreatment == LinebreakTreatment.Encode)
                    {
                        requiresEncoding = true;
                    }
                }

                // a basic ascii char. literal representation.
                else if (((ch1 >= 33) && (ch1 <= 60)) ||
                         ((ch1 >= 62) && (ch1 <= 126)))
                {
                }

                // an equal sign.  by itself, does not trigger QP encoding.
                else if (ch1 == '=')
                {
                }

                // an encode required character.
                else
                {
                    requiresEncoding = true;
                }
            }

            return(requiresEncoding);
        }
Exemple #16
0
        // ------------------------- EncodeChars -----------------------------------
        // Quoted-Printable encode the chars of a string without regard for the line
        // length maximum.
        private static string EncodeChars(
            string InValue, QuotedPrintableTraits InTraits)
        {
            char[]        newLineChars = Environment.NewLine.ToCharArray( );
            StringBuilder sb           = new StringBuilder(InValue.Length * 2);

            // first pass.  encode one char at a time, without regard to 76 char line
            // limit.
            for (int Ix = 0; Ix < InValue.Length; ++Ix)
            {
                char ch1 = InValue[Ix];

                // one of the "other" chars to encode.
                if ((InTraits.OtherEncodeChars != null) &&
                    (InTraits.OtherEncodeChars.IndexOf(ch1) != -1))
                {
                    sb.Append(EncodeChar(InTraits, ch1));
                }

                // space or tab.  encoding depends on if followed by crlf or not.
                else if ((ch1 == 9) || (ch1 == 32))
                {
                    char[] nxChars =
                        AcCommon.PullCharArray(InValue, Ix + 1, newLineChars.Length);
                    if ((InTraits.LinebreakTreatment == LinebreakTreatment.Break) &&
                        (AcCommon.CompareEqual(nxChars, newLineChars) == true))
                    {
                        sb.Append(EncodeChar(InTraits, ch1));
                    }
                    else
                    {
                        sb.Append(ch1);
                    }
                }

                // Linebreak sequence handled as a line break.
                else if ((ch1 == newLineChars[0]) &&
                         (InTraits.LinebreakTreatment == LinebreakTreatment.Break) &&
                         (AcCommon.CompareEqual(
                              newLineChars,
                              AcCommon.PullCharArray(InValue, Ix, newLineChars.Length))
                          == true))
                {
                    sb.Append("\r\n");
                }

                // a basic ascii char. literal representation.
                else if (((ch1 >= 33) && (ch1 <= 60)) ||
                         ((ch1 >= 62) && (ch1 <= 126)))
                {
                    sb.Append(ch1);
                }

                else
                {
                    sb.Append(EncodeChar(InTraits, ch1));
                }
            }

            return(sb.ToString( ));
        }
Exemple #17
0
        // -------------------- ScanWord_IsolateWord ---------------------------
        private static void ScanWord_IsolateWord(
            string InString,
            int InBx,
            ref WordCursor InOutResults,
            TextTraits InTraits)
        {
            int    Bx, Fx, Ix, Lx;
            string word;

            Bx = InBx;
            char ch1 = InString[Bx];

            // is quoted. the word runs to the closing quote.
            if (IsOpenQuoteChar(ch1) == true)
            {
                Ix = ScanCloseQuote(InString, Bx, InTraits.QuoteEncapsulation);
                if (Ix == -1)
                {
                    throw(new ApplicationException("Closing quote not found starting at position " +
                                                   Bx + " in " + InString));
                }
                Lx   = Ix - Bx + 1;
                word = InString.Substring(Bx, Lx);
                InOutResults.SetWord(word, WordClassification.Quoted, Bx);
                return;
            }

            // look for a brace or delim character.
            char[]          combo   = AcCommon.Concat(InTraits.DelimChars, InTraits.BraceChars);
            ScanCharResults results = ScanEqual(InString, Bx, combo);

            Fx  = results.ResultPos;
            ch1 = results.ResultChar;

            // found a brace char
            if ((InTraits.IsOpenBraceChar(ch1) == true) &&
                (InTraits.IsDelimChar(ch1) == false))
            {
                Ix = ScanCloseBrace(InString, Fx);
                if (Ix == -1)
                {
                    throw(new ApplicationException("Closing brace not found starting at position " +
                                                   Fx + " in " + InString));
                }
                Lx   = Ix - Bx + 1;
                word = InString.Substring(Bx, Lx);
                if (Bx == Fx)
                {
                    InOutResults.SetWord(word, WordClassification.Braced, Bx);
                }
                else
                {
                    InOutResults.SetWord(word, WordClassification.NameBraced, Bx, ch1);
                }
            }

            // no delim found. all word to the end of the string.
            else if (Fx == -1)
            {
                word = InString.Substring(Bx);
                InOutResults.SetWord(word, WordClassification.Name, Bx);
            }

            // delim is same position as the word.  so there is no word, only a delim.
            else if (Fx == Bx)
            {
                InOutResults.SetNullWord( );
            }

            // we have a word that ends with a delim.
            else
            {
                Lx   = Fx - Bx;
                word = InString.Substring(Bx, Lx);
                InOutResults.SetWord(word, WordClassification.Name, Bx);
            }
        }
Exemple #18
0
        // ------------------------------- LoadLines -------------------------------
        /// <summary>
        /// load the lines of the part from an array of all the lines of the message.
        /// </summary>
        /// <param name="InLines"></param>
        /// <param name="InLineBx"></param>
        /// <param name="InLineCx"></param>
        /// <returns>reference to this object</returns>
        public MimeMessagePart LoadLines(string[] InLines, int InLineBx, int InLineCx)
        {
            int    lineBx = InLineBx;
            int    lineCx = InLineCx;
            string line   = null;

            // first thing.  if this is the top part, check for and trim off the initial
            // "+OK" response line from the server. ( server sends this line, followed immed
            // by the message data.  this is the first opportunity to strip it out. )
            if ((PartCode == MimePartCode.Top) &&
                (lineCx > 0) &&
                (InLines[0].Length >= 3) &&
                (InLines[0].Substring(0, 3) == "+OK"))
            {
                ++lineBx;
                --lineCx;
            }

            // calc the number of property lines, then calc the number of message lines.
            int propLineCx = CountPropertyLines(InLines, lineBx, lineCx);
            int msgLineCx  = lineCx - propLineCx;
            int msgLineBx  = lineBx + propLineCx;

            // store the part property lines.
            mPropertyLines = new string[propLineCx];
            for (int Ix = 0; Ix < propLineCx; ++Ix)
            {
                mPropertyLines[Ix] = InLines[Ix + lineBx];
            }

            // load the property dictionary from the property lines.
            LoadPropertyDictionary( );

            // reduce the message line bounds by one in case the first line is blank.
            if (msgLineCx > 0)
            {
                line = InLines[msgLineBx];
                if (line == "")
                {
                    msgLineCx -= 1;
                    msgLineBx += 1;
                }
            }

            // store the message lines of the part.
            mMessageLines = new string[msgLineCx];
            for (int Ix = 0; Ix < msgLineCx; ++Ix)
            {
                mMessageLines[Ix] = InLines[Ix + msgLineBx];
            }

            // the message lines are either quoted-printable or base64 encoded.
            // Decode the message lines.
            if (AcCommon.StringValue(Properties.ContentTransferEncoding)
                == "quoted-printable")
            {
                mRawMessageLines = mMessageLines;
                mMessageLines    = QuotedPrintable.DecodeLines(mRawMessageLines);
            }

            return(this);
        }
        /// <summary>
        /// Examine the string for its WordClassification content.
        /// </summary>
        /// <param name="InWord"></param>
        /// <param name="InTraits"></param>
        /// <returns></returns>
        public static CharObjectPair CalcWordClassification(
            string InWord, TextTraits InTraits)
        {
            int Fx = 0, Ix = 0;
            WordClassification wc = WordClassification.None;
            char braceChar        = ' ';
            char ch1              = AcCommon.PullChar(InWord, 0);
            int  Ex               = InWord.Length - 1;

            // is quoted. the word runs to the closing quote.
            if (Scanner.IsOpenQuoteChar(ch1) == true)
            {
                Ix = Scanner.ScanCloseQuote(InWord, 0, InTraits.QuoteEncapsulation);
                if (Ix == Ex)
                {
                    wc = WordClassification.Quoted;
                }
                else
                {
                    wc = WordClassification.MixedText;
                }
            }

            // check if the string is a Braced or NameBraced word.
            if (wc == WordClassification.None)
            {
                char[] combo = AcCommon.Concat(InTraits.DelimChars, InTraits.BraceChars);
                Scanner.ScanCharResults results = Scanner.ScanEqual(InWord, 0, combo);
                Fx  = results.ResultPos;
                ch1 = results.ResultChar;

                // found a brace char
                if ((InTraits.IsOpenBraceChar(ch1) == true) &&
                    (InTraits.IsDelimChar(ch1) == false))
                {
                    Ix = Scanner.ScanCloseBrace(InWord, Fx);
                    if (Ix == Ex)
                    {
                        braceChar = ch1;
                        if (Fx == 0)
                        {
                            wc = WordClassification.Braced;
                        }
                        else
                        {
                            wc = WordClassification.NameBraced;
                        }
                    }
                }
            }

            // word is all delimeter.
            if (wc == WordClassification.None)
            {
                Fx = Scanner.ScanNotEqual(InWord, 0, InTraits.DelimChars).a;
                if (Fx >= 0)
                {
                    wc = WordClassification.Delimeter;
                }
            }

            // check if a numeric string.
            if (wc == WordClassification.None)
            {
                char[] digitChars =
                    new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.', '+', '-' };
                Fx = Scanner.ScanNotEqual(InWord, 0, digitChars).a;
                if (Fx == -1)
                {
                    wc = WordClassification.Numeric;
                    try
                    {
                        double vx = double.Parse(InWord);
                    }
                    catch (Exception)
                    {
                        wc = WordClassification.None;
                    }
                }
            }

            // any delim chars in the string.  if not, the string is a name.  otherwise, it is
            // mixed.
            if (wc == WordClassification.None)
            {
                Fx = Scanner.ScanEqual(InWord, 0, InTraits.DelimChars).a;
                if (Fx == -1)
                {
                    wc = WordClassification.Name;
                }
                else
                {
                    wc = WordClassification.MixedText;
                }
            }

            return(new CharObjectPair(braceChar, wc));
        }
        /// <summary>
        /// Crack the component parts of the encoded-word string starting at InBx
        /// in the string. Return an object pair. pair.a is a bool set to false if the
        /// encoded-word string is not correctly formed. pair.b is a MimeEncodedWord object
        /// containing the component parts of the cracked word.
        /// </summary>
        /// <param name="InString"></param>
        /// <param name="InBx"></param>
        /// <returns></returns>
        public static BoolObjectPair CrackEncodedWord(string InString, int InBx)
        {
            int             Fx, Ix, Lx, RemLx;
            string          ws            = null;
            MimeEncodedWord ew            = new MimeEncodedWord( );
            bool            isEncodedWord = true;

            try
            {
                // isolate the next 80 chars from the string as a workspace ( encoded words are
                // limited to 75 chars or less )
                ew.Bx = InBx;
                ws    = AcCommon.PullString(InString, InBx, 80, null).ToLower( );
                Ix    = 0;

                // isolate the charset name
                Ix = 2;
                if (isEncodedWord == true)
                {
                    RemLx = ws.Length - Ix;
                    if (RemLx <= 3)
                    {
                        isEncodedWord = false;
                    }
                    else
                    {
                        Fx = ws.IndexOf("?q?", Ix);
                        if (Fx == -1)
                        {
                            isEncodedWord = false;
                        }
                        else
                        {
                            Lx         = Fx - Ix;
                            ew.CharSet = InString.Substring(InBx + Ix, Lx);
                            Ix         = Fx + 3;
                        }
                    }
                }

                // quoted-printable encoded text runs until "?="
                if (isEncodedWord == true)
                {
                    RemLx = ws.Length - Ix;
                    if (RemLx <= 2)
                    {
                        isEncodedWord = false;
                    }
                    else
                    {
                        Fx = ws.IndexOf("?=", Ix);
                        if (Fx == -1)
                        {
                            isEncodedWord = false;
                        }
                        else
                        {
                            Lx = Fx - Ix;
                            string qpEncoded = InString.Substring(InBx + Ix, Lx);
                            ew.DecodedValue = QuotedPrintable.DecodeString(qpEncoded);
                            ew.Lx           = Fx + 2;
                        }
                    }
                }
            }
            catch (Exception)
            {
                isEncodedWord = false;
            }
            return(new BoolObjectPair(isEncodedWord, ew));
        }
 /// <summary>
 /// standard set of delimeter characters.
 /// </summary>
 /// <returns></returns>
 public static char[] StandardDelimChars( )
 {
     char[] ch3 = new char[] { ' ', '\t', ',' };
     return(AcCommon.Concat(ch3, StandardBraceChars( )));
 }