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