#include <sys_defs.h>
#include <string.h>
#include <msg.h>
#include <stringops.h>
#include <mymalloc.h>
#include <vstring.h>
#include <split_at.h>
#include <dict.h>
#include <events.h>
#include <strip_addr.h>
#include <mail_params.h>
#include <maps.h>
#include <match_parent_style.h>
#include <mail_proto.h>
#include "transport.h"
static int transport_match_parent_style;
#define STR(x) vstring_str(x)
static void transport_wildcard_init(TRANSPORT_INFO *);
TRANSPORT_INFO *transport_pre_init(const char *transport_maps_name,
const char *transport_maps)
{
TRANSPORT_INFO *tp;
tp = (TRANSPORT_INFO *) mymalloc(sizeof(*tp));
tp->transport_path = maps_create(transport_maps_name, transport_maps,
DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX
| DICT_FLAG_NO_REGSUB);
tp->wildcard_channel = tp->wildcard_nexthop = 0;
tp->transport_errno = 0;
tp->expire = 0;
return (tp);
}
void transport_post_init(TRANSPORT_INFO *tp)
{
transport_match_parent_style = match_parent_style(VAR_TRANSPORT_MAPS);
transport_wildcard_init(tp);
}
void transport_free(TRANSPORT_INFO *tp)
{
if (tp->transport_path)
maps_free(tp->transport_path);
if (tp->wildcard_channel)
vstring_free(tp->wildcard_channel);
if (tp->wildcard_nexthop)
vstring_free(tp->wildcard_nexthop);
myfree((char *) tp);
}
static void update_entry(const char *new_channel, const char *new_nexthop,
const char *rcpt_domain, VSTRING *channel,
VSTRING *nexthop)
{
if (*new_channel == 0) {
if (*new_nexthop != 0)
vstring_strcpy(nexthop, new_nexthop);
}
else {
vstring_strcpy(channel, new_channel);
if (*new_nexthop != 0)
vstring_strcpy(nexthop, new_nexthop);
else if (strcmp(STR(channel), MAIL_SERVICE_ERROR) != 0)
vstring_strcpy(nexthop, rcpt_domain);
else
vstring_strcpy(nexthop, "Address is undeliverable");
}
}
static int find_transport_entry(TRANSPORT_INFO *tp, const char *key,
const char *rcpt_domain, int flags,
VSTRING *channel, VSTRING *nexthop)
{
char *saved_value;
const char *host;
const char *value;
dict_errno = 0;
#define FOUND 1
#define NOTFOUND 0
if ((value = maps_find(tp->transport_path, key, flags)) == 0)
return (NOTFOUND);
else {
saved_value = mystrdup(value);
host = split_at(saved_value, ':');
update_entry(saved_value, host ? host : "", rcpt_domain,
channel, nexthop);
myfree(saved_value);
return (FOUND);
}
}
static void transport_wildcard_init(TRANSPORT_INFO *tp)
{
VSTRING *channel = vstring_alloc(10);
VSTRING *nexthop = vstring_alloc(10);
if (tp->wildcard_channel)
vstring_free(tp->wildcard_channel);
if (tp->wildcard_nexthop)
vstring_free(tp->wildcard_nexthop);
#define WILDCARD "*"
#define FULL 0
#define PARTIAL DICT_FLAG_FIXED
if (find_transport_entry(tp, WILDCARD, "", FULL, channel, nexthop)) {
tp->transport_errno = 0;
tp->wildcard_channel = channel;
tp->wildcard_nexthop = nexthop;
if (msg_verbose)
msg_info("wildcard_{chan:hop}={%s:%s}",
vstring_str(channel), vstring_str(nexthop));
} else {
tp->transport_errno = dict_errno;
vstring_free(channel);
vstring_free(nexthop);
tp->wildcard_channel = 0;
tp->wildcard_nexthop = 0;
}
tp->expire = event_time() + 30;
}
int transport_lookup(TRANSPORT_INFO *tp, const char *addr,
const char *rcpt_domain,
VSTRING *channel, VSTRING *nexthop)
{
char *stripped_addr;
char *ratsign = 0;
const char *name;
const char *next;
int found;
#define STREQ(x,y) (strcmp((x), (y)) == 0)
#define DISCARD_EXTENSION ((char **) 0)
if (*addr == 0) {
msg_warn("transport_lookup: null address - skipping table lookup");
return (NOTFOUND);
}
if ((ratsign = strrchr(addr, '@')) == 0 || ratsign[1] == 0)
msg_panic("transport_lookup: bad address: \"%s\"", addr);
if (find_transport_entry(tp, addr, rcpt_domain, FULL, channel, nexthop))
return (FOUND);
if (dict_errno != 0)
return (NOTFOUND);
if ((stripped_addr = strip_addr(addr, DISCARD_EXTENSION,
*var_rcpt_delim)) != 0) {
found = find_transport_entry(tp, stripped_addr, rcpt_domain, PARTIAL,
channel, nexthop);
myfree(stripped_addr);
if (found)
return (FOUND);
if (dict_errno != 0)
return (NOTFOUND);
}
for (name = ratsign + 1; *name != 0; name = next) {
if (find_transport_entry(tp, name, rcpt_domain, PARTIAL, channel, nexthop))
return (FOUND);
if (dict_errno != 0)
return (NOTFOUND);
if ((next = strchr(name + 1, '.')) == 0)
break;
if (transport_match_parent_style == MATCH_FLAG_PARENT)
next++;
}
if (tp->transport_errno || event_time() > tp->expire)
transport_wildcard_init(tp);
if (tp->transport_errno) {
dict_errno = tp->transport_errno;
return (NOTFOUND);
} else if (tp->wildcard_channel) {
update_entry(STR(tp->wildcard_channel), STR(tp->wildcard_nexthop),
rcpt_domain, channel, nexthop);
return (FOUND);
}
return (NOTFOUND);
}