From 89b0b58edde7d074d612ba5cfdf5bd229094159f Mon Sep 17 00:00:00 2001 From: Michael Schroeder Date: Wed, 6 Sep 2017 14:46:55 +0200 Subject: [PATCH] Forbid 'if' richops in 'or' context and 'unless' richops in 'and' context Guide users to the correct operator instead. --- 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 45082bb411..705d4cbbed 100644 --- a/build/parseReqs.c +++ b/build/parseReqs.c @@ -224,7 +224,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 af98f23666..1659a23e96 100644 --- a/lib/rpmds.c +++ b/lib/rpmds.c @@ -1476,11 +1476,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; @@ -1500,10 +1527,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; } @@ -1518,6 +1549,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) { @@ -1525,8 +1559,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; @@ -1535,18 +1568,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; } @@ -1555,6 +1598,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 272fcadc1e..4a16a14752 100644 --- a/lib/rpmds.h +++ b/lib/rpmds.h @@ -486,6 +486,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