]> git.sur5r.net Git - openldap/blob - libraries/libldap/tmplout.c
Don't provide ldap.OpenLDAP.org and dc=OpenLDAP, dc=Org as the defaults.
[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( *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( 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( 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( *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( 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( *p )) {
713                     ++p;
714                 }
715                 s = outval;
716             } else if ( !notascii && ( s = strchr( outval, ' ' )) != NULL ) {
717                 *s++ = '\0';
718                 while ( isspace( *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
887     memset( (char *)&t, 0, sizeof( struct tm ));
888     if ( (int) strlen( ldtimestr ) < 13 ) {
889         return( fmterr );
890     }
891
892     for ( p = ldtimestr; p - ldtimestr < 12; ++p ) {
893         if ( !isdigit( *p )) {
894             return( fmterr );
895         }
896     }
897
898     p = ldtimestr;
899     t.tm_year = GET2BYTENUM( p ); p += 2;
900     t.tm_mon = GET2BYTENUM( p ) - 1; p += 2;
901     t.tm_mday = GET2BYTENUM( p ); p += 2;
902     t.tm_hour = GET2BYTENUM( p ); p += 2;
903     t.tm_min = GET2BYTENUM( p ); p += 2;
904     t.tm_sec = GET2BYTENUM( p ); p += 2;
905
906     if (( zone = *p ) == 'Z' ) {        /* GMT */
907         zone = '\0';    /* no need to indicate on screen, so we make it null */
908     }
909
910     gmttime = gtime( &t );
911
912     timestr = ldap_pvt_ctime( &gmttime, timebuf );
913    
914     timestr[ strlen( timestr ) - 1 ] = zone;    /* replace trailing newline */
915     if ( dateonly ) {
916         SAFEMEMCPY( timestr + 11, timestr + 20, strlen( timestr + 20 ) + 1 );
917     }
918
919     return( timestr );
920 }
921
922
923
924 /* gtime.c - inverse gmtime */
925 /* gtime(): the inverse of localtime().
926         This routine was supplied by Mike Accetta at CMU many years ago.
927  */
928
929 static int      dmsize[] = {
930     31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
931 };
932
933 #define dysize(y)       \
934         (((y) % 4) ? 365 : (((y) % 100) ? 366 : (((y) % 400) ? 365 : 366)))
935
936 #define YEAR(y)         ((y) >= 100 ? (y) : (y) + 1900)
937
938 /* \f */
939
940 static long     gtime ( struct tm *tm )
941 {
942     register int    i,
943                     sec,
944                     mins,
945                     hour,
946                     mday,
947                     mon,
948                     year;
949     register long   result;
950
951     if ((sec = tm -> tm_sec) < 0 || sec > 59
952             || (mins = tm -> tm_min) < 0 || mins > 59
953             || (hour = tm -> tm_hour) < 0 || hour > 24
954             || (mday = tm -> tm_mday) < 1 || mday > 31
955             || (mon = tm -> tm_mon + 1) < 1 || mon > 12)
956         return ((long) -1);
957     if (hour == 24) {
958         hour = 0;
959         mday++;
960     }
961     year = YEAR (tm -> tm_year);
962
963     result = 0L;
964     for (i = 1970; i < year; i++)
965         result += dysize (i);
966     if (dysize (year) == 366 && mon >= 3)
967         result++;
968     while (--mon)
969         result += dmsize[mon - 1];
970     result += mday - 1;
971     result = 24 * result + hour;
972     result = 60 * result + mins;
973     result = 60 * result + sec;
974
975     return result;
976 }
977
978 static int
979 searchaction( LDAP *ld, char *buf, char *base, LDAPMessage *entry, char *dn,
980         struct ldap_tmplitem *tip, int labelwidth, int rdncount,
981         writeptype writeproc, void *writeparm, char *eol, char *urlprefix )
982 {
983     int                 err = 0, lderr, i, count, html;
984     char                **vals, **members;
985     char                *value, *filtpattern, *attr, *selectname;
986     char                *retattrs[2], filter[ 256 ];
987     LDAPMessage         *ldmp;
988     struct timeval      timeout;
989
990     html = ( urlprefix != NULL );
991
992     for ( i = 0; tip->ti_args != NULL && tip->ti_args[ i ] != NULL; ++i ) {
993         ;
994     }
995     if ( i < 3 ) {
996         return( LDAP_PARAM_ERROR );
997     }
998     attr = tip->ti_args[ 0 ];
999     filtpattern = tip->ti_args[ 1 ];
1000     retattrs[ 0 ] = tip->ti_args[ 2 ];
1001     retattrs[ 1 ] = NULL;
1002     selectname = tip->ti_args[ 3 ];
1003
1004     vals = NULL;
1005     if ( attr == NULL ) {
1006         value = NULL;
1007     } else if ( strcasecmp( attr, "-dnb" ) == 0 ) {
1008         return( LDAP_PARAM_ERROR );
1009     } else if ( strcasecmp( attr, "-dnt" ) == 0 ) {
1010         value = dn;
1011     } else if (( vals = ldap_get_values( ld, entry, attr )) != NULL ) {
1012         value = vals[ 0 ];
1013     } else {
1014         value = NULL;
1015     }
1016
1017     ldap_build_filter( filter, sizeof( filter ), filtpattern, NULL, NULL, NULL,
1018             value, NULL );
1019
1020     if ( html ) {
1021         /*
1022          * if we are generating HTML, we add an HREF link that embodies this
1023          * search action as an LDAP URL, instead of actually doing the search
1024          * now.
1025          */
1026         sprintf( buf, "<DT><A HREF=\"%s", urlprefix );
1027         if ( base != NULL ) {
1028             strcat_escaped( buf, base );
1029         }
1030         strcat( buf, "??sub?" );
1031         strcat_escaped( buf, filter );
1032         sprintf( buf + strlen( buf ), "\"><B>%s</B></A><DD><BR>%s",
1033                 tip->ti_label, eol );
1034         if ((*writeproc)( writeparm, buf, strlen( buf )) < 0 ) {
1035             return( LDAP_LOCAL_ERROR );
1036         }
1037         return( LDAP_SUCCESS );
1038     }
1039
1040     timeout.tv_sec = SEARCH_TIMEOUT_SECS;
1041     timeout.tv_usec = 0;
1042
1043 #ifdef LDAP_CONNECTIONLESS
1044     if ( LDAP_IS_CLDAP( ld ))
1045         lderr = cldap_search_s( ld, base, LDAP_SCOPE_SUBTREE, filter, retattrs,
1046                 0, &ldmp, NULL );
1047     else
1048 #endif /* LDAP_CONNECTIONLESS */
1049         lderr = ldap_search_st( ld, base, LDAP_SCOPE_SUBTREE, filter, retattrs,
1050                 0, &timeout, &ldmp );
1051
1052     if ( lderr == LDAP_SUCCESS || NONFATAL_LDAP_ERR( lderr )) {
1053         if (( count = ldap_count_entries( ld, ldmp )) > 0 ) {
1054             if (( members = (char **)malloc( (count + 1) * sizeof(char *)))
1055                     == NULL ) {
1056                 err = LDAP_NO_MEMORY;
1057             } else {
1058                 for ( i = 0, entry = ldap_first_entry( ld, ldmp );
1059                         entry != NULL;
1060                         entry = ldap_next_entry( ld, entry ), ++i ) {
1061                     members[ i ] = ldap_get_dn( ld, entry );
1062                 }
1063                 members[ i ] = NULL;
1064
1065                 ldap_sort_values( ld, members, ldap_sort_strcasecmp );
1066
1067                 err = do_vals2text( ld, NULL, members, tip->ti_label,
1068                         html ? -1 : 0, LDAP_SYN_DN, writeproc, writeparm,
1069                         eol, rdncount, urlprefix );
1070
1071                 ldap_value_free( members );
1072             }
1073         }
1074         ldap_msgfree( ldmp );
1075     }
1076
1077     
1078     if ( vals != NULL ) {
1079         ldap_value_free( vals );
1080     }
1081
1082     return(( err == LDAP_SUCCESS ) ? lderr : err );
1083 }