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