/* * Copyright (c) 2003-2006 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: t-mm.c,v 1.40 2006/10/05 04:27:39 ca Exp $") #include "sm/error.h" #include "sm/sysexits.h" #include "sm/ctype.h" #include "sm/heap.h" #include "sm/memops.h" #include "sm/maps.h" #include "sm/mapc.h" #include "sm/map.h" #include "sm/mapclasses.h" #include "sm/bdb.h" #include "sm/cdb_map.h" #include "sm/io.h" static int Verbose = 0; #define SEP ':' #define SEP_WSPC (-1) #define MAPC_TYPE "hash" #define MAPC_NAME "aliases" #define MAPC_FILE "aliases.db" #define MAX_IN_LEN (256 * 1024) #define SM_COMM_CHAR '#' #define SMM_FL_LWR_KEY 0x00000001u /* change key to lower case */ #define SMM_FL_SKIP_LEAD_SP 0x00000002u /* skip leading whitespace on RHS */ #define SMM_FL_WHSP_DELIM 0x00000004u /* use whitespace as delimiter */ #define SMM_FL_EMPTY_KEY 0x00000008u /* allow empty keys */ #define SMM_FL_SKIP_COMM 0x00000010u /* skip comments */ #ifndef SM_ALLOW_ROOT # define SM_ALLOW_ROOT 0 #endif static char buf[MAX_IN_LEN]; #if MAX_IN_LEN >= INT_MAX ERROR _MAX_IN_LEN >= _INT_MAX #endif static int sm_makemap(sm_map_P map, int sep, uint32_t flags) { sm_ret_T ret; size_t len; sm_map_key_T key; sm_map_data_T data; char *delim, *rhs; int c; uint u, line; line = 0; while (fgets(buf, sizeof(buf), stdin) != NULL) { ++line; if (SM_IS_FLAG(flags, SMM_FL_SKIP_COMM)) { char *p; if (*buf == '\0' || *buf == SM_COMM_CHAR) continue; p = buf; while (*p != '\0' && ISSPACE(*p)) p++; if (*p == '\0') continue; } /* find key/value separator */ if (SM_IS_FLAG(flags, SMM_FL_WHSP_DELIM)) delim = strpbrk(buf, " \t"); else delim = strchr(buf, sep); if (delim == NULL) { /* complain about missing delim */ fprintf(stderr, "no key/value separator found; line=%u\n", line); continue; } SM_ASSERT(delim < buf + sizeof(buf)); SM_ASSERT(delim >= buf); u = (delim - buf) + 1; *delim = '\0'; c = '\0'; /* skip over leading space; make this optional? */ if (SM_IS_FLAG(flags, SMM_FL_SKIP_LEAD_SP)) { while (u < sizeof(buf) && (c = buf[u]) != '\0' && isspace(c) && c != '\n') ++u; } if (u >= sizeof(buf) || c == '\0' || c == '\n') { fprintf(stderr, "rhs is empty, line=%u, u=%u\n", line, u); continue; } rhs = buf + u; while (u < sizeof(buf) && (c = buf[u]) != '\0' && c != '\n') ++u; if (u >= sizeof(buf) || c != '\n') { fprintf(stderr, "can't find end of rhs; line=%u\n", line); continue; } buf[u] = '\0'; len = strlen(buf); if (SM_IS_FLAG(flags, SMM_FL_LWR_KEY)) { uint j; for (j = 0; j < len; j++) { c = buf[j]; if (ISUPPER(c)) buf[j] = TOLOWER(c); } } if (!SM_IS_FLAG(flags, SMM_FL_EMPTY_KEY) && len == 0) { fprintf(stderr,"empty key; line=%u\n", line); continue; } sm_str_assign(key, NULL, (uchar *)buf, len, len); sm_str_assign(data, NULL, (uchar *)rhs, strlen(rhs), strlen(rhs)); if (Verbose > 0) { fprintf(stderr, "lhs='%s' [%d], rhs='%s' [%d]\n" , (char *)sm_str_data(&key) , sm_str_getlen(&key) , (char *)sm_str_data(&data) , sm_str_getlen(&data) ); } ret = sm_map_add(map, &key, &data, SMMAP_AFL_UNIQUE); if (sm_is_err(ret)) { fprintf(stderr, "sm_map_add=failed, error=%s; line=%u\n" , smerr2txt(ret), line); sm_map_close(map, 0); return ret; } } ret = sm_map_close(map, 0); return ret; } static int sm_openmap(const char *mapname, const char *mapfile, const char *maptype, int sep, uint32_t flags) { sm_ret_T ret; sm_maps_P maps; sm_map_P map; sm_cstr_P mtype, mname; maps = NULL; mtype = mname = NULL; ret = sm_maps_init(&maps); if (maps == NULL) return ret; mtype = sm_cstr_scpyn0((const uchar *)maptype, strlen(maptype)); if (mtype == NULL) goto error; mname = sm_cstr_scpyn0((const uchar *)mapname, strlen(mapname)); if (mname == NULL) goto error; ret = sm_bdb_class_create(maps); #if MTA_USE_TINYCDB ret = sm_cdb_class_create(maps); #endif #if 0 /* "delete" the file to make sure the result won't contain garbage? */ ret = truncate(mapfile, 0); #endif /* 0 */ map = NULL; ret = sm_map_open(maps, mname, mtype, SMAP_MODE_CREATE, mapfile, SMAP_MODE_RDWR, &map, SMPO_END); if (!sm_is_success(ret)) goto error; ret = sm_makemap(map, sep, flags); ret = sm_maps_term(maps); SM_CSTR_FREE(mtype); SM_CSTR_FREE(mname); return ret; error: sm_maps_term(maps); return ret; } static void usage(const char *prg) { fprintf(stderr, "usage: %s [options]\n" "-E allow empty key\n" "-f do not convert keys to lower case\n" "-F file name of db file [%s]\n" "-l n list available map classes (n: verbosity)\n" "-n name name of map [%s]\n" "-s do not ignore comment or empty lines\n" "-T type map type [%s]\n" "-t c separator [%c]\n" "-V increase verbosity\n" "-w use whitespace as separator\n" "Options -t and -w are mutually exclusive.\n" "\n%s creates a %s map from the data provided via stdin.\n" "Entries must be of the form\n" "lhs%crhs\n" "(where '%c' is the default separator)\n" "Entries cannot span multiple lines.\n" , prg , MAPC_FILE , MAPC_NAME , MAPC_TYPE , SEP , prg , MAPC_TYPE , SEP , SEP ); exit(EX_USAGE); } int main(int argc, char *argv[]) { int c, sep; uint32_t flags; int list_mapc; char *mapname, *mapfile, *maptype, *prg; prg = argv[0]; sep = (int) SEP; mapname = MAPC_NAME; mapfile = MAPC_FILE; maptype = MAPC_TYPE; list_mapc = -1; flags = SMM_FL_LWR_KEY|SMM_FL_SKIP_LEAD_SP|SMM_FL_SKIP_COMM; #if !SM_ALLOW_ROOT if (getuid() == 0 || geteuid() == 0) { fprintf(stderr, "%s: ERROR: do not run this as super-user!\n", prg); exit(EX_USAGE); } #endif while ((c = getopt(argc, argv, "EfF:hl:n:SsT:t:Vw")) != -1) { switch (c) { case 'E': flags |= SMM_FL_EMPTY_KEY; break; case 'f': flags &= ~SMM_FL_LWR_KEY; break; case 'F': SM_STRDUP_OPT(mapfile, optarg); break; case 'l': list_mapc = (int) strtol(optarg, NULL, 0); break; case 'n': SM_STRDUP_OPT(mapname, optarg); break; case 's': flags &= ~SMM_FL_SKIP_COMM; break; case 'S': flags &= ~SMM_FL_SKIP_LEAD_SP; break; case 'T': SM_STRDUP_OPT(maptype, optarg); break; case 't': if (SM_IS_FLAG(flags, SMM_FL_WHSP_DELIM)) { usage(prg); /* NOTREACHED */ return EX_USAGE; } sep = (int) (*optarg); break; case 'V': ++Verbose; break; case 'w': if (sep != (int) SEP) { usage(prg); /* NOTREACHED */ return EX_USAGE; } flags |= SMM_FL_WHSP_DELIM; break; case 'h': default: usage(prg); /* NOTREACHED */ return EX_USAGE; } } if (list_mapc >= 0) { sm_ret_T ret; sm_maps_P maps; maps = NULL; ret = sm_maps_init(&maps); if (NULL == maps) exit(EX_OSERR); ret = sm_maps_create(maps); if (sm_is_err(ret)) exit(EX_OSERR); c = sm_mapc_list(smioout, maps, list_mapc, SMMAPC_FL_FILE|SMMAPC_FL_ADD); } else c = sm_openmap(mapname, mapfile, maptype, sep, flags); return c; }