ircc

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

commit 5d3567b161dcd0b1bfa1cb5ad062cafdbd85a69f
parent b2e2266f329a895a12faeb7857ec09ca9fc03867
Author: Christoph Lohmann <20h@r-36.net>
Date:   Tue, 20 Mar 2018 06:20:34 +0100

Add Linux version.

Diffstat:
linux/Makefile | 46++++++++++++++++++++++++++++++++++++++++++++++
linux/README | 17+++++++++++++++++
linux/arg.h | 19+++++++++++++++++++
linux/ircc.c | 816+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
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; +} +