Home | History | Annotate | Download | only in lib
      1 /*
      2  * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
      3  * Use is subject to license terms.
      4  */
      5 #pragma ident	"%Z%%M%	%I%	%E% SMI"
      6 
      7 /* auxprop.c - auxilliary property support
      8  * Rob Siemborski
      9  * $Id: auxprop.c,v 1.10 2003/03/19 18:25:27 rjs3 Exp $
     10  */
     11 /*
     12  * Copyright (c) 1998-2003 Carnegie Mellon University.  All rights reserved.
     13  *
     14  * Redistribution and use in source and binary forms, with or without
     15  * modification, are permitted provided that the following conditions
     16  * are met:
     17  *
     18  * 1. Redistributions of source code must retain the above copyright
     19  *    notice, this list of conditions and the following disclaimer.
     20  *
     21  * 2. Redistributions in binary form must reproduce the above copyright
     22  *    notice, this list of conditions and the following disclaimer in
     23  *    the documentation and/or other materials provided with the
     24  *    distribution.
     25  *
     26  * 3. The name "Carnegie Mellon University" must not be used to
     27  *    endorse or promote products derived from this software without
     28  *    prior written permission. For permission or any other legal
     29  *    details, please contact
     30  *      Office of Technology Transfer
     31  *      Carnegie Mellon University
     32  *      5000 Forbes Avenue
     33  *      Pittsburgh, PA  15213-3890
     34  *      (412) 268-4387, fax: (412) 268-7395
     35  *      tech-transfer (at) andrew.cmu.edu
     36  *
     37  * 4. Redistributions of any form whatsoever must retain the following
     38  *    acknowledgment:
     39  *    "This product includes software developed by Computing Services
     40  *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
     41  *
     42  * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
     43  * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
     44  * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
     45  * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     46  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
     47  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
     48  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     49  */
     50 
     51 #include <config.h>
     52 #include <sasl.h>
     53 #include <prop.h>
     54 #include <ctype.h>
     55 #include "saslint.h"
     56 
     57 struct proppool
     58 {
     59     struct proppool *next;
     60 
     61     size_t size;          /* Size of Block */
     62     size_t unused;        /* Space unused in this pool between end
     63 			   * of char** area and beginning of char* area */
     64 
     65     char data[1];         /* Variable Sized */
     66 };
     67 
     68 struct propctx  {
     69     struct propval *values;
     70     struct propval *prev_val; /* Previous value used by set/setvalues */
     71 
     72     unsigned used_values, allocated_values;
     73 
     74     char *data_end; /* Bottom of string area in current pool */
     75     char **list_end; /* Top of list area in current pool */
     76 
     77     struct proppool *mem_base;
     78     struct proppool *mem_cur;
     79 };
     80 
     81 typedef struct auxprop_plug_list
     82 {
     83     struct auxprop_plug_list *next;
     84     const sasl_auxprop_plug_t *plug;
     85 #ifdef _SUN_SDK_
     86     char *plugname;
     87 #endif /* _SUN_SDK_ */
     88 } auxprop_plug_list_t;
     89 
     90 #ifndef _SUN_SDK_
     91 static auxprop_plug_list_t *auxprop_head = NULL;
     92 #endif /* !_SUN_SDK_ */
     93 
     94 static struct proppool *alloc_proppool(size_t size)
     95 {
     96     struct proppool *ret;
     97     /* minus 1 for the one that is already a part of the array
     98      * in the struct */
     99     size_t total_size = sizeof(struct proppool) + size - 1;
    100 #ifdef _SUN_SDK_
    101     ret = sasl_sun_ALLOC(total_size);
    102 #else
    103     ret = sasl_ALLOC(total_size);
    104 #endif /* _SUN_SDK_*/
    105     if(!ret) return NULL;
    106 
    107     memset(ret, 0, total_size);
    108 
    109     ret->size = ret->unused = size;
    110 
    111     return ret;
    112 }
    113 
    114 /* Resize a proppool.  Invalidates the unused value for this pool */
    115 static struct proppool *resize_proppool(struct proppool *pool, size_t size)
    116 {
    117     struct proppool *ret;
    118 
    119     if(pool->size >= size) return pool;
    120 #ifdef _SUN_SDK_
    121     ret = sasl_sun_REALLOC(pool, sizeof(struct proppool) + size);
    122 #else
    123     ret = sasl_REALLOC(pool, sizeof(struct proppool) + size);
    124 #endif /* _SUN_SDK_*/
    125     if(!ret) return NULL;
    126 
    127     ret->size = size;
    128 
    129     return ret;
    130 }
    131 
    132 static int prop_init(struct propctx *ctx, unsigned estimate)
    133 {
    134     const unsigned VALUES_SIZE = PROP_DEFAULT * sizeof(struct propval);
    135 
    136     ctx->mem_base = alloc_proppool(VALUES_SIZE + estimate);
    137     if(!ctx->mem_base) return SASL_NOMEM;
    138 
    139     ctx->mem_cur = ctx->mem_base;
    140 
    141     ctx->values = (struct propval *)ctx->mem_base->data;
    142     ctx->mem_base->unused = ctx->mem_base->size - VALUES_SIZE;
    143     ctx->allocated_values = PROP_DEFAULT;
    144     ctx->used_values = 0;
    145 
    146     ctx->data_end = ctx->mem_base->data + ctx->mem_base->size;
    147     ctx->list_end = (char **)(ctx->mem_base->data + VALUES_SIZE);
    148 
    149     ctx->prev_val = NULL;
    150 
    151     return SASL_OK;
    152 }
    153 
    154 /* create a property context
    155  *  estimate -- an estimate of the storage needed for requests & responses
    156  *              0 will use module default
    157  * returns NULL on error
    158  */
    159 struct propctx *prop_new(unsigned estimate)
    160 {
    161     struct propctx *new_ctx;
    162 
    163     if(!estimate) estimate = PROP_DEFAULT * 255;
    164 
    165 #ifdef _SUN_SDK_
    166     new_ctx = sasl_sun_ALLOC(sizeof(struct propctx));
    167 #else
    168     new_ctx = sasl_ALLOC(sizeof(struct propctx));
    169 #endif /* _SUN_SDK_*/
    170     if(!new_ctx) return NULL;
    171 
    172     if(prop_init(new_ctx, estimate) != SASL_OK) {
    173 	prop_dispose(&new_ctx);
    174     }
    175 
    176     return new_ctx;
    177 }
    178 
    179 /* create new propctx which duplicates the contents of an existing propctx
    180  * returns -1 on error
    181  */
    182 int prop_dup(struct propctx *src_ctx, struct propctx **dst_ctx)
    183 {
    184     struct proppool *pool;
    185     struct propctx *retval = NULL;
    186     unsigned i;
    187     int result;
    188     size_t total_size = 0, values_size;
    189 
    190     if(!src_ctx || !dst_ctx) return SASL_BADPARAM;
    191 
    192     /* What is the total allocated size of src_ctx? */
    193     pool = src_ctx->mem_base;
    194     while(pool) {
    195 	total_size += pool->size;
    196 	pool = pool->next;
    197     }
    198 
    199     /* allocate the new context */
    200     retval = prop_new(total_size);
    201     if(!retval) return SASL_NOMEM;
    202 
    203     retval->used_values = src_ctx->used_values;
    204     retval->allocated_values = src_ctx->used_values + 1;
    205 
    206     values_size = (retval->allocated_values * sizeof(struct propval));
    207 
    208     retval->mem_base->unused = retval->mem_base->size - values_size;
    209 
    210     retval->list_end = (char **)(retval->mem_base->data + values_size);
    211     /* data_end should still be OK */
    212 
    213     /* Now dup the values */
    214     for(i=0; i<src_ctx->used_values; i++) {
    215 	retval->values[i].name = src_ctx->values[i].name;
    216 	result = prop_setvals(retval, retval->values[i].name,
    217 			      src_ctx->values[i].values);
    218 	if(result != SASL_OK)
    219 	    goto fail;
    220     }
    221 
    222     retval->prev_val = src_ctx->prev_val;
    223 
    224     *dst_ctx = retval;
    225     return SASL_OK;
    226 
    227     fail:
    228     if(retval) prop_dispose(&retval);
    229     return result;
    230 }
    231 
    232 /*
    233  * dispose of property context
    234  *  ctx      -- is disposed and set to NULL; noop if ctx or *ctx is NULL
    235  */
    236 void prop_dispose(struct propctx **ctx)
    237 {
    238     struct proppool *tmp;
    239 
    240     if(!ctx || !*ctx) return;
    241 
    242     while((*ctx)->mem_base) {
    243 	tmp = (*ctx)->mem_base;
    244 	(*ctx)->mem_base = tmp->next;
    245 #ifdef _SUN_SDK_
    246         sasl_sun_FREE(tmp);
    247 #else
    248 	sasl_FREE(tmp);
    249 #endif /* _SUN_SDK_*/
    250     }
    251 
    252 #ifdef _SUN_SDK_
    253     sasl_sun_FREE(*ctx);
    254 #else
    255     sasl_FREE(*ctx);
    256 #endif /* _SUN_SDK_*/
    257     *ctx = NULL;
    258 
    259     return;
    260 }
    261 
    262 /* Add property names to request
    263  *  ctx       -- context from prop_new()
    264  *  names     -- list of property names; must persist until context freed
    265  *               or requests cleared
    266  *
    267  * NOTE: may clear values from context as side-effect
    268  * returns -1 on error
    269  */
    270 int prop_request(struct propctx *ctx, const char **names)
    271 {
    272     unsigned i, new_values, total_values;
    273 
    274     if(!ctx || !names) return SASL_BADPARAM;
    275 
    276     /* Count how many we need to add */
    277     for(new_values=0; names[new_values]; new_values++);
    278 
    279     /* Do we need to add ANY? */
    280     if(!new_values) return SASL_OK;
    281 
    282     /* We always want atleast on extra to mark the end of the array */
    283     total_values = new_values + ctx->used_values + 1;
    284 
    285     /* Do we need to increase the size of our propval table? */
    286     if(total_values > ctx->allocated_values) {
    287 	unsigned max_in_pool;
    288 
    289 	/* Do we need a larger base pool? */
    290 	max_in_pool = ctx->mem_base->size / sizeof(struct propval);
    291 
    292 	if(total_values <= max_in_pool) {
    293 	    /* Don't increase the size of the base pool, just use what
    294 	       we need */
    295 	    ctx->allocated_values = total_values;
    296 	    ctx->mem_base->unused =
    297 		ctx->mem_base->size - (sizeof(struct propval)
    298 				       * ctx->allocated_values);
    299       	} else {
    300 	    /* We need to allocate more! */
    301 	    unsigned new_alloc_length;
    302 	    size_t new_size;
    303 
    304 	    new_alloc_length = 2 * ctx->allocated_values;
    305 	    while(total_values > new_alloc_length) {
    306 		new_alloc_length *= 2;
    307 	    }
    308 
    309 	    new_size = new_alloc_length * sizeof(struct propval);
    310 	    ctx->mem_base = resize_proppool(ctx->mem_base, new_size);
    311 
    312 	    if(!ctx->mem_base) {
    313 		ctx->values = NULL;
    314 		ctx->allocated_values = ctx->used_values = 0;
    315 		return SASL_NOMEM;
    316 	    }
    317 
    318 	    /* It worked! Update the structure! */
    319 	    ctx->values = (struct propval *)ctx->mem_base->data;
    320 	    ctx->allocated_values = new_alloc_length;
    321 	    ctx->mem_base->unused = ctx->mem_base->size
    322 		- sizeof(struct propval) * ctx->allocated_values;
    323 	}
    324 
    325 	/* Clear out new propvals */
    326 	memset(&(ctx->values[ctx->used_values]), 0,
    327 	       sizeof(struct propval) * (ctx->allocated_values - ctx->used_values));
    328 
    329         /* Finish updating the context -- we've extended the list! */
    330 	/* ctx->list_end = (char **)(ctx->values + ctx->allocated_values); */
    331 	/* xxx test here */
    332 	ctx->list_end = (char **)(ctx->values + total_values);
    333     }
    334 
    335     /* Now do the copy, or referencing rather */
    336     for(i=0;i<new_values;i++) {
    337 	unsigned j, flag;
    338 
    339 	flag = 0;
    340 
    341 	/* Check for dups */
    342 	for(j=0;j<ctx->used_values;j++) {
    343 	    if(!strcmp(ctx->values[j].name, names[i])) {
    344 		flag = 1;
    345 		break;
    346 	    }
    347 	}
    348 
    349 	/* We already have it... skip! */
    350 	if(flag) continue;
    351 
    352 	ctx->values[ctx->used_values++].name = names[i];
    353     }
    354 
    355     prop_clear(ctx, 0);
    356 
    357     return SASL_OK;
    358 }
    359 
    360 /* return array of struct propval from the context
    361  *  return value persists until next call to
    362  *   prop_request, prop_clear or prop_dispose on context
    363  */
    364 const struct propval *prop_get(struct propctx *ctx)
    365 {
    366     if(!ctx) return NULL;
    367 
    368     return ctx->values;
    369 }
    370 
    371 /* Fill in an array of struct propval based on a list of property names
    372  *  return value persists until next call to
    373  *   prop_request, prop_clear or prop_dispose on context
    374  *  returns -1 on error (no properties ever requested, ctx NULL, etc)
    375  *  returns number of matching properties which were found (values != NULL)
    376  *  if a name requested here was never requested by a prop_request, then
    377  *  the name field of the associated vals entry will be set to NULL
    378  */
    379 int prop_getnames(struct propctx *ctx, const char **names,
    380 		  struct propval *vals)
    381 {
    382     int found_names = 0;
    383 
    384     struct propval *cur = vals;
    385     const char **curname;
    386 
    387     if(!ctx || !names || !vals) return SASL_BADPARAM;
    388 
    389     for(curname = names; *curname; curname++) {
    390 	struct propval *val;
    391 	for(val = ctx->values; val->name; val++) {
    392 	    if(!strcmp(*curname,val->name)) {
    393 		found_names++;
    394 		memcpy(cur, val, sizeof(struct propval));
    395 		goto next;
    396 	    }
    397 	}
    398 
    399 	/* If we are here, we didn't find it */
    400 	memset(cur, 0, sizeof(struct propval));
    401 
    402 	next:
    403 	cur++;
    404     }
    405 
    406     return found_names;
    407 }
    408 
    409 
    410 /* clear values and optionally requests from property context
    411  *  ctx      -- property context
    412  *  requests -- 0 = don't clear requests, 1 = clear requests
    413  */
    414 void prop_clear(struct propctx *ctx, int requests)
    415 {
    416     struct proppool *new_pool, *tmp;
    417     unsigned i;
    418 
    419 #ifdef _SUN_SDK_
    420     if(!ctx) return;
    421 #endif /* _SUN_SDK_ */
    422 
    423     /* We're going to need a new proppool once we reset things */
    424     new_pool = alloc_proppool(ctx->mem_base->size +
    425 			      (ctx->used_values+1) * sizeof(struct propval));
    426 
    427     if(requests) {
    428 	/* We're wiping the whole shebang */
    429 	ctx->used_values = 0;
    430     } else {
    431 	/* Need to keep around old requets */
    432 	struct propval *new_values = (struct propval *)new_pool->data;
    433 	for(i=0; i<ctx->used_values; i++) {
    434 	    new_values[i].name = ctx->values[i].name;
    435 	}
    436     }
    437 
    438     while(ctx->mem_base) {
    439 	tmp = ctx->mem_base;
    440 	ctx->mem_base = tmp->next;
    441 #ifdef _SUN_SDK_
    442 	sasl_sun_FREE(tmp);
    443 #else
    444 	sasl_FREE(tmp);
    445 #endif /* _SUN_SDK_ */
    446     }
    447 
    448     /* Update allocation-related metadata */
    449     ctx->allocated_values = ctx->used_values+1;
    450     new_pool->unused =
    451 	new_pool->size - (ctx->allocated_values * sizeof(struct propval));
    452 
    453     /* Setup pointers for the values array */
    454     ctx->values = (struct propval *)new_pool->data;
    455     ctx->prev_val = NULL;
    456 
    457     /* Setup the pools */
    458     ctx->mem_base = ctx->mem_cur = new_pool;
    459 
    460     /* Reset list_end and data_end for the new memory pool */
    461     ctx->list_end =
    462 	(char **)((char *)ctx->mem_base->data + ctx->allocated_values * sizeof(struct propval));
    463     ctx->data_end = (char *)ctx->mem_base->data + ctx->mem_base->size;
    464 
    465     return;
    466 }
    467 
    468 /*
    469  * erase the value of a property
    470  */
    471 void prop_erase(struct propctx *ctx, const char *name)
    472 {
    473     struct propval *val;
    474     int i;
    475 
    476     if(!ctx || !name) return;
    477 
    478     for(val = ctx->values; val->name; val++) {
    479 	if(!strcmp(name,val->name)) {
    480 	    if(!val->values) break;
    481 
    482 	    /*
    483 	     * Yes, this is casting away the const, but
    484 	     * we should be okay because the only place this
    485 	     * memory should be is in the proppool's
    486 	     */
    487 	    for(i=0;val->values[i];i++) {
    488 		memset((void *)(val->values[i]),0,strlen(val->values[i]));
    489 		val->values[i] = NULL;
    490 	    }
    491 
    492 	    val->values = NULL;
    493 	    val->nvalues = 0;
    494 	    val->valsize = 0;
    495 	    break;
    496 	}
    497     }
    498 
    499     return;
    500 }
    501 
    502 /****fetcher interfaces****/
    503 
    504 /* format the requested property names into a string
    505  *  ctx    -- context from prop_new()/prop_request()
    506  *  sep    -- separator between property names (unused if none requested)
    507  *  seplen -- length of separator, if < 0 then strlen(sep) will be used
    508  *  outbuf -- output buffer
    509  *  outmax -- maximum length of output buffer including NUL terminator
    510  *  outlen -- set to length of output string excluding NUL terminator
    511  * returns 0 on success and amount of additional space needed on failure
    512  */
    513 int prop_format(struct propctx *ctx, const char *sep, int seplen,
    514 		char *outbuf, unsigned outmax, unsigned *outlen)
    515 {
    516     unsigned needed, flag = 0;
    517     struct propval *val;
    518 
    519     if(!ctx || !outbuf) return SASL_BADPARAM;
    520 
    521     if(!sep) seplen = 0;
    522     if(seplen < 0) seplen = strlen(sep);
    523 
    524     needed = seplen * (ctx->used_values - 1);
    525     for(val = ctx->values; val->name; val++) {
    526 	needed += strlen(val->name);
    527     }
    528 
    529     if(!outmax) return (needed + 1); /* Because of unsigned funkiness */
    530     if(needed > (outmax - 1)) return (needed - (outmax - 1));
    531 
    532     *outbuf = '\0';
    533     if(outlen) *outlen = needed;
    534 
    535     if(needed == 0) return SASL_OK;
    536 
    537     for(val = ctx->values; val->name; val++) {
    538 	if(seplen && flag) {
    539 	    strncat(outbuf, sep, seplen);
    540 	} else {
    541 	    flag = 1;
    542 	}
    543 	strcat(outbuf, val->name);
    544     }
    545 
    546     return SASL_OK;
    547 }
    548 
    549 /* add a property value to the context
    550  *  ctx    -- context from prop_new()/prop_request()
    551  *  name   -- name of property to which value will be added
    552  *            if NULL, add to the same name as previous prop_set/setvals call
    553  *  value  -- a value for the property; will be copied into context
    554  *            if NULL, remove existing values
    555  *  vallen -- length of value, if <= 0 then strlen(value) will be used
    556  */
    557 int prop_set(struct propctx *ctx, const char *name,
    558 	     const char *value, int vallen)
    559 {
    560     struct propval *cur;
    561 
    562     if(!ctx) return SASL_BADPARAM;
    563     if(!name && !ctx->prev_val) return SASL_BADPARAM;
    564 
    565     if(name) {
    566 	struct propval *val;
    567 
    568 	ctx->prev_val = NULL;
    569 
    570 	for(val = ctx->values; val->name; val++) {
    571 	    if(!strcmp(name,val->name)){
    572 		ctx->prev_val = val;
    573 		break;
    574 	    }
    575 	}
    576 
    577 	/* Couldn't find it! */
    578 	if(!ctx->prev_val) return SASL_BADPARAM;
    579     }
    580 
    581     cur = ctx->prev_val;
    582 
    583     if(name) /* New Entry */ {
    584 	unsigned nvalues = 1; /* 1 for NULL entry */
    585 	const char **old_values = NULL;
    586 	char **tmp, **tmp2;
    587 	size_t size;
    588 
    589 	if(cur->values) {
    590 
    591 	    if(!value) {
    592 		/* If we would be adding a null value, then we are done */
    593 		return SASL_OK;
    594 	    }
    595 
    596 	    old_values = cur->values;
    597 	    tmp = (char **)cur->values;
    598 	    while(*tmp) {
    599 		nvalues++;
    600 		tmp++;
    601 	    }
    602 
    603 	}
    604 
    605 	if(value) {
    606 	    nvalues++; /* for the new value */
    607 	}
    608 
    609 	size = nvalues * sizeof(char*);
    610 
    611 	if(size > ctx->mem_cur->unused) {
    612 	    size_t needed;
    613 
    614 	    for(needed = ctx->mem_cur->size * 2; needed < size; needed *= 2);
    615 
    616 	    /* Allocate a new proppool */
    617 	    ctx->mem_cur->next = alloc_proppool(needed);
    618 	    if(!ctx->mem_cur->next) return SASL_NOMEM;
    619 
    620 	    ctx->mem_cur = ctx->mem_cur->next;
    621 
    622 	    ctx->list_end = (char **)ctx->mem_cur->data;
    623 	    ctx->data_end = ctx->mem_cur->data + needed;
    624 	}
    625 
    626 	/* Grab the memory */
    627 	ctx->mem_cur->unused -= size;
    628 	cur->values = (const char **)ctx->list_end;
    629 	cur->values[nvalues - 1] = NULL;
    630 
    631 	/* Finish updating the context */
    632 	ctx->list_end = (char **)(cur->values + nvalues);
    633 
    634 	/* If we don't have an actual value to fill in, we are done */
    635 	if(!value)
    636 	    return SASL_OK;
    637 
    638 	tmp2 = (char **)cur->values;
    639 	if(old_values) {
    640 	    tmp = (char **)old_values;
    641 
    642 	    while(*tmp) {
    643 		*tmp2 = *tmp;
    644 		tmp++; tmp2++;
    645 	    }
    646 	}
    647 
    648 	/* Now allocate the last entry */
    649 	if(vallen <= 0)
    650 	    size = (size_t)(strlen(value) + 1);
    651 	else
    652 	    size = (size_t)(vallen + 1);
    653 
    654 	if(size > ctx->mem_cur->unused) {
    655 	    size_t needed;
    656 
    657 	    needed = ctx->mem_cur->size * 2;
    658 
    659 	    while(needed < size) {
    660 		needed *= 2;
    661 	    }
    662 
    663 	    /* Allocate a new proppool */
    664 	    ctx->mem_cur->next = alloc_proppool(needed);
    665 	    if(!ctx->mem_cur->next) return SASL_NOMEM;
    666 
    667 	    ctx->mem_cur = ctx->mem_cur->next;
    668 	    ctx->list_end = (char **)ctx->mem_cur->data;
    669 	    ctx->data_end = ctx->mem_cur->data + needed;
    670 	}
    671 
    672 	/* Update the data_end pointer */
    673 	ctx->data_end -= size;
    674 	ctx->mem_cur->unused -= size;
    675 
    676 	/* Copy and setup the new value! */
    677 	memcpy(ctx->data_end, value, size-1);
    678 	ctx->data_end[size - 1] = '\0';
    679 	cur->values[nvalues - 2] = ctx->data_end;
    680 
    681 	cur->nvalues++;
    682 	cur->valsize += (size - 1);
    683     } else /* Appending an entry */ {
    684 	char **tmp;
    685 	size_t size;
    686 
    687 	/* If we are setting it to be NULL, we are done */
    688 	if(!value) return SASL_OK;
    689 
    690 	size = sizeof(char*);
    691 
    692 	/* Is it in the current pool, and will it fit in the unused space? */
    693 	if(size > ctx->mem_cur->unused &&
    694 	    (void *)cur->values > (void *)(ctx->mem_cur->data) &&
    695 	    (void *)cur->values < (void *)(ctx->mem_cur->data + ctx->mem_cur->size)) {
    696 	    /* recursively call the not-fast way */
    697 	    return prop_set(ctx, cur->name, value, vallen);
    698 	}
    699 
    700 	/* Note the invariant: the previous value list must be
    701 	   at the top of the CURRENT pool at this point */
    702 
    703 	/* Grab the memory */
    704 	ctx->mem_cur->unused -= size;
    705 	ctx->list_end++;
    706 
    707 	*(ctx->list_end - 1) = NULL;
    708 	tmp = (ctx->list_end - 2);
    709 
    710 	/* Now allocate the last entry */
    711 	if(vallen <= 0)
    712 	    size = strlen(value) + 1;
    713 	else
    714 	    size = vallen + 1;
    715 
    716 	if(size > ctx->mem_cur->unused) {
    717 	    size_t needed;
    718 
    719 	    needed = ctx->mem_cur->size * 2;
    720 
    721 	    while(needed < size) {
    722 		needed *= 2;
    723 	    }
    724 
    725 	    /* Allocate a new proppool */
    726 	    ctx->mem_cur->next = alloc_proppool(needed);
    727 	    if(!ctx->mem_cur->next) return SASL_NOMEM;
    728 
    729 	    ctx->mem_cur = ctx->mem_cur->next;
    730 	    ctx->list_end = (char **)ctx->mem_cur->data;
    731 	    ctx->data_end = ctx->mem_cur->data + needed;
    732 	}
    733 
    734 	/* Update the data_end pointer */
    735 	ctx->data_end -= size;
    736 	ctx->mem_cur->unused -= size;
    737 
    738 	/* Copy and setup the new value! */
    739 	memcpy(ctx->data_end, value, size-1);
    740 	ctx->data_end[size - 1] = '\0';
    741 	*tmp = ctx->data_end;
    742 
    743 	cur->nvalues++;
    744 	cur->valsize += (size - 1);
    745     }
    746 
    747     return SASL_OK;
    748 }
    749 
    750 
    751 /* set the values for a property
    752  *  ctx    -- context from prop_new()/prop_request()
    753  *  name   -- name of property to which value will be added
    754  *            if NULL, add to the same name as previous prop_set/setvals call
    755  *  values -- array of values, ending in NULL.  Each value is a NUL terminated
    756  *            string
    757  */
    758 int prop_setvals(struct propctx *ctx, const char *name,
    759 		 const char **values)
    760 {
    761     const char **val = values;
    762     int result = SASL_OK;
    763 
    764     if(!ctx) return SASL_BADPARAM;
    765 
    766     /* If they want us to add no values, we can do that */
    767     if(!values) return SASL_OK;
    768 
    769     /* Basically, use prop_set to do all our dirty work for us */
    770     if(name) {
    771 	result = prop_set(ctx, name, *val, 0);
    772 	val++;
    773     }
    774 
    775     for(;*val;val++) {
    776 	if(result != SASL_OK) return result;
    777 	result = prop_set(ctx, NULL, *val,0);
    778     }
    779 
    780     return result;
    781 }
    782 
    783 /* Request a set of auxiliary properties
    784  *  conn         connection context
    785  *  propnames    list of auxiliary property names to request ending with
    786  *               NULL.
    787  *
    788  * Subsequent calls will add items to the request list.  Call with NULL
    789  * to clear the request list.
    790  *
    791  * errors
    792  *  SASL_OK       -- success
    793  *  SASL_BADPARAM -- bad count/conn parameter
    794  *  SASL_NOMEM    -- out of memory
    795  */
    796 int sasl_auxprop_request(sasl_conn_t *conn, const char **propnames)
    797 {
    798     int result;
    799     sasl_server_conn_t *sconn;
    800 
    801     if(!conn) return SASL_BADPARAM;
    802     if(conn->type != SASL_CONN_SERVER)
    803 	PARAMERROR(conn);
    804 
    805     sconn = (sasl_server_conn_t *)conn;
    806 
    807     if(!propnames) {
    808 	prop_clear(sconn->sparams->propctx,1);
    809 	return SASL_OK;
    810     }
    811 
    812     result = prop_request(sconn->sparams->propctx, propnames);
    813     RETURN(conn, result);
    814 }
    815 
    816 
    817 /* Returns current auxiliary property context.
    818  * Use functions in prop.h to access content
    819  *
    820  *  if authentication hasn't completed, property values may be empty/NULL
    821  *
    822  *  properties not recognized by active plug-ins will be left empty/NULL
    823  *
    824  *  returns NULL if conn is invalid.
    825  */
    826 struct propctx *sasl_auxprop_getctx(sasl_conn_t *conn)
    827 {
    828     sasl_server_conn_t *sconn;
    829 
    830     if(!conn || conn->type != SASL_CONN_SERVER) return NULL;
    831 
    832     sconn = (sasl_server_conn_t *)conn;
    833 
    834     return sconn->sparams->propctx;
    835 }
    836 
    837 /* add an auxiliary property plugin */
    838 #ifdef _SUN_SDK_
    839 int sasl_auxprop_add_plugin(const char *plugname,
    840                             sasl_auxprop_init_t *auxpropfunc)
    841 {
    842     return (_sasl_auxprop_add_plugin(_sasl_gbl_ctx(), plugname, auxpropfunc));
    843 }
    844 
    845 int _sasl_auxprop_add_plugin(void *ctx,
    846                              const char *plugname,
    847                              sasl_auxprop_init_t *auxpropfunc)
    848 #else
    849 int sasl_auxprop_add_plugin(const char *plugname,
    850 			    sasl_auxprop_init_t *auxpropfunc)
    851 #endif /* _SUN_SDK_ */
    852 {
    853     int result, out_version;
    854     auxprop_plug_list_t *new_item;
    855     sasl_auxprop_plug_t *plug;
    856 #ifdef _SUN_SDK_
    857     _sasl_global_context_t *gctx = ctx == NULL ? _sasl_gbl_ctx() : ctx;
    858     auxprop_plug_list_t *auxprop_head;
    859     const sasl_utils_t *sasl_global_utils;
    860     auxprop_plug_list_t *l;
    861 
    862     auxprop_head = gctx->auxprop_head;
    863     sasl_global_utils = gctx->sasl_server_global_utils;
    864 
    865   /* Check to see if this plugin has already been registered */
    866     for (l = auxprop_head; l != NULL; l = l->next) {
    867 	if (strcmp(plugname, l->plugname) == 0) {
    868 	    return SASL_OK;
    869 	}
    870     }
    871 #endif /* _SUN_SDK_ */
    872 
    873     result = auxpropfunc(sasl_global_utils, SASL_AUXPROP_PLUG_VERSION,
    874 			 &out_version, &plug, plugname);
    875 
    876     if(result != SASL_OK) {
    877 #ifdef _SUN_SDK_
    878 	__sasl_log(gctx, gctx->server_global_callbacks.callbacks,
    879 		SASL_LOG_ERR, "auxpropfunc error %i\n",result);
    880 #else
    881 	_sasl_log(NULL, SASL_LOG_ERR, "auxpropfunc error %i\n",result);
    882 #endif /* _SUN_SDK_ */
    883 	return result;
    884     }
    885 
    886     /* We require that this function is implemented */
    887     if(!plug->auxprop_lookup) return SASL_BADPROT;
    888 
    889 #ifdef _SUN_SDK_
    890     /* Check plugin to make sure name is non-NULL */
    891     if (plug->name == NULL) {
    892 	__sasl_log(gctx, gctx->server_global_callbacks.callbacks,
    893 		SASL_LOG_ERR, "invalid auxprop plugin %s", plugname);
    894 	return SASL_BADPROT;
    895     }
    896 #endif /* _SUN_SDK_ */
    897 
    898     new_item = sasl_ALLOC(sizeof(auxprop_plug_list_t));
    899     if(!new_item) return SASL_NOMEM;
    900 
    901 #ifdef _SUN_SDK_
    902     if(_sasl_strdup(plugname, &new_item->plugname, NULL) != SASL_OK) {
    903 	sasl_FREE(new_item);
    904 	return SASL_NOMEM;
    905     }
    906 #endif /* _SUN_SDK_ */
    907     /* These will load from least-important to most important */
    908     new_item->plug = plug;
    909     new_item->next = auxprop_head;
    910 #ifdef _SUN_SDK_
    911     gctx->auxprop_head = new_item;
    912 #else
    913     auxprop_head = new_item;
    914 #endif /* _SUN_SDK_ */
    915 
    916     return SASL_OK;
    917 }
    918 
    919 #ifdef _SUN_SDK_
    920 void _sasl_auxprop_free(_sasl_global_context_t *gctx)
    921 #else
    922 void _sasl_auxprop_free()
    923 #endif /* _SUN_SDK_ */
    924 {
    925     auxprop_plug_list_t *ptr, *ptr_next;
    926 #ifdef _SUN_SDK_
    927     const sasl_utils_t *sasl_global_utils = gctx->sasl_server_global_utils;
    928 
    929     for(ptr = (auxprop_plug_list_t *)gctx->auxprop_head; ptr; ptr = ptr_next) {
    930 #else
    931 
    932     for(ptr = auxprop_head; ptr; ptr = ptr_next) {
    933 #endif /* _SUN_SDK_ */
    934 	ptr_next = ptr->next;
    935 	if(ptr->plug->auxprop_free)
    936 	    ptr->plug->auxprop_free(ptr->plug->glob_context,
    937 				    sasl_global_utils);
    938 #ifdef _SUN_SDK_
    939 	sasl_FREE(ptr->plugname);
    940 #endif /* _SUN_SDK_ */
    941 	sasl_FREE(ptr);
    942     }
    943 
    944 #ifdef _SUN_SDK_
    945     gctx->auxprop_head = NULL;
    946 #else
    947     auxprop_head = NULL;
    948 #endif /* _SUN_SDK_ */
    949 }
    950 
    951 
    952 /* Do the callbacks for auxprop lookups */
    953 void _sasl_auxprop_lookup(sasl_server_params_t *sparams,
    954 			  unsigned flags,
    955 			  const char *user, unsigned ulen)
    956 {
    957     sasl_getopt_t *getopt;
    958     int ret, found = 0;
    959     void *context;
    960     const char *plist = NULL;
    961     auxprop_plug_list_t *ptr;
    962 #ifdef _SUN_SDK_
    963     _sasl_global_context_t *gctx = sparams->utils->conn->gctx;
    964     auxprop_plug_list_t *auxprop_head = gctx->auxprop_head;
    965 #endif /* _SUN_SDK_ */
    966 
    967     if(_sasl_getcallback(sparams->utils->conn,
    968 			 SASL_CB_GETOPT, &getopt, &context) == SASL_OK) {
    969 	ret = getopt(context, NULL, "auxprop_plugin", &plist, NULL);
    970 	if(ret != SASL_OK) plist = NULL;
    971     }
    972 
    973     if(!plist) {
    974 	/* Do lookup in all plugins */
    975 	for(ptr = auxprop_head; ptr; ptr = ptr->next) {
    976 	    found=1;
    977 	    ptr->plug->auxprop_lookup(ptr->plug->glob_context,
    978 				      sparams, flags, user, ulen);
    979 	}
    980     } else {
    981 	char *pluginlist = NULL, *freeptr = NULL, *thisplugin = NULL;
    982 
    983 	if(_sasl_strdup(plist, &pluginlist, NULL) != SASL_OK) return;
    984 	thisplugin = freeptr = pluginlist;
    985 
    986 	/* Do lookup in all *specified* plugins, in order */
    987 	while(*thisplugin) {
    988 	    char *p;
    989 	    int last=0;
    990 
    991 	    while(*thisplugin && isspace((int)*thisplugin)) thisplugin++;
    992 	    if(!(*thisplugin)) break;
    993 
    994 	    for(p = thisplugin;*p != '\0' && !isspace((int)*p); p++);
    995 	    if(*p == '\0') last = 1;
    996 	    else *p='\0';
    997 
    998 	    for(ptr = auxprop_head; ptr; ptr = ptr->next) {
    999 		/* Skip non-matching plugins */
   1000 		if(!ptr->plug->name
   1001 		   || strcasecmp(ptr->plug->name, thisplugin))
   1002 		    continue;
   1003 
   1004 		found=1;
   1005 		ptr->plug->auxprop_lookup(ptr->plug->glob_context,
   1006 					  sparams, flags, user, ulen);
   1007 	    }
   1008 
   1009 	    if(last) break;
   1010 
   1011 	    thisplugin = p+1;
   1012 	}
   1013 
   1014 	sasl_FREE(freeptr);
   1015     }
   1016 
   1017     if(!found)
   1018 	_sasl_log(sparams->utils->conn, SASL_LOG_DEBUG,
   1019 		  "could not find auxprop plugin, was searching for '%s'",
   1020 		  plist ? plist : "[all]");
   1021 }
   1022