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