typedef struct attr_semantics {
char *as_name;
int as_m_valued; /* Is multivalued? */
- int as_final; /* If true, no further expansion is tried. */
+ int as_priority; /* Priority level of this attribut type */
int as_syntax; /* How to interpret values */
int as_m_entries; /* Can resolve to several entries? */
int as_kind; /* Recipient, sender, etc. */
#define AS_KIND_ERRORS 2 /* For ErrorsTo and similar */
#define AS_KIND_REQUEST 3
#define AS_KIND_OWNER 4
-#define AS_KIND_FORWARD_TO_HOST 5 /* Expand at some other host */
+#define AS_KIND_ROUTE_TO_HOST 5 /* Expand at some other host */
#define AS_KIND_ALLOWED_SENDER 6 /* Can send to group */
#define AS_KIND_MODERATOR 7
+#define AS_KIND_ROUTE_TO_ADDR 8 /* Rewrite recipient address as */
AttrSemantics **attr_semantics = NULL;
+int current_priority = 0;
typedef struct subst {
char sub_char;
char **groupclasses = NULL;
char **def_attr = NULL;
+char **mydomains = NULL; /* FQDNs not to route elsewhere */
static void load_config( char *filespec );
static void split_address( char *address, char **localpart, char **domainpart);
AttrSemantics *as;
as = calloc( 1, sizeof( AttrSemantics ) );
+ as->as_priority = current_priority;
p = s;
while ( isspace ( *p ) )
p++;
as->as_m_valued = 1;
} else if ( !strcasecmp( p, "multiple-entries" ) ) {
as->as_m_entries = 1;
- } else if ( !strcasecmp( p, "final" ) ) {
- as->as_final = 1;
} else if ( !strcasecmp( p, "local-native-mailbox" ) ) {
as->as_syntax = AS_SYNTAX_NATIVE_MB;
} else if ( !strcasecmp( p, "rfc822" ) ) {
}
} else if ( !strcasecmp( p, "host" ) ) {
as->as_kind = AS_SYNTAX_HOST;
- } else if ( !strcasecmp( p, "forward-to-host" ) ) {
- as->as_kind = AS_KIND_FORWARD_TO_HOST;
+ } else if ( !strcasecmp( p, "route-to-host" ) ) {
+ as->as_kind = AS_KIND_ROUTE_TO_HOST;
+ } else if ( !strcasecmp( p, "route-to-address" ) ) {
+ as->as_kind = AS_KIND_ROUTE_TO_ADDR;
} else if ( !strcasecmp( p, "recipient" ) ) {
as->as_kind = AS_KIND_RECIPIENT;
} else if ( !strcasecmp( p, "errors" ) ) {
} else if ( !strncmp(line, "group-classes", p-line) ) {
p += strspn(p, " \t");
add_single_to( &groupclasses, p );
+ } else if ( !strncmp(line, "priority", p-line) ) {
+ p += strspn(p, " \t");
+ current_priority = atoi(p);
+ } else if ( !strncmp(line, "domain", p-line) ) {
+ p += strspn(p, " \t");
+ add_single_to( &mydomains, p );
} else {
syslog( LOG_ALERT,
"Unparseable config definition at line %d",
char *p, *s, *d;
int i;
char filter[1024];
- char realfilter[1024];
LDAPMessage *e, *res;
int rc;
char **attrlist;
filter[ sizeof( filter ) - 1 ] = '\0';
}
- for ( s = filter, d = realfilter; *s; s++, d++ ) {
- if ( *s == '*' ) {
- *d++ = '\\';
- }
- *d = *s;
- }
- *d = '\0';
-
if ( ludp->lud_attrs ) {
attrlist = ludp->lud_attrs;
} else {
res = NULL;
/* TBC: we don't read the host, dammit */
rc = ldap_search_st( ld, ludp->lud_dn, ludp->lud_scope,
- realfilter, attrlist, 0,
+ filter, attrlist, 0,
&timeout, &res );
/* some other trouble - try again later */
return( resolved );
}
+static int
+is_my_domain(
+ char * domain
+)
+{
+ char **d;
+
+ if ( d == NULL )
+ return 0;
+ for ( d = mydomains; *d; d++ ) {
+ if ( !strcmp(*d,domain) ) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
/*
* The entry engine processes an entry. Normally, each entry will resolve
* to one or more values that will be added to the 'to' argument. This
char buf[1024];
char *localpart, *domainpart;
Subst substs[2];
+ int cur_priority = 0;
+ char *route_to_host = NULL;
+ char *route_to_address = NULL;
+ char *nvals[2];
for ( i=0; attr_semantics[i] != NULL; i++ ) {
AttrSemantics *as = attr_semantics[i];
int nent;
+ if ( as->as_priority < cur_priority ) {
+ /*
+ * We already got higher priority information,
+ * so no further work to do, ignore the rest.
+ */
+ break;
+ }
vals = ldap_get_values( ld, e, as->as_name );
if ( !vals || vals[0] == NULL ) {
continue;
}
switch ( as->as_kind ) {
case AS_KIND_RECIPIENT:
+ cur_priority = as->as_priority;
if ( ! ( type & ( USER | GROUP_MEMBERS ) ) )
break;
switch ( as->as_syntax ) {
break;
case AS_KIND_ERRORS:
+ cur_priority = as->as_priority;
/* This is a group with special processing */
if ( type & GROUP_ERRORS ) {
switch (as->as_kind) {
break;
case AS_KIND_REQUEST:
+ cur_priority = as->as_priority;
/* This is a group with special processing */
if ( type & GROUP_REQUEST ) {
add_to( current_to, current_nto, vals );
break;
case AS_KIND_OWNER:
+ cur_priority = as->as_priority;
/* This is a group with special processing */
if ( type & GROUP_REQUEST ) {
add_to( current_to, current_nto, vals );
}
break;
+ case AS_KIND_ROUTE_TO_HOST:
+ if ( !is_my_domain( vals[0] ) ) {
+ cur_priority = as->as_priority;
+ route_to_host = strdup( vals[0] );
+ }
+ break;
+
+ case AS_KIND_ROUTE_TO_ADDR:
+ if ( strcmp( vals[0], address ) ) {
+ cur_priority = as->as_priority;
+ route_to_address = strdup( vals[0] );
+ }
+ break;
+
default:
syslog( LOG_ALERT,
"Invalid kind %d", as->as_kind );
/* Error, TBC */
}
ldap_value_free( vals );
- if ( as->as_final ) {
- return( resolved );
+ }
+ if ( route_to_host ) {
+ char *p;
+ if ( !route_to_address ) {
+ route_to_address = strdup( address );
+ }
+ /* This makes use of the percent hack, but there's no choice */
+ p = strchr( route_to_address, '@' );
+ if ( p ) {
+ *p = '%';
}
+ sprintf( buf, "%s@%s", route_to_address, route_to_host );
+ nvals[0] = buf;
+ nvals[1] = NULL;
+ add_to( current_to, current_nto, nvals );
+ resolved = 1;
+ free( route_to_host );
+ free( route_to_address );
+ } else if ( route_to_address ) {
+ nvals[0] = route_to_address;
+ nvals[1] = NULL;
+ add_to( current_to, current_nto, nvals );
+ resolved = 1;
+ free( route_to_address );
}
+
return( resolved );
}