xref: /illumos-gate/usr/src/lib/libldap5/sources/ldap/common/disptmpl.c (revision ddb365bfc9e868ad24ccdcb0dc91af18b10df082)
1 /*
2  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 /*
7  * The contents of this file are subject to the Netscape Public
8  * License Version 1.1 (the "License"); you may not use this file
9  * except in compliance with the License. You may obtain a copy of
10  * the License at http://www.mozilla.org/NPL/
11  *
12  * Software distributed under the License is distributed on an "AS
13  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
14  * implied. See the License for the specific language governing
15  * rights and limitations under the License.
16  *
17  * The Original Code is Mozilla Communicator client code, released
18  * March 31, 1998.
19  *
20  * The Initial Developer of the Original Code is Netscape
21  * Communications Corporation. Portions created by Netscape are
22  * Copyright (C) 1998-1999 Netscape Communications Corporation. All
23  * Rights Reserved.
24  *
25  * Contributor(s):
26  */
27 /*
28  * Copyright (c) 1993, 1994 Regents of the University of Michigan.
29  * All rights reserved.
30  *
31  * Redistribution and use in source and binary forms are permitted
32  * provided that this notice is preserved and that due credit is given
33  * to the University of Michigan at Ann Arbor. The name of the University
34  * may not be used to endorse or promote products derived from this
35  * software without specific prior written permission. This software
36  * is provided ``as is'' without express or implied warranty.
37  */
38 /*
39  * disptmpl.c:  display template library routines for LDAP clients
40  */
41 
42 #include "ldap-int.h"
43 #include "disptmpl.h"
44 
45 static void free_disptmpl( struct ldap_disptmpl *tmpl );
46 static int read_next_tmpl( char **bufp, long *blenp,
47 	struct ldap_disptmpl **tmplp, int dtversion );
48 
49 static char		*tmploptions[] = {
50     "addable", "modrdn",
51     "altview",
52     NULL
53 };
54 
55 
56 static unsigned long	tmploptvals[] = {
57     LDAP_DTMPL_OPT_ADDABLE, LDAP_DTMPL_OPT_ALLOWMODRDN,
58     LDAP_DTMPL_OPT_ALTVIEW,
59 };
60 
61 
62 static char		*itemtypes[] = {
63     "cis",			"mls",			"dn",
64     "bool",			"jpeg",			"jpegbtn",
65     "fax",			"faxbtn",		"audiobtn",
66     "time",			"date",			"url",
67     "searchact",		"linkact",		"adddnact",
68     "addact",			"verifyact",		"mail",
69     NULL
70 };
71 
72 static unsigned long	itemsynids[] = {
73     LDAP_SYN_CASEIGNORESTR,	LDAP_SYN_MULTILINESTR,	LDAP_SYN_DN,
74     LDAP_SYN_BOOLEAN,		LDAP_SYN_JPEGIMAGE,	LDAP_SYN_JPEGBUTTON,
75     LDAP_SYN_FAXIMAGE,		LDAP_SYN_FAXBUTTON,	LDAP_SYN_AUDIOBUTTON,
76     LDAP_SYN_TIME,		LDAP_SYN_DATE,		LDAP_SYN_LABELEDURL,
77     LDAP_SYN_SEARCHACTION,	LDAP_SYN_LINKACTION,	LDAP_SYN_ADDDNACTION,
78     LDAP_SYN_ADDDNACTION,	LDAP_SYN_VERIFYDNACTION,LDAP_SYN_RFC822ADDR,
79 };
80 
81 
82 static char		*itemoptions[] = {
83     "ro",		       		"sort",
84     "1val",				"hide",
85     "required",				"hideiffalse",
86     NULL
87 };
88 
89 
90 static unsigned long	itemoptvals[] = {
91     LDAP_DITEM_OPT_READONLY,		LDAP_DITEM_OPT_SORTVALUES,
92     LDAP_DITEM_OPT_SINGLEVALUED,	LDAP_DITEM_OPT_HIDEIFEMPTY,
93     LDAP_DITEM_OPT_VALUEREQUIRED,	LDAP_DITEM_OPT_HIDEIFFALSE,
94 };
95 
96 
97 #define ADDEF_CONSTANT	"constant"
98 #define ADDEF_ADDERSDN	"addersdn"
99 
100 
101 int
102 LDAP_CALL
103 ldap_init_templates( char *file, struct ldap_disptmpl **tmpllistp )
104 {
105     FILE	*fp;
106     char	*buf;
107     long	rlen, len;
108     int		rc, eof;
109 
110     *tmpllistp = NULLDISPTMPL;
111 
112     if (( fp = fopen( file, "rF" )) == NULL ) {
113 	return( LDAP_TMPL_ERR_FILE );
114     }
115 
116     if ( fseek( fp, 0L, SEEK_END ) != 0 ) {	/* move to end to get len */
117 	fclose( fp );
118 	return( LDAP_TMPL_ERR_FILE );
119     }
120 
121     len = ftell( fp );
122 
123     if ( fseek( fp, 0L, SEEK_SET ) != 0 ) {	/* back to start of file */
124 	fclose( fp );
125 	return( LDAP_TMPL_ERR_FILE );
126     }
127 
128     if (( buf = NSLDAPI_MALLOC( (size_t)len )) == NULL ) {
129 	fclose( fp );
130 	return( LDAP_TMPL_ERR_MEM );
131     }
132 
133     rlen = fread( buf, 1, (size_t)len, fp );
134     eof = feof( fp );
135     fclose( fp );
136 
137     if ( rlen != len && !eof ) {	/* error:  didn't get the whole file */
138 	NSLDAPI_FREE( buf );
139 	return( LDAP_TMPL_ERR_FILE );
140     }
141 
142     rc = ldap_init_templates_buf( buf, rlen, tmpllistp );
143     NSLDAPI_FREE( buf );
144 
145     return( rc );
146 }
147 
148 
149 int
150 LDAP_CALL
151 ldap_init_templates_buf( char *buf, long buflen,
152 	struct ldap_disptmpl **tmpllistp )
153 {
154     int				rc = 0, version;
155     char			**toks;
156     struct ldap_disptmpl	*prevtmpl, *tmpl;
157 
158     *tmpllistp = prevtmpl = NULLDISPTMPL;
159 
160     if ( ldap_next_line_tokens( &buf, &buflen, &toks ) != 2 ||
161 	    strcasecmp( toks[ 0 ], "version" ) != 0 ) {
162 	ldap_free_strarray( toks );
163 	return( LDAP_TMPL_ERR_SYNTAX );
164     }
165     version = atoi( toks[ 1 ] );
166     ldap_free_strarray( toks );
167     if ( version != LDAP_TEMPLATE_VERSION ) {
168 	return( LDAP_TMPL_ERR_VERSION );
169     }
170 
171     while ( buflen > 0 && ( rc = read_next_tmpl( &buf, &buflen, &tmpl,
172 	    version )) == 0 && tmpl != NULLDISPTMPL ) {
173 	if ( prevtmpl == NULLDISPTMPL ) {
174 	    *tmpllistp = tmpl;
175 	} else {
176 	    prevtmpl->dt_next = tmpl;
177 	}
178 	prevtmpl = tmpl;
179     }
180 
181     if ( rc != 0 ) {
182 	ldap_free_templates( *tmpllistp );
183     }
184 
185     return( rc );
186 }
187 
188 
189 
190 void
191 LDAP_CALL
192 ldap_free_templates( struct ldap_disptmpl *tmpllist )
193 {
194     struct ldap_disptmpl	*tp, *nexttp;
195 
196     if ( tmpllist != NULL ) {
197 	for ( tp = tmpllist; tp != NULL; tp = nexttp ) {
198 	    nexttp = tp->dt_next;
199 	    free_disptmpl( tp );
200 	}
201     }
202 }
203 
204 
205 static void
206 free_disptmpl( struct ldap_disptmpl *tmpl )
207 {
208     if ( tmpl != NULL ) {
209 	if ( tmpl->dt_name != NULL ) {
210 	    NSLDAPI_FREE(  tmpl->dt_name );
211 	}
212 
213 	if ( tmpl->dt_pluralname != NULL ) {
214 	    NSLDAPI_FREE( tmpl->dt_pluralname );
215 	}
216 
217 	if ( tmpl->dt_iconname != NULL ) {
218 	    NSLDAPI_FREE( tmpl->dt_iconname );
219 	}
220 
221 	if ( tmpl->dt_authattrname != NULL ) {
222 	    NSLDAPI_FREE( tmpl->dt_authattrname );
223 	}
224 
225 	if ( tmpl->dt_defrdnattrname != NULL ) {
226 	    NSLDAPI_FREE( tmpl->dt_defrdnattrname );
227 	}
228 
229 	if ( tmpl->dt_defaddlocation != NULL ) {
230 	    NSLDAPI_FREE( tmpl->dt_defaddlocation );
231 	}
232 
233 	if (  tmpl->dt_oclist != NULL ) {
234 	    struct ldap_oclist	*ocp, *nextocp;
235 
236 	    for ( ocp = tmpl->dt_oclist; ocp != NULL; ocp = nextocp ) {
237 		nextocp = ocp->oc_next;
238 		ldap_free_strarray( ocp->oc_objclasses );
239 		NSLDAPI_FREE( ocp );
240 	    }
241 	}
242 
243 	if (  tmpl->dt_adddeflist != NULL ) {
244 	    struct ldap_adddeflist	*adp, *nextadp;
245 
246 	    for ( adp = tmpl->dt_adddeflist; adp != NULL; adp = nextadp ) {
247 		nextadp = adp->ad_next;
248 		if( adp->ad_attrname != NULL ) {
249 		    NSLDAPI_FREE( adp->ad_attrname );
250 		}
251 		if( adp->ad_value != NULL ) {
252 		    NSLDAPI_FREE( adp->ad_value );
253 		}
254 		NSLDAPI_FREE( adp );
255 	    }
256 	}
257 
258 	if (  tmpl->dt_items != NULL ) {
259 	    struct ldap_tmplitem	*rowp, *nextrowp, *colp, *nextcolp;
260 
261 	    for ( rowp = tmpl->dt_items; rowp != NULL; rowp = nextrowp ) {
262 		nextrowp = rowp->ti_next_in_col;
263 		for ( colp = rowp; colp != NULL; colp = nextcolp ) {
264 		    nextcolp = colp->ti_next_in_row;
265 		    if ( colp->ti_attrname != NULL ) {
266 			NSLDAPI_FREE( colp->ti_attrname );
267 		    }
268 		    if ( colp->ti_label != NULL ) {
269 			NSLDAPI_FREE( colp->ti_label );
270 		    }
271 		    if ( colp->ti_args != NULL ) {
272 			ldap_free_strarray( colp->ti_args );
273 		    }
274 		    NSLDAPI_FREE( colp );
275 		}
276 	    }
277 	}
278 
279 	NSLDAPI_FREE( tmpl );
280     }
281 }
282 
283 
284 struct ldap_disptmpl *
285 LDAP_CALL
286 ldap_first_disptmpl( struct ldap_disptmpl *tmpllist )
287 {
288     return( tmpllist );
289 }
290 
291 
292 struct ldap_disptmpl *
293 LDAP_CALL
294 ldap_next_disptmpl( struct ldap_disptmpl *tmpllist,
295 	struct ldap_disptmpl *tmpl )
296 {
297     return( tmpl == NULLDISPTMPL ? tmpl : tmpl->dt_next );
298 }
299 
300 
301 struct ldap_disptmpl *
302 LDAP_CALL
303 ldap_name2template( char *name, struct ldap_disptmpl *tmpllist )
304 {
305     struct ldap_disptmpl	*dtp;
306 
307     for ( dtp = ldap_first_disptmpl( tmpllist ); dtp != NULLDISPTMPL;
308 	    dtp = ldap_next_disptmpl( tmpllist, dtp )) {
309 	if ( strcasecmp( name, dtp->dt_name ) == 0 ) {
310 	    return( dtp );
311 	}
312     }
313 
314     return( NULLDISPTMPL );
315 }
316 
317 
318 struct ldap_disptmpl *
319 LDAP_CALL
320 ldap_oc2template( char **oclist, struct ldap_disptmpl *tmpllist )
321 {
322     struct ldap_disptmpl	*dtp;
323     struct ldap_oclist		*oclp;
324     int				i, j, needcnt, matchcnt;
325 
326     if ( tmpllist == NULL || oclist == NULL || oclist[ 0 ] == NULL ) {
327 	return( NULLDISPTMPL );
328     }
329 
330     for ( dtp = ldap_first_disptmpl( tmpllist ); dtp != NULLDISPTMPL;
331 		dtp = ldap_next_disptmpl( tmpllist, dtp )) {
332 	for ( oclp = dtp->dt_oclist; oclp != NULLOCLIST;
333 		oclp = oclp->oc_next ) {
334 	    needcnt = matchcnt = 0;
335 	    for ( i = 0; oclp->oc_objclasses[ i ] != NULL; ++i ) {
336 		for ( j = 0; oclist[ j ] != NULL; ++j ) {
337 		    if ( strcasecmp( oclist[ j ], oclp->oc_objclasses[ i ] )
338 			    == 0 ) {
339 			++matchcnt;
340 		    }
341 		}
342 		++needcnt;
343 	    }
344 
345 	    if ( matchcnt == needcnt ) {
346 		return( dtp );
347 	    }
348 	}
349     }
350 
351     return( NULLDISPTMPL );
352 }
353 
354 
355 struct ldap_tmplitem *
356 LDAP_CALL
357 ldap_first_tmplrow( struct ldap_disptmpl *tmpl )
358 {
359     return( tmpl->dt_items );
360 }
361 
362 
363 struct ldap_tmplitem *
364 LDAP_CALL
365 ldap_next_tmplrow( struct ldap_disptmpl *tmpl, struct ldap_tmplitem *row )
366 {
367     return( row == NULLTMPLITEM ? row : row->ti_next_in_col );
368 }
369 
370 
371 struct ldap_tmplitem *
372 LDAP_CALL
373 ldap_first_tmplcol( struct ldap_disptmpl *tmpl, struct ldap_tmplitem *row )
374 {
375     return( row );
376 }
377 
378 
379 struct ldap_tmplitem *
380 LDAP_CALL
381 ldap_next_tmplcol( struct ldap_disptmpl *tmpl, struct ldap_tmplitem *row,
382 	struct ldap_tmplitem *col )
383 {
384     return( col == NULLTMPLITEM ? col : col->ti_next_in_row );
385 }
386 
387 
388 char **
389 LDAP_CALL
390 ldap_tmplattrs( struct ldap_disptmpl *tmpl, char **includeattrs,
391 	int exclude, unsigned long syntaxmask )
392 {
393 /*
394  * this routine should filter out duplicate attributes...
395  */
396     struct ldap_tmplitem	*tirowp, *ticolp;
397     int			i, attrcnt, memerr;
398     char		**attrs;
399 
400     attrcnt = 0;
401     memerr = 0;
402 
403     if (( attrs = (char **)NSLDAPI_MALLOC( sizeof( char * ))) == NULL ) {
404 	return( NULL );
405     }
406 
407     if ( includeattrs != NULL ) {
408 	for ( i = 0; !memerr && includeattrs[ i ] != NULL; ++i ) {
409 	    if (( attrs = (char **)NSLDAPI_REALLOC( attrs, ( attrcnt + 2 ) *
410 		    sizeof( char * ))) == NULL || ( attrs[ attrcnt++ ] =
411 		    nsldapi_strdup( includeattrs[ i ] )) == NULL ) {
412 		memerr = 1;
413 	    } else {
414 		attrs[ attrcnt ] = NULL;
415 	    }
416 	}
417     }
418 
419     for ( tirowp = ldap_first_tmplrow( tmpl );
420 	    !memerr && tirowp != NULLTMPLITEM;
421 	    tirowp = ldap_next_tmplrow( tmpl, tirowp )) {
422 	for ( ticolp = ldap_first_tmplcol( tmpl, tirowp );
423 		ticolp != NULLTMPLITEM;
424 		ticolp = ldap_next_tmplcol( tmpl, tirowp, ticolp )) {
425 
426 	    if ( syntaxmask != 0 ) {
427 		if (( exclude &&
428 			( syntaxmask & ticolp->ti_syntaxid ) != 0 ) ||
429 			( !exclude &&
430 			( syntaxmask & ticolp->ti_syntaxid ) == 0 )) {
431 		    continue;
432 		}
433 	    }
434 
435 	    if ( ticolp->ti_attrname != NULL ) {
436 		if (( attrs = (char **)NSLDAPI_REALLOC( attrs, ( attrcnt + 2 )
437 			* sizeof( char * ))) == NULL || ( attrs[ attrcnt++ ] =
438 			nsldapi_strdup( ticolp->ti_attrname )) == NULL ) {
439 		    memerr = 1;
440 		} else {
441 		    attrs[ attrcnt ] = NULL;
442 		}
443 	    }
444 	}
445     }
446 
447     if ( memerr || attrcnt == 0 ) {
448 	for ( i = 0; i < attrcnt; ++i ) {
449 	    if ( attrs[ i ] != NULL ) {
450 		NSLDAPI_FREE( attrs[ i ] );
451 	    }
452 	}
453 
454 	NSLDAPI_FREE( (char *)attrs );
455 	return( NULL );
456     }
457 
458     return( attrs );
459 }
460 
461 
462 static int
463 read_next_tmpl( char **bufp, long *blenp, struct ldap_disptmpl **tmplp,
464 	int dtversion )
465 {
466     int				i, j, tokcnt, samerow, adsource;
467     char			**toks, *itemopts;
468     struct ldap_disptmpl	*tmpl = NULL;
469     struct ldap_oclist		*ocp = NULL, *prevocp = NULL;
470     struct ldap_adddeflist	*adp = NULL, *prevadp = NULL;
471     struct ldap_tmplitem	*rowp = NULL, *ip = NULL, *previp = NULL;
472 
473     /*
474      * template name comes first
475      */
476     if (( tokcnt = ldap_next_line_tokens( bufp, blenp, &toks )) != 1 ) {
477 	ldap_free_strarray( toks );
478 	return( tokcnt == 0 ? 0 : LDAP_TMPL_ERR_SYNTAX );
479     }
480 
481     if (( tmpl = (struct ldap_disptmpl *)NSLDAPI_CALLOC( 1,
482 	    sizeof( struct ldap_disptmpl ))) == NULL ) {
483 	ldap_free_strarray( toks );
484 	return(  LDAP_TMPL_ERR_MEM );
485     }
486     tmpl->dt_name = toks[ 0 ];
487     NSLDAPI_FREE( (char *)toks );
488 
489     /*
490      * template plural name comes next
491      */
492     if (( tokcnt = ldap_next_line_tokens( bufp, blenp, &toks )) != 1 ) {
493 	ldap_free_strarray( toks );
494 	free_disptmpl( tmpl );
495 	return( LDAP_TMPL_ERR_SYNTAX );
496     }
497     tmpl->dt_pluralname = toks[ 0 ];
498     NSLDAPI_FREE( (char *)toks );
499 
500     /*
501      * template icon name is next
502      */
503     if (( tokcnt = ldap_next_line_tokens( bufp, blenp, &toks )) != 1 ) {
504 	ldap_free_strarray( toks );
505 	free_disptmpl( tmpl );
506 	return( LDAP_TMPL_ERR_SYNTAX );
507     }
508     tmpl->dt_iconname = toks[ 0 ];
509     NSLDAPI_FREE( (char *)toks );
510 
511     /*
512      * template options come next
513      */
514     if (( tokcnt = ldap_next_line_tokens( bufp, blenp, &toks )) < 1 ) {
515 	ldap_free_strarray( toks );
516 	free_disptmpl( tmpl );
517 	return( LDAP_TMPL_ERR_SYNTAX );
518     }
519     for ( i = 0; toks[ i ] != NULL; ++i ) {
520 	for ( j = 0; tmploptions[ j ] != NULL; ++j ) {
521 	    if ( strcasecmp( toks[ i ], tmploptions[ j ] ) == 0 ) {
522 		tmpl->dt_options |= tmploptvals[ j ];
523 	    }
524 	}
525     }
526     ldap_free_strarray( toks );
527 
528     /*
529      * object class list is next
530      */
531     while (( tokcnt = ldap_next_line_tokens( bufp, blenp, &toks )) > 0 ) {
532 	if (( ocp = (struct ldap_oclist *)NSLDAPI_CALLOC( 1,
533 		sizeof( struct ldap_oclist ))) == NULL ) {
534 	    ldap_free_strarray( toks );
535 	    free_disptmpl( tmpl );
536 	    return( LDAP_TMPL_ERR_MEM );
537 	}
538 	ocp->oc_objclasses = toks;
539 	if ( tmpl->dt_oclist == NULL ) {
540 	    tmpl->dt_oclist = ocp;
541 	} else {
542 	    prevocp->oc_next = ocp;
543 	}
544 	prevocp = ocp;
545     }
546     if ( tokcnt < 0 ) {
547 	free_disptmpl( tmpl );
548 	return( LDAP_TMPL_ERR_SYNTAX );
549     }
550 
551     /*
552      * read name of attribute to authenticate as
553      */
554     if (( tokcnt = ldap_next_line_tokens( bufp, blenp, &toks )) != 1 ) {
555 	ldap_free_strarray( toks );
556 	free_disptmpl( tmpl );
557 	return( LDAP_TMPL_ERR_SYNTAX );
558     }
559     if ( toks[ 0 ][ 0 ] != '\0' ) {
560 	tmpl->dt_authattrname = toks[ 0 ];
561     } else {
562 	NSLDAPI_FREE( toks[ 0 ] );
563     }
564     NSLDAPI_FREE( (char *)toks );
565 
566     /*
567      * read default attribute to use for RDN
568      */
569     if (( tokcnt = ldap_next_line_tokens( bufp, blenp, &toks )) != 1 ) {
570 	ldap_free_strarray( toks );
571 	free_disptmpl( tmpl );
572 	return( LDAP_TMPL_ERR_SYNTAX );
573     }
574     tmpl->dt_defrdnattrname = toks[ 0 ];
575     NSLDAPI_FREE( (char *)toks );
576 
577     /*
578      * read default location for new entries
579      */
580     if (( tokcnt = ldap_next_line_tokens( bufp, blenp, &toks )) != 1 ) {
581 	ldap_free_strarray( toks );
582 	free_disptmpl( tmpl );
583 	return( LDAP_TMPL_ERR_SYNTAX );
584     }
585     if ( toks[ 0 ][ 0 ] != '\0' ) {
586 	tmpl->dt_defaddlocation = toks[ 0 ];
587     } else {
588 	NSLDAPI_FREE( toks[ 0 ] );
589     }
590     NSLDAPI_FREE( (char *)toks );
591 
592     /*
593      * read list of rules used to define default values for new entries
594      */
595     while (( tokcnt = ldap_next_line_tokens( bufp, blenp, &toks )) > 0 ) {
596 	if ( strcasecmp( ADDEF_CONSTANT, toks[ 0 ] ) == 0 ) {
597 	    adsource = LDAP_ADSRC_CONSTANTVALUE;
598 	} else if ( strcasecmp( ADDEF_ADDERSDN, toks[ 0 ] ) == 0 ) {
599 	    adsource = LDAP_ADSRC_ADDERSDN;
600 	} else {
601 	    adsource = 0;
602 	}
603 	if ( adsource == 0 || tokcnt < 2 ||
604 		( adsource == LDAP_ADSRC_CONSTANTVALUE && tokcnt != 3 ) ||
605 		( adsource == LDAP_ADSRC_ADDERSDN && tokcnt != 2 )) {
606 	    ldap_free_strarray( toks );
607 	    free_disptmpl( tmpl );
608 	    return( LDAP_TMPL_ERR_SYNTAX );
609 	}
610 
611 	if (( adp = (struct ldap_adddeflist *)NSLDAPI_CALLOC( 1,
612 		sizeof( struct ldap_adddeflist ))) == NULL ) {
613 	    ldap_free_strarray( toks );
614 	    free_disptmpl( tmpl );
615 	    return( LDAP_TMPL_ERR_MEM );
616 	}
617 	adp->ad_source = adsource;
618 	adp->ad_attrname = toks[ 1 ];
619 	if ( adsource == LDAP_ADSRC_CONSTANTVALUE ) {
620 	    adp->ad_value = toks[ 2 ];
621 	}
622 	NSLDAPI_FREE( toks[ 0 ] );
623 	NSLDAPI_FREE( (char *)toks );
624 
625 	if ( tmpl->dt_adddeflist == NULL ) {
626 	    tmpl->dt_adddeflist = adp;
627 	} else {
628 	    prevadp->ad_next = adp;
629 	}
630 	prevadp = adp;
631     }
632 
633     /*
634      * item list is next
635      */
636     samerow = 0;
637     while (( tokcnt = ldap_next_line_tokens( bufp, blenp, &toks )) > 0 ) {
638 	if ( strcasecmp( toks[ 0 ], "item" ) == 0 ) {
639 	    if ( tokcnt < 4 ) {
640 		ldap_free_strarray( toks );
641 		free_disptmpl( tmpl );
642 		return( LDAP_TMPL_ERR_SYNTAX );
643 	    }
644 
645 	    if (( ip = (struct ldap_tmplitem *)NSLDAPI_CALLOC( 1,
646 		    sizeof( struct ldap_tmplitem ))) == NULL ) {
647 		ldap_free_strarray( toks );
648 		free_disptmpl( tmpl );
649 		return( LDAP_TMPL_ERR_MEM );
650 	    }
651 
652 	    /*
653 	     * find syntaxid from config file string
654 	     */
655 	    while (( itemopts = strrchr( toks[ 1 ], ',' )) != NULL ) {
656 		*itemopts++ = '\0';
657 		for ( i = 0; itemoptions[ i ] != NULL; ++i ) {
658 		    if ( strcasecmp( itemopts, itemoptions[ i ] ) == 0 ) {
659 			break;
660 		    }
661 		}
662 		if ( itemoptions[ i ] == NULL ) {
663 		    ldap_free_strarray( toks );
664 		    free_disptmpl( tmpl );
665 		    return( LDAP_TMPL_ERR_SYNTAX );
666 		}
667 		ip->ti_options |= itemoptvals[ i ];
668 	    }
669 
670 	    for ( i = 0; itemtypes[ i ] != NULL; ++i ) {
671 		if ( strcasecmp( toks[ 1 ], itemtypes[ i ] ) == 0 ) {
672 		    break;
673 		}
674 	    }
675 	    if ( itemtypes[ i ] == NULL ) {
676 		ldap_free_strarray( toks );
677 		free_disptmpl( tmpl );
678 		return( LDAP_TMPL_ERR_SYNTAX );
679 	    }
680 
681 	    NSLDAPI_FREE( toks[ 0 ] );
682 	    NSLDAPI_FREE( toks[ 1 ] );
683 	    ip->ti_syntaxid = itemsynids[ i ];
684 	    ip->ti_label = toks[ 2 ];
685 	    if ( toks[ 3 ][ 0 ] == '\0' ) {
686 		ip->ti_attrname = NULL;
687 		NSLDAPI_FREE( toks[ 3 ] );
688 	    } else {
689 		ip->ti_attrname = toks[ 3 ];
690 	    }
691 	    if ( toks[ 4 ] != NULL ) {	/* extra args. */
692 		for ( i = 0; toks[ i + 4 ] != NULL; ++i ) {
693 		    ;
694 		}
695 		if (( ip->ti_args = (char **)NSLDAPI_CALLOC( i + 1,
696 			sizeof( char * ))) == NULL ) {
697 		    free_disptmpl( tmpl );
698 		    return( LDAP_TMPL_ERR_MEM );
699 		}
700 		for ( i = 0; toks[ i + 4 ] != NULL; ++i ) {
701 		    ip->ti_args[ i ] = toks[ i + 4 ];
702 		}
703 	    }
704 	    NSLDAPI_FREE( (char *)toks );
705 
706 	    if ( tmpl->dt_items == NULL ) {
707 		tmpl->dt_items = rowp = ip;
708 	    } else if ( samerow ) {
709 		previp->ti_next_in_row = ip;
710 	    } else {
711 		rowp->ti_next_in_col = ip;
712 		rowp = ip;
713 	    }
714 	    previp = ip;
715 	    samerow = 0;
716 	} else if ( strcasecmp( toks[ 0 ], "samerow" ) == 0 ) {
717 	    ldap_free_strarray( toks );
718 	    samerow = 1;
719 	} else {
720 	    ldap_free_strarray( toks );
721 	    free_disptmpl( tmpl );
722 	    return( LDAP_TMPL_ERR_SYNTAX );
723 	}
724     }
725     if ( tokcnt < 0 ) {
726 	free_disptmpl( tmpl );
727 	return( LDAP_TMPL_ERR_SYNTAX );
728     }
729 
730     *tmplp = tmpl;
731     return( 0 );
732 }
733 
734 
735 struct tmplerror {
736 	int	e_code;
737 	char	*e_reason;
738 };
739 
740 #ifdef SUN
741 static struct tmplerror ldap_tmplerrlist[] = {
742 	{ LDAP_TMPL_ERR_VERSION, 0},
743 	{ LDAP_TMPL_ERR_MEM,     0},
744 	{ LDAP_TMPL_ERR_SYNTAX,  0},
745 	{ LDAP_TMPL_ERR_FILE,    0},
746 	{ -1, 0 }
747 };
748 #else
749 static struct tmplerror ldap_tmplerrlist[] = {
750 	{ LDAP_TMPL_ERR_VERSION, "Bad template version"		},
751 	{ LDAP_TMPL_ERR_MEM,     "Out of memory"		},
752 	{ LDAP_TMPL_ERR_SYNTAX,  "Bad template syntax"		},
753 	{ LDAP_TMPL_ERR_FILE,    "File error reading template"	},
754 	{ -1, 0 }
755 };
756 #endif
757 
758 char *
759 LDAP_CALL
760 ldap_tmplerr2string( int err )
761 {
762 	static int init_flag = 0;
763 	int	i;
764 
765 	/* Multiple threads should be ok since they assign same strings */
766 	if (init_flag == 0) {
767 		ldap_tmplerrlist[0].e_reason =
768 			dgettext(TEXT_DOMAIN, "Bad template version");
769 		ldap_tmplerrlist[1].e_reason =
770 			dgettext(TEXT_DOMAIN, "Out of memory");
771 		ldap_tmplerrlist[2].e_reason =
772 			dgettext(TEXT_DOMAIN, "Bad template syntax");
773 		ldap_tmplerrlist[3].e_reason =
774 			dgettext(TEXT_DOMAIN, "File error reading template");
775 		init_flag = 1;
776 	}
777 
778 	for ( i = 0; ldap_tmplerrlist[i].e_code != -1; i++ ) {
779 		if ( err == ldap_tmplerrlist[i].e_code )
780 			return( ldap_tmplerrlist[i].e_reason );
781 	}
782 
783 	return(dgettext(TEXT_DOMAIN, "Unknown error") );
784 }
785