]> git.sur5r.net Git - openldap/blob - clients/fax500/main.c
Include ac/ctype.h
[openldap] / clients / fax500 / main.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/socket.h>
19 #include <ac/string.h>
20 #include <ac/ctype.h>
21 #include <ac/syslog.h>
22 #include <ac/time.h>
23 #include <ac/wait.h>
24 #include <ac/unistd.h>
25 #include <ac/errno.h>
26
27 #ifdef HAVE_SYS_PARAM_H
28 #include <sys/param.h>
29 #endif
30 #ifdef HAVE_SYS_RESOURCE_H
31 #include <sys/resource.h>
32 #endif
33
34 #include <sysexits.h>
35
36 #include "lber.h"
37 #include "ldap.h"
38
39 #include "fax500.h"
40
41 #include <ldapconfig.h>
42
43 #define USER            0
44 #define GROUP_ERRORS    1
45 #define GROUP_REQUEST   2
46 #define GROUP_MEMBERS   3
47
48 #define ERRORS  "errors"
49 #define REQUEST "request"
50 #define MEMBERS "members"
51
52 LDAP    *ld;
53 char    *errorsfrom = NULL;
54 char    *mailfrom = NULL;
55 char    *host = NULL;
56 int     hostlen = 0;
57
58 int     identity;
59 #define MAIL500 1       
60 #define FAX500  2
61
62 typedef struct errs {
63         int             e_code;
64 #define E_USERUNKNOWN           1
65 #define E_AMBIGUOUS             2
66 #define E_NOEMAIL               3
67 #define E_NOREQUEST             4
68 #define E_NOERRORS              5
69 #define E_BADMEMBER             6
70 #define E_JOINMEMBERNOEMAIL     7
71 #define E_MEMBERNOEMAIL         8
72 #define E_NOFAXNUM              9
73 #define E_JOINMEMBERNOFAXNUM    10
74 #define E_MEMBERNOFAXNUM        11
75 #define E_FAXTOEMAILMBR         12
76
77         char            *e_addr;
78         LDAPMessage     *e_msg;
79 } Error;
80
81 typedef struct groupto {
82         char    *g_dn;
83         char    *g_errorsto;
84         char    **g_members;
85 } Group;
86
87 typedef struct baseinfo {
88         char    *b_dn;          /* dn to start searching at */
89         char    b_rdnpref;      /* give rdn's preference when searching? */
90         char    *b_filter[3];   /* filter to apply - name substituted for %s */
91                                 /* (up to three of them) */
92 } Base;
93
94 Base    base[] = 
95         { "ou=People, o=University of Michigan, c=US", 0,
96                 "uid=%s", "cn=%s", NULL,
97           "ou=System Groups, ou=Groups, o=University of Michigan, c=US", 1,
98                 "(&(cn=%s)(associatedDomain=%h))", NULL, NULL,
99           "ou=User Groups, ou=Groups, o=University of Michigan, c=US", 1,
100                 "(&(cn=%s)(associatedDomain=%h))", NULL, NULL,
101           NULL
102         };
103
104 char    *sendmailargs[] = { FAX_SENDMAIL, "-oMrX.500", "-odi", "-oi", "-f", NULL, NULL };
105
106 static char     *attrs[] = { "objectClass", "title", "postaladdress",
107                         "telephoneNumber", "mail", "description",
108                         "errorsTo", "rfc822ErrorsTo", "requestsTo",
109                         "rfc822RequestsTo", "joinable", "cn", "member",
110                         "facsimileTelephoneNumber", NULL };
111
112
113 static void do_address(char *name, char ***to, int *nto, Group **togroups, int *ngroups, Error **err, int *nerr, int type);
114 static int  do_group(LDAPMessage *e, char *dn, char ***to, int *nto, Group **togroups, int *ngroups, Error **err, int *nerr);
115 static void do_group_members(LDAPMessage *e, char *dn, char ***to, int *nto, Group **togroups, int *ngroups, Error **err, int *nerr);
116 static void send_message(char **to);
117 static void send_errors(Error *err, int nerr);
118 static void do_noemailorfax(FILE *fp, Error *err, int namelen, int errtype);
119 static void do_ambiguous(FILE *fp, Error *err, int namelen);
120 static int  count_values(char **list);
121 static void add_to(char ***list, int *nlist, char **new);
122 static int  isgroup(LDAPMessage *e);
123 static void add_error(Error **err, int *nerr, int code, char *addr, LDAPMessage *msg);
124 static void add_group(char *dn, Group **list, int *nlist);
125 static void unbind_and_exit(int rc);
126 static int  group_loop(char *dn);
127 static void send_group(Group *group, int ngroup);
128 static int  has_attributes(LDAPMessage *e, char *attr1, char *attr2);
129 static char **get_attributes_mail_dn(LDAPMessage *e, char *attr1, char *attr2);
130 static char *canonical(char *s);
131 static int  connect_to_x500 (void);
132 static void do_group_errors (LDAPMessage *e, char *dn, char ***to, int *nto, Error **err, int *nerr);
133 static void do_group_request (LDAPMessage *e, char *dn, char ***to, int *nto, Error **err, int *nerr);
134 static void add_member (char *gdn, char *dn, char ***to, int *nto, Group **togroups, int *ngroups, Error **err, int *nerr);
135
136
137 int
138 main ( int argc, char **argv )
139 {
140         char            *myname;
141         char            **tolist;
142         Error           *errlist;
143         Group           *togroups;
144         int             numto, ngroups, numerr, nargs;
145         int             i, j;
146
147         while ( (i = getopt( argc, argv, "f:h:m:" )) != EOF ) {
148                 switch( i ) {
149                 case 'f':       /* who it's from & where errors should go */
150                         mailfrom = strdup( optarg );
151                         for ( j = 0; sendmailargs[j] != NULL; j++ ) {
152                                 if ( strcmp( sendmailargs[j], "-f" ) == 0 ) {
153                                         sendmailargs[j+1] = mailfrom;
154                                         break;
155                                 }
156                         }
157                         break;
158
159                 case 'h':       /* hostname */
160                         host = strdup( optarg );
161                         hostlen = strlen(host);
162                         break;
163
164                                 /* mailer-daemon address - who we should */
165                 case 'm':       /* say errors come from */
166                         errorsfrom = strdup( optarg );
167                         break;
168
169                 default:
170                         syslog( LOG_ALERT, "unknown option" );
171                         break;
172                 }
173         }
174
175         if ( (myname = strrchr( argv[0], '/' )) == NULL )
176                 myname = strdup( argv[0] );
177         else
178                 myname = strdup( myname + 1 );
179         if (!strcmp(myname, "mail500")) {
180                 identity = MAIL500;
181         } else if (!strcmp(myname, "fax500")) {
182                 identity = FAX500;
183         } else {
184                 /* I give up, I must be mail500 */
185                 identity = MAIL500;
186         }
187
188 #ifdef LOG_MAIL
189         openlog( myname, OPENLOG_OPTIONS, LOG_MAIL );
190 #else
191         openlog( myname, OPENLOG_OPTIONS );
192 #endif
193
194         if ( mailfrom == NULL ) {
195                 syslog( LOG_ALERT, "required argument -f not present" );
196                 exit( EX_TEMPFAIL );
197         }
198         if ( errorsfrom == NULL ) {
199                 syslog( LOG_ALERT, "required argument -m not present" );
200                 exit( EX_TEMPFAIL );
201         }
202         if ( host == NULL ) {
203                 syslog( LOG_ALERT, "required argument -h not present" );
204                 exit( EX_TEMPFAIL );
205         }
206
207         if ( connect_to_x500() != 0 )
208                 exit( EX_TEMPFAIL );
209
210         setuid( geteuid() );
211 /*
212 {
213         char    buf[1024];
214         int     i;
215
216         strcpy( buf, argv[0] );
217         for ( i = 1; i < argc; i++ ) {
218                 strcat( buf, " " );
219                 strcat( buf, argv[i] );
220         }
221
222         syslog( LOG_ALERT, "args: (%s)", buf );
223 }
224 */
225         numto = 0;
226         add_to( &tolist, &numto, sendmailargs );
227         nargs = numto;
228         ngroups = numerr = 0;
229         togroups = NULL;
230         errlist = NULL;
231         for ( i = optind; i < argc; i++ ) {
232                 char    *s;
233                 int     type;
234
235                 for ( j = 0; argv[i][j] != '\0'; j++ ) {
236                         if ( argv[i][j] == '.' || argv[i][j] == '_' )
237                                 argv[i][j] = ' ';
238                 }
239
240                 type = USER;
241                 if ( (s = strrchr( argv[i], '-' )) != NULL ) {
242                         s++;
243
244                         if ( strcmp( s, ERRORS ) == 0 ) {
245                                 type = GROUP_ERRORS;
246                                 *(--s) = '\0';
247                         } else if ( strcmp( s, REQUEST ) == 0 ) {
248                                 type = GROUP_REQUEST;
249                                 *(--s) = '\0';
250                         } else if ( strcmp( s, MEMBERS ) == 0 ) {
251                                 type = GROUP_MEMBERS;
252                                 *(--s) = '\0';
253                         }
254                 }
255
256                 do_address( argv[i], &tolist, &numto, &togroups, &ngroups,
257                     &errlist, &numerr, type );
258         }
259
260         /*
261          * If we have both errors and successful deliveries to make or if
262          * if there are any groups to deliver to, we basically need to read
263          * the message twice.  So, we have to put it in a tmp file.
264          */
265
266         if ( numerr > 0 && numto > nargs || ngroups > 0 ) {
267                 FILE    *fp;
268                 char    buf[BUFSIZ];
269
270                 umask( 077 );
271                 if ( (fp = tmpfile()) == NULL ) {
272                         syslog( LOG_ALERT, "could not open tmp file" );
273                         unbind_and_exit( EX_TEMPFAIL );
274                 }
275
276                 /* copy the message to a temp file */
277                 while ( fgets( buf, sizeof(buf), stdin ) != NULL ) {
278                         if ( fputs( buf, fp ) == EOF ) {
279                                 syslog( LOG_ALERT, "error writing tmpfile" );
280                                 unbind_and_exit( EX_TEMPFAIL );
281                         }
282                 }
283
284                 if ( dup2( fileno( fp ), 0 ) == -1 ) {
285                         syslog( LOG_ALERT, "could not dup2 tmpfile" );
286                         unbind_and_exit( EX_TEMPFAIL );
287                 }
288
289                 fclose( fp );
290         }
291
292         /* deal with errors */
293         if ( numerr > 0 ) {
294                 (void) rewind( stdin );
295                 send_errors( errlist, numerr );
296         }
297
298         (void) ldap_unbind( ld );
299
300         /* send to groups with errorsTo */
301         if ( ngroups > 0 ) {
302                 (void) rewind( stdin );
303                 send_group( togroups, ngroups );
304         }
305
306         /* send to expanded aliases and groups w/o errorsTo */
307         if ( numto > nargs ) {
308                 (void) rewind( stdin );
309                 send_message( tolist );
310         }
311
312         return( EX_OK );
313 }
314
315 static int
316 connect_to_x500( void )
317 {
318         int sizelimit = FAX_MAXAMBIGUOUS;
319         int deref = LDAP_DEREF_ALWAYS;
320
321         if ( (ld = ldap_open( NULL, 0 )) == NULL ) {
322                 syslog( LOG_ALERT, "ldap_open failed" );
323                 return( -1 );
324         }
325
326         ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &sizelimit);
327         ldap_set_option(ld, LDAP_OPT_DEREF, &deref);
328
329         if ( ldap_simple_bind_s( ld, NULL, NULL ) != LDAP_SUCCESS ) {
330                 syslog( LOG_ALERT, "ldap_simple_bind_s failed" );
331                 return( -1 );
332         }
333
334         return( 0 );
335 }
336
337
338 static void
339 do_address(
340         char            *name,
341         char            ***to,
342         int             *nto,
343         Group           **togroups,
344         int             *ngroups,
345         Error           **err,
346         int             *nerr,
347         int             type
348 )
349 {
350         int             rc, b, f, match;
351         LDAPMessage     *e, *res;
352         struct timeval  timeout;
353         char            *dn;
354         char            filter[1024];
355         char            **mail;
356         char            **fax;
357
358         /*
359          * Look up the name in X.500, add the appropriate addresses found
360          * to the to list, or to the err list in case of error.  Groups are
361          * handled by the do_group routine, individuals are handled here.
362          * When looking up name, we follow the bases hierarchy, looking
363          * in base[0] first, then base[1], etc.  For each base, there is
364          * a set of search filters to try, in order.  If something goes
365          * wrong here trying to contact X.500, we exit with EX_TEMPFAIL.
366          * If the b_rdnpref flag is set, then we give preference to entries
367          * that matched name because it's their rdn, otherwise not.
368          */
369
370         timeout.tv_sec = FAX_TIMEOUT;
371         timeout.tv_usec = 0;
372         for ( b = 0, match = 0; !match && base[b].b_dn != NULL; b++ ) {
373                 for ( f = 0; base[b].b_filter[f] != NULL; f++ ) {
374                         char    *format, *p, *argv[3];
375                         int     argc;
376
377                         for ( argc = 0; argc < 3; argc++ ) {
378                                 argv[argc] = NULL;
379                         }
380
381                         format = strdup( base[b].b_filter[f] );
382                         for ( argc = 0, p = format; *p; p++ ) {
383                                 if ( *p == '%' ) {
384                                         switch ( *++p ) {
385                                         case 's':       /* %s is the name */
386                                                 argv[argc] = name;
387                                                 break;
388
389                                         case 'h':       /* %h is the host */
390                                                 *p = 's';
391                                                 argv[argc] = host;
392                                                 break;
393
394                                         default:
395                                                 syslog( LOG_ALERT,
396                                                     "unknown format %c", *p );
397                                                 break;
398                                         }
399
400                                         argc++;
401                                 }
402                         }
403
404                         /* three names ought to do... */
405                         sprintf( filter, format, argv[0], argv[1], argv[2] );
406                         free( format );
407
408                         res = NULL;
409                         rc = ldap_search_st( ld, base[b].b_dn,
410                             LDAP_SCOPE_SUBTREE, filter, attrs, 0, &timeout,
411                             &res );
412
413                         /* some other trouble - try again later */
414                         if ( rc != LDAP_SUCCESS &&
415                             rc != LDAP_SIZELIMIT_EXCEEDED ) {
416                                 syslog( LOG_ALERT, "return %d from X.500", rc );
417                                 unbind_and_exit( EX_TEMPFAIL );
418                         }
419
420                         if ( (match = ldap_count_entries( ld, res )) != 0 )
421                                 break;
422
423                         ldap_msgfree( res );
424                 }
425
426                 if ( match )
427                         break;
428         }
429
430         /* trouble - try again later */
431         if ( match == -1 ) {
432                 syslog( LOG_ALERT, "error parsing result from X.500" );
433                 unbind_and_exit( EX_TEMPFAIL );
434         }
435
436         /* no matches - bounce with user unknown */
437         if ( match == 0 ) {
438                 add_error( err, nerr, E_USERUNKNOWN, name, NULLMSG );
439                 return;
440         }
441
442         /* more than one match - bounce with ambiguous user? */
443         if ( match > 1 ) {
444                 LDAPMessage     *next, *tmpres = NULL;
445                 char            *dn;
446                 char            **xdn;
447
448                 /* not giving rdn preference - bounce with ambiguous user */
449                 if ( base[b].b_rdnpref == 0 ) {
450                         add_error( err, nerr, E_AMBIGUOUS, name, res );
451                         return;
452                 }
453
454                 /*
455                  * giving rdn preference - see if any entries were matched
456                  * because of their rdn.  If so, collect them to deal with
457                  * later (== 1 we deliver, > 1 we bounce).
458                  */
459
460                 for ( e = ldap_first_entry( ld, res ); e != NULL; e = next ) {
461                         next = ldap_next_entry( ld, e );
462                         dn = ldap_get_dn( ld, e );
463                         xdn = ldap_explode_dn( dn, 1 );
464
465                         /* XXX bad, but how else can we do it? XXX */
466                         if ( strcasecmp( xdn[0], name ) == 0 ) {
467                                 ldap_delete_result_entry( &res, e );
468                                 ldap_add_result_entry( &tmpres, e );
469                         }
470
471                         ldap_value_free( xdn );
472                         free( dn );
473                 }
474
475                 /* nothing matched by rdn - go ahead and bounce */
476                 if ( tmpres == NULL ) {
477                         add_error( err, nerr, E_AMBIGUOUS, name, res );
478                         return;
479
480                 /* more than one matched by rdn - bounce with rdn matches */
481                 } else if ( (match = ldap_count_entries( ld, tmpres )) > 1 ) {
482                         add_error( err, nerr, E_AMBIGUOUS, name, tmpres );
483                         return;
484
485                 /* trouble... */
486                 } else if ( match < 0 ) {
487                         syslog( LOG_ALERT, "error parsing result from X.500" );
488                         unbind_and_exit( EX_TEMPFAIL );
489                 }
490
491                 /* otherwise one matched by rdn - send to it */
492                 ldap_msgfree( res );
493                 res = tmpres;
494         }
495
496         /*
497          * if we get this far, it means that we found a single match for
498          * name.  for a user, we deliver to the mail attribute or bounce
499          * with address and phone if no mail attr.  for a group, we
500          * deliver to all members or bounce to rfc822ErrorsTo if no members.
501          */
502
503         /* trouble */
504         if ( (e = ldap_first_entry( ld, res )) == NULL ) {
505                 syslog( LOG_ALERT, "error parsing entry from X.500" );
506                 unbind_and_exit( EX_TEMPFAIL );
507         }
508
509         dn = ldap_get_dn( ld, e );
510
511         if ( type == GROUP_ERRORS ) {
512                 /* sent to group-errors - resend to [rfc822]ErrorsTo attr */
513                 do_group_errors( e, dn, to, nto, err, nerr );
514
515         } else if ( type == GROUP_REQUEST ) {
516                 /* sent to group-request - resend to [rfc822]RequestsTo attr */
517                 do_group_request( e, dn, to, nto, err, nerr );
518
519         } else if ( type == GROUP_MEMBERS ) {
520                 /* sent to group-members - expand */
521                 do_group_members( e, dn, to, nto, togroups, ngroups, err,
522                     nerr );
523
524         } else if ( isgroup( e ) ) {
525                 /* 
526                  * sent to group - resend from [rfc822]ErrorsTo if it's there,
527                  * otherwise, expand the group
528                  */
529
530                 do_group( e, dn, to, nto, togroups, ngroups, err, nerr );
531
532                 ldap_msgfree( res );
533
534         } else {
535                 /*
536                  * sent to user - mail attribute => add it to the to list,
537                  * otherwise bounce
538                  */
539                 switch (identity) {
540                 case FAX500 :   
541                         if ( (mail = ldap_get_values( ld, e, 
542                              "facsimileTelephoneNumber" )) != NULL ) {
543                                 char *fdn;
544                                 char **ufn;
545                                 int i;
546
547                                 fdn = ldap_get_dn( ld, e );
548                                 ufn = ldap_explode_dn( fdn, 1 );
549                                 /* Convert spaces to underscores for rp */
550                                 for (i = 0; ufn[0][i] != '\0'; i++) {
551                                         if (ufn[0][i] == ' ') {
552                                                 ufn[0][i] = '_';
553                                         }
554                                 }
555                                 *mail = faxtotpc(*mail, ufn[0]);
556                                 
557                                 add_to(to, nto, mail);
558
559                                 if (fdn != NULL) {
560                                         free(fdn);
561                                 }
562                                 ldap_value_free( ufn );
563                                 ldap_value_free( mail );
564                                 ldap_msgfree( res );
565                         } else {
566                                 add_error( err, nerr, E_NOFAXNUM, 
567                                            name, res );
568                         }
569                         break;
570                 case MAIL500 :
571                         if ( (mail = ldap_get_values( ld, e, "mail" ))
572                              != NULL ) {
573                                 add_to( to, nto, mail );
574
575                                 ldap_value_free( mail );
576                                 ldap_msgfree( res );
577                         } else {
578                                 add_error( err, nerr, E_NOEMAIL, 
579                                            name, res );
580                         }
581                         break;
582                 }
583         }
584
585         free( dn );
586
587         return;
588 }
589
590 static int
591 do_group(
592         LDAPMessage     *e,
593         char            *dn,
594         char            ***to,
595         int             *nto,
596         Group           **togroups,
597         int             *ngroups,
598         Error           **err,
599         int             *nerr
600 )
601 {
602         /*
603          * If this group has an rfc822ErrorsTo attribute, we need to
604          * arrange for errors involving this group to go there, not
605          * to the sender.  Since sendmail only has the concept of a
606          * single sender, we arrange for errors to go to groupname-errors,
607          * which we then handle specially when (if) it comes back to us
608          * by expanding to all the rfc822ErrorsTo addresses.  If it has no
609          * rfc822ErrorsTo attribute, we call do_group_members() to expand
610          * the group.
611          */
612
613         if ( group_loop( dn ) ) {
614                 return( -1 );
615         }
616
617         if ( has_attributes( e, "rfc822ErrorsTo", "errorsTo" ) ) {
618                 add_group( dn, togroups, ngroups );
619
620                 return( 0 );
621         }
622
623         do_group_members( e, dn, to, nto, togroups, ngroups, err, nerr );
624
625         return( 0 );
626 }
627
628 /* ARGSUSED */
629 static void
630 do_group_members(
631         LDAPMessage     *e,
632         char            *dn,
633         char            ***to,
634         int             *nto,
635         Group           **togroups,
636         int             *ngroups,
637         Error           **err,
638         int             *nerr
639 )
640 {
641         int             i, rc;
642         char            *ndn;
643         char            **mail, **member, **joinable;
644         char            filter[256];
645         LDAPMessage     *ee, *res;
646         struct timeval  timeout;
647
648         /*
649          * if all has gone according to plan, we've already arranged for
650          * errors to go to the [rfc822]ErrorsTo attributes (if they exist),
651          * so all we have to do here is arrange to send to the
652          * rfc822Mailbox attribute, the member attribute, and anyone who
653          * has joined the group by setting memberOfGroup equal to the
654          * group dn.
655          */
656
657         /* add members in the group itself - mail attribute */
658         if ( (mail = ldap_get_values( ld, e, "mail" )) != NULL ) {
659                 /* 
660                  * These are only email addresses - impossible 
661                  * to have a fax number
662                  */
663                 switch (identity) {
664                 case FAX500 :
665                         /* XXX How do we want to inform sender that the */
666                         /* group they sent to has email-only members? */
667                         break;
668                 case MAIL500 :
669                         add_to( to, nto, mail );
670                         break;
671                 }
672
673                 ldap_value_free( mail ); 
674         }
675
676         /* add members in the group itself - member attribute */
677         if ( (member = ldap_get_values( ld, e, "member" )) != NULL ) {
678                 for ( i = 0; member[i] != NULL; i++ ) {
679                         add_member( dn, member[i], to, nto, togroups,
680                             ngroups, err, nerr );
681                 }
682
683                 ldap_value_free( member );
684         }
685
686         /* add members who have joined by setting memberOfGroup */
687         if ( (joinable = ldap_get_values( ld, e, "joinable" )) != NULL ) {
688                 if ( strcasecmp( joinable[0], "FALSE" ) == 0 ) {
689                         ldap_value_free( joinable );
690                         return;
691                 }
692                 ldap_value_free( joinable );
693
694                 sprintf( filter, "(memberOfGroup=%s)", dn );
695
696                 timeout.tv_sec = FAX_TIMEOUT;
697                 timeout.tv_usec = 0;
698
699                 /* for each subtree to look in... */
700                 {
701                         int sizelimit = FAX_MAXMEMBERS;
702                         ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &sizelimit);
703                 }       
704                 for ( i = 0; base[i].b_dn != NULL; i++ ) {
705                         /* find entries that have joined this group... */
706                         rc = ldap_search_st( ld, base[i].b_dn,
707                             LDAP_SCOPE_SUBTREE, filter, attrs, 0, &timeout,
708                             &res );
709
710                         if ( rc == LDAP_SIZELIMIT_EXCEEDED ||
711                             rc == LDAP_TIMELIMIT_EXCEEDED ) {
712                                 syslog( LOG_ALERT,
713                                     "group search limit exceeded %d", rc );
714                                 unbind_and_exit( EX_TEMPFAIL );
715                         }
716
717                         if ( rc != LDAP_SUCCESS ) {
718                                 syslog( LOG_ALERT, "group search bad return %d",
719                                     rc );
720                                 unbind_and_exit( EX_TEMPFAIL );
721                         }
722
723                         /* for each entry that has joined... */
724                         for ( ee = ldap_first_entry( ld, res ); ee != NULL;
725                             ee = ldap_next_entry( ld, ee ) ) {
726                                 if ( isgroup( ee ) ) {
727                                         ndn = ldap_get_dn( ld, ee );
728
729                                         if ( do_group( e, ndn, to, nto,
730                                             togroups, ngroups, err, nerr )
731                                             == -1 ) {
732                                                 syslog( LOG_ALERT,
733                                                     "group loop (%s) (%s)",
734                                                     dn, ndn );
735                                         }
736
737                                         free( ndn );
738
739                                         continue;
740                                 }
741
742                                 /* add them to the to list */
743                                 switch (identity) {
744                                 case FAX500 :   
745                                         if ( (mail = ldap_get_values( ld, ee, 
746                                               "facsimileTelephoneNumber" )) 
747                                               != NULL ) {
748                                                 char *fdn;
749                                                 char **ufn;
750                                                 int i;
751
752                                                 fdn = ldap_get_dn( ld, ee );
753                                                 ufn = ldap_explode_dn( fdn, 1 );
754                                                 /* 
755                                                  * Convert spaces to 
756                                                  * underscores for rp
757                                                  */
758                                                 for (i = 0; ufn[0][i] != '\0'; 
759                                                      i++) {
760                                                         if (ufn[0][i] == ' ') {
761                                                                 ufn[0][i] = '_';
762                                                         }
763                                                 }
764                                                 *mail = faxtotpc(*mail, ufn[0]);
765                                                 add_to(to, nto, mail);
766
767                                                 ldap_value_free( mail );
768                                                 ldap_msgfree( res );
769                                         } else {
770                                                 ndn = ldap_get_dn( ld, ee );
771
772                                                 add_error( err, nerr,
773                                                     E_JOINMEMBERNOFAXNUM, ndn, 
774                                                     NULLMSG );
775
776                                                 free( ndn );
777                                         }
778                                         break;
779                                 case MAIL500 :
780                                         if ( (mail = ldap_get_values( ld, ee, 
781                                              "mail" )) != NULL ) {
782                                                 add_to( to, nto, mail );
783
784                                                 ldap_value_free( mail );
785
786                                         /* else generate a bounce */
787                                         } else {
788                                                 ndn = ldap_get_dn( ld, ee );
789
790                                                 add_error( err, nerr,
791                                                     E_JOINMEMBERNOEMAIL, ndn, 
792                                                     NULLMSG );
793
794                                                 free( ndn );
795                                         }
796                                         break;
797                                 }
798                         }
799
800                         ldap_msgfree( res );
801                 }
802                 {
803                         int sizelimit = FAX_MAXAMBIGUOUS;
804                         ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &sizelimit);
805                 }       
806         }
807
808         return;
809 }
810
811 static void
812 add_member(
813         char            *gdn,
814         char            *dn,
815         char            ***to,
816         int             *nto,
817         Group           **togroups,
818         int             *ngroups,
819         Error           **err,
820         int             *nerr
821 )
822 {
823         char            *ndn;
824         char            **mail;
825         int             rc;
826         LDAPMessage     *res, *e;
827         struct timeval  timeout;
828
829         timeout.tv_sec = FAX_TIMEOUT;
830         timeout.tv_usec = 0;
831         if ( (rc = ldap_search_st( ld, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
832             attrs, 0, &timeout, &res )) != LDAP_SUCCESS ) {
833                 if ( rc == LDAP_NO_SUCH_OBJECT ) {
834                         add_error( err, nerr, E_BADMEMBER, dn, NULLMSG );
835
836                         return;
837                 } else {
838                         syslog( LOG_ALERT, "member search bad return %d", rc );
839
840                         unbind_and_exit( EX_TEMPFAIL );
841                 }
842         }
843
844         if ( (e = ldap_first_entry( ld, res )) == NULL ) {
845                 syslog( LOG_ALERT, "member search error parsing entry" );
846
847                 unbind_and_exit( EX_TEMPFAIL );
848         }
849         ndn = ldap_get_dn( ld, e );
850
851         /* allow groups within groups */
852         if ( isgroup( e ) ) {
853                 if ( do_group( e, ndn, to, nto, togroups, ngroups, err, nerr )
854                     == -1 ) {
855                         syslog( LOG_ALERT, "group loop (%s) (%s)", gdn, ndn );
856                 }
857
858                 free( ndn );
859
860                 return;
861         }
862
863         switch (identity) {
864         case FAX500 :   
865                 if ( (mail = ldap_get_values( ld, e, 
866                      "facsimileTelephoneNumber" )) != NULL ) {
867                         char *fdn;
868                         char **ufn;
869                         int i;
870                         fdn = ldap_get_dn( ld, e );
871                         ufn = ldap_explode_dn( fdn, 1 );
872                         /* Convert spaces to underscores for rp */
873                         for (i = 0; ufn[0][i] != '\0'; i++) {
874                                 if (ufn[0][i] == ' ') {
875                                         ufn[0][i] = '_';
876                                 }
877                         }
878                         *mail = faxtotpc(*mail, ufn[0]);
879                         add_to(to, nto, mail);
880
881                         ldap_value_free( mail );
882                         ldap_msgfree( res );
883                 } else {
884                         add_error( err, nerr, E_MEMBERNOFAXNUM, ndn, NULLMSG );
885                 }
886                 break;
887         case MAIL500 :
888                 /* send to the member's mail attribute */
889                 if ( (mail = ldap_get_values( ld, e, "mail" )) != NULL ) {
890                         add_to( to, nto, mail );
891
892                         ldap_value_free( mail );
893
894                 /* else generate a bounce */
895                 } else {
896                         add_error( err, nerr, E_MEMBERNOEMAIL, ndn, NULLMSG );
897                 }
898
899                 free( ndn );
900         }
901         return;
902 }
903
904 static void
905 do_group_request(
906         LDAPMessage     *e,
907         char            *dn,
908         char            ***to,
909         int             *nto,
910         Error           **err,
911         int             *nerr
912 )
913 {
914         char            **requeststo;
915
916         if ( (requeststo = get_attributes_mail_dn( e, "rfc822RequestsTo",
917             "requestsTo" )) != NULL ) {
918                 add_to( to, nto, requeststo );
919
920                 ldap_value_free( requeststo );
921         } else {
922                 add_error( err, nerr, E_NOREQUEST, dn, NULLMSG );
923         }
924 }
925
926 static void
927 do_group_errors(
928         LDAPMessage     *e,
929         char            *dn,
930         char            ***to,
931         int             *nto,
932         Error           **err,
933         int             *nerr
934 )
935 {
936         char            **errorsto;
937
938         if ( (errorsto = get_attributes_mail_dn( e, "rfc822ErrorsTo",
939             "errorsTo" )) != NULL ) {
940                 add_to( to, nto, errorsto );
941
942                 ldap_value_free( errorsto );
943         } else {
944                 add_error( err, nerr, E_NOERRORS, dn, NULLMSG );
945         }
946 }
947
948 static void
949 send_message( char **to )
950 {
951         int     pid;
952 #ifndef HAVE_WAITPID
953         WAITSTATUSTYPE  status;
954 #endif
955
956
957         /* parent */
958         if ( (pid = fork()) != 0 ) {
959 #ifdef HAVE_WAITPID
960                 waitpid( pid, (int *) NULL, 0 );
961 #else
962                 wait4( pid, &status, WAIT_FLAGS, 0 );
963 #endif
964         /* child */
965         } else {
966                 /* to includes sendmailargs */
967                 execv( FAX_SENDMAIL, to );
968
969                 syslog( LOG_ALERT, "execv failed" );
970
971                 exit( EX_TEMPFAIL );
972         }
973 }
974
975 static void
976 send_group( Group *group, int ngroup )
977 {
978         int     i, pid;
979         char    **argv;
980         int     argc;
981         char    *iargv[7];
982 #ifndef HAVE_WAITPID
983         WAITSTATUSTYPE  status;
984 #endif
985
986         for ( i = 0; i < ngroup; i++ ) {
987                 (void) rewind( stdin );
988
989                 iargv[0] = FAX_SENDMAIL;
990                 iargv[1] = "-f";
991                 iargv[2] = group[i].g_errorsto;
992                 iargv[3] = "-oMrX.500";
993                 iargv[4] = "-odi";
994                 iargv[5] = "-oi";
995                 iargv[6] = NULL;
996
997                 argv = NULL;
998                 argc = 0;
999                 add_to( &argv, &argc, iargv );
1000                 add_to( &argv, &argc, group[i].g_members );
1001
1002
1003                 /* parent */
1004                 if ( (pid = fork()) != 0 ) {
1005 #ifdef HAVE_WAITPID
1006                         waitpid( pid, (int *) NULL, 0 );
1007 #else
1008                         wait4( pid, &status, WAIT_FLAGS, 0 );
1009 #endif
1010                 /* child */
1011                 } else {
1012                         execv( FAX_SENDMAIL, argv );
1013
1014                         syslog( LOG_ALERT, "execv failed" );
1015
1016                         exit( EX_TEMPFAIL );
1017                 }
1018         }
1019
1020         return;
1021 }
1022
1023 static void
1024 send_errors( Error *err, int nerr )
1025 {
1026         int             i, namelen;
1027         FILE            *fp;
1028         char            buf[1024];
1029
1030         sprintf( buf, "%s -oMrX.500 -odi -oi -f %s %s", FAX_SENDMAIL, errorsfrom,
1031             mailfrom );
1032         if ( (fp = popen( buf, "w" )) == NULL ) {
1033                 syslog( LOG_ALERT, "could not popen sendmail for errs" );
1034                 return;
1035         }
1036
1037         fprintf( fp, "To: %s\n", mailfrom );
1038         fprintf( fp, "From: %s\n", errorsfrom );
1039         fprintf( fp, "Subject: undeliverable mail\n" );
1040         fprintf( fp, "\n" );
1041         fprintf( fp, "The following errors occurred when trying to deliver the attached mail:\n" );
1042         for ( i = 0; i < nerr; i++ ) {
1043                 namelen = strlen( err[i].e_addr );
1044                 fprintf( fp, "\n" );
1045
1046                 switch ( err[i].e_code ) {
1047                 case E_USERUNKNOWN:
1048                         fprintf( fp, "%s: User unknown\n", err[i].e_addr );
1049                         break;
1050
1051                 case E_BADMEMBER:
1052                         fprintf( fp, "%s: Group member does not exist\n",
1053                             err[i].e_addr );
1054                         break;
1055
1056                 case E_NOREQUEST:
1057                         fprintf( fp, "%s: Group exists but has no request address\n",
1058                             err[i].e_addr );
1059                         break;
1060
1061                 case E_NOERRORS:
1062                         fprintf( fp, "%s: Group exists but has no errors-to address\n",
1063                             err[i].e_addr );
1064                         break;
1065
1066                 case E_AMBIGUOUS:
1067                         do_ambiguous( fp, &err[i], namelen );
1068                         break;
1069
1070                 case E_NOEMAIL:
1071                         do_noemailorfax( fp, &err[i], namelen, E_NOEMAIL );
1072                         break;
1073
1074                 case E_MEMBERNOEMAIL:
1075                         fprintf( fp, "%s: Group member exists but does not have an email address\n",
1076                             err[i].e_addr );
1077                         break;
1078
1079                 case E_JOINMEMBERNOEMAIL:
1080                         fprintf( fp, "%s: User has joined group but does not have an email address\n",
1081                             err[i].e_addr );
1082                         break;
1083
1084                 case E_NOFAXNUM:
1085                         do_noemailorfax( fp, &err[i], namelen, E_NOFAXNUM );
1086                         break;
1087
1088                 case E_MEMBERNOFAXNUM:
1089                         fprintf( fp, "%s: Group member exists but does not have a fax number\n",
1090                             err[i].e_addr );
1091                         break;
1092
1093                 case E_JOINMEMBERNOFAXNUM:
1094                         fprintf( fp, "%s: User has joined group but does not have a fax number\n",
1095                             err[i].e_addr );
1096                         break;
1097
1098                 default:
1099                         syslog( LOG_ALERT, "unknown error %d", err[i].e_code );
1100                         unbind_and_exit( EX_TEMPFAIL );
1101                         break;
1102                 }
1103         }
1104
1105         fprintf( fp, "\n------- The original message sent:\n\n" );
1106
1107         while ( fgets( buf, sizeof(buf), stdin ) != NULL ) {
1108                 fputs( buf, fp );
1109         }
1110
1111         if ( pclose( fp ) == -1 ) {
1112                 syslog( LOG_ALERT, "pclose failed" );
1113                 unbind_and_exit( EX_TEMPFAIL );
1114         }
1115
1116         return;
1117 }
1118
1119
1120 static void
1121 do_noemailorfax( FILE *fp, Error *err, int namelen, int errtype )
1122 {
1123         int             i, last;
1124         char            *dn, *rdn;
1125         char            **ufn, **vals;
1126
1127         if (errtype == E_NOFAXNUM) {
1128                 fprintf(fp, "%s: User has no facsimile number registered.\n",
1129                         err->e_addr );
1130         } else if (errtype == E_NOEMAIL) {
1131                 fprintf(fp, "%s: User has no email address registered.\n",
1132                         err->e_addr );
1133         }
1134         fprintf( fp, "%*s  Name, title, postal address and phone for '%s':\n\n",
1135             namelen, " ", err->e_addr );
1136
1137         /* name */
1138         dn = ldap_get_dn( ld, err->e_msg );
1139         ufn = ldap_explode_dn( dn, 1 );
1140         rdn = strdup( ufn[0] );
1141         if ( strcasecmp( rdn, err->e_addr ) == 0 ) {
1142                 if ( (vals = ldap_get_values( ld, err->e_msg, "cn" ))
1143                     != NULL ) {
1144                         for ( i = 0; vals[i]; i++ ) {
1145                                 last = strlen( vals[i] ) - 1;
1146                                 if ( isdigit( vals[i][last] ) ) {
1147                                         rdn = strdup( vals[i] );
1148                                         break;
1149                                 }
1150                         }
1151
1152                         ldap_value_free( vals );
1153                 }
1154         }
1155         fprintf( fp, "%*s  %s\n", namelen, " ", rdn );
1156         free( dn );
1157         free( rdn );
1158         ldap_value_free( ufn );
1159
1160         /* titles or descriptions */
1161         if ( (vals = ldap_get_values( ld, err->e_msg, "title" )) == NULL &&
1162             (vals = ldap_get_values( ld, err->e_msg, "description" ))
1163             == NULL ) {
1164                 fprintf( fp, "%*s  No title or description registered\n",
1165                     namelen, " " );
1166         } else {
1167                 for ( i = 0; vals[i] != NULL; i++ ) {
1168                         fprintf( fp, "%*s  %s\n", namelen, " ", vals[i] );
1169                 }
1170
1171                 ldap_value_free( vals );
1172         }
1173
1174         /* postal address */
1175         if ( (vals = ldap_get_values( ld, err->e_msg, "postalAddress" ))
1176             == NULL ) {
1177                 fprintf( fp, "%*s  No postal address registered\n", namelen,
1178                     " " );
1179         } else {
1180                 fprintf( fp, "%*s  ", namelen, " " );
1181                 for ( i = 0; vals[0][i] != '\0'; i++ ) {
1182                         if ( vals[0][i] == '$' ) {
1183                                 fprintf( fp, "\n%*s  ", namelen, " " );
1184                                 while ( isspace( vals[0][i+1] ) )
1185                                         i++;
1186                         } else {
1187                                 fprintf( fp, "%c", vals[0][i] );
1188                         }
1189                 }
1190                 fprintf( fp, "\n" );
1191
1192                 ldap_value_free( vals );
1193         }
1194
1195         /* telephone number */
1196         if ( (vals = ldap_get_values( ld, err->e_msg, "telephoneNumber" ))
1197             == NULL ) {
1198                 fprintf( fp, "%*s  No phone number registered\n", namelen,
1199                     " " );
1200         } else {
1201                 for ( i = 0; vals[i] != NULL; i++ ) {
1202                         fprintf( fp, "%*s  %s\n", namelen, " ", vals[i] );
1203                 }
1204
1205                 ldap_value_free( vals );
1206         }
1207 }
1208
1209 /* ARGSUSED */
1210 static void
1211 do_ambiguous( FILE *fp, Error *err, int namelen )
1212 {
1213         int             i, last;
1214         char            *dn, *rdn;
1215         char            **ufn, **vals;
1216         LDAPMessage     *e;
1217
1218         i = ldap_result2error( ld, err->e_msg, 0 );
1219
1220         fprintf( fp, "%s: Ambiguous user.  %s%d matches found:\n\n",
1221             err->e_addr, i == LDAP_SIZELIMIT_EXCEEDED ? "First " : "",
1222             ldap_count_entries( ld, err->e_msg ) );
1223
1224         for ( e = ldap_first_entry( ld, err->e_msg ); e != NULL;
1225             e = ldap_next_entry( ld, e ) ) {
1226                 dn = ldap_get_dn( ld, e );
1227                 ufn = ldap_explode_dn( dn, 1 );
1228                 rdn = strdup( ufn[0] );
1229                 if ( strcasecmp( rdn, err->e_addr ) == 0 ) {
1230                         if ( (vals = ldap_get_values( ld, e, "cn" )) != NULL ) {
1231                                 for ( i = 0; vals[i]; i++ ) {
1232                                         last = strlen( vals[i] ) - 1;
1233                                         if ( isdigit( vals[i][last] ) ) {
1234                                                 rdn = strdup( vals[i] );
1235                                                 break;
1236                                         }
1237                                 }
1238
1239                                 ldap_value_free( vals );
1240                         }
1241                 }
1242
1243                 if ( isgroup( e ) ) {
1244                         vals = ldap_get_values( ld, e, "description" );
1245                 } else {
1246                         vals = ldap_get_values( ld, e, "title" );
1247                 }
1248
1249                 fprintf( fp, "    %-20s %s\n", rdn, vals ? vals[0] : "" );
1250                 for ( i = 1; vals && vals[i] != NULL; i++ ) {
1251                         fprintf( fp, "                         %s\n", vals[i] );
1252                 }
1253
1254                 free( dn );
1255                 free( rdn );
1256                 ldap_value_free( ufn );
1257                 if ( vals != NULL )
1258                         ldap_value_free( vals );
1259         }
1260 }
1261
1262 static int
1263 count_values( char **list )
1264 {
1265         int     i;
1266
1267         for ( i = 0; list && list[i] != NULL; i++ )
1268                 ;       /* NULL */
1269
1270         return( i );
1271 }
1272
1273 static void
1274 add_to( char ***list, int *nlist, char **new )
1275 {
1276         int     i, nnew, oldnlist;
1277
1278         nnew = count_values( new );
1279
1280         oldnlist = *nlist;
1281         if ( *list == NULL || *nlist == 0 ) {
1282                 *list = (char **) malloc( (nnew + 1) * sizeof(char *) );
1283                 *nlist = nnew;
1284         } else {
1285                 *list = (char **) realloc( *list, *nlist * sizeof(char *) +
1286                     nnew * sizeof(char *) + sizeof(char *) );
1287                 *nlist += nnew;
1288         }
1289
1290         for ( i = 0; i < nnew; i++ ) {
1291                 (*list)[i + oldnlist] = strdup( new[i] );
1292         }
1293         (*list)[*nlist] = NULL;
1294
1295         return;
1296 }
1297
1298 static int
1299 isgroup( LDAPMessage *e )
1300 {
1301         int     i;
1302         char    **oclist;
1303
1304         oclist = ldap_get_values( ld, e, "objectClass" );
1305
1306         for ( i = 0; oclist[i] != NULL; i++ ) {
1307                 if ( strcasecmp( oclist[i], "rfc822MailGroup" ) == 0 ) {
1308                         ldap_value_free( oclist );
1309                         return( 1 );
1310                 }
1311         }
1312         ldap_value_free( oclist );
1313
1314         return( 0 );
1315 }
1316
1317 static void
1318 add_error( Error **err, int *nerr, int code, char *addr, LDAPMessage *msg )
1319 {
1320         if ( *nerr == 0 ) {
1321                 *err = (Error *) malloc( sizeof(Error) );
1322         } else {
1323                 *err = (Error *) realloc( *err, (*nerr + 1) * sizeof(Error) );
1324         }
1325
1326         (*err)[*nerr].e_code = code;
1327         (*err)[*nerr].e_addr = strdup( addr );
1328         (*err)[*nerr].e_msg = msg;
1329         (*nerr)++;
1330
1331         return;
1332 }
1333
1334 static void
1335 add_group( char *dn, Group **list, int *nlist )
1336 {
1337         int     i, namelen;
1338         char    **ufn;
1339
1340         for ( i = 0; i < *nlist; i++ ) {
1341                 if ( strcmp( dn, (*list)[i].g_dn ) == 0 ) {
1342                         syslog( LOG_ALERT, "group loop 2 detected (%s)", dn );
1343                         return;
1344                 }
1345         }
1346
1347         ufn = ldap_explode_dn( dn, 1 );
1348         namelen = strlen( ufn[0] );
1349
1350         if ( *nlist == 0 ) {
1351                 *list = (Group *) malloc( sizeof(Group) );
1352         } else {
1353                 *list = (Group *) realloc( *list, (*nlist + 1) *
1354                     sizeof(Group) );
1355         }
1356
1357         /* send errors to groupname-errors@host */
1358         (*list)[*nlist].g_errorsto = (char *) malloc( namelen + sizeof(ERRORS)
1359             + hostlen + 2 );
1360         sprintf( (*list)[*nlist].g_errorsto, "%s-%s@%s", ufn[0], ERRORS, host );
1361         (void) canonical( (*list)[*nlist].g_errorsto );
1362
1363         /* send to groupname-members@host - make it a list for send_group */
1364         (*list)[*nlist].g_members = (char **) malloc( 2 * sizeof(char *) );
1365         (*list)[*nlist].g_members[0] = (char *) malloc( namelen +
1366             sizeof(MEMBERS) + hostlen + 2 );
1367         sprintf( (*list)[*nlist].g_members[0], "%s-%s@%s", ufn[0], MEMBERS,
1368             host );
1369         (void) canonical( (*list)[*nlist].g_members[0] );
1370         (*list)[*nlist].g_members[1] = NULL;
1371
1372         /* save the group's dn so we can check for loops above */
1373         (*list)[*nlist].g_dn = strdup( dn );
1374
1375         (*nlist)++;
1376
1377         ldap_value_free( ufn );
1378
1379         return;
1380 }
1381
1382 static void
1383 unbind_and_exit( int rc )
1384 {
1385         int     i;
1386
1387         if ( (i = ldap_unbind( ld )) != LDAP_SUCCESS )
1388                 syslog( LOG_ALERT, "ldap_unbind failed %d\n", i );
1389
1390         exit( rc );
1391 }
1392
1393 static char *
1394 canonical( char *s )
1395 {
1396         char    *saves = s;
1397
1398         for ( ; *s != '\0'; s++ ) {
1399                 if ( *s == ' ' )
1400                         *s = '.';
1401         }
1402
1403         return( saves );
1404 }
1405
1406 static int
1407 group_loop( char *dn )
1408 {
1409         int             i;
1410         static char     **groups;
1411         static int      ngroups;
1412
1413         for ( i = 0; i < ngroups; i++ ) {
1414                 if ( strcmp( dn, groups[i] ) == 0 )
1415                         return( 1 );
1416         }
1417
1418         if ( ngroups == 0 )
1419                 groups = (char **) malloc( sizeof(char *) );
1420         else
1421                 groups = (char **) realloc( groups,
1422                     (ngroups + 1) * sizeof(char *) );
1423
1424         groups[ngroups++] = strdup( dn );
1425
1426         return( 0 );
1427 }
1428
1429 static int
1430 has_attributes( LDAPMessage *e, char *attr1, char *attr2 )
1431 {
1432         char    **attr;
1433
1434         if ( (attr = ldap_get_values( ld, e, attr1 )) != NULL ) {
1435                 ldap_value_free( attr );
1436                 return( 1 );
1437         }
1438
1439         if ( (attr = ldap_get_values( ld, e, attr2 )) != NULL ) {
1440                 ldap_value_free( attr );
1441                 return( 1 );
1442         }
1443
1444         return( 0 );
1445 }
1446
1447 static char **
1448 get_attributes_mail_dn( LDAPMessage *e, char *attr1, char *attr2 )
1449 {
1450         LDAPMessage     *ee, *res;
1451         char            **vals, **dnlist, **mail;
1452         int             nto, i, rc;
1453         struct timeval  timeout;
1454
1455         vals = ldap_get_values( ld, e, attr1 );
1456         for ( nto = 0; vals != NULL && vals[nto] != NULL; nto++ )
1457                 ;       /* NULL */
1458
1459         if ( (dnlist = ldap_get_values( ld, e, attr2 )) != NULL ) {
1460                 timeout.tv_sec = FAX_TIMEOUT;
1461                 timeout.tv_usec = 0;
1462
1463                 for ( i = 0; dnlist[i] != NULL; i++ ) {
1464                         if ( (rc = ldap_search_st( ld, dnlist[i],
1465                             LDAP_SCOPE_BASE, "(objectclass=*)", attrs, 0,
1466                             &timeout, &res )) != LDAP_SUCCESS ) {
1467                                 if ( rc != LDAP_NO_SUCH_OBJECT ) {
1468                                         unbind_and_exit( EX_TEMPFAIL );
1469                                 }
1470
1471                                 syslog( LOG_ALERT, "bad (%s) dn (%s)", attr2,
1472                                     dnlist[i] );
1473
1474                                 continue;
1475                         }
1476
1477                         if ( (ee = ldap_first_entry( ld, res )) == NULL ) {
1478                                 syslog( LOG_ALERT, "error parsing x500 entry" );
1479                                 continue;
1480                         }
1481
1482                         if ( (mail = ldap_get_values( ld, ee, "mail" ))
1483                             != NULL ) {
1484                                 add_to( &vals, &nto, mail );
1485
1486                                 ldap_value_free( mail );
1487                         }
1488
1489                         ldap_msgfree( res );
1490                 }
1491         }
1492
1493         return( vals );
1494 }