MythTV  master
ParseBinary.cpp
Go to the documentation of this file.
1 /* ParseBinary.cpp
2 
3  Copyright (C) David C. J. Matthews 2004 dm at prolingua.co.uk
4 
5  This program is free software; you can redistribute it and/or
6  modify it under the terms of the GNU General Public License
7  as published by the Free Software Foundation; either version 2
8  of the License, or (at your option) any later version.
9 
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  GNU General Public License for more details.
14 
15  You should have received a copy of the GNU General Public License
16  along with this program; if not, write to the Free Software
17  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
18  Or, point your browser to http://www.gnu.org/copyleft/gpl.html
19 
20 */
21 
22 /*
23 Parser for ASN1 binary notation. Does minimal syntax checking, assuming that this will already have
24 been done before the binary was produced. Creates a MHParseNode tree structure.
25 */
26 
27 #include "Engine.h"
28 #include "ParseBinary.h"
29 #include "ASN1Codes.h"
30 #include "ParseNode.h"
31 #include "BaseClasses.h"
32 #include "Root.h"
33 #include "Groups.h"
34 #include "Logging.h"
35 
36 #define INDEFINITE_LENGTH (-1)
37 
38 // Get the next byte. In most all cases it's an error if we reach end-of-file
39 // and we throw an exception.
41 {
42  if (m_p >= m_data.size())
43  {
44  MHERROR("Unexpected end of file");
45  }
46 
47  return m_data[m_p++];
48 }
49 
50 
51 // Parse a string argument. ASN1 strings can include nulls as valid characters.
53 {
54  // TODO: Don't deal with indefinite length at the moment.
55  if (endStr == INDEFINITE_LENGTH)
56  {
57  MHERROR("Indefinite length strings are not implemented");
58  }
59 
60  int nLength = endStr - m_p;
61  unsigned char *stringValue = (unsigned char *)malloc(nLength + 1);
62  if (stringValue == nullptr)
63  {
64  MHERROR("Out of memory");
65  }
66 
67  unsigned char *p = stringValue;
68 
69  while (m_p < endStr)
70  {
71  *p++ = GetNextChar();
72  }
73 
74  str.Copy(MHOctetString((const char *)stringValue, nLength));
75  free(stringValue);
76 }
77 
78 // Parse an integer argument. Also used for bool and enum.
79 int MHParseBinary::ParseInt(int endInt)
80 {
81  int intVal = 0;
82  bool firstByte = true;
83 
84  if (endInt == INDEFINITE_LENGTH)
85  {
86  MHERROR("Indefinite length integers are not implemented");
87  }
88 
89  while (m_p < endInt)
90  {
91  unsigned char ch = GetNextChar();
92 
93  // Integer values are signed so if the top bit is set in the first byte
94  // we need to set the sign bit.
95  if (firstByte && ch >= 128)
96  {
97  intVal = -1;
98  }
99 
100  firstByte = false;
101  intVal = (intVal << 8) | ch;
102  }
103 
104  return intVal;
105 }
106 
107 
108 // Simple recursive parser for ASN1 BER.
110 {
111  unsigned char ch;
112  // Tag class
113  enum { Universal, Context/*, Pseudo*/ } tagClass = Universal;
114  // Byte count of end of this item. Set to INDEFINITE_LENGTH if the length is Indefinite.
115  int endOfItem;
116  unsigned int tagNumber = 0;
117 
118  // Read the first character.
119  ch = GetNextChar();
120 
121  // ASN1 Coding rules: Top two bits (0 and 1) indicate the tag class.
122  // 0x00 - Universal, 0x40 - Application, 0x80 - Context-specific, 0xC0 - Private
123  // We only use Universal and Context.
124  switch (ch & 0xC0)
125  {
126  case 0x00: // Universal
127  tagClass = Universal;
128  break;
129  case 0x80:
130  tagClass = Context;
131  break;
132  default:
133  MHERROR(QString("Invalid tag class = %1").arg(ch, 0, 16));
134  }
135 
136  // Bit 2 indicates whether it is a simple or compound type. Not used.
137  // Lower bits are the tag number.
138  tagNumber = ch & 0x1f;
139 
140  if (tagNumber == 0x1f) // Except that if it is 0x1F then the tag is encoded in the following bytes.
141  {
142  tagNumber = 0;
143 
144  do
145  {
146  ch = GetNextChar();
147  tagNumber = (tagNumber << 7) | (ch & 0x7f);
148  }
149  while ((ch & 0x80) != 0); // Top bit set means there's more to come.
150  }
151 
152  // Next byte is the length. If it is less than 128 it is the actual length, otherwise it
153  // gives the number of bytes containing the length, except that if this is zero the item
154  // has an "indefinite" length and is terminated by two zero bytes.
155  ch = GetNextChar();
156 
157  if (ch & 0x80)
158  {
159  int lengthOfLength = ch & 0x7f;
160 
161  if (lengthOfLength == 0)
162  {
163  endOfItem = INDEFINITE_LENGTH;
164  }
165  else
166  {
167  endOfItem = 0;
168 
169  while (lengthOfLength--)
170  {
171  ch = GetNextChar();
172  endOfItem = (endOfItem << 8) | ch;
173  }
174 
175  endOfItem += m_p;
176  }
177  }
178  else
179  {
180  endOfItem = ch + m_p;
181  }
182 
183  if (tagClass == Context)
184  {
185  MHPTagged *pNode = new MHPTagged(tagNumber);
186 
187  try
188  {
189  // The argument here depends on the particular tag we're processing.
190  switch (tagNumber)
191  {
193  case C_OBSCURED_INPUT:
195  case C_WRAP_AROUND:
196  case C_TEXT_WRAPPING:
197  case C_INITIALLY_ACTIVE:
198  case C_MOVING_CURSOR:
199  case C_SHARED:
200  case C_ENGINE_RESP:
201  case C_TILING:
203  {
204  // BOOL
205  // If there is no argument we need to indicate that so that it gets
206  // the correct default value.
207  if (m_p != endOfItem)
208  {
209  int intVal = ParseInt(endOfItem); // May raise an exception
210  pNode->AddArg(new MHPBool(intVal != 0));
211  }
212 
213  break;
214  }
215 
216  case C_INPUT_TYPE:
217  case C_SLIDER_STYLE:
218  case C_TERMINATION:
219  case C_ORIENTATION:
221  case C_BUTTON_STYLE:
222  case C_START_CORNER:
223  case C_LINE_ORIENTATION:
225  case C_STORAGE:
226  {
227  // ENUM
228  if (m_p != endOfItem)
229  {
230  int intVal = ParseInt(endOfItem); // May raise an exception
231  pNode->AddArg(new MHPEnum(intVal));
232  }
233 
234  break;
235  }
236 
237  case C_INITIAL_PORTION:
238  case C_STEP_SIZE:
240  case C_INITIAL_VALUE:
241  case C_IP_CONTENT_HOOK:
242  case C_MAX_VALUE:
243  case C_MIN_VALUE:
246  case C_TEXT_CONTENT_HOOK:
248  case C_MAX_LENGTH:
249  case C_CHARACTER_SET:
252  case C_LOOPING:
254  case C_STANDARD_VERSION:
256  case C_CONTENT_HOOK:
258  case C_COMPONENT_TAG:
259  case C_ORIGINAL_VOLUME:
261  case C_CONTENT_SIZE:
262  {
263  // INT
264  if (m_p != endOfItem)
265  {
266  int intVal = ParseInt(endOfItem); // May raise an exception
267  pNode->AddArg(new MHPInt(intVal));
268  }
269 
270  break;
271  }
272 
274  case C_CONTENT_REFERENCE:
275  case C_FONT_ATTRIBUTES:
276  case C_CHAR_LIST:
277  case C_NAME:
278  case C_ORIGINAL_LABEL:
279  {
280  // STRING
281  // Unlike INT, BOOL and ENUM we can't distinguish an empty string
282  // from a missing string.
283  MHOctetString str;
284  ParseString(endOfItem, str);
285  pNode->AddArg(new MHPString(str));
286  break;
287  }
288 
289  default:
290  {
291  // Everything else has either no argument or is self-describing
292  // TODO: Handle indefinite length.
293  if (endOfItem == INDEFINITE_LENGTH)
294  {
295  MHERROR("Indefinite length arguments are not implemented");
296  }
297 
298  while (m_p < endOfItem)
299  {
300  pNode->AddArg(DoParse());
301  }
302  }
303  }
304  }
305  catch (...)
306  {
307  // Memory clean-up
308  delete pNode;
309  throw;
310  }
311 
312  return pNode;
313  }
314 
315  // Universal - i.e. a primitive type.
316  // Tag values
317 
318  switch (tagNumber)
319  {
320  case U_BOOL: // Boolean
321  {
322  int intVal = ParseInt(endOfItem);
323  return new MHPBool(intVal != 0);
324  }
325  case U_INT: // Integer
326  {
327  int intVal = ParseInt(endOfItem);
328  return new MHPInt(intVal);
329  }
330  case U_ENUM: // ENUM
331  {
332  int intVal = ParseInt(endOfItem);
333  return new MHPEnum(intVal);
334  }
335  case U_STRING: // String
336  {
337  MHOctetString str;
338  ParseString(endOfItem, str);
339  return new MHPString(str);
340  }
341  case U_NULL: // ASN1 NULL
342  {
343  return new MHPNull;
344  }
345  case U_SEQUENCE: // Sequence
346  {
347  MHParseSequence *pNode = new MHParseSequence();
348 
349  if (endOfItem == INDEFINITE_LENGTH)
350  {
351  MHERROR("Indefinite length sequences are not implemented");
352  }
353 
354  try
355  {
356  while (m_p < endOfItem)
357  {
358  pNode->Append(DoParse());
359  }
360  }
361  catch (...)
362  {
363  // Memory clean-up if error.
364  delete pNode;
365  throw;
366  }
367 
368  return pNode;
369  }
370  default:
371  MHERROR(QString("Unknown universal %1").arg(tagNumber));
372  }
373 }
#define U_SEQUENCE
Definition: ASN1Codes.h:30
#define C_CONTENT_CACHE_PRIORITY
Definition: ASN1Codes.h:94
#define U_INT
Definition: ASN1Codes.h:26
#define C_STANDARD_VERSION
Definition: ASN1Codes.h:36
#define C_INITIAL_PORTION
Definition: ASN1Codes.h:139
#define MHERROR(__text)
Definition: Logging.h:42
#define C_OBSCURED_INPUT
Definition: ASN1Codes.h:144
#define C_COMPONENT_TAG
Definition: ASN1Codes.h:131
QHash< QString, Action * > Context
Definition: action.h:75
#define U_BOOL
Definition: ASN1Codes.h:25
#define C_INPUT_EVENT_REGISTER
Definition: ASN1Codes.h:84
#define C_MAX_VALUE
Definition: ASN1Codes.h:136
#define C_HORIZONTAL_JUSTIFICATION
Definition: ASN1Codes.h:120
#define C_INITIAL_VALUE
Definition: ASN1Codes.h:138
#define C_ORIGINAL_LINE_STYLE
Definition: ASN1Codes.h:116
#define C_INPUT_TYPE
Definition: ASN1Codes.h:142
#define C_ORIGINAL_VOLUME
Definition: ASN1Codes.h:132
#define C_TERMINATION
Definition: ASN1Codes.h:133
#define C_START_CORNER
Definition: ASN1Codes.h:123
#define C_BITMAP_CONTENT_HOOK
Definition: ASN1Codes.h:79
#define C_MIN_VALUE
Definition: ASN1Codes.h:137
#define C_STREAM_CONTENT_HOOK
Definition: ASN1Codes.h:78
unsigned char GetNextChar()
Definition: ParseBinary.cpp:40
#define C_INITIALLY_ACTIVE
Definition: ASN1Codes.h:89
#define C_NAME
Definition: ASN1Codes.h:97
#define C_ORIENTATION
Definition: ASN1Codes.h:135
#define C_LOOPING
Definition: ASN1Codes.h:127
#define C_TILING
Definition: ASN1Codes.h:112
void Copy(const MHOctetString &str)
#define U_STRING
Definition: ASN1Codes.h:27
QByteArray m_data
Definition: ParseBinary.h:50
#define C_CONTENT_REFERENCE
Definition: ASN1Codes.h:102
#define C_STORAGE
Definition: ASN1Codes.h:126
#define C_SHARED
Definition: ASN1Codes.h:92
#define C_MAX_LENGTH
Definition: ASN1Codes.h:145
#define C_CONTENT_HOOK
Definition: ASN1Codes.h:90
#define C_OBJECT_INFORMATION
Definition: ASN1Codes.h:37
#define C_ENGINE_RESP
Definition: ASN1Codes.h:134
MHParseNode * DoParse()
#define C_LINE_ART_CONTENT_HOOK
Definition: ASN1Codes.h:80
#define C_MULTIPLE_SELECTION
Definition: ASN1Codes.h:108
#define U_ENUM
Definition: ASN1Codes.h:29
#define C_WRAP_AROUND
Definition: ASN1Codes.h:107
#define C_TEXT_CONTENT_HOOK
Definition: ASN1Codes.h:73
#define C_PROGRAM_CONNECTION_TAG
Definition: ASN1Codes.h:99
#define C_VERTICAL_JUSTIFICATION
Definition: ASN1Codes.h:121
#define C_INITIALLY_AVAILABLE
Definition: ASN1Codes.h:98
#define C_ORIGINAL_TRANSPARENCY
Definition: ASN1Codes.h:113
#define C_CHAR_LIST
Definition: ASN1Codes.h:143
#define C_STEP_SIZE
Definition: ASN1Codes.h:140
#define C_BUTTON_STYLE
Definition: ASN1Codes.h:147
#define C_MOVING_CURSOR
Definition: ASN1Codes.h:87
#define INDEFINITE_LENGTH
Definition: ParseBinary.cpp:36
int ParseInt(int endInt)
Definition: ParseBinary.cpp:79
#define U_NULL
Definition: ASN1Codes.h:28
#define C_BORDERED_BOUNDING_BOX
Definition: ASN1Codes.h:114
#define C_ORIGINAL_LABEL
Definition: ASN1Codes.h:146
#define C_LINE_ORIENTATION
Definition: ASN1Codes.h:122
#define C_FONT_ATTRIBUTES
Definition: ASN1Codes.h:76
#define C_CONTENT_SIZE
Definition: ASN1Codes.h:93
void ParseString(int endStr, MHOctetString &str)
Definition: ParseBinary.cpp:52
#define C_TEXT_WRAPPING
Definition: ASN1Codes.h:124
void Append(BASE b)
Definition: BaseClasses.h:62
#define C_ORIGINAL_GC_PRIORITY
Definition: ASN1Codes.h:40
#define C_SLIDER_STYLE
Definition: ASN1Codes.h:141
#define C_ORIGINAL_LINE_WIDTH
Definition: ASN1Codes.h:115
void AddArg(MHParseNode *pArg)
Definition: ParseNode.cpp:28
#define C_CHARACTER_SET
Definition: ASN1Codes.h:71
#define C_IP_CONTENT_HOOK
Definition: ASN1Codes.h:77