Index: cmd.c
===================================================================
--- cmd.c	(revision 8ef8c4edc180520c5363f4f9d95f21efe6cddba4)
+++ cmd.c	(revision 626f420e1ca14762a251234a70f0a3f62a48df69)
@@ -22,4 +22,5 @@
 #include <syslog.h>
 #include <unistd.h>
+#include <vis.h>
 #include <event.h>
 
@@ -79,4 +80,5 @@
 	struct icb_group *ig = is->group;
 	struct icb_session *s;
+	char whom[ICB_MAXNICKLEN];
 
 	if (strlen(arg) == 0) {
@@ -85,10 +87,12 @@
 	}
 
+	icb_vis(whom, arg, ICB_MAXNICKLEN);
+
 	LIST_FOREACH(s, &ig->sess, entry) {
-		if (strcmp(s->nick, arg) == 0)
+		if (strcmp(s->nick, whom) == 0)
 			break;
 	}
 	if (s == NULL) {
-		icb_status(is, STATUS_NOTIFY, "%s is not signed on", arg);
+		icb_status(is, STATUS_NOTIFY, "%s is not signed on", whom);
 		return;
 	}
@@ -110,4 +114,5 @@
 	struct icb_group *ig;
 	struct icb_session *s;
+	char whom[ICB_MAXNICKLEN];
 
 	/* to boot or not to boot, that is the question */
@@ -119,7 +124,14 @@
 	}
 
+	if (strlen(whom) == 0) {
+		icb_error(is, "Invalid user");
+		return;
+	}
+
+	icb_vis(whom, arg, ICB_MAXNICKLEN);
+
 	/* who would be a target then? */
 	LIST_FOREACH(s, &ig->sess, entry) {
-		if (strcmp(s->nick, arg) == 0)
+		if (strcmp(s->nick, whom) == 0)
 			break;
 	}
@@ -140,4 +152,5 @@
 	struct icb_group *ig;
 	struct icb_session *s;
+	char group[ICB_MAXGRPLEN];
 	int changing = 0;
 
@@ -147,6 +160,8 @@
 	}
 
+	icb_vis(group, arg, ICB_MAXGRPLEN);
+
 	LIST_FOREACH(ig, &groups, entry) {
-		if (strcmp(ig->name, arg) == 0)
+		if (strcmp(ig->name, group) == 0)
 			break;
 	}
@@ -156,10 +171,10 @@
 			return;
 		} else {
-			if ((ig = icb_addgroup(is, arg, NULL)) == NULL) {
+			if ((ig = icb_addgroup(is, group, NULL)) == NULL) {
 				icb_error(is, "Can't create group");
 				return;
 			}
 			icb_log(NULL, LOG_DEBUG, "%s created group %s",
-			    is->nick, arg);
+			    is->nick, group);
 		}
 	}
@@ -208,4 +223,5 @@
 	struct icb_group *ig = is->group;
 	struct icb_session *s;
+	char nick[ICB_MAXNICKLEN];
 
 	if (strlen(arg) == 0) {
@@ -221,6 +237,7 @@
 	if (strlen(arg) > ICB_MAXNICKLEN)
 		arg[ICB_MAXNICKLEN - 1] = '\0';
+	icb_vis(nick, arg, ICB_MAXNICKLEN);
 	LIST_FOREACH(s, &ig->sess, entry) {
-		if (strcmp(s->nick, arg) == 0) {
+		if (strcmp(s->nick, nick) == 0) {
 			icb_error(is, "Nick is already in use");
 			return;
@@ -228,6 +245,6 @@
 	}
 	icb_status_group(ig, NULL, STATUS_NAME,
-	    "%s changed nickname to %s", is->nick, arg);
-	strlcpy(is->nick, arg, sizeof is->nick);
+	    "%s changed nickname to %s", is->nick, nick);
+	strlcpy(is->nick, nick, sizeof is->nick);
 }
 
@@ -284,4 +301,5 @@
 	struct icb_group *ig = is->group;
 	struct icb_session *s;
+	char whom[ICB_MAXNICKLEN];
 
 	if (!ig->mod) {		/* if there is no mod, let anyone grab it */
@@ -294,6 +312,7 @@
 			return;
 		}
+		icb_vis(whom, arg, ICB_MAXNICKLEN);
 		LIST_FOREACH(s, &ig->sess, entry) {
-			if (strcmp(s->nick, arg) == 0)
+			if (strcmp(s->nick, whom) == 0)
 				break;
 		}
@@ -311,4 +330,5 @@
 {
 	struct icb_group *ig = is->group;
+	char topic[ICB_MAXTOPICLEN];
 
 	if (strlen(arg) == 0) {	/* querying the topic */
@@ -324,5 +344,6 @@
 			return;
 		}
-		strlcpy(ig->topic, arg, sizeof ig->topic);
+		icb_vis(topic, arg, ICB_MAXTOPICLEN);
+		strlcpy(ig->topic, topic, sizeof ig->topic);
 		icb_status_group(ig, NULL, STATUS_TOPIC,
 		    "%s changed the topic to \"%s\"", is->nick, ig->topic);
@@ -334,14 +355,16 @@
 {
 	struct icb_group *ig;
+	char group[ICB_MAXGRPLEN];
 
 	if (strlen(arg) == 0)
 		return icb_who(is, NULL);
 
+	icb_vis(group, arg, ICB_MAXGRPLEN);
 	LIST_FOREACH(ig, &groups, entry) {
-		if (strcmp(ig->name, arg) == 0)
+		if (strcmp(ig->name, group) == 0)
 			break;
 	}
 	if (ig == NULL) {
-		icb_error(is, "The group %s doesn't exist.", arg);
+		icb_error(is, "The group %s doesn't exist.", group);
 		return;
 	}
Index: dns.c
===================================================================
--- dns.c	(revision 8ef8c4edc180520c5363f4f9d95f21efe6cddba4)
+++ dns.c	(revision 626f420e1ca14762a251234a70f0a3f62a48df69)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2013 Mike Belopuhov
+ * Copyright (c) 2014 Mike Belopuhov
  * Copyright (c) 2009 Michael Shalayeff
  * All rights reserved.
@@ -39,5 +39,4 @@
 void dns_done(int, short, void *);
 int dns_pipe;
-
 
 struct icbd_dnsquery {
Index: icb.c
===================================================================
--- icb.c	(revision 8ef8c4edc180520c5363f4f9d95f21efe6cddba4)
+++ icb.c	(revision 626f420e1ca14762a251234a70f0a3f62a48df69)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2009, 2010, 2013 Mike Belopuhov
+ * Copyright (c) 2009, 2010, 2013, 2014 Mike Belopuhov
  *
  * Permission to use, copy, modify, and distribute this software for any
@@ -23,4 +23,5 @@
 #include <syslog.h>
 #include <unistd.h>
+#include <ctype.h>
 #include <event.h>
 
@@ -141,17 +142,21 @@
  */
 void
-icb_login(struct icb_session *is, char *group, char *nick, char *client)
+icb_login(struct icb_session *is, char *grp, char *nick, char *client)
 {
 	char *defgrp = "1";
 	struct icb_group *ig;
 	struct icb_session *s;
-
-	if (!nick || strlen(nick) == 0) {
+	char group[ICB_MAXGRPLEN];
+
+	if (!nick || strlen(nick) == 0 ||
+	    icb_vis(is->nick, nick, ICB_MAXNICKLEN)) {
 		icb_error(is, "Invalid nick");
 		icb_drop(is, NULL);
 		return;
 	}
-	if (!group || strlen(group) == 0)
-		group = defgrp;
+	if (!grp || strlen(grp) == 0)
+		strlcpy(group, defgrp, ICB_MAXGRPLEN);
+	else
+		icb_vis(group, grp, ICB_MAXNICKLEN);
 	LIST_FOREACH(ig, &groups, entry) {
 		if (strcmp(ig->name, group) == 0)
@@ -169,9 +174,9 @@
 			}
 			icb_log(NULL, LOG_DEBUG, "%s created group %s",
-			    nick, group);
+			    is->nick, group);
 		}
 	}
 	LIST_FOREACH(s, &ig->sess, entry) {
-		if (strcmp(s->nick, nick) == 0) {
+		if (strcmp(s->nick, is->nick) == 0) {
 			icb_error(is, "Nick is already in use");
 			icb_drop(is, NULL);
@@ -181,5 +186,5 @@
 
 	if (client && strlen(client) > 0)
-		strlcpy(is->client, client, sizeof is->client);
+		icb_vis(is->client, client, sizeof is->client);
 	strlcpy(is->nick, nick, sizeof is->nick);
 	is->group = ig;
@@ -240,8 +245,11 @@
  */
 void
-icb_privmsg(struct icb_session *is, char *whom, char *msg)
+icb_privmsg(struct icb_session *is, char *to, char *msg)
 {
 	struct icb_group *ig = is->group;
 	struct icb_session *s;
+	char whom[ICB_MAXNICKLEN];
+
+	icb_vis(whom, to, ICB_MAXNICKLEN);
 
 	LIST_FOREACH(s, &ig->sess, entry) {
@@ -263,7 +271,10 @@
 {
 	void (*handler)(struct icb_session *, char *);
-
-	if ((handler = icb_cmd_lookup(cmd)) == NULL) {
-		icb_error(is, "Unsupported command: %s", cmd);
+	char command[32]; /* XXX */
+
+	icb_vis(command, cmd, sizeof command);
+
+	if ((handler = icb_cmd_lookup(command)) == NULL) {
+		icb_error(is, "Unsupported command: %s", command);
 		return;
 	}
@@ -510,10 +521,8 @@
  *  icb_ismod: checks whether group is moderated by "is"
  */
-int
+inline int
 icb_ismod(struct icb_group *ig, struct icb_session *is)
 {
-	if (ig->mod && ig->mod == is)
-		return (1);
-	return (0);
+	return (ig->mod == is);
 }
 
@@ -600,2 +609,26 @@
 	icb_send(is, buf, buflen + 1);
 }
+
+/*
+ *  icb_vis: strnvis-like function that escapes percentages as well
+ */
+int
+icb_vis(char *dst, const char *src, size_t dstsize)
+{
+	int si = 0, di = 0, td;
+
+	while ((size_t)di < dstsize && src[si] != '\0') {
+		if (src[si] == '%')
+			dst[di++] = '%', dst[di] = '%';
+		else if (isgraph(src[si]))
+			dst[di] = src[si];
+		else {
+			td = snprintf(&dst[di], dstsize - di,
+			    "\\%03o", (unsigned char)src[si]);
+			di += td - 1;
+		}
+		si++, di++;
+	}
+	dst[MIN((size_t)di, dstsize)] = '\0';
+	return (0);
+}
Index: icb.h
===================================================================
--- icb.h	(revision 8ef8c4edc180520c5363f4f9d95f21efe6cddba4)
+++ icb.h	(revision 626f420e1ca14762a251234a70f0a3f62a48df69)
@@ -122,23 +122,25 @@
 /* icb.c */
 struct icb_group *icb_addgroup(struct icb_session *, char *, char *);
-void icb_cmdout(struct icb_session *, int, char *);
-void icb_delgroup(struct icb_group *);
-void icb_error(struct icb_session *, const char *, ...);
-void icb_init(struct icbd_callbacks *);
-void icb_input(struct icb_session *);
-int  icb_ismod(struct icb_group *, struct icb_session *);
-int  icb_modpermit(struct icb_session *);
-int  icb_pass(struct icb_group *, struct icb_session *, struct icb_session *);
-void icb_privmsg(struct icb_session *, char *, char *);
-void icb_remove(struct icb_session *, char *);
-void icb_sendfmt(struct icb_session *, const char *, ...);
-void icb_start(struct icb_session *);
-void icb_status(struct icb_session *, int, const char *, ...);
-void icb_status_group(struct icb_group *, struct icb_session *, int ,
-         const char *, ...);
-void icb_who(struct icb_session *, struct icb_group *);
+void		 icb_cmdout(struct icb_session *, int, char *);
+void		 icb_delgroup(struct icb_group *);
+void		 icb_error(struct icb_session *, const char *, ...);
+void		 icb_init(struct icbd_callbacks *);
+void		 icb_input(struct icb_session *);
+inline int	 icb_ismod(struct icb_group *, struct icb_session *);
+int		 icb_modpermit(struct icb_session *);
+int		 icb_pass(struct icb_group *, struct icb_session *,
+		     struct icb_session *);
+void		 icb_privmsg(struct icb_session *, char *, char *);
+void		 icb_remove(struct icb_session *, char *);
+void		 icb_sendfmt(struct icb_session *, const char *, ...);
+void		 icb_start(struct icb_session *);
+void		 icb_status(struct icb_session *, int, const char *, ...);
+void		 icb_status_group(struct icb_group *, struct icb_session *,
+		    int, const char *, ...);
+void		 icb_who(struct icb_session *, struct icb_group *);
+int		 icb_vis(char *, const char *, size_t);
 
 /* callbacks from icbd.c */
-void (*icb_drop)(struct icb_session *, char *);
-void (*icb_log)(struct icb_session *, int, const char *, ...);
-void (*icb_send)(struct icb_session *, char *, ssize_t);
+void		(*icb_drop)(struct icb_session *, char *);
+void		(*icb_log)(struct icb_session *, int, const char *, ...);
+void		(*icb_send)(struct icb_session *, char *, ssize_t);
