3 * Copyright (c) 1990 Regents of the University of Michigan.
6 * Redistribution and use in source and binary forms are permitted
7 * provided that this notice is preserved and that due credit is given
8 * to the University of Michigan at Ann Arbor. The name of the University
9 * may not be used to endorse or promote products derived from this
10 * software without specific prior written permission. This software
11 * is provided ``as is'' without express or implied warranty.
13 * Copyright 1998,1999 The OpenLDAP Foundation
14 * COPYING RESTRICTIONS APPLY. See COPYRIGHT File in top level directory
15 * of this package for details.
22 #include <ac/stdlib.h>
25 #include <ac/signal.h>
26 #include <ac/string.h>
27 #include <ac/sysexits.h>
28 #include <ac/syslog.h>
30 #include <ac/unistd.h>
35 #ifdef HAVE_SYS_PARAM_H
36 #include <sys/param.h>
39 #ifdef HAVE_SYS_RESOURCE_H
40 #include <sys/resource.h>
45 #include "ldap_defaults.h"
47 #ifndef MAIL500_BOUNCEFROM
48 #define MAIL500_BOUNCEFROM "<>"
52 #define GROUP_ERRORS 0x02
53 #define GROUP_REQUEST 0x04
54 #define GROUP_MEMBERS 0x08
55 #define GROUP_OWNER 0x10
58 #define ERRORS "errors"
59 #define REQUEST "request"
60 #define REQUESTS "requests"
61 #define MEMBERS "members"
63 #define OWNERS "owners"
66 char *vacationhost = NULL;
67 char *errorsfrom = MAIL500_BOUNCEFROM;
68 char *mailfrom = NULL;
70 char *ldaphost = NULL;
76 #define E_USERUNKNOWN 1
82 #define E_JOINMEMBERNOEMAIL 7
83 #define E_MEMBERNOEMAIL 8
85 #define E_NOMEMBERS 10
87 #define E_GROUPUNKNOWN 12
93 #define e_msg e_union.e_u_msg
94 #define e_loop e_union.e_u_loop
97 typedef struct groupto {
104 typedef struct baseinfo {
107 char b_rdnpref; /* give rdn's preference when searching? */
108 int b_search; /* ORed with the type of thing the address */
109 /* looks like (USER, GROUP_ERRORS, etc.) */
110 /* to see if this should be searched */
114 * We should limit the search to objectclass=mailRecipient or
115 * objectclass=mailGroup.
120 {"dc=StlInter, dc=Net",
122 {"mail=%s", "mailAlternateAddress=%s", NULL}},
129 char *sendmailargs[] = { MAIL500_SENDMAIL, "-oMrLDAP", "-odi", "-oi", "-f", NULL, NULL };
131 typedef struct attr_semantics {
133 int as_m_valued; /* Is multivalued? */
134 int as_final; /* If true, no further expansion is tried. */
135 int as_syntax; /* How to interpret values */
136 int as_m_entries; /* Can resolve to several entries? */
137 int as_kind; /* Recipient, sender, etc. */
138 char *as_param; /* Extra info for filters and things alike */
141 #define AS_SYNTAX_UNKNOWN 0 /* Unqualified mailbox name */
142 #define AS_SYNTAX_NATIVE_MB 1 /* Unqualified mailbox name */
143 #define AS_SYNTAX_RFC822 2 /* RFC822 mail address */
144 #define AS_SYNTAX_HOST 3
145 #define AS_SYNTAX_DN 4 /* A directory entry */
146 #define AS_SYNTAX_RFC822_EXT 5
147 #define AS_SYNTAX_URL 6 /* mailto: or ldap: URL */
148 #define AS_SYNTAX_BOOL_FILTER 7 /* For joinable, filter in as_param */
150 #define AS_KIND_UNKNOWN 0
151 #define AS_KIND_RECIPIENT 1
152 #define AS_KIND_ERRORS 2 /* For ErrorsTo and similar */
153 #define AS_KIND_REQUEST 3
154 #define AS_KIND_OWNER 4
155 #define AS_KIND_FORWARD_TO_HOST 5 /* Expand at some other host */
156 #define AS_KIND_ALLOWED_SENDER 6 /* Can send to group */
157 #define AS_KIND_MODERATOR 7
159 AttrSemantics **attr_semantics = NULL;
161 typedef struct subst {
166 char **groupclasses = NULL;
167 char **def_attr = NULL;
169 static void load_config( char *filespec );
170 static void split_address( char *address, char **localpart, char **domainpart);
171 static int entry_engine( LDAPMessage *e, char *dn, char *address, char ***to, int *nto, Group **togroups, int *ngroups, Error **err, int *nerr, int type );
172 static void do_address( char *name, char ***to, int *nto, Group **togroups, int *ngroups, Error **err, int *nerr, int type );
173 static void send_message( char **to );
174 static void send_errors( Error *err, int nerr );
175 static void do_noemail( FILE *fp, Error *err, int namelen );
176 static void do_ambiguous( FILE *fp, Error *err, int namelen );
177 static int count_values( char **list );
178 static void add_to( char ***list, int *nlist, char **new );
179 static void add_single_to( char ***list, char *new );
180 static int isgroup( LDAPMessage *e );
181 static void add_error( Error **err, int *nerr, int code, char *addr, LDAPMessage *msg );
182 static void unbind_and_exit( int rc ) LDAP_GCCATTR((noreturn));
183 static void send_group( Group *group, int ngroup );
185 static int connect_to_x500( void );
189 main ( int argc, char **argv )
195 int numto, ngroups, numerr, nargs;
197 char *conffile = NULL;
199 if ( (myname = strrchr( argv[0], '/' )) == NULL )
200 myname = strdup( argv[0] );
202 myname = strdup( myname + 1 );
205 (void) SIGNAL( SIGPIPE, SIG_IGN );
209 openlog( myname, OPENLOG_OPTIONS, LOG_MAIL );
211 openlog( myname, OPENLOG_OPTIONS );
214 while ( (i = getopt( argc, argv, "d:C:f:h:l:m:v:" )) != EOF ) {
216 case 'd': /* turn on debugging */
217 debug = atoi( optarg );
220 case 'C': /* path to configuration file */
221 conffile = strdup( optarg );
224 case 'f': /* who it's from & where errors should go */
225 mailfrom = strdup( optarg );
226 for ( j = 0; sendmailargs[j] != NULL; j++ ) {
227 if ( strcmp( sendmailargs[j], "-f" ) == 0 ) {
228 sendmailargs[j+1] = mailfrom;
234 case 'h': /* hostname */
235 host = strdup( optarg );
236 hostlen = strlen(host);
239 case 'l': /* ldap host */
240 ldaphost = strdup( optarg );
243 /* mailer-daemon address - who we should */
244 case 'm': /* say errors come from */
245 errorsfrom = strdup( optarg );
248 case 'v': /* vacation host */
249 vacationhost = strdup( optarg );
253 syslog( LOG_ALERT, "unknown option" );
258 if ( mailfrom == NULL ) {
259 syslog( LOG_ALERT, "required argument -f not present" );
262 if ( errorsfrom == NULL ) {
263 syslog( LOG_ALERT, "required argument -m not present" );
266 /* if ( host == NULL ) { */
267 /* syslog( LOG_ALERT, "required argument -h not present" ); */
268 /* exit( EX_TEMPFAIL ); */
270 if ( conffile == NULL ) {
271 syslog( LOG_ALERT, "required argument -C not present" );
275 load_config( conffile );
277 if ( connect_to_x500() != 0 )
286 syslog( LOG_ALERT, "running as %d", geteuid() );
287 strcpy( buf, argv[0] );
288 for ( i = 1; i < argc; i++ ) {
290 strcat( buf, argv[i] );
293 syslog( LOG_ALERT, "args: (%s)", buf );
298 add_to( &tolist, &numto, sendmailargs );
300 ngroups = numerr = 0;
303 for ( i = optind; i < argc; i++ ) {
306 char *localpart, *domainpart;
309 /* TBC: Make this processing optional */
310 /* for ( j = 0; argv[i][j] != '\0'; j++ ) { */
311 /* if ( argv[i][j] == '.' || argv[i][j] == '_' ) */
312 /* argv[i][j] = ' '; */
316 split_address( argv[i], &localpart, &domainpart );
317 if ( (s = strrchr( localpart, '-' )) != NULL ) {
320 if ((strcasecmp(s, ERROR) == 0) ||
321 (strcasecmp(s, ERRORS) == 0)) {
324 } else if ((strcasecmp(s, REQUEST) == 0) ||
325 (strcasecmp(s, REQUESTS) == 0)) {
326 type = GROUP_REQUEST;
328 } else if ( strcasecmp( s, MEMBERS ) == 0 ) {
329 type = GROUP_MEMBERS;
331 } else if ((strcasecmp(s, OWNER) == 0) ||
332 (strcasecmp(s, OWNERS) == 0)) {
339 sprintf( address, "%s@%s", localpart, domainpart );
343 sprintf( address, "%s@%s", localpart, domainpart );
346 do_address( address, &tolist, &numto, &togroups, &ngroups,
347 &errlist, &numerr, type );
351 * If we have both errors and successful deliveries to make or if
352 * if there are any groups to deliver to, we basically need to read
353 * the message twice. So, we have to put it in a tmp file.
356 if ( numerr > 0 && numto > nargs || ngroups > 0 ) {
361 if ( (fp = tmpfile()) == NULL ) {
362 syslog( LOG_ALERT, "could not open tmp file" );
363 unbind_and_exit( EX_TEMPFAIL );
366 /* copy the message to a temp file */
367 while ( fgets( buf, sizeof(buf), stdin ) != NULL ) {
368 if ( fputs( buf, fp ) == EOF ) {
369 syslog( LOG_ALERT, "error writing tmpfile" );
370 unbind_and_exit( EX_TEMPFAIL );
374 if ( dup2( fileno( fp ), 0 ) == -1 ) {
375 syslog( LOG_ALERT, "could not dup2 tmpfile" );
376 unbind_and_exit( EX_TEMPFAIL );
382 /* deal with errors */
385 syslog( LOG_ALERT, "sending errors" );
387 (void) rewind( stdin );
388 send_errors( errlist, numerr );
391 (void) ldap_unbind( ld );
393 /* send to groups with errorsTo */
396 syslog( LOG_ALERT, "sending to groups with errorsto" );
398 (void) rewind( stdin );
399 send_group( togroups, ngroups );
402 /* send to expanded aliases and groups w/o errorsTo */
403 if ( numto > nargs ) {
405 syslog( LOG_ALERT, "sending to aliases and groups" );
407 (void) rewind( stdin );
408 send_message( tolist );
415 get_config_line( FILE *cf, int *lineno)
417 static char buf[2048];
423 room = sizeof( buf );
424 while ( fgets( &buf[pos], room, cf ) ) {
427 /* Delete whitespace at the beginning of new data */
428 if ( isspace( buf[pos] ) ) {
430 for ( s = buf+pos; isspace(*s); s++ )
432 for ( d = buf+pos; *s; s++, d++ ) {
439 if ( buf[len-1] != '\n' ) {
440 syslog( LOG_ALERT, "Definition too long at line %d",
446 if ( strspn( buf, " \t\n" ) == len )
448 if ( buf[len-2] == '\\' ) {
450 room = sizeof(buf) - pos;
453 /* We have a real line, we will exit the loop */
461 add_url ( char *url, int rdnpref, int typemask )
467 b = calloc(1, sizeof(Base));
469 syslog( LOG_ALERT, "Out of memory" );
472 b->b_url = strdup( url );
473 b->b_rdnpref = rdnpref;
474 b->b_search = typemask;
476 if ( base == NULL ) {
477 base = calloc(2, sizeof(LDAPURLDesc *));
479 syslog( LOG_ALERT, "Out of memory" );
484 for ( size = 0; base[size]; size++ )
487 list_temp = realloc( base, size*sizeof(LDAPURLDesc *) );
489 syslog( LOG_ALERT, "Out of memory" );
499 add_def_attr( char *s )
505 p += strspn( p, "\t," );
506 q = strpbrk( p, " \t," );
509 add_single_to( &def_attr, p );
511 add_single_to( &def_attr, p );
519 add_attr_semantics( char *s )
524 as = calloc( 1, sizeof( AttrSemantics ) );
526 while ( isspace ( *p ) )
529 while ( !isspace ( *q ) && *q != '\0' )
532 as->as_name = strdup( p );
536 while ( isspace ( *p ) )
539 while ( !isspace ( *q ) && *q != '\0' )
542 if ( !strcasecmp( p, "multivalued" ) ) {
544 } else if ( !strcasecmp( p, "multiple-entries" ) ) {
545 as->as_m_entries = 1;
546 } else if ( !strcasecmp( p, "final" ) ) {
548 } else if ( !strcasecmp( p, "local-native-mailbox" ) ) {
549 as->as_syntax = AS_SYNTAX_NATIVE_MB;
550 } else if ( !strcasecmp( p, "rfc822" ) ) {
551 as->as_syntax = AS_SYNTAX_RFC822;
552 } else if ( !strcasecmp( p, "rfc822-extended" ) ) {
553 as->as_syntax = AS_SYNTAX_RFC822_EXT;
554 } else if ( !strcasecmp( p, "dn" ) ) {
555 as->as_syntax = AS_SYNTAX_DN;
556 } else if ( !strcasecmp( p, "url" ) ) {
557 as->as_syntax = AS_SYNTAX_URL;
558 } else if ( !strncasecmp( p, "search-with-filter=", 19 ) ) {
559 as->as_syntax = AS_SYNTAX_BOOL_FILTER;
560 q = strchr( p, '=' );
563 while ( *q && !isspace( *q ) ) {
568 as->as_param = strdup( p );
571 as->as_param = strdup( p );
576 "Missing filter in %s", s );
579 } else if ( !strcasecmp( p, "host" ) ) {
580 as->as_kind = AS_SYNTAX_HOST;
581 } else if ( !strcasecmp( p, "forward-to-host" ) ) {
582 as->as_kind = AS_KIND_FORWARD_TO_HOST;
583 } else if ( !strcasecmp( p, "recipient" ) ) {
584 as->as_kind = AS_KIND_RECIPIENT;
585 } else if ( !strcasecmp( p, "errors" ) ) {
586 as->as_kind = AS_KIND_ERRORS;
587 } else if ( !strcasecmp( p, "request" ) ) {
588 as->as_kind = AS_KIND_REQUEST;
589 } else if ( !strcasecmp( p, "owner" ) ) {
590 as->as_kind = AS_KIND_OWNER;
593 "Unknown semantics word %s", p );
598 if ( attr_semantics == NULL ) {
599 attr_semantics = calloc(2, sizeof(AttrSemantics *));
600 if ( !attr_semantics ) {
601 syslog( LOG_ALERT, "Out of memory" );
604 attr_semantics[0] = as;
607 AttrSemantics **list_temp;
608 for ( size = 0; attr_semantics[size]; size++ )
611 list_temp = realloc( attr_semantics,
612 size*sizeof(AttrSemantics *) );
614 syslog( LOG_ALERT, "Out of memory" );
617 attr_semantics = list_temp;
618 attr_semantics[size-2] = as;
619 attr_semantics[size-1] = NULL;
624 load_config( char *filespec )
633 cf = fopen( filespec, "r" );
635 perror( "Opening config file" );
639 while ( ( line = get_config_line( cf,&lineno ) ) ) {
640 p = strpbrk( line, " \t" );
643 "Missing space at line %d", lineno );
646 if ( !strncmp( line, "search", p-line ) ) {
647 p += strspn( p, " \t" );
651 add_url( p, rdnpref, typemask );
652 } else if ( !strncmp(line, "attribute", p-line) ) {
653 p += strspn(p, " \t");
654 add_attr_semantics( p );
655 } else if ( !strncmp(line, "default-attributes", p-line) ) {
656 p += strspn(p, " \t");
658 } else if ( !strncmp(line, "group-classes", p-line) ) {
659 p += strspn(p, " \t");
660 add_single_to( &groupclasses, p );
663 "Unparseable config definition at line %d",
672 connect_to_x500( void )
676 if ( (ld = ldap_init( ldaphost, 0 )) == NULL ) {
677 syslog( LOG_ALERT, "ldap_init failed" );
681 /* TBC: Set this only when it makes sense
682 opt = MAIL500_MAXAMBIGUOUS;
683 ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &opt);
685 opt = LDAP_DEREF_ALWAYS;
686 ldap_set_option(ld, LDAP_OPT_DEREF, &opt);
688 if ( ldap_simple_bind_s( ld, NULL, NULL ) != LDAP_SUCCESS ) {
689 syslog( LOG_ALERT, "ldap_simple_bind_s failed" );
697 new_group( char *dn, Group **list, int *nlist )
702 for ( i = 0; i < *nlist; i++ ) {
703 if ( strcmp( dn, (*list)[i].g_dn ) == 0 ) {
704 syslog( LOG_ALERT, "group loop 2 detected (%s)", dn );
710 *list = (Group *) malloc( sizeof(Group) );
712 *list = (Group *) realloc( *list, (*nlist + 1) *
718 (*list)[*nlist].g_errorsto = NULL;
719 (*list)[*nlist].g_members = NULL;
720 (*list)[*nlist].g_nmembers = 0;
721 /* save the group's dn so we can check for loops above */
722 (*list)[*nlist].g_dn = strdup( dn );
726 return( this_group );
738 if ( ( p = strrchr( address, '@' ) ) == NULL ) {
739 *localpart = strdup( address );
742 *localpart = malloc( p - address + 1 );
743 strncpy( *localpart, address, p - address );
744 (*localpart)[p - address] = '\0';
746 *domainpart = strdup( p );
765 LDAPMessage *res, *e;
766 struct timeval timeout;
768 timeout.tv_sec = MAIL500_TIMEOUT;
770 for ( i = 0; dnlist[i]; i++ ) {
771 if ( (rc = ldap_search_st( ld, dnlist[i], LDAP_SCOPE_BASE,
772 "(objectclass=*)", def_attr, 0,
773 &timeout, &res )) != LDAP_SUCCESS ) {
774 if ( rc == LDAP_NO_SUCH_OBJECT ) {
775 add_error( err, nerr, E_BADMEMBER, dnlist[i], NULL );
778 syslog( LOG_ALERT, "member search return 0x%x", rc );
780 unbind_and_exit( EX_TEMPFAIL );
783 if ( (e = ldap_first_entry( ld, res )) == NULL ) {
784 syslog( LOG_ALERT, "member search error parsing entry" );
785 unbind_and_exit( EX_TEMPFAIL );
787 if ( entry_engine( e, dnlist[i], address, to, nto,
788 togroups, ngroups, err, nerr,
789 USER | GROUP_MEMBERS ) ) {
817 char realfilter[1024];
818 LDAPMessage *e, *res;
821 struct timeval timeout;
826 timeout.tv_sec = MAIL500_TIMEOUT;
829 rc = ldap_url_parse( url, &ludp );
832 case LDAP_URL_ERR_NOTLDAP:
834 "Not an LDAP URL: %s", url );
836 case LDAP_URL_ERR_BADENCLOSURE:
838 "Bad Enclosure in URL: %s", url );
840 case LDAP_URL_ERR_BADURL:
842 "Bad URL: %s", url );
844 case LDAP_URL_ERR_BADHOST:
846 "Host is invalid in URL: %s", url );
848 case LDAP_URL_ERR_BADATTRS:
850 "Attributes are invalid in URL: %s", url );
852 case LDAP_URL_ERR_BADSCOPE:
854 "Scope is invalid in URL: %s", url );
856 case LDAP_URL_ERR_BADFILTER:
858 "Filter is invalid in URL: %s", url );
860 case LDAP_URL_ERR_BADEXTS:
862 "Extensions are invalid in URL: %s", url );
864 case LDAP_URL_ERR_MEM:
866 "Out of memory parsing URL: %s", url );
868 case LDAP_URL_ERR_PARAM:
870 "bad parameter parsing URL: %s", url );
874 "Unknown error %d parsing URL: %s",
878 add_error( err, nerr, E_BADMEMBER,
884 for ( s = ludp->lud_filter, d = filter; *s; s++,d++ ) {
891 for ( i = 0; substs[i].sub_char != '\0';
893 if ( *s == substs[i].sub_char ) {
894 for ( p = substs[i].sub_value;
902 if ( substs[i].sub_char == '\0' ) {
904 "unknown format %c", *s );
912 strncpy( filter, ludp->lud_filter, sizeof( filter ) - 1 );
913 filter[ sizeof( filter ) - 1 ] = '\0';
916 for ( s = filter, d = realfilter; *s; s++, d++ ) {
924 if ( ludp->lud_attrs ) {
925 attrlist = ludp->lud_attrs;
930 /* TBC: we don't read the host, dammit */
931 rc = ldap_search_st( ld, ludp->lud_dn, ludp->lud_scope,
932 realfilter, attrlist, 0,
935 /* some other trouble - try again later */
936 if ( rc != LDAP_SUCCESS &&
937 rc != LDAP_SIZELIMIT_EXCEEDED ) {
938 syslog( LOG_ALERT, "return 0x%x from X.500",
940 unbind_and_exit( EX_TEMPFAIL );
943 match = ldap_count_entries( ld, res );
945 /* trouble - try again later */
947 syslog( LOG_ALERT, "error parsing result from X.500" );
948 unbind_and_exit( EX_TEMPFAIL );
951 if ( match == 1 || multi_entry ) {
952 for ( e = ldap_first_entry( ld, res ); e != NULL;
953 e = ldap_next_entry( ld, e ) ) {
954 dn = ldap_get_dn( ld, e );
955 resolved = entry_engine( e, dn, address, to, nto,
959 add_error( err, nerr, E_NOEMAIL, address, res );
965 /* more than one match - bounce with ambiguous user? */
967 LDAPMessage *next, *tmpres = NULL;
971 /* not giving rdn preference - bounce with ambiguous user */
972 if ( rdnpref == 0 ) {
973 add_error( err, nerr, E_AMBIGUOUS, address, res );
978 * giving rdn preference - see if any entries were matched
979 * because of their rdn. If so, collect them to deal with
980 * later (== 1 we deliver, > 1 we bounce).
983 for ( e = ldap_first_entry( ld, res ); e != NULL; e = next ) {
984 next = ldap_next_entry( ld, e );
985 dn = ldap_get_dn( ld, e );
986 xdn = ldap_explode_dn( dn, 1 );
988 /* XXX bad, but how else can we do it? XXX */
989 if ( strcasecmp( xdn[0], address ) == 0 ) {
990 ldap_delete_result_entry( &res, e );
991 ldap_add_result_entry( &tmpres, e );
994 ldap_value_free( xdn );
998 /* nothing matched by rdn - go ahead and bounce */
999 if ( tmpres == NULL ) {
1000 add_error( err, nerr, E_AMBIGUOUS, address, res );
1003 /* more than one matched by rdn - bounce with rdn matches */
1004 } else if ( (match = ldap_count_entries( ld, tmpres )) > 1 ) {
1005 add_error( err, nerr, E_AMBIGUOUS, address, tmpres );
1009 } else if ( match < 0 ) {
1010 syslog( LOG_ALERT, "error parsing result from X.500" );
1011 unbind_and_exit( EX_TEMPFAIL );
1014 /* otherwise one matched by rdn - send to it */
1015 ldap_msgfree( res );
1019 if ( (e = ldap_first_entry( ld, res )) == NULL ) {
1020 syslog( LOG_ALERT, "error parsing entry from X.500" );
1021 unbind_and_exit( EX_TEMPFAIL );
1024 dn = ldap_get_dn( ld, e );
1026 resolved = entry_engine( e, dn, address, to, nto,
1030 add_error( err, nerr, E_NOEMAIL, address, res );
1031 /* Don't free res if we passed it to add_error */
1033 ldap_msgfree( res );
1056 for ( i = 0; urllist[i]; i++ ) {
1058 if ( !strncasecmp( urllist[i], "mail:", 5 ) ) {
1061 vals[0] = urllist[i] + 5;
1063 add_to( to, nto, vals );
1066 } else if ( ldap_is_ldap_url( urllist[i] ) ) {
1068 resolved = search_ldap_url( urllist[i], NULL,
1069 address, 0, multi_entry,
1070 to, nto, togroups, ngroups,
1073 /* Produce some sensible error here */
1081 * The entry engine processes an entry. Normally, each entry will resolve
1082 * to one or more values that will be added to the 'to' argument. This
1083 * argument needs not be the global 'to' list, it may be the g_to field
1084 * in a group. Groups have no special treatment, unless they require
1105 char ***current_to = to;
1106 int *current_nto = nto;
1107 Group *current_group = NULL;
1109 char *localpart, *domainpart;
1112 for ( i=0; attr_semantics[i] != NULL; i++ ) {
1113 AttrSemantics *as = attr_semantics[i];
1116 vals = ldap_get_values( ld, e, as->as_name );
1117 if ( !vals || vals[0] == NULL ) {
1120 nent = count_values( vals );
1121 if ( nent > 1 && !as->as_m_valued ) {
1122 add_error( err, nerr, E_AMBIGUOUS, address, e );
1125 switch ( as->as_kind ) {
1126 case AS_KIND_RECIPIENT:
1127 if ( ! ( type & ( USER | GROUP_MEMBERS ) ) )
1129 switch ( as->as_syntax ) {
1130 case AS_SYNTAX_RFC822:
1131 add_to( current_to, current_nto, vals );
1134 case AS_SYNTAX_RFC822_EXT:
1135 add_to( current_to, current_nto, vals );
1138 case AS_SYNTAX_NATIVE_MB:
1139 /* We used to concatenate mailHost if set here */
1141 * We used to send a copy to the vacation host
1142 * if onVacation to uid@vacationhost
1144 add_to( current_to, current_nto, vals );
1149 if ( dn_search( vals, address,
1150 current_to, current_nto,
1158 if ( url_list_search( vals, address,
1160 current_to, current_nto,
1162 err, nerr, type ) ) {
1167 case AS_SYNTAX_BOOL_FILTER:
1168 if ( strcasecmp( vals[0], "true" ) ) {
1171 substs[0].sub_char = 'D';
1172 substs[0].sub_value = dn;
1173 substs[1].sub_char = '\0';
1174 substs[1].sub_value = NULL;
1175 if ( url_list_search( vals, address,
1177 current_to, current_nto,
1179 err, nerr, type ) ) {
1186 "Invalid syntax %d for kind %d",
1187 as->as_syntax, as->as_kind );
1192 case AS_KIND_ERRORS:
1193 /* This is a group with special processing */
1194 if ( type & GROUP_ERRORS ) {
1195 switch (as->as_kind) {
1196 case AS_SYNTAX_RFC822:
1197 add_to( current_to, current_nto, vals );
1203 "Invalid syntax %d for kind %d",
1204 as->as_syntax, as->as_kind );
1207 current_group = new_group( dn, togroups,
1209 current_to = ¤t_group->g_members;
1210 current_nto = ¤t_group->g_nmembers;
1211 split_address( address,
1212 &localpart, &domainpart );
1214 sprintf( buf, "%s-%s@%s",
1220 sprintf( buf, "%s-%s@%s",
1225 current_group->g_errorsto = strdup( buf );
1229 case AS_KIND_REQUEST:
1230 /* This is a group with special processing */
1231 if ( type & GROUP_REQUEST ) {
1232 add_to( current_to, current_nto, vals );
1238 /* This is a group with special processing */
1239 if ( type & GROUP_REQUEST ) {
1240 add_to( current_to, current_nto, vals );
1247 "Invalid kind %d", as->as_kind );
1250 ldap_value_free( vals );
1251 if ( as->as_final ) {
1272 int b, resolved = 0;
1274 for ( b = 0; base[b] != NULL; b++ ) {
1276 if ( ! (base[b]->b_search & type) ) {
1280 resolved = search_ldap_url( base[b]->b_url, substs, name,
1282 base[b]->b_m_entries,
1283 to, nto, togroups, ngroups,
1303 struct timeval timeout;
1304 char *localpart, *domainpart;
1309 * Look up the name in X.500, add the appropriate addresses found
1310 * to the to list, or to the err list in case of error. Groups are
1311 * handled by the do_group routine, individuals are handled here.
1312 * When looking up name, we follow the bases hierarchy, looking
1313 * in base[0] first, then base[1], etc. For each base, there is
1314 * a set of search filters to try, in order. If something goes
1315 * wrong here trying to contact X.500, we exit with EX_TEMPFAIL.
1316 * If the b_rdnpref flag is set, then we give preference to entries
1317 * that matched name because it's their rdn, otherwise not.
1320 split_address( name, &localpart, &domainpart );
1321 timeout.tv_sec = MAIL500_TIMEOUT;
1322 timeout.tv_usec = 0;
1323 substs[0].sub_char = 'm';
1324 substs[0].sub_value = name;
1325 substs[1].sub_char = 'h';
1326 substs[1].sub_value = host;
1327 substs[2].sub_char = 'l';
1328 substs[2].sub_value = localpart;
1329 substs[3].sub_char = 'd';
1330 substs[3].sub_value = domainpart;
1331 substs[4].sub_char = '\0';
1332 substs[4].sub_value = NULL;
1334 resolved = search_bases( NULL, substs, name,
1335 to, nto, togroups, ngroups,
1339 /* not resolved - bounce with user unknown */
1340 if ( type == USER ) {
1341 add_error( err, nerr, E_USERUNKNOWN, name, NULL );
1343 add_error( err, nerr, E_GROUPUNKNOWN, name, NULL );
1349 send_message( char **to )
1352 #ifndef HAVE_WAITPID
1353 WAITSTATUSTYPE status;
1360 strcpy( buf, to[0] );
1361 for ( i = 1; to[i] != NULL; i++ ) {
1363 strcat( buf, to[i] );
1366 syslog( LOG_ALERT, "send_message execing sendmail: (%s)", buf );
1370 if ( (pid = fork()) != 0 ) {
1372 waitpid( pid, (int *) NULL, 0 );
1374 wait4( pid, &status, WAIT_FLAGS, 0 );
1378 /* to includes sendmailargs */
1379 execv( MAIL500_SENDMAIL, to );
1381 syslog( LOG_ALERT, "execv failed" );
1383 exit( EX_TEMPFAIL );
1388 send_group( Group *group, int ngroup )
1394 #ifndef HAVE_WAITPID
1395 WAITSTATUSTYPE status;
1398 for ( i = 0; i < ngroup; i++ ) {
1399 (void) rewind( stdin );
1401 iargv[0] = MAIL500_SENDMAIL;
1403 iargv[2] = group[i].g_errorsto;
1404 iargv[3] = "-oMrX.500";
1411 add_to( &argv, &argc, iargv );
1412 add_to( &argv, &argc, group[i].g_members );
1418 strcpy( buf, argv[0] );
1419 for ( i = 1; i < argc; i++ ) {
1421 strcat( buf, argv[i] );
1424 syslog( LOG_ALERT, "execing sendmail: (%s)", buf );
1428 if ( (pid = fork()) != 0 ) {
1430 waitpid( pid, (int *) NULL, 0 );
1432 wait4( pid, &status, WAIT_FLAGS, 0 );
1436 execv( MAIL500_SENDMAIL, argv );
1438 syslog( LOG_ALERT, "execv failed" );
1440 exit( EX_TEMPFAIL );
1446 send_errors( Error *err, int nerr )
1448 int pid, i, namelen;
1453 #ifndef HAVE_WAITPID
1454 WAITSTATUSTYPE status;
1457 if ( strcmp( MAIL500_BOUNCEFROM, mailfrom ) == 0 ) {
1458 mailfrom = errorsfrom;
1461 argv[0] = MAIL500_SENDMAIL;
1462 argv[1] = "-oMrX.500";
1466 argv[5] = MAIL500_BOUNCEFROM;
1473 strcpy( buf, argv[0] );
1474 for ( i = 1; argv[i] != NULL; i++ ) {
1476 strcat( buf, argv[i] );
1479 syslog( LOG_ALERT, "execing sendmail: (%s)", buf );
1482 if ( pipe( fd ) == -1 ) {
1483 syslog( LOG_ALERT, "cannot create pipe" );
1484 exit( EX_TEMPFAIL );
1487 if ( (pid = fork()) != 0 ) {
1488 if ( (fp = fdopen( fd[1], "w" )) == NULL ) {
1489 syslog( LOG_ALERT, "cannot fdopen pipe" );
1490 exit( EX_TEMPFAIL );
1493 fprintf( fp, "To: %s\n", mailfrom );
1494 fprintf( fp, "From: %s\n", errorsfrom );
1495 fprintf( fp, "Subject: undeliverable mail\n" );
1496 fprintf( fp, "\n" );
1497 fprintf( fp, "The following errors occurred when trying to deliver the attached mail:\n" );
1498 for ( i = 0; i < nerr; i++ ) {
1499 namelen = strlen( err[i].e_addr );
1500 fprintf( fp, "\n" );
1502 switch ( err[i].e_code ) {
1504 fprintf( fp, "%s: User unknown\n", err[i].e_addr );
1507 case E_GROUPUNKNOWN:
1508 fprintf( fp, "%s: Group unknown\n", err[i].e_addr );
1512 fprintf( fp, "%s: Group member does not exist\n",
1514 fprintf( fp, "This could be because the distinguished name of the person has changed\n" );
1515 fprintf( fp, "If this is the case, the problem can be solved by removing and\n" );
1516 fprintf( fp, "then re-adding the person to the group.\n" );
1520 fprintf( fp, "%s: Group exists but has no request address\n",
1525 fprintf( fp, "%s: Group exists but has no errors-to address\n",
1530 fprintf( fp, "%s: Group exists but has no owner\n",
1535 do_ambiguous( fp, &err[i], namelen );
1539 do_noemail( fp, &err[i], namelen );
1542 case E_MEMBERNOEMAIL:
1543 fprintf( fp, "%s: Group member exists but does not have an email address\n",
1547 case E_JOINMEMBERNOEMAIL:
1548 fprintf( fp, "%s: User has joined group but does not have an email address\n",
1553 fprintf( fp, "%s: User has created a mail loop by adding address %s to their X.500 entry\n",
1554 err[i].e_addr, err[i].e_loop );
1558 fprintf( fp, "%s: Group has no members\n",
1563 syslog( LOG_ALERT, "unknown error %d", err[i].e_code );
1564 unbind_and_exit( EX_TEMPFAIL );
1569 fprintf( fp, "\n------- The original message sent:\n\n" );
1571 while ( fgets( buf, sizeof(buf), stdin ) != NULL ) {
1577 waitpid( pid, (int *) NULL, 0 );
1579 wait4( pid, &status, WAIT_FLAGS, 0 );
1584 execv( MAIL500_SENDMAIL, argv );
1586 syslog( LOG_ALERT, "execv failed" );
1588 exit( EX_TEMPFAIL );
1593 do_noemail( FILE *fp, Error *err, int namelen )
1599 fprintf(fp, "%s: User has no email address registered.\n",
1601 fprintf( fp, "%*s Name, title, postal address and phone for '%s':\n\n",
1602 namelen, " ", err->e_addr );
1605 dn = ldap_get_dn( ld, err->e_msg );
1606 ufn = ldap_explode_dn( dn, 1 );
1607 rdn = strdup( ufn[0] );
1608 if ( strcasecmp( rdn, err->e_addr ) == 0 ) {
1609 if ( (vals = ldap_get_values( ld, err->e_msg, "cn" ))
1611 for ( i = 0; vals[i]; i++ ) {
1612 last = strlen( vals[i] ) - 1;
1613 if ( isdigit((unsigned char) vals[i][last]) ) {
1614 rdn = strdup( vals[i] );
1619 ldap_value_free( vals );
1622 fprintf( fp, "%*s %s\n", namelen, " ", rdn );
1625 ldap_value_free( ufn );
1627 /* titles or descriptions */
1628 if ( (vals = ldap_get_values( ld, err->e_msg, "title" )) == NULL &&
1629 (vals = ldap_get_values( ld, err->e_msg, "description" ))
1631 fprintf( fp, "%*s No title or description registered\n",
1634 for ( i = 0; vals[i] != NULL; i++ ) {
1635 fprintf( fp, "%*s %s\n", namelen, " ", vals[i] );
1638 ldap_value_free( vals );
1641 /* postal address */
1642 if ( (vals = ldap_get_values( ld, err->e_msg, "postalAddress" ))
1644 fprintf( fp, "%*s No postal address registered\n", namelen,
1647 fprintf( fp, "%*s ", namelen, " " );
1648 for ( i = 0; vals[0][i] != '\0'; i++ ) {
1649 if ( vals[0][i] == '$' ) {
1650 fprintf( fp, "\n%*s ", namelen, " " );
1651 while ( isspace((unsigned char) vals[0][i+1]) )
1654 fprintf( fp, "%c", vals[0][i] );
1657 fprintf( fp, "\n" );
1659 ldap_value_free( vals );
1662 /* telephone number */
1663 if ( (vals = ldap_get_values( ld, err->e_msg, "telephoneNumber" ))
1665 fprintf( fp, "%*s No phone number registered\n", namelen,
1668 for ( i = 0; vals[i] != NULL; i++ ) {
1669 fprintf( fp, "%*s %s\n", namelen, " ", vals[i] );
1672 ldap_value_free( vals );
1678 do_ambiguous( FILE *fp, Error *err, int namelen )
1685 i = ldap_result2error( ld, err->e_msg, 0 );
1687 fprintf( fp, "%s: Ambiguous user. %s%d matches found:\n\n",
1688 err->e_addr, i == LDAP_SIZELIMIT_EXCEEDED ? "First " : "",
1689 ldap_count_entries( ld, err->e_msg ) );
1691 for ( e = ldap_first_entry( ld, err->e_msg ); e != NULL;
1692 e = ldap_next_entry( ld, e ) ) {
1693 dn = ldap_get_dn( ld, e );
1694 ufn = ldap_explode_dn( dn, 1 );
1695 rdn = strdup( ufn[0] );
1696 if ( strcasecmp( rdn, err->e_addr ) == 0 ) {
1697 if ( (vals = ldap_get_values( ld, e, "cn" )) != NULL ) {
1698 for ( i = 0; vals[i]; i++ ) {
1699 last = strlen( vals[i] ) - 1;
1700 if (isdigit((unsigned char) vals[i][last])) {
1701 rdn = strdup( vals[i] );
1706 ldap_value_free( vals );
1711 if ( isgroup( e ) ) {
1712 vals = ldap_get_values( ld, e, "description" );
1714 vals = ldap_get_values( ld, e, "title" );
1717 vals = ldap_get_values( ld, e, "description" );
1719 fprintf( fp, " %-20s %s\n", rdn, vals ? vals[0] : "" );
1720 for ( i = 1; vals && vals[i] != NULL; i++ ) {
1721 fprintf( fp, " %s\n", vals[i] );
1726 ldap_value_free( ufn );
1728 ldap_value_free( vals );
1733 count_values( char **list )
1737 for ( i = 0; list && list[i] != NULL; i++ )
1744 add_to( char ***list, int *nlist, char **new )
1746 int i, nnew, oldnlist;
1748 nnew = count_values( new );
1751 if ( *list == NULL || *nlist == 0 ) {
1752 *list = (char **) malloc( (nnew + 1) * sizeof(char *) );
1755 *list = (char **) realloc( *list, *nlist * sizeof(char *) +
1756 nnew * sizeof(char *) + sizeof(char *) );
1760 for ( i = 0; i < nnew; i++ )
1761 (*list)[i + oldnlist] = strdup( new[i] );
1762 (*list)[*nlist] = NULL;
1766 add_single_to( char ***list, char *new )
1770 if ( *list == NULL ) {
1772 *list = (char **) malloc( 2 * sizeof(char *) );
1774 nlist = count_values( *list );
1775 *list = (char **) realloc( *list,
1776 ( nlist + 2 ) * sizeof(char *) );
1779 (*list)[nlist] = strdup( new );
1780 (*list)[nlist+1] = NULL;
1784 isgroup( LDAPMessage *e )
1789 if ( !groupclasses ) {
1793 oclist = ldap_get_values( ld, e, "objectClass" );
1795 for ( i = 0; oclist[i] != NULL; i++ ) {
1796 for ( j = 0; groupclasses[j] != NULL; j++ ) {
1797 if ( strcasecmp( oclist[i], groupclasses[j] ) == 0 ) {
1798 ldap_value_free( oclist );
1803 ldap_value_free( oclist );
1809 add_error( Error **err, int *nerr, int code, char *addr, LDAPMessage *msg )
1812 *err = (Error *) malloc( sizeof(Error) );
1814 *err = (Error *) realloc( *err, (*nerr + 1) * sizeof(Error) );
1817 (*err)[*nerr].e_code = code;
1818 (*err)[*nerr].e_addr = strdup( addr );
1819 (*err)[*nerr].e_msg = msg;
1824 unbind_and_exit( int rc )
1828 if ( (i = ldap_unbind( ld )) != LDAP_SUCCESS )
1829 syslog( LOG_ALERT, "ldap_unbind failed %d\n", i );