/* Delivery User Functions
* Aaron Stone, 9 Feb 2004 */
/*
$Id: dsn.c 1904 2005-10-18 08:11:39Z paul $
Copyright (C) 2004 Aaron Stone aaron at serendipity dot cx
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <stdlib.h>
#include <string.h>
#include "dsn.h"
#include "list.h"
#include "auth.h"
#include "debug.h"
/* Enhanced Status Codes from RFC 1893
* Nota Bene: should be updated to include
* its successor, RFC 3463, too.
* */
/* Top level codes */
static const char * const DSN_STRINGS_CLASS[] = {
/* nop */ "", "",
/* 2.. */ "Success",
/* nop */ "",
/* 4.. */ "Persistent Transient Failure",
/* 5.. */ "Permanent Failure"
};
/* Second the Third level codes */
static const char * const DSN_STRINGS_SUBJECT[] = {
/* nop */ "",
/* .1. */ "Address Status",
/* .2. */ "Mailbox Status",
/* .3. */ "Mail System Status",
/* .4. */ "Network and Routing Status",
/* .5. */ "Mail Delivery Protocol Status",
/* .6. */ "Message Content or Message Media Status",
/* .7. */ "Security or Policy Status"
};
/* Address Status */
static const char * const DSN_STRINGS_DETAIL_ONE[] = {
/* .1.0 */ "Other address status",
/* .1.1 */ "Bad destination mailbox address",
/* .1.2 */ "Bad destination system address",
/* .1.3 */ "Bad destination mailbox address syntax",
/* .1.4 */ "Destination mailbox address ambiguous",
/* .1.5 */ "Destination mailbox address valid",
/* .1.6 */ "Mailbox has moved",
/* .1.7 */ "Bad sender's mailbox address syntax",
/* .1.8 */ "Bad sender's system address"
};
/* Mailbox Status */
static const char * const DSN_STRINGS_DETAIL_TWO[] = {
/* .2.0 */ "Other or undefined mailbox status",
/* .2.1 */ "Mailbox disabled, not accepting messages",
/* .2.2 */ "Mailbox full",
/* .2.3 */ "Message length exceeds administrative limit",
/* .2.4 */ "Mailing list expansion problem"
};
/* Mail System Status */
static const char * const DSN_STRINGS_DETAIL_THREE[] = {
/* .3.0 */ "Other or undefined mail system status",
/* .3.1 */ "Mail system full",
/* .3.2 */ "System not accepting network messages",
/* .3.3 */ "System not capable of selected features",
/* .3.4 */ "Message too big for system"
};
/* Network and Routing Status */
static const char * const DSN_STRINGS_DETAIL_FOUR[] = {
/* .4.0 */ "Other or undefined network or routing status",
/* .4.1 */ "No answer from host",
/* .4.2 */ "Bad connection",
/* .4.3 */ "Routing server failure",
/* .4.4 */ "Unable to route",
/* .4.5 */ "Network congestion",
/* .4.6 */ "Routing loop detected",
/* .4.7 */ "Delivery time expired"
};
/* Mail Delivery Protocol Status */
static const char * const DSN_STRINGS_DETAIL_FIVE[] = {
/* .5.0 */ "Other or undefined protocol status",
/* .5.1 */ "Invalid command",
/* .5.2 */ "Syntax error",
/* .5.3 */ "Too many recipients",
/* .5.4 */ "Invalid command arguments",
/* .5.5 */ "Wrong protocol version"
};
/* Message Content or Message Media Status */
static const char * const DSN_STRINGS_DETAIL_SIX[] = {
/* .6.0 */ "Other or undefined media error",
/* .6.1 */ "Media not supported",
/* .6.2 */ "Conversion required and prohibited",
/* .6.3 */ "Conversion required but not supported",
/* .6.4 */ "Conversion with loss performed",
/* .6.5 */ "Conversion failed"
};
/* Security or Policy Status */
static const char * const DSN_STRINGS_DETAIL_SEVEN[] = {
/* .7.0 */ "Other or undefined security status",
/* .7.1 */ "Delivery not authorized, message refused",
/* .7.2 */ "Mailing list expansion prohibited",
/* .7.3 */ "Security conversion required but not possible",
/* .7.4 */ "Security features not supported",
/* .7.5 */ "Cryptographic failure",
/* .7.6 */ "Cryptographic algorithm not supported",
/* .7.7 */ "Message integrity failure"
};
/* Convert the DSN code into a descriptive message.
* Returns 0 on success, -1 on failure. */
int dsn_tostring(delivery_status_t dsn, const char ** const class,
const char ** const subject, const char ** const detail)
{
if (dsn.class == 2 || dsn.class == 4 || dsn.class == 5)
*class = DSN_STRINGS_CLASS[dsn.class];
else
return -1;
switch (dsn.subject) {
case 1:
if (dsn.detail >= 0 && dsn.detail <= 8)
*detail = DSN_STRINGS_DETAIL_ONE[dsn.detail];
else
return -1;
break;
case 2:
if (dsn.detail >= 0 && dsn.detail <= 4)
*detail = DSN_STRINGS_DETAIL_TWO[dsn.detail];
else
return -1;
break;
case 3:
if (dsn.detail >= 0 && dsn.detail <= 4)
*detail = DSN_STRINGS_DETAIL_THREE[dsn.detail];
else
return -1;
break;
case 4:
if (dsn.detail >= 0 && dsn.detail <= 7)
*detail = DSN_STRINGS_DETAIL_FOUR[dsn.detail];
else
return -1;
break;
case 5:
if (dsn.detail >= 0 && dsn.detail <= 5)
*detail = DSN_STRINGS_DETAIL_FIVE[dsn.detail];
else
return -1;
break;
case 6:
if (dsn.detail >= 0 && dsn.detail <= 5)
*detail = DSN_STRINGS_DETAIL_SIX[dsn.detail];
else
return -1;
break;
case 7:
if (dsn.detail >= 0 && dsn.detail <= 7)
*detail = DSN_STRINGS_DETAIL_SEVEN[dsn.detail];
else
return -1;
break;
default:
return -1;
}
/* If we made it this far, then the subject was valid. */
*subject = DSN_STRINGS_SUBJECT[dsn.subject];
return 0;
}
int dsnuser_init(deliver_to_user_t * dsnuser)
{
dsnuser->useridnr = 0;
dsnuser->dsn.class = 0;
dsnuser->dsn.subject = 0;
dsnuser->dsn.detail = 0;
dsnuser->address = NULL;
dsnuser->mailbox = NULL;
dsnuser->userids = (struct list *) dm_malloc(sizeof(struct list));
if (dsnuser->userids == NULL)
return -1;
dsnuser->forwards = (struct list *) dm_malloc(sizeof(struct list));
if (dsnuser->forwards == NULL) {
dm_free(dsnuser->userids);
return -1;
}
list_init(dsnuser->userids);
list_init(dsnuser->forwards);
trace(TRACE_DEBUG, "%s, %s: dsnuser initialized",
__FILE__, __func__);
return 0;
}
void dsnuser_free(deliver_to_user_t * dsnuser)
{
dsnuser->useridnr = 0;
dsnuser->dsn.class = 0;
dsnuser->dsn.subject = 0;
dsnuser->dsn.detail = 0;
/* These are nominally const, but
* we really do want to free them. */
dm_free((char *) dsnuser->address);
dm_free((char *) dsnuser->mailbox);
list_freelist(&dsnuser->userids->start);
list_freelist(&dsnuser->forwards->start);
dm_free(dsnuser->userids);
dm_free(dsnuser->forwards);
trace(TRACE_DEBUG, "%s, %s: dsnuser freed",
__FILE__, __func__);
}
int dsnuser_resolve_list(struct list *deliveries)
{
int ret;
struct element *element;
/* Loop through the users list */
for (element = list_getstart(deliveries); element != NULL;
element = element->nextnode) {
if ((ret = dsnuser_resolve((deliver_to_user_t *) element->data)) != 0) {
return ret;
}
}
return 0;
}
int dsnuser_resolve(deliver_to_user_t *delivery)
{
u64_t userid;
int alias_count = 0, domain_count = 0;
char *domain = NULL;
char *username = NULL;
/* If the userid is already set, then we're doing direct-to-userid. */
if (delivery->useridnr != 0) {
/* This seems to be the only way to see if a useridnr is valid. */
username = auth_get_userid(delivery->useridnr);
if (username != NULL) {
/* Free the username, we don't actually need it. */
dm_free(username);
/* Copy the delivery useridnr into the userids list. */
if (list_nodeadd(delivery->userids,
&delivery->useridnr,
sizeof(delivery->useridnr)) == 0) {
trace(TRACE_ERROR,
"%s, %s: out of memory",
__FILE__, __func__);
return -1;
}
/* The userid was valid... */
delivery->dsn.class = DSN_CLASS_OK; /* Success. */
delivery->dsn.subject = 1; /* Address related. */
delivery->dsn.detail = 5; /* Valid. */
} else { /* from: 'if (username != NULL)' */
/* The userid was invalid... */
delivery->dsn.class = DSN_CLASS_FAIL; /* Permanent failure. */
delivery->dsn.subject = 1; /* Address related. */
delivery->dsn.detail = 1; /* Does not exist. */
}
}
/* We don't have a useridnr, so we have either a username or an alias. */
/* Here's the process:
if (address has alias)
resolve aliases
else if (address is username)
deliver to username
else if (address' domain matches a catch-all)
deliver to domain-user
*/
else { /* from: 'if (delivery->useridnr != 0)' */
if (! strlen(delivery->address))
return 0;
alias_count = auth_check_user_ext(delivery->address,
delivery->userids,
delivery->forwards, 0);
trace(TRACE_DEBUG,
"%s, %s: user [%s] found total of [%d] aliases",
__FILE__, __func__, delivery->address, alias_count);
if (alias_count > 0) {
/* The address has aliases. */
delivery->dsn.class = DSN_CLASS_OK; /* Success. */
delivery->dsn.subject = 1; /* Address related. */
delivery->dsn.detail = 5; /* Valid. */
} else {
/* No aliases found for the address. */
int user_exists;
user_exists = auth_user_exists(delivery->address, &userid);
if (user_exists < 0) {
/* An error occurred */
trace(TRACE_ERROR,
"%s, %s: error checking user [%s]",
__FILE__, __func__, delivery->address);
return -1;
} else if (user_exists == 1) {
if (list_nodeadd(delivery->userids, &userid,
sizeof(u64_t)) == 0) {
trace(TRACE_ERROR, "%s, %s: out of memory",
__FILE__, __func__);
return -1;
} else {
trace(TRACE_DEBUG,
"%s, %s: added user [%s] id [%llu] to delivery list",
__FILE__, __func__, delivery->address, userid);
/* The userid was valid... */
delivery->dsn.class = DSN_CLASS_OK; /* Success. */
delivery->dsn.subject = 1; /* Address related. */
delivery->dsn.detail = 5; /* Valid. */
}
} else { /* from: if (user_exists < 0) || if (user_exists == 1)... */
trace(TRACE_INFO,
"%s, %s: user [%s] checking for domain forwards.",
__FILE__, __func__, delivery->address);
domain = strchr(delivery->address, '@');
if (domain == NULL) {
/* That's it, we're done here. */
/* Permanent failure... */
delivery->dsn.class = DSN_CLASS_FAIL; /* Permanent failure. */
delivery->dsn.subject = 1; /* Address related. */
delivery->dsn.detail = 1; /* Does not exist. */
} else {
trace(TRACE_DEBUG,
"%s, %s: domain [%s] checking for domain forwards",
__FILE__, __func__, domain);
/* Checking for domain aliases */
domain_count =
auth_check_user_ext(domain,
delivery->userids,
delivery->forwards, 0);
trace(TRACE_DEBUG,
"%s, %s: domain [%s] found total of [%d] aliases",
__FILE__, __func__, domain, domain_count);
if (domain_count == 0) {
/* Permanent failure... */
delivery->dsn.class = DSN_CLASS_FAIL; /* Permanent failure. */
delivery->dsn.subject = 1; /* Address related. */
delivery->dsn.detail = 1; /* Does not exist. */
} else { /* from: 'if (domain_count == 0)' */
/* The userid was valid... */
delivery->dsn.class = DSN_CLASS_OK; /* Success. */
delivery->dsn.subject = 1; /* Address related. */
delivery->dsn.detail = 5; /* Valid. */
} /* from: 'if (domain_count == 0)' */
} /* from: 'if (domain == NULL)' */
} /* from: 'switch (auth_user_exists(delivery->address, &userid))' */
} /* from: 'if (alias_count > 0) || else' */
} /* from: 'if (delivery->useridnr != 0)' */
return 0;
}
void dsnuser_free_list(struct list *deliveries)
{
struct element *tmp;
for (tmp = list_getstart(deliveries); tmp != NULL;
tmp = tmp->nextnode)
dsnuser_free((deliver_to_user_t *) tmp->data);
list_freelist(&deliveries->start);
}
delivery_status_t dsnuser_worstcase_int(int ok, int temp, int fail, int fail_quota)
{
delivery_status_t dsn;
dsn.class = DSN_CLASS_NONE;
dsn.subject = 0;
dsn.detail = 0;
if (ok)
dsn.class = DSN_CLASS_OK;
if (fail_quota)
dsn.class = DSN_CLASS_QUOTA;
if (fail)
dsn.class = DSN_CLASS_FAIL;
if (temp)
dsn.class = DSN_CLASS_TEMP;
return dsn;
}
delivery_status_t dsnuser_worstcase_list(struct list * deliveries)
{
delivery_status_t dsn;
struct element *tmp;
int ok = 0, temp = 0, fail = 0, fail_quota = 0;
/* Get one reasonable error code for everyone. */
for (tmp = list_getstart(deliveries); tmp != NULL;
tmp = tmp->nextnode) {
dsn = ((deliver_to_user_t *) tmp->data)->dsn;
switch (dsn.class) {
case DSN_CLASS_OK:
/* Success. */
ok = 1;
break;
case DSN_CLASS_TEMP:
/* Temporary transient failure. */
temp = 1;
break;
case DSN_CLASS_FAIL:
case DSN_CLASS_QUOTA:
/* Permanent failure. */
if (dsn.subject == 2)
fail_quota = 1;
else
fail = 1;
break;
case DSN_CLASS_NONE:
/* Nothing doing. */
break;
}
}
/* If we never made it into the list, all zeroes will
* yield a temporary failure, which is pretty reasonable. */
return dsnuser_worstcase_int(ok, temp, fail, fail_quota);
}
syntax highlighted by Code2HTML, v. 0.9.1