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