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