2 * Copyright (c) 1990 Regents of the University of Michigan.
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.
17 #include <sys/types.h>
20 #include <sys/param.h>
21 #include <sys/resource.h>
28 #include "ldapconfig.h"
31 #define GROUP_ERRORS 0x02
32 #define GROUP_REQUEST 0x04
33 #define GROUP_MEMBERS 0x08
34 #define GROUP_OWNER 0x10
37 #define ERRORS "errors"
38 #define REQUEST "request"
39 #define REQUESTS "requests"
40 #define MEMBERS "members"
42 #define OWNERS "owners"
45 char *vacationhost = NULL;
46 char *errorsfrom = NULL;
47 char *mailfrom = NULL;
49 char *ldaphost = LDAPHOST;
55 #define E_USERUNKNOWN 1
61 #define E_JOINMEMBERNOEMAIL 7
62 #define E_MEMBERNOEMAIL 8
64 #define E_NOMEMBERS 10
66 #define E_GROUPUNKNOWN 12
72 #define e_msg e_union.e_u_msg
73 #define e_loop e_union.e_u_loop
76 typedef struct groupto {
82 typedef struct baseinfo {
83 char *b_dn; /* dn to start searching at */
84 char b_rdnpref; /* give rdn's preference when searching? */
85 int b_search; /* ORed with the type of thing the address */
86 /* looks like (USER, GROUP_ERRORS, etc.) */
87 /* to see if this should be searched */
88 char *b_filter[3]; /* filter to apply - name substituted for %s */
89 /* (up to three of them) */
93 { "ou=People, o=University of Michigan, c=US",
95 "uid=%s", "cn=%s", NULL,
96 "ou=System Groups, ou=Groups, o=University of Michigan, c=US",
98 "(&(cn=%s)(associatedDomain=%h))", NULL, NULL,
99 "ou=User Groups, ou=Groups, o=University of Michigan, c=US",
101 "(&(cn=%s)(associatedDomain=%h))", NULL, NULL,
105 char *sendmailargs[] = { MAIL500_SENDMAIL, "-oMrX.500", "-odi", "-oi", "-f", NULL, NULL };
107 static char *attrs[] = { "objectClass", "title", "postaladdress",
108 "telephoneNumber", "mail", "description", "owner",
109 "errorsTo", "rfc822ErrorsTo", "requestsTo",
110 "rfc822RequestsTo", "joinable", "cn", "member",
111 "moderator", "onVacation", "uid",
112 "suppressNoEmailError", NULL };
116 static do_group_members();
117 static send_message();
118 static send_errors();
120 static do_ambiguous();
125 static unbind_and_exit();
128 static has_attributes();
129 static char **get_attributes_mail_dn();
130 static char *canonical();
140 int numto, ngroups, numerr, nargs;
143 extern int optind, errno;
146 if ( (myname = strrchr( argv[0], '/' )) == NULL )
147 myname = strdup( argv[0] );
149 myname = strdup( myname + 1 );
152 openlog( myname, OPENLOG_OPTIONS, LOG_MAIL );
154 openlog( myname, OPENLOG_OPTIONS );
157 while ( (i = getopt( argc, argv, "d:f:h:l:m:v:" )) != EOF ) {
159 case 'd': /* turn on debugging */
160 debug = atoi( optarg );
163 case 'f': /* who it's from & where errors should go */
164 mailfrom = strdup( optarg );
165 for ( j = 0; sendmailargs[j] != NULL; j++ ) {
166 if ( strcmp( sendmailargs[j], "-f" ) == 0 ) {
167 sendmailargs[j+1] = mailfrom;
173 case 'h': /* hostname */
174 host = strdup( optarg );
175 hostlen = strlen(host);
178 case 'l': /* ldap host */
179 ldaphost = strdup( optarg );
182 /* mailer-daemon address - who we should */
183 case 'm': /* say errors come from */
184 errorsfrom = strdup( optarg );
187 case 'v': /* vacation host */
188 vacationhost = strdup( optarg );
192 syslog( LOG_ALERT, "unknown option" );
197 if ( mailfrom == NULL ) {
198 syslog( LOG_ALERT, "required argument -f not present" );
201 if ( errorsfrom == NULL ) {
202 syslog( LOG_ALERT, "required argument -m not present" );
205 if ( host == NULL ) {
206 syslog( LOG_ALERT, "required argument -h not present" );
210 if ( connect_to_x500() != 0 )
219 syslog( LOG_ALERT, "running as %d", geteuid() );
220 strcpy( buf, argv[0] );
221 for ( i = 1; i < argc; i++ ) {
223 strcat( buf, argv[i] );
226 syslog( LOG_ALERT, "args: (%s)", buf );
231 add_to( &tolist, &numto, sendmailargs );
233 ngroups = numerr = 0;
236 for ( i = optind; i < argc; i++ ) {
240 for ( j = 0; argv[i][j] != '\0'; j++ ) {
241 if ( argv[i][j] == '.' || argv[i][j] == '_' )
246 if ( (s = strrchr( argv[i], '-' )) != NULL ) {
249 if ((strcasecmp(s, ERROR) == 0) ||
250 (strcasecmp(s, ERRORS) == 0)) {
253 } else if ((strcasecmp(s, REQUEST) == 0) ||
254 (strcasecmp(s, REQUESTS) == 0)) {
255 type = GROUP_REQUEST;
257 } else if ( strcasecmp( s, MEMBERS ) == 0 ) {
258 type = GROUP_MEMBERS;
260 } else if ((strcasecmp(s, OWNER) == 0) ||
261 (strcasecmp(s, OWNERS) == 0)) {
267 do_address( argv[i], &tolist, &numto, &togroups, &ngroups,
268 &errlist, &numerr, type );
272 * If we have both errors and successful deliveries to make or if
273 * if there are any groups to deliver to, we basically need to read
274 * the message twice. So, we have to put it in a tmp file.
277 if ( numerr > 0 && numto > nargs || ngroups > 0 ) {
282 if ( (fp = tmpfile()) == NULL ) {
283 syslog( LOG_ALERT, "could not open tmp file" );
284 unbind_and_exit( EX_TEMPFAIL );
287 /* copy the message to a temp file */
288 while ( fgets( buf, sizeof(buf), stdin ) != NULL ) {
289 if ( fputs( buf, fp ) == EOF ) {
290 syslog( LOG_ALERT, "error writing tmpfile" );
291 unbind_and_exit( EX_TEMPFAIL );
295 if ( dup2( fileno( fp ), 0 ) == -1 ) {
296 syslog( LOG_ALERT, "could not dup2 tmpfile" );
297 unbind_and_exit( EX_TEMPFAIL );
303 /* deal with errors */
306 syslog( LOG_ALERT, "sending errors" );
308 (void) rewind( stdin );
309 send_errors( errlist, numerr );
312 (void) ldap_unbind( ld );
314 /* send to groups with errorsTo */
317 syslog( LOG_ALERT, "sending to groups with errorsto" );
319 (void) rewind( stdin );
320 send_group( togroups, ngroups );
323 /* send to expanded aliases and groups w/o errorsTo */
324 if ( numto > nargs ) {
326 syslog( LOG_ALERT, "sending to aliases and groups" );
328 (void) rewind( stdin );
329 send_message( tolist );
337 if ( (ld = ldap_open( ldaphost, LDAP_PORT )) == NULL ) {
338 syslog( LOG_ALERT, "ldap_open failed" );
341 ld->ld_sizelimit = MAIL500_MAXAMBIGUOUS;
342 ld->ld_deref = LDAP_DEREF_ALWAYS;
344 if ( ldap_simple_bind_s( ld, MAIL500_BINDDN, NULL ) != LDAP_SUCCESS ) {
345 syslog( LOG_ALERT, "ldap_simple_bind_s failed" );
359 for ( i = 0; a[i] != '\0'; i++ ) {
360 if ( a[i] != b[i] ) {
365 if ( b[i] == ' ' || b[i] == '.' || b[i] == '_' )
379 do_address( name, to, nto, togroups, ngroups, err, nerr, type )
390 LDAPMessage *e, *res;
391 struct timeval timeout;
394 char realfilter[1024];
395 char **mail, **onvacation = NULL, **uid = NULL;
398 * Look up the name in X.500, add the appropriate addresses found
399 * to the to list, or to the err list in case of error. Groups are
400 * handled by the do_group routine, individuals are handled here.
401 * When looking up name, we follow the bases hierarchy, looking
402 * in base[0] first, then base[1], etc. For each base, there is
403 * a set of search filters to try, in order. If something goes
404 * wrong here trying to contact X.500, we exit with EX_TEMPFAIL.
405 * If the b_rdnpref flag is set, then we give preference to entries
406 * that matched name because it's their rdn, otherwise not.
409 timeout.tv_sec = MAIL500_TIMEOUT;
411 for ( b = 0, match = 0; !match && base[b].b_dn != NULL; b++ ) {
412 if ( ! (base[b].b_search & type) ) {
415 for ( f = 0; base[b].b_filter[f] != NULL; f++ ) {
416 char *format, *p, *s, *d;
420 for ( argc = 0; argc < 3; argc++ ) {
424 format = strdup( base[b].b_filter[f] );
425 for ( argc = 0, p = format; *p; p++ ) {
428 case 's': /* %s is the name */
432 case 'h': /* %h is the host */
439 "unknown format %c", *p );
447 /* three names ought to do... */
448 sprintf( filter, format, argv[0], argv[1], argv[2] );
450 for ( s = filter, d = realfilter; *s; s++, d++ ) {
459 rc = ldap_search_st( ld, base[b].b_dn,
460 LDAP_SCOPE_SUBTREE, realfilter, attrs, 0, &timeout,
463 /* some other trouble - try again later */
464 if ( rc != LDAP_SUCCESS &&
465 rc != LDAP_SIZELIMIT_EXCEEDED ) {
466 syslog( LOG_ALERT, "return 0x%x from X.500",
468 unbind_and_exit( EX_TEMPFAIL );
471 if ( (match = ldap_count_entries( ld, res )) != 0 )
481 /* trouble - try again later */
483 syslog( LOG_ALERT, "error parsing result from X.500" );
484 unbind_and_exit( EX_TEMPFAIL );
487 /* no matches - bounce with user unknown */
489 if ( type == USER ) {
490 add_error( err, nerr, E_USERUNKNOWN, name, NULLMSG );
492 add_error( err, nerr, E_GROUPUNKNOWN, name, NULLMSG );
497 /* more than one match - bounce with ambiguous user? */
499 LDAPMessage *next, *tmpres = NULL;
503 /* not giving rdn preference - bounce with ambiguous user */
504 if ( base[b].b_rdnpref == 0 ) {
505 add_error( err, nerr, E_AMBIGUOUS, name, res );
510 * giving rdn preference - see if any entries were matched
511 * because of their rdn. If so, collect them to deal with
512 * later (== 1 we deliver, > 1 we bounce).
515 for ( e = ldap_first_entry( ld, res ); e != NULL; e = next ) {
516 next = ldap_next_entry( ld, e );
517 dn = ldap_get_dn( ld, e );
518 xdn = ldap_explode_dn( dn, 1 );
520 /* XXX bad, but how else can we do it? XXX */
521 if ( strcasecmp( xdn[0], name ) == 0 ) {
522 ldap_delete_result_entry( &res, e );
523 ldap_add_result_entry( &tmpres, e );
526 ldap_value_free( xdn );
530 /* nothing matched by rdn - go ahead and bounce */
531 if ( tmpres == NULL ) {
532 add_error( err, nerr, E_AMBIGUOUS, name, res );
535 /* more than one matched by rdn - bounce with rdn matches */
536 } else if ( (match = ldap_count_entries( ld, tmpres )) > 1 ) {
537 add_error( err, nerr, E_AMBIGUOUS, name, tmpres );
541 } else if ( match < 0 ) {
542 syslog( LOG_ALERT, "error parsing result from X.500" );
543 unbind_and_exit( EX_TEMPFAIL );
546 /* otherwise one matched by rdn - send to it */
552 * if we get this far, it means that we found a single match for
553 * name. for a user, we deliver to the mail attribute or bounce
554 * with address and phone if no mail attr. for a group, we
555 * deliver to all members or bounce to rfc822ErrorsTo if no members.
559 if ( (e = ldap_first_entry( ld, res )) == NULL ) {
560 syslog( LOG_ALERT, "error parsing entry from X.500" );
561 unbind_and_exit( EX_TEMPFAIL );
564 dn = ldap_get_dn( ld, e );
566 if ( type == GROUP_ERRORS ) {
567 /* sent to group-errors - resend to [rfc822]ErrorsTo attr */
568 do_group_errors( e, dn, to, nto, err, nerr );
570 } else if ( type == GROUP_REQUEST ) {
571 /* sent to group-request - resend to [rfc822]RequestsTo attr */
572 do_group_request( e, dn, to, nto, err, nerr );
574 } else if ( type == GROUP_MEMBERS ) {
575 /* sent to group-members - expand */
576 do_group_members( e, dn, to, nto, togroups, ngroups, err,
579 } else if ( type == GROUP_OWNER ) {
580 /* sent to group-owner - resend to owner attr */
581 do_group_owner( e, dn, to, nto, err, nerr );
583 } else if ( isgroup( e ) ) {
585 * sent to group - resend from [rfc822]ErrorsTo if it's there,
586 * otherwise, expand the group
589 do_group( e, dn, to, nto, togroups, ngroups, err, nerr );
595 * sent to user - mail attribute => add it to the to list,
598 if ( (mail = ldap_get_values( ld, e, "mail" )) != NULL ) {
603 /* try to detect simple mail loops */
604 sprintf( buf, "%s@%s", name, host );
605 for ( i = 0; mail[i] != NULL; i++ ) {
607 * address is the same as the one we're
608 * sending to - mail loop. syslog the
609 * problem, bounce a message back to the
610 * sender (who else?), and delete the bogus
611 * addr from the list.
614 if ( (h = strchr( mail[i], '@' )) != NULL ) {
616 if ( strcasecmp( h, host ) == 0 ) {
618 "potential loop detected (%s)",
623 if ( mailcmp( buf, mail[i] ) == 0 ) {
625 "loop detected (%s)", mail[i] );
627 /* remove the bogus address */
628 for ( j = i; mail[j] != NULL; j++ ) {
633 if ( mail[0] != NULL ) {
634 add_to( to, nto, mail );
636 add_error( err, nerr, E_NOEMAIL, name, res );
639 ldap_value_free( mail );
641 add_error( err, nerr, E_NOEMAIL, name, res );
645 * If the user is on vacation, send a copy of the mail to
646 * the vacation server. The address is constructed from
647 * the vacationhost (set in a command line argument) and
648 * the uid (XXX this should be more general XXX).
651 if ( vacationhost != NULL && (onvacation = ldap_get_values( ld,
652 e, "onVacation" )) != NULL && strcasecmp( onvacation[0],
657 if ( (uid = ldap_get_values( ld, e, "uid" )) != NULL ) {
658 sprintf( buf, "%s@%s", uid[0], vacationhost );
663 add_to( to, nto, vaddr );
666 "user without a uid on vacation (%s)",
672 if ( onvacation != NULL ) {
673 ldap_value_free( onvacation );
676 ldap_value_free( uid );
684 do_group( e, dn, to, nto, togroups, ngroups, err, nerr )
698 * If this group has an rfc822ErrorsTo attribute, we need to
699 * arrange for errors involving this group to go there, not
700 * to the sender. Since sendmail only has the concept of a
701 * single sender, we arrange for errors to go to groupname-errors,
702 * which we then handle specially when (if) it comes back to us
703 * by expanding to all the rfc822ErrorsTo addresses. If it has no
704 * rfc822ErrorsTo attribute, we call do_group_members() to expand
708 if ( group_loop( dn ) ) {
713 * check for moderated groups - if the group has a moderator
714 * attribute, we check to see if the from address is one of
715 * the moderator values. if so, continue on. if not, arrange
716 * to send the mail to the moderator(s). need to do this before
717 * we change the from below.
720 if ( (moderator = ldap_get_values( ld, e, "moderator" )) != NULL ) {
721 /* check if it came from any of the group's moderators */
722 for ( i = 0; moderator[i] != NULL; i++ ) {
723 if ( strcasecmp( moderator[i], mailfrom ) == 0 )
727 /* not from the moderator? */
728 if ( moderator[i] == NULL ) {
729 add_to( to, nto, moderator );
730 ldap_value_free( moderator );
734 /* else from the moderator - fall through and deliver it */
737 if ( has_attributes( e, "rfc822ErrorsTo", "errorsTo" ) ) {
738 add_group( dn, togroups, ngroups );
743 do_group_members( e, dn, to, nto, togroups, ngroups, err, nerr );
750 do_group_members( e, dn, to, nto, togroups, ngroups, err, nerr )
760 int i, rc, anymembers;
762 char **mail, **member, **joinable, **suppress;
764 LDAPMessage *ee, *res;
765 struct timeval timeout;
768 * if all has gone according to plan, we've already arranged for
769 * errors to go to the [rfc822]ErrorsTo attributes (if they exist),
770 * so all we have to do here is arrange to send to the
771 * rfc822Mailbox attribute, the member attribute, and anyone who
772 * has joined the group by setting memberOfGroup equal to the
776 /* add members in the group itself - mail attribute */
778 if ( (mail = ldap_get_values( ld, e, "mail" )) != NULL ) {
780 add_to( to, nto, mail );
782 ldap_value_free( mail );
785 /* add members in the group itself - member attribute */
786 if ( (member = ldap_get_values( ld, e, "member" )) != NULL ) {
787 suppress = ldap_get_values( ld, e, "suppressNoEmailError" );
789 for ( i = 0; member[i] != NULL; i++ ) {
790 if ( strcasecmp( dn, member[i] ) == 0 ) {
791 syslog( LOG_ALERT, "group (%s) contains itself",
795 add_member( dn, member[i], to, nto, togroups,
796 ngroups, err, nerr, suppress );
800 ldap_value_free( suppress );
802 ldap_value_free( member );
805 /* add members who have joined by setting memberOfGroup */
806 if ( (joinable = ldap_get_values( ld, e, "joinable" )) != NULL ) {
807 if ( strcasecmp( joinable[0], "FALSE" ) == 0 ) {
808 if ( ! anymembers ) {
809 add_error( err, nerr, E_NOMEMBERS, dn,
813 ldap_value_free( joinable );
816 ldap_value_free( joinable );
818 sprintf( filter, "(memberOfGroup=%s)", dn );
820 timeout.tv_sec = MAIL500_TIMEOUT;
823 /* for each subtree to look in... */
824 ld->ld_sizelimit = MAIL500_MAXGROUPMEMBERS;
825 for ( i = 0; base[i].b_dn != NULL; i++ ) {
826 /* find entries that have joined this group... */
827 rc = ldap_search_st( ld, base[i].b_dn,
828 LDAP_SCOPE_SUBTREE, filter, attrs, 0, &timeout,
831 if ( rc == LDAP_SIZELIMIT_EXCEEDED ||
832 rc == LDAP_TIMELIMIT_EXCEEDED ) {
834 "group search limit exceeded %d", rc );
835 unbind_and_exit( EX_TEMPFAIL );
838 if ( rc != LDAP_SUCCESS ) {
839 syslog( LOG_ALERT, "group search return 0x%x",
841 unbind_and_exit( EX_TEMPFAIL );
844 /* for each entry that has joined... */
845 for ( ee = ldap_first_entry( ld, res ); ee != NULL;
846 ee = ldap_next_entry( ld, ee ) ) {
848 if ( isgroup( ee ) ) {
849 ndn = ldap_get_dn( ld, ee );
851 if ( do_group( e, ndn, to, nto,
852 togroups, ngroups, err, nerr )
855 "group loop (%s) (%s)",
864 /* add them to the to list */
865 if ( (mail = ldap_get_values( ld, ee, "mail" ))
867 add_to( to, nto, mail );
869 ldap_value_free( mail );
871 /* else generate a bounce */
873 ndn = ldap_get_dn( ld, ee );
875 add_error( err, nerr,
876 E_JOINMEMBERNOEMAIL, ndn, NULLMSG );
884 ld->ld_sizelimit = MAIL500_MAXAMBIGUOUS;
887 if ( ! anymembers ) {
888 add_error( err, nerr, E_NOMEMBERS, dn, NULLMSG );
894 add_member( gdn, dn, to, nto, togroups, ngroups, err, nerr, suppress )
908 LDAPMessage *res, *e;
909 struct timeval timeout;
911 timeout.tv_sec = MAIL500_TIMEOUT;
913 if ( (rc = ldap_search_st( ld, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
914 attrs, 0, &timeout, &res )) != LDAP_SUCCESS ) {
915 if ( rc == LDAP_NO_SUCH_OBJECT ) {
916 add_error( err, nerr, E_BADMEMBER, dn, NULLMSG );
920 syslog( LOG_ALERT, "member search return 0x%x", rc );
922 unbind_and_exit( EX_TEMPFAIL );
926 if ( (e = ldap_first_entry( ld, res )) == NULL ) {
927 syslog( LOG_ALERT, "member search error parsing entry" );
929 unbind_and_exit( EX_TEMPFAIL );
931 ndn = ldap_get_dn( ld, e );
933 /* allow groups within groups */
934 if ( isgroup( e ) ) {
935 if ( do_group( e, ndn, to, nto, togroups, ngroups, err, nerr )
937 syslog( LOG_ALERT, "group loop (%s) (%s)", gdn, ndn );
945 /* send to the member's mail attribute */
946 if ( (mail = ldap_get_values( ld, e, "mail" )) != NULL ) {
947 add_to( to, nto, mail );
949 ldap_value_free( mail );
951 /* else generate a bounce */
953 if ( suppress == NULL || strcasecmp( suppress[0], "FALSE" )
955 add_error( err, nerr, E_MEMBERNOEMAIL, ndn, NULLMSG );
964 do_group_request( e, dn, to, nto, err, nerr )
974 if ( (requeststo = get_attributes_mail_dn( e, "rfc822RequestsTo",
975 "requestsTo" )) != NULL ) {
976 add_to( to, nto, requeststo );
978 ldap_value_free( requeststo );
980 add_error( err, nerr, E_NOREQUEST, dn, NULLMSG );
986 do_group_errors( e, dn, to, nto, err, nerr )
996 if ( (errorsto = get_attributes_mail_dn( e, "rfc822ErrorsTo",
997 "errorsTo" )) != NULL ) {
998 add_to( to, nto, errorsto );
1000 ldap_value_free( errorsto );
1002 add_error( err, nerr, E_NOERRORS, dn, NULLMSG );
1008 do_group_owner( e, dn, to, nto, err, nerr )
1018 if ( (owner = get_attributes_mail_dn( e, "", "owner" )) != NULL ) {
1019 add_to( to, nto, owner );
1020 ldap_value_free( owner );
1022 add_error( err, nerr, E_NOOWNER, dn, NULLMSG );
1033 WAITSTATUSTYPE status;
1040 strcpy( buf, to[0] );
1041 for ( i = 1; to[i] != NULL; i++ ) {
1043 strcat( buf, to[i] );
1046 syslog( LOG_ALERT, "send_message execing sendmail: (%s)", buf );
1050 if ( pid = fork() ) {
1052 waitpid( pid, (int *) NULL, 0 );
1054 wait4( pid, &status, WAIT_FLAGS, 0 );
1058 /* to includes sendmailargs */
1059 execv( MAIL500_SENDMAIL, to );
1061 syslog( LOG_ALERT, "execv failed" );
1063 exit( EX_TEMPFAIL );
1068 send_group( group, ngroup )
1077 WAITSTATUSTYPE status;
1080 for ( i = 0; i < ngroup; i++ ) {
1081 (void) rewind( stdin );
1083 iargv[0] = MAIL500_SENDMAIL;
1085 iargv[2] = group[i].g_errorsto;
1086 iargv[3] = "-oMrX.500";
1093 add_to( &argv, &argc, iargv );
1094 add_to( &argv, &argc, group[i].g_members );
1100 strcpy( buf, argv[0] );
1101 for ( i = 1; i < argc; i++ ) {
1103 strcat( buf, argv[i] );
1106 syslog( LOG_ALERT, "execing sendmail: (%s)", buf );
1110 if ( pid = fork() ) {
1112 waitpid( pid, (int *) NULL, 0 );
1114 wait4( pid, &status, WAIT_FLAGS, 0 );
1118 execv( MAIL500_SENDMAIL, argv );
1120 syslog( LOG_ALERT, "execv failed" );
1122 exit( EX_TEMPFAIL );
1130 send_errors( err, nerr )
1134 int pid, i, namelen;
1140 WAITSTATUSTYPE status;
1143 argv[0] = MAIL500_SENDMAIL;
1144 argv[1] = "-oMrX.500";
1148 argv[5] = errorsfrom;
1155 strcpy( buf, argv[0] );
1156 for ( i = 1; argv[i] != NULL; i++ ) {
1158 strcat( buf, argv[i] );
1161 syslog( LOG_ALERT, "execing sendmail: (%s)", buf );
1164 if ( pipe( fd ) == -1 ) {
1165 syslog( LOG_ALERT, "cannot create pipe" );
1166 exit( EX_TEMPFAIL );
1169 if ( pid = fork() ) {
1170 if ( (fp = fdopen( fd[1], "w" )) == NULL ) {
1171 syslog( LOG_ALERT, "cannot fdopen pipe" );
1172 exit( EX_TEMPFAIL );
1175 fprintf( fp, "To: %s\n", mailfrom );
1176 fprintf( fp, "From: %s\n", errorsfrom );
1177 fprintf( fp, "Subject: undeliverable mail\n" );
1178 fprintf( fp, "\n" );
1179 fprintf( fp, "The following errors occurred when trying to deliver the attached mail:\n" );
1180 for ( i = 0; i < nerr; i++ ) {
1181 namelen = strlen( err[i].e_addr );
1182 fprintf( fp, "\n" );
1184 switch ( err[i].e_code ) {
1186 fprintf( fp, "%s: User unknown\n", err[i].e_addr );
1189 case E_GROUPUNKNOWN:
1190 fprintf( fp, "%s: Group unknown\n", err[i].e_addr );
1194 fprintf( fp, "%s: Group member does not exist\n",
1196 fprintf( fp, "This could be because the distinguished name of the person has changed\n" );
1197 fprintf( fp, "If this is the case, the problem can be solved by removing and\n" );
1198 fprintf( fp, "then re-adding the person to the group.\n" );
1202 fprintf( fp, "%s: Group exists but has no request address\n",
1207 fprintf( fp, "%s: Group exists but has no errors-to address\n",
1212 fprintf( fp, "%s: Group exists but has no owner\n",
1217 do_ambiguous( fp, &err[i], namelen );
1221 do_noemail( fp, &err[i], namelen );
1224 case E_MEMBERNOEMAIL:
1225 fprintf( fp, "%s: Group member exists but does not have an email address\n",
1229 case E_JOINMEMBERNOEMAIL:
1230 fprintf( fp, "%s: User has joined group but does not have an email address\n",
1235 fprintf( fp, "%s: User has created a mail loop by adding address %s to their X.500 entry\n",
1236 err[i].e_addr, err[i].e_loop );
1240 fprintf( fp, "%s: Group has no members\n",
1245 syslog( LOG_ALERT, "unknown error %d", err[i].e_code );
1246 unbind_and_exit( EX_TEMPFAIL );
1251 fprintf( fp, "\n------- The original message sent:\n\n" );
1253 while ( fgets( buf, sizeof(buf), stdin ) != NULL ) {
1259 waitpid( pid, (int *) NULL, 0 );
1261 wait4( pid, &status, WAIT_FLAGS, 0 );
1266 execv( MAIL500_SENDMAIL, argv );
1268 syslog( LOG_ALERT, "execv failed" );
1270 exit( EX_TEMPFAIL );
1277 do_noemail( fp, err, namelen )
1286 fprintf(fp, "%s: User has no email address registered.\n",
1288 fprintf( fp, "%*s Name, title, postal address and phone for '%s':\n\n",
1289 namelen, " ", err->e_addr );
1292 dn = ldap_get_dn( ld, err->e_msg );
1293 ufn = ldap_explode_dn( dn, 1 );
1294 rdn = strdup( ufn[0] );
1295 if ( strcasecmp( rdn, err->e_addr ) == 0 ) {
1296 if ( (vals = ldap_get_values( ld, err->e_msg, "cn" ))
1298 for ( i = 0; vals[i]; i++ ) {
1299 last = strlen( vals[i] ) - 1;
1300 if ( isdigit( vals[i][last] ) ) {
1301 rdn = strdup( vals[i] );
1306 ldap_value_free( vals );
1309 fprintf( fp, "%*s %s\n", namelen, " ", rdn );
1312 ldap_value_free( ufn );
1314 /* titles or descriptions */
1315 if ( (vals = ldap_get_values( ld, err->e_msg, "title" )) == NULL &&
1316 (vals = ldap_get_values( ld, err->e_msg, "description" ))
1318 fprintf( fp, "%*s No title or description registered\n",
1321 for ( i = 0; vals[i] != NULL; i++ ) {
1322 fprintf( fp, "%*s %s\n", namelen, " ", vals[i] );
1325 ldap_value_free( vals );
1328 /* postal address */
1329 if ( (vals = ldap_get_values( ld, err->e_msg, "postalAddress" ))
1331 fprintf( fp, "%*s No postal address registered\n", namelen,
1334 fprintf( fp, "%*s ", namelen, " " );
1335 for ( i = 0; vals[0][i] != '\0'; i++ ) {
1336 if ( vals[0][i] == '$' ) {
1337 fprintf( fp, "\n%*s ", namelen, " " );
1338 while ( isspace( vals[0][i+1] ) )
1341 fprintf( fp, "%c", vals[0][i] );
1344 fprintf( fp, "\n" );
1346 ldap_value_free( vals );
1349 /* telephone number */
1350 if ( (vals = ldap_get_values( ld, err->e_msg, "telephoneNumber" ))
1352 fprintf( fp, "%*s No phone number registered\n", namelen,
1355 for ( i = 0; vals[i] != NULL; i++ ) {
1356 fprintf( fp, "%*s %s\n", namelen, " ", vals[i] );
1359 ldap_value_free( vals );
1365 do_ambiguous( fp, err, namelen )
1375 i = ldap_result2error( ld, err->e_msg, 0 );
1377 fprintf( fp, "%s: Ambiguous user. %s%d matches found:\n\n",
1378 err->e_addr, i == LDAP_SIZELIMIT_EXCEEDED ? "First " : "",
1379 ldap_count_entries( ld, err->e_msg ) );
1381 for ( e = ldap_first_entry( ld, err->e_msg ); e != NULL;
1382 e = ldap_next_entry( ld, e ) ) {
1383 dn = ldap_get_dn( ld, e );
1384 ufn = ldap_explode_dn( dn, 1 );
1385 rdn = strdup( ufn[0] );
1386 if ( strcasecmp( rdn, err->e_addr ) == 0 ) {
1387 if ( (vals = ldap_get_values( ld, e, "cn" )) != NULL ) {
1388 for ( i = 0; vals[i]; i++ ) {
1389 last = strlen( vals[i] ) - 1;
1390 if ( isdigit( vals[i][last] ) ) {
1391 rdn = strdup( vals[i] );
1396 ldap_value_free( vals );
1400 if ( isgroup( e ) ) {
1401 vals = ldap_get_values( ld, e, "description" );
1403 vals = ldap_get_values( ld, e, "title" );
1406 fprintf( fp, " %-20s %s\n", rdn, vals ? vals[0] : "" );
1407 for ( i = 1; vals && vals[i] != NULL; i++ ) {
1408 fprintf( fp, " %s\n", vals[i] );
1413 ldap_value_free( ufn );
1415 ldap_value_free( vals );
1420 count_values( list )
1425 for ( i = 0; list && list[i] != NULL; i++ )
1432 add_to( list, nlist, new )
1437 int i, nnew, oldnlist;
1439 nnew = count_values( new );
1442 if ( *list == NULL || *nlist == 0 ) {
1443 *list = (char **) malloc( (nnew + 1) * sizeof(char *) );
1446 *list = (char **) realloc( *list, *nlist * sizeof(char *) +
1447 nnew * sizeof(char *) + sizeof(char *) );
1451 for ( i = 0; i < nnew; i++ )
1452 (*list)[i + oldnlist] = strdup( new[i] );
1453 (*list)[*nlist] = NULL;
1465 oclist = ldap_get_values( ld, e, "objectClass" );
1467 for ( i = 0; oclist[i] != NULL; i++ ) {
1468 if ( strcasecmp( oclist[i], "rfc822MailGroup" ) == 0 ) {
1469 ldap_value_free( oclist );
1473 ldap_value_free( oclist );
1479 add_error( err, nerr, code, addr, msg )
1487 *err = (Error *) malloc( sizeof(Error) );
1489 *err = (Error *) realloc( *err, (*nerr + 1) * sizeof(Error) );
1492 (*err)[*nerr].e_code = code;
1493 (*err)[*nerr].e_addr = strdup( addr );
1494 (*err)[*nerr].e_msg = msg;
1501 add_group( dn, list, nlist )
1509 for ( i = 0; i < *nlist; i++ ) {
1510 if ( strcmp( dn, (*list)[i].g_dn ) == 0 ) {
1511 syslog( LOG_ALERT, "group loop 2 detected (%s)", dn );
1516 ufn = ldap_explode_dn( dn, 1 );
1517 namelen = strlen( ufn[0] );
1519 if ( *nlist == 0 ) {
1520 *list = (Group *) malloc( sizeof(Group) );
1522 *list = (Group *) realloc( *list, (*nlist + 1) *
1526 /* send errors to groupname-errors@host */
1527 (*list)[*nlist].g_errorsto = (char *) malloc( namelen + sizeof(ERRORS)
1529 sprintf( (*list)[*nlist].g_errorsto, "%s-%s@%s", ufn[0], ERRORS, host );
1530 (void) canonical( (*list)[*nlist].g_errorsto );
1532 /* send to groupname-members@host - make it a list for send_group */
1533 (*list)[*nlist].g_members = (char **) malloc( 2 * sizeof(char *) );
1534 (*list)[*nlist].g_members[0] = (char *) malloc( namelen +
1535 sizeof(MEMBERS) + hostlen + 2 );
1536 sprintf( (*list)[*nlist].g_members[0], "%s-%s@%s", ufn[0], MEMBERS,
1538 (void) canonical( (*list)[*nlist].g_members[0] );
1539 (*list)[*nlist].g_members[1] = NULL;
1541 /* save the group's dn so we can check for loops above */
1542 (*list)[*nlist].g_dn = strdup( dn );
1546 ldap_value_free( ufn );
1552 unbind_and_exit( rc )
1557 if ( (i = ldap_unbind( ld )) != LDAP_SUCCESS )
1558 syslog( LOG_ALERT, "ldap_unbind failed %d\n", i );
1569 for ( ; *s != '\0'; s++ ) {
1582 static char **groups;
1585 for ( i = 0; i < ngroups; i++ ) {
1586 if ( strcmp( dn, groups[i] ) == 0 )
1591 groups = (char **) malloc( sizeof(char *) );
1593 groups = (char **) realloc( groups,
1594 (ngroups + 1) * sizeof(char *) );
1596 groups[ngroups++] = strdup( dn );
1602 has_attributes( e, attr1, attr2 )
1609 if ( (attr = ldap_get_values( ld, e, attr1 )) != NULL ) {
1610 ldap_value_free( attr );
1614 if ( (attr = ldap_get_values( ld, e, attr2 )) != NULL ) {
1615 ldap_value_free( attr );
1623 get_attributes_mail_dn( e, attr1, attr2 )
1626 char *attr2; /* this one is dn-valued */
1628 LDAPMessage *ee, *res;
1629 char **vals, **dnlist, **mail, **grname, **graddr;
1632 struct timeval timeout;
1634 dn = ldap_get_dn( ld, e );
1636 vals = ldap_get_values( ld, e, attr1 );
1637 for ( nto = 0; vals != NULL && vals[nto] != NULL; nto++ )
1640 if ( (dnlist = ldap_get_values( ld, e, attr2 )) != NULL ) {
1641 timeout.tv_sec = MAIL500_TIMEOUT;
1642 timeout.tv_usec = 0;
1644 for ( i = 0; dnlist[i] != NULL; i++ ) {
1645 if ( (rc = ldap_search_st( ld, dnlist[i],
1646 LDAP_SCOPE_BASE, "(objectclass=*)", attrs, 0,
1647 &timeout, &res )) != LDAP_SUCCESS ) {
1648 if ( rc != LDAP_NO_SUCH_OBJECT ) {
1649 unbind_and_exit( EX_TEMPFAIL );
1652 syslog( LOG_ALERT, "bad (%s) dn (%s)", attr2,
1658 if ( (ee = ldap_first_entry( ld, res )) == NULL ) {
1659 syslog( LOG_ALERT, "error parsing x500 entry" );
1663 if ( isgroup(ee) ) {
1666 grname = ldap_explode_dn( dnlist[i], 1 );
1668 /* groupname + host + @ + null */
1669 graddr[0] = (char *) malloc( strlen( grname[0] )
1670 + strlen( host ) + 2 );
1672 sprintf( graddr[0], "%s@%s", grname[0], host);
1673 (void) canonical( graddr[0] );
1675 add_to( &vals, &nto, graddr );
1678 ldap_value_free( grname );
1679 } else if ( (mail = ldap_get_values( ld, ee, "mail" ))
1681 add_to( &vals, &nto, mail );
1683 ldap_value_free( mail );
1686 ldap_msgfree( res );