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