]> git.sur5r.net Git - openldap/blob - clients/gopher/go500gw.c
Remove incorrect use of <matched> entry
[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/stdlib.h>
18
19 #include <ac/ctype.h>
20 #include <ac/signal.h>
21 #include <ac/socket.h>
22 #include <ac/string.h>
23 #include <ac/syslog.h>
24 #include <ac/time.h>
25 #include <ac/unistd.h>
26 #include <ac/wait.h>
27
28 #include <ac/setproctitle.h>
29
30 #ifdef HAVE_SYS_PARAM_H
31 #include <sys/param.h>
32 #endif
33
34 #ifdef HAVE_SYS_RESOURCE_H
35 #include <sys/resource.h>
36 #endif
37
38
39 #include <ldap.h>
40 #include <disptmpl.h>
41
42 #define ldap_debug debug
43 #include "ldap_log.h"
44
45 #include "lutil.h"
46
47
48 #include "ldap_defaults.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, ber_len_t 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( EXIT_FAILURE );
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         socklen_t               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( EXIT_FAILURE );
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( EXIT_SUCCESS );
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( EXIT_FAILURE );
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( EXIT_FAILURE );
316         }
317
318 #ifdef SO_REUSEADDR
319         /* set option so clients can't keep us from coming back up */
320                 one = 1;
321         if ( setsockopt( s, SOL_SOCKET, SO_REUSEADDR, (char *) &one,
322             sizeof(one) ) < 0 ) {
323                 perror( "setsockopt" );
324                 exit( EXIT_FAILURE );
325         }
326 #endif
327 #ifdef SO_KEEPALIVE
328                 /* enable keep alives */
329                 one = 1;
330         if ( setsockopt( s, SOL_SOCKET, SO_KEEPALIVE, (char *) &one,
331             sizeof(one) ) < 0 ) {
332                 perror( "setsockopt" );
333                 exit( EXIT_FAILURE );
334         }
335 #endif
336
337         /* bind to a name */
338         addr.sin_family = AF_INET;
339         addr.sin_addr.s_addr = htonl(INADDR_ANY);
340         addr.sin_port = htons( port );
341         if ( bind( s, (struct sockaddr *) &addr, sizeof(addr) ) ) {
342                 perror( "bind" );
343                 exit( EXIT_FAILURE );
344         }
345
346         /* listen for connections */
347         if ( listen( s, 5 ) == -1 ) {
348                 perror( "listen" );
349                 exit( EXIT_FAILURE );
350         }
351
352         if ( debug )
353                 printf( "go500gw listening on port %d\n", port );
354
355         return( s );
356 }
357
358 static RETSIGTYPE
359 wait4child( int sig )
360 {
361 #ifndef HAVE_WAITPID
362         WAITSTATUSTYPE     status;
363 #endif
364
365         if ( debug ) printf( "parent: catching child status\n" );
366
367 #ifdef HAVE_WAITPID
368         while (waitpid ((pid_t) -1, (int *) NULL, WAIT_FLAGS) > 0)
369                 ;       /* NULL */
370 #else 
371         while (wait4( (pid_t) -1, &status, WAIT_FLAGS, 0 ) > 0 )
372                 ;       /* NULL */
373 #endif
374
375         (void) SIGNAL( SIGCHLD, wait4child );
376 }
377
378 static void
379 do_queries( int s )
380 {
381         char            buf[1024], *query;
382         int             len;
383         FILE            *fp;
384         int             rc;
385         int             deref;
386         struct timeval  timeout;
387         fd_set          readfds;
388         LDAP            *ld;
389
390         if ( (fp = fdopen( s, "a+")) == NULL ) {
391                 perror( "fdopen" );
392                 exit( EXIT_FAILURE );
393         }
394
395         timeout.tv_sec = GO500GW_TIMEOUT;
396         timeout.tv_usec = 0;
397         FD_ZERO( &readfds );
398         FD_SET( fileno( fp ), &readfds );
399
400         if ( (rc = select( dtblsize, &readfds, 0, 0, &timeout )) <= 0 )
401                 exit( EXIT_FAILURE );
402
403         if ( fgets( buf, sizeof(buf), fp ) == NULL )
404                 exit( EXIT_FAILURE );
405
406         len = strlen( buf );
407         if ( debug ) {
408                 fprintf( stderr, "got %d bytes\n", len );
409 #ifdef LDAP_DEBUG
410                 ber_bprint( buf, len );
411 #endif
412         }
413
414         /* strip of \r \n */
415         if ( buf[len - 1] == '\n' )
416                 buf[len - 1] = '\0';
417         len--;
418         if ( buf[len - 1] == '\r' )
419                 buf[len - 1] = '\0';
420         len--;
421
422         query = buf;
423
424         /* strip off leading white space */
425         while ( isspace( (unsigned char) *query )) {
426                 ++query;
427                 --len;
428         }
429
430         rewind(fp);
431
432         if ( *query == 'H' || *query == 'L' || *query == 'E' ) {
433                 switch ( *query++ ) {
434                 case 'H':       /* help file */
435                         do_help( fp );
436                         break;
437
438                 case 'L':       /* size limit explanation */
439                         do_sizelimit( fp, *query );
440                         break;
441
442                 case 'E':       /* error explanation */
443                         do_error( fp, query );
444                         break;
445                 }
446
447                 fprintf( fp, ".\r\n" );
448                 rewind(fp);
449
450                 exit( EXIT_SUCCESS );
451                 /* NOT REACHED */
452         }
453
454         if ( (ld = ldap_init( ldaphost, ldapport )) == NULL ) {
455                 if ( debug ) perror( "ldap_init" );
456                 fprintf(fp, "0An error occurred (explanation)\tE%d\t%s\t%d\r\n",
457                     LDAP_SERVER_DOWN, myhost, myport );
458                 fprintf( fp, ".\r\n" );
459                 rewind(fp);
460                 exit( EXIT_FAILURE );
461         }
462
463         deref = LDAP_DEREF_ALWAYS;
464         if ( !searchaliases )
465                 deref = LDAP_DEREF_FINDING;
466
467         ldap_set_option(ld, LDAP_OPT_DEREF, &deref);
468
469         if ( (rc = ldap_simple_bind_s( ld, NULL, NULL ))
470             != LDAP_SUCCESS ) {
471                 if ( debug ) ldap_perror( ld, "ldap_simple_bind_s" );
472                 fprintf(fp, "0An error occurred (explanation)\tE%d\t%s\t%d\r\n",
473                     rc, myhost, myport );
474                 fprintf( fp, ".\r\n" );
475                 rewind(fp);
476                 exit( EXIT_FAILURE );
477         }
478
479         switch ( *query++ ) {
480         case 'R':       /* read an entry */
481                 do_read( ld, fp, query );
482                 break;
483
484         case 'S':       /* search */
485                 do_search( ld, fp, query );
486                 break;
487
488         case 'M':       /* X.500 menu */
489                 do_menu( ld, fp, query );
490                 break;
491
492         default:
493                 do_menu( ld, fp, "" );
494                 break;
495         }
496
497         fprintf( fp, ".\r\n" );
498         rewind(fp);
499
500         exit( EXIT_SUCCESS );
501         /* NOT REACHED */
502 }
503
504 static char *
505 pick_oc( char **oclist )
506 {
507         int     i;
508
509         if ( oclist == NULL )
510                 return( "unknown" );
511
512         for ( i = 0; oclist[i] != NULL; i++ ) {
513                 if ( strcasecmp( oclist[i], "top" ) != 0 &&
514                     strcasecmp( oclist[i], "quipuObject" ) != 0 &&
515                     strcasecmp( oclist[i], "quipuNonLeafObject" ) != 0 )
516                         return( oclist[i] );
517         }
518
519         return( "unknown" );
520 }
521
522 static int
523 isnonleaf( LDAP *ld, char **oclist, char *dn )
524 {
525         int     i, quipuobject = 0;
526
527         if ( oclist == NULL )
528                 return( 0 );
529
530         for ( i = 0; oclist[i] != NULL; i++ ) {
531                 if ( strcasecmp( oclist[i], "quipuObject" ) == 0 )
532                         quipuobject = 1;
533                 if ( strcasecmp( oclist[i], "quipuNonLeafObject" ) == 0 ||
534                     strcasecmp( oclist[i], "externalNonLeafObject" ) == 0 )
535                         return( 1 );
536         }
537
538         /*
539          * not a quipu thang - no easy way to tell leaves from nonleaves
540          * except by trying to search or list.  ldap only lets us search.
541          */
542
543         /* expensive - just guess for now */
544         return( quipuobject ? 0 : 1 );
545
546 #ifdef notdef
547         if ( !quipuobject ) {
548                 int             rc, numentries;
549                 struct timeval  timeout;
550                 LDAPMessage     *res = NULL;
551                 static char     *attrs[] = { "objectClass", 0 };
552                 int sizelimit = 1;
553
554                 timeout.tv_sec = GO500GW_TIMEOUT;
555                 timeout.tv_usec = 0;
556                 ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &sizelimit);
557                 if ( (rc = ldap_search_st( ld, dn, LDAP_SCOPE_ONELEVEL,
558                     "(objectClass=*)", attrs, 0, &timeout, &res ))
559                     == LDAP_SUCCESS || rc == LDAP_SIZELIMIT_EXCEEDED ) {
560                         sizelimit = LDAP_NO_LIMIT;
561                         ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &sizelimit);
562
563                         numentries = ldap_count_entries( ld, res );
564                         if ( res != NULL )
565                                 ldap_msgfree( res );
566                         return( numentries == 1 ? 1 : 0 );
567                 }
568         }
569
570         return( 0 );
571 #endif
572 }
573
574 static void
575 do_menu( LDAP *ld, FILE *fp, char *dn )
576 {
577         char            **s;
578         char            *rdn = NULL;
579         LDAPFriendlyMap *fm = NULL;
580
581         if ( strcmp( dn, "" ) != 0 ) {
582                 s = ldap_explode_dn( dn, 1 );
583
584                 if ( s[1] == NULL )
585                         rdn = ldap_friendly_name( friendlyfile, s[0], &fm );
586                 else
587                         rdn = s[0];
588                 fprintf( fp, "0Read %s entry\tR%s\t%s\t%d\r\n", rdn ? rdn: s[0],
589                     dn, myhost, myport );
590
591                 ldap_value_free( s );
592         } else {
593                 fprintf( fp, "0About the Gopher to X.500 Gateway\tH\t%s\t%d\r\n",
594                     myhost, myport );
595         }
596
597         fprintf( fp, "7Search %s\tS%s\t%s\t%d\r\n", rdn ? rdn : "root", dn,
598             myhost, myport );
599
600         do_list( ld, fp, dn );
601
602         ldap_free_friendlymap( &fm );
603 }
604
605 static void
606 do_list( LDAP *ld, FILE *fp, char *dn )
607 {
608         int             rc;
609         LDAPMessage     *e, *res;
610         struct timeval  timeout;
611         LDAPFriendlyMap *fm = NULL;
612         static char     *attrs[] = { "objectClass", 0 };
613         int deref = LDAP_DEREF_FINDING;
614
615         timeout.tv_sec = GO500GW_TIMEOUT;
616         timeout.tv_usec = 0;
617
618         ldap_set_option(ld, LDAP_OPT_DEREF, &deref);
619
620         if ( (rc = ldap_search_st( ld, dn, LDAP_SCOPE_ONELEVEL,
621             "(!(objectClass=dSA))", attrs, 0, &timeout, &res )) != LDAP_SUCCESS
622             && rc != LDAP_SIZELIMIT_EXCEEDED ) {
623                 fprintf(fp, "0An error occurred (explanation)\tE%d\t%s\t%d\r\n",
624                     rc, myhost, myport );
625                 return;
626         }
627
628         deref = LDAP_DEREF_ALWAYS;
629         ldap_set_option(ld, LDAP_OPT_DEREF, &deref);
630
631         if ( ldap_count_entries( ld, res ) < 1 ) {
632                 return;
633         }
634
635 #ifdef GO500GW_SORT_ATTR
636         ldap_sort_entries( ld, &res, GO500GW_SORT_ATTR, strcasecmp );
637 #endif
638
639         fm = NULL;
640         for ( e = ldap_first_entry( ld, res ); e != NULL;
641             e = ldap_next_entry( ld, e ) ) {
642                 char    **s, **oc;
643                 char    *rdn, *doc;
644
645                 dn = ldap_get_dn( ld, e );
646                 s = ldap_explode_dn( dn, 1 );
647                 oc = ldap_get_values( ld, e, "objectClass" );
648
649                 doc = pick_oc( oc );
650                 if ( strcasecmp( doc, "country" ) == 0 ) {
651                         rdn = ldap_friendly_name( friendlyfile, s[0], &fm );
652                 } else {
653                         rdn = s[0];
654                 }
655                 if ( rdn == NULL ) {
656                         rdn = s[0];
657                 }
658
659                 if ( strncasecmp( rdn, "{ASN}", 5 ) != 0 ) {
660                         if ( isnonleaf( ld, oc, dn ) ) {
661                                 fprintf( fp, "1%s (%s)\tM%s\t%s\t%d\r\n", rdn,
662                                     doc, dn, myhost, myport );
663                         } else {
664                                 fprintf( fp, "0%s (%s)\tR%s\t%s\t%d\r\n", rdn,
665                                     doc, dn, myhost, myport );
666                         }
667                 }
668
669                 free( dn );
670                 ldap_value_free( s );
671                 ldap_value_free( oc );
672         }
673         ldap_free_friendlymap( &fm );
674
675         if ( ldap_result2error( ld, res, 1 ) == LDAP_SIZELIMIT_EXCEEDED ) {
676                 fprintf( fp, "0A size limit was exceeded (explanation)\tLL\t%s\t%d\r\n",
677                     myhost, myport );
678         }
679 }
680
681 static int
682 isoc( char **ocl, char *oc )
683 {
684         int     i;
685
686         for ( i = 0; ocl[i] != NULL; i++ ) {
687                 if ( strcasecmp( ocl[i], oc ) == 0 )
688                         return( 1 );
689         }
690
691         return( 0 );
692 }
693
694 static int
695 make_scope( LDAP *ld, char *dn )
696 {
697         int             scope;
698         char            **oc;
699         LDAPMessage     *res;
700         struct timeval  timeout;
701         static char     *attrs[] = { "objectClass", 0 };
702
703         if ( strcmp( dn, "" ) == 0 )
704                 return( LDAP_SCOPE_ONELEVEL );
705
706         timeout.tv_sec = GO500GW_TIMEOUT;
707         timeout.tv_usec = 0;
708         if ( ldap_search_st( ld, dn, LDAP_SCOPE_BASE, "objectClass=*",
709             attrs, 0, &timeout, &res ) != LDAP_SUCCESS ) {
710                 return( -1 );
711         }
712
713         oc = ldap_get_values( ld, ldap_first_entry( ld, res ), "objectClass" );
714
715         if ( isoc( oc, "organization" ) || isoc( oc, "organizationalUnit" ) )
716                 scope = LDAP_SCOPE_SUBTREE;
717         else
718                 scope = LDAP_SCOPE_ONELEVEL;
719
720         ldap_value_free( oc );
721         ldap_msgfree( res );
722
723         return( scope );
724 }
725
726 static void
727 do_search( LDAP *ld, FILE *fp, char *query )
728 {
729         int deref;
730         int             scope;
731         char            *base, *filter;
732         char            *filtertype;
733         int             count, rc;
734         struct timeval  timeout;
735         LDAPFiltInfo    *fi;
736         LDAPMessage     *e, *res;
737         LDAPFiltDesc    *filtd;
738         static char     *attrs[] = { "objectClass", 0 };
739
740         if ( (filter = strchr( query, '\t' )) == NULL ) {
741                 fprintf( fp, "3Missing filter!\r\n" );
742                 exit( EXIT_FAILURE );
743         }
744         *filter++ = '\0';
745         base = query;
746
747 #ifdef GO500GW_UFN
748         if ( strchr( filter, ',' ) != NULL ) {
749                 ldap_ufn_setprefix( ld, base );
750                 timeout.tv_sec = GO500GW_TIMEOUT;
751                 timeout.tv_usec = 0;
752                 ldap_ufn_timeout( (void *) &timeout );
753
754                 deref = LDAP_DEREF_FINDING;
755                 ldap_set_option(ld, LDAP_OPT_DEREF, &deref);
756
757                 if ( (rc = ldap_ufn_search_s( ld, filter, attrs, 0, &res ))
758                     != LDAP_SUCCESS && rc != LDAP_SIZELIMIT_EXCEEDED ) {
759                         fprintf(fp,
760                             "0An error occurred (explanation)\t@%d\t%s\t%d\r\n",
761                             rc, myhost, myport );
762                         return;
763                 }
764
765                 count = ldap_count_entries( ld, res );
766         } else {
767 #endif
768                 if ( (scope = make_scope( ld, base )) == -1 ) {
769                         fprintf( fp, "3Bad scope\r\n" );
770                         exit( EXIT_FAILURE );
771                 }
772
773                 filtertype = (scope == LDAP_SCOPE_ONELEVEL ?
774                     "go500gw onelevel" : "go500gw subtree");
775                 deref = (scope == LDAP_SCOPE_ONELEVEL ?
776                     LDAP_DEREF_FINDING : LDAP_DEREF_ALWAYS);
777                 ldap_set_option(ld, LDAP_OPT_DEREF, &deref);
778                 timeout.tv_sec = GO500GW_TIMEOUT;
779                 timeout.tv_usec = 0;
780
781                 if ( (filtd = ldap_init_getfilter( filterfile )) == NULL ) {
782                         fprintf( stderr, "Cannot open filter file (%s)\n",
783                             filterfile );
784                         exit( EXIT_FAILURE );
785                 }
786
787                 count = 0;
788                 res = NULL;
789                 for ( fi = ldap_getfirstfilter( filtd, filtertype, filter );
790                     fi != NULL; fi = ldap_getnextfilter( filtd ) )
791                 {
792                         if ( (rc = ldap_search_st( ld, base, scope,
793                             fi->lfi_filter, attrs, 0, &timeout, &res ))
794                             != LDAP_SUCCESS && rc != LDAP_SIZELIMIT_EXCEEDED ) {
795                                 fprintf(fp, "0An error occurred (explanation)\tE%d\t%s\t%d\r\n",
796                                     rc, myhost, myport );
797                                 return;
798                         }
799                         if ( (count = ldap_count_entries( ld, res )) != 0 )
800                                 break;
801                 }
802                 deref = LDAP_DEREF_ALWAYS;
803                 ldap_set_option(ld, LDAP_OPT_DEREF, &deref);
804                 ldap_getfilter_free( filtd );
805 #ifdef GO500GW_UFN
806         }
807 #endif
808
809         if ( count == 0 ) {
810                 return;
811         }
812
813         if ( count == 1 ) {
814                 char    *dn, **oc;
815
816                 e = ldap_first_entry( ld, res );
817                 oc = ldap_get_values( ld, e, "objectClass" );
818                 dn = ldap_get_dn( ld, e );
819
820                 if ( isnonleaf( ld, oc, dn ) ) {
821                         do_menu( ld, fp, dn );
822
823                         free( dn );
824                         return;
825                 }
826
827                 free( dn );
828                 ldap_value_free( oc );
829         }
830
831 #ifdef GO500GW_SORT_ATTR
832         ldap_sort_entries( ld, &res, GO500GW_SORT_ATTR, strcasecmp );
833 #endif
834
835         for ( e = ldap_first_entry( ld, res ); e != NULL;
836             e = ldap_next_entry( ld, e ) ) {
837                 char    **s, **oc;
838                 char    *dn;
839
840                 dn = ldap_get_dn( ld, e );
841                 s = ldap_explode_dn( dn, 1 );
842                 oc = ldap_get_values( ld, e, "objectClass" );
843
844                 if ( isnonleaf( ld, oc, dn ) )
845                         fprintf( fp, "1%s (%s)\tM%s\t%s\t%d\r\n", s[0],
846                             pick_oc( oc ), dn, myhost, myport );
847                 else
848                         fprintf( fp, "0%s (%s)\tR%s\t%s\t%d\r\n", s[0],
849                             pick_oc( oc ), dn, myhost, myport );
850
851                 free( dn );
852                 ldap_value_free( s );
853                 ldap_value_free( oc );
854         }
855
856         if ( ldap_result2error( ld, res, 1 ) == LDAP_SIZELIMIT_EXCEEDED ) {
857                 fprintf( fp, "0A size limit was exceeded (explanation)\tLS\t%s\t%d\r\n",
858                     myhost, myport );
859         }
860 }
861
862
863 static int
864 entry2textwrite( void *fp, char *buf, ber_len_t len )
865 {
866         return( fwrite( buf, len, 1, (FILE *)fp ) == 0 ? -1 : len );
867 }
868
869 static void
870 do_read( LDAP *ld, FILE *fp, char *dn )
871 {
872         static struct ldap_disptmpl *tmpllist;
873
874         ldap_init_templates( templatefile, &tmpllist );
875
876         if ( ldap_entry2text_search( ld, dn, NULL, NULL, tmpllist, NULL, NULL,
877             entry2textwrite,(void *) fp, "\r\n", rdncount, 0 )
878             != LDAP_SUCCESS ) {
879                 int ld_errno = 0;
880                 ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &ld_errno);
881
882                 fprintf(fp,
883                     "0An error occurred (explanation)\t@%s\t%s\t%d\r\n",
884                     ldap_err2string( ld_errno ), myhost, myport );
885         }
886
887         if ( tmpllist != NULL ) {
888                 ldap_free_templates( tmpllist );
889         }
890 }
891
892 static void
893 do_help( FILE *op )
894 {
895         FILE    *fp;
896         char    line[BUFSIZ];
897
898         if ( (fp = fopen( helpfile, "r" )) == NULL ) {
899                 fprintf( op, "Cannot access helpfile (%s)\r\n", helpfile );
900                 return;
901         }
902
903         while ( fgets( line, sizeof(line), fp ) != NULL ) {
904                 line[ strlen( line ) - 1 ] = '\0';
905
906                 fprintf( op, "%s\r\n", line );
907         }
908
909         fclose( fp );
910 }
911
912 static void
913 do_sizelimit( FILE *fp, char type )
914 {
915         if ( type == 'S' ) {
916                 fprintf( fp, "The query you specified was not specific enough, causing a size limit\r\n" );
917                 fprintf( fp, "to be exceeded and the first several matches found to be returned.\r\n" );
918                 fprintf( fp, "If you did not find the match you were looking for, try issuing a more\r\n" );
919                 fprintf( fp, "specific query, for example one that contains both first and last name.\r\n" );
920         } else {
921                 fprintf( fp, "Not all entries could be returned because a size limit was exceeded.\r\n" );
922                 fprintf( fp, "There is no way to defeat this feature, but if you know who you are\r\n" );
923                 fprintf( fp, "looking for, try choosing the \"Search\" option listed above and\r\n" );
924                 fprintf( fp, "specifying the name of the person you want.\r\n" );
925         }
926         fprintf( fp, ".\r\n" );
927 }
928
929 static void
930 do_error( FILE *fp, char *s )
931 {
932         int     code;
933
934         code = atoi( s );
935
936         fprintf( fp, "An error occurred searching X.500.  The error code was %d\r\n", code );
937         fprintf( fp, "The corresponding error is: %s\r\n", ldap_err2string( code ) );
938         fprintf( fp, "No additional information is available\r\n" );
939         fprintf( fp, ".\r\n" );
940 }