From b1f76f67414e2504a08bec77003ee502319772a9 Mon Sep 17 00:00:00 2001 From: Michael Schroeder Date: Wed, 6 Sep 2017 14:46:55 +0200 Subject: Forbid 'if' richops in 'or' context and 'unless' richops in 'and' context Guide users to the correct operator instead. (cherry picked from commit 99e6de810904c2b9d32341832f541108c196cc97) --- build/parseReqs.c | 2 +- lib/rpmds.c | 93 ++++++++++++++++++++++++++++++++++++++++++++++--------- lib/rpmds.h | 11 +++++++ 3 files changed, 90 insertions(+), 16 deletions(-) diff --git a/build/parseReqs.c b/build/parseReqs.c index a443505e4..bbf04af1e 100644 --- a/build/parseReqs.c +++ b/build/parseReqs.c @@ -232,7 +232,7 @@ rpmRC parseRCPOT(rpmSpec spec, Package pkg, const char *field, rpmTagVal tagN, } data.spec = spec; data.sb = newStringBuf(); - if (rpmrichParse(&r, &emsg, parseRCPOTRichCB, &data) != RPMRC_OK) { + if (rpmrichParseForTag(&r, &emsg, parseRCPOTRichCB, &data, nametag) != RPMRC_OK) { freeStringBuf(data.sb); goto exit; } diff --git a/lib/rpmds.c b/lib/rpmds.c index c57ac1f7f..d7c08be60 100644 --- a/lib/rpmds.c +++ b/lib/rpmds.c @@ -1453,11 +1453,38 @@ static rpmRC parseSimpleDep(const char **dstrp, char **emsg, rpmrichParseFunctio return RPMRC_OK; } -static rpmRC rpmrichParseInternal(const char **dstrp, char **emsg, rpmrichParseFunction cb, void *cbdata, int *nowithp) +#define RICHPARSE_CHECK (1 << 0) +#define RICHPARSE_NO_WITH (1 << 1) +#define RICHPARSE_NO_AND (1 << 2) +#define RICHPARSE_NO_OR (1 << 3) + +static rpmRC rpmrichParseCheck(rpmrichOp op, int check, char **emsg) +{ + if ((op == RPMRICHOP_WITH || op == RPMRICHOP_WITHOUT) && (check & RICHPARSE_NO_WITH) != 0) { + if (emsg) + rasprintf(emsg, _("Illegal ops in with/without")); + return RPMRC_FAIL; + } + if ((check & RICHPARSE_CHECK) == 0) + return RPMRC_OK; + if ((op == RPMRICHOP_AND || op == RPMRICHOP_IF) && (check & RICHPARSE_NO_AND) != 0) { + if (emsg) + rasprintf(emsg, _("Illegal context for 'unless', please use 'or' instead")); + return RPMRC_FAIL; + } + if ((op == RPMRICHOP_OR || op == RPMRICHOP_UNLESS) && (check & RICHPARSE_NO_OR) != 0) { + if (emsg) + rasprintf(emsg, _("Illegal context for 'if', please use 'and' instead")); + return RPMRC_FAIL; + } + return RPMRC_OK; +} + +static rpmRC rpmrichParseInternal(const char **dstrp, char **emsg, rpmrichParseFunction cb, void *cbdata, int *checkp) { const char *p = *dstrp, *pe; - rpmrichOp op = RPMRICHOP_SINGLE, chainop = 0; - int nowith = 0; + rpmrichOp op = RPMRICHOP_SINGLE, firstop = RPMRICHOP_SINGLE, chainop = 0; + int check = checkp ? *checkp : 0; if (cb(cbdata, RPMRICH_PARSE_ENTER, p, 0, 0, 0, 0, op, emsg) != RPMRC_OK) return RPMRC_FAIL; @@ -1477,10 +1504,14 @@ static rpmRC rpmrichParseInternal(const char **dstrp, char **emsg, rpmrichParseF } return RPMRC_FAIL; } - if (*p == '(') { - if (rpmrichParseInternal(&p, emsg, cb, cbdata, &nowith) != RPMRC_OK) - return RPMRC_FAIL; - } else { + if (*p == '(') { + int subcheck = check & RICHPARSE_CHECK; + if (rpmrichParseInternal(&p, emsg, cb, cbdata, &subcheck) != RPMRC_OK) + return RPMRC_FAIL; + if (op == RPMRICHOP_IF || op == RPMRICHOP_UNLESS) + subcheck &= ~(RICHPARSE_NO_AND | RICHPARSE_NO_OR); + check |= subcheck; + } else { if (parseSimpleDep(&p, emsg, cb, cbdata) != RPMRC_OK) return RPMRC_FAIL; } @@ -1495,6 +1526,9 @@ static rpmRC rpmrichParseInternal(const char **dstrp, char **emsg, rpmrichParseF pe = p; if (parseRichDepOp(&pe, &op, emsg) != RPMRC_OK) return RPMRC_FAIL; + if (firstop == RPMRICHOP_SINGLE) + firstop = op; + if (op == RPMRICHOP_ELSE && (chainop == RPMRICHOP_IF || chainop == RPMRICHOP_UNLESS)) chainop = 0; if (chainop && op != chainop) { @@ -1502,8 +1536,7 @@ static rpmRC rpmrichParseInternal(const char **dstrp, char **emsg, rpmrichParseF rasprintf(emsg, _("Cannot chain different ops")); return RPMRC_FAIL; } - if (chainop && op != RPMRICHOP_AND && op != RPMRICHOP_OR && - op != RPMRICHOP_WITH) { + if (chainop && op != RPMRICHOP_AND && op != RPMRICHOP_OR && op != RPMRICHOP_WITH) { if (emsg) rasprintf(emsg, _("Can only chain and/or/with ops")); return RPMRC_FAIL; @@ -1512,18 +1545,28 @@ static rpmRC rpmrichParseInternal(const char **dstrp, char **emsg, rpmrichParseF return RPMRC_FAIL; chainop = op; p = pe; - if (nowithp && op != RPMRICHOP_WITH && op != RPMRICHOP_WITHOUT && op != RPMRICHOP_OR) - *nowithp = 1; } - if ((op == RPMRICHOP_WITH || op == RPMRICHOP_WITHOUT) && nowith) { - if (emsg) - rasprintf(emsg, _("Illegal ops in with/without")); + + /* check for illegal combinations */ + if (rpmrichParseCheck(firstop, check, emsg) != RPMRC_OK) return RPMRC_FAIL; - } + + /* update check data */ + if (firstop == RPMRICHOP_IF) + check |= RICHPARSE_NO_OR; + if (firstop == RPMRICHOP_UNLESS) + check |= RICHPARSE_NO_AND; + if (op == RPMRICHOP_AND || op == RPMRICHOP_OR) + check &= ~(RICHPARSE_NO_AND | RICHPARSE_NO_OR); + if (op != RPMRICHOP_SINGLE && op != RPMRICHOP_WITH && op != RPMRICHOP_WITHOUT && op != RPMRICHOP_OR) + check |= RICHPARSE_NO_WITH; + p++; if (cb(cbdata, RPMRICH_PARSE_LEAVE, *dstrp, p - *dstrp , 0, 0, 0, op, emsg) != RPMRC_OK) return RPMRC_FAIL; *dstrp = p; + if (checkp) + *checkp |= check; return RPMRC_OK; } @@ -1532,6 +1575,26 @@ rpmRC rpmrichParse(const char **dstrp, char **emsg, rpmrichParseFunction cb, voi return rpmrichParseInternal(dstrp, emsg, cb, cbdata, NULL); } +rpmRC rpmrichParseForTag(const char **dstrp, char **emsg, rpmrichParseFunction cb, void *cbdata, rpmTagVal tagN) +{ + int check = RICHPARSE_CHECK; + if (rpmrichParseInternal(dstrp, emsg, cb, cbdata, &check) != RPMRC_OK) + return RPMRC_FAIL; + switch (tagN) { + case RPMTAG_CONFLICTNAME: + case RPMTAG_SUPPLEMENTNAME: + case RPMTAG_ENHANCENAME: + if (rpmrichParseCheck(RPMRICHOP_OR, check, emsg) != RPMRC_OK) + return RPMRC_FAIL; + break; + default: + if (rpmrichParseCheck(RPMRICHOP_AND, check, emsg) != RPMRC_OK) + return RPMRC_FAIL; + break; + } + return RPMRC_OK; +} + struct rpmdsParseRichDepData { rpmds dep; rpmsenseFlags depflags; diff --git a/lib/rpmds.h b/lib/rpmds.h index b1a587dfa..a13dfbef3 100644 --- a/lib/rpmds.h +++ b/lib/rpmds.h @@ -495,6 +495,17 @@ typedef rpmRC (*rpmrichParseFunction) (void *cbdata, rpmrichParseType type, */ rpmRC rpmrichParse(const char **dstrp, char **emsg, rpmrichParseFunction cb, void *cbdata); +/** + * Parse a rich dependency string for a specific tag + * @param dstrp pointer to sting, will be updated + * @param emsg returns the error string, can be NULL + * @param cb callback function + * @param cbdata callback function data + * @param tagN type of dependency + * @return RPMRC_OK on success + */ +rpmRC rpmrichParseForTag(const char **dstrp, char **emsg, rpmrichParseFunction cb, void *cbdata, rpmTagVal tagN); + /** * Return if current depenency is rich -- cgit v1.2.1