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