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