ircc

Simple IRC client
git clone git://r-36.net/ircc
Log | Files | Refs | LICENSE

ircc.c (18168B)


      1 /*
      2  *  Copy me if you can.
      3  *  by 20h
      4  */
      5 
      6 #include <stdlib.h>
      7 #include <string.h>
      8 #include <stdio.h>
      9 #include <stdarg.h>
     10 #include <unistd.h>
     11 #include <fcntl.h>
     12 #include <time.h>
     13 #include <pthread.h>
     14 #include <netdb.h>
     15 #include <sys/types.h>
     16 #include <sys/socket.h>
     17 #include <netinet/in.h>
     18 #include <arpa/inet.h>
     19 #include <openssl/bio.h>
     20 #include <openssl/ssl.h>
     21 #include <openssl/err.h>
     22 #include "arg.h"
     23 
     24 #define VERSION "ircc 3rd ed. Linux 1st ed."
     25 #define nil NULL
     26 
     27 int tls, debug, ignoreflood, anickused, havenickserv, doautojoin;
     28 char *argv0, *lastchan, *clientinfo, *nick, *anick, *passwd;
     29 
     30 typedef struct command command;
     31 struct command {
     32 	char **params;
     33 	int len;
     34 	int isalloc;
     35 };
     36 
     37 command *ignorel, *joinl;
     38 
     39 void *
     40 realloci(void *p, int i, int d)
     41 {
     42 	
     43 	p = realloc(p, i);
     44 	if(p == nil) {
     45 		perror("realloc");
     46 		exit(1);
     47 	}
     48 
     49 	if(d != 0)
     50 		memset(p, 0, i);
     51 
     52 	return (void *)p;
     53 }
     54 
     55 int
     56 swrite(void *sock, char *buf, int l)
     57 {
     58 	int ret;
     59 
     60 	if(!tls)
     61 		ret = write(*(int *)sock, buf, l);
     62 	else {
     63 		ret = BIO_write((BIO *)sock, buf, l);
     64 		(void)BIO_flush((BIO *)sock);
     65 	}
     66 
     67 	return ret;
     68 }
     69 
     70 int
     71 sread(void *sock, char *buf, int l)
     72 {
     73 	int ret;
     74 	
     75 	if(!tls)
     76 		ret = read(*(int *)sock, buf, l);
     77 	else {
     78 		ret = BIO_read((BIO *)sock, buf, l);
     79 		(void)BIO_flush((BIO *)sock);
     80 	}
     81 
     82 	return ret;
     83 }
     84 
     85 void
     86 tprintf(void *sock, char *fmt, ...)
     87 {
     88 	va_list fmtargs;
     89 	char buf[8192];
     90 
     91 	va_start(fmtargs, fmt);
     92 	vsnprintf(buf, sizeof(buf) - 1, fmt, fmtargs);
     93 	va_end(fmtargs);
     94 
     95 	if(swrite(sock, buf, strlen(buf)) < 0)
     96 		perror("write");
     97 
     98 	return;
     99 }
    100 
    101 int
    102 connecttcp(char *host, char *service)
    103 {
    104 	int sock;
    105 	struct addrinfo *ai, *a;
    106 
    107 	sock = -1;
    108 
    109 	if(getaddrinfo(host, service, nil, &ai) < 0) {
    110 		perror("getaddrinfo");
    111 		return -1;
    112 	}
    113 
    114 	for(a = ai; a; a = a->ai_next) {
    115 		sock = socket(a->ai_family, a->ai_socktype, a->ai_protocol);
    116 		if(sock < 0) {
    117 			perror("socket");
    118 			sock = -1;
    119 			break;
    120 		}
    121 
    122 		if(connect(sock, a->ai_addr, a->ai_addrlen) < 0) {	
    123 			perror("connect");
    124 			sock = -1;
    125 			break;
    126 		} else
    127 			break;
    128 	}
    129 
    130 	freeaddrinfo(ai);
    131 
    132 	return sock;
    133 } 
    134 
    135 void
    136 freecmd(command *cmd)
    137 {
    138 	int i;
    139 
    140 	i = -1;
    141 	if(cmd != nil) {
    142 		if(cmd->isalloc)
    143 			while(++i < cmd->len)
    144 				if(cmd->params[i] != nil)
    145 					free(cmd->params[i]);
    146 		if(cmd->params != nil)
    147 			free(cmd->params);
    148 		free(cmd);
    149 	}
    150 
    151 	return;
    152 }
    153 
    154 int
    155 addlist(command *c, char *p)
    156 {
    157 	int i;
    158 
    159 	i = -1;
    160 	if(c != nil) {
    161 		if(c->isalloc) {
    162 			p = strdup(p);
    163 			while(++i < c->len) {
    164 				if(c->params[i] == nil) {
    165 					c->params[i] = p;
    166 
    167 					return 2;
    168 				}
    169 			}
    170 		}
    171 
    172 		c->len++;
    173 		c->params = realloc(c->params, sizeof(char *) * c->len);
    174 		c->params[c->len - 1] = p;
    175 
    176 		return 1;
    177 	}
    178 
    179 	return 0;
    180 }
    181 
    182 int
    183 searchlist(command *c, char *p)
    184 {
    185 	int i;
    186 
    187 	i = -1;
    188 	if(c != nil)
    189 		while(++i < c->len)
    190 			if(c->params[i] != nil)
    191 				if(!strcasecmp(c->params[i], p))
    192 					return i;
    193 
    194 	return -1;
    195 }
    196 
    197 int
    198 dellist(command *c, char *p)
    199 {
    200 	int i;
    201 	
    202 	if(c != nil) {
    203 		i = searchlist(c, p);
    204 		if(i != -1) {
    205 			free(c->params[i]);
    206 			c->params[i] = nil;
    207 			c->len--;
    208 			return 1;
    209 		}
    210 	}
    211 
    212 	return 0;
    213 }
    214 
    215 command *
    216 parsecmd(command *cmd, char *data, int cmds)
    217 {
    218 	command *ret;
    219 	char *next, *last, *temp;
    220 
    221 	temp = nil;
    222 
    223 	if(cmd == nil) {
    224 		ret = realloci(nil, sizeof(command), 2);
    225 		last = data;
    226 	} else {
    227 		ret = cmd;
    228 		if(ret->isalloc) {
    229 			temp = strdup(ret->params[ret->len - 1]);
    230 			dellist(ret, ret->params[ret->len - 1]);
    231 		} else {
    232 			ret->len--;
    233 			temp = ret->params[ret->len];
    234 		}
    235 		last = temp;
    236 	}
    237 	while((next = strchr(last, ' ')) != nil && ret->len < cmds) {
    238 		*next = '\0';
    239 		next++;
    240 		if(last[0] == ':')
    241 			last++;
    242 		addlist(ret, last);
    243 		last = next;
    244 	}
    245 	if(last[0] == ':')
    246 		last++;
    247 	if(last != nil)
    248 		addlist(ret, last);
    249 
    250 	if(temp != nil && cmd->isalloc)
    251 		free(temp);
    252 
    253 	return ret;
    254 }
    255 
    256 char *
    257 mktimestamp(char bord, char bord_e)
    258 {
    259 	time_t tim;
    260 	struct tm tm;
    261 	char *ret;
    262 
    263 	time(&tim);
    264 	localtime_r(&tim, &tm);
    265 
    266 	ret = malloc(31);
    267 	snprintf(ret, 30, "%c%.2d:%.2d%c", bord, tm.tm_hour, tm.tm_min, bord_e);
    268 
    269 	return ret;
    270 }
    271 
    272 void
    273 setchan(char *new)
    274 {
    275 
    276 	if(lastchan != nil)
    277 		free(lastchan);
    278 
    279 	lastchan = realloci(nil, strlen(new) + 1, 2);
    280 	strcpy(lastchan, new);
    281 }
    282 
    283 void
    284 autojoin(void *sock)
    285 {
    286 	int i;
    287 
    288 	for(i = 0; i < joinl->len; i++)
    289 		tprintf(sock, "JOIN %s\r\n", joinl->params[i]);
    290 	setchan(joinl->params[joinl->len - 1]);
    291 }
    292 
    293 int
    294 gotcommand(void *sock, char *data, int len)
    295 {
    296 	command *cmd;
    297 	char *last, *next, *send, *cpy, *test, *tmstmp;
    298 	int clen;
    299 	time_t tim;
    300 
    301 	clen = len;
    302 	last = data;
    303 	send = realloci(nil, 513, 2);
    304 
    305 	while((next = strchr(last, '\n')) !=  nil) {
    306 		*next++ = '\0';
    307 		clen -= (next - last);
    308 		if(*(next - 2) == '\r')
    309 			*(next - 2) = '\0';
    310 
    311 		cpy = strdup(last);
    312 		cmd = parsecmd(nil, last, 3);
    313 		tmstmp = mktimestamp('(', ')');
    314 
    315 		if(cmd->len > 1) {
    316 			if(!strncasecmp(cmd->params[0], "PING", 4)) {
    317 				tprintf(sock, "PONG %s\r\n", cmd->params[1]);
    318 				goto c_end;
    319 			}
    320 			if(!strncasecmp(cmd->params[0], "ERROR", 5)) {
    321 				printf("%sERROR%% %s %s %s\n", tmstmp, cmd->params[1], cmd->params[2], cmd->params[3]);
    322 				goto c_end;
    323 			}
    324 		}
    325 		if(cmd->len > 2) {
    326 			if(!strncasecmp(cmd->params[1], "JOIN", 4)) {
    327 				printf("%s(%s) has joined %s\n", tmstmp, cmd->params[0], cmd->params[2]);
    328 				goto c_end;
    329 			}
    330 			if(!strncasecmp(cmd->params[1], "PART", 4)) {
    331 				printf("%s(%s) has parted %s\n", tmstmp, cmd->params[0], cmd->params[2]);
    332 				goto c_end;
    333 			}
    334 			if(!strncasecmp(cmd->params[1], "MODE", 4)) {
    335 				printf("%s(%s)-m-(%s)%% %s\n", tmstmp, cmd->params[0], cmd->params[2], cmd->params[3]);
    336 				goto c_end;
    337 			}
    338 			if(!strncasecmp(cmd->params[1], "QUIT", 4)) {
    339 				printf("%s(%s) has quit (%s %s)\n", tmstmp, cmd->params[0], cmd->params[2], (cmd->len > 3) ? cmd->params[3] : "");
    340 				goto c_end;
    341 			}
    342 			if((test = strchr(cmd->params[0], '!')) != nil)
    343 				*test = '\0';
    344 			if(!strncasecmp(cmd->params[1], "NICK", 4)) {
    345 				printf("%s(%s) changed nick to %s\n", tmstmp, cmd->params[0], cmd->params[2]);
    346 				if(searchlist(ignorel, cmd->params[0]) != -1) {
    347 					dellist(ignorel, cmd->params[0]);
    348 					addlist(ignorel, cmd->params[2]);
    349 				}
    350 				goto c_end;
    351 			}
    352 		}
    353 		if(cmd->len > 3) {
    354 			if(strlen(cmd->params[1]) == 3) {
    355 				if(ignoreflood) {
    356 					if(!strncasecmp(cmd->params[1], "001", 3) ||
    357 							!strncasecmp(cmd->params[1], "002", 3) ||
    358 							!strncasecmp(cmd->params[1], "003", 3) ||
    359 							!strncasecmp(cmd->params[1], "004", 3) ||
    360 							!strncasecmp(cmd->params[1], "005", 3) ||
    361 							!strncasecmp(cmd->params[1], "250", 3) ||
    362 							!strncasecmp(cmd->params[1], "251", 3) ||
    363 							!strncasecmp(cmd->params[1], "252", 3) ||
    364 							!strncasecmp(cmd->params[1], "253", 3) ||
    365 							!strncasecmp(cmd->params[1], "254", 3) ||
    366 							!strncasecmp(cmd->params[1], "255", 3) ||
    367 							!strncasecmp(cmd->params[1], "265", 3) ||
    368 							!strncasecmp(cmd->params[1], "266", 3) ||
    369 							!strncasecmp(cmd->params[1], "317", 3) ||
    370 							!strncasecmp(cmd->params[1], "318", 3) ||
    371 							!strncasecmp(cmd->params[1], "366", 3) ||
    372 							!strncasecmp(cmd->params[1], "375", 3) ||
    373 							!strncasecmp(cmd->params[1], "372", 3))
    374 						goto c_end;
    375 				}
    376 				if(!strncasecmp(cmd->params[1], "376", 3)) {
    377 					if(doautojoin && (!havenickserv || anickused))
    378 						autojoin(sock);
    379 					goto c_end;
    380 				}
    381 					
    382 				printf("%s(%s)(%s)%% %s\n", tmstmp, cmd->params[0], cmd->params[1], cmd->params[3]);
    383 				if(!strncasecmp(cmd->params[1], "433", 3) && anick != nil) {
    384 					if(anickused) {
    385 						printf("%s(IRCC)%% Sorry, but nick and anick are used. Try something different.\n", tmstmp);
    386 						goto c_end;
    387 					}
    388 					tprintf(sock, "NICK %s\r\n", anick);
    389 					anickused = 1;
    390 				}
    391 				goto c_end;
    392 			}
    393 			if(!strncasecmp(cmd->params[1], "INVITE", 6)) {
    394 				printf("%s(%s) invited you to %s\n", tmstmp, cmd->params[0], cmd->params[3]);
    395 				goto c_end;
    396 			}
    397 			if(!strncasecmp(cmd->params[1], "TOPIC", 5)) {
    398 				printf("%s(%s) topicchange in %s to \"%s\"\n", tmstmp, cmd->params[0], cmd->params[2], cmd->params[3]);
    399 				goto c_end;
    400 			}
    401 			if(!strncasecmp(cmd->params[1], "KICK", 4)) {
    402 				if(parsecmd(cmd, nil, 4) != nil)
    403 					printf("%s(%s) kicked %s out of %s \"%s\"\n", tmstmp, cmd->params[0], cmd->params[3], cmd->params[2], cmd->params[4]);
    404 				else
    405 					printf("%s(%s) kicked %s out of %s\n", tmstmp, cmd->params[0], cmd->params[3], cmd->params[2]);
    406 				goto c_end;
    407 			}
    408 			if(searchlist(ignorel, cmd->params[0]) == -1 &&
    409 				searchlist(ignorel, cmd->params[2]) == -1) {
    410 				if(!strncasecmp(cmd->params[1], "NOTICE", 6)) {
    411 					printf("%s(%s|%s)%% %s\n", tmstmp, cmd->params[0], cmd->params[2], cmd->params[3]);
    412 					if(!strncasecmp(cmd->params[0], "NickServ", 8) && havenickserv) {
    413 						printf("in case NickServ\n");
    414 						tprintf(sock, "PRIVMSG NickServ :IDENTIFY %s\r\n", passwd);
    415 						memset(passwd, 0, strlen(passwd));
    416 						havenickserv = 0;
    417 						if(doautojoin)
    418 							autojoin(sock);
    419 					}
    420 					goto c_end;
    421 				}
    422 				if(!strncasecmp(cmd->params[1], "PRIVMSG", 7)) {
    423 					if(*(cmd->params[3]) == '\x01') {
    424 						if(!strncasecmp(cmd->params[3]+1, "VERSION", 7))
    425 							tprintf(sock, "NOTICE %s :\x01VERSION " VERSION "\x01\r\n", cmd->params[0]);
    426 						if(!strncasecmp(cmd->params[3]+1, "TIME", 4)) {
    427 							tim = time(0);
    428 							test = ctime(&tim);
    429 							test[strlen(test) - 1] = '\0';
    430 							tprintf(sock, "NOTICE %s :\x01TIME %s\x01\r\n", cmd->params[0], test);
    431 						}
    432 						if(!strncasecmp(cmd->params[3]+1, "PING", 4))
    433 							tprintf(sock, "NOTICE %s :\x01PONG %s\r\n", cmd->params[0], cmd->params[3]+6);
    434 						if(!strncasecmp(cmd->params[3]+1, "ACTION", 6)) {
    435 							*(cmd->params[3] + strlen(cmd->params[3])-1) = '\0';
    436 							printf("%s(%s+%s) %s\n", tmstmp, cmd->params[0], cmd->params[2], cmd->params[3]+8);
    437 							goto c_end;
    438 						}
    439 						if(!strncasecmp(cmd->params[3]+1, "CLIENTINFO", 10))
    440 							tprintf(sock, "NOTICE %s :\x01CLIENTINFO PING VERSION TIME USERINFO CLIENTINFO\x01\r\n", cmd->params[0]);
    441 						if(!strncasecmp(cmd->params[3]+1, "USERINFO", 8))
    442 							tprintf(sock, "NOTICE %s :\x01USERINFO %s\x01\r\n", cmd->params[0], (clientinfo != nil) ? clientinfo : "<nil>");
    443 					}
    444 					printf("%s(%s-%s)%% %s\n", tmstmp, cmd->params[0], cmd->params[2], cmd->params[3]);
    445 					goto c_end;
    446 				}
    447 				if(!strncasecmp(cmd->params[0], "NOTICE", 6)) {
    448 					printf("%sNOTICE%% %s\n", tmstmp, cmd->params[3]);
    449 					goto c_end;
    450 				}
    451 			} else
    452 				goto c_end;
    453 		}
    454 		printf("%s\n",cpy);
    455 c_end:
    456 		free(tmstmp);
    457 		free(cpy);
    458 		freecmd(cmd);
    459 		last = next;
    460 	}
    461 
    462 	if(clen > 0)
    463 		strcpy(data, data + len - clen);
    464 	if(send != nil)
    465 		free(send);
    466 
    467 	return clen;
    468 }
    469 
    470 void *
    471 recvproc(void *sock)
    472 {
    473 	char *recv;
    474 	int len, overl;
    475 
    476 	recv = realloci(nil, 1025, 2);
    477 	len = 1;
    478 	overl = 0;
    479 
    480 	while(len > 0 && sock != nil) {
    481 		len = sread(sock, recv + overl, 1024 - overl);
    482 
    483 		if(debug)
    484 			printf("%s\n", recv);
    485 		
    486 		if(len > 1)
    487 			overl = gotcommand(sock, recv, len + overl);
    488 	}
    489 
    490 	free(recv);
    491 
    492 	return nil;
    493 }
    494 
    495 void
    496 usage(void)
    497 {
    498 
    499 	fprintf(stdout, "usage: %s [-dit] [-a anick] [-c clientinfo]"
    500 			" [-j #channel ...]"
    501 			" [-k passwd]"
    502 			" [-n nick] [-o port] [-p pass] [-u user]"
    503 			" server\n",
    504 			argv0);
    505 
    506 	exit(1);
    507 }
    508 
    509 int
    510 main(int argc, char *argv[])
    511 {
    512 	void *sock;
    513 	int i, so;
    514 	char *send, *rad, *tmstmp, *user, *pass, *port;
    515 	command *cmd;
    516 	pthread_t th;
    517 	BIO *bio;
    518 	SSL_CTX *ctx;
    519 	SSL *ssl;
    520 
    521 	ctx = nil;
    522 	user = "root";
    523 	pass = nil;
    524 	nick = "root";
    525 	anick = nil;
    526 	port = nil;
    527 	tls = 0;
    528 	clientinfo = "The user has not specified his details.";
    529 	joinl = realloci(nil, sizeof(command), 2);
    530 	joinl->isalloc = 1;
    531 
    532 	ARGBEGIN {
    533 	case 'u':
    534 		user = EARGF(usage());
    535 		break;
    536 	case 'p':
    537 		pass = EARGF(usage());
    538 		break;
    539 	case 't':
    540 		tls = 1;
    541 		break;
    542 	case 'd':
    543 		debug = 1;
    544 		break;
    545 	case 'c':
    546 		clientinfo = EARGF(usage());
    547 		break;
    548 	case 'i':
    549 		ignoreflood = 1;
    550 		break;
    551 	case 'n':
    552 		nick = EARGF(usage());
    553 		break;
    554 	case 'o':
    555 		port = EARGF(usage());
    556 		break;
    557 	case 'a':
    558 		anick = EARGF(usage());
    559 		break;
    560 	case 'k':
    561 		havenickserv = 1;
    562 		passwd = EARGF(usage());
    563 		break;
    564 	case 'j':
    565 		addlist(joinl, EARGF(usage()));
    566 		doautojoin = 1;
    567 		break;
    568 	default:
    569 		usage();
    570 	} ARGEND;
    571 			
    572 			
    573 	if(argc < 1)
    574 		usage();
    575 
    576 	if(!tls) {	
    577 		so = connecttcp(argv[0], (port == nil)? "6667" : port);
    578 		if(so < 0) {
    579 			perror("connecttcp");
    580 			exit(1);
    581 		}
    582 
    583 		sock = &so;
    584 	} else {
    585 		ERR_load_crypto_strings();
    586 		SSL_library_init();
    587 
    588 		ctx = SSL_CTX_new(SSLv23_client_method());
    589 		if(ctx == nil) {
    590 			perror("SSL_CTX_new");
    591 			ERR_print_errors_fp(stderr);
    592 			exit(1);
    593 		}
    594 		bio = BIO_new_ssl_connect(ctx);
    595 		if(bio == nil) {
    596 			perror("BIO_new_ssl_connect");
    597 			ERR_print_errors_fp(stderr);
    598 			exit(1);
    599 		}
    600 
    601 		BIO_get_ssl(bio, &ssl);
    602 		SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);	
    603 
    604 		BIO_set_conn_port(bio, (port == nil)? "994" : port);
    605 		BIO_set_conn_hostname(bio, argv[0]); 
    606 		if(BIO_do_connect(bio) <= 0) {
    607 			perror("BIO_do_connect");
    608 			ERR_print_errors_fp(stderr);
    609 			exit(1);
    610 		}
    611 
    612 		sock = bio;
    613 	}
    614 
    615 	if(user == nil)
    616 		user = nick;
    617 
    618 	if(pass != nil)
    619 		tprintf(sock, "PASS %s\r\n", pass);
    620 	tprintf(sock, "NICK %s\r\n", nick);
    621 	tprintf(sock, "USER %s localhost %s :%s\r\n", user, nick, user);
    622 
    623 	if(pthread_create(&th, nil, recvproc, sock) < 0) {
    624 		perror("pthread_create");
    625 		exit(1);
    626 	}
    627 
    628 	ignorel = realloci(nil, sizeof(command), 2);
    629 	ignorel->isalloc = 1;
    630 	send = realloci(nil, 513, 2);
    631 	rad = realloci(nil, 513, 2);
    632 
    633 	while(sock > 0) {
    634 		i = -1;
    635 		cmd = nil;
    636 		memset(send, 0, 513);
    637 		memset(rad, 0, 513);
    638 
    639 		while(read(0, &rad[++i], 1) && i < 400 && sock != nil) {
    640 			if(rad[i] == '\n') {
    641 				rad[i] = '\0';
    642 				break;
    643 			}
    644 		}
    645 		rad[i + 1] = '\0';
    646 
    647 		tmstmp = mktimestamp('(', ')');
    648 		if(rad[0] == '/')
    649 			cmd = parsecmd(nil, rad, 2);
    650 
    651 		if(cmd != nil) {
    652 			if(cmd->len > 0) {
    653 				switch(cmd->params[0][1]) {
    654 				case 'H':
    655 				case 'h':
    656 					printf("%s Help for ircc:\n", tmstmp);
    657 					printf("%s   /a chan text - send /me text to chan\n", tmstmp);
    658 					printf("%s   /c chan what - send CTCP what to chan\n", tmstmp);
    659 					printf("%s   /d - turn debugging on or off\n", tmstmp);
    660 					printf("%s   /h - show this help\n", tmstmp);
    661 					printf("%s   /i chan - ignore/unignore chan\n", tmstmp);
    662 					printf("%s   /j chan - join chan\n", tmstmp);
    663 					printf("%s   /k user [readon] - kick a user\n", tmstmp);
    664 					printf("%s   /l [chan] - list channels on server\n", tmstmp);
    665 					printf("%s   /m chan text - send text to chan\n", tmstmp);
    666 					printf("%s   /n nick - change nickname to nick\n", tmstmp);
    667 					printf("%s   /o chan mode [user] - set mode on chan\n", tmstmp);
    668 					printf("%s   /p chan - part chan\n", tmstmp);
    669 					printf("%s   /q [msg] - quit ircc\n", tmstmp);
    670 					printf("%s   /s chan - set active chan\n", tmstmp);
    671 					printf("%s   /t chan [topic] - set/show topic of chan\n", tmstmp);
    672 					printf("%s   /u chan - WHO of chan\n", tmstmp);
    673 					printf("%s   /w chan - WHOIS of chan\n", tmstmp);
    674 					goto end_e;
    675 				case 'D':
    676 				case 'd':
    677 					if(debug)
    678 						debug = 0;
    679 					else
    680 						debug = 1;
    681 					printf("%s debug %d\n", tmstmp, debug);
    682 					goto end_e;
    683 				case 'Q':
    684 				case 'q':
    685 					sprintf(send, "QUIT :\r\n");
    686 					if(cmd->len == 2)
    687 						sprintf(send + 6, "%s\r\n", cmd->params[1]);
    688 					if(cmd->len > 2)
    689 						sprintf(send + 6, "%s %s\r\n", cmd->params[1], cmd->params[2]);
    690 					swrite(sock, send, strlen(send));
    691 					if(tls)
    692 						BIO_free_all(sock);
    693 					else
    694 						close(*(int *)sock);
    695 					sock = nil;
    696 					goto end_e;
    697 				case 'L':
    698 				case 'l':
    699 					sprintf(send, "LIST\r\n");
    700 					if(cmd->len == 2)
    701 						sprintf(send + 4, " %s\r\n", cmd->params[1]);
    702 					if(cmd->len > 2)
    703 						sprintf(send + 4, " %s %s\r\n", cmd->params[1], cmd->params[2]);
    704 					swrite(sock, send, strlen(send));
    705 					goto end_e;
    706 				case 'S':
    707 				case 's':
    708 					if(cmd->len > 1)
    709 						setchan(cmd->params[1]);
    710 					else
    711 						printf("%s\n", (lastchan == nil) ? "<nil>": lastchan);
    712 					goto end_e;
    713 				default:
    714 					break;
    715 				}
    716 			}
    717 			if(cmd->len > 1) {
    718 				switch(cmd->params[0][1]) {
    719 				case 'J':
    720 				case 'j':
    721 					tprintf(sock, "JOIN %s\r\n", cmd->params[1]);
    722 					setchan(cmd->params[1]);
    723 					goto end_e;
    724 				case 'P':
    725 				case 'p':
    726 					tprintf(sock, "PART %s\r\n", cmd->params[1]);
    727 					goto end_e;
    728 				case 'N':
    729 				case 'n':
    730 					tprintf(sock, "NICK %s\r\n", cmd->params[1]);
    731 					goto end_e;
    732 				case 'W':
    733 				case 'w':
    734 					tprintf(sock, "WHOIS %s\r\n", cmd->params[1]);
    735 					goto end_e;
    736 				case 'U':
    737 				case 'u':
    738 					tprintf(sock, "WHO %s\r\n", cmd->params[1]);
    739 					goto end_e;
    740 				case 'T':
    741 				case 't':
    742 					sprintf(send, "TOPIC %s\r\n", cmd->params[1]);
    743 					if(cmd->len > 2)
    744 						sprintf(send + strlen(send) - 2, " :%s\r\n", cmd->params[2]);
    745 					tprintf(sock, send);
    746 					goto end_e;
    747 				case 'I':
    748 				case 'i':
    749 					if(searchlist(ignorel, cmd->params[1]) != -1) {
    750 						dellist(ignorel, cmd->params[1]);
    751 						printf("%s no more ignored. %d\n", cmd->params[1], searchlist(ignorel, cmd->params[1]));
    752 					} else {
    753 						addlist(ignorel, cmd->params[1]);
    754 						printf("%s will be ignored.\n", cmd->params[1]);
    755 					}
    756 					goto end_e;
    757 				default:
    758 					break;
    759 				}
    760 			}
    761 			if(cmd->len > 2) {
    762 				switch(cmd->params[0][1]) {
    763 				case 'M':
    764 				case 'm':
    765 					tprintf(sock, "PRIVMSG %s :%s\r\n", cmd->params[1], cmd->params[2]);
    766 					goto end_e;
    767 				case 'A':
    768 				case 'a':
    769 					tprintf(sock, "PRIVMSG %s :%sACTION %s\x01\r\n", "\x01", cmd->params[1], cmd->params[2]);
    770 					goto end_e;
    771 				case 'O':
    772 				case 'o':
    773 					if(cmd->len > 3)
    774 						tprintf(sock, "MODE %s %s %s\r\n", cmd->params[1], cmd->params[2], cmd->params[3]);
    775 					else
    776 						tprintf(sock, "MODE %s %s\r\n", cmd->params[1], cmd->params[2]);
    777 					goto end_e;
    778 				case 'C':
    779 				case 'c':
    780 					printf("%s(%s) ctcp %s\n", tmstmp, cmd->params[1], cmd->params[2]);
    781 					tprintf(sock, "PRIVMSG %s :\x01%s\x01\r\n", cmd->params[1], cmd->params[2]);
    782 					goto end_e;
    783 				case 'K':
    784 				case 'k':
    785 					sprintf(send, "KICK %s %s\r\n", cmd->params[1], cmd->params[2]);
    786 					if(cmd->len > 3)
    787 						sprintf(send + strlen(send) - 2, " :%s\r\n", cmd->params[3]);
    788 					tprintf(sock, send);
    789 					goto end_e;
    790 				default:
    791 					break;
    792 				}
    793 			}
    794 		}
    795 		if(lastchan != nil)
    796 			tprintf(sock, "PRIVMSG %s :%s\r\n", lastchan, rad);
    797 		else
    798 			printf(".");
    799 end_e:
    800 		printf("%s\n", tmstmp);
    801 		free(tmstmp);
    802 		freecmd(cmd);
    803 	}
    804 
    805 	if(lastchan != nil)
    806 		free(lastchan);
    807 	free(send);
    808 	free(rad);
    809 	freecmd(ignorel);
    810 	freecmd(joinl);
    811 	if(tls)
    812 		SSL_CTX_free(ctx);
    813 
    814 	return 0;
    815 }
    816