]> git.sur5r.net Git - openldap/blob - libraries/libldap/tmplout.c
Add simple copyright notice.
[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     } else {
269         for ( rowp = ldap_first_tmplrow( tmpl );
270                 NONFATAL_LDAP_ERR( err ) && rowp != NULLTMPLITEM;
271                 rowp = ldap_next_tmplrow( tmpl, rowp )) {
272             for ( colp = ldap_first_tmplcol( tmpl, rowp ); colp != NULLTMPLITEM;
273                     colp = ldap_next_tmplcol( tmpl, rowp, colp )) {
274                 vals = NULL;
275                 if ( colp->ti_attrname == NULL || ( vals = ldap_get_values( ld,
276                         entry, colp->ti_attrname )) == NULL ) {
277                     freevals = 0;
278                     if ( !LDAP_IS_TMPLITEM_OPTION_SET( colp,
279                             LDAP_DITEM_OPT_HIDEIFEMPTY ) && defattrs != NULL
280                             && colp->ti_attrname != NULL ) {
281                         for ( i = 0; defattrs[ i ] != NULL; ++i ) {
282                             if ( strcasecmp( colp->ti_attrname, defattrs[ i ] )
283                                     == 0 ) {
284                                 break;
285                             }
286                         }
287                         if ( defattrs[ i ] != NULL ) {
288                             vals = defvals[ i ];
289                         }
290                     }
291                 } else {
292                     freevals = 1;
293                     if ( LDAP_IS_TMPLITEM_OPTION_SET( colp,
294                             LDAP_DITEM_OPT_SORTVALUES ) && vals[ 0 ] != NULL
295                             && vals[ 1 ] != NULL ) {
296                         ldap_sort_values( ld, vals, ldap_sort_strcasecmp );
297                     }
298                 }
299
300                 /*
301                  * don't bother even calling do_vals2text() if no values
302                  * or boolean with value false and "hide if false" option set
303                  */
304                 show = ( vals != NULL && vals[ 0 ] != NULL );
305                 if ( show && LDAP_GET_SYN_TYPE( colp->ti_syntaxid )
306                         == LDAP_SYN_TYPE_BOOLEAN && LDAP_IS_TMPLITEM_OPTION_SET(
307                         colp, LDAP_DITEM_OPT_HIDEIFFALSE ) &&
308                         TOUPPER( vals[ 0 ][ 0 ] ) != 'T' ) {
309                     show = 0;
310                 }
311
312                 if ( colp->ti_syntaxid == LDAP_SYN_SEARCHACTION ) {
313                     if (( opts & LDAP_DISP_OPT_DOSEARCHACTIONS ) != 0 ) {
314                         if ( colp->ti_attrname == NULL || ( show &&
315                                 TOUPPER( vals[ 0 ][ 0 ] ) == 'T' )) {
316                             err = searchaction( ld, buf, base, entry, dn, colp,
317                                     labelwidth, rdncount, writeproc,
318                                     writeparm, eol, urlprefix );
319                         }
320                     }
321                     show = 0;
322                 }
323
324                 if ( show ) {
325                     err = do_vals2text( ld, buf, vals, colp->ti_label,
326                         labelwidth, colp->ti_syntaxid, writeproc, writeparm,
327                         eol, rdncount, urlprefix );
328                 }
329
330                 if ( freevals ) {
331                     ldap_value_free( vals );
332                 }
333             }
334         }
335     }
336
337     if ( html  && !(( opts & LDAP_DISP_OPT_HTMLBODYONLY ) != 0 )) {
338         sprintf( buf, "</BODY>%s</HTML>%s", eol, eol );
339         (*writeproc)( writeparm, buf, strlen( buf ));
340     }
341
342     free( dn );
343     if ( freebuf ) {
344         free( buf );
345     }
346
347     return( err );
348 }
349
350         
351 int
352 ldap_entry2text_search(
353         LDAP                    *ld,
354         char                    *dn,            /* if NULL, use entry */
355         char                    *base,          /* if NULL, no search actions */
356         LDAPMessage             *entry,         /* if NULL, use dn */
357         struct ldap_disptmpl*   tmpllist,       /* if NULL, load default file */
358         char                    **defattrs,
359         char                    ***defvals,
360         writeptype              writeproc,
361         void                    *writeparm,
362         char                    *eol,
363         int                     rdncount,       /* if 0, display full DN */
364         unsigned long           opts
365 )
366 {
367     Debug( LDAP_DEBUG_TRACE, "ldap_entry2text_search\n", 0, 0, 0 );
368
369     return( do_entry2text_search( ld, dn, base, entry, tmpllist, defattrs,
370             defvals, writeproc, writeparm, eol, rdncount, opts, NULL ));
371 }
372
373
374
375 int
376 ldap_entry2html_search(
377         LDAP                    *ld,
378         char                    *dn,            /* if NULL, use entry */
379         char                    *base,          /* if NULL, no search actions */
380         LDAPMessage             *entry,         /* if NULL, use dn */
381         struct ldap_disptmpl*   tmpllist,       /* if NULL, load default file */
382         char                    **defattrs,
383         char                    ***defvals,
384         writeptype              writeproc,
385         void                    *writeparm,
386         char                    *eol,
387         int                     rdncount,       /* if 0, display full DN */
388         unsigned long           opts,
389         char                    *urlprefix
390 )
391 {
392     Debug( LDAP_DEBUG_TRACE, "ldap_entry2html_search\n", 0, 0, 0 );
393
394     return( do_entry2text_search( ld, dn, base, entry, tmpllist, defattrs,
395             defvals, writeproc, writeparm, eol, rdncount, opts, urlprefix ));
396 }
397
398
399 static int
400 do_entry2text_search(
401         LDAP                    *ld,
402         char                    *dn,            /* if NULL, use entry */
403         char                    *base,          /* if NULL, no search actions */
404         LDAPMessage             *entry,         /* if NULL, use dn */
405         struct ldap_disptmpl*   tmpllist,       /* if NULL, load default file */
406         char                    **defattrs,
407         char                    ***defvals,
408         writeptype              writeproc,
409         void                    *writeparm,
410         char                    *eol,
411         int                     rdncount,       /* if 0, display full DN */
412         unsigned long           opts,
413         char                    *urlprefix
414 )
415 {
416     int                         err, freedn, freetmpls, html;
417     char                        *buf, **fetchattrs, **vals;
418     LDAPMessage                 *ldmp;
419     struct ldap_disptmpl        *tmpl;
420     struct timeval              timeout;
421
422     if ( dn == NULL && entry == NULLMSG ) {
423         ld->ld_errno = LDAP_PARAM_ERROR;
424         return( ld->ld_errno );
425     }
426
427     html = ( urlprefix != NULL );
428
429     timeout.tv_sec = SEARCH_TIMEOUT_SECS;
430     timeout.tv_usec = 0;
431
432     if (( buf = malloc( LDAP_DTMPL_BUFSIZ )) == NULL ) {
433         ld->ld_errno = LDAP_NO_MEMORY;
434         return( ld->ld_errno );
435     }
436
437     freedn = freetmpls = 0;
438     tmpl = NULL;
439
440     if ( tmpllist == NULL ) {
441         if (( err = ldap_init_templates( TEMPLATEFILE, &tmpllist )) != 0 ) {
442             sprintf( buf, "%sUnable to read template file %s (error %d)%s%s",
443                     html ? "<!-- " : "", TEMPLATEFILE, err,
444                     html ? "-->" : "", eol );
445             (*writeproc)( writeparm, buf, strlen( buf ));
446         }
447         freetmpls = 1;
448     }
449
450     if ( dn == NULL ) {
451         if (( dn = ldap_get_dn( ld, entry )) == NULL ) {
452             free( buf );
453             if ( freetmpls ) {
454                 ldap_free_templates( tmpllist );
455             }
456             return( ld->ld_errno );
457         }
458         freedn = 1;
459     }
460
461
462     if ( tmpllist != NULL ) {
463         ldmp = NULLMSG;
464
465         if ( entry == NULL ) {
466             char        *ocattrs[2];
467
468             ocattrs[0] = OCATTRNAME;
469             ocattrs[1] = NULL;
470 #ifdef LDAP_CONNECTIONLESS
471             if ( LDAP_IS_CLDAP( ld ))
472                     err = cldap_search_s( ld, dn, LDAP_SCOPE_BASE,
473                         "objectClass=*", ocattrs, 0, &ldmp, NULL );
474             else
475 #endif /* LDAP_CONNECTIONLESS */
476                     err = ldap_search_st( ld, dn, LDAP_SCOPE_BASE,
477                             "objectClass=*", ocattrs, 0, &timeout, &ldmp );
478
479             if ( err == LDAP_SUCCESS ) {
480                 entry = ldap_first_entry( ld, ldmp );
481             }
482         }
483
484         if ( entry != NULL ) {
485             vals = ldap_get_values( ld, entry, OCATTRNAME );
486             tmpl = ldap_oc2template( vals, tmpllist );
487             if ( vals != NULL ) {
488                 ldap_value_free( vals );
489             }
490         }
491         if ( ldmp != NULL ) {
492             ldap_msgfree( ldmp );
493         }
494     }
495
496     entry = NULL;
497
498     if ( tmpl == NULL ) {
499         fetchattrs = NULL;
500     } else {
501         fetchattrs = ldap_tmplattrs( tmpl, NULL, 1, LDAP_SYN_OPT_DEFER );
502     }
503
504 #ifdef LDAP_CONNECTIONLESS
505     if ( LDAP_IS_CLDAP( ld ))
506         err = cldap_search_s( ld, dn, LDAP_SCOPE_BASE, "objectClass=*",
507                 fetchattrs, 0, &ldmp, NULL );
508     else
509 #endif /* LDAP_CONNECTIONLESS */
510         err = ldap_search_st( ld, dn, LDAP_SCOPE_BASE, "objectClass=*",
511                 fetchattrs, 0, &timeout, &ldmp );
512
513     if ( freedn ) {
514         free( dn );
515     }
516     if ( fetchattrs != NULL ) {
517         ldap_value_free( fetchattrs );
518     }
519
520     if ( err != LDAP_SUCCESS ||
521             ( entry = ldap_first_entry( ld, ldmp )) == NULL ) {
522         if ( freetmpls ) {
523             ldap_free_templates( tmpllist );
524         }
525         free( buf );
526         return( ld->ld_errno );
527     }
528
529     err = do_entry2text( ld, buf, base, entry, tmpl, defattrs, defvals,
530             writeproc, writeparm, eol, rdncount, opts, urlprefix );
531
532     free( buf );
533     if ( freetmpls ) {
534         ldap_free_templates( tmpllist );
535     }
536     ldap_msgfree( ldmp );
537     return( err );
538 }
539             
540
541 int
542 ldap_vals2text(
543         LDAP                    *ld,
544         char                    *buf,           /* NULL for "use internal" */
545         char                    **vals,
546         char                    *label,
547         int                     labelwidth,     /* 0 means use default */
548         unsigned long           syntaxid,
549         writeptype              writeproc,
550         void                    *writeparm,
551         char                    *eol,
552         int                     rdncount
553 )
554 {
555     Debug( LDAP_DEBUG_TRACE, "ldap_vals2text\n", 0, 0, 0 );
556
557     return( do_vals2text( ld, buf, vals, label, labelwidth, syntaxid,
558                 writeproc, writeparm, eol, rdncount, NULL ));
559 }
560
561
562 int
563 ldap_vals2html(
564         LDAP                    *ld,
565         char                    *buf,           /* NULL for "use internal" */
566         char                    **vals,
567         char                    *label,
568         int                     labelwidth,     /* 0 means use default */
569         unsigned long           syntaxid,
570         writeptype              writeproc,
571         void                    *writeparm,
572         char                    *eol,
573         int                     rdncount,
574         char                    *urlprefix
575 )
576 {
577     Debug( LDAP_DEBUG_TRACE, "ldap_vals2html\n", 0, 0, 0 );
578
579     if ( urlprefix == NULL ) {
580         urlprefix = DEF_LDAP_URL_PREFIX;
581     }
582
583     return( do_vals2text( ld, buf, vals, label, labelwidth, syntaxid,
584                 writeproc, writeparm, eol, rdncount, urlprefix ));
585 }
586
587
588 static int
589 do_vals2text(
590         LDAP                    *ld,
591         char                    *buf,           /* NULL for "use internal" */
592         char                    **vals,
593         char                    *label,
594         int                     labelwidth,     /* 0 means use default */
595         unsigned long           syntaxid,
596         writeptype              writeproc,
597         void                    *writeparm,
598         char                    *eol,
599         int                     rdncount,
600         char                    *urlprefix
601 )
602 {
603     int         i, html, writeoutval, freebuf, notascii;
604     char        *p, *s, *outval;
605
606
607     if ( vals == NULL ) {
608         return( LDAP_SUCCESS );
609     }
610
611     html = ( urlprefix != NULL );
612
613     switch( LDAP_GET_SYN_TYPE( syntaxid )) {
614     case LDAP_SYN_TYPE_TEXT:
615     case LDAP_SYN_TYPE_BOOLEAN:
616         break;          /* we only bother with these two types... */
617     default:
618         return( LDAP_SUCCESS );
619     }
620
621     if ( labelwidth == 0 || labelwidth < 0 ) {
622         labelwidth = DEF_LABEL_WIDTH;
623     }
624
625     if ( buf == NULL ) {
626         if (( buf = malloc( LDAP_DTMPL_BUFSIZ )) == NULL ) {
627             ld->ld_errno = LDAP_NO_MEMORY;
628             return( ld->ld_errno );
629         }
630         freebuf = 1;
631     } else {
632         freebuf = 0;
633     }
634
635     output_label( buf, label, labelwidth, writeproc, writeparm, eol, html );
636
637     for ( i = 0; vals[ i ] != NULL; ++i ) {
638         for ( p = vals[ i ]; *p != '\0'; ++p ) {
639             if ( !isascii( *p )) {
640                 break;
641             }
642         }
643         notascii = ( *p != '\0' );
644         outval = notascii ? "(unable to display non-ASCII text value)"
645                 : vals[ i ];
646
647         writeoutval = 0;        /* if non-zero, write outval after switch */
648
649         switch( syntaxid ) {
650         case LDAP_SYN_CASEIGNORESTR:
651             ++writeoutval;
652             break;
653
654         case LDAP_SYN_RFC822ADDR:
655             if ( html ) {
656                 strcpy( buf, "<DD><A HREF=\"mailto:" );
657                 strcat_escaped( buf, outval );
658                 sprintf( buf + strlen( buf ), "\">%s</A><BR>%s", outval, eol );
659                 (*writeproc)( writeparm, buf, strlen( buf ));
660             } else {
661                 ++writeoutval;
662             }
663             break;
664
665         case LDAP_SYN_DN:       /* for now */
666             output_dn( buf, outval, labelwidth, rdncount, writeproc,
667                     writeparm, eol, urlprefix );
668             break;
669
670         case LDAP_SYN_MULTILINESTR:
671             if ( i > 0 && !html ) {
672                 output_label( buf, label, labelwidth, writeproc,
673                         writeparm, eol, html );
674             }
675
676             p = s = outval;
677             while (( s = strchr( s, '$' )) != NULL ) {
678                 *s++ = '\0';
679                 while ( isspace( *s )) {
680                     ++s;
681                 }
682                 if ( html ) {
683                     sprintf( buf, "<DD>%s<BR>%s", p, eol );
684                 } else {
685                     sprintf( buf, "%-*s%s%s", labelwidth, " ", p, eol );
686                 }
687                 (*writeproc)( writeparm, buf, strlen( buf ));
688                 p = s;
689             }
690             outval = p;
691             ++writeoutval;
692             break;
693
694         case LDAP_SYN_BOOLEAN:
695             outval = TOUPPER( outval[ 0 ] ) == 'T' ? "TRUE" : "FALSE";
696             ++writeoutval;
697             break;
698
699         case LDAP_SYN_TIME:
700         case LDAP_SYN_DATE:
701             outval = time2text( outval, syntaxid == LDAP_SYN_DATE );
702             ++writeoutval;
703             break;
704
705         case LDAP_SYN_LABELEDURL:
706             if ( !notascii && ( p = strchr( outval, '$' )) != NULL ) {
707                 *p++ = '\0';
708                 while ( isspace( *p )) {
709                     ++p;
710                 }
711                 s = outval;
712             } else if ( !notascii && ( s = strchr( outval, ' ' )) != NULL ) {
713                 *s++ = '\0';
714                 while ( isspace( *s )) {
715                     ++s;
716                 }
717                 p = outval;
718             } else {
719                 s = "URL";
720                 p = outval;
721             }
722
723             /*
724              * at this point `s' points to the label & `p' to the URL
725              */
726             if ( html ) {
727                 sprintf( buf, "<DD><A HREF=\"%s\">%s</A><BR>%s", p, s, eol );
728             } else {
729                 sprintf( buf, "%-*s%s%s%-*s%s%s", labelwidth, " ",
730                     s, eol, labelwidth + 2, " ",p , eol );
731             }
732             (*writeproc)( writeparm, buf, strlen( buf ));
733             break;
734
735         default:
736             sprintf( buf, " Can't display item type %ld%s",
737                     syntaxid, eol );
738             (*writeproc)( writeparm, buf, strlen( buf ));
739         }
740
741         if ( writeoutval ) {
742             if ( html ) {
743                 sprintf( buf, "<DD>%s<BR>%s", outval, eol );
744             } else {
745                 sprintf( buf, "%-*s%s%s", labelwidth, " ", outval, eol );
746             }
747             (*writeproc)( writeparm, buf, strlen( buf ));
748         }
749     }
750
751     if ( freebuf ) {
752         free( buf );
753     }
754
755     return( LDAP_SUCCESS );
756 }
757
758
759 static int
760 max_label_len( struct ldap_disptmpl *tmpl )
761 {
762     struct ldap_tmplitem        *rowp, *colp;
763     int                         len, maxlen;
764
765     maxlen = 0;
766
767     for ( rowp = ldap_first_tmplrow( tmpl ); rowp != NULLTMPLITEM;
768             rowp = ldap_next_tmplrow( tmpl, rowp )) {
769         for ( colp = ldap_first_tmplcol( tmpl, rowp ); colp != NULLTMPLITEM;
770                 colp = ldap_next_tmplcol( tmpl, rowp, colp )) {
771             if (( len = strlen( colp->ti_label )) > maxlen ) {
772                 maxlen = len;
773             }
774         }
775     }
776
777     return( maxlen );
778 }
779
780
781 static int
782 output_label( char *buf, char *label, int width, writeptype writeproc,
783         void *writeparm, char *eol, int html )
784 {
785     char        *p;
786
787     if ( html ) {
788         sprintf( buf, "<DT><B>%s</B>", label );
789     } else {
790         sprintf( buf, " %s:", label );
791         p = buf + strlen( buf );
792
793         while ( p - buf < width ) {
794             *p++ = ' ';
795         }
796
797         *p = '\0';
798         strcat( buf, eol );
799     }
800
801     return ((*writeproc)( writeparm, buf, strlen( buf )));
802 }
803
804
805 static int
806 output_dn( char *buf, char *dn, int width, int rdncount,
807         writeptype writeproc, void *writeparm, char *eol, char *urlprefix )
808 {
809     char        **dnrdns;
810     int         i;
811
812     if (( dnrdns = ldap_explode_dn( dn, 1 )) == NULL ) {
813         return( -1 );
814     }
815
816     if ( urlprefix != NULL ) {
817         sprintf( buf, "<DD><A HREF=\"%s", urlprefix );
818         strcat_escaped( buf, dn );
819         strcat( buf, "\">" );
820     } else if ( width > 0 ) {
821         sprintf( buf, "%-*s", width, " " );
822     } else {
823         *buf = '\0';
824     }
825
826     for ( i = 0; dnrdns[ i ] != NULL && ( rdncount == 0 || i < rdncount );
827             ++i ) {
828         if ( i > 0 ) {
829             strcat( buf, ", " );
830         }
831         strcat( buf, dnrdns[ i ] );
832     }
833
834     if ( urlprefix != NULL ) {
835         strcat( buf, "</A><BR>" );
836     }
837
838     ldap_value_free( dnrdns );
839
840     strcat( buf, eol );
841
842     return ((*writeproc)( writeparm, buf, strlen( buf )));
843 }
844
845
846
847 #define HREF_CHAR_ACCEPTABLE( c )       (( c >= '-' && c <= '9' ) ||    \
848                                          ( c >= '@' && c <= 'Z' ) ||    \
849                                          ( c == '_' ) ||                \
850                                          ( c >= 'a' && c <= 'z' ))
851
852 static void
853 strcat_escaped( char *s1, char *s2 )
854 {
855     char        *p, *q;
856     char        *hexdig = "0123456789ABCDEF";
857
858     p = s1 + strlen( s1 );
859     for ( q = s2; *q != '\0'; ++q ) {
860         if ( HREF_CHAR_ACCEPTABLE( *q )) {
861             *p++ = *q;
862         } else {
863             *p++ = '%';
864             *p++ = hexdig[ *q >> 4 ];
865             *p++ = hexdig[ *q & 0x0F ];
866         }
867     }
868
869     *p = '\0';
870 }
871
872
873 #define GET2BYTENUM( p )        (( *p - '0' ) * 10 + ( *(p+1) - '0' ))
874
875 static char *
876 time2text( char *ldtimestr, int dateonly )
877 {
878     struct tm           t;
879     char                *p, *timestr, zone, *fmterr = "badly formatted time";
880     time_t              gmttime;
881     char                timebuf[32];
882
883     memset( (char *)&t, 0, sizeof( struct tm ));
884     if ( (int) strlen( ldtimestr ) < 13 ) {
885         return( fmterr );
886     }
887
888     for ( p = ldtimestr; p - ldtimestr < 12; ++p ) {
889         if ( !isdigit( *p )) {
890             return( fmterr );
891         }
892     }
893
894     p = ldtimestr;
895     t.tm_year = GET2BYTENUM( p ); p += 2;
896     t.tm_mon = GET2BYTENUM( p ) - 1; p += 2;
897     t.tm_mday = GET2BYTENUM( p ); p += 2;
898     t.tm_hour = GET2BYTENUM( p ); p += 2;
899     t.tm_min = GET2BYTENUM( p ); p += 2;
900     t.tm_sec = GET2BYTENUM( p ); p += 2;
901
902     if (( zone = *p ) == 'Z' ) {        /* GMT */
903         zone = '\0';    /* no need to indicate on screen, so we make it null */
904     }
905
906     gmttime = gtime( &t );
907
908     timestr = ldap_int_ctime( &gmttime, timebuf );
909    
910     timestr[ strlen( timestr ) - 1 ] = zone;    /* replace trailing newline */
911     if ( dateonly ) {
912         SAFEMEMCPY( timestr + 11, timestr + 20, strlen( timestr + 20 ) + 1 );
913     }
914
915     return( timestr );
916 }
917
918
919
920 /* gtime.c - inverse gmtime */
921 /* gtime(): the inverse of localtime().
922         This routine was supplied by Mike Accetta at CMU many years ago.
923  */
924
925 static int      dmsize[] = {
926     31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
927 };
928
929 #define dysize(y)       \
930         (((y) % 4) ? 365 : (((y) % 100) ? 366 : (((y) % 400) ? 365 : 366)))
931
932 #define YEAR(y)         ((y) >= 100 ? (y) : (y) + 1900)
933
934 /* \f */
935
936 static long     gtime ( struct tm *tm )
937 {
938     register int    i,
939                     sec,
940                     mins,
941                     hour,
942                     mday,
943                     mon,
944                     year;
945     register long   result;
946
947     if ((sec = tm -> tm_sec) < 0 || sec > 59
948             || (mins = tm -> tm_min) < 0 || mins > 59
949             || (hour = tm -> tm_hour) < 0 || hour > 24
950             || (mday = tm -> tm_mday) < 1 || mday > 31
951             || (mon = tm -> tm_mon + 1) < 1 || mon > 12)
952         return ((long) -1);
953     if (hour == 24) {
954         hour = 0;
955         mday++;
956     }
957     year = YEAR (tm -> tm_year);
958
959     result = 0L;
960     for (i = 1970; i < year; i++)
961         result += dysize (i);
962     if (dysize (year) == 366 && mon >= 3)
963         result++;
964     while (--mon)
965         result += dmsize[mon - 1];
966     result += mday - 1;
967     result = 24 * result + hour;
968     result = 60 * result + mins;
969     result = 60 * result + sec;
970
971     return result;
972 }
973
974 static int
975 searchaction( LDAP *ld, char *buf, char *base, LDAPMessage *entry, char *dn,
976         struct ldap_tmplitem *tip, int labelwidth, int rdncount,
977         writeptype writeproc, void *writeparm, char *eol, char *urlprefix )
978 {
979     int                 err = 0, lderr, i, count, html;
980     char                **vals, **members;
981     char                *value, *filtpattern, *attr, *selectname;
982     char                *retattrs[2], filter[ 256 ];
983     LDAPMessage         *ldmp;
984     struct timeval      timeout;
985
986     html = ( urlprefix != NULL );
987
988     for ( i = 0; tip->ti_args != NULL && tip->ti_args[ i ] != NULL; ++i ) {
989         ;
990     }
991     if ( i < 3 ) {
992         return( LDAP_PARAM_ERROR );
993     }
994     attr = tip->ti_args[ 0 ];
995     filtpattern = tip->ti_args[ 1 ];
996     retattrs[ 0 ] = tip->ti_args[ 2 ];
997     retattrs[ 1 ] = NULL;
998     selectname = tip->ti_args[ 3 ];
999
1000     vals = NULL;
1001     if ( attr == NULL ) {
1002         value = NULL;
1003     } else if ( strcasecmp( attr, "-dnb" ) == 0 ) {
1004         return( LDAP_PARAM_ERROR );
1005     } else if ( strcasecmp( attr, "-dnt" ) == 0 ) {
1006         value = dn;
1007     } else if (( vals = ldap_get_values( ld, entry, attr )) != NULL ) {
1008         value = vals[ 0 ];
1009     } else {
1010         value = NULL;
1011     }
1012
1013     ldap_build_filter( filter, sizeof( filter ), filtpattern, NULL, NULL, NULL,
1014             value, NULL );
1015
1016     if ( html ) {
1017         /*
1018          * if we are generating HTML, we add an HREF link that embodies this
1019          * search action as an LDAP URL, instead of actually doing the search
1020          * now.
1021          */
1022         sprintf( buf, "<DT><A HREF=\"%s", urlprefix );
1023         if ( base != NULL ) {
1024             strcat_escaped( buf, base );
1025         }
1026         strcat( buf, "??sub?" );
1027         strcat_escaped( buf, filter );
1028         sprintf( buf + strlen( buf ), "\"><B>%s</B></A><DD><BR>%s",
1029                 tip->ti_label, eol );
1030         if ((*writeproc)( writeparm, buf, strlen( buf )) < 0 ) {
1031             return( LDAP_LOCAL_ERROR );
1032         }
1033         return( LDAP_SUCCESS );
1034     }
1035
1036     timeout.tv_sec = SEARCH_TIMEOUT_SECS;
1037     timeout.tv_usec = 0;
1038
1039 #ifdef LDAP_CONNECTIONLESS
1040     if ( LDAP_IS_CLDAP( ld ))
1041         lderr = cldap_search_s( ld, base, LDAP_SCOPE_SUBTREE, filter, retattrs,
1042                 0, &ldmp, NULL );
1043     else
1044 #endif /* LDAP_CONNECTIONLESS */
1045         lderr = ldap_search_st( ld, base, LDAP_SCOPE_SUBTREE, filter, retattrs,
1046                 0, &timeout, &ldmp );
1047
1048     if ( lderr == LDAP_SUCCESS || NONFATAL_LDAP_ERR( lderr )) {
1049         if (( count = ldap_count_entries( ld, ldmp )) > 0 ) {
1050             if (( members = (char **)malloc( (count + 1) * sizeof(char *)))
1051                     == NULL ) {
1052                 err = LDAP_NO_MEMORY;
1053             } else {
1054                 for ( i = 0, entry = ldap_first_entry( ld, ldmp );
1055                         entry != NULL;
1056                         entry = ldap_next_entry( ld, entry ), ++i ) {
1057                     members[ i ] = ldap_get_dn( ld, entry );
1058                 }
1059                 members[ i ] = NULL;
1060
1061                 ldap_sort_values( ld, members, ldap_sort_strcasecmp );
1062
1063                 err = do_vals2text( ld, NULL, members, tip->ti_label,
1064                         html ? -1 : 0, LDAP_SYN_DN, writeproc, writeparm,
1065                         eol, rdncount, urlprefix );
1066
1067                 ldap_value_free( members );
1068             }
1069         }
1070         ldap_msgfree( ldmp );
1071     }
1072
1073     
1074     if ( vals != NULL ) {
1075         ldap_value_free( vals );
1076     }
1077
1078     return(( err == LDAP_SUCCESS ) ? lderr : err );
1079 }