--- orig/meta1-1.0.PreAlpha28.0/libsmmap/t-mm.c	2008-01-09 07:28:37.000000000 +0200
+++ meta1-1.0.PreAlpha28.0/libsmmap/t-mm.c	2009-06-09 02:05:16.000000000 +0300
@@ -22,10 +22,13 @@
 #include "sm/mapclasses.h"
 #include "sm/bdb.h"
 #include "sm/cdb_map.h"
+#include "sm/net.h"
+#include "sm/rfc2821.h"
 
 #include "sm/io.h"
 
 static int Verbose = 0;
+static uint line = 0;
 
 #define SEP	':'
 #define SEP_WSPC	(-1)
@@ -40,11 +43,211 @@
 #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 */
+#define SMM_FL_STRICT_SYNTAX_CHECK 0x00000020u /* strict syntax check */
 
 #ifndef SM_ALLOW_ROOT
 # define SM_ALLOW_ROOT 0
 #endif
 
+struct prefix_tab {
+	const char *name;
+	int (*check_fn) (const char *, uint);
+};
+
+static const struct prefix_tab *
+find_prefix(struct prefix_tab *tab, const char *pfx, uint pfxlen)
+{
+	for (; tab->name; tab++) {
+		size_t len = strlen(tab->name);
+		if (len == pfxlen && memcmp(tab->name, pfx, pfxlen) == 0)
+			return tab;
+	}
+	return NULL;
+}
+
+/* Valid addresses for from: and to: are RFC 2821 addresses
+   without the angle backets (localpart@domain) as well as
+   partial addresses in the form localpart and @domain... */
+static int
+check_email(const char *buf, uint len)
+{
+	uint i;
+	sm_a2821_T addr;
+	sm_ret_T ret;
+	sm_str_T str;
+	sm_rpool_P rpool;
+	
+	if (buf[0] == '<') {
+		fprintf(stderr, "%u: %.*s: do not use angle brackets",
+			line, len, buf);
+		return 1;
+	}
+	if (buf[0] == '@')
+		return 0;
+	else if (strchr (buf, '@') == 0) {
+		/* Looks like a localpart. */
+		if (strchr (buf, '.')) {
+			fprintf(stderr, "%u: %.*s: warning: suspicious e-mail address\n",
+			line, len, buf);
+		}
+		return 0;
+	}
+	
+	sm_str_assign(str, NULL, (uchar *)buf, len, len);
+	rpool = sm_rpool_new(NULL);
+	A2821_INIT_RP(&addr, rpool);
+	ret = t2821_scan((sm_rdstr_P)&str, &addr, 0);
+	if (!sm_is_error(ret))
+		ret = t2821_parse(&addr, R2821_CORRECT&~R2821_ANGLE);
+	a2821_free(&addr);
+	sm_rpool_delete(rpool);
+	if (sm_is_error(ret)) {
+		fprintf(stderr, "%u: %.*s: invalid e-mail address\n",
+			line, len, buf);
+		return 1;
+	}
+	return 0;
+}
+	
+static int
+check_ipaddr(const char *buf, uint len)
+{
+	char ipbuf[16];
+	ipv4_T ipv4;
+		
+	if (len < sizeof(ipbuf)) {
+		memcpy(ipbuf, buf, len);
+		ipbuf[len] = 0;
+
+		if (!sm_is_error(sm_inet_a2ipv4(ipbuf, NULL, &ipv4)))
+			return 0;
+	}
+		
+	fprintf(stderr, "%u: %.*s: invalid IPv4 address\n", line, len, buf);
+	return 1;
+}
+		
+
+static struct prefix_tab access_pfx_tab[] = {
+	{ "from", check_email },
+	{ "to", check_email },
+	{ "cltaddr", check_ipaddr },
+	{ "cltname", NULL },
+	{ "cltresolve", NULL },
+	{ "mxbadip", check_ipaddr },
+	{ "certissuer", NULL },
+	{ "certsubject", NULL },
+	{ "protectedrcpt", NULL },
+	{ "smtps_session_conf", NULL },
+	{ "smtpc_rcpt_conf", NULL },
+	{ NULL }
+};
+
+#define QUICK_PFX "quick:"
+#define QUICK_PFX_LEN (sizeof(QUICK_PFX) - 1)
+
+#define ERROR_PFX "error:"
+#define ERROR_PFX_LEN (sizeof(ERROR_PFX) - 1)
+
+static int
+is_access_rhs(const char *start, uint len)
+{
+	static char *rhs[] = { "relay", "ok", "reject", "discard",
+			       "cont", NULL };
+	int i;
+
+	for (i = 0; rhs[i]; i++)
+		if (strlen(rhs[i]) == len && memcmp(rhs[i], start, len) == 0)
+			return 1;
+
+	if (len > ERROR_PFX_LEN
+	    && memcmp(start, ERROR_PFX, ERROR_PFX_LEN) == 0) {
+		/* FIXME: Check for XYZ A.B.C.D format */
+		return 1;
+	 }
+	 return 0;
+}
+
+int
+check_access_syntax (sm_str_P key, sm_str_P data)
+{
+	uint i, len, col = 0;
+	const char *start, *p;
+	const struct prefix_tab *pfx;
+	
+	len = sm_str_getlen(key);
+	start = sm_str_data(key);
+
+	for (i = 0; i < len; i++)
+		if (start[i] == ':') {
+			col = i;
+			break;
+		}
+	if (!col) {
+		fprintf(stderr, "%u: %.*s: no prefix in key\n",
+			line, len, start);
+		return 1;
+	}
+
+	/* Check the key */
+	pfx = find_prefix(access_pfx_tab, start, col);
+	if (!pfx) {
+		fprintf(stderr, "%u: %.*s: unknown prefix\n",
+			line, col, start);
+		return 1;
+	}
+	if (pfx->check_fn && pfx->check_fn(start + col + 1, len - col - 1))
+		return 1;
+
+	/* Check value */
+	len = sm_str_getlen(data);
+	start = sm_str_data(data);
+
+	if (len > QUICK_PFX_LEN
+	    && memcmp(start, QUICK_PFX, QUICK_PFX_LEN) == 0) {
+		len -= QUICK_PFX_LEN;
+		start += QUICK_PFX_LEN;
+	}
+
+	if (!is_access_rhs(start, len)) {
+		fprintf(stderr, "%u: %.*s: invalid rhs\n", line, len, start);
+		return 1;
+	}
+		
+	
+	return 0;
+}
+
+
+typedef int (*check_syntax_t) (sm_str_P, sm_str_P);
+
+static check_syntax_t check_syntax;
+
+static struct syntax_tab {
+	const char *name;
+	check_syntax_t cfn;
+} syntax_tab[] = {
+	{ "access", check_access_syntax },
+	{ "aliases", NULL },
+	{ "qmgr_conf", NULL },
+	{ NULL }
+};
+	  
+static void
+set_syntax(const char *arg)
+{
+	struct syntax_tab *sp;
+
+	for (sp = syntax_tab; sp->name; sp++)
+		if (strcmp(sp->name, arg) == 0) {
+			check_syntax = sp->cfn;
+			return;
+		}
+	fprintf(stderr, "%u: unknown format: %s\n", line, arg);
+	exit(EX_USAGE);
+}
+	
+		   
 static char buf[MAX_IN_LEN];
 #if MAX_IN_LEN >= INT_MAX
 ERROR _MAX_IN_LEN >= _INT_MAX
@@ -59,8 +262,9 @@
 	sm_map_data_T data;
 	char *delim, *rhs;
 	int c;
-	uint u, line;
-
+	uint u;
+	unsigned err_count = 0;
+	
 	line = 0;
 	while (fgets(buf, sizeof(buf), stdin) != NULL) {
 		++line;
@@ -87,8 +291,7 @@
 		if (NULL == delim) {
 			/* complain about missing delim */
 			fprintf(stderr,
-				"no key/value separator found; line=%u\n",
-					line);
+				"%u: no key/value separator found\n", line);
 			continue;
 		}
 
@@ -105,7 +308,7 @@
 				++u;
 		}
 		if (u >= sizeof(buf) || c == '\0' || c == '\n') {
-			fprintf(stderr, "rhs is empty, line=%u, u=%u\n",
+			fprintf(stderr, "%u: rhs is empty, u=%u\n",
 				line, u);
 			continue;
 		}
@@ -113,7 +316,7 @@
 		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",
+			fprintf(stderr, "%u: can't find end of rhs\n",
 				line);
 			continue;
 		}
@@ -131,7 +334,7 @@
 		}
 
 		if (!SM_IS_FLAG(flags, SMM_FL_EMPTY_KEY) && len == 0) {
-			fprintf(stderr,"empty key; line=%u\n", line);
+			fprintf(stderr,"%u: empty key\n", line);
 			continue;
 		}
 		sm_str_assign(key, NULL, (uchar *)buf, len, len);
@@ -145,6 +348,9 @@
 				, sm_str_getlen(&data)
 				);
 		}
+		/* --gray: */
+		if (check_syntax && check_syntax(&key, &data))
+			err_count++;
 
 		ret = sm_map_add(map, &key, &data, SMMAP_AFL_UNIQUE);
 		if (sm_is_err(ret)) {
@@ -156,6 +362,9 @@
 	}
 
 	ret = sm_map_close(map, 0);
+	/* --gray */
+	if (err_count && SM_IS_FLAG(flags, SMM_FL_STRICT_SYNTAX_CHECK))
+		ret = sm_err_temp(EINVAL);
 	return ret;
 }
 
@@ -225,9 +434,12 @@
 usage(const char *prg)
 {
 	fprintf(stderr, "usage: %s [options]\n"
+		"-e        exit with error on syntax errors (see -H)\n"
 		"-E        allow empty key\n"
 		"-f        do not convert keys to lower case\n"
 		"-F file   name of db file [%s]\n"
+		"-H format validate input syntax according to format\n"
+		"          valid formats are: access, aliases, qmgr_conf\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"
@@ -280,8 +492,11 @@
 		exit(EX_USAGE);
 	}
 #endif
-	while ((c = getopt(argc, argv, "EfF:hl:n:SsT:t:uVw")) != -1) {
+	while ((c = getopt(argc, argv, "eEfF:hH:l:n:SsT:t:uVw")) != -1) {
 		switch (c) {
+		  case 'e':
+			flags |= SMM_FL_STRICT_SYNTAX_CHECK;
+			break;
 		  case 'E':
 			flags |= SMM_FL_EMPTY_KEY;
 			break;
@@ -291,6 +506,9 @@
 		  case 'F':
 			SM_STRDUP_OPT(mapfile, optarg);
 			break;
+		  case 'H':
+			set_syntax(optarg);
+			break;
 		  case 'l':
 			list_mapc = (int) strtol(optarg, NULL, 0);
 			break;
@@ -351,8 +569,11 @@
 	}
 	else {
 		c = sm_openmap(mapname, mapfile, maptype, sep, flags);
-		if (sm_is_err(c) && rmonfail) {
- 			(void) unlink(mapfile);
+		if (sm_is_err(c)) {
+			fprintf(stderr, "%s: %s\n",
+				prg, smerr2txt(c));
+			if (rmonfail)
+				unlink(mapfile);
 		}
 	}
 	return c;
