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