MythTV  0.26-pre
ParseBinary.cpp
Go to the documentation of this file.
00001 /* ParseBinary.cpp
00002 
00003    Copyright (C)  David C. J. Matthews 2004  dm at prolingua.co.uk
00004 
00005    This program is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU General Public License
00007    as published by the Free Software Foundation; either version 2
00008    of the License, or (at your option) any later version.
00009 
00010    This program is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013    GNU General Public License for more details.
00014 
00015    You should have received a copy of the GNU General Public License
00016    along with this program; if not, write to the Free Software
00017    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00018    Or, point your browser to http://www.gnu.org/copyleft/gpl.html
00019 
00020 */
00021 
00022 /*
00023 Parser for ASN1 binary notation.  Does minimal syntax checking, assuming that this will already have
00024 been done before the binary was produced.  Creates a MHParseNode tree structure.
00025 */
00026 
00027 #include "Engine.h"
00028 #include "ParseBinary.h"
00029 #include "ASN1Codes.h"
00030 #include "ParseNode.h"
00031 #include "BaseClasses.h"
00032 #include "Root.h"
00033 #include "Groups.h"
00034 #include "Logging.h"
00035 
00036 MHParseBinary::MHParseBinary(QByteArray &program)
00037 {
00038     m_data = program;
00039     m_p = 0;
00040 }
00041 
00042 #define INDEFINITE_LENGTH   (-1)
00043 
00044 // Get the next byte.  In most all cases it's an error if we reach end-of-file
00045 // and we throw an exception.
00046 unsigned char MHParseBinary::GetNextChar()
00047 {
00048     if (m_p >= (int)m_data.size())
00049     {
00050         MHERROR("Unexpected end of file");
00051     }
00052 
00053     return m_data[m_p++];
00054 }
00055 
00056 
00057 // Parse a string argument.  ASN1 strings can include nulls as valid characters.
00058 void MHParseBinary::ParseString(int endStr, MHOctetString &str)
00059 {
00060     // TODO: Don't deal with indefinite length at the moment.
00061     if (endStr == INDEFINITE_LENGTH)
00062     {
00063         MHERROR("Indefinite length strings are not implemented");
00064     }
00065 
00066     int nLength = endStr - m_p;
00067     unsigned char *stringValue = (unsigned char *)malloc(endStr - m_p);
00068     unsigned char *p = stringValue;
00069 
00070     while (m_p < endStr)
00071     {
00072         *p++ = GetNextChar();
00073     }
00074 
00075     str.Copy(MHOctetString((const char *)stringValue, nLength));
00076     free(stringValue);
00077 }
00078 
00079 // Parse an integer argument.  Also used for bool and enum.
00080 int MHParseBinary::ParseInt(int endInt)
00081 {
00082     int intVal = 0;
00083     bool firstByte = true;
00084 
00085     if (endInt == INDEFINITE_LENGTH)
00086     {
00087         MHERROR("Indefinite length integers are not implemented");
00088     }
00089 
00090     while (m_p < endInt)
00091     {
00092         unsigned char ch = GetNextChar();
00093 
00094         // Integer values are signed so if the top bit is set in the first byte
00095         // we need to set the sign bit.
00096         if (firstByte && ch >= 128)
00097         {
00098             intVal = -1;
00099         }
00100 
00101         firstByte = false;
00102         intVal = (intVal << 8) | ch;
00103     }
00104 
00105     return intVal;
00106 }
00107 
00108 
00109 //  Simple recursive parser for ASN1 BER.
00110 MHParseNode *MHParseBinary::DoParse()
00111 {
00112     unsigned char ch;
00113     // Tag class
00114     enum { Universal, Context/*, Pseudo*/ } tagClass = Universal;
00115     // Byte count of end of this item.  Set to INDEFINITE_LENGTH if the length is Indefinite.
00116     int endOfItem;
00117     unsigned int tagNumber = 0;
00118 
00119     // Read the first character.
00120     ch = GetNextChar();
00121 
00122     // ASN1 Coding rules: Top two bits (0 and 1) indicate the tag class.
00123     // 0x00 - Universal,  0x40 - Application, 0x80 - Context-specific, 0xC0 - Private
00124     // We only use Universal and Context.
00125     switch (ch & 0xC0)
00126     {
00127         case 0x00: // Universal
00128             tagClass = Universal;
00129             break;
00130         case 0x80:
00131             tagClass = Context;
00132             break;
00133         default:
00134             MHERROR(QString("Invalid tag class = %1").arg(ch, 0, 16));
00135     }
00136 
00137     // Bit 2 indicates whether it is a simple or compound type.  Not used.
00138     // Lower bits are the tag number.
00139     tagNumber = ch & 0x1f;
00140 
00141     if (tagNumber == 0x1f)   // Except that if it is 0x1F then the tag is encoded in the following bytes.
00142     {
00143         tagNumber = 0;
00144 
00145         do
00146         {
00147             ch = GetNextChar();
00148             tagNumber = (tagNumber << 7) | (ch & 0x7f);
00149         }
00150         while (ch & 0x80);   // Top bit set means there's more to come.
00151     }
00152 
00153     // Next byte is the length.  If it is less than 128 it is the actual length, otherwise it
00154     // gives the number of bytes containing the length, except that if this is zero the item
00155     // has an "indefinite" length and is terminated by two zero bytes.
00156     ch = GetNextChar();
00157 
00158     if (ch & 0x80)
00159     {
00160         int lengthOfLength = ch & 0x7f;
00161 
00162         if (lengthOfLength == 0)
00163         {
00164             endOfItem = INDEFINITE_LENGTH;
00165         }
00166         else
00167         {
00168             endOfItem = 0;
00169 
00170             while (lengthOfLength--)
00171             {
00172                 ch = GetNextChar();
00173                 endOfItem = (endOfItem << 8) | ch;
00174             }
00175 
00176             endOfItem += m_p;
00177         }
00178     }
00179     else
00180     {
00181         endOfItem = ch + m_p;
00182     }
00183 
00184     if (tagClass == Context)
00185     {
00186         MHPTagged *pNode = new MHPTagged(tagNumber);
00187 
00188         try
00189         {
00190             // The argument here depends on the particular tag we're processing.
00191             switch (tagNumber)
00192             {
00193                 case C_MULTIPLE_SELECTION:
00194                 case C_OBSCURED_INPUT:
00195                 case C_INITIALLY_AVAILABLE:
00196                 case C_WRAP_AROUND:
00197                 case C_TEXT_WRAPPING:
00198                 case C_INITIALLY_ACTIVE:
00199                 case C_MOVING_CURSOR:
00200                 case C_SHARED:
00201                 case C_ENGINE_RESP:
00202                 case C_TILING:
00203                 case C_BORDERED_BOUNDING_BOX:
00204                 {
00205                     // BOOL
00206                     // If there is no argument we need to indicate that so that it gets
00207                     // the correct default value.
00208                     if (m_p != endOfItem)
00209                     {
00210                         int intVal = ParseInt(endOfItem); // May raise an exception
00211                         pNode->AddArg(new MHPBool(intVal != 0));
00212                     }
00213 
00214                     break;
00215                 }
00216 
00217                 case C_INPUT_TYPE:
00218                 case C_SLIDER_STYLE:
00219                 case C_TERMINATION:
00220                 case C_ORIENTATION:
00221                 case C_HORIZONTAL_JUSTIFICATION:
00222                 case C_BUTTON_STYLE:
00223                 case C_START_CORNER:
00224                 case C_LINE_ORIENTATION:
00225                 case C_VERTICAL_JUSTIFICATION:
00226                 case C_STORAGE:
00227                 {
00228                     // ENUM
00229                     if (m_p != endOfItem)
00230                     {
00231                         int intVal = ParseInt(endOfItem); // May raise an exception
00232                         pNode->AddArg(new MHPEnum(intVal));
00233                     }
00234                 }
00235 
00236                 case C_INITIAL_PORTION:
00237                 case C_STEP_SIZE:
00238                 case C_INPUT_EVENT_REGISTER:
00239                 case C_INITIAL_VALUE:
00240                 case C_IP_CONTENT_HOOK:
00241                 case C_MAX_VALUE:
00242                 case C_MIN_VALUE:
00243                 case C_LINE_ART_CONTENT_HOOK:
00244                 case C_BITMAP_CONTENT_HOOK:
00245                 case C_TEXT_CONTENT_HOOK:
00246                 case C_STREAM_CONTENT_HOOK:
00247                 case C_MAX_LENGTH:
00248                 case C_CHARACTER_SET:
00249                 case C_ORIGINAL_TRANSPARENCY:
00250                 case C_ORIGINAL_GC_PRIORITY:
00251                 case C_LOOPING:
00252                 case C_ORIGINAL_LINE_STYLE:
00253                 case C_STANDARD_VERSION:
00254                 case C_ORIGINAL_LINE_WIDTH:
00255                 case C_CONTENT_HOOK:
00256                 case C_CONTENT_CACHE_PRIORITY:
00257                 case C_COMPONENT_TAG:
00258                 case C_ORIGINAL_VOLUME:
00259                 case C_PROGRAM_CONNECTION_TAG:
00260                 case C_CONTENT_SIZE:
00261                 {
00262                     // INT
00263                     if (m_p != endOfItem)
00264                     {
00265                         int intVal = ParseInt(endOfItem); // May raise an exception
00266                         pNode->AddArg(new MHPInt(intVal));
00267                     }
00268                 }
00269 
00270                 case C_OBJECT_INFORMATION:
00271                 case C_CONTENT_REFERENCE:
00272                 case C_FONT_ATTRIBUTES:
00273                 case C_CHAR_LIST:
00274                 case C_NAME:
00275                 case C_ORIGINAL_LABEL:
00276                 {
00277                     // STRING
00278                     // Unlike INT, BOOL and ENUM we can't distinguish an empty string
00279                     // from a missing string.
00280                     MHOctetString str;
00281                     ParseString(endOfItem, str);
00282                     pNode->AddArg(new MHPString(str));
00283                 }
00284 
00285                 default:
00286                 {
00287                     // Everything else has either no argument or is self-describing
00288                     // TODO: Handle indefinite length.
00289                     if (endOfItem == INDEFINITE_LENGTH)
00290                     {
00291                         MHERROR("Indefinite length arguments are not implemented");
00292                     }
00293 
00294                     while (m_p < endOfItem)
00295                     {
00296                         pNode->AddArg(DoParse());
00297                     }
00298                 }
00299             }
00300         }
00301         catch (...)
00302         {
00303             // Memory clean-up
00304             delete pNode;
00305             throw;
00306         }
00307 
00308         return pNode;
00309     }
00310     else   // Universal - i.e. a primitive type.
00311     {
00312         // Tag values
00313 
00314         switch (tagNumber)
00315         {
00316             case U_BOOL: // Boolean
00317             {
00318                 int intVal = ParseInt(endOfItem);
00319                 return new MHPBool(intVal != 0);
00320             }
00321             case U_INT: // Integer
00322             {
00323                 int intVal = ParseInt(endOfItem);
00324                 return new MHPInt(intVal);
00325             }
00326             case U_ENUM: // ENUM
00327             {
00328                 int intVal = ParseInt(endOfItem);
00329                 return new MHPEnum(intVal);
00330             }
00331             case U_STRING: // String
00332             {
00333                 MHOctetString str;
00334                 ParseString(endOfItem, str);
00335                 return new MHPString(str);
00336             }
00337             case U_NULL: // ASN1 NULL
00338             {
00339                 return new MHPNull;
00340             }
00341             case U_SEQUENCE: // Sequence
00342             {
00343                 MHParseSequence *pNode = new MHParseSequence();
00344 
00345                 if (endOfItem == INDEFINITE_LENGTH)
00346                 {
00347                     MHERROR("Indefinite length sequences are not implemented");
00348                 }
00349 
00350                 try
00351                 {
00352                     while (m_p < endOfItem)
00353                     {
00354                         pNode->Append(DoParse());
00355                     }
00356                 }
00357                 catch (...)
00358                 {
00359                     // Memory clean-up if error.
00360                     delete pNode;
00361                     throw;
00362                 }
00363 
00364                 return pNode;
00365             }
00366             default:
00367                 MHERROR(QString("Unknown universal %1").arg(tagNumber));
00368         }
00369     }
00370 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends