tAdd Linux version. - ircc - Simple IRC client
 (HTM) git clone git://r-36.net/ircc
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) LICENSE
       ---
 (DIR) commit 5d3567b161dcd0b1bfa1cb5ad062cafdbd85a69f
 (DIR) parent b2e2266f329a895a12faeb7857ec09ca9fc03867
 (HTM) 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(-)
       ---
 (DIR) diff --git a/linux/Makefile b/linux/Makefile
       t@@ -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}"
       +
 (DIR) diff --git a/linux/README b/linux/README
       t@@ -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
       +
 (DIR) diff --git a/linux/arg.h b/linux/arg.h
       t@@ -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
       +
 (DIR) diff --git a/linux/ircc.c b/linux/ircc.c
       t@@ -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;
       +}
       +