/* * 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; }