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