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