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