/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2015 Gary Mills
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <strings.h>
#include <string.h>
#include <lber.h>
#include <ldap.h>
#include "db_item_c.h"
#include "nisdb_mt.h"
#include "ldap_util.h"
#include "ldap_structs.h"
#include "ldap_val.h"
#include "ldap_ruleval.h"
#include "ldap_op.h"
#include "ldap_nisdbquery.h"
#include "ldap_attr.h"
#include "ldap_xdr.h"
#include "ldap_ldap.h"
item *
buildItem(int len, void *value) {
char *myself = "buildItem";
item *i = am(myself, sizeof (*i));
int mlen = len;
if (i == 0)
return (0);
/*
* To this function, a NULL value, or a length less than or equal
* zero means an item with no value. Hence, buildItem(0, 0) is
* _not_ the right way to create index_value == 0 to indicate
* deletion.
*/
if (value == 0 || len <= 0) {
i->itemvalue.itemvalue_len = 0;
i->itemvalue.itemvalue_val = 0;
return (i);
}
/*
* NIS+ usually stores the terminating NUL for strings, so we add
* it here just in case. This means we usually waste a byte for
* binary column values...
*/
if (len > 0 && ((char *)value)[len-1] != '\0')
mlen++;
i->itemvalue.itemvalue_len = len;
i->itemvalue.itemvalue_val = am(myself, mlen);
if (mlen > 0 && i->itemvalue.itemvalue_val == 0) {
free(i);
return (0);
}
memcpy(i->itemvalue.itemvalue_val, value, len);
return (i);
}
void
freeItem(item *i) {
if (i != 0) {
sfree(i->itemvalue.itemvalue_val);
free(i);
}
}
void
freeQcomp(db_qcomp *qc, int doFree) {
if (qc == 0)
return;
freeItem(qc->index_value);
if (doFree)
free(qc);
}
db_query *
buildQuery(int num_components, db_qcomp *components) {
char *myself = "buildQuery";
db_query *q = am(myself, sizeof (*q));
if (q == 0)
return (0);
q->components.components_len = num_components;
q->components.components_val = components;
return (q);
}
/*
* Clone a db_query. The 'numComps' parameter can be used to specify
* the number of db_qcomp's to allocate (in the 'components.components_val'
* array), if 'components.components_len' hasn't yet reached its expected
* maximum value.
*/
db_query *
cloneQuery(db_query *old, int numComps) {
db_query *new;
int i;
char *myself = "cloneQuery";
if (old == 0)
return (0);
new = am(myself, sizeof (*new));
if (new == 0)
return (0);
if (old->components.components_len > numComps)
numComps = old->components.components_len;
new->components.components_val = am(myself,
sizeof (new->components.components_val[0]) *
numComps);
if (numComps > 0 && new->components.components_val == 0) {
free(new);
return (0);
}
for (i = 0; i < old->components.components_len; i++) {
item *it;
if (old->components.components_val[i].index_value == 0) {
new->components.components_val[i].index_value = 0;
new->components.components_val[i].which_index =
old->components.components_val[i].which_index;
continue;
}
it = buildItem(old->components.components_val[i].index_value->
itemvalue.itemvalue_len,
old->components.components_val[i].index_value->
itemvalue.itemvalue_val);
if (it == 0) {
new->components.components_len = i + 1;
freeQuery(new);
return (0);
}
new->components.components_val[i].index_value = it;
new->components.components_val[i].which_index =
old->components.components_val[i].which_index;
}
new->components.components_len = old->components.components_len;
return (new);
}
void
freeQuery(db_query *q) {
int i;
if (q == 0)
return;
for (i = 0; i < q->components.components_len; i++) {
freeItem(q->components.components_val[i].index_value);
}
sfree(q->components.components_val);
sfree(q);
}
void
freeQueries(db_query **q, int numQ) {
int i;
if (q == 0)
return;
for (i = 0; i < numQ; i++)
freeQuery(q[i]);
sfree(q);
}
/*
* Given an array index[0..num-1] of pointers to strings of the form
* "name=value", create the corresponding db_queries. "name=" indicates
* deletion, which results in a db_query component where index_value == 0.
*
* The __nis_table_mapping_t structure is used to translate column
* names to indices.
*
* If 'rvP' is non-NULL, the searchable columns from the 'index'
* name/value pairs are used to retrieve copies of the corresponding NIS+
* entries, and '*rvP' is initialized with the current entry values
* and object attributes. Names/values supplied in 'index' override
* those from existing NIS+ entries.
*/
db_query **
createQuery(int num, char **index, __nis_table_mapping_t *t,
__nis_rule_value_t **rvP, int *numVals) {
db_query **q;
db_qcomp *qc;
int i, j, n, a, nv, niv;
__nis_rule_value_t *rvq;
__nis_buffer_t b = {0, 0};
char *table = 0;
char *myself = "createQuery";
rvq = initRuleValue(1, 0);
if (rvq == 0)
return (0);
if (numVals == 0)
numVals = &nv;
*numVals = 0;
if (rvP != 0) {
/*
* Try to obtain a copy of the table object, in order to
* determine the searchable columns. A failure isn't
* necessarily fatal; we just try to compose the entire
* LDAP data from the col=val pairs.
*/
table = fullObjName(F, t->objName);
if (table == 0) {
logmsg(MSG_NOTIMECHECK, LOG_ERR,
"%s: Error converting \"%s\" to FQ object name",
myself, NIL(t->objName));
freeRuleValue(rvq, 1);
return (0);
}
}
/* Create a rule-value from the col=val pairs */
for (n = 0; n < num; n++) {
char *value;
if ((value = strchr(index[n], '=')) == 0) {
logmsg(MSG_NOTIMECHECK, LOG_WARNING,
"%s: no '=' in \"%s\"",
myself, index[n]);
continue;
}
*value = '\0';
value++;
for (a = 0; a < t->numColumns; a++) {
if (strcmp(index[n], t->column[a]) == 0) {
/* Add col=val pair to 'rvq' */
if (addSCol2RuleValue(index[n], value, rvq)) {
freeRuleValue(rvq, 1);
sfree(table);
return (0);
}
break;
}
}
if (a >= t->numColumns) {
logmsg(MSG_NOTIMECHECK, LOG_WARNING,
"%s: Ignoring unknown column \"%s\"",
myself, NIL(index[n]));
}
}
/*
* Find out if any of the columns specified via the 'index'
* array are multi-valued.
*/
for (n = 0, niv = 1; n < rvq->numColumns; n++) {
if (rvq->colVal[n].numVals > 1)
niv *= rvq->colVal[n].numVals;
}
*numVals = 1;
sfree(b.buf);
sfree(table);
if (rvq->numColumns <= 0) {
freeRuleValue(rvq, *numVals);
*numVals = 0;
return (0);
}
/*
* If any column name was repeated in the col=val pairs (but with
* different values), 'rvq' will have one or more multi-valued
* column values. We now convert those into an array of rule-values
* where every column is single-valued.
*
* Since we want all combinations of column values, the number
* of array elements is the product of all column value counts.
*
* There are four possible combinations of 'index' and NIS+ data:
*
* (1) Only single-valued 'index' columns, and at most one NIS+
* entry, so 'rvq' is complete, and '*numVals' == 1.
*
* (2) Single-valued 'index' columns, but multiple NIS+ entries.
* '*numVals' reflects the number of NIS+ entries, and no
* expansion of 'index' column values to array elements is
* needed.
*
* (3) At least one multi-valued 'index', and multiple NIS+
* entries. We already rejected the NIS+ data for this case
* above, so it is in fact equivalent to case (4).
*
* (4) At least one multi-valued 'index', but at most one NIS+
* entry. This is the case where we must expand the multi-valued
* columns to multiple array elements.
*/
if (niv > 1 && *numVals == 1) {
__nis_rule_value_t *rv;
int repeat;
/*
* By using initRuleValue() to create 'rv', and make each
* element a clone of 'rvq', we save a lot of code. The
* down side is that 'rv' only really needs one element
* for each rv[].colVal[].val array, but we know that at
* least one rvq->colVal[].val array has more than one
* element. Hence, making 'rv' a clone of 'rvq' will waste
* memory.
*
* However, we believe this waste is acceptable, because
* we expect that 'niv' will be small. Also, we are executing
* in the context of a utility command, not in a daemon.
*/
rv = initRuleValue(niv, rvq);
if (rv == 0) {
freeRuleValue(rvq, 1);
*numVals = 0;
return (0);
}
/*
* For each column value in 'rvq', copy to the appropriate
* place in 'rv', so that the end result is that all
* combinations of values are enumerated, and each
* 'rv[n].colVal[i]' is single-valued.
*
* We do this by traversing the rv[] array 'rvq->numColumns'
* times, where each traversal 'i' works on the values
* for rvq->colVal[i]. A repeat factor 'repeat' starts out
* at '1', and is multiplied by 'rvq->colVal[i].numVals'
* at the end of each traversal. Every value
* rvq->colVal[i].val[j] is repeated 'repeat' times.
*
* This algorithm works by regarding the rv[] array as
* an I-dimensional array (I = rvq->numColumns), where
* each dimension 'i' corresponds to the values for
* rvq->colVal[i]. The I-dimensional array is stored
* in column-major order.
*
* Since the 'rv' elements start out as copies of 'rvq',
* we achieve the "copy" of the 'rvq' column values by
* deleting those we don't want from the 'rv' elements.
*/
for (i = 0, repeat = 1; i < rvq->numColumns; i++) {
int r, k;
for (n = 0, j = 0, r = 0; n < niv; n++) {
/*
* Free all but element 'j' of the
* rv[n].colVal[i].val array.
*/
for (k = 0; k < rv[n].colVal[i].numVals; k++) {
/* Leave element 'j' in place */
if (k == j)
continue;
sfree(rv[n].colVal[i].val[k].
value);
}
rv[n].colVal[i].numVals = 1;
/* Move element 'j' to zero */
if (j != 0)
rv[n].colVal[i].val[0] =
rv[n].colVal[i].val[j];
/*
* Increment the repeat index 'r'. If >=
* 'repeat', reset 'r' and increment the
* value index 'j'. If 'j' >=
* rvq->colVal[i].numVals, start over on
* the column values for column 'i' (i.e.,
* reset 'j' to zero).
*/
r += 1;
if (r >= repeat) {
r = 0;
j += 1;
if (j >= rvq->colVal[i].numVals)
j = 0;
}
}
repeat *= rvq->colVal[i].numVals;
}
*numVals = niv;
freeRuleValue(rvq, 1);
rvq = rv;
rv = 0;
}
q = am(myself, *numVals * sizeof (q[0]));
if (q == 0) {
freeRuleValue(rvq, *numVals);
return (0);
}
/*
* Create queries from the rvq[] array.
*/
for (a = 0; a < *numVals; a++) {
int nn, err = 0;
qc = am(myself, rvq[a].numColumns * sizeof (*qc));
if (qc != 0) {
for (nn = 0, i = 0; i < rvq[a].numColumns; i++) {
for (j = 0; j < t->numColumns; j++) {
if (strcmp(rvq[a].colName[i],
t->column[j]) == 0) {
break;
}
}
if (j >= t->numColumns)
continue;
qc[nn].which_index = j;
if (rvq[a].colVal[i].numVals > 0) {
qc[nn].index_value = buildItem(
rvq[a].colVal[i].val[0].length,
rvq[a].colVal[i].val[0].value);
if (qc[nn].index_value == 0)
err++;
} else {
logmsg(MSG_NOTIMECHECK, LOG_ERR,
"%s: No values for [%d]%s",
myself, a, rvq[a].colName[i]);
err++;
}
nn++;
}
if (err == 0)
q[a] = buildQuery(nn, qc);
}
if (err > 0 || q[a] == 0) {
freeQueries(q, a);
for (a = 0; a < nn; a++)
freeQcomp(&qc[a], F);
sfree(qc);
freeRuleValue(rvq, *numVals);
return (0);
}
}
if (rvP != 0) {
*rvP = rvq;
} else {
freeRuleValue(rvq, 1);
*numVals = 0;
}
return (q);
}
void
printQuery(db_query *q, __nis_table_mapping_t *t) {
int i, mc = -1;
char *myself = "printQuery";
char *val[NIS_MAXCOLUMNS];
if (q == 0)
return;
(void) memset(val, 0, sizeof (val));
/*
* Collect the values, which may be out of order in 'q'.
* Remember the largest index.
*/
for (i = 0; i < q->components.components_len; i++) {
int ix = q->components.components_val[i].which_index;
if (ix >= NIS_MAXCOLUMNS ||
(t != 0 && ix >= t->numColumns))
continue;
if (ix > mc)
mc = ix;
val[ix] = q->components.components_val[i].index_value->
itemvalue.itemvalue_val;
}
/* Print the values we collected */
for (i = 0; i <= mc; i++) {
p2buf(myself, "%s%s", (i != 0 ? " " : ""),
(val[i] != 0 ? val[i] : ""));
}
/* If we printed anything, add a newline */
if (mc >= 0)
p2buf(myself, "\n");
}
/*
* Verify that the db_query's 'q' and 'fq' match, in the sense that if
* they both have a value for a certain index, the values are the same.
*/
int
verifyQueryMatch(db_query *q, db_query *fq) {
int i, j, match;
if (fq == 0)
return (1);
if (q == 0)
return ((fq == 0) ? 1 : 0);
for (i = 0, match = 1; match && i < q->components.components_len;
i++) {
for (j = 0; j < fq->components.components_len; j++) {
int len, flen;
/* Same index ? */
if (q->components.components_val[i].which_index !=
fq->components.components_val[j].
which_index)
continue;
/*
* If one 'index_value' is NULL, the other one must
* be NULL as well.
*/
if (q->components.components_val[i].index_value == 0) {
if (fq->components.components_val[j].
index_value == 0)
continue;
else {
match = 0;
break;
}
}
if (fq->components.components_val[j].index_value ==
0) {
match = 0;
break;
}
/* Same value lengths ? */
len = q->components.components_val[i].index_value->
itemvalue.itemvalue_len;
flen = fq->components.components_val[j].index_value->
itemvalue.itemvalue_len;
if (len != flen) {
/*
* There's a twist here: the input query
* may well _not_ count a concluding NUL
* in a string value, while the output
* usually will. So, if the difference in
* length is one, and the "extra" byte is
* a zero-valued one, we accept equality.
* 'q' is assumed to be the output, and
* 'fq' the input.
*/
if (!(len > 0 && len == (flen+1) &&
q->components.components_val[i].
index_value->
itemvalue.itemvalue_val[len-1] == 0)) {
match = 0;
break;
}
}
/* Same value ? */
if (memcmp(q->components.components_val[i].index_value->
itemvalue.itemvalue_val,
fq->components.components_val[j].index_value->
itemvalue.itemvalue_val,
flen) != 0) {
match = 0;
break;
}
}
}
return (match);
}
/*
* Remove those queries in 'q' that don't match t->index.
* Returns a pointer to the filtered array, which could be
* a compacted version of the original, or a new copy; in
* the latter case, the original will have been freed.
*
* Filtered/removed db_query's are freed.
*/
db_query **
filterQuery(__nis_table_mapping_t *t, db_query **q, db_query *qin,
__nis_obj_attr_t ***objAttr, int *numQueries) {
db_query **new;
__nis_obj_attr_t **attr;
int i, nq, nn;
char *myself = "filterQuery";
if ((t == 0 && qin == 0) || q == 0 ||
numQueries == 0 || *numQueries <= 0)
return (q);
nq = *numQueries;
new = am(myself, nq * sizeof (new[0]));
if (objAttr != 0)
attr = am(myself, nq * sizeof (attr[0]));
else
attr = 0;
if (new == 0 || (objAttr != 0 && attr == 0)) {
sfree(new);
freeQueries(q, nq);
sfree(attr);
if (objAttr != 0) {
freeObjAttr(*objAttr, nq);
*objAttr = 0;
}
*numQueries = -1;
return (0);
}
for (i = 0, nn = 0; i < nq; i++) {
int retain = 1;
if (t != 0)
retain = verifyIndexMatch(t, q[i], 0, 0, 0);
if (retain && qin != 0)
retain = verifyQueryMatch(q[i], qin);
if (retain) {
new[nn] = q[i];
if (objAttr != 0)
attr[nn] = (*objAttr)[i];
nn++;
} else {
freeQuery(q[i]);
q[i] = 0;
if (objAttr != 0) {
freeSingleObjAttr((*objAttr)[i]);
(*objAttr)[i] = 0;
}
}
}
/* All q[i]'s are either in 'new', or have been deleted */
free(q);
if (objAttr != 0) {
sfree(*objAttr);
*objAttr = attr;
}
*numQueries = nn;
return (new);
}
db_query **
createNisPlusEntry(__nis_table_mapping_t *t, __nis_rule_value_t *rv,
db_query *qin, __nis_obj_attr_t ***objAttr,
int *numQueries) {
db_query **query = 0;
int r, i, j, ir;
__nis_value_t *rval, *lval;
__nis_mapping_item_t *litem;
int numItems;
int nq;
__nis_obj_attr_t **attr = 0;
char **dn = 0;
int numDN = 0;
char *myself = "createNisPlusEntry";
if (t == 0 || t->objectDN == 0 || rv == 0)
return (0);
/* Establish default, per-thread, search base */
__nisdb_get_tsd()->searchBase = t->objectDN->read.base;
for (r = 0, nq = 0; r < t->numRulesFromLDAP; r++) {
int nrq, ntq, err;
db_query **newq;
__nis_obj_attr_t **newattr;
rval = buildRvalue(&t->ruleFromLDAP[r]->rhs,
mit_ldap, rv, NULL);
if (rval == 0)
continue;
litem = buildLvalue(&t->ruleFromLDAP[r]->lhs, &rval,
&numItems);
if (litem == 0) {
freeValue(rval, 1);
/* XXX Should this be a fatal error ? */
continue;
}
lval = 0;
for (i = 0; i < numItems; i++) {
__nis_value_t *tmpval, *old;
tmpval = getMappingItem(&litem[i],
mit_nisplus, 0, 0, NULL);
/*
* If the LHS specifies an out-of-context LDAP or
* NIS+ item, we do the update right here. We
* don't add any values to 'lval'; instead, we
* skip to the next item. (However, we still
* get a string representation of the LHS in case
* we need to report an error.)
*/
if (litem[i].type == mit_ldap) {
int stat;
if (dn == 0)
dn = findDNs(myself, rv, 1,
t->objectDN->write.base,
&numDN);
stat = storeLDAP(&litem[i], i, numItems, rval,
t->objectDN, dn, numDN);
if (stat != LDAP_SUCCESS) {
char *iname = "<unknown>";
if (tmpval != 0 &&
tmpval->numVals == 1)
iname = tmpval->val[0].value;
logmsg(MSG_NOTIMECHECK, LOG_ERR,
"%s: LDAP store \"%s\": %s",
myself, iname,
ldap_err2string(stat));
}
freeValue(tmpval, 1);
continue;
}
old = lval;
lval = concatenateValues(old, tmpval);
freeValue(tmpval, 1);
freeValue(old, 1);
}
freeMappingItem(litem, numItems);
if (lval == 0 || lval->numVals <= 0 || rval->numVals <= 0) {
freeValue(lval, 1);
freeValue(rval, 1);
continue;
}
/*
* We now have a number of possible cases. The notation
* used in the table is:
*
* single A single value (numVals == 1)
* single/rep A single value with repeat == 1
* multi[N] N values
* multi[N]/rep M values with repeat == 1
* (M) M resulting db_query's
*
* lval \ rval single single/rep multi[N] multi[N]/rep
* single (1) (1) (1) (1)
* single/rep (1) (1) (N) (N)
* multi[M] (1) (1) (1) 1+(N-1)/M
* multi[M]/rep (1) (1) (1) 1+(N-1)/M
*
* Of course, we already have 'nq' db_query's from previous
* rules, so the resulting number of queries is max(1,nq)
* times the numbers in the table above.
*/
/* The number of queries resulting from the current rule */
if (rval->numVals > 1) {
if (lval->numVals == 1 && lval->repeat)
nrq = rval->numVals;
else if (lval->numVals > 1 && rval->repeat)
nrq = 1 + ((rval->numVals-1)/lval->numVals);
else
nrq = 1;
} else {
nrq = 1;
}
/* Total number of queries after adding the current rule */
if (nq <= 0)
ntq = nrq;
else
ntq = nq * nrq;
if (ntq > nq) {
newq = realloc(query, ntq * sizeof (query[0]));
newattr = realloc(attr, ntq * sizeof (attr[0]));
if (newq == 0 || newattr == 0) {
logmsg(MSG_NOMEM, LOG_ERR,
"%s: realloc(%d) => NULL",
myself, ntq * sizeof (query[0]));
freeValue(lval, 1);
freeValue(rval, 1);
freeQueries(query, nq);
freeObjAttr(attr, nq);
sfree(newq);
freeDNs(dn, numDN);
return (0);
}
query = newq;
attr = newattr;
}
/*
* Copy/clone the existing queries to the new array,
* remembering that realloc() has done the first 'nq'
* ones.
*
* If there's an error (probably memory allocation), we
* still go through the rest of the array, so that it's
* simple to free the elements when we clean up.
*/
for (i = 1, err = 0; i < nrq; i++) {
for (j = 0; j < nq; j++) {
query[(nq*i)+j] = cloneQuery(query[j],
t->numColumns);
if (query[(nq*i)+j] == 0 &&
query[j] != 0)
err++;
attr[(nq*i)+j] = cloneObjAttr(attr[j]);
if (attr[(nq*i)+j] == 0 &&
attr[j] != 0)
err++;
}
}
if (err > 0) {
freeValue(lval, 1);
freeValue(rval, 1);
freeQueries(query, ntq);
freeObjAttr(attr, ntq);
freeDNs(dn, numDN);
return (0);
}
/*
* Special case if nq == 0 (i.e., the first time we
* allocated db_query's). If so, we now allocate empty
* db_qcomp arrays, which simplifies subsequent
* copying of values.
*/
if (nq <= 0) {
(void) memset(query, 0, ntq * sizeof (query[0]));
(void) memset(attr, 0, ntq * sizeof (attr[0]));
for (i = 0, err = 0; i < ntq; i++) {
query[i] = am(myself, sizeof (*query[i]));
if (query[i] == 0) {
err++;
break;
}
query[i]->components.components_val =
am(myself, t->numColumns *
sizeof (query[i]->components.components_val[0]));
if (query[i]->components.components_val == 0) {
err++;
break;
}
query[i]->components.components_len = 0;
}
if (err > 0) {
freeValue(lval, 1);
freeValue(rval, 1);
freeQueries(query, ntq);
freeObjAttr(attr, ntq);
freeDNs(dn, numDN);
return (0);
}
}
/* Now we're ready to add the new values */
for (i = 0, ir = 0; i < lval->numVals; i++) {
char *oaName = 0;
int index;
/* Find column index */
for (index = 0; index < t->numColumns;
index++) {
if (strncmp(t->column[index],
lval->val[i].value,
lval->val[i].length) == 0)
break;
}
if (index >= t->numColumns) {
/*
* Could be one of the special object
* attributes.
*/
oaName = isObjAttr(&lval->val[i]);
if (oaName == 0)
continue;
}
for (j = i*nrq; j < (i+1)*nrq; j++) {
int k;
/* If we're out of values, repeat last one */
ir = (j < rval->numVals) ?
j : rval->numVals - 1;
/*
* Step through the query array, adding
* the new value every 'nrq' queries, and
* starting at 'query[j % nrq]'.
*/
for (k = j % nrq, err = 0; k < ntq; k += nrq) {
int ic, c;
if (oaName != 0) {
int fail = setObjAttrField(
oaName,
&rval->val[ir],
&attr[k]);
if (fail) {
err++;
break;
}
continue;
}
ic = query[k]->components.
components_len;
/*
* If we've already filled this
* query, the new value is a dup
* which we'll ignore.
*/
if (ic >= t->numColumns)
continue;
/*
* Do we already have a value for
* this 'index' ?
*/
for (c = 0; c < ic; c++) {
if (query[k]->components.
components_val[c].
which_index == index)
break;
}
/* If no previous value, add it */
if (c >= ic) {
int l;
char *v;
query[k]->components.
components_val[ic].
which_index = index;
l = rval->val[ir].length;
v = rval->val[ir].value;
if (rval->type == vt_string &&
l > 0 &&
v[l-1] != '\0' &&
v[l] == '\0')
l++;
query[k]->components.
components_val[ic].
index_value =
buildItem(l, v);
if (query[k]->
components.
components_val[ic].
index_value == 0) {
err++;
break;
}
query[k]->components.
components_len++;
}
}
if (err > 0) {
freeValue(lval, 1);
freeValue(rval, 1);
freeQueries(query, ntq);
freeObjAttr(attr, ntq);
freeDNs(dn, numDN);
return (0);
}
}
}
freeValue(lval, 1);
freeValue(rval, 1);
nq = ntq;
}
freeDNs(dn, numDN);
if (nq <= 0) {
sfree(query);
query = 0;
}
/* Should we filter on index or input query ? */
if (query != 0) {
if (t->index.numIndexes > 0)
query = filterQuery(t, query, qin, &attr, &nq);
else if (qin != 0)
query = filterQuery(0, query, qin, &attr, &nq);
}
if (query != 0 && numQueries != 0)
*numQueries = nq;
if (objAttr != 0)
*objAttr = attr;
else
freeObjAttr(attr, nq);
return (query);
}
/*
* Given a table mapping and a rule-value, convert to an array of
* (db_query *), using the fromLDAP ruleset.
*
* On entry, '*numQueries' holds the number of elements in the 'rv'
* array. On exit, it holds the number of (db_query *)'s in the return
* value array.
*/
db_query **
ruleValue2Query(__nis_table_mapping_t *t, __nis_rule_value_t *rv,
db_query *qin, __nis_obj_attr_t ***objAttr, int *numQueries) {
db_query **q = 0, ***qp = 0;
int i, nqp, nq, *nnp = 0, nv;
__nis_obj_attr_t **attr = 0, ***atp = 0;
char *myself = "ruleValue2Query";
if (t == 0 || rv == 0 || numQueries == 0)
return (0);
nv = *numQueries;
if (nv <= 0)
return (0);
/*
* 'qp' is an array of (db_query **), and we get one element for
* each call to createNisPlusEntry(); i.e., one for each rule-value.
*
* 'nnp[i]' is the count of (db_query *) in each 'qp[i]'.
*/
qp = am(myself, nv * sizeof (*qp));
nnp = am(myself, nv * sizeof (*nnp));
atp = am(myself, nv * sizeof (*atp));
if (qp == 0 || nnp == 0 || atp == 0) {
sfree(qp);
sfree(nnp);
sfree(atp);
return (0);
}
for (i = 0, nq = 0, nqp = 0; i < nv; i++) {
qp[nqp] = createNisPlusEntry(t, &rv[i], qin, &atp[nqp],
&nnp[nqp]);
/* If we fail, abort (XXX??? or continue ???) */
if (qp[nqp] == 0)
goto cleanup;
nq += nnp[nqp];
nqp++;
}
/* If we didn't get any (db_query **)'s, return failure */
if (nqp == 0 || nq <= 0)
goto cleanup;
q = am(myself, nq * sizeof (q[0]));
attr = am(myself, nq * sizeof (attr[0]));
if (q == 0 || attr == 0) {
nq = 0;
goto cleanup;
}
/* Convert 'qp' to an array of (db_query *)'s */
for (i = 0, nq = 0; i < nqp; i++) {
(void) memcpy(&q[nq], qp[i], nnp[i] * sizeof (qp[i][0]));
(void) memcpy(&attr[nq], atp[i], nnp[i] * sizeof (atp[i][0]));
nq += nnp[i];
free(qp[i]);
free(atp[i]);
}
*numQueries = nq;
if (objAttr != 0)
*objAttr = attr;
else
freeObjAttr(attr, nq);
/* Make sure 'cleanup' doesn't free the db_query pointers */
nqp = 0;
cleanup:
for (i = 0; i < nqp; i++) {
freeQueries(qp[i], nnp[i]);
sfree(atp[i]);
}
sfree(qp);
sfree(nnp);
sfree(atp);
return (q);
}
db_query *
pseudoEntryObj2Query(entry_obj *e, nis_object *tobj, __nis_rule_value_t *rv) {
db_query *qbuf;
db_qcomp *qcbuf;
int nc, i;
__nis_rule_value_t *rvt = 0;
char *myself = "pseudoEntryObj2Query";
nc = e->en_cols.en_cols_len - 1;
if (e == 0 || nc < 0 || nc > NIS_MAXCOLUMNS)
return (0);
/*
* If 'rvP' is non-NULL, build a rule value from the pseudo-
* nis_object in e->en_cols.en_cols_val[0].
*/
if (rv != 0) {
nis_object *o;
o = unmakePseudoEntryObj(e, tobj);
if (o == 0)
return (0);
rvt = addObjAttr2RuleValue(o, 0);
nis_destroy_object(o);
if (rvt == 0)
return (0);
}
qbuf = am(myself, sizeof (*qbuf));
/*
* If there are no columns (other than the pseudo-entry object),
* we're done.
*/
if (nc == 0)
return (qbuf);
qcbuf = am(myself, nc * sizeof (*qcbuf));
if (qcbuf == 0) {
sfree(qcbuf);
if (rvt != 0)
freeRuleValue(rvt, 1);
return (0);
}
/*
* Build the db_query, remembering that e->en_cols.en_cols_val[0]
* is the pseudo-nis_object.
*/
qbuf->components.components_val = qcbuf;
qbuf->components.components_len = nc;
for (i = 0; i < nc; i++) {
qcbuf[i].which_index = i;
qcbuf[i].index_value = buildItem(
e->en_cols.en_cols_val[i+1].ec_value.ec_value_len,
e->en_cols.en_cols_val[i+1].ec_value.ec_value_val);
if (qcbuf[i].index_value == 0) {
freeQuery(qbuf);
if (rvt != 0)
freeRuleValue(rvt, 1);
return (0);
}
}
if (rvt != 0) {
*rv = *rvt;
sfree(rvt);
}
return (qbuf);
}
/*
* Given an input query 'q', and a db_query work buffer 'qbuf', return
* a pointer to a query with one component corresponding to component
* 'index' in 'q'.
*
* Note that no memory is allocated, and that the returned query has
* pointers into 'q'.
*/
db_query *
queryFromComponent(db_query *q, int index, db_query *qbuf) {
if (q == 0 || index < 0 || index >= q->components.components_len ||
qbuf == 0)
return (0);
qbuf->components.components_len = 1;
qbuf->components.components_val = &q->components.components_val[index];
return (qbuf);
}