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