aboutsummaryrefslogtreecommitdiff
path: root/src/engine/condition.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/engine/condition.cpp')
-rw-r--r--src/engine/condition.cpp1030
1 files changed, 1030 insertions, 0 deletions
diff --git a/src/engine/condition.cpp b/src/engine/condition.cpp
new file mode 100644
index 00000000..28391d2d
--- /dev/null
+++ b/src/engine/condition.cpp
@@ -0,0 +1,1030 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5
6//
7// parse rules
8//
9// value variable | literal | integer | version
10// comparison-operator < | > | <= | >= | = | <> | >< | << | >>
11// term value | value comparison-operator value | ( expression )
12// boolean-factor term | NOT term
13// boolean-term boolean-factor | boolean-factor AND boolean-term
14// expression boolean-term | boolean-term OR expression
15//
16
17
18// constants
19
20#define COMPARISON 0x00010000
21#define INSENSITIVE 0x00020000
22
23enum BURN_SYMBOL_TYPE
24{
25 // terminals
26 BURN_SYMBOL_TYPE_NONE = 0,
27 BURN_SYMBOL_TYPE_END = 1,
28 BURN_SYMBOL_TYPE_OR = 2, // OR
29 BURN_SYMBOL_TYPE_AND = 3, // AND
30 BURN_SYMBOL_TYPE_NOT = 4, // NOT
31 BURN_SYMBOL_TYPE_LT = 5 | COMPARISON, // <
32 BURN_SYMBOL_TYPE_GT = 6 | COMPARISON, // >
33 BURN_SYMBOL_TYPE_LE = 7 | COMPARISON, // <=
34 BURN_SYMBOL_TYPE_GE = 8 | COMPARISON, // >=
35 BURN_SYMBOL_TYPE_EQ = 9 | COMPARISON, // =
36 BURN_SYMBOL_TYPE_NE = 10 | COMPARISON, // <>
37 BURN_SYMBOL_TYPE_BAND = 11 | COMPARISON, // ><
38 BURN_SYMBOL_TYPE_HIEQ = 12 | COMPARISON, // <<
39 BURN_SYMBOL_TYPE_LOEQ = 13 | COMPARISON, // >>
40 BURN_SYMBOL_TYPE_LT_I = 5 | COMPARISON | INSENSITIVE, // ~<
41 BURN_SYMBOL_TYPE_GT_I = 6 | COMPARISON | INSENSITIVE, // ~>
42 BURN_SYMBOL_TYPE_LE_I = 7 | COMPARISON | INSENSITIVE, // ~<=
43 BURN_SYMBOL_TYPE_GE_I = 8 | COMPARISON | INSENSITIVE, // ~>=
44 BURN_SYMBOL_TYPE_EQ_I = 9 | COMPARISON | INSENSITIVE, // ~=
45 BURN_SYMBOL_TYPE_NE_I = 10 | COMPARISON | INSENSITIVE, // ~<>
46 BURN_SYMBOL_TYPE_BAND_I = 11 | COMPARISON | INSENSITIVE, // ~><
47 BURN_SYMBOL_TYPE_HIEQ_I = 12 | COMPARISON | INSENSITIVE, // ~<<
48 BURN_SYMBOL_TYPE_LOEQ_I = 13 | COMPARISON | INSENSITIVE, // ~>>
49 BURN_SYMBOL_TYPE_LPAREN = 14, // (
50 BURN_SYMBOL_TYPE_RPAREN = 15, // )
51 BURN_SYMBOL_TYPE_NUMBER = 16,
52 BURN_SYMBOL_TYPE_IDENTIFIER = 17,
53 BURN_SYMBOL_TYPE_LITERAL = 18,
54 BURN_SYMBOL_TYPE_VERSION = 19,
55};
56
57
58// structs
59
60struct BURN_SYMBOL
61{
62 BURN_SYMBOL_TYPE Type;
63 DWORD iPosition;
64 BURN_VARIANT Value;
65};
66
67struct BURN_CONDITION_PARSE_CONTEXT
68{
69 BURN_VARIABLES* pVariables;
70 LPCWSTR wzCondition;
71 LPCWSTR wzRead;
72 BURN_SYMBOL NextSymbol;
73 BOOL fError;
74};
75
76
77// internal function declarations
78
79static HRESULT ParseExpression(
80 __in BURN_CONDITION_PARSE_CONTEXT* pContext,
81 __out BOOL* pf
82 );
83static HRESULT ParseBooleanTerm(
84 __in BURN_CONDITION_PARSE_CONTEXT* pContext,
85 __out BOOL* pf
86 );
87static HRESULT ParseBooleanFactor(
88 __in BURN_CONDITION_PARSE_CONTEXT* pContext,
89 __out BOOL* pf
90 );
91static HRESULT ParseTerm(
92 __in BURN_CONDITION_PARSE_CONTEXT* pContext,
93 __out BOOL* pf
94 );
95static HRESULT ParseValue(
96 __in BURN_CONDITION_PARSE_CONTEXT* pContext,
97 __out BURN_VARIANT* pValue
98 );
99static HRESULT Expect(
100 __in BURN_CONDITION_PARSE_CONTEXT* pContext,
101 __in BURN_SYMBOL_TYPE symbolType
102 );
103static HRESULT NextSymbol(
104 __in BURN_CONDITION_PARSE_CONTEXT* pContext
105 );
106static HRESULT CompareValues(
107 __in BURN_SYMBOL_TYPE comparison,
108 __in BURN_VARIANT leftOperand,
109 __in BURN_VARIANT rightOperand,
110 __out BOOL* pfResult
111 );
112static HRESULT CompareStringValues(
113 __in BURN_SYMBOL_TYPE comparison,
114 __in_z LPCWSTR wzLeftOperand,
115 __in_z LPCWSTR wzRightOperand,
116 __out BOOL* pfResult
117 );
118static HRESULT CompareIntegerValues(
119 __in BURN_SYMBOL_TYPE comparison,
120 __in LONGLONG llLeftOperand,
121 __in LONGLONG llRightOperand,
122 __out BOOL* pfResult
123 );
124static HRESULT CompareVersionValues(
125 __in BURN_SYMBOL_TYPE comparison,
126 __in DWORD64 qwLeftOperand,
127 __in DWORD64 qwRightOperand,
128 __out BOOL* pfResult
129 );
130
131
132// function definitions
133
134extern "C" HRESULT ConditionEvaluate(
135 __in BURN_VARIABLES* pVariables,
136 __in_z LPCWSTR wzCondition,
137 __out BOOL* pf
138 )
139{
140 HRESULT hr = S_OK;
141 BURN_CONDITION_PARSE_CONTEXT context = { };
142 BOOL f = FALSE;
143
144 context.pVariables = pVariables;
145 context.wzCondition = wzCondition;
146 context.wzRead = wzCondition;
147
148 hr = NextSymbol(&context);
149 ExitOnFailure(hr, "Failed to read next symbol.");
150
151 hr = ParseExpression(&context, &f);
152 ExitOnFailure(hr, "Failed to parse expression.");
153
154 hr = Expect(&context, BURN_SYMBOL_TYPE_END);
155 ExitOnFailure(hr, "Failed to expect end symbol.");
156
157 LogId(REPORT_VERBOSE, MSG_CONDITION_RESULT, wzCondition, LoggingTrueFalseToString(f));
158
159 *pf = f;
160 hr = S_OK;
161
162LExit:
163 if (context.fError)
164 {
165 Assert(FAILED(hr));
166 LogErrorId(hr, MSG_FAILED_PARSE_CONDITION, wzCondition, NULL, NULL);
167 }
168
169 return hr;
170}
171
172extern "C" HRESULT ConditionGlobalCheck(
173 __in BURN_VARIABLES* pVariables,
174 __in BURN_CONDITION* pCondition,
175 __in BOOTSTRAPPER_DISPLAY display,
176 __in_z LPCWSTR wzBundleName,
177 __out DWORD *pdwExitCode,
178 __out BOOL *pfContinueExecution
179 )
180{
181 HRESULT hr = S_OK;
182 BOOL fSuccess = TRUE;
183 HRESULT hrError = HRESULT_FROM_WIN32(ERROR_OLD_WIN_VERSION);
184 OS_VERSION osv = OS_VERSION_UNKNOWN;
185 DWORD dwServicePack = 0;
186
187 OsGetVersion(&osv, &dwServicePack);
188
189 // Always error on Windows 2000 or lower
190 if (OS_VERSION_WIN2000 >= osv)
191 {
192 fSuccess = FALSE;
193 }
194 else
195 {
196 if (NULL != pCondition->sczConditionString)
197 {
198 hr = ConditionEvaluate(pVariables, pCondition->sczConditionString, &fSuccess);
199 ExitOnFailure(hr, "Failed to evaluate condition: %ls", pCondition->sczConditionString);
200 }
201 }
202
203 if (!fSuccess)
204 {
205 // Display the error messagebox, as long as we're in an appropriate display mode
206 hr = SplashScreenDisplayError(display, wzBundleName, hrError);
207 ExitOnFailure(hr, "Failed to display error dialog");
208
209 *pdwExitCode = static_cast<DWORD>(hrError);
210 *pfContinueExecution = FALSE;
211 }
212
213LExit:
214 return hr;
215}
216
217HRESULT ConditionGlobalParseFromXml(
218 __in BURN_CONDITION* pCondition,
219 __in IXMLDOMNode* pixnBundle
220 )
221{
222 HRESULT hr = S_OK;
223 IXMLDOMNode* pixnNode = NULL;
224 BSTR bstrExpression = NULL;
225
226 // select variable nodes
227 hr = XmlSelectSingleNode(pixnBundle, L"Condition", &pixnNode);
228 if (S_FALSE == hr)
229 {
230 ExitFunction1(hr = S_OK);
231 }
232 ExitOnFailure(hr, "Failed to select condition node.");
233
234 // @Condition
235 hr = XmlGetText(pixnNode, &bstrExpression);
236 ExitOnFailure(hr, "Failed to get Condition inner text.");
237
238 hr = StrAllocString(&pCondition->sczConditionString, bstrExpression, 0);
239 ExitOnFailure(hr, "Failed to copy condition string from BSTR");
240
241LExit:
242 ReleaseBSTR(bstrExpression);
243 ReleaseObject(pixnNode);
244
245 return hr;
246}
247
248
249// internal function definitions
250
251static HRESULT ParseExpression(
252 __in BURN_CONDITION_PARSE_CONTEXT* pContext,
253 __out BOOL* pf
254 )
255{
256 HRESULT hr = S_OK;
257 BOOL fFirst = FALSE;
258 BOOL fSecond = FALSE;
259
260 hr = ParseBooleanTerm(pContext, &fFirst);
261 ExitOnFailure(hr, "Failed to parse boolean-term.");
262
263 if (BURN_SYMBOL_TYPE_OR == pContext->NextSymbol.Type)
264 {
265 hr = NextSymbol(pContext);
266 ExitOnFailure(hr, "Failed to read next symbol.");
267
268 hr = ParseExpression(pContext, &fSecond);
269 ExitOnFailure(hr, "Failed to parse expression.");
270
271 *pf = fFirst || fSecond;
272 }
273 else
274 {
275 *pf = fFirst;
276 }
277
278LExit:
279 return hr;
280}
281
282static HRESULT ParseBooleanTerm(
283 __in BURN_CONDITION_PARSE_CONTEXT* pContext,
284 __out BOOL* pf
285 )
286{
287 HRESULT hr = S_OK;
288 BOOL fFirst = FALSE;
289 BOOL fSecond = FALSE;
290
291 hr = ParseBooleanFactor(pContext, &fFirst);
292 ExitOnFailure(hr, "Failed to parse boolean-factor.");
293
294 if (BURN_SYMBOL_TYPE_AND == pContext->NextSymbol.Type)
295 {
296 hr = NextSymbol(pContext);
297 ExitOnFailure(hr, "Failed to read next symbol.");
298
299 hr = ParseBooleanTerm(pContext, &fSecond);
300 ExitOnFailure(hr, "Failed to parse boolean-term.");
301
302 *pf = fFirst && fSecond;
303 }
304 else
305 {
306 *pf = fFirst;
307 }
308
309LExit:
310 return hr;
311}
312
313static HRESULT ParseBooleanFactor(
314 __in BURN_CONDITION_PARSE_CONTEXT* pContext,
315 __out BOOL* pf
316 )
317{
318 HRESULT hr = S_OK;
319 BOOL fNot = FALSE;
320 BOOL f = FALSE;
321
322 if (BURN_SYMBOL_TYPE_NOT == pContext->NextSymbol.Type)
323 {
324 hr = NextSymbol(pContext);
325 ExitOnFailure(hr, "Failed to read next symbol.");
326
327 fNot = TRUE;
328 }
329
330 hr = ParseTerm(pContext, &f);
331 ExitOnFailure(hr, "Failed to parse term.");
332
333 *pf = fNot ? !f : f;
334
335LExit:
336 return hr;
337}
338
339static HRESULT ParseTerm(
340 __in BURN_CONDITION_PARSE_CONTEXT* pContext,
341 __out BOOL* pf
342 )
343{
344 HRESULT hr = S_OK;
345 BURN_VARIANT firstValue = { };
346 BURN_VARIANT secondValue = { };
347
348 if (BURN_SYMBOL_TYPE_LPAREN == pContext->NextSymbol.Type)
349 {
350 hr = NextSymbol(pContext);
351 ExitOnFailure(hr, "Failed to read next symbol.");
352
353 hr = ParseExpression(pContext, pf);
354 ExitOnFailure(hr, "Failed to parse expression.");
355
356 hr = Expect(pContext, BURN_SYMBOL_TYPE_RPAREN);
357 ExitOnFailure(hr, "Failed to expect right parenthesis.");
358
359 ExitFunction1(hr = S_OK);
360 }
361
362 hr = ParseValue(pContext, &firstValue);
363 ExitOnFailure(hr, "Failed to parse value.");
364
365 if (COMPARISON & pContext->NextSymbol.Type)
366 {
367 BURN_SYMBOL_TYPE comparison = pContext->NextSymbol.Type;
368
369 hr = NextSymbol(pContext);
370 ExitOnFailure(hr, "Failed to read next symbol.");
371
372 hr = ParseValue(pContext, &secondValue);
373 ExitOnFailure(hr, "Failed to parse value.");
374
375 hr = CompareValues(comparison, firstValue, secondValue, pf);
376 ExitOnFailure(hr, "Failed to compare value.");
377 }
378 else
379 {
380 LONGLONG llValue = 0;
381 LPWSTR sczValue = NULL;
382 DWORD64 qwValue = 0;
383 switch (firstValue.Type)
384 {
385 case BURN_VARIANT_TYPE_NONE:
386 *pf = FALSE;
387 break;
388 case BURN_VARIANT_TYPE_STRING:
389 hr = BVariantGetString(&firstValue, &sczValue);
390 if (SUCCEEDED(hr))
391 {
392 *pf = sczValue && *sczValue;
393 }
394 StrSecureZeroFreeString(sczValue);
395 break;
396 case BURN_VARIANT_TYPE_NUMERIC:
397 hr = BVariantGetNumeric(&firstValue, &llValue);
398 if (SUCCEEDED(hr))
399 {
400 *pf = 0 != llValue;
401 }
402 SecureZeroMemory(&llValue, sizeof(llValue));
403 break;
404 case BURN_VARIANT_TYPE_VERSION:
405 hr = BVariantGetVersion(&firstValue, &qwValue);
406 if (SUCCEEDED(hr))
407 {
408 *pf = 0 != qwValue;
409 }
410 SecureZeroMemory(&llValue, sizeof(qwValue));
411 break;
412 default:
413 ExitFunction1(hr = E_UNEXPECTED);
414 }
415 }
416
417LExit:
418 BVariantUninitialize(&firstValue);
419 BVariantUninitialize(&secondValue);
420 return hr;
421}
422
423static HRESULT ParseValue(
424 __in BURN_CONDITION_PARSE_CONTEXT* pContext,
425 __out BURN_VARIANT* pValue
426 )
427{
428 HRESULT hr = S_OK;
429
430 // Symbols don't encrypt their value, so can access the value directly.
431 switch (pContext->NextSymbol.Type)
432 {
433 case BURN_SYMBOL_TYPE_IDENTIFIER:
434 Assert(BURN_VARIANT_TYPE_STRING == pContext->NextSymbol.Value.Type);
435
436 // find variable
437 hr = VariableGetVariant(pContext->pVariables, pContext->NextSymbol.Value.sczValue, pValue);
438 if (E_NOTFOUND != hr)
439 {
440 ExitOnRootFailure(hr, "Failed to find variable.");
441 }
442 break;
443
444 case BURN_SYMBOL_TYPE_NUMBER: __fallthrough;
445 case BURN_SYMBOL_TYPE_LITERAL: __fallthrough;
446 case BURN_SYMBOL_TYPE_VERSION:
447 // steal value of symbol
448 memcpy_s(pValue, sizeof(BURN_VARIANT), &pContext->NextSymbol.Value, sizeof(BURN_VARIANT));
449 memset(&pContext->NextSymbol.Value, 0, sizeof(BURN_VARIANT));
450 break;
451
452 default:
453 pContext->fError = TRUE;
454 hr = E_INVALIDDATA;
455 ExitOnRootFailure(hr, "Failed to parse condition '%ls' at position: %u", pContext->wzCondition, pContext->NextSymbol.iPosition);
456 }
457
458 // get next symbol
459 hr = NextSymbol(pContext);
460 ExitOnFailure(hr, "Failed to read next symbol.");
461
462LExit:
463 return hr;
464}
465
466//
467// Expect - expects a symbol.
468//
469static HRESULT Expect(
470 __in BURN_CONDITION_PARSE_CONTEXT* pContext,
471 __in BURN_SYMBOL_TYPE symbolType
472 )
473{
474 HRESULT hr = S_OK;
475
476 if (pContext->NextSymbol.Type != symbolType)
477 {
478 pContext->fError = TRUE;
479 hr = E_INVALIDDATA;
480 ExitOnRootFailure(hr, "Failed to parse condition '%ls' at position: %u", pContext->wzCondition, pContext->NextSymbol.iPosition);
481 }
482
483 hr = NextSymbol(pContext);
484 ExitOnFailure(hr, "Failed to read next symbol.");
485
486LExit:
487 return hr;
488}
489
490//
491// NextSymbol - finds the next symbol in an expression string.
492//
493static HRESULT NextSymbol(
494 __in BURN_CONDITION_PARSE_CONTEXT* pContext
495 )
496{
497 HRESULT hr = S_OK;
498 WORD charType = 0;
499 DWORD iPosition = 0;
500 DWORD n = 0;
501
502 // free existing symbol
503 BVariantUninitialize(&pContext->NextSymbol.Value);
504 memset(&pContext->NextSymbol, 0, sizeof(BURN_SYMBOL));
505
506 // skip past blanks
507 while (L'\0' != pContext->wzRead[0])
508 {
509 ::GetStringTypeW(CT_CTYPE1, pContext->wzRead, 1, &charType);
510 if (0 == (C1_BLANK & charType))
511 {
512 break; // no blank, done
513 }
514 ++pContext->wzRead;
515 }
516 iPosition = (DWORD)(pContext->wzRead - pContext->wzCondition);
517
518 // read depending on first character type
519 switch (pContext->wzRead[0])
520 {
521 case L'\0':
522 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_END;
523 break;
524 case L'~':
525 switch (pContext->wzRead[1])
526 {
527 case L'=':
528 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_EQ_I;
529 n = 2;
530 break;
531 case L'>':
532 switch (pContext->wzRead[2])
533 {
534 case '=':
535 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_GE_I;
536 n = 3;
537 break;
538 case L'>':
539 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_LOEQ_I;
540 n = 3;
541 break;
542 case L'<':
543 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_BAND_I;
544 n = 3;
545 break;
546 default:
547 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_GT_I;
548 n = 2;
549 }
550 break;
551 case L'<':
552 switch (pContext->wzRead[2])
553 {
554 case '=':
555 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_LE_I;
556 n = 3;
557 break;
558 case L'<':
559 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_HIEQ_I;
560 n = 3;
561 break;
562 case '>':
563 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_NE_I;
564 n = 3;
565 break;
566 default:
567 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_LT_I;
568 n = 2;
569 }
570 break;
571 default:
572 // error
573 pContext->fError = TRUE;
574 hr = E_INVALIDDATA;
575 ExitOnRootFailure(hr, "Failed to parse condition \"%ls\". Unexpected '~' operator at position %d.", pContext->wzCondition, iPosition);
576 }
577 break;
578 case L'>':
579 switch (pContext->wzRead[1])
580 {
581 case L'=':
582 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_GE;
583 n = 2;
584 break;
585 case L'>':
586 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_LOEQ;
587 n = 2;
588 break;
589 case L'<':
590 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_BAND;
591 n = 2;
592 break;
593 default:
594 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_GT;
595 n = 1;
596 }
597 break;
598 case L'<':
599 switch (pContext->wzRead[1])
600 {
601 case L'=':
602 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_LE;
603 n = 2;
604 break;
605 case L'<':
606 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_HIEQ;
607 n = 2;
608 break;
609 case L'>':
610 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_NE;
611 n = 2;
612 break;
613 default:
614 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_LT;
615 n = 1;
616 }
617 break;
618 case L'=':
619 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_EQ;
620 n = 1;
621 break;
622 case L'(':
623 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_LPAREN;
624 n = 1;
625 break;
626 case L')':
627 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_RPAREN;
628 n = 1;
629 break;
630 case L'"': // literal
631 do
632 {
633 ++n;
634 if (L'\0' == pContext->wzRead[n])
635 {
636 // error
637 pContext->fError = TRUE;
638 hr = E_INVALIDDATA;
639 ExitOnRootFailure(hr, "Failed to parse condition \"%ls\". Unterminated literal at position %d.", pContext->wzCondition, iPosition);
640 }
641 } while (L'"' != pContext->wzRead[n]);
642 ++n; // terminating '"'
643
644 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_LITERAL;
645 hr = BVariantSetString(&pContext->NextSymbol.Value, &pContext->wzRead[1], n - 2);
646 ExitOnFailure(hr, "Failed to set symbol value.");
647 break;
648 default:
649 if (C1_DIGIT & charType || L'-' == pContext->wzRead[0])
650 {
651 do
652 {
653 ++n;
654 ::GetStringTypeW(CT_CTYPE1, &pContext->wzRead[n], 1, &charType);
655 if (C1_ALPHA & charType || L'_' == pContext->wzRead[n])
656 {
657 // error, identifier cannot start with a digit
658 pContext->fError = TRUE;
659 hr = E_INVALIDDATA;
660 ExitOnRootFailure(hr, "Failed to parse condition \"%ls\". Identifier cannot start at a digit, at position %d.", pContext->wzCondition, iPosition);
661 }
662 } while (C1_DIGIT & charType);
663
664 // number
665 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_NUMBER;
666
667 LONGLONG ll = 0;
668 hr = StrStringToInt64(pContext->wzRead, n, &ll);
669 if (FAILED(hr))
670 {
671 pContext->fError = TRUE;
672 hr = E_INVALIDDATA;
673 ExitOnRootFailure(hr, "Failed to parse condition \"%ls\". Constant too big, at position %d.", pContext->wzCondition, iPosition);
674 }
675
676 hr = BVariantSetNumeric(&pContext->NextSymbol.Value, ll);
677 ExitOnFailure(hr, "Failed to set symbol value.");
678 }
679 else if (C1_ALPHA & charType || L'_' == pContext->wzRead[0])
680 {
681 ::GetStringTypeW(CT_CTYPE1, &pContext->wzRead[1], 1, &charType);
682 if (L'v' == pContext->wzRead[0] && C1_DIGIT & charType)
683 {
684 // version
685 DWORD cParts = 1;
686 for (;;)
687 {
688 ++n;
689 if (L'.' == pContext->wzRead[n])
690 {
691 ++cParts;
692 if (4 < cParts)
693 {
694 // error, too many parts in version
695 pContext->fError = TRUE;
696 hr = E_INVALIDDATA;
697 ExitOnRootFailure(hr, "Failed to parse condition \"%ls\". Version can have a maximum of 4 parts, at position %d.", pContext->wzCondition, iPosition);
698 }
699 }
700 else
701 {
702 ::GetStringTypeW(CT_CTYPE1, &pContext->wzRead[n], 1, &charType);
703 if (C1_DIGIT != (C1_DIGIT & charType))
704 {
705 break;
706 }
707 }
708 }
709
710 // Symbols don't encrypt their value, so can access the value directly.
711 hr = FileVersionFromStringEx(&pContext->wzRead[1], n - 1, &pContext->NextSymbol.Value.qwValue);
712 if (FAILED(hr))
713 {
714 pContext->fError = TRUE;
715 hr = E_INVALIDDATA;
716 ExitOnRootFailure(hr, "Failed to parse condition \"%ls\". Invalid version format, at position %d.", pContext->wzCondition, iPosition);
717 }
718
719 pContext->NextSymbol.Value.Type = BURN_VARIANT_TYPE_VERSION;
720 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_VERSION;
721 }
722 else
723 {
724 do
725 {
726 ++n;
727 ::GetStringTypeW(CT_CTYPE1, &pContext->wzRead[n], 1, &charType);
728 } while (C1_ALPHA & charType || C1_DIGIT & charType || L'_' == pContext->wzRead[n]);
729
730 if (2 == n && CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, pContext->wzRead, 2, L"OR", 2))
731 {
732 // OR
733 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_OR;
734 }
735 else if (3 == n && CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, pContext->wzRead, 3, L"AND", 3))
736 {
737 // AND
738 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_AND;
739 }
740 else if (3 == n && CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, pContext->wzRead, 3, L"NOT", 3))
741 {
742 // NOT
743 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_NOT;
744 }
745 else
746 {
747 // identifier
748 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_IDENTIFIER;
749 hr = BVariantSetString(&pContext->NextSymbol.Value, pContext->wzRead, n);
750 ExitOnFailure(hr, "Failed to set symbol value.");
751 }
752 }
753 }
754 else
755 {
756 // error, unexpected character
757 pContext->fError = TRUE;
758 hr = E_INVALIDDATA;
759 ExitOnRootFailure(hr, "Failed to parse condition \"%ls\". Unexpected character at position %d.", pContext->wzCondition, iPosition);
760 }
761 }
762 pContext->NextSymbol.iPosition = iPosition;
763 pContext->wzRead += n;
764
765LExit:
766 return hr;
767}
768
769//
770// CompareValues - compares two variant values using a given comparison.
771//
772static HRESULT CompareValues(
773 __in BURN_SYMBOL_TYPE comparison,
774 __in BURN_VARIANT leftOperand,
775 __in BURN_VARIANT rightOperand,
776 __out BOOL* pfResult
777 )
778{
779 HRESULT hr = S_OK;
780 LONGLONG llLeft = 0;
781 DWORD64 qwLeft = 0;
782 LPWSTR sczLeft = NULL;
783 LONGLONG llRight = 0;
784 DWORD64 qwRight = 0;
785 LPWSTR sczRight = NULL;
786
787 // get values to compare based on type
788 if (BURN_VARIANT_TYPE_STRING == leftOperand.Type && BURN_VARIANT_TYPE_STRING == rightOperand.Type)
789 {
790 hr = BVariantGetString(&leftOperand, &sczLeft);
791 ExitOnFailure(hr, "Failed to get the left string");
792 hr = BVariantGetString(&rightOperand, &sczRight);
793 ExitOnFailure(hr, "Failed to get the right string");
794 hr = CompareStringValues(comparison, sczLeft, sczRight, pfResult);
795 }
796 else if (BURN_VARIANT_TYPE_NUMERIC == leftOperand.Type && BURN_VARIANT_TYPE_NUMERIC == rightOperand.Type)
797 {
798 hr = BVariantGetNumeric(&leftOperand, &llLeft);
799 ExitOnFailure(hr, "Failed to get the left numeric");
800 hr = BVariantGetNumeric(&rightOperand, &llRight);
801 ExitOnFailure(hr, "Failed to get the right numeric");
802 hr = CompareIntegerValues(comparison, llLeft, llRight, pfResult);
803 }
804 else if (BURN_VARIANT_TYPE_VERSION == leftOperand.Type && BURN_VARIANT_TYPE_VERSION == rightOperand.Type)
805 {
806 hr = BVariantGetVersion(&leftOperand, &qwLeft);
807 ExitOnFailure(hr, "Failed to get the left version");
808 hr = BVariantGetVersion(&rightOperand, &qwRight);
809 ExitOnFailure(hr, "Failed to get the right version");
810 hr = CompareVersionValues(comparison, qwLeft, qwRight, pfResult);
811 }
812 else if (BURN_VARIANT_TYPE_VERSION == leftOperand.Type && BURN_VARIANT_TYPE_STRING == rightOperand.Type)
813 {
814 hr = BVariantGetVersion(&leftOperand, &qwLeft);
815 ExitOnFailure(hr, "Failed to get the left version");
816 hr = BVariantGetVersion(&rightOperand, &qwRight);
817 if (FAILED(hr))
818 {
819 if (DISP_E_TYPEMISMATCH != hr)
820 {
821 ExitOnFailure(hr, "Failed to get the right version");
822 }
823 *pfResult = (BURN_SYMBOL_TYPE_NE == comparison);
824 hr = S_OK;
825 }
826 else
827 {
828 hr = CompareVersionValues(comparison, qwLeft, qwRight, pfResult);
829 }
830 }
831 else if (BURN_VARIANT_TYPE_STRING == leftOperand.Type && BURN_VARIANT_TYPE_VERSION == rightOperand.Type)
832 {
833 hr = BVariantGetVersion(&rightOperand, &qwRight);
834 ExitOnFailure(hr, "Failed to get the right version");
835 hr = BVariantGetVersion(&leftOperand, &qwLeft);
836 if (FAILED(hr))
837 {
838 if (DISP_E_TYPEMISMATCH != hr)
839 {
840 ExitOnFailure(hr, "Failed to get the left version");
841 }
842 *pfResult = (BURN_SYMBOL_TYPE_NE == comparison);
843 hr = S_OK;
844 }
845 else
846 {
847 hr = CompareVersionValues(comparison, qwLeft, qwRight, pfResult);
848 }
849 }
850 else if (BURN_VARIANT_TYPE_NUMERIC == leftOperand.Type && BURN_VARIANT_TYPE_STRING == rightOperand.Type)
851 {
852 hr = BVariantGetNumeric(&leftOperand, &llLeft);
853 ExitOnFailure(hr, "Failed to get the left numeric");
854 hr = BVariantGetNumeric(&rightOperand, &llRight);
855 if (FAILED(hr))
856 {
857 if (DISP_E_TYPEMISMATCH != hr)
858 {
859 ExitOnFailure(hr, "Failed to get the right numeric");
860 }
861 *pfResult = (BURN_SYMBOL_TYPE_NE == comparison);
862 hr = S_OK;
863 }
864 else
865 {
866 hr = CompareIntegerValues(comparison, llLeft, llRight, pfResult);
867 }
868 }
869 else if (BURN_VARIANT_TYPE_STRING == leftOperand.Type && BURN_VARIANT_TYPE_NUMERIC == rightOperand.Type)
870 {
871 hr = BVariantGetNumeric(&rightOperand, &llRight);
872 ExitOnFailure(hr, "Failed to get the right numeric");
873 hr = BVariantGetNumeric(&leftOperand, &llLeft);
874 if (FAILED(hr))
875 {
876 if (DISP_E_TYPEMISMATCH != hr)
877 {
878 ExitOnFailure(hr, "Failed to get the left numeric");
879 }
880 *pfResult = (BURN_SYMBOL_TYPE_NE == comparison);
881 hr = S_OK;
882 }
883 else
884 {
885 hr = CompareIntegerValues(comparison, llLeft, llRight, pfResult);
886 }
887 }
888 else
889 {
890 // not a combination that can be compared
891 *pfResult = (BURN_SYMBOL_TYPE_NE == comparison);
892 }
893
894LExit:
895 SecureZeroMemory(&qwLeft, sizeof(DWORD64));
896 SecureZeroMemory(&llLeft, sizeof(LONGLONG));
897 StrSecureZeroFreeString(sczLeft);
898 SecureZeroMemory(&qwRight, sizeof(DWORD64));
899 SecureZeroMemory(&llRight, sizeof(LONGLONG));
900 StrSecureZeroFreeString(sczRight);
901
902 return hr;
903}
904
905//
906// CompareStringValues - compares two string values using a given comparison.
907//
908static HRESULT CompareStringValues(
909 __in BURN_SYMBOL_TYPE comparison,
910 __in_z LPCWSTR wzLeftOperand,
911 __in_z LPCWSTR wzRightOperand,
912 __out BOOL* pfResult
913 )
914{
915 HRESULT hr = S_OK;
916 DWORD dwCompareString = (comparison & INSENSITIVE) ? NORM_IGNORECASE : 0;
917 int cchLeft = lstrlenW(wzLeftOperand);
918 int cchRight = lstrlenW(wzRightOperand);
919
920 switch (comparison)
921 {
922 case BURN_SYMBOL_TYPE_LT:
923 case BURN_SYMBOL_TYPE_GT:
924 case BURN_SYMBOL_TYPE_LE:
925 case BURN_SYMBOL_TYPE_GE:
926 case BURN_SYMBOL_TYPE_EQ:
927 case BURN_SYMBOL_TYPE_NE:
928 case BURN_SYMBOL_TYPE_LT_I:
929 case BURN_SYMBOL_TYPE_GT_I:
930 case BURN_SYMBOL_TYPE_LE_I:
931 case BURN_SYMBOL_TYPE_GE_I:
932 case BURN_SYMBOL_TYPE_EQ_I:
933 case BURN_SYMBOL_TYPE_NE_I:
934 {
935 int i = ::CompareStringW(LOCALE_INVARIANT, dwCompareString, wzLeftOperand, cchLeft, wzRightOperand, cchRight);
936 hr = CompareIntegerValues(comparison, i, CSTR_EQUAL, pfResult);
937 }
938 break;
939 case BURN_SYMBOL_TYPE_BAND:
940 case BURN_SYMBOL_TYPE_BAND_I:
941 // test if left string contains right string
942 for (int i = 0; (i + cchRight) <= cchLeft; ++i)
943 {
944 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, dwCompareString, wzLeftOperand + i, cchRight, wzRightOperand, cchRight))
945 {
946 *pfResult = TRUE;
947 ExitFunction();
948 }
949 }
950 *pfResult = FALSE;
951 break;
952 case BURN_SYMBOL_TYPE_HIEQ:
953 case BURN_SYMBOL_TYPE_HIEQ_I:
954 // test if left string starts with right string
955 *pfResult = cchLeft >= cchRight && CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, dwCompareString, wzLeftOperand, cchRight, wzRightOperand, cchRight);
956 break;
957 case BURN_SYMBOL_TYPE_LOEQ:
958 case BURN_SYMBOL_TYPE_LOEQ_I:
959 // test if left string ends with right string
960 *pfResult = cchLeft >= cchRight && CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, dwCompareString, wzLeftOperand + (cchLeft - cchRight), cchRight, wzRightOperand, cchRight);
961 break;
962 default:
963 ExitFunction1(hr = E_INVALIDARG);
964 }
965
966LExit:
967 return hr;
968}
969
970//
971// CompareIntegerValues - compares two integer values using a given comparison.
972//
973static HRESULT CompareIntegerValues(
974 __in BURN_SYMBOL_TYPE comparison,
975 __in LONGLONG llLeftOperand,
976 __in LONGLONG llRightOperand,
977 __out BOOL* pfResult
978 )
979{
980 HRESULT hr = S_OK;
981
982 switch (comparison)
983 {
984 case BURN_SYMBOL_TYPE_LT: case BURN_SYMBOL_TYPE_LT_I: *pfResult = llLeftOperand < llRightOperand; break;
985 case BURN_SYMBOL_TYPE_GT: case BURN_SYMBOL_TYPE_GT_I: *pfResult = llLeftOperand > llRightOperand; break;
986 case BURN_SYMBOL_TYPE_LE: case BURN_SYMBOL_TYPE_LE_I: *pfResult = llLeftOperand <= llRightOperand; break;
987 case BURN_SYMBOL_TYPE_GE: case BURN_SYMBOL_TYPE_GE_I: *pfResult = llLeftOperand >= llRightOperand; break;
988 case BURN_SYMBOL_TYPE_EQ: case BURN_SYMBOL_TYPE_EQ_I: *pfResult = llLeftOperand == llRightOperand; break;
989 case BURN_SYMBOL_TYPE_NE: case BURN_SYMBOL_TYPE_NE_I: *pfResult = llLeftOperand != llRightOperand; break;
990 case BURN_SYMBOL_TYPE_BAND: case BURN_SYMBOL_TYPE_BAND_I: *pfResult = (llLeftOperand & llRightOperand) ? TRUE : FALSE; break;
991 case BURN_SYMBOL_TYPE_HIEQ: case BURN_SYMBOL_TYPE_HIEQ_I: *pfResult = ((llLeftOperand >> 16) & 0xFFFF) == llRightOperand; break;
992 case BURN_SYMBOL_TYPE_LOEQ: case BURN_SYMBOL_TYPE_LOEQ_I: *pfResult = (llLeftOperand & 0xFFFF) == llRightOperand; break;
993 default:
994 ExitFunction1(hr = E_INVALIDARG);
995 }
996
997LExit:
998 return hr;
999}
1000
1001//
1002// CompareVersionValues - compares two quad-word version values using a given comparison.
1003//
1004static HRESULT CompareVersionValues(
1005 __in BURN_SYMBOL_TYPE comparison,
1006 __in DWORD64 qwLeftOperand,
1007 __in DWORD64 qwRightOperand,
1008 __out BOOL* pfResult
1009 )
1010{
1011 HRESULT hr = S_OK;
1012
1013 switch (comparison)
1014 {
1015 case BURN_SYMBOL_TYPE_LT: *pfResult = qwLeftOperand < qwRightOperand; break;
1016 case BURN_SYMBOL_TYPE_GT: *pfResult = qwLeftOperand > qwRightOperand; break;
1017 case BURN_SYMBOL_TYPE_LE: *pfResult = qwLeftOperand <= qwRightOperand; break;
1018 case BURN_SYMBOL_TYPE_GE: *pfResult = qwLeftOperand >= qwRightOperand; break;
1019 case BURN_SYMBOL_TYPE_EQ: *pfResult = qwLeftOperand == qwRightOperand; break;
1020 case BURN_SYMBOL_TYPE_NE: *pfResult = qwLeftOperand != qwRightOperand; break;
1021 case BURN_SYMBOL_TYPE_BAND: *pfResult = (qwLeftOperand & qwRightOperand) ? TRUE : FALSE; break;
1022 case BURN_SYMBOL_TYPE_HIEQ: *pfResult = ((qwLeftOperand >> 16) & 0xFFFF) == qwRightOperand; break;
1023 case BURN_SYMBOL_TYPE_LOEQ: *pfResult = (qwLeftOperand & 0xFFFF) == qwRightOperand; break;
1024 default:
1025 ExitFunction1(hr = E_INVALIDARG);
1026 }
1027
1028LExit:
1029 return hr;
1030}