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