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