]> git.sur5r.net Git - openldap/blob - libraries/libldap/tmplout.c
Fix typo in previous commit.
[openldap] / libraries / libldap / tmplout.c
1 /*
2  * Copyright 1998-1999 The OpenLDAP Foundation, All Rights Reserved.
3  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
4  */
5 /*
6  * tmplout.c:  display template library output routines for LDAP clients
7  * 12 April 1994 by Mark C Smith
8  */
9
10 #include "portable.h"
11
12 #include <stdio.h>
13 #include <stdlib.h>
14
15 #include <ac/ctype.h>
16 #include <ac/string.h>
17 #include <ac/time.h>
18
19 #ifdef HAVE_SYS_FILE_H
20 #include <sys/file.h>
21 #endif
22
23 #include "lber.h"
24 #include "ldap.h"
25
26 #include "ldap_log.h"
27 #include "disptmpl.h"
28
29 #include "ldap-int.h"
30 #include "ldapconfig.h"
31
32 /* local functions */
33 static int do_entry2text LDAP_P((
34         LDAP *ld, char *buf, char *base, LDAPMessage *entry,
35         struct ldap_disptmpl *tmpl, char **defattrs, char ***defvals,
36         writeptype writeproc, void *writeparm, char *eol, int rdncount,
37         unsigned long opts, char *urlprefix ));
38 static int do_entry2text_search LDAP_P((
39         LDAP *ld, char *dn, char *base,
40         LDAPMessage *entry, struct ldap_disptmpl *tmpllist, char **defattrs,
41         char ***defvals, writeptype writeproc, void *writeparm, char *eol,
42         int rdncount, unsigned long opts, char *urlprefix ));
43 static int do_vals2text LDAP_P((
44         LDAP *ld, char *buf, char **vals, char *label,
45         int labelwidth, unsigned long syntaxid, writeptype writeproc,
46         void *writeparm, char *eol, int rdncount, char *urlprefix ));
47 static int max_label_len LDAP_P(( struct ldap_disptmpl *tmpl ));
48 static int output_label LDAP_P((
49         char *buf, char *label, int width,
50         writeptype writeproc, void *writeparm, char *eol, int html ));
51 static int output_dn LDAP_P((
52         char *buf, char *dn, int width, int rdncount,
53         writeptype writeproc, void *writeparm, char *eol, char *urlprefix ));
54 static void strcat_escaped LDAP_P(( char *s1, char *s2 ));
55 static char *time2text LDAP_P(( char *ldtimestr, int dateonly ));
56 static long gtime LDAP_P(( struct tm *tm ));
57 static int searchaction LDAP_P((
58         LDAP *ld, char *buf, char *base, LDAPMessage *entry,
59         char *dn, struct ldap_tmplitem *tip, int labelwidth, int rdncount,
60         writeptype writeproc, void *writeparm, char *eol, char *urlprefix ));
61
62 #define DEF_LABEL_WIDTH         15
63 #define SEARCH_TIMEOUT_SECS     120
64 #define OCATTRNAME              "objectClass"
65
66
67 #define NONFATAL_LDAP_ERR( err )        ( err == LDAP_SUCCESS || \
68         err == LDAP_TIMELIMIT_EXCEEDED || err == LDAP_SIZELIMIT_EXCEEDED )
69
70 #define DEF_LDAP_URL_PREFIX     "ldap:///"
71
72  
73 int
74 ldap_entry2text(
75         LDAP                    *ld,
76         char                    *buf,           /* NULL for "use internal" */
77         LDAPMessage             *entry,
78         struct ldap_disptmpl    *tmpl,
79         char                    **defattrs,
80         char                    ***defvals,
81         writeptype              writeproc,
82         void                    *writeparm,
83         char                    *eol,
84         int                     rdncount,
85         unsigned long           opts
86 )
87 {
88     Debug( LDAP_DEBUG_TRACE, "ldap_entry2text\n", 0, 0, 0 );
89
90     return( do_entry2text( ld, buf, NULL, entry, tmpl, defattrs, defvals,
91                 writeproc, writeparm, eol, rdncount, opts, NULL ));
92
93 }
94
95
96
97 int
98 ldap_entry2html(
99         LDAP                    *ld,
100         char                    *buf,           /* NULL for "use internal" */
101         LDAPMessage             *entry,
102         struct ldap_disptmpl    *tmpl,
103         char                    **defattrs,
104         char                    ***defvals,
105         writeptype              writeproc,
106         void                    *writeparm,
107         char                    *eol,
108         int                     rdncount,
109         unsigned long           opts,
110         char                    *base,
111         char                    *urlprefix
112 )
113 {
114     Debug( LDAP_DEBUG_TRACE, "ldap_entry2html\n", 0, 0, 0 );
115
116     if ( urlprefix == NULL ) {
117         urlprefix = DEF_LDAP_URL_PREFIX;
118     }
119
120     return( do_entry2text( ld, buf, base, entry, tmpl, defattrs, defvals,
121                 writeproc, writeparm, eol, rdncount, opts, urlprefix ));
122 }
123
124
125 static int
126 do_entry2text(
127         LDAP                    *ld,
128         char                    *buf,           /* NULL for use-internal */
129         char                    *base,          /* used for search actions */
130         LDAPMessage             *entry,
131         struct ldap_disptmpl    *tmpl,
132         char                    **defattrs,
133         char                    ***defvals,
134         writeptype              writeproc,
135         void                    *writeparm,
136         char                    *eol,
137         int                     rdncount,
138         unsigned long           opts,
139         char                    *urlprefix      /* if non-NULL, do HTML */
140 )
141 {
142     int                         i, err, html, show, labelwidth;
143     int                         freebuf,  freevals;
144     char                        *dn, **vals;
145     struct ldap_tmplitem        *rowp, *colp;
146
147     if (( dn = ldap_get_dn( ld, entry )) == NULL ) {
148         return( ld->ld_errno );
149     }
150
151     if ( buf == NULL ) {
152         if (( buf = malloc( LDAP_DTMPL_BUFSIZ )) == NULL ) {
153             ld->ld_errno = LDAP_NO_MEMORY;
154             free( dn );
155             return( ld->ld_errno );
156         }
157         freebuf = 1;
158     } else {
159         freebuf = 0;
160     }
161
162     html = ( urlprefix != NULL );
163
164     if ( html ) {
165         /*
166          * add HTML intro. and title
167          */
168         if (!(( opts & LDAP_DISP_OPT_HTMLBODYONLY ) != 0 )) {
169             sprintf( buf, "<HTML>%s<HEAD>%s<TITLE>%s%s - ", eol, eol, eol,
170                     ( tmpl == NULL ) ? "Entry" : tmpl->dt_name );
171             (*writeproc)( writeparm, buf, strlen( buf ));
172             output_dn( buf, dn, 0, rdncount, writeproc, writeparm, "", NULL );
173             sprintf( buf, "%s</TITLE>%s</HEAD>%s<BODY>%s<H3>%s - ", eol, eol,
174                     eol, eol, ( tmpl == NULL ) ? "Entry" : tmpl->dt_name );
175             (*writeproc)( writeparm, buf, strlen( buf ));
176             output_dn( buf, dn, 0, rdncount, writeproc, writeparm, "", NULL );
177             sprintf( buf, "</H3>%s", eol );
178             (*writeproc)( writeparm, buf, strlen( buf ));
179         }
180
181         if (( opts & LDAP_DISP_OPT_NONLEAF ) != 0 &&
182                 ( vals = ldap_explode_dn( dn, 0 )) != NULL ) {
183             char        *untagged;
184
185             /*
186              * add "Move Up" link
187              */
188             sprintf( buf, "<A HREF=\"%s", urlprefix );
189             for ( i = 1; vals[ i ] != NULL; ++i ) {
190                 if ( i > 1 ) {
191                      strcat_escaped( buf, ", " );
192                 }
193                 strcat_escaped( buf, vals[ i ] );
194             }
195             if ( vals[ 1 ] != NULL ) {
196                 untagged = strchr( vals[ 1 ], '=' );
197             } else {
198                 untagged = "=The World";
199             }
200             sprintf( buf + strlen( buf ),
201                     "%s\">Move Up To <EM>%s</EM></A>%s<BR>",
202                     ( vals[ 1 ] == NULL ) ? "??one" : "",
203                     ( untagged != NULL ) ? untagged + 1 : vals[ 1 ], eol );
204             (*writeproc)( writeparm, buf, strlen( buf ));
205
206             /*
207              * add "Browse" link
208              */
209             untagged = strchr( vals[ 0 ], '=' );
210             sprintf( buf, "<A HREF=\"%s", urlprefix );
211             strcat_escaped( buf, dn );
212             sprintf( buf + strlen( buf ), "??one?(!(objectClass=dsa))\">Browse Below <EM>%s</EM></A>%s%s",
213                     ( untagged != NULL ) ? untagged + 1 : vals[ 0 ], eol, eol );
214             (*writeproc)( writeparm, buf, strlen( buf ));
215
216             ldap_value_free( vals );
217         }
218
219         (*writeproc)( writeparm, "<HR>", 4 );   /* horizontal rule */
220     } else {
221         (*writeproc)( writeparm, "\"", 1 );
222         output_dn( buf, dn, 0, rdncount, writeproc, writeparm, "", NULL );
223         sprintf( buf, "\"%s", eol );
224         (*writeproc)( writeparm, buf, strlen( buf ));
225     }
226
227     if ( tmpl != NULL && ( opts & LDAP_DISP_OPT_AUTOLABELWIDTH ) != 0 ) {
228         labelwidth = max_label_len( tmpl ) + 3;
229     } else {
230         labelwidth = DEF_LABEL_WIDTH;;
231     }
232
233     err = LDAP_SUCCESS;
234
235     if ( tmpl == NULL ) {
236         BerElement      *ber;
237         char            *attr;
238
239         ber = NULL;
240         for ( attr = ldap_first_attribute( ld, entry, &ber );
241                 NONFATAL_LDAP_ERR( err ) && attr != NULL;
242                 attr = ldap_next_attribute( ld, entry, ber )) {
243             if (( vals = ldap_get_values( ld, entry, attr )) == NULL ) {
244                 freevals = 0;
245                 if ( defattrs != NULL ) {
246                     for ( i = 0; defattrs[ i ] != NULL; ++i ) {
247                         if ( strcasecmp( attr, defattrs[ i ] ) == 0 ) {
248                             break;
249                         }
250                     }
251                     if ( defattrs[ i ] != NULL ) {
252                         vals = defvals[ i ];
253                     }
254                 }
255             } else {
256                 freevals = 1;
257             }
258
259             *attr = TOUPPER( (unsigned char) *attr );
260
261             err = do_vals2text( ld, buf, vals, attr, labelwidth,
262                     LDAP_SYN_CASEIGNORESTR, writeproc, writeparm, eol, 
263                     rdncount, urlprefix );
264             if ( freevals ) {
265                 ldap_value_free( vals );
266             }
267         }
268
269         if( ber != NULL) {
270                 ber_free( ber, 0 );
271         }
272     } else {
273         for ( rowp = ldap_first_tmplrow( tmpl );
274                 NONFATAL_LDAP_ERR( err ) && rowp != NULLTMPLITEM;
275                 rowp = ldap_next_tmplrow( tmpl, rowp )) {
276             for ( colp = ldap_first_tmplcol( tmpl, rowp ); colp != NULLTMPLITEM;
277                     colp = ldap_next_tmplcol( tmpl, rowp, colp )) {
278                 vals = NULL;
279                 if ( colp->ti_attrname == NULL || ( vals = ldap_get_values( ld,
280                         entry, colp->ti_attrname )) == NULL ) {
281                     freevals = 0;
282                     if ( !LDAP_IS_TMPLITEM_OPTION_SET( colp,
283                             LDAP_DITEM_OPT_HIDEIFEMPTY ) && defattrs != NULL
284                             && colp->ti_attrname != NULL ) {
285                         for ( i = 0; defattrs[ i ] != NULL; ++i ) {
286                             if ( strcasecmp( colp->ti_attrname, defattrs[ i ] )
287                                     == 0 ) {
288                                 break;
289                             }
290                         }
291                         if ( defattrs[ i ] != NULL ) {
292                             vals = defvals[ i ];
293                         }
294                     }
295                 } else {
296                     freevals = 1;
297                     if ( LDAP_IS_TMPLITEM_OPTION_SET( colp,
298                             LDAP_DITEM_OPT_SORTVALUES ) && vals[ 0 ] != NULL
299                             && vals[ 1 ] != NULL ) {
300                         ldap_sort_values( ld, vals, ldap_sort_strcasecmp );
301                     }
302                 }
303
304                 /*
305                  * don't bother even calling do_vals2text() if no values
306                  * or boolean with value false and "hide if false" option set
307                  */
308                 show = ( vals != NULL && vals[ 0 ] != NULL );
309                 if ( show && LDAP_GET_SYN_TYPE( colp->ti_syntaxid )
310                         == LDAP_SYN_TYPE_BOOLEAN && LDAP_IS_TMPLITEM_OPTION_SET(
311                         colp, LDAP_DITEM_OPT_HIDEIFFALSE ) &&
312                         TOUPPER( (unsigned char) vals[ 0 ][ 0 ] ) != 'T' ) {
313                     show = 0;
314                 }
315
316                 if ( colp->ti_syntaxid == LDAP_SYN_SEARCHACTION ) {
317                     if (( opts & LDAP_DISP_OPT_DOSEARCHACTIONS ) != 0 ) {
318                         if ( colp->ti_attrname == NULL || ( show &&
319                                 TOUPPER( (unsigned char) vals[ 0 ][ 0 ] ) == 'T' )) {
320                             err = searchaction( ld, buf, base, entry, dn, colp,
321                                     labelwidth, rdncount, writeproc,
322                                     writeparm, eol, urlprefix );
323                         }
324                     }
325                     show = 0;
326                 }
327
328                 if ( show ) {
329                     err = do_vals2text( ld, buf, vals, colp->ti_label,
330                         labelwidth, colp->ti_syntaxid, writeproc, writeparm,
331                         eol, rdncount, urlprefix );
332                 }
333
334                 if ( freevals ) {
335                     ldap_value_free( vals );
336                 }
337             }
338         }
339     }
340
341     if ( html  && !(( opts & LDAP_DISP_OPT_HTMLBODYONLY ) != 0 )) {
342         sprintf( buf, "</BODY>%s</HTML>%s", eol, eol );
343         (*writeproc)( writeparm, buf, strlen( buf ));
344     }
345
346     free( dn );
347     if ( freebuf ) {
348         free( buf );
349     }
350
351     return( err );
352 }
353
354         
355 int
356 ldap_entry2text_search(
357         LDAP                    *ld,
358         char                    *dn,            /* if NULL, use entry */
359         char                    *base,          /* if NULL, no search actions */
360         LDAPMessage             *entry,         /* if NULL, use dn */
361         struct ldap_disptmpl*   tmpllist,       /* if NULL, load default file */
362         char                    **defattrs,
363         char                    ***defvals,
364         writeptype              writeproc,
365         void                    *writeparm,
366         char                    *eol,
367         int                     rdncount,       /* if 0, display full DN */
368         unsigned long           opts
369 )
370 {
371     Debug( LDAP_DEBUG_TRACE, "ldap_entry2text_search\n", 0, 0, 0 );
372
373     return( do_entry2text_search( ld, dn, base, entry, tmpllist, defattrs,
374             defvals, writeproc, writeparm, eol, rdncount, opts, NULL ));
375 }
376
377
378
379 int
380 ldap_entry2html_search(
381         LDAP                    *ld,
382         char                    *dn,            /* if NULL, use entry */
383         char                    *base,          /* if NULL, no search actions */
384         LDAPMessage             *entry,         /* if NULL, use dn */
385         struct ldap_disptmpl*   tmpllist,       /* if NULL, load default file */
386         char                    **defattrs,
387         char                    ***defvals,
388         writeptype              writeproc,
389         void                    *writeparm,
390         char                    *eol,
391         int                     rdncount,       /* if 0, display full DN */
392         unsigned long           opts,
393         char                    *urlprefix
394 )
395 {
396     Debug( LDAP_DEBUG_TRACE, "ldap_entry2html_search\n", 0, 0, 0 );
397
398     return( do_entry2text_search( ld, dn, base, entry, tmpllist, defattrs,
399             defvals, writeproc, writeparm, eol, rdncount, opts, urlprefix ));
400 }
401
402
403 static int
404 do_entry2text_search(
405         LDAP                    *ld,
406         char                    *dn,            /* if NULL, use entry */
407         char                    *base,          /* if NULL, no search actions */
408         LDAPMessage             *entry,         /* if NULL, use dn */
409         struct ldap_disptmpl*   tmpllist,       /* if NULL, load default file */
410         char                    **defattrs,
411         char                    ***defvals,
412         writeptype              writeproc,
413         void                    *writeparm,
414         char                    *eol,
415         int                     rdncount,       /* if 0, display full DN */
416         unsigned long           opts,
417         char                    *urlprefix
418 )
419 {
420     int                         err, freedn, freetmpls, html;
421     char                        *buf, **fetchattrs, **vals;
422     LDAPMessage                 *ldmp;
423     struct ldap_disptmpl        *tmpl;
424     struct timeval              timeout;
425
426     if ( dn == NULL && entry == NULLMSG ) {
427         ld->ld_errno = LDAP_PARAM_ERROR;
428         return( ld->ld_errno );
429     }
430
431     html = ( urlprefix != NULL );
432
433     timeout.tv_sec = SEARCH_TIMEOUT_SECS;
434     timeout.tv_usec = 0;
435
436     if (( buf = malloc( LDAP_DTMPL_BUFSIZ )) == NULL ) {
437         ld->ld_errno = LDAP_NO_MEMORY;
438         return( ld->ld_errno );
439     }
440
441     freedn = freetmpls = 0;
442     tmpl = NULL;
443
444     if ( tmpllist == NULL ) {
445         if (( err = ldap_init_templates( TEMPLATEFILE, &tmpllist )) != 0 ) {
446             sprintf( buf, "%sUnable to read template file %s (error %d)%s%s",
447                     html ? "<!-- " : "", TEMPLATEFILE, err,
448                     html ? "-->" : "", eol );
449             (*writeproc)( writeparm, buf, strlen( buf ));
450         }
451         freetmpls = 1;
452     }
453
454     if ( dn == NULL ) {
455         if (( dn = ldap_get_dn( ld, entry )) == NULL ) {
456             free( buf );
457             if ( freetmpls ) {
458                 ldap_free_templates( tmpllist );
459             }
460             return( ld->ld_errno );
461         }
462         freedn = 1;
463     }
464
465
466     if ( tmpllist != NULL ) {
467         ldmp = NULLMSG;
468
469         if ( entry == NULL ) {
470             char        *ocattrs[2];
471
472             ocattrs[0] = OCATTRNAME;
473             ocattrs[1] = NULL;
474 #ifdef LDAP_CONNECTIONLESS
475             if ( LDAP_IS_CLDAP( ld ))
476                     err = cldap_search_s( ld, dn, LDAP_SCOPE_BASE,
477                         "objectClass=*", ocattrs, 0, &ldmp, NULL );
478             else
479 #endif /* LDAP_CONNECTIONLESS */
480                     err = ldap_search_st( ld, dn, LDAP_SCOPE_BASE,
481                             "objectClass=*", ocattrs, 0, &timeout, &ldmp );
482
483             if ( err == LDAP_SUCCESS ) {
484                 entry = ldap_first_entry( ld, ldmp );
485             }
486         }
487
488         if ( entry != NULL ) {
489             vals = ldap_get_values( ld, entry, OCATTRNAME );
490             tmpl = ldap_oc2template( vals, tmpllist );
491             if ( vals != NULL ) {
492                 ldap_value_free( vals );
493             }
494         }
495         if ( ldmp != NULL ) {
496             ldap_msgfree( ldmp );
497         }
498     }
499
500     entry = NULL;
501
502     if ( tmpl == NULL ) {
503         fetchattrs = NULL;
504     } else {
505         fetchattrs = ldap_tmplattrs( tmpl, NULL, 1, LDAP_SYN_OPT_DEFER );
506     }
507
508 #ifdef LDAP_CONNECTIONLESS
509     if ( LDAP_IS_CLDAP( ld ))
510         err = cldap_search_s( ld, dn, LDAP_SCOPE_BASE, "objectClass=*",
511                 fetchattrs, 0, &ldmp, NULL );
512     else
513 #endif /* LDAP_CONNECTIONLESS */
514         err = ldap_search_st( ld, dn, LDAP_SCOPE_BASE, "objectClass=*",
515                 fetchattrs, 0, &timeout, &ldmp );
516
517     if ( freedn ) {
518         free( dn );
519     }
520     if ( fetchattrs != NULL ) {
521         ldap_value_free( fetchattrs );
522     }
523
524     if ( err != LDAP_SUCCESS ||
525             ( entry = ldap_first_entry( ld, ldmp )) == NULL ) {
526         if ( freetmpls ) {
527             ldap_free_templates( tmpllist );
528         }
529         free( buf );
530         return( ld->ld_errno );
531     }
532
533     err = do_entry2text( ld, buf, base, entry, tmpl, defattrs, defvals,
534             writeproc, writeparm, eol, rdncount, opts, urlprefix );
535
536     free( buf );
537     if ( freetmpls ) {
538         ldap_free_templates( tmpllist );
539     }
540     ldap_msgfree( ldmp );
541     return( err );
542 }
543             
544
545 int
546 ldap_vals2text(
547         LDAP                    *ld,
548         char                    *buf,           /* NULL for "use internal" */
549         char                    **vals,
550         char                    *label,
551         int                     labelwidth,     /* 0 means use default */
552         unsigned long           syntaxid,
553         writeptype              writeproc,
554         void                    *writeparm,
555         char                    *eol,
556         int                     rdncount
557 )
558 {
559     Debug( LDAP_DEBUG_TRACE, "ldap_vals2text\n", 0, 0, 0 );
560
561     return( do_vals2text( ld, buf, vals, label, labelwidth, syntaxid,
562                 writeproc, writeparm, eol, rdncount, NULL ));
563 }
564
565
566 int
567 ldap_vals2html(
568         LDAP                    *ld,
569         char                    *buf,           /* NULL for "use internal" */
570         char                    **vals,
571         char                    *label,
572         int                     labelwidth,     /* 0 means use default */
573         unsigned long           syntaxid,
574         writeptype              writeproc,
575         void                    *writeparm,
576         char                    *eol,
577         int                     rdncount,
578         char                    *urlprefix
579 )
580 {
581     Debug( LDAP_DEBUG_TRACE, "ldap_vals2html\n", 0, 0, 0 );
582
583     if ( urlprefix == NULL ) {
584         urlprefix = DEF_LDAP_URL_PREFIX;
585     }
586
587     return( do_vals2text( ld, buf, vals, label, labelwidth, syntaxid,
588                 writeproc, writeparm, eol, rdncount, urlprefix ));
589 }
590
591
592 static int
593 do_vals2text(
594         LDAP                    *ld,
595         char                    *buf,           /* NULL for "use internal" */
596         char                    **vals,
597         char                    *label,
598         int                     labelwidth,     /* 0 means use default */
599         unsigned long           syntaxid,
600         writeptype              writeproc,
601         void                    *writeparm,
602         char                    *eol,
603         int                     rdncount,
604         char                    *urlprefix
605 )
606 {
607     int         i, html, writeoutval, freebuf, notascii;
608     char        *p, *s, *outval;
609
610
611     if ( vals == NULL ) {
612         return( LDAP_SUCCESS );
613     }
614
615     html = ( urlprefix != NULL );
616
617     switch( LDAP_GET_SYN_TYPE( syntaxid )) {
618     case LDAP_SYN_TYPE_TEXT:
619     case LDAP_SYN_TYPE_BOOLEAN:
620         break;          /* we only bother with these two types... */
621     default:
622         return( LDAP_SUCCESS );
623     }
624
625     if ( labelwidth == 0 || labelwidth < 0 ) {
626         labelwidth = DEF_LABEL_WIDTH;
627     }
628
629     if ( buf == NULL ) {
630         if (( buf = malloc( LDAP_DTMPL_BUFSIZ )) == NULL ) {
631             ld->ld_errno = LDAP_NO_MEMORY;
632             return( ld->ld_errno );
633         }
634         freebuf = 1;
635     } else {
636         freebuf = 0;
637     }
638
639     output_label( buf, label, labelwidth, writeproc, writeparm, eol, html );
640
641     for ( i = 0; vals[ i ] != NULL; ++i ) {
642         for ( p = vals[ i ]; *p != '\0'; ++p ) {
643             if ( !isascii( *p )) {
644                 break;
645             }
646         }
647         notascii = ( *p != '\0' );
648         outval = notascii ? "(unable to display non-ASCII text value)"
649                 : vals[ i ];
650
651         writeoutval = 0;        /* if non-zero, write outval after switch */
652
653         switch( syntaxid ) {
654         case LDAP_SYN_CASEIGNORESTR:
655             ++writeoutval;
656             break;
657
658         case LDAP_SYN_RFC822ADDR:
659             if ( html ) {
660                 strcpy( buf, "<DD><A HREF=\"mailto:" );
661                 strcat_escaped( buf, outval );
662                 sprintf( buf + strlen( buf ), "\">%s</A><BR>%s", outval, eol );
663                 (*writeproc)( writeparm, buf, strlen( buf ));
664             } else {
665                 ++writeoutval;
666             }
667             break;
668
669         case LDAP_SYN_DN:       /* for now */
670             output_dn( buf, outval, labelwidth, rdncount, writeproc,
671                     writeparm, eol, urlprefix );
672             break;
673
674         case LDAP_SYN_MULTILINESTR:
675             if ( i > 0 && !html ) {
676                 output_label( buf, label, labelwidth, writeproc,
677                         writeparm, eol, html );
678             }
679
680             p = s = outval;
681             while (( s = strchr( s, '$' )) != NULL ) {
682                 *s++ = '\0';
683                 while ( isspace( (unsigned char) *s )) {
684                     ++s;
685                 }
686                 if ( html ) {
687                     sprintf( buf, "<DD>%s<BR>%s", p, eol );
688                 } else {
689                     sprintf( buf, "%-*s%s%s", labelwidth, " ", p, eol );
690                 }
691                 (*writeproc)( writeparm, buf, strlen( buf ));
692                 p = s;
693             }
694             outval = p;
695             ++writeoutval;
696             break;
697
698         case LDAP_SYN_BOOLEAN:
699             outval = TOUPPER((unsigned char) outval[0]) == 'T' ? "TRUE" : "FALSE";
700             ++writeoutval;
701             break;
702
703         case LDAP_SYN_TIME:
704         case LDAP_SYN_DATE:
705             outval = time2text( outval, syntaxid == LDAP_SYN_DATE );
706             ++writeoutval;
707             break;
708
709         case LDAP_SYN_LABELEDURL:
710             if ( !notascii && ( p = strchr( outval, '$' )) != NULL ) {
711                 *p++ = '\0';
712                 while ( isspace( (unsigned char) *p )) {
713                     ++p;
714                 }
715                 s = outval;
716             } else if ( !notascii && ( s = strchr( outval, ' ' )) != NULL ) {
717                 *s++ = '\0';
718                 while ( isspace( (unsigned char) *s )) {
719                     ++s;
720                 }
721                 p = outval;
722             } else {
723                 s = "URL";
724                 p = outval;
725             }
726
727             /*
728              * at this point `s' points to the label & `p' to the URL
729              */
730             if ( html ) {
731                 sprintf( buf, "<DD><A HREF=\"%s\">%s</A><BR>%s", p, s, eol );
732             } else {
733                 sprintf( buf, "%-*s%s%s%-*s%s%s", labelwidth, " ",
734                     s, eol, labelwidth + 2, " ",p , eol );
735             }
736             (*writeproc)( writeparm, buf, strlen( buf ));
737             break;
738
739         default:
740             sprintf( buf, " Can't display item type %ld%s",
741                     syntaxid, eol );
742             (*writeproc)( writeparm, buf, strlen( buf ));
743         }
744
745         if ( writeoutval ) {
746             if ( html ) {
747                 sprintf( buf, "<DD>%s<BR>%s", outval, eol );
748             } else {
749                 sprintf( buf, "%-*s%s%s", labelwidth, " ", outval, eol );
750             }
751             (*writeproc)( writeparm, buf, strlen( buf ));
752         }
753     }
754
755     if ( freebuf ) {
756         free( buf );
757     }
758
759     return( LDAP_SUCCESS );
760 }
761
762
763 static int
764 max_label_len( struct ldap_disptmpl *tmpl )
765 {
766     struct ldap_tmplitem        *rowp, *colp;
767     int                         len, maxlen;
768
769     maxlen = 0;
770
771     for ( rowp = ldap_first_tmplrow( tmpl ); rowp != NULLTMPLITEM;
772             rowp = ldap_next_tmplrow( tmpl, rowp )) {
773         for ( colp = ldap_first_tmplcol( tmpl, rowp ); colp != NULLTMPLITEM;
774                 colp = ldap_next_tmplcol( tmpl, rowp, colp )) {
775             if (( len = strlen( colp->ti_label )) > maxlen ) {
776                 maxlen = len;
777             }
778         }
779     }
780
781     return( maxlen );
782 }
783
784
785 static int
786 output_label( char *buf, char *label, int width, writeptype writeproc,
787         void *writeparm, char *eol, int html )
788 {
789     char        *p;
790
791     if ( html ) {
792         sprintf( buf, "<DT><B>%s</B>", label );
793     } else {
794         sprintf( buf, " %s:", label );
795         p = buf + strlen( buf );
796
797         while ( p - buf < width ) {
798             *p++ = ' ';
799         }
800
801         *p = '\0';
802         strcat( buf, eol );
803     }
804
805     return ((*writeproc)( writeparm, buf, strlen( buf )));
806 }
807
808
809 static int
810 output_dn( char *buf, char *dn, int width, int rdncount,
811         writeptype writeproc, void *writeparm, char *eol, char *urlprefix )
812 {
813     char        **dnrdns;
814     int         i;
815
816     if (( dnrdns = ldap_explode_dn( dn, 1 )) == NULL ) {
817         return( -1 );
818     }
819
820     if ( urlprefix != NULL ) {
821         sprintf( buf, "<DD><A HREF=\"%s", urlprefix );
822         strcat_escaped( buf, dn );
823         strcat( buf, "\">" );
824     } else if ( width > 0 ) {
825         sprintf( buf, "%-*s", width, " " );
826     } else {
827         *buf = '\0';
828     }
829
830     for ( i = 0; dnrdns[ i ] != NULL && ( rdncount == 0 || i < rdncount );
831             ++i ) {
832         if ( i > 0 ) {
833             strcat( buf, ", " );
834         }
835         strcat( buf, dnrdns[ i ] );
836     }
837
838     if ( urlprefix != NULL ) {
839         strcat( buf, "</A><BR>" );
840     }
841
842     ldap_value_free( dnrdns );
843
844     strcat( buf, eol );
845
846     return ((*writeproc)( writeparm, buf, strlen( buf )));
847 }
848
849
850
851 #define HREF_CHAR_ACCEPTABLE( c )       (( c >= '-' && c <= '9' ) ||    \
852                                          ( c >= '@' && c <= 'Z' ) ||    \
853                                          ( c == '_' ) ||                \
854                                          ( c >= 'a' && c <= 'z' ))
855
856 static void
857 strcat_escaped( char *s1, char *s2 )
858 {
859     char        *p, *q;
860     char        *hexdig = "0123456789ABCDEF";
861
862     p = s1 + strlen( s1 );
863     for ( q = s2; *q != '\0'; ++q ) {
864         if ( HREF_CHAR_ACCEPTABLE( *q )) {
865             *p++ = *q;
866         } else {
867             *p++ = '%';
868             *p++ = hexdig[ *q >> 4 ];
869             *p++ = hexdig[ *q & 0x0F ];
870         }
871     }
872
873     *p = '\0';
874 }
875
876
877 #define GET2BYTENUM( p )        (( *p - '0' ) * 10 + ( *(p+1) - '0' ))
878
879 static char *
880 time2text( char *ldtimestr, int dateonly )
881 {
882     struct tm           t;
883     char                *p, *timestr, zone, *fmterr = "badly formatted time";
884     time_t              gmttime;
885     char                timebuf[32];
886         int ndigits;
887
888         if (strlen( ldtimestr ) < 12 ) {
889                 return( fmterr );
890         }
891
892     for ( ndigits=0; isdigit((unsigned char) ldtimestr[ndigits]); ndigits++) {
893                 ; /* EMPTY */
894     }
895
896         if ( ndigits != 12 && ndigits != 14) {
897             return( fmterr );
898         }
899         
900     memset( (char *)&t, 0, sizeof( struct tm ));
901
902     p = ldtimestr;
903
904         if( ndigits == 14) {
905                 /* came with a century */
906                 /* POSIX says tm_year should be year - 1900 */
907         t.tm_year = 100 * GET2BYTENUM( p ) - 1900;
908                 p += 2;
909         }
910     t.tm_year = GET2BYTENUM( p ); p += 2;
911
912     t.tm_mon = GET2BYTENUM( p ) - 1; p += 2;
913     t.tm_mday = GET2BYTENUM( p ); p += 2;
914     t.tm_hour = GET2BYTENUM( p ); p += 2;
915     t.tm_min = GET2BYTENUM( p ); p += 2;
916     t.tm_sec = GET2BYTENUM( p ); p += 2;
917
918     if (( zone = *p ) == 'Z' ) {        /* GMT */
919         zone = '\0';    /* no need to indicate on screen, so we make it null */
920     }
921
922     gmttime = gtime( &t );
923
924     timestr = ldap_pvt_ctime( &gmttime, timebuf );
925    
926     timestr[ strlen( timestr ) - 1 ] = zone;    /* replace trailing newline */
927     if ( dateonly ) {
928         SAFEMEMCPY( timestr + 11, timestr + 20, strlen( timestr + 20 ) + 1 );
929     }
930
931     return( timestr );
932 }
933
934
935
936 /* gtime.c - inverse gmtime */
937 /* gtime(): the inverse of localtime().
938         This routine was supplied by Mike Accetta at CMU many years ago.
939  */
940
941 static int      dmsize[] = {
942     31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
943 };
944
945 #define dysize(y)       \
946         (((y) % 4) ? 365 : (((y) % 100) ? 366 : (((y) % 400) ? 365 : 366)))
947
948 /*
949  * Y2K YEAR
950  */
951         /* per POSIX tm_year should be offset by 1900 */
952 #define YEAR_POSIX(y)           ((y) + 1900)
953
954         /*
955          * year is < 1900, year is offset by 1900
956          */
957 #define YEAR_CAREFUL(y)         ((y) < 1900 ? (y) + 1900 : (y))
958
959         /*
960         ** if year is < 1990 and < 70 must be offset by 2000 as Unix epoch
961         ** started in 1970.  if year is < 1990 but >= 70, offset by 1900.
962         ** if year is >= 1900, it must be the real year.
963         */ 
964 #define YEAR_PEDANTIC(y)                ((y) < 1900 \
965                                                 ? ((y) < 70 ? (y) + 2000 ? (y) + 1900) \
966                                                 : (y))
967
968 #define YEAR(y) YEAR_CAREFUL(y)
969
970 /* \f */
971
972 static long     gtime ( struct tm *tm )
973 {
974     register int    i,
975                     sec,
976                     mins,
977                     hour,
978                     mday,
979                     mon,
980                     year;
981     register long   result;
982
983     if ((sec = tm -> tm_sec) < 0 || sec > 59
984             || (mins = tm -> tm_min) < 0 || mins > 59
985             || (hour = tm -> tm_hour) < 0 || hour > 24
986             || (mday = tm -> tm_mday) < 1 || mday > 31
987             || (mon = tm -> tm_mon + 1) < 1 || mon > 12)
988         return ((long) -1);
989     if (hour == 24) {
990         hour = 0;
991         mday++;
992     }
993     year = YEAR (tm -> tm_year);
994
995     result = 0L;
996     for (i = 1970; i < year; i++)
997         result += dysize (i);
998     if (dysize (year) == 366 && mon >= 3)
999         result++;
1000     while (--mon)
1001         result += dmsize[mon - 1];
1002     result += mday - 1;
1003     result = 24 * result + hour;
1004     result = 60 * result + mins;
1005     result = 60 * result + sec;
1006
1007     return result;
1008 }
1009
1010 static int
1011 searchaction( LDAP *ld, char *buf, char *base, LDAPMessage *entry, char *dn,
1012         struct ldap_tmplitem *tip, int labelwidth, int rdncount,
1013         writeptype writeproc, void *writeparm, char *eol, char *urlprefix )
1014 {
1015     int                 err = 0, lderr, i, count, html;
1016     char                **vals, **members;
1017     char                *value, *filtpattern, *attr, *selectname;
1018     char                *retattrs[2], filter[ 256 ];
1019     LDAPMessage         *ldmp;
1020     struct timeval      timeout;
1021
1022     html = ( urlprefix != NULL );
1023
1024     for ( i = 0; tip->ti_args != NULL && tip->ti_args[ i ] != NULL; ++i ) {
1025         ;
1026     }
1027     if ( i < 3 ) {
1028         return( LDAP_PARAM_ERROR );
1029     }
1030     attr = tip->ti_args[ 0 ];
1031     filtpattern = tip->ti_args[ 1 ];
1032     retattrs[ 0 ] = tip->ti_args[ 2 ];
1033     retattrs[ 1 ] = NULL;
1034     selectname = tip->ti_args[ 3 ];
1035
1036     vals = NULL;
1037     if ( attr == NULL ) {
1038         value = NULL;
1039     } else if ( strcasecmp( attr, "-dnb" ) == 0 ) {
1040         return( LDAP_PARAM_ERROR );
1041     } else if ( strcasecmp( attr, "-dnt" ) == 0 ) {
1042         value = dn;
1043     } else if (( vals = ldap_get_values( ld, entry, attr )) != NULL ) {
1044         value = vals[ 0 ];
1045     } else {
1046         value = NULL;
1047     }
1048
1049     ldap_build_filter( filter, sizeof( filter ), filtpattern, NULL, NULL, NULL,
1050             value, NULL );
1051
1052     if ( html ) {
1053         /*
1054          * if we are generating HTML, we add an HREF link that embodies this
1055          * search action as an LDAP URL, instead of actually doing the search
1056          * now.
1057          */
1058         sprintf( buf, "<DT><A HREF=\"%s", urlprefix );
1059         if ( base != NULL ) {
1060             strcat_escaped( buf, base );
1061         }
1062         strcat( buf, "??sub?" );
1063         strcat_escaped( buf, filter );
1064         sprintf( buf + strlen( buf ), "\"><B>%s</B></A><DD><BR>%s",
1065                 tip->ti_label, eol );
1066         if ((*writeproc)( writeparm, buf, strlen( buf )) < 0 ) {
1067             return( LDAP_LOCAL_ERROR );
1068         }
1069         return( LDAP_SUCCESS );
1070     }
1071
1072     timeout.tv_sec = SEARCH_TIMEOUT_SECS;
1073     timeout.tv_usec = 0;
1074
1075 #ifdef LDAP_CONNECTIONLESS
1076     if ( LDAP_IS_CLDAP( ld ))
1077         lderr = cldap_search_s( ld, base, LDAP_SCOPE_SUBTREE, filter, retattrs,
1078                 0, &ldmp, NULL );
1079     else
1080 #endif /* LDAP_CONNECTIONLESS */
1081         lderr = ldap_search_st( ld, base, LDAP_SCOPE_SUBTREE, filter, retattrs,
1082                 0, &timeout, &ldmp );
1083
1084     if ( lderr == LDAP_SUCCESS || NONFATAL_LDAP_ERR( lderr )) {
1085         if (( count = ldap_count_entries( ld, ldmp )) > 0 ) {
1086             if (( members = (char **)malloc( (count + 1) * sizeof(char *)))
1087                     == NULL ) {
1088                 err = LDAP_NO_MEMORY;
1089             } else {
1090                 for ( i = 0, entry = ldap_first_entry( ld, ldmp );
1091                         entry != NULL;
1092                         entry = ldap_next_entry( ld, entry ), ++i ) {
1093                     members[ i ] = ldap_get_dn( ld, entry );
1094                 }
1095                 members[ i ] = NULL;
1096
1097                 ldap_sort_values( ld, members, ldap_sort_strcasecmp );
1098
1099                 err = do_vals2text( ld, NULL, members, tip->ti_label,
1100                         html ? -1 : 0, LDAP_SYN_DN, writeproc, writeparm,
1101                         eol, rdncount, urlprefix );
1102
1103                 ldap_value_free( members );
1104             }
1105         }
1106         ldap_msgfree( ldmp );
1107     }
1108
1109     
1110     if ( vals != NULL ) {
1111         ldap_value_free( vals );
1112     }
1113
1114     return(( err == LDAP_SUCCESS ) ? lderr : err );
1115 }