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