--- milter-greylist-3.0/autowhite.c	Tue Sep  5 00:05:58 2006
+++ milter-greylist-3.0-patched/autowhite.c	Fri Mar 16 02:43:03 2007
@@ -255,6 +255,48 @@
 	return;
 }
 
+void
+autowhite_add_ent(struct sockaddr *sa, socklen_t salen, char *from, char *rcpt,
+    time_t date)
+{
+	char addr[IPADDRSTRLEN];
+	struct autowhite *aw, *next_aw;
+	ipaddr *mask;
+	struct autowhite_bucket *b;
+
+	mask = NULL;
+	if (!iptostring(sa, salen, addr, sizeof(addr)))
+		return;
+	switch(sa->sa_family) {
+	case AF_INET:
+		mask = (ipaddr *)&conf.c_match_mask;
+		break;
+#ifdef AF_INET6
+	case AF_INET6:
+		mask = (ipaddr *)&conf.c_match_mask6;
+		break;
+#endif
+	}
+	autowhite_timeout();
+
+	AUTOWHITE_RDLOCK;
+	b = &autowhite_buckets[BUCKET_HASH(sa, from, rcpt, AUTOWHITE_BUCKETS)];
+	pthread_mutex_lock(&b->bucket_mtx);
+	for (aw = TAILQ_FIRST(&b->b_autowhite_head); aw; aw = next_aw) {
+		next_aw = TAILQ_NEXT(aw, ab_list);
+		if (ip_match(sa, aw->a_sa, mask) &&
+		    !strcasecmp(from, aw->a_from) &&
+		    !strcasecmp(rcpt, aw->a_rcpt))
+			break;
+	}
+
+	/* Entry not found, create it. */
+	if (aw == NULL)
+		aw = autowhite_get(sa, salen, from, rcpt, date);
+	pthread_mutex_unlock(&b->bucket_mtx);
+	AUTOWHITE_UNLOCK;
+}
+
 int
 autowhite_check(sa, salen, from, rcpt, queueid, gldelay, autowhite)
 	struct sockaddr *sa;
@@ -471,6 +513,42 @@
 	free(aw);
 
 	return;
+}
+
+int
+autowhite_del(struct sockaddr *sa, socklen_t salen, char *from, char *rcpt)
+{
+	struct autowhite *aw, *next_aw;
+	ipaddr *mask;
+	int error;
+
+	mask = NULL;
+	error = ENOENT;
+	switch (sa->sa_family) {
+	case AF_INET:
+		mask = (ipaddr *)&conf.c_match_mask;
+		break;
+#ifdef AF_INET6
+	case AF_INET6:
+		mask = (ipaddr *)&conf.c_match_mask6;
+		break;
+#endif
+	}
+
+	AUTOWHITE_WRLOCK;
+	for (aw = TAILQ_FIRST(&autowhite_head); aw; aw = next_aw) {
+		next_aw = TAILQ_NEXT(aw, a_list);
+		if (ip_match(sa, aw->a_sa, mask) &&
+		    !strcasecmp(from, aw->a_from) &&
+		    !strcasecmp(rcpt, aw->a_rcpt)) {
+			autowhite_put(aw);
+			error = 0;
+			break;
+		}
+	}
+	AUTOWHITE_UNLOCK;
+
+	return (error);
 }
 
 int
--- milter-greylist-3.0/autowhite.h	Tue Sep  5 00:05:58 2006
+++ milter-greylist-3.0-patched/autowhite.h	Fri Mar 16 02:43:03 2007
@@ -72,9 +72,11 @@
 void autowhite_put(struct autowhite *);
 void autowhite_add(struct sockaddr *, socklen_t, char *, char *, time_t *,
     char *);
+void	autowhite_add_ent(struct sockaddr *, socklen_t, char *, char *, time_t);
 int autowhite_check(struct sockaddr *, socklen_t, char *, char *, 
     char *, time_t, time_t);
 int autowhite_textdump(FILE *);
+int	autowhite_del(struct sockaddr *, socklen_t, char *, char *);
 int autowhite_del_addr(struct sockaddr *, socklen_t);
 
 #endif /* _AUTOWHITE_H_ */
--- milter-greylist-3.0/sync.c	Tue Nov  7 06:12:12 2006
+++ milter-greylist-3.0-patched/sync.c	Fri Mar 16 02:52:26 2007
@@ -56,6 +56,7 @@
 #include "pending.h"
 #include "sync.h"
 #include "conf.h"
+#include "dump.h"
 #include "autowhite.h"
 #include "milter-greylist.h"
 
@@ -913,10 +914,10 @@
 	char *awstr;
 	char *cookie;
 	char line[LINELEN + 1];
-	peer_sync_t action;
+	peer_sync_t action = 0;
 	sockaddr_t addr;
 	socklen_t addrlen;
-	time_t date;
+	time_t date = 0;
 	time_t aw = time(NULL) + conf.c_autowhite_validity;
 
 	fprintf(stream, "200 Yeah, what do you want?\n");
@@ -954,6 +955,26 @@
 		} else if ((strncmp(cmd, "vers2", CMDLEN)) == 0) {
 			sync_vers(stream, 2);
 			continue;
+		} else if ((strncmp(cmd, "dump_grey", CMDLEN)) == 0) {
+			int greylisted_count;
+
+			dump_header(stream);
+			greylisted_count = pending_textdump(stream);
+			fprintf(stream, "#\n# Summary: %d greylisted\n#\n",
+			    greylisted_count);
+
+			fflush(stream);
+			continue;
+		} else if ((strncmp(cmd, "dump_white", CMDLEN)) == 0) {
+			int whitelisted_count;
+
+			dump_header(stream);
+			whitelisted_count = autowhite_textdump(stream);
+			fprintf(stream, "#\n# Summary: %d whitelisted\n#\n",
+			    whitelisted_count);
+
+			fflush(stream);
+			continue;
 		} else if ((strncmp(cmd, "add", CMDLEN)) == 0) {
 			action = PS_CREATE;
 		} else if ((strncmp(cmd, "del2", CMDLEN)) == 0) {
@@ -963,6 +984,11 @@
 			action = PS_DELETE;
 		} else if ((strncmp(cmd, "flush", CMDLEN)) == 0) {
 			action = PS_FLUSH;
+		} else if ((strncmp(cmd, "whiteadd", CMDLEN)) == 0) {
+			action = PS_CREATE_WHITE;
+		} else if ((strncmp(cmd, "whitedel", CMDLEN)) == 0) {
+			action = PS_DELETE_WHITE;
+			aw = -2;
 		} else {
 			fprintf(stream, "102 Invalid command \"%s\"\n", cmd);
 			fflush(stream);
@@ -1054,29 +1080,31 @@
 		(void)strncpy_rmsp(rcpt_clean, rcpt, ADDRLEN);
 		rcpt = rcpt_clean;
 
-		/*
-		 * get { "date" valid_date }
-		 */
-		if ((keyword = strtok_r(NULL, sep, &cookie)) == NULL) {
-			fprintf(stream, "103 Incomplete command\n");
-			fflush(stream);
-			continue;
-		}
+		if (aw != -2) {
+			/*
+			 * get { "date" valid_date }
+			 */
+			if ((keyword = strtok_r(NULL, sep, &cookie)) == NULL) {
+				fprintf(stream, "103 Incomplete command\n");
+				fflush(stream);
+				continue;
+			}
 
-		if (strncmp(keyword, "date", CMDLEN) != 0) {
-			fprintf(stream, 
-			    "104 Unexpected keyword \"%s\"\n", keyword);
-			fflush(stream);
-			continue;
-		}
+			if (strncmp(keyword, "date", CMDLEN) != 0) {
+				fprintf(stream, 
+				    "104 Unexpected keyword \"%s\"\n", keyword);
+				fflush(stream);
+				continue;
+			}
 
-		if ((datestr = strtok_r(NULL, sep, &cookie)) == NULL) {
-			fprintf(stream, "103 Incomplete command\n");
-			fflush(stream);
-			continue;
-		}
+			if ((datestr = strtok_r(NULL, sep, &cookie)) == NULL) {
+				fprintf(stream, "103 Incomplete command\n");
+				fflush(stream);
+				continue;
+			}
 
-		date = atoi(datestr);
+			date = atoi(datestr);
+		}
 
 		if (aw == -1) {
 			/*
@@ -1133,6 +1161,13 @@
 			pending_del_addr(SA(&addr), addrlen, NULL, 0);
 			autowhite_del_addr(SA(&addr), addrlen);
 		}
+		if (action == PS_CREATE_WHITE)
+			autowhite_add_ent(SA(&addr), addrlen, from, rcpt, date);
+		if (action == PS_DELETE_WHITE && autowhite_del(SA(&addr),
+		    addrlen, from, rcpt)) {
+			fprintf(stream, "201 Entry not found\n");
+			fflush(stream);
+		}
 
 		/* Flush modifications to disk */
 		dump_flush();
@@ -1185,6 +1220,17 @@
 	fprintf(stream, 
 	    "203 flush addr <ip> -- remove anything about an ip "
 	    " (version 3 only)\n");
+	fprintf(stream, "203 dump_grey -- dump the content of the greylist "
+	    " here\n");
+	fprintf(stream, "203 dump_white -- dump the content of the whitelist "
+	    " here\n");
+	fprintf(stream, 
+	    "203 whiteadd addr <ip> from <email> rcpt <email> date <time> "
+	    " -- add an entry in the autowhite\n");
+	fprintf(stream, 
+	    "203 whitedel addr <ip> from <email> rcpt <email> -- remove an "
+	    "entry from the autowhite\n");
+	
 	fflush(stream);
 
 	return;
--- milter-greylist-3.0/sync.h	Tue Sep  5 00:05:59 2006
+++ milter-greylist-3.0-patched/sync.h	Fri Mar 16 02:44:36 2007
@@ -75,7 +75,14 @@
 
 #define P_LOCAL	1
 
-typedef enum { PS_CREATE, PS_DELETE, PS_DELETE2, PS_FLUSH } peer_sync_t;
+typedef enum {
+	PS_CREATE,
+	PS_DELETE,
+	PS_DELETE2,
+	PS_FLUSH,
+	PS_CREATE_WHITE,
+	PS_DELETE_WHITE
+} peer_sync_t;
 
 struct sync {
 	struct peer *s_peer;
