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