1/*
2 * Copyright 2006 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
48static void free_disptmpl( struct ldap_disptmpl *tmpl );
49static int read_next_tmpl( char **bufp, long *blenp,
50	struct ldap_disptmpl **tmplp, int dtversion );
51
52static char		*tmploptions[] = {
53    "addable", "modrdn",
54    "altview",
55    NULL
56};
57
58
59static unsigned long	tmploptvals[] = {
60    LDAP_DTMPL_OPT_ADDABLE, LDAP_DTMPL_OPT_ALLOWMODRDN,
61    LDAP_DTMPL_OPT_ALTVIEW,
62};
63
64
65static 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
75static 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
85static char		*itemoptions[] = {
86    "ro",		       		"sort",
87    "1val",				"hide",
88    "required",				"hideiffalse",
89    NULL
90};
91
92
93static 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
104int
105LDAP_CALL
106ldap_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, "rF" )) == 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
152int
153LDAP_CALL
154ldap_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
193void
194LDAP_CALL
195ldap_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
208static void
209free_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
287struct ldap_disptmpl *
288LDAP_CALL
289ldap_first_disptmpl( struct ldap_disptmpl *tmpllist )
290{
291    return( tmpllist );
292}
293
294
295struct ldap_disptmpl *
296LDAP_CALL
297ldap_next_disptmpl( struct ldap_disptmpl *tmpllist,
298	struct ldap_disptmpl *tmpl )
299{
300    return( tmpl == NULLDISPTMPL ? tmpl : tmpl->dt_next );
301}
302
303
304struct ldap_disptmpl *
305LDAP_CALL
306ldap_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
321struct ldap_disptmpl *
322LDAP_CALL
323ldap_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
358struct ldap_tmplitem *
359LDAP_CALL
360ldap_first_tmplrow( struct ldap_disptmpl *tmpl )
361{
362    return( tmpl->dt_items );
363}
364
365
366struct ldap_tmplitem *
367LDAP_CALL
368ldap_next_tmplrow( struct ldap_disptmpl *tmpl, struct ldap_tmplitem *row )
369{
370    return( row == NULLTMPLITEM ? row : row->ti_next_in_col );
371}
372
373
374struct ldap_tmplitem *
375LDAP_CALL
376ldap_first_tmplcol( struct ldap_disptmpl *tmpl, struct ldap_tmplitem *row )
377{
378    return( row );
379}
380
381
382struct ldap_tmplitem *
383LDAP_CALL
384ldap_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
391char **
392LDAP_CALL
393ldap_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
465static int
466read_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
738struct tmplerror {
739	int	e_code;
740	char	*e_reason;
741};
742
743#ifdef SUN
744static 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
752static 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
761char *
762LDAP_CALL
763ldap_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