|  |  | @ -7533,12 +7533,12 @@ static void event_tree(K_TREE *event_tree, char *list, char *reply, size_t siz, | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | // Events status/settings
 |  |  |  | // Events status/settings
 | 
			
		
	
		
		
			
				
					
					|  |  |  | static char *cmd_events(__maybe_unused PGconn *conn, char *cmd, char *id, |  |  |  | static char *cmd_events(__maybe_unused PGconn *conn, char *cmd, char *id, | 
			
		
	
		
		
			
				
					
					|  |  |  | 			__maybe_unused tv_t *now, __maybe_unused char *by, |  |  |  | 			tv_t *now, __maybe_unused char *by, | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 			__maybe_unused char *code, __maybe_unused char *inet, |  |  |  | 			__maybe_unused char *code, __maybe_unused char *inet, | 
			
		
	
		
		
			
				
					
					|  |  |  | 			__maybe_unused tv_t *cd, K_TREE *trf_root) |  |  |  | 			__maybe_unused tv_t *cd, K_TREE *trf_root) | 
			
		
	
		
		
			
				
					
					|  |  |  | { |  |  |  | { | 
			
		
	
		
		
			
				
					
					|  |  |  | 	K_ITEM *i_action, *i_cmd, *i_list, *i_ip, *i_eventname, *i_lifetime; |  |  |  | 	K_ITEM *i_action, *i_cmd, *i_list, *i_ip, *i_eventname, *i_lifetime; | 
			
		
	
		
		
			
				
					
					|  |  |  | 	K_ITEM *i_des, *i_item; |  |  |  | 	K_ITEM *i_des, *i_item, *next_item; | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 	K_TREE_CTX ctx[1]; |  |  |  | 	K_TREE_CTX ctx[1]; | 
			
		
	
		
		
			
				
					
					|  |  |  | 	IPS *ips; |  |  |  | 	IPS *ips; | 
			
		
	
		
		
			
				
					
					|  |  |  | 	char *action, *alert_cmd, *list, *ip, *eventname, *des; |  |  |  | 	char *action, *alert_cmd, *list, *ip, *eventname, *des; | 
			
		
	
	
		
		
			
				
					|  |  | @ -7798,7 +7798,6 @@ static char *cmd_events(__maybe_unused PGconn *conn, char *cmd, char *id, | 
			
		
	
		
		
			
				
					
					|  |  |  | 		 *	You need to BOTH use this AND remove the optioncontrol |  |  |  | 		 *	You need to BOTH use this AND remove the optioncontrol | 
			
		
	
		
		
			
				
					
					|  |  |  | 		 *	record from the database to permanently remove a ban |  |  |  | 		 *	record from the database to permanently remove a ban | 
			
		
	
		
		
			
				
					
					|  |  |  | 		 *	(since there's no cmd_expopts ... yet) */ |  |  |  | 		 *	(since there's no cmd_expopts ... yet) */ | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		bool found = false; |  |  |  | 		bool found = false; | 
			
		
	
		
		
			
				
					
					|  |  |  | 		i_ip = require_name(trf_root, "ip", 1, NULL, reply, siz); |  |  |  | 		i_ip = require_name(trf_root, "ip", 1, NULL, reply, siz); | 
			
		
	
		
		
			
				
					
					|  |  |  | 		if (!i_ip) |  |  |  | 		if (!i_ip) | 
			
		
	
	
		
		
			
				
					|  |  | @ -7825,8 +7824,142 @@ static char *cmd_events(__maybe_unused PGconn *conn, char *cmd, char *id, | 
			
		
	
		
		
			
				
					
					|  |  |  | 			APPEND_REALLOC(buf, off, len, " unbanned"); |  |  |  | 			APPEND_REALLOC(buf, off, len, " unbanned"); | 
			
		
	
		
		
			
				
					
					|  |  |  | 		} else { |  |  |  | 		} else { | 
			
		
	
		
		
			
				
					
					|  |  |  | 			APPEND_REALLOC(buf, off, len, |  |  |  | 			APPEND_REALLOC(buf, off, len, | 
			
		
	
		
		
			
				
					
					|  |  |  | 					"ERR.unknown ip+eventname"); |  |  |  | 					"ERR.unknown BAN ip+eventname"); | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		} | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 	} else if (strcasecmp(action, "ok") == 0) { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		/* OK the ip+eventname with optional lifetime
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		 * N.B. this doesn't survive a CKDB restart | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		 *	use just cmd_setopts for permanent OKs */ | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		bool found = false; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		oldlife = 0; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		i_ip = require_name(trf_root, "ip", 1, NULL, reply, siz); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		if (!i_ip) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 			return strdup(reply); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		ip = transfer_data(i_ip); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		i_eventname = require_name(trf_root, "eventname", 1, NULL, reply, siz); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		if (!i_eventname) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 			return strdup(reply); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		eventname = transfer_data(i_eventname); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		i_lifetime = optional_name(trf_root, "lifetime", 1, | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 					   (char *)intpatt, reply, siz); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		if (i_lifetime) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 			lifetime = atoi(transfer_data(i_lifetime)); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		else { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 			if (*reply) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 				return strdup(reply); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 			// Forever
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 			lifetime = 0; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		} | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		i_des = optional_name(trf_root, "des", 1, NULL, reply, siz); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		if (i_des) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 			des = transfer_data(i_des); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		else { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 			if (*reply) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 				return strdup(reply); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 			des = NULL; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		} | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		K_WLOCK(ips_free); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		i_item = find_ips(IPS_GROUP_OK, ip, eventname, NULL); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		if (i_item) { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 			DATA_IPS(ips, i_item); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 			found = true; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 			oldlife = ips->lifetime; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 			ips->lifetime = lifetime; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 			// Don't change it if it's not supplied
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 			if (des) { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 				LIST_MEM_SUB(ips_free, ips->description); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 				FREENULL(ips->description); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 				ips->description = strdup(des); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 				LIST_MEM_ADD(ips_free, ips->description); | 
			
		
	
		
		
			
				
					
					|  |  |  | 			} |  |  |  | 			} | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		} else { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 			ips_add(IPS_GROUP_OK, ip, eventname, | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 				is_elimitname(eventname, true), des, true, | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 				false, lifetime, true); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		} | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		K_WUNLOCK(ips_free); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		APPEND_REALLOC_INIT(buf, off, len); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		APPEND_REALLOC(buf, off, len, "ok."); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		if (found) { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 			snprintf(tmp, sizeof(tmp), "already %s/%s %d->%d", | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 				 ip, eventname, oldlife, lifetime); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		} else { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 			snprintf(tmp, sizeof(tmp), "ok %s/%s %d", | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 				 ip, eventname, lifetime); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		} | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		APPEND_REALLOC(buf, off, len, tmp); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 	} else if (strcasecmp(action, "unok") == 0) { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		/* UnOK the ip+eventname - sets lifetime to 1 meaning
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		 *  it expires 1 second after it was created | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		 *  so next access will remove the OK and succeed | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		 * N.B. if it was a permanent 'cmd_setopts' OK, the unOK | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		 *	won't survive a CKDB restart. | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		 *	You need to BOTH use this AND remove the optioncontrol | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		 *	record from the database to permanently remove an OK | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		 *	(since there's no cmd_expopts ... yet) */ | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		bool found = false; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		i_ip = require_name(trf_root, "ip", 1, NULL, reply, siz); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		if (!i_ip) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 			return strdup(reply); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		ip = transfer_data(i_ip); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		i_eventname = require_name(trf_root, "eventname", 1, NULL, reply, siz); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		if (!i_eventname) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 			return strdup(reply); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		eventname = transfer_data(i_eventname); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		K_WLOCK(ips_free); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		i_item = find_ips(IPS_GROUP_OK, ip, eventname, NULL); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		if (i_item) { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 			found = true; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 			DATA_IPS(ips, i_item); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 			ips->lifetime = 1; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		} | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		K_WUNLOCK(ips_free); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		APPEND_REALLOC_INIT(buf, off, len); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		if (found) { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 			APPEND_REALLOC(buf, off, len, "ok."); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 			APPEND_REALLOC(buf, off, len, ip); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 			APPEND_REALLOC(buf, off, len, "/"); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 			APPEND_REALLOC(buf, off, len, eventname); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 			APPEND_REALLOC(buf, off, len, " unOKed"); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		} else { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 			APPEND_REALLOC(buf, off, len, | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 					"ERR.unknown OK ip+eventname"); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		} | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 	} else if (strcasecmp(action, "expire") == 0) { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		/* Expire all ips that are too old
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		 *  and remove all non-current */ | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		bool expire; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		rows = 0; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		K_WLOCK(ips_free); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		i_item = first_in_ktree(ips_root, ctx); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		while (i_item) { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 			DATA_IPS(ips, i_item); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 			expire = false; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 			if (!CURRENT(&(ips->expirydate))) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 				expire = true; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 			else if (ips->lifetime != 0 && | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 				 (int)tvdiff(now, &(ips->createdate)) > ips->lifetime) { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 					expire = true; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 				} | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 			if (expire) { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 				rows++; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 				next_item = next_in_ktree(ctx); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 				remove_from_ktree(ips_root, i_item); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 				k_unlink_item(ips_store, i_item); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 				if (ips->description) { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 					LIST_MEM_SUB(ips_free, ips->description); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 					FREENULL(ips->description); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 				} | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 				k_add_head(ips_free, i_item); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 				i_item = next_item; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 				continue; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 			} | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 			i_item = next_in_ktree(ctx); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		} | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		K_WUNLOCK(ips_free); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		APPEND_REALLOC_INIT(buf, off, len); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		snprintf(tmp, sizeof(tmp), "ok.expired %d", rows); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		APPEND_REALLOC(buf, off, len, tmp); | 
			
		
	
		
		
			
				
					
					|  |  |  | 	} else { |  |  |  | 	} else { | 
			
		
	
		
		
			
				
					
					|  |  |  | 		snprintf(reply, siz, "unknown action '%s'", action); |  |  |  | 		snprintf(reply, siz, "unknown action '%s'", action); | 
			
		
	
		
		
			
				
					
					|  |  |  | 		LOGERR("%s() %s.%s", __func__, id, reply); |  |  |  | 		LOGERR("%s() %s.%s", __func__, id, reply); | 
			
		
	
	
		
		
			
				
					|  |  | 
 |