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