/* * Copyright (c) 2003-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: lookupaddr.c,v 1.28 2007/01/22 17:24:06 ca Exp $") #include "sm/assert.h" #include "sm/error.h" #include "sm/memops.h" #include "sm/heap.h" #include "sm/str.h" #include "sm/strexp.h" #include "map.h" #include "sm/map.h" #include "sm/maps.h" #include "sm/mapc.h" /* ** SM_MAP_REPLDIG -- Replace %n pattern in rhs (wrapper for sm_str_expdig()) ** ** Parameters: ** str -- string for intermediate result ** rhs -- input/output string ** argc -- number of valid elements in argv (0 - 10) ** argv -- array of strings to use for expansion ** ** Returns: ** >=0: number of replacements ** <0: usual sm_error code */ static sm_ret_T sm_map_repldig(sm_str_P str, sm_str_P rhs, uint argc, sm_str_P *argv) { sm_ret_T ret; sm_str_clr(str); ret = sm_str_expdig(rhs, str, '\0', argc, argv); if (sm_is_success(ret)) { sm_str_clr(rhs); sm_str_cat(rhs, str); } return ret; } /* ** SM_MAP_EXPMAC -- Expand macro in dst ** (callback for sm_str_expmac_cb()) ** ** Parameters: ** src -- input string (containing macro) ** len -- len of src ** dst -- string to be expanded (output) ** mac_begin -- begin of macro in src ** mac_end -- end of macro in src ** ctx -- context ** ** Returns: ** >=0: number of replacements ** <0: usual sm_error code */ struct addr_macros_S { const char *am_macro; uint am_repl; }; typedef struct addr_macros_S addr_macros_T, *addr_macros_P; #define ADDR_MAC_NONE 0 #define ADDR_MAC_USER 1 #define ADDR_MAC_DETAIL 2 #define ADDR_MAC_DOMAIN 3 #define ADDR_MAC_TAG 4 #define ADDR_MAC_DELIM 5 #define ADDR_MAC_SUBDOM 6 #define ADDR_MAC_EXT 7 static addr_macros_T addr_macros[] = { { "user", ADDR_MAC_USER }, { "detail", ADDR_MAC_DETAIL }, { "domain", ADDR_MAC_DOMAIN }, { "tag", ADDR_MAC_TAG }, { "delimiter", ADDR_MAC_DELIM }, { "subdomain", ADDR_MAC_SUBDOM }, { "extension", ADDR_MAC_EXT }, { NULL, ADDR_MAC_NONE } }; struct macexp_ctx_S { sm_str_P macexp_user; sm_str_P macexp_detail; sm_str_P macexp_domain; sm_str_P macexp_tag; uchar macexp_delim; uint macexp_subdom; }; typedef struct macexp_ctx_S macexp_ctx_T, *macexp_ctx_P; static sm_ret_T sm_mac_expmac(const sm_str_P src, uint len, sm_str_P dst, uint mac_begin, uint mac_end, void *ctx) { sm_ret_T ret; uint u; size_t l; sm_str_P repl; macexp_ctx_P macexp_ctx; if (mac_begin >= mac_end || mac_begin >= len || mac_end > len) return sm_error_perm(SM_EM_MAP, SM_E_UNEXPECTED); SM_REQUIRE(src != NULL); SM_REQUIRE(dst != NULL); SM_REQUIRE(ctx != NULL); macexp_ctx = (macexp_ctx_P) ctx; l = mac_end - mac_begin + 1; for (u = 0; addr_macros[u].am_macro != NULL; u++) { if (strncmp(addr_macros[u].am_macro, (const char *)sm_str_data(src) + mac_begin, l) == 0) { repl = NULL; switch (addr_macros[u].am_repl) { case ADDR_MAC_USER: repl = macexp_ctx->macexp_user; break; case ADDR_MAC_DETAIL: repl = macexp_ctx->macexp_detail; break; case ADDR_MAC_DOMAIN: repl = macexp_ctx->macexp_domain; break; case ADDR_MAC_TAG: repl = macexp_ctx->macexp_tag; break; case ADDR_MAC_DELIM: ret = sm_str_put(dst, macexp_ctx->macexp_delim); if (sm_is_err(ret)) return ret; return 1; break; case ADDR_MAC_SUBDOM: repl = macexp_ctx->macexp_domain; if (NULL == repl) break; else { sm_str_T subdom; sm_str_assign(subdom, NULL, sm_str_data(repl), macexp_ctx->macexp_subdom, sm_str_getlen(repl)); ret = sm_str_cat(dst, &subdom); if (sm_is_err(ret)) return ret; return 1; } break; case ADDR_MAC_EXT: repl = macexp_ctx->macexp_detail; if (NULL == repl) break; ret = sm_str_put(dst, macexp_ctx->macexp_delim); if (sm_is_err(ret)) return ret; repl = macexp_ctx->macexp_detail; break; default: break; } if (NULL == repl) return 0; ret = sm_str_cat(dst, repl); if (sm_is_err(ret)) return ret; return 1; } } return SM_SUCCESS; /* return sm_error_perm(SM_EM_MAP, SM_E_NOTFOUND); */ } /* ** SM_MAP_REPLMAC -- Replace ${macro} pattern in rhs ** (wrapper for sm_str_expmac_cb()) ** ** Parameters: ** str -- string for intermediate result ** rhs -- input/output string ** macexp_ctx -- macro expansion context ** ** Returns: ** usual sm_error code */ static sm_ret_T sm_map_replmac(sm_str_P str, sm_str_P rhs, macexp_ctx_P macexp_ctx) { sm_ret_T ret; sm_str_clr(str); ret = sm_str_expmac_cb(rhs, str, 0, sm_mac_expmac, macexp_ctx); if (ret > 0) { sm_str_clr(rhs); sm_str_cat(rhs, str); } return ret; } /* ** Add flags (also for lookup_ip): ** lookup also .domain ** lookup only Tag: ** others? */ /* ** SM_MAP_LOOKUP_ADDR -- Lookup RFC 2821 address ** ** Parameters: ** map -- map ** user -- user part of address ** detail -- detail part of address ** must be NULL iff there is no "delim" in the address ** domain -- domain part of address ** tag -- tag including delimiter (NULL for no tag) ** delim -- delimiter ** flags -- flags to control lookup ** rhs -- rhs of matching entry (output) ** ** Returns: ** <0: usual sm_error code ** >=0: number of macro replacements ** should it also return which case matched? ** ** Note: the lookup cases are controlled by flags and whether domain or ** detail are NULL. That is, there is no flag to say to lookup an ** address with or without domain, that is implicit by passing ** a domain part (or NULL). */ #define UCH_AT ((uchar) '@') #define UCH_STAR ((uchar) '*') #define UCH_PLUS ((uchar) '+') #define SM_MAX_REPL 4 sm_ret_T sm_map_lookup_addr(sm_map_P map, sm_str_P user, sm_str_P detail, sm_str_P domain, sm_str_P tag, uchar delim, uint32_t flags, sm_str_P rhs) { sm_ret_T ret; uint len, len_domain, j; uint32_t mapflags; sm_mapc_P mapc; sm_str_P str, delimstr, subdom; sm_str_P argv[SM_MAX_REPL]; macexp_ctx_T macexp_ctx; if (NULL == map) return sm_error_perm(SM_EM_MAP, SM_E_NOMAP); SM_IS_MAP(map); mapc = map->sm_map_class; SM_IS_MAPC(mapc); if (NULL == mapc->sm_mapc_lookupf) return sm_error_perm(SM_EM_MAP, SM_E_NOTIMPL); delimstr = NULL; subdom = NULL; len = 2; if (user != NULL) len += sm_str_getlen(user); if (detail != NULL) len += sm_str_getlen(detail) + 1; if (domain != NULL) { len_domain = sm_str_getlen(domain); len += len_domain + 1; --len_domain; } else len_domain = 0; if (tag != NULL) len += sm_str_getlen(tag); j = sm_str_getmax(rhs); str = sm_str_new(NULL, len, SMMAP_IS_LFL(flags, SMMAP_LFL_REPL|SMMAP_LFL_MCR_REPL) ? j : len + 2); if (NULL == str) return sm_error_perm(SM_EM_MAP, ENOMEM); if (SMMAP_IS_LFL(flags, SMMAP_LFL_REPL)) { delimstr = sm_str_scpyn(NULL, " ", 2, 4); if (NULL == delimstr) { ret = sm_error_perm(SM_EM_MAP, ENOMEM); goto error; } ret = sm_str_wr_elem(delimstr, 0, delim); if (len_domain > 0) { subdom = sm_str_new(NULL, len_domain, len_domain + 2); if (NULL == subdom) { ret = sm_error_perm(SM_EM_MAP, ENOMEM); goto error; } } } if (SMMAP_IS_LFL(flags, SMMAP_LFL_MCR_REPL)) { macexp_ctx.macexp_user = user; macexp_ctx.macexp_detail = detail; macexp_ctx.macexp_domain = domain; macexp_ctx.macexp_tag = tag; macexp_ctx.macexp_delim = delim; } ret = sm_error_perm(SM_EM_MAP, SM_E_NOTFOUND); /* ** Macros to put '@' into the right place (after localpart or before domain) ** depending on the flag SMMAP_LFL_NOAT. */ /* put an '@' as separator after localpart */ #define SM_AT_LOCAL \ (SMMAP_IS_LFL(flags, SMMAP_LFL_NOAT) \ ? false : sm_is_err(sm_str_put(str, UCH_AT))) /* put an '@' as separator before domain */ #define SM_AT_DOM \ (SMMAP_IS_LFL(flags, SMMAP_LFL_NOAT) \ ? sm_is_err(sm_str_put(str, UCH_AT)) : false) j = 0; do { if (SMMAP_IS_LFL(flags, SMMAP_LFL_MCR_REPL)) macexp_ctx.macexp_subdom = j; mapflags = SMMAP_FL_LWR_KEY | (tag != NULL ? SMMAP_FL_HAS_TAG : 0) | (user != NULL ? SMMAP_FL_HAS_LOCALPART : 0) | ((detail != NULL && sm_str_getlen(detail) > 0) ? SMMAP_FL_HAS_DETAIL : 0) | (domain != NULL ? SMMAP_FL_HAS_DOMAIN : 0) ; /* complete lookup (tag user delim detail @ domain) */ if (SMMAP_IS_LFL(flags, SMMAP_LFL_FULL) && SMMAP_LT_M_CAPS(map, mapflags)) { sm_str_clr(str); if ( (tag != NULL && sm_is_err(sm_str_cat(str, tag))) || (user != NULL && sm_is_err(sm_str_cat(str, user))) || (detail != NULL && (sm_is_err(sm_str_put(str, delim)) || sm_is_err(sm_str_cat(str, detail)))) || SM_AT_LOCAL || (domain != NULL && (SM_AT_DOM || sm_is_err(sm_str_catpart(str, (sm_rdstr_P) domain, j, len_domain)))) ) { ret = sm_error_perm(SM_EM_MAP, EINVAL); /* XXX */ goto error; } ret = mapc->sm_mapc_lookupf(map, mapflags, str, rhs); if (SM_MAP_DATA2BIG == ret) goto done; if (SM_SUCCESS == ret) { if (SMMAP_IS_LFL(flags, SMMAP_LFL_REPL) && j > 0 && subdom != NULL) { sm_str_clr(subdom); ret = sm_str_catpart(subdom, (sm_rdstr_P) domain, 0, j); if (sm_is_err(ret)) goto error; argv[0] = subdom; ret = sm_map_repldig(str, rhs, 1, argv); if (sm_is_err(ret)) goto error; } if (SMMAP_IS_LFL(flags, SMMAP_LFL_MCR_REPL)) { macexp_ctx.macexp_subdom = j; ret = sm_map_replmac(str, rhs, &macexp_ctx); if (sm_is_err(ret)) goto error; } goto done; } } /* lookup with ++ (tag user delim + @ domain) */ if (SMMAP_IS_LFL(flags, SMMAP_LFL_DETPLUS) && detail != NULL && sm_str_getlen(detail) > 0 && SMMAP_LT_M_CAPS(map, mapflags)) { sm_str_clr(str); if ( (tag != NULL && sm_is_err(sm_str_cat(str, tag))) || (user != NULL && sm_is_err(sm_str_cat(str, user))) || (detail != NULL && (sm_is_err(sm_str_put(str, delim)) || sm_is_err(sm_str_put(str, UCH_PLUS)))) || SM_AT_LOCAL || (domain != NULL && (SM_AT_DOM || sm_is_err(sm_str_catpart(str, (sm_rdstr_P) domain, j, len_domain)))) ) { ret = sm_error_perm(SM_EM_MAP, EINVAL); /* XXX */ goto error; } ret = mapc->sm_mapc_lookupf(map, mapflags, str, rhs); if (SM_MAP_DATA2BIG == ret) goto done; if (SM_SUCCESS == ret) { if (SMMAP_IS_LFL(flags, SMMAP_LFL_REPL)) { argv[0] = detail; ret = sm_map_repldig(str, rhs, 1, argv); if (sm_is_err(ret)) goto error; } if (SMMAP_IS_LFL(flags, SMMAP_LFL_MCR_REPL)) { ret = sm_map_replmac(str, rhs, &macexp_ctx); if (sm_is_err(ret)) goto error; } goto done; } } /* lookup with +* (tag user delim * @ domain) */ if (SMMAP_IS_LFL(flags, SMMAP_LFL_DETSTAR) && detail != NULL && SMMAP_LT_M_CAPS(map, mapflags)) { sm_str_clr(str); if ( (tag != NULL && sm_is_err(sm_str_cat(str, tag))) || (user != NULL && sm_is_err(sm_str_cat(str, user))) || (detail != NULL && (sm_is_err(sm_str_put(str, delim)) || sm_is_err(sm_str_put(str, UCH_STAR)))) || SM_AT_LOCAL || (domain != NULL && (SM_AT_DOM || sm_is_err(sm_str_catpart(str, (sm_rdstr_P) domain, j, len_domain)))) ) { ret = sm_error_perm(SM_EM_MAP, EINVAL); /* XXX */ goto error; } ret = mapc->sm_mapc_lookupf(map, mapflags, str, rhs); if (SM_MAP_DATA2BIG == ret) goto done; if (SM_SUCCESS == ret) { if (SMMAP_IS_LFL(flags, SMMAP_LFL_REPL)) { argv[0] = detail; ret = sm_map_repldig(str, rhs, 1, argv); if (sm_is_err(ret)) goto error; } if (SMMAP_IS_LFL(flags, SMMAP_LFL_MCR_REPL)) { ret = sm_map_replmac(str, rhs, &macexp_ctx); if (sm_is_err(ret)) goto error; } goto done; } } /* lookup with * (tag user * @ domain) */ if (SMMAP_IS_LFL(flags, SMMAP_LFL_STAR) && SMMAP_LT_M_CAPS(map, mapflags)) { sm_str_clr(str); if ( (tag != NULL && sm_is_err(sm_str_cat(str, tag))) || (user != NULL && sm_is_err(sm_str_cat(str, user))) || sm_is_err(sm_str_put(str, UCH_STAR)) || SM_AT_LOCAL || (domain != NULL && (SM_AT_DOM || sm_is_err(sm_str_catpart(str, (sm_rdstr_P) domain, j, len_domain)))) ) { ret = sm_error_perm(SM_EM_MAP, EINVAL); /* XXX */ goto error; } ret = mapc->sm_mapc_lookupf(map, mapflags, str, rhs); if (SM_MAP_DATA2BIG == ret) goto done; if (SM_SUCCESS == ret) { if (SMMAP_IS_LFL(flags, SMMAP_LFL_REPL)) { argv[0] = delimstr; argv[1] = detail; ret = sm_map_repldig(str, rhs, 2, argv); if (sm_is_err(ret)) goto error; } if (SMMAP_IS_LFL(flags, SMMAP_LFL_MCR_REPL)) { ret = sm_map_replmac(str, rhs, &macexp_ctx); if (sm_is_err(ret)) goto error; } goto done; } } /* ** lookup without detail (tag user @ domain). ** This does not match if +detail exists (i.e., if delim ** is in the address) unless SMMAP_LFL_IMPLDET is set. */ if (SMMAP_IS_LFL(flags, SMMAP_LFL_USER) && SMMAP_LT_M_CAPS(map, mapflags) && (NULL == detail || SMMAP_IS_LFL(flags, SMMAP_LFL_IMPLDET))) { mapflags = SMMAP_FL_LWR_KEY | (tag != NULL ? SMMAP_FL_HAS_TAG : 0) | (user != NULL ? SMMAP_FL_HAS_LOCALPART : 0) | ((detail != NULL && sm_str_getlen(detail) > 0) ? SMMAP_FL_HAS_DETAIL : 0) | (domain != NULL ? SMMAP_FL_HAS_DOMAIN : 0) ; sm_str_clr(str); if ( (tag != NULL && sm_is_err(sm_str_cat(str, tag))) || (user != NULL && sm_is_err(sm_str_cat(str, user))) || SM_AT_LOCAL || (domain != NULL && (SM_AT_DOM || sm_is_err(sm_str_catpart(str, (sm_rdstr_P) domain, j, len_domain)))) ) { ret = sm_error_perm(SM_EM_MAP, EINVAL); /* XXX */ goto error; } ret = mapc->sm_mapc_lookupf(map, mapflags, str, rhs); if (SM_MAP_DATA2BIG == ret) goto done; if (SM_SUCCESS == ret) { if (SMMAP_IS_LFL(flags, SMMAP_LFL_REPL)) { argv[0] = delimstr; argv[1] = detail; ret = sm_map_repldig(str, rhs, NULL == detail ? 1 : 2, argv); if (sm_is_err(ret)) goto error; } if (SMMAP_IS_LFL(flags, SMMAP_LFL_MCR_REPL)) { ret = sm_map_replmac(str, rhs, &macexp_ctx); if (sm_is_err(ret)) goto error; } goto done; } } /* ** XXX This might not be the best order! ** user@sub.domain - sub.domain - user@.domain - .domain ** Wouldn't this be better: ** user@sub.domain - user@.domain - sub.domain - .domain */ mapflags = SMMAP_FL_LWR_KEY | (tag != NULL ? SMMAP_FL_HAS_TAG : 0) | (domain != NULL ? SMMAP_FL_HAS_DOMAIN : 0) ; /* lookup just domain (@ domain) */ if (SMMAP_IS_LFL(flags, SMMAP_LFL_DOMAIN) && SMMAP_LT_M_CAPS(map, mapflags)) { sm_str_clr(str); if ( (tag != NULL && sm_is_err(sm_str_cat(str, tag))) || (domain != NULL && (SM_AT_DOM || sm_is_err(sm_str_catpart(str, (sm_rdstr_P) domain, j, len_domain)))) ) { ret = sm_error_perm(SM_EM_MAP, EINVAL); /* XXX */ goto error; } ret = mapc->sm_mapc_lookupf(map, SMMAP_FL_LWR_KEY, str, rhs); if (SM_MAP_DATA2BIG == ret) goto done; if (SM_SUCCESS == ret) { if (SMMAP_IS_LFL(flags, SMMAP_LFL_REPL)) { argv[0] = user; argv[1] = detail; ret = sm_map_repldig(str, rhs, 2, argv); if (sm_is_err(ret)) goto error; } if (SMMAP_IS_LFL(flags, SMMAP_LFL_MCR_REPL)) { ret = sm_map_replmac(str, rhs, &macexp_ctx); if (sm_is_err(ret)) goto error; } goto done; } } if (domain != NULL && SMMAP_IS_LFL(flags, SMMAP_LFL_DOTSUBDOM)) { ++j; /* search for next '.' */ while (j < len_domain && sm_str_rd_elem(domain, j) != '.') ++j; } } while (domain != NULL && SMMAP_IS_LFL(flags, SMMAP_LFL_DOTSUBDOM) && j < len_domain); /* ** This needs only to be done if domain is not NULL otherwise ** it's the same as above! */ if (domain != NULL && SMMAP_IS_LFL(flags, SMMAP_LFL_LOCAL)) { mapflags = SMMAP_FL_LWR_KEY | (tag != NULL ? SMMAP_FL_HAS_TAG : 0) | (user != NULL ? SMMAP_FL_HAS_LOCALPART : 0) | ((detail != NULL && sm_str_getlen(detail) > 0) ? SMMAP_FL_HAS_DETAIL : 0) ; /* complete lookup (tag user delim detail) */ if (SMMAP_IS_LFL(flags, SMMAP_LFL_FULL) && SMMAP_LT_M_CAPS(map, mapflags)) { sm_str_clr(str); if ( (tag != NULL && sm_is_err(sm_str_cat(str, tag))) || (user != NULL && sm_is_err(sm_str_cat(str, user))) || (detail != NULL && (sm_is_err(sm_str_put(str, delim)) || sm_is_err(sm_str_cat(str, detail)))) || SM_AT_LOCAL ) { ret = sm_error_perm(SM_EM_MAP, EINVAL); /* XXX */ goto error; } ret = mapc->sm_mapc_lookupf(map, mapflags, str, rhs); if (SM_MAP_DATA2BIG == ret) goto done; if (SM_SUCCESS == ret) goto done; } /* lookup with ++ (tag user delim +)*/ if (SMMAP_IS_LFL(flags, SMMAP_LFL_DETPLUS) && detail != NULL && sm_str_getlen(detail) > 0 && SMMAP_LT_M_CAPS(map, mapflags)) { sm_str_clr(str); if ( (tag != NULL && sm_is_err(sm_str_cat(str, tag))) || (user != NULL && sm_is_err(sm_str_cat(str, user))) || (detail != NULL && (sm_is_err(sm_str_put(str, delim)) || sm_is_err(sm_str_put(str, UCH_PLUS)))) || SM_AT_LOCAL ) { ret = sm_error_perm(SM_EM_MAP, EINVAL); /* XXX */ goto error; } ret = mapc->sm_mapc_lookupf(map, mapflags, str, rhs); if (SM_MAP_DATA2BIG == ret) goto done; if (SM_SUCCESS == ret) { if (SMMAP_IS_LFL(flags, SMMAP_LFL_REPL)) { argv[0] = detail; ret = sm_map_repldig(str, rhs, 1, argv); if (sm_is_err(ret)) goto error; } if (SMMAP_IS_LFL(flags, SMMAP_LFL_MCR_REPL)) { ret = sm_map_replmac(str, rhs, &macexp_ctx); if (sm_is_err(ret)) goto error; } goto done; } } /* lookup with +* (tag user delim *) */ if (SMMAP_IS_LFL(flags, SMMAP_LFL_DETSTAR) && detail != NULL && SMMAP_LT_M_CAPS(map, mapflags)) { sm_str_clr(str); if ( (tag != NULL && sm_is_err(sm_str_cat(str, tag))) || (user != NULL && sm_is_err(sm_str_cat(str, user))) || (detail != NULL && (sm_is_err(sm_str_put(str, delim)) || sm_is_err(sm_str_put(str, UCH_STAR)))) || SM_AT_LOCAL ) { ret = sm_error_perm(SM_EM_MAP, EINVAL); /* XXX */ goto error; } ret = mapc->sm_mapc_lookupf(map, mapflags, str, rhs); if (SM_MAP_DATA2BIG == ret) goto done; if (SM_SUCCESS == ret) { if (SMMAP_IS_LFL(flags, SMMAP_LFL_REPL)) { argv[0] = detail; ret = sm_map_repldig(str, rhs, 1, argv); if (sm_is_err(ret)) goto error; } if (SMMAP_IS_LFL(flags, SMMAP_LFL_MCR_REPL)) { ret = sm_map_replmac(str, rhs, &macexp_ctx); if (sm_is_err(ret)) goto error; } goto done; } } /* lookup with * (tag user *) */ if (SMMAP_IS_LFL(flags, SMMAP_LFL_STAR) && SMMAP_LT_M_CAPS(map, mapflags)) { sm_str_clr(str); if ( (tag != NULL && sm_is_err(sm_str_cat(str, tag))) || (user != NULL && sm_is_err(sm_str_cat(str, user))) || sm_is_err(sm_str_put(str, UCH_STAR)) || SM_AT_LOCAL ) { ret = sm_error_perm(SM_EM_MAP, EINVAL); /* XXX */ goto error; } ret = mapc->sm_mapc_lookupf(map, mapflags, str, rhs); if (SM_MAP_DATA2BIG == ret) goto done; if (SM_SUCCESS == ret) { if (SMMAP_IS_LFL(flags, SMMAP_LFL_REPL)) { argv[0] = delimstr; argv[1] = detail; ret = sm_map_repldig(str, rhs, 2, argv); if (sm_is_err(ret)) goto error; } if (SMMAP_IS_LFL(flags, SMMAP_LFL_MCR_REPL)) { ret = sm_map_replmac(str, rhs, &macexp_ctx); if (sm_is_err(ret)) goto error; } goto done; } } mapflags = SMMAP_FL_LWR_KEY | (tag != NULL ? SMMAP_FL_HAS_TAG : 0) | (user != NULL ? SMMAP_FL_HAS_LOCALPART : 0) ; /* lookup without detail (tag user) */ if (SMMAP_IS_LFL(flags, SMMAP_LFL_USER) && SMMAP_LT_M_CAPS(map, mapflags) && (NULL == detail || SMMAP_IS_LFL(flags, SMMAP_LFL_IMPLDET))) { sm_str_clr(str); if ( (tag != NULL && sm_is_err(sm_str_cat(str, tag))) || (user != NULL && sm_is_err(sm_str_cat(str, user))) || SM_AT_LOCAL ) { ret = sm_error_perm(SM_EM_MAP, EINVAL); /* XXX */ goto error; } ret = mapc->sm_mapc_lookupf(map, mapflags, str, rhs); if (SM_MAP_DATA2BIG == ret) goto done; if (SM_SUCCESS == ret) { if (SMMAP_IS_LFL(flags, SMMAP_LFL_REPL)) { argv[0] = user; argv[1] = detail; ret = sm_map_repldig(str, rhs, 2, argv); if (sm_is_err(ret)) goto error; } if (SMMAP_IS_LFL(flags, SMMAP_LFL_MCR_REPL)) { ret = sm_map_replmac(str, rhs, &macexp_ctx); if (sm_is_err(ret)) goto error; } goto done; } } } done: SM_STR_FREE(delimstr); SM_STR_FREE(subdom); sm_str_free(str); return ret; error: SM_STR_FREE(delimstr); SM_STR_FREE(subdom); sm_str_free(str); return ret; }