commit 5d3567b161dcd0b1bfa1cb5ad062cafdbd85a69f
parent b2e2266f329a895a12faeb7857ec09ca9fc03867
Author: Christoph Lohmann <20h@r-36.net>
Date:   Tue, 20 Mar 2018 06:20:34 +0100
Add Linux version.
Diffstat:
4 files changed, 898 insertions(+), 0 deletions(-)
diff --git a/linux/Makefile b/linux/Makefile
@@ -0,0 +1,46 @@
+PROGRAM = ircc
+VERSION = _Lin
+
+PREFIX = /usr
+
+CC = cc 
+
+CFLAGS = -O2 -Wall -I. -I/usr/include 
+LDFLAGS = -g -L/usr/lib -L. -lc -lpthread -lssl
+
+CFILES = ircc.c 
+
+OBJECTS = ${CFILES:.c=.o}
+
+all:	$(PROGRAM)
+
+${PROGRAM} : ${OBJECTS}
+	${CC} ${LDFLAGS} -o ${PROGRAM} ${OBJECTS}
+
+.SUFFIXES : .c .H
+
+.c.o :
+	${CC} ${CFLAGS} -c $<
+.c :
+	${CC} ${CFLAGS} -c $<
+
+
+clean :
+	@rm -f *.o ${PROGRAM} core *~
+
+install: $(PROGRAM)
+	@mkdir -p ${PREFIX}/bin
+	@cp -f ${PROGRAM} ${PREFIX}/bin
+	@chmod 755 ${PREFIX}/bin/${PROGRAM}
+
+uninstall:
+	@rm -f ${PREFIX}/bin/$(PROGRAM)
+
+dist:
+	@mkdir -p "${PROGRAM}${VERSION}"
+	@ln README Makefile *.c *.h "${PROGRAM}${VERSION}"
+	@tar -cf "${PROGRAM}${VERSION}.tar" "${PROGRAM}${VERSION}"
+	@gzip "${PROGRAM}${VERSION}.tar"
+	@mv "${PROGRAM}${VERSION}.tar.gz" "${PROGRAM}${VERSION}.tgz"
+	@rm -rf "${PROGRAM}${VERSION}"
+
diff --git a/linux/README b/linux/README
@@ -0,0 +1,17 @@
+
+Usage:
+
+	-d		debugging on
+	-i		ignore motd
+	-t		use SSL over the line
+	-a anick	alternative nickname, if nickname is used
+	-c clientinfo	string to be sent on a CTCP VERSION
+	-j #channel	channel that should be joined on startup (can be used
+			multiple times)
+	-k passwd	password for the nickserv authentication
+	-n nick		nickname to be used
+	-o port		port for the server (default: 6667)
+	-p pass		password for the server authentication	
+	-u user		username
+	server		server that should be connected to
+
diff --git a/linux/arg.h b/linux/arg.h
@@ -0,0 +1,19 @@
+#ifndef ARG_H
+#define ARG_H
+
+#define USED(x) ((void)(x))	
+
+extern char *argv0;
+
+#define	ARGBEGIN	for(argv0 = *argv, argv++, argc--;\
+			    argv[0] && argv[0][0]=='-' && argv[0][1];\
+			    argc--, argv++) {\
+				char _argc;\
+				_argc = argv[0][1];\
+				switch(_argc)
+#define	ARGEND		USED(_argc);} USED(argv);USED(argc);
+#define	EARGF(x)	((argv[1] == nil)? ((x), abort(), (char *)0) :\
+			(argc--, argv++, argv[0]))
+
+#endif
+
diff --git a/linux/ircc.c b/linux/ircc.c
@@ -0,0 +1,816 @@
+/*
+ *  Copy me if you can.
+ *  by 20h
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <time.h>
+#include <pthread.h>
+#include <netdb.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <openssl/bio.h>
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include "arg.h"
+
+#define VERSION "ircc 3rd ed. Linux 1st ed."
+#define nil NULL
+
+int tls, debug, ignoreflood, anickused, havenickserv, doautojoin;
+char *argv0, *lastchan, *clientinfo, *nick, *anick, *passwd;
+
+typedef struct command command;
+struct command {
+	char **params;
+	int len;
+	int isalloc;
+};
+
+command *ignorel, *joinl;
+
+void *
+realloci(void *p, int i, int d)
+{
+	
+	p = realloc(p, i);
+	if(p == nil) {
+		perror("realloc");
+		exit(1);
+	}
+
+	if(d != 0)
+		memset(p, 0, i);
+
+	return (void *)p;
+}
+
+int
+swrite(void *sock, char *buf, int l)
+{
+	int ret;
+
+	if(!tls)
+		ret = write(*(int *)sock, buf, l);
+	else {
+		ret = BIO_write((BIO *)sock, buf, l);
+		(void)BIO_flush((BIO *)sock);
+	}
+
+	return ret;
+}
+
+int
+sread(void *sock, char *buf, int l)
+{
+	int ret;
+	
+	if(!tls)
+		ret = read(*(int *)sock, buf, l);
+	else {
+		ret = BIO_read((BIO *)sock, buf, l);
+		(void)BIO_flush((BIO *)sock);
+	}
+
+	return ret;
+}
+
+void
+tprintf(void *sock, char *fmt, ...)
+{
+	va_list fmtargs;
+	char buf[8192];
+
+	va_start(fmtargs, fmt);
+	vsnprintf(buf, sizeof(buf) - 1, fmt, fmtargs);
+	va_end(fmtargs);
+
+	if(swrite(sock, buf, strlen(buf)) < 0)
+		perror("write");
+
+	return;
+}
+
+int
+connecttcp(char *host, char *service)
+{
+	int sock;
+	struct addrinfo *ai, *a;
+
+	sock = -1;
+
+	if(getaddrinfo(host, service, nil, &ai) < 0) {
+		perror("getaddrinfo");
+		return -1;
+	}
+
+	for(a = ai; a; a = a->ai_next) {
+		sock = socket(a->ai_family, a->ai_socktype, a->ai_protocol);
+		if(sock < 0) {
+			perror("socket");
+			sock = -1;
+			break;
+		}
+
+		if(connect(sock, a->ai_addr, a->ai_addrlen) < 0) {	
+			perror("connect");
+			sock = -1;
+			break;
+		} else
+			break;
+	}
+
+	freeaddrinfo(ai);
+
+	return sock;
+} 
+
+void
+freecmd(command *cmd)
+{
+	int i;
+
+	i = -1;
+	if(cmd != nil) {
+		if(cmd->isalloc)
+			while(++i < cmd->len)
+				if(cmd->params[i] != nil)
+					free(cmd->params[i]);
+		if(cmd->params != nil)
+			free(cmd->params);
+		free(cmd);
+	}
+
+	return;
+}
+
+int
+addlist(command *c, char *p)
+{
+	int i;
+
+	i = -1;
+	if(c != nil) {
+		if(c->isalloc) {
+			p = strdup(p);
+			while(++i < c->len) {
+				if(c->params[i] == nil) {
+					c->params[i] = p;
+
+					return 2;
+				}
+			}
+		}
+
+		c->len++;
+		c->params = realloc(c->params, sizeof(char *) * c->len);
+		c->params[c->len - 1] = p;
+
+		return 1;
+	}
+
+	return 0;
+}
+
+int
+searchlist(command *c, char *p)
+{
+	int i;
+
+	i = -1;
+	if(c != nil)
+		while(++i < c->len)
+			if(c->params[i] != nil)
+				if(!strcasecmp(c->params[i], p))
+					return i;
+
+	return -1;
+}
+
+int
+dellist(command *c, char *p)
+{
+	int i;
+	
+	if(c != nil) {
+		i = searchlist(c, p);
+		if(i != -1) {
+			free(c->params[i]);
+			c->params[i] = nil;
+			c->len--;
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+command *
+parsecmd(command *cmd, char *data, int cmds)
+{
+	command *ret;
+	char *next, *last, *temp;
+
+	temp = nil;
+
+	if(cmd == nil) {
+		ret = realloci(nil, sizeof(command), 2);
+		last = data;
+	} else {
+		ret = cmd;
+		if(ret->isalloc) {
+			temp = strdup(ret->params[ret->len - 1]);
+			dellist(ret, ret->params[ret->len - 1]);
+		} else {
+			ret->len--;
+			temp = ret->params[ret->len];
+		}
+		last = temp;
+	}
+	while((next = strchr(last, ' ')) != nil && ret->len < cmds) {
+		*next = '\0';
+		next++;
+		if(last[0] == ':')
+			last++;
+		addlist(ret, last);
+		last = next;
+	}
+	if(last[0] == ':')
+		last++;
+	if(last != nil)
+		addlist(ret, last);
+
+	if(temp != nil && cmd->isalloc)
+		free(temp);
+
+	return ret;
+}
+
+char *
+mktimestamp(char bord, char bord_e)
+{
+	time_t tim;
+	struct tm tm;
+	char *ret;
+
+	time(&tim);
+	localtime_r(&tim, &tm);
+
+	ret = malloc(31);
+	snprintf(ret, 30, "%c%.2d:%.2d%c", bord, tm.tm_hour, tm.tm_min, bord_e);
+
+	return ret;
+}
+
+void
+setchan(char *new)
+{
+
+	if(lastchan != nil)
+		free(lastchan);
+
+	lastchan = realloci(nil, strlen(new) + 1, 2);
+	strcpy(lastchan, new);
+}
+
+void
+autojoin(void *sock)
+{
+	int i;
+
+	for(i = 0; i < joinl->len; i++)
+		tprintf(sock, "JOIN %s\r\n", joinl->params[i]);
+	setchan(joinl->params[joinl->len - 1]);
+}
+
+int
+gotcommand(void *sock, char *data, int len)
+{
+	command *cmd;
+	char *last, *next, *send, *cpy, *test, *tmstmp;
+	int clen;
+	time_t tim;
+
+	clen = len;
+	last = data;
+	send = realloci(nil, 513, 2);
+
+	while((next = strchr(last, '\n')) !=  nil) {
+		*next++ = '\0';
+		clen -= (next - last);
+		if(*(next - 2) == '\r')
+			*(next - 2) = '\0';
+
+		cpy = strdup(last);
+		cmd = parsecmd(nil, last, 3);
+		tmstmp = mktimestamp('(', ')');
+
+		if(cmd->len > 1) {
+			if(!strncasecmp(cmd->params[0], "PING", 4)) {
+				tprintf(sock, "PONG %s\r\n", cmd->params[1]);
+				goto c_end;
+			}
+			if(!strncasecmp(cmd->params[0], "ERROR", 5)) {
+				printf("%sERROR%% %s %s %s\n", tmstmp, cmd->params[1], cmd->params[2], cmd->params[3]);
+				goto c_end;
+			}
+		}
+		if(cmd->len > 2) {
+			if(!strncasecmp(cmd->params[1], "JOIN", 4)) {
+				printf("%s(%s) has joined %s\n", tmstmp, cmd->params[0], cmd->params[2]);
+				goto c_end;
+			}
+			if(!strncasecmp(cmd->params[1], "PART", 4)) {
+				printf("%s(%s) has parted %s\n", tmstmp, cmd->params[0], cmd->params[2]);
+				goto c_end;
+			}
+			if(!strncasecmp(cmd->params[1], "MODE", 4)) {
+				printf("%s(%s)-m-(%s)%% %s\n", tmstmp, cmd->params[0], cmd->params[2], cmd->params[3]);
+				goto c_end;
+			}
+			if(!strncasecmp(cmd->params[1], "QUIT", 4)) {
+				printf("%s(%s) has quit (%s %s)\n", tmstmp, cmd->params[0], cmd->params[2], (cmd->len > 3) ? cmd->params[3] : "");
+				goto c_end;
+			}
+			if((test = strchr(cmd->params[0], '!')) != nil)
+				*test = '\0';
+			if(!strncasecmp(cmd->params[1], "NICK", 4)) {
+				printf("%s(%s) changed nick to %s\n", tmstmp, cmd->params[0], cmd->params[2]);
+				if(searchlist(ignorel, cmd->params[0]) != -1) {
+					dellist(ignorel, cmd->params[0]);
+					addlist(ignorel, cmd->params[2]);
+				}
+				goto c_end;
+			}
+		}
+		if(cmd->len > 3) {
+			if(strlen(cmd->params[1]) == 3) {
+				if(ignoreflood) {
+					if(!strncasecmp(cmd->params[1], "001", 3) ||
+							!strncasecmp(cmd->params[1], "002", 3) ||
+							!strncasecmp(cmd->params[1], "003", 3) ||
+							!strncasecmp(cmd->params[1], "004", 3) ||
+							!strncasecmp(cmd->params[1], "005", 3) ||
+							!strncasecmp(cmd->params[1], "250", 3) ||
+							!strncasecmp(cmd->params[1], "251", 3) ||
+							!strncasecmp(cmd->params[1], "252", 3) ||
+							!strncasecmp(cmd->params[1], "253", 3) ||
+							!strncasecmp(cmd->params[1], "254", 3) ||
+							!strncasecmp(cmd->params[1], "255", 3) ||
+							!strncasecmp(cmd->params[1], "265", 3) ||
+							!strncasecmp(cmd->params[1], "266", 3) ||
+							!strncasecmp(cmd->params[1], "317", 3) ||
+							!strncasecmp(cmd->params[1], "318", 3) ||
+							!strncasecmp(cmd->params[1], "366", 3) ||
+							!strncasecmp(cmd->params[1], "375", 3) ||
+							!strncasecmp(cmd->params[1], "372", 3))
+						goto c_end;
+				}
+				if(!strncasecmp(cmd->params[1], "376", 3)) {
+					if(doautojoin && (!havenickserv || anickused))
+						autojoin(sock);
+					goto c_end;
+				}
+					
+				printf("%s(%s)(%s)%% %s\n", tmstmp, cmd->params[0], cmd->params[1], cmd->params[3]);
+				if(!strncasecmp(cmd->params[1], "433", 3) && anick != nil) {
+					if(anickused) {
+						printf("%s(IRCC)%% Sorry, but nick and anick are used. Try something different.\n", tmstmp);
+						goto c_end;
+					}
+					tprintf(sock, "NICK %s\r\n", anick);
+					anickused = 1;
+				}
+				goto c_end;
+			}
+			if(!strncasecmp(cmd->params[1], "INVITE", 6)) {
+				printf("%s(%s) invited you to %s\n", tmstmp, cmd->params[0], cmd->params[3]);
+				goto c_end;
+			}
+			if(!strncasecmp(cmd->params[1], "TOPIC", 5)) {
+				printf("%s(%s) topicchange in %s to \"%s\"\n", tmstmp, cmd->params[0], cmd->params[2], cmd->params[3]);
+				goto c_end;
+			}
+			if(!strncasecmp(cmd->params[1], "KICK", 4)) {
+				if(parsecmd(cmd, nil, 4) != nil)
+					printf("%s(%s) kicked %s out of %s \"%s\"\n", tmstmp, cmd->params[0], cmd->params[3], cmd->params[2], cmd->params[4]);
+				else
+					printf("%s(%s) kicked %s out of %s\n", tmstmp, cmd->params[0], cmd->params[3], cmd->params[2]);
+				goto c_end;
+			}
+			if(searchlist(ignorel, cmd->params[0]) == -1 &&
+				searchlist(ignorel, cmd->params[2]) == -1) {
+				if(!strncasecmp(cmd->params[1], "NOTICE", 6)) {
+					printf("%s(%s|%s)%% %s\n", tmstmp, cmd->params[0], cmd->params[2], cmd->params[3]);
+					if(!strncasecmp(cmd->params[0], "NickServ", 8) && havenickserv) {
+						printf("in case NickServ\n");
+						tprintf(sock, "PRIVMSG NickServ :IDENTIFY %s\r\n", passwd);
+						memset(passwd, 0, strlen(passwd));
+						havenickserv = 0;
+						if(doautojoin)
+							autojoin(sock);
+					}
+					goto c_end;
+				}
+				if(!strncasecmp(cmd->params[1], "PRIVMSG", 7)) {
+					if(*(cmd->params[3]) == '\x01') {
+						if(!strncasecmp(cmd->params[3]+1, "VERSION", 7))
+							tprintf(sock, "NOTICE %s :\x01VERSION " VERSION "\x01\r\n", cmd->params[0]);
+						if(!strncasecmp(cmd->params[3]+1, "TIME", 4)) {
+							tim = time(0);
+							test = ctime(&tim);
+							test[strlen(test) - 1] = '\0';
+							tprintf(sock, "NOTICE %s :\x01TIME %s\x01\r\n", cmd->params[0], test);
+						}
+						if(!strncasecmp(cmd->params[3]+1, "PING", 4))
+							tprintf(sock, "NOTICE %s :\x01PONG %s\r\n", cmd->params[0], cmd->params[3]+6);
+						if(!strncasecmp(cmd->params[3]+1, "ACTION", 6)) {
+							*(cmd->params[3] + strlen(cmd->params[3])-1) = '\0';
+							printf("%s(%s+%s) %s\n", tmstmp, cmd->params[0], cmd->params[2], cmd->params[3]+8);
+							goto c_end;
+						}
+						if(!strncasecmp(cmd->params[3]+1, "CLIENTINFO", 10))
+							tprintf(sock, "NOTICE %s :\x01CLIENTINFO PING VERSION TIME USERINFO CLIENTINFO\x01\r\n", cmd->params[0]);
+						if(!strncasecmp(cmd->params[3]+1, "USERINFO", 8))
+							tprintf(sock, "NOTICE %s :\x01USERINFO %s\x01\r\n", cmd->params[0], (clientinfo != nil) ? clientinfo : "<nil>");
+					}
+					printf("%s(%s-%s)%% %s\n", tmstmp, cmd->params[0], cmd->params[2], cmd->params[3]);
+					goto c_end;
+				}
+				if(!strncasecmp(cmd->params[0], "NOTICE", 6)) {
+					printf("%sNOTICE%% %s\n", tmstmp, cmd->params[3]);
+					goto c_end;
+				}
+			} else
+				goto c_end;
+		}
+		printf("%s\n",cpy);
+c_end:
+		free(tmstmp);
+		free(cpy);
+		freecmd(cmd);
+		last = next;
+	}
+
+	if(clen > 0)
+		strcpy(data, data + len - clen);
+	if(send != nil)
+		free(send);
+
+	return clen;
+}
+
+void *
+recvproc(void *sock)
+{
+	char *recv;
+	int len, overl;
+
+	recv = realloci(nil, 1025, 2);
+	len = 1;
+	overl = 0;
+
+	while(len > 0 && sock != nil) {
+		len = sread(sock, recv + overl, 1024 - overl);
+
+		if(debug)
+			printf("%s\n", recv);
+		
+		if(len > 1)
+			overl = gotcommand(sock, recv, len + overl);
+	}
+
+	free(recv);
+
+	return nil;
+}
+
+void
+usage(void)
+{
+
+	fprintf(stdout, "usage: %s [-dit] [-a anick] [-c clientinfo]"
+			" [-j #channel ...]"
+			" [-k passwd]"
+			" [-n nick] [-o port] [-p pass] [-u user]"
+			" server\n",
+			argv0);
+
+	exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+	void *sock;
+	int i, so;
+	char *send, *rad, *tmstmp, *user, *pass, *port;
+	command *cmd;
+	pthread_t th;
+	BIO *bio;
+	SSL_CTX *ctx;
+	SSL *ssl;
+
+	ctx = nil;
+	user = "root";
+	pass = nil;
+	nick = "root";
+	anick = nil;
+	port = nil;
+	tls = 0;
+	clientinfo = "The user has not specified his details.";
+	joinl = realloci(nil, sizeof(command), 2);
+	joinl->isalloc = 1;
+
+	ARGBEGIN {
+	case 'u':
+		user = EARGF(usage());
+		break;
+	case 'p':
+		pass = EARGF(usage());
+		break;
+	case 't':
+		tls = 1;
+		break;
+	case 'd':
+		debug = 1;
+		break;
+	case 'c':
+		clientinfo = EARGF(usage());
+		break;
+	case 'i':
+		ignoreflood = 1;
+		break;
+	case 'n':
+		nick = EARGF(usage());
+		break;
+	case 'o':
+		port = EARGF(usage());
+		break;
+	case 'a':
+		anick = EARGF(usage());
+		break;
+	case 'k':
+		havenickserv = 1;
+		passwd = EARGF(usage());
+		break;
+	case 'j':
+		addlist(joinl, EARGF(usage()));
+		doautojoin = 1;
+		break;
+	default:
+		usage();
+	} ARGEND;
+			
+			
+	if(argc < 1)
+		usage();
+
+	if(!tls) {	
+		so = connecttcp(argv[0], (port == nil)? "6667" : port);
+		if(so < 0) {
+			perror("connecttcp");
+			exit(1);
+		}
+
+		sock = &so;
+	} else {
+		ERR_load_crypto_strings();
+		SSL_library_init();
+
+		ctx = SSL_CTX_new(SSLv23_client_method());
+		if(ctx == nil) {
+			perror("SSL_CTX_new");
+			ERR_print_errors_fp(stderr);
+			exit(1);
+		}
+		bio = BIO_new_ssl_connect(ctx);
+		if(bio == nil) {
+			perror("BIO_new_ssl_connect");
+			ERR_print_errors_fp(stderr);
+			exit(1);
+		}
+
+		BIO_get_ssl(bio, &ssl);
+		SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);	
+
+		BIO_set_conn_port(bio, (port == nil)? "994" : port);
+		BIO_set_conn_hostname(bio, argv[0]); 
+		if(BIO_do_connect(bio) <= 0) {
+			perror("BIO_do_connect");
+			ERR_print_errors_fp(stderr);
+			exit(1);
+		}
+
+		sock = bio;
+	}
+
+	if(user == nil)
+		user = nick;
+
+	if(pass != nil)
+		tprintf(sock, "PASS %s\r\n", pass);
+	tprintf(sock, "NICK %s\r\n", nick);
+	tprintf(sock, "USER %s localhost %s :%s\r\n", user, nick, user);
+
+	if(pthread_create(&th, nil, recvproc, sock) < 0) {
+		perror("pthread_create");
+		exit(1);
+	}
+
+	ignorel = realloci(nil, sizeof(command), 2);
+	ignorel->isalloc = 1;
+	send = realloci(nil, 513, 2);
+	rad = realloci(nil, 513, 2);
+
+	while(sock > 0) {
+		i = -1;
+		cmd = nil;
+		memset(send, 0, 513);
+		memset(rad, 0, 513);
+
+		while(read(0, &rad[++i], 1) && i < 400 && sock != nil) {
+			if(rad[i] == '\n') {
+				rad[i] = '\0';
+				break;
+			}
+		}
+		rad[i + 1] = '\0';
+
+		tmstmp = mktimestamp('(', ')');
+		if(rad[0] == '/')
+			cmd = parsecmd(nil, rad, 2);
+
+		if(cmd != nil) {
+			if(cmd->len > 0) {
+				switch(cmd->params[0][1]) {
+				case 'H':
+				case 'h':
+					printf("%s Help for ircc:\n", tmstmp);
+					printf("%s   /a chan text - send /me text to chan\n", tmstmp);
+					printf("%s   /c chan what - send CTCP what to chan\n", tmstmp);
+					printf("%s   /d - turn debugging on or off\n", tmstmp);
+					printf("%s   /h - show this help\n", tmstmp);
+					printf("%s   /i chan - ignore/unignore chan\n", tmstmp);
+					printf("%s   /j chan - join chan\n", tmstmp);
+					printf("%s   /k user [readon] - kick a user\n", tmstmp);
+					printf("%s   /l [chan] - list channels on server\n", tmstmp);
+					printf("%s   /m chan text - send text to chan\n", tmstmp);
+					printf("%s   /n nick - change nickname to nick\n", tmstmp);
+					printf("%s   /o chan mode [user] - set mode on chan\n", tmstmp);
+					printf("%s   /p chan - part chan\n", tmstmp);
+					printf("%s   /q [msg] - quit ircc\n", tmstmp);
+					printf("%s   /s chan - set active chan\n", tmstmp);
+					printf("%s   /t chan [topic] - set/show topic of chan\n", tmstmp);
+					printf("%s   /u chan - WHO of chan\n", tmstmp);
+					printf("%s   /w chan - WHOIS of chan\n", tmstmp);
+					goto end_e;
+				case 'D':
+				case 'd':
+					if(debug)
+						debug = 0;
+					else
+						debug = 1;
+					printf("%s debug %d\n", tmstmp, debug);
+					goto end_e;
+				case 'Q':
+				case 'q':
+					sprintf(send, "QUIT :\r\n");
+					if(cmd->len == 2)
+						sprintf(send + 6, "%s\r\n", cmd->params[1]);
+					if(cmd->len > 2)
+						sprintf(send + 6, "%s %s\r\n", cmd->params[1], cmd->params[2]);
+					swrite(sock, send, strlen(send));
+					if(tls)
+						BIO_free_all(sock);
+					else
+						close(*(int *)sock);
+					sock = nil;
+					goto end_e;
+				case 'L':
+				case 'l':
+					sprintf(send, "LIST\r\n");
+					if(cmd->len == 2)
+						sprintf(send + 4, " %s\r\n", cmd->params[1]);
+					if(cmd->len > 2)
+						sprintf(send + 4, " %s %s\r\n", cmd->params[1], cmd->params[2]);
+					swrite(sock, send, strlen(send));
+					goto end_e;
+				case 'S':
+				case 's':
+					if(cmd->len > 1)
+						setchan(cmd->params[1]);
+					else
+						printf("%s\n", (lastchan == nil) ? "<nil>": lastchan);
+					goto end_e;
+				default:
+					break;
+				}
+			}
+			if(cmd->len > 1) {
+				switch(cmd->params[0][1]) {
+				case 'J':
+				case 'j':
+					tprintf(sock, "JOIN %s\r\n", cmd->params[1]);
+					setchan(cmd->params[1]);
+					goto end_e;
+				case 'P':
+				case 'p':
+					tprintf(sock, "PART %s\r\n", cmd->params[1]);
+					goto end_e;
+				case 'N':
+				case 'n':
+					tprintf(sock, "NICK %s\r\n", cmd->params[1]);
+					goto end_e;
+				case 'W':
+				case 'w':
+					tprintf(sock, "WHOIS %s\r\n", cmd->params[1]);
+					goto end_e;
+				case 'U':
+				case 'u':
+					tprintf(sock, "WHO %s\r\n", cmd->params[1]);
+					goto end_e;
+				case 'T':
+				case 't':
+					sprintf(send, "TOPIC %s\r\n", cmd->params[1]);
+					if(cmd->len > 2)
+						sprintf(send + strlen(send) - 2, " :%s\r\n", cmd->params[2]);
+					tprintf(sock, send);
+					goto end_e;
+				case 'I':
+				case 'i':
+					if(searchlist(ignorel, cmd->params[1]) != -1) {
+						dellist(ignorel, cmd->params[1]);
+						printf("%s no more ignored. %d\n", cmd->params[1], searchlist(ignorel, cmd->params[1]));
+					} else {
+						addlist(ignorel, cmd->params[1]);
+						printf("%s will be ignored.\n", cmd->params[1]);
+					}
+					goto end_e;
+				default:
+					break;
+				}
+			}
+			if(cmd->len > 2) {
+				switch(cmd->params[0][1]) {
+				case 'M':
+				case 'm':
+					tprintf(sock, "PRIVMSG %s :%s\r\n", cmd->params[1], cmd->params[2]);
+					goto end_e;
+				case 'A':
+				case 'a':
+					tprintf(sock, "PRIVMSG %s :%sACTION %s\x01\r\n", "\x01", cmd->params[1], cmd->params[2]);
+					goto end_e;
+				case 'O':
+				case 'o':
+					if(cmd->len > 3)
+						tprintf(sock, "MODE %s %s %s\r\n", cmd->params[1], cmd->params[2], cmd->params[3]);
+					else
+						tprintf(sock, "MODE %s %s\r\n", cmd->params[1], cmd->params[2]);
+					goto end_e;
+				case 'C':
+				case 'c':
+					printf("%s(%s) ctcp %s\n", tmstmp, cmd->params[1], cmd->params[2]);
+					tprintf(sock, "PRIVMSG %s :\x01%s\x01\r\n", cmd->params[1], cmd->params[2]);
+					goto end_e;
+				case 'K':
+				case 'k':
+					sprintf(send, "KICK %s %s\r\n", cmd->params[1], cmd->params[2]);
+					if(cmd->len > 3)
+						sprintf(send + strlen(send) - 2, " :%s\r\n", cmd->params[3]);
+					tprintf(sock, send);
+					goto end_e;
+				default:
+					break;
+				}
+			}
+		}
+		if(lastchan != nil)
+			tprintf(sock, "PRIVMSG %s :%s\r\n", lastchan, rad);
+		else
+			printf(".");
+end_e:
+		printf("%s\n", tmstmp);
+		free(tmstmp);
+		freecmd(cmd);
+	}
+
+	if(lastchan != nil)
+		free(lastchan);
+	free(send);
+	free(rad);
+	freecmd(ignorel);
+	freecmd(joinl);
+	if(tls)
+		SSL_CTX_free(ctx);
+
+	return 0;
+}
+