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