]> git.sur5r.net Git - openldap/blob - clients/gopher/go500gw.c
added ber_print_error() and renamed lber_bprint into ber_ namespace.
[openldap] / clients / gopher / go500gw.c
1 /*
2  * Copyright (c) 1990 Regents of the University of Michigan.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that this notice is preserved and that due credit is given
7  * to the University of Michigan at Ann Arbor. The name of the University
8  * may not be used to endorse or promote products derived from this
9  * software without specific prior written permission. This software
10  * is provided ``as is'' without express or implied warranty.
11  */
12
13 #include "portable.h"
14
15 #include <stdio.h>
16
17 #include <ac/ctype.h>
18 #include <ac/signal.h>
19 #include <ac/socket.h>
20 #include <ac/string.h>
21 #include <ac/syslog.h>
22 #include <ac/time.h>
23 #include <ac/unistd.h>
24 #include <ac/wait.h>
25
26 #include <sys/resource.h>
27
28 #ifdef HAVE_SYS_PARAM_H
29 #include <sys/param.h>
30 #endif
31
32 #include "lber.h"
33 #include "ldap.h"
34
35 #if LDAP_VERSION < LDAP_VERSION3
36 /* quick fix until we have ldap_set_options */
37 #include "../libraries/libldap/ldap-int.h"
38 #endif
39
40 #include "disptmpl.h"
41
42 #include "ldapconfig.h"
43
44 int     debug;
45 int     dosyslog;
46 int     inetd;
47 int     dtblsize;
48
49 char            *ldaphost = LDAPHOST;
50 int             ldapport = LDAP_PORT;
51 int             searchaliases = 1;
52 char            *helpfile = GO500GW_HELPFILE;
53 char            *filterfile = FILTERFILE;
54 char            *templatefile = TEMPLATEFILE;
55 char            *friendlyfile = FRIENDLYFILE;
56 int             rdncount = GO500GW_RDNCOUNT;
57
58 static set_socket();
59 static RETSIGTYPE wait4child();
60 static do_queries();
61 static do_menu();
62 static do_list();
63 static do_search();
64 static do_read();
65 static do_help();
66 static do_sizelimit();
67 static do_error();
68 extern int strcasecmp();
69
70 char    myhost[MAXHOSTNAMELEN];
71 int     myport = GO500GW_PORT;
72
73 static usage( name )
74 char    *name;
75 {
76         fprintf( stderr, "usage: %s [-d debuglevel] [-I] [-p port] [-P ldapport] [-l]\r\n\t[-x ldaphost] [-a] [-h helpfile] [-f filterfile] [-t templatefile] [-c rdncount]\r\n", name );
77         exit( 1 );
78 }
79
80 main (argc, argv)
81 int     argc;
82 char    **argv;
83 {
84         int                     s, ns, rc;
85         int                     port = -1;
86         int                     i, pid;
87         char                    *myname;
88         fd_set                  readfds;
89         struct hostent          *hp;
90         struct sockaddr_in      from;
91         int                     fromlen;
92         RETSIGTYPE                      wait4child();
93         extern char             *optarg;
94         extern char             **Argv;
95         extern int              Argc;
96
97         /* for setproctitle */
98         Argv = argv;
99         Argc = argc;
100
101         while ( (i = getopt( argc, argv, "P:ad:f:h:lp:t:x:Ic:" )) != EOF ) {
102                 switch( i ) {
103                 case 'a':       /* search aliases */
104                         searchaliases = 0;
105                         break;
106
107                 case 'd':       /* debugging level */
108                         debug = atoi( optarg );
109 #ifdef LDAP_DEBUG
110                         ldap_debug = debug;
111 #else
112                         fprintf( stderr, "warning: ldap debugging requires LDAP_DEBUG\n" );
113 #endif
114                         break;
115
116                 case 'f':       /* ldap filter file */
117                         filterfile = strdup( optarg );
118                         break;
119
120                 case 'h':       /* gopher help file */
121                         helpfile = strdup( optarg );
122                         break;
123
124                 case 'l':       /* log to LOG_LOCAL3 */
125                         dosyslog = 1;
126                         break;
127
128                 case 'p':       /* port to listen on */
129                         port = atoi( optarg );
130                         break;
131
132                 case 'P':       /* port to connect to ldap server */
133                         ldapport = atoi( optarg );
134                         break;
135
136                 case 't':       /* ldap template file */
137                         templatefile = strdup( optarg );
138                         break;
139
140                 case 'x':       /* ldap server hostname */
141                         ldaphost = strdup( optarg );
142                         break;
143
144                 case 'I':       /* run from inetd */
145                         inetd = 1;
146                         break;
147
148                 case 'c':       /* count of DN components to show */
149                         rdncount = atoi( optarg );
150                         break;
151
152                 default:
153                         usage( argv[0] );
154                 }
155         }
156
157 #ifdef HAVE_SYSCONF
158         dtblsize = sysconf( _SC_OPEN_MAX );
159 #elif HAVE_GETDTABLESIZE
160         dtblsize = getdtablesize();
161 #else
162         dtblsize = FD_SETSIZE;
163 #endif
164
165 #ifdef FD_SETSIZE
166         if ( dtblsize > FD_SETSIZE ) {
167                 dtblsize = FD_SETSIZE;
168         }
169 #endif  /* FD_SETSIZE*/
170
171
172
173 #ifdef GO500GW_HOSTNAME
174         strcpy( myhost, GO500GW_HOSTNAME );
175 #else
176         if ( myhost[0] == '\0' && gethostname( myhost, sizeof(myhost) )
177             == -1 ) {
178                 perror( "gethostname" );
179                 exit( 1 );
180         }
181 #endif
182
183         /* detach if stderr is redirected or no debugging */
184         if ( inetd == 0 )
185                 (void) detach( debug );
186
187         if ( (myname = strrchr( argv[0], '/' )) == NULL )
188                 myname = strdup( argv[0] );
189         else
190                 myname = strdup( myname + 1 );
191
192         if ( dosyslog ) {
193 #ifdef LOG_LOCAL3
194                 openlog( myname, OPENLOG_OPTIONS, LOG_LOCAL3 );
195 #else
196                 openlog( myname, OPENLOG_OPTIONS );
197 #endif
198         }
199         if ( dosyslog )
200                 syslog( LOG_INFO, "initializing" );
201
202         /* set up the socket to listen on */
203         if ( inetd == 0 ) {
204                 s = set_socket( port );
205
206                 /* arrange to reap children */
207                 (void) SIGNAL( SIGCHLD, wait4child );
208         }
209
210         if ( inetd ) {
211                 fromlen = sizeof(from);
212                 if ( getpeername( 0, (struct sockaddr *) &from, &fromlen )
213                     == 0 ) {
214                         hp = gethostbyaddr( (char *) &(from.sin_addr.s_addr),
215                             sizeof(from.sin_addr.s_addr), AF_INET );
216                         Debug( LDAP_DEBUG_ARGS, "connection from %s (%s)\n",
217                             (hp == NULL) ? "unknown" : hp->h_name,
218                             inet_ntoa( from.sin_addr ), 0 );
219
220                         if ( dosyslog ) {
221                                 syslog( LOG_INFO, "connection from %s (%s)",
222                                     (hp == NULL) ? "unknown" : hp->h_name,
223                                     inet_ntoa( from.sin_addr ) );
224                         }
225
226                         setproctitle( hp == NULL ? inet_ntoa( from.sin_addr ) :
227                             hp->h_name );
228                 }
229
230                 do_queries( 0 );
231
232                 close( 0 );
233
234                 exit( 0 );
235         }
236
237         for ( ;; ) {
238                 FD_ZERO( &readfds );
239                 FD_SET( s, &readfds );
240
241                 if ( (rc = select( dtblsize, &readfds, 0, 0 ,0 )) == -1 ) {
242                         if ( debug ) perror( "select" );
243                         continue;
244                 } else if ( rc == 0 ) {
245                         continue;
246                 }
247
248                 if ( ! FD_ISSET( s, &readfds ) )
249                         continue;
250
251                 fromlen = sizeof(from);
252                 if ( (ns = accept( s, (struct sockaddr *) &from, &fromlen ))
253                     == -1 ) {
254                         if ( debug ) perror( "accept" );
255                         exit( 1 );
256                 }
257
258                 hp = gethostbyaddr( (char *) &(from.sin_addr.s_addr),
259                     sizeof(from.sin_addr.s_addr), AF_INET );
260
261                 if ( dosyslog ) {
262                         syslog( LOG_INFO, "TCP connection from %s (%s)",
263                             (hp == NULL) ? "unknown" : hp->h_name,
264                             inet_ntoa( from.sin_addr ) );
265                 }
266
267                 switch( pid = fork() ) {
268                 case 0:         /* child */
269                         close( s );
270                         do_queries( ns );
271                         break;
272
273                 case -1:        /* failed */
274                         perror( "fork" );
275                         break;
276
277                 default:        /* parent */
278                         close( ns );
279                         if ( debug )
280                                 fprintf( stderr, "forked child %d\n", pid );
281                         break;
282                 }
283         }
284         /* NOT REACHED */
285 }
286
287 static set_socket( port )
288 int     port;
289 {
290         int                     s, one;
291         struct sockaddr_in      addr;
292
293         if ( port == -1 )
294                 port = GO500GW_PORT;
295         myport = port;
296
297         if ( (s = socket( AF_INET, SOCK_STREAM, 0 )) == -1 ) {
298                 perror( "socket" );
299                 exit( 1 );
300         }
301
302         /* set option so clients can't keep us from coming back up */
303         one = 1;
304         if ( setsockopt( s, SOL_SOCKET, SO_REUSEADDR, (char *) &one,
305             sizeof(one) ) < 0 ) {
306                 perror( "setsockopt" );
307                 exit( 1 );
308         }
309
310         /* bind to a name */
311         addr.sin_family = AF_INET;
312         addr.sin_addr.s_addr = INADDR_ANY;
313         addr.sin_port = htons( port );
314         if ( bind( s, (struct sockaddr *) &addr, sizeof(addr) ) ) {
315                 perror( "bind" );
316                 exit( 1 );
317         }
318
319         /* listen for connections */
320         if ( listen( s, 5 ) == -1 ) {
321                 perror( "listen" );
322                 exit( 1 );
323         }
324
325         if ( debug )
326                 printf( "go500gw listening on port %d\n", port );
327
328         return( s );
329 }
330
331 static RETSIGTYPE
332 wait4child()
333 {
334 #ifndef HAVE_WAITPID
335         WAITSTATUSTYPE     status;
336 #endif
337
338         if ( debug ) printf( "parent: catching child status\n" );
339
340 #ifdef HAVE_WAITPID
341         while (waitpid ((pid_t) -1, NULL, WAIT_FLAGS) > 0)
342                 ;       /* NULL */
343 #else 
344         while (wait3( &status, WAIT_FLAGS, 0 ) > 0 )
345                 ;       /* NULL */
346 #endif
347
348         (void) SIGNAL( SIGCHLD, wait4child );
349 }
350
351 static do_queries( s )
352 int     s;
353 {
354         char            buf[1024], *query;
355         int             len;
356         FILE            *fp;
357         int             rc;
358         struct timeval  timeout;
359         fd_set          readfds;
360         LDAP            *ld;
361
362         if ( (fp = fdopen( s, "a+")) == NULL ) {
363                 perror( "fdopen" );
364                 exit( 1 );
365         }
366
367         timeout.tv_sec = GO500GW_TIMEOUT;
368         timeout.tv_usec = 0;
369         FD_ZERO( &readfds );
370         FD_SET( fileno( fp ), &readfds );
371
372         if ( (rc = select( dtblsize, &readfds, 0, 0, &timeout )) <= 0 )
373                 exit( 1 );
374
375         if ( fgets( buf, sizeof(buf), fp ) == NULL )
376                 exit( 1 );
377
378         len = strlen( buf );
379         if ( debug ) {
380                 fprintf( stderr, "got %d bytes\n", len );
381 #ifdef LDAP_DEBUG
382                 lber_bprint( buf, len );
383 #endif
384         }
385
386         /* strip of \r \n */
387         if ( buf[len - 1] == '\n' )
388                 buf[len - 1] = '\0';
389         len--;
390         if ( buf[len - 1] == '\r' )
391                 buf[len - 1] = '\0';
392         len--;
393
394         query = buf;
395
396         /* strip off leading white space */
397         while ( isspace( *query )) {
398                 ++query;
399                 --len;
400         }
401
402         rewind(fp);
403
404         if ( *query == 'H' || *query == 'L' || *query == 'E' ) {
405                 switch ( *query++ ) {
406                 case 'H':       /* help file */
407                         do_help( fp );
408                         break;
409
410                 case 'L':       /* size limit explanation */
411                         do_sizelimit( fp, *query );
412                         break;
413
414                 case 'E':       /* error explanation */
415                         do_error( fp, query );
416                         break;
417                 }
418
419                 fprintf( fp, ".\r\n" );
420                 rewind(fp);
421
422                 exit( 0 );
423                 /* NOT REACHED */
424         }
425
426         if ( (ld = ldap_open( ldaphost, ldapport )) == NULL ) {
427                 if ( debug ) perror( "ldap_open" );
428                 fprintf(fp, "0An error occurred (explanation)\tE%d\t%s\t%d\r\n",
429                     LDAP_SERVER_DOWN, myhost, myport );
430                 fprintf( fp, ".\r\n" );
431                 rewind(fp);
432                 exit( 1 );
433         }
434
435         ld->ld_deref = LDAP_DEREF_ALWAYS;
436         if ( !searchaliases )
437                 ld->ld_deref = LDAP_DEREF_FINDING;
438
439         if ( (rc = ldap_simple_bind_s( ld, GO500GW_BINDDN, NULL ))
440             != LDAP_SUCCESS ) {
441                 if ( debug ) ldap_perror( ld, "ldap_simple_bind_s" );
442                 fprintf(fp, "0An error occurred (explanation)\tE%d\t%s\t%d\r\n",
443                     rc, myhost, myport );
444                 fprintf( fp, ".\r\n" );
445                 rewind(fp);
446                 exit( 1 );
447         }
448
449         switch ( *query++ ) {
450         case 'R':       /* read an entry */
451                 do_read( ld, fp, query );
452                 break;
453
454         case 'S':       /* search */
455                 do_search( ld, fp, query, 1 );
456                 break;
457
458         case 'M':       /* X.500 menu */
459                 do_menu( ld, fp, query );
460                 break;
461
462         default:
463                 do_menu( ld, fp, "" );
464                 break;
465         }
466
467         fprintf( fp, ".\r\n" );
468         rewind(fp);
469
470         exit( 0 );
471         /* NOT REACHED */
472 }
473
474 static char *pick_oc( oclist )
475 char    **oclist;
476 {
477         int     i;
478
479         if ( oclist == NULL )
480                 return( "unknown" );
481
482         for ( i = 0; oclist[i] != NULL; i++ ) {
483                 if ( strcasecmp( oclist[i], "top" ) != 0 &&
484                     strcasecmp( oclist[i], "quipuObject" ) != 0 &&
485                     strcasecmp( oclist[i], "quipuNonLeafObject" ) != 0 )
486                         return( oclist[i] );
487         }
488
489         return( "unknown" );
490 }
491
492 static isnonleaf( ld, oclist, dn )
493 LDAP    *ld;
494 char    **oclist;
495 char    *dn;
496 {
497         int     i, quipuobject = 0;
498
499         if ( oclist == NULL )
500                 return( 0 );
501
502         for ( i = 0; oclist[i] != NULL; i++ ) {
503                 if ( strcasecmp( oclist[i], "quipuObject" ) == 0 )
504                         quipuobject = 1;
505                 if ( strcasecmp( oclist[i], "quipuNonLeafObject" ) == 0 ||
506                     strcasecmp( oclist[i], "externalNonLeafObject" ) == 0 )
507                         return( 1 );
508         }
509
510         /*
511          * not a quipu thang - no easy way to tell leaves from nonleaves
512          * except by trying to search or list.  ldap only lets us search.
513          */
514
515         /* expensive - just guess for now */
516         return( quipuobject ? 0 : 1 );
517
518 #ifdef notdef
519         if ( !quipuobject ) {
520                 int             rc, numentries;
521                 struct timeval  timeout;
522                 LDAPMessage     *res = NULL;
523                 static char     *attrs[] = { "objectClass", 0 };
524
525                 timeout.tv_sec = GO500GW_TIMEOUT;
526                 timeout.tv_usec = 0;
527                 ld->ld_sizelimit = 1;
528                 if ( (rc = ldap_search_st( ld, dn, LDAP_SCOPE_ONELEVEL,
529                     "(objectClass=*)", attrs, 0, &timeout, &res ))
530                     == LDAP_SUCCESS || rc == LDAP_SIZELIMIT_EXCEEDED ) {
531                         ld->ld_sizelimit = LDAP_NO_LIMIT;
532
533                         numentries = ldap_count_entries( ld, res );
534                         if ( res != NULL )
535                                 ldap_msgfree( res );
536                         return( numentries == 1 ? 1 : 0 );
537                 }
538         }
539
540         return( 0 );
541 #endif
542 }
543
544 static do_menu( ld, fp, dn )
545 LDAP    *ld;
546 FILE    *fp;
547 char    *dn;
548 {
549         char            **s;
550         char            *rdn = NULL;
551         FriendlyMap     *fm = NULL;
552
553         if ( strcmp( dn, "" ) != 0 ) {
554                 s = ldap_explode_dn( dn, 1 );
555
556                 if ( s[1] == NULL )
557                         rdn = ldap_friendly_name( friendlyfile, s[0], &fm );
558                 else
559                         rdn = s[0];
560                 fprintf( fp, "0Read %s entry\tR%s\t%s\t%d\r\n", rdn ? rdn: s[0],
561                     dn, myhost, myport );
562
563                 ldap_value_free( s );
564         } else {
565                 fprintf( fp, "0About the Gopher to X.500 Gateway\tH\t%s\t%d\r\n",
566                     myhost, myport );
567         }
568
569         fprintf( fp, "7Search %s\tS%s\t%s\t%d\r\n", rdn ? rdn : "root", dn,
570             myhost, myport );
571
572         do_list( ld, fp, dn );
573
574         ldap_free_friendlymap( &fm );
575 }
576
577 static do_list( ld, fp, dn )
578 LDAP    *ld;
579 FILE    *fp;
580 char    *dn;
581 {
582         int             rc;
583         LDAPMessage     *e, *res;
584         struct timeval  timeout;
585         FriendlyMap     *fm = NULL;
586         static char     *attrs[] = { "objectClass", 0 };
587
588         timeout.tv_sec = GO500GW_TIMEOUT;
589         timeout.tv_usec = 0;
590         ld->ld_deref = LDAP_DEREF_FINDING;
591         if ( (rc = ldap_search_st( ld, dn, LDAP_SCOPE_ONELEVEL,
592             "(!(objectClass=dSA))", attrs, 0, &timeout, &res )) != LDAP_SUCCESS
593             && rc != LDAP_SIZELIMIT_EXCEEDED ) {
594                 fprintf(fp, "0An error occurred (explanation)\tE%d\t%s\t%d\r\n",
595                     rc, myhost, myport );
596                 return;
597         }
598         ld->ld_deref = LDAP_DEREF_ALWAYS;
599
600         if ( ldap_count_entries( ld, res ) < 1 ) {
601                 return;
602         }
603
604 #ifdef GO500GW_SORT_ATTR
605         ldap_sort_entries( ld, &res, GO500GW_SORT_ATTR, strcasecmp );
606 #endif
607
608         fm = NULL;
609         for ( e = ldap_first_entry( ld, res ); e != NULL;
610             e = ldap_next_entry( ld, e ) ) {
611                 char    **s, **oc;
612                 char    *rdn, *doc;
613
614                 dn = ldap_get_dn( ld, e );
615                 s = ldap_explode_dn( dn, 1 );
616                 oc = ldap_get_values( ld, e, "objectClass" );
617
618                 doc = pick_oc( oc );
619                 if ( strcasecmp( doc, "country" ) == 0 ) {
620                         rdn = ldap_friendly_name( friendlyfile, s[0], &fm );
621                 } else {
622                         rdn = s[0];
623                 }
624                 if ( rdn == NULL ) {
625                         rdn = s[0];
626                 }
627
628                 if ( strncasecmp( rdn, "{ASN}", 5 ) != 0 ) {
629                         if ( isnonleaf( ld, oc, dn ) ) {
630                                 fprintf( fp, "1%s (%s)\tM%s\t%s\t%d\r\n", rdn,
631                                     doc, dn, myhost, myport );
632                         } else {
633                                 fprintf( fp, "0%s (%s)\tR%s\t%s\t%d\r\n", rdn,
634                                     doc, dn, myhost, myport );
635                         }
636                 }
637
638                 free( dn );
639                 ldap_value_free( s );
640                 ldap_value_free( oc );
641         }
642         ldap_free_friendlymap( &fm );
643
644         if ( ldap_result2error( ld, res, 1 ) == LDAP_SIZELIMIT_EXCEEDED ) {
645                 fprintf( fp, "0A size limit was exceeded (explanation)\tLL\t%s\t%d\r\n",
646                     myhost, myport );
647         }
648 }
649
650 static isoc( ocl, oc )
651 char    **ocl;
652 char    *oc;
653 {
654         int     i;
655
656         for ( i = 0; ocl[i] != NULL; i++ ) {
657                 if ( strcasecmp( ocl[i], oc ) == 0 )
658                         return( 1 );
659         }
660
661         return( 0 );
662 }
663
664 static int make_scope( ld, dn )
665 LDAP    *ld;
666 char    *dn;
667 {
668         int             scope;
669         char            **oc;
670         LDAPMessage     *res;
671         struct timeval  timeout;
672         static char     *attrs[] = { "objectClass", 0 };
673
674         if ( strcmp( dn, "" ) == 0 )
675                 return( LDAP_SCOPE_ONELEVEL );
676
677         timeout.tv_sec = GO500GW_TIMEOUT;
678         timeout.tv_usec = 0;
679         if ( ldap_search_st( ld, dn, LDAP_SCOPE_BASE, "objectClass=*",
680             attrs, 0, &timeout, &res ) != LDAP_SUCCESS ) {
681                 return( -1 );
682         }
683
684         oc = ldap_get_values( ld, ldap_first_entry( ld, res ), "objectClass" );
685
686         if ( isoc( oc, "organization" ) || isoc( oc, "organizationalUnit" ) )
687                 scope = LDAP_SCOPE_SUBTREE;
688         else
689                 scope = LDAP_SCOPE_ONELEVEL;
690
691         ldap_value_free( oc );
692         ldap_msgfree( res );
693
694         return( scope );
695 }
696
697 static do_search( ld, fp, query )
698 LDAP    *ld;
699 FILE    *fp;
700 char    *query;
701 {
702         int             scope;
703         char            *base, *filter;
704         char            *filtertype;
705         int             count, rc;
706         struct timeval  timeout;
707         LDAPFiltInfo    *fi;
708         LDAPMessage     *e, *res;
709         LDAPFiltDesc    *filtd;
710         static char     *attrs[] = { "objectClass", 0 };
711
712         if ( (filter = strchr( query, '\t' )) == NULL ) {
713                 fprintf( fp, "3Missing filter!\r\n" );
714                 exit( 1 );
715         }
716         *filter++ = '\0';
717         base = query;
718
719 #ifdef GO500GW_UFN
720         if ( strchr( filter, ',' ) != NULL ) {
721                 ldap_ufn_setprefix( ld, base );
722                 timeout.tv_sec = GO500GW_TIMEOUT;
723                 timeout.tv_usec = 0;
724                 ldap_ufn_timeout( (void *) &timeout );
725                 ld->ld_deref = LDAP_DEREF_FINDING;
726
727                 if ( (rc = ldap_ufn_search_s( ld, filter, attrs, 0, &res ))
728                     != LDAP_SUCCESS && rc != LDAP_SIZELIMIT_EXCEEDED ) {
729                         fprintf(fp,
730                             "0An error occurred (explanation)\t@%d\t%s\t%d\r\n",
731                             rc, myhost, myport );
732                         return;
733                 }
734
735                 count = ldap_count_entries( ld, res );
736         } else {
737 #endif
738                 if ( (scope = make_scope( ld, base )) == -1 ) {
739                         fprintf( fp, "3Bad scope\r\n" );
740                         exit( 1 );
741                 }
742
743                 filtertype = (scope == LDAP_SCOPE_ONELEVEL ?
744                     "go500gw onelevel" : "go500gw subtree");
745                 ld->ld_deref = (scope == LDAP_SCOPE_ONELEVEL ?
746                     LDAP_DEREF_FINDING : LDAP_DEREF_ALWAYS);
747                 timeout.tv_sec = GO500GW_TIMEOUT;
748                 timeout.tv_usec = 0;
749
750                 if ( (filtd = ldap_init_getfilter( filterfile )) == NULL ) {
751                         fprintf( stderr, "Cannot open filter file (%s)\n",
752                             filterfile );
753                         exit( 1 );
754                 }
755
756                 count = 0;
757                 res = NULL;
758                 for ( fi = ldap_getfirstfilter( filtd, filtertype, filter );
759                     fi != NULL; fi = ldap_getnextfilter( filtd ) )
760                 {
761                         if ( (rc = ldap_search_st( ld, base, scope,
762                             fi->lfi_filter, attrs, 0, &timeout, &res ))
763                             != LDAP_SUCCESS && rc != LDAP_SIZELIMIT_EXCEEDED ) {
764                                 fprintf(fp, "0An error occurred (explanation)\tE%d\t%s\t%d\r\n",
765                                     rc, myhost, myport );
766                                 return( 1 );
767                         }
768                         if ( (count = ldap_count_entries( ld, res )) != 0 )
769                                 break;
770                 }
771                 ld->ld_deref = LDAP_DEREF_ALWAYS;
772                 ldap_getfilter_free( filtd );
773 #ifdef GO500GW_UFN
774         }
775 #endif
776
777         if ( count == 0 ) {
778                 return( 0 );
779         }
780
781         if ( count == 1 ) {
782                 char    *dn, **s, **oc;
783                 int     rc;
784
785                 e = ldap_first_entry( ld, res );
786                 oc = ldap_get_values( ld, e, "objectClass" );
787                 dn = ldap_get_dn( ld, e );
788
789                 if ( isnonleaf( ld, oc, dn ) ) {
790                         rc = do_menu( ld, fp, dn );
791
792                         free( dn );
793                         return( rc );
794                 }
795
796                 free( dn );
797                 ldap_value_free( oc );
798         }
799
800 #ifdef GO500GW_SORT_ATTR
801         ldap_sort_entries( ld, &res, GO500GW_SORT_ATTR, strcasecmp );
802 #endif
803
804         for ( e = ldap_first_entry( ld, res ); e != NULL;
805             e = ldap_next_entry( ld, e ) ) {
806                 char    **s, **oc;
807                 char    *dn;
808
809                 dn = ldap_get_dn( ld, e );
810                 s = ldap_explode_dn( dn, 1 );
811                 oc = ldap_get_values( ld, e, "objectClass" );
812
813                 if ( isnonleaf( ld, oc, dn ) )
814                         fprintf( fp, "1%s (%s)\tM%s\t%s\t%d\r\n", s[0],
815                             pick_oc( oc ), dn, myhost, myport );
816                 else
817                         fprintf( fp, "0%s (%s)\tR%s\t%s\t%d\r\n", s[0],
818                             pick_oc( oc ), dn, myhost, myport );
819
820                 free( dn );
821                 ldap_value_free( s );
822                 ldap_value_free( oc );
823         }
824
825         if ( ldap_result2error( ld, res, 1 ) == LDAP_SIZELIMIT_EXCEEDED ) {
826                 fprintf( fp, "0A size limit was exceeded (explanation)\tLS\t%s\t%d\r\n",
827                     myhost, myport );
828         }
829 }
830
831
832 static int
833 entry2textwrite( void *fp, char *buf, int len )
834 {
835         return( fwrite( buf, len, 1, (FILE *)fp ) == 0 ? -1 : len );
836 }
837
838 static do_read( ld, fp, dn )
839 LDAP    *ld;
840 FILE    *fp;
841 char    *dn;
842 {
843         static struct ldap_disptmpl *tmpllist;
844
845         ldap_init_templates( templatefile, &tmpllist );
846
847         if ( ldap_entry2text_search( ld, dn, NULL, NULL, tmpllist, NULL, NULL,
848             entry2textwrite,(void *) fp, "\r\n", rdncount, 0 )
849             != LDAP_SUCCESS ) {
850                 fprintf(fp,
851                     "0An error occurred (explanation)\t@%d\t%s\t%d\r\n",
852                     ld->ld_errno, myhost, myport );
853         }
854
855         if ( tmpllist != NULL ) {
856                 ldap_free_templates( tmpllist );
857         }
858 }
859
860 static do_help( op )
861 FILE    *op;
862 {
863         FILE    *fp;
864         char    line[BUFSIZ];
865
866         if ( (fp = fopen( helpfile, "r" )) == NULL ) {
867                 fprintf( op, "Cannot access helpfile (%s)\r\n", helpfile );
868                 return;
869         }
870
871         while ( fgets( line, sizeof(line), fp ) != NULL ) {
872                 line[ strlen( line ) - 1 ] = '\0';
873
874                 fprintf( op, "%s\r\n", line );
875         }
876
877         fclose( fp );
878 }
879
880 static do_sizelimit( fp, type )
881 FILE    *fp;
882 char    type;
883 {
884         if ( type == 'S' ) {
885                 fprintf( fp, "The query you specified was not specific enough, causing a size limit\r\n" );
886                 fprintf( fp, "to be exceeded and the first several matches found to be returned.\r\n" );
887                 fprintf( fp, "If you did not find the match you were looking for, try issuing a more\r\n" );
888                 fprintf( fp, "specific query, for example one that contains both first and last name.\r\n" );
889         } else {
890                 fprintf( fp, "Not all entries could be returned because a size limit was exceeded.\r\n" );
891                 fprintf( fp, "There is no way to defeat this feature, but if you know who you are\r\n" );
892                 fprintf( fp, "looking for, try choosing the \"Search\" option listed above and\r\n" );
893                 fprintf( fp, "specifying the name of the person you want.\r\n" );
894         }
895         fprintf( fp, ".\r\n" );
896 }
897
898 static do_error( fp, s )
899 FILE    *fp;
900 char    *s;
901 {
902         int     code;
903
904         code = atoi( s );
905
906         fprintf( fp, "An error occurred searching X.500.  The error code was %d\r\n", code );
907         fprintf( fp, "The corresponding error is: %s\r\n", ldap_err2string( code ) );
908         fprintf( fp, "No additional information is available\r\n" );
909         fprintf( fp, ".\r\n" );
910 }