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