/*
* Copyright (c) 2004, 2005 Sendmail, Inc. and its suppliers.
* All rights reserved.
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the sendmail distribution.
*/
#include "sm/generic.h"
SM_RCSID("@(#)$Id: adbr.c,v 1.14 2006/06/11 04:17:52 ca Exp $")
#include "sm/types.h"
#include "sm/str.h"
#include "sm/mta.h"
#include "sm/rfc2821.h"
#include "sm/actdb-int.h"
/*
** AQ_RCPT_SS_INSERT -- insert aq_rcpt_new into ss link list
**
** Parameters:
** aq_rcpt_old -- previous aq_rcpt_new
** aq_rcpt_new -- aq_rcpt_new to insert
**
** Returns:
** SM_SUCCESS
**
** Locking: aq_rcpt_new* must be under control of caller
**
** Invariant:
** if not all elements in the ring are the same then there is
** exactly one element F for which: L=pred(F) > F ("First"/"Head"),
** (and succ(L)=F: L: Last, F: First)
** for all others E: pred <= E.
** for each element E:
** if E isn't the first nor the last: pred(E) <= E <= succ(E)
** if E is the first: pred(E) >= E <= succ(E)
** if E is the last: pred(E) <= E >= succ(E)
**
** Problem: there's no explicit head (or tail) of the "list"
** because it is a ring: X -> B -> D -> E -> X
** hence the code must be prepared to "walk" in any direction
**
** Possible enhancement: use a tree with sublists as it is
** done for edbc.
**
** Last code review:
** Last code change:
*/
sm_ret_T
aq_rcpt_ss_insert(aq_rcpt_P aq_rcpt_old, aq_rcpt_P aq_rcpt_new)
{
int direction;
aq_rcpt_P aq_rcpt_nxt, aq_rcpt_cur;
SM_IS_AQ_RCPT(aq_rcpt_new);
if (aq_rcpt_old == NULL)
{
AQR_SS_INIT(aq_rcpt_new);
return SM_SUCCESS;
}
SM_IS_AQ_RCPT(aq_rcpt_old);
/*
** Shouldn't happen but let's be nice here.
** However, later on a non-NULL domain is required, so this isn't
** consistent.
*/
if (aq_rcpt_new->aqr_domain == NULL ||
aq_rcpt_old->aqr_domain == NULL)
{
AQR_SS_APP(aq_rcpt_old, aq_rcpt_new);
return SM_SUCCESS;
}
#define DIR(d) ((d > 0) ? 1 : ((d < 0) ? (-1) : 0))
aq_rcpt_cur = aq_rcpt_nxt = aq_rcpt_old;
direction = sm_str_casecmp(aq_rcpt_new->aqr_domain,
aq_rcpt_nxt->aqr_domain);
if (direction == 0)
{
AQR_SS_APP(aq_rcpt_old, aq_rcpt_new);
return SM_SUCCESS;
}
else if (direction < 0)
{
/*
** initial: new < nxt
** go to the "left" (pred)
** stop if new >= nxt (can't be on first round)
** found place to insert (append to nxt)
** stop if nxt > cur (L > F)
** this means new is less than every element in the ring
** and nxt is the largest element, hence new must be
** appended to nxt
** stop if elem is reached:
** this means new is less than every element in the ring
** and old is the largest element, hence new must be
** appended to old (= nxt)
*/
while ((aq_rcpt_nxt = AQR_SS_PRED(aq_rcpt_nxt)) != aq_rcpt_old
&& !(sm_str_casecmp(aq_rcpt_new->aqr_domain,
aq_rcpt_nxt->aqr_domain) >= 0 ||
sm_str_casecmp(aq_rcpt_nxt->aqr_domain,
aq_rcpt_cur->aqr_domain) > 0))
{
aq_rcpt_cur = aq_rcpt_nxt;
}
AQR_SS_APP(aq_rcpt_nxt, aq_rcpt_new);
}
else
{
/*
** initial: new > nxt
** go to the "right" (succ)
** stop if new <= nxt (can't be on first round)
** found place to insert (prepend to nxt)
** stop if nxt < cur (F > L)
** this means new is greater than every element in the ring
** and next is the smallest element, hence new must be
** prepended to nxt
** stop if old is reached:
** this means new is greater than every element in the ring
** and old is the smallest element, hence new must be
** prepended to old (= nxt)
*/
while ((aq_rcpt_nxt = AQR_SS_SUCC(aq_rcpt_nxt)) != aq_rcpt_old
&& !(sm_str_casecmp(aq_rcpt_new->aqr_domain,
aq_rcpt_nxt->aqr_domain) <= 0 ||
sm_str_casecmp(aq_rcpt_nxt->aqr_domain,
aq_rcpt_cur->aqr_domain) < 0))
{
aq_rcpt_cur = aq_rcpt_nxt;
}
AQR_SS_PRE(aq_rcpt_nxt, aq_rcpt_new);
}
return SM_SUCCESS;
}
/*
** AQ_RCPT_SET_DOMAIN -- create and initialize aqr_domain
**
** Parameters:
** aq_rcpt -- AQ recipient
** defaultdomain -- domain to use if recipient doesn't have one
**
** Returns:
** usual sm_error code; ENOMEM, address scan/parse errors
**
** Side Effects:
**
** Locking: aq_rcpt_new must be under control of caller
**
** Last code review: 2005-03-20 04:21:38
** Last code change: 2006-06-11 04:03:09
*/
sm_ret_T
aq_rcpt_set_domain(aq_rcpt_P aq_rcpt, sm_str_P defaultdomain)
{
sm_ret_T ret;
sm_a2821_T a_rcpt, a_domain;
sm_a2821_P prcpt, pdomain;
SM_IS_AQ_RCPT(aq_rcpt);
ret = SM_SUCCESS;
if (aq_rcpt->aqr_domain != NULL)
return ret;
pdomain = &a_domain; /* pdomain is just pointer to a_domain */
prcpt = &a_rcpt; /* prcpt is just pointer to a_rcpt */
A2821_INIT_RP(prcpt, NULL);
A2821_INIT_RP(pdomain, NULL);
ret = t2821_scan((sm_rdstr_P) aq_rcpt->aqr_pa, prcpt, 0);
if (sm_is_err(ret))
goto error;
ret = t2821_parse(prcpt, R2821_CORRECT & ~R2821_AT);
if (sm_is_err(ret))
goto error;
aq_rcpt->aqr_domain = sm_str_new(NULL,
SM_MIN(MAXDOMAINLEN,
sm_str_getlen(aq_rcpt->aqr_pa)),
MAXDOMAINLEN + 2);
if (aq_rcpt->aqr_domain == NULL)
{
ret = sm_error_temp(SM_EM_AQ, ENOMEM);
goto error;
}
ret = t2821_extract_domain(NULL, prcpt, &pdomain);
if (!sm_is_err(ret))
ret = t2821_str(pdomain, aq_rcpt->aqr_domain, 0);
else if (sm_error_perm(SM_EM_ADDR, R2821_ERR_FQDN) == ret)
ret = sm_str_cpy(aq_rcpt->aqr_domain, defaultdomain);
if (sm_is_err(ret))
goto error;
sm_str2lower(aq_rcpt->aqr_domain);
a2821_free(pdomain);
a2821_free(prcpt);
return ret;
error:
a2821_free(pdomain);
a2821_free(prcpt);
SM_STR_FREE(aq_rcpt->aqr_domain);
return ret;
}
/*
** AQ_RCPT_EQ_DOMAIN -- compare domain part of two recipients
**
** Parameters:
** aq_rcpt1 -- aq_rcpt
** aq_rcpt2 -- aq_rcpt
** defaultdomain -- domain to use if recipient doesn't have one
**
** Returns:
** SM_SUCCESS: domain parts are identical
** SM_NOMATCH: domain parts are not identical
** <0: usual sm_error code
**
** Locking: aq_rcpt{1,2} must be under control of caller
**
** Side Effects:
** aqr_domain may be populated.
*/
sm_ret_T
aq_rcpt_eq_domain(aq_rcpt_P aq_rcpt1, aq_rcpt_P aq_rcpt2, sm_str_P defaultdomain)
{
sm_ret_T ret;
SM_IS_AQ_RCPT(aq_rcpt1);
SM_IS_AQ_RCPT(aq_rcpt2);
ret = SM_SUCCESS;
if (aq_rcpt1->aqr_domain == NULL)
{
ret = aq_rcpt_set_domain(aq_rcpt1, defaultdomain);
if (sm_is_err(ret))
return ret;
}
if (aq_rcpt2->aqr_domain == NULL)
{
ret = aq_rcpt_set_domain(aq_rcpt2, defaultdomain);
if (sm_is_err(ret))
return ret;
}
if (sm_str_casecmp(aq_rcpt1->aqr_domain, aq_rcpt2->aqr_domain) == 0)
return SM_SUCCESS;
return SM_NOMATCH;
}
syntax highlighted by Code2HTML, v. 0.9.1