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