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;
+}
+