diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..72b27f0
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+src/*.d
+src/*.o
+.*.swp
diff --git a/GNUmakefile b/GNUmakefile
new file mode 100644
index 0000000..69a7d90
--- /dev/null
+++ b/GNUmakefile
@@ -0,0 +1,62 @@
+# Common
+prefix= /usr/local
+
+CC=clang
+CFLAGS= -Wall -Werror -Wextra
+#CFLAGS+= -std=c99
+CFLAGS+= -std=gnu99
+#CFLAGS+= -Os
+CFLAGS+= -g
+
+LDFLAGS= -lgnutls -lpthread -ldl -rdynamic
+LDFLAGS+= -g
+#LDFLAGS+= -Os
+
+# Target: cbot
+cbot_BIN= cbot
+cbot_OBJ= $(subst .c,.o,$(wildcard src/*.c))
+
+# Targets: plugins
+plugins_SO= $(subst .c,.so,$(wildcard src/plugin/*.c))
+
+# Rules
+all: $(cbot_BIN) $(plugins_SO)
+
+$(cbot_BIN): $(cbot_OBJ)
+	$(CC) $(CFLAGS) $(LDFLAGS) -o $(cbot_BIN) $(cbot_OBJ)
+
+%.so: %.c
+	$(CC) -shared -nostartfiles -fPIC $(CFLAGS) $*.c -o $*.so
+	$(CC) -MM $(CFLAGS) $*.c > $*.d
+	@mv -f $*.d $*.d.tmp
+	@sed -e 's|.*:|$*.o:|' < $*.d.tmp > $*.d
+	@sed -e 's/.*://' -e 's/\\$$//' < $*.d.tmp | fmt -1 | sed -e 's/^ *//' -e 's/$$/:/' >> $*.d
+	@rm -f $*.d.tmp
+
+-include $(cbot_OBJ:.o=.d)
+
+%.o: %.c
+	$(CC) -c $(CFLAGS) $*.c -o $*.o
+	$(CC) -MM $(CFLAGS) $*.c > $*.d
+	@mv -f $*.d $*.d.tmp
+	@sed -e 's|.*:|$*.o:|' < $*.d.tmp > $*.d
+	@sed -e 's/.*://' -e 's/\\$$//' < $*.d.tmp | fmt -1 | sed -e 's/^ *//' -e 's/$$/:/' >> $*.d
+	@rm -f $*.d.tmp
+
+clean:
+	$(RM) */*.o */*.d */*/*.o */*/*.d */*/*.so
+	$(RM) $(cbot_BIN) AllTests
+	$(RM) -rf clang-analyzer
+
+check:
+	scan-build -o clang-analyzer make
+	clang -o AllTests $(wildcard tests/*.c)
+	./AllTests
+
+tags:
+	ctags -o .tags -a $(wildcard src/*.[hc])
+
+.PHONY: all clean install uninstall tags
+
+start:
+	./cbot
diff --git a/README b/README
new file mode 100644
index 0000000..2a44ea5
--- /dev/null
+++ b/README
@@ -0,0 +1,11 @@
+Dependencies :
+  - Fungi : cabal install fungi
+  - gnutls-devel
+
+Configuring :
+  - irc server and port : src/config.h
+  - password and NICK in bot.b98
+
+Running the bot :
+  - make
+  - ./cbot
diff --git a/bot.b98 b/bot.b98
new file mode 100644
index 0000000..cf3f206
--- /dev/null
+++ b/bot.b98
@@ -0,0 +1,3 @@
+<>>#;>:#,_v;"PASS my_secrets"da"NICK b98"da"USER b98 0 0 b98"da"JOIN #test"da
+          >~'P-#v_~'I-#v_~'N-#v_~'G-#v_ad"uoarg GNOP"ck,v
+          ^     <      <      <      <                  <
diff --git a/src/b98.c b/src/b98.c
new file mode 100644
index 0000000..2ca408f
--- /dev/null
+++ b/src/b98.c
@@ -0,0 +1,112 @@
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/select.h>
+#include <unistd.h>
+
+#include "b98.h"
+#include "tls.h"
+
+pid_t pid = 0;
+int from_b98[2];
+int to_b98[2];
+
+#define PARENT_READ  from_b98[0]
+#define CHILD_WRITE  from_b98[1]
+#define CHILD_READ   to_b98[0]
+#define PARENT_WRITE to_b98[1]
+
+int bot_init(void)
+{
+    if (pipe (from_b98)) {
+        perror("from_b98 pipe");
+        return 1;
+    }
+    if (pipe (to_b98)) {
+        perror("to_b98 pipe");
+        return 1;
+    }
+    pid = fork ();
+    if (pid == (pid_t) 0) {  // Child process
+        close(PARENT_WRITE);
+        close(PARENT_READ);
+
+        dup2(CHILD_READ,  STDIN_FILENO);  close(CHILD_READ);
+        dup2(CHILD_WRITE, STDOUT_FILENO);  close(CHILD_WRITE);
+
+        execvp("fungi", (char *[]) { "fungi", "bot.b98" } );
+        perror("execv");
+        return 1;
+    }
+    else if (pid < (pid_t) 0) {
+        fprintf (stderr, "Fork failed.\n");
+        return 1;
+    }
+    else {  // Parent process
+        close(CHILD_READ);
+        close(CHILD_WRITE);
+        return 0;
+    }
+}
+
+int bot_loop(int socket_fd)
+{
+    fd_set rfds;
+    while (1) {
+        struct timeval timeout;
+        timeout.tv_sec = 1;
+        timeout.tv_usec = 0;
+
+        FD_ZERO(&rfds);
+        FD_SET(PARENT_READ, &rfds);
+        FD_SET(socket_fd, &rfds);
+        select(20, &rfds, NULL, NULL, &timeout);
+        if ( FD_ISSET(PARENT_READ, &rfds) ) {
+            static char sbuf[512]; /* string buffer */
+            static int n = 0; /* nition in buffer */
+            ssize_t len = n + read(PARENT_READ, sbuf + n, 512 - n);
+            while (n < len) {
+                if ((n > 0 && sbuf[n] == '\n' && sbuf[n - 1] == '\r') ) { /* If we got a full message */
+                    tls_send(sbuf, n);
+                    sbuf[n - 1] = '\0';
+                    printf(">>> %s\n", sbuf);
+                    memmove(sbuf, sbuf + n + 1, len - n - 1);
+                    len = len - n - 1;
+                    if (len == 1)
+                        len = 0;
+                    n = 0; /* we reinitialise n for the next run */
+                } else if (n == 512) { /* If we got a full buffer without finding a \r\n */
+                    fprintf(stderr, "We got a full buffer without finding a \r\n");
+                    return 2;
+                } else { /* Nothing to do otherwise but roll the next char */
+                    n++;
+                }
+            }
+        } else if ( FD_ISSET(socket_fd, &rfds) ) {
+            tls_read(&bot_handle_input);
+        }
+    }
+}
+
+int bot_handle_input(const char* const sbuf, int len)
+{
+    static char msg[512];
+    static int pos = 0; /* position in msg buffer */
+    for (int n = 0; n < len; n++) {
+        msg[pos] = sbuf[n];
+        if ((n > 0 && sbuf[n] == '\n' && sbuf[n - 1] == '\r') ) { /* If we got a full message */
+            write(PARENT_WRITE, msg, n);
+            msg[pos - 1] = '\0';
+            printf("<<< %s\n", msg);
+            pos = 0; /* we reinitialise pos for the next run */
+        } else if (pos == sizeof(msg)) { /* If we got a full buffer without finding a \r\n */
+            fprintf(stderr, "We got a full buffer without finding a \r\n");
+            return 2;
+        } else { /* Nothing to do otherwise but roll the next char */
+            pos++;
+        }
+    }
+
+    return 0;
+}
diff --git a/src/b98.h b/src/b98.h
new file mode 100644
index 0000000..3414814
--- /dev/null
+++ b/src/b98.h
@@ -0,0 +1,8 @@
+#ifndef _B98_H_
+#define _B98_H_
+
+int bot_init(void);
+int bot_loop(int socket_fd);
+int bot_handle_input(const char* const sbuf, int len);
+
+#endif
diff --git a/src/bot.c b/src/bot.c
new file mode 100644
index 0000000..f18e8a1
--- /dev/null
+++ b/src/bot.c
@@ -0,0 +1,18 @@
+#include "b98.h"
+#include "config.h"
+#include "tls.h"
+
+int main()
+{
+    int err = bot_init();
+    if (err)
+        return err;
+
+    tls_init();
+    int socket_fd = tls_connect(HOST, PORT);
+
+    bot_loop(socket_fd);
+
+    tls_clean();
+    return 0;
+}
diff --git a/src/config.h b/src/config.h
new file mode 100644
index 0000000..ffad107
--- /dev/null
+++ b/src/config.h
@@ -0,0 +1,7 @@
+#ifndef _CONFIG_H_
+#define _CONFIG_H_
+
+#define HOST "example.com"
+#define PORT 6667
+
+#endif
diff --git a/src/tcp.c b/src/tcp.c
new file mode 100644
index 0000000..659a39e
--- /dev/null
+++ b/src/tcp.c
@@ -0,0 +1,65 @@
+#include <arpa/inet.h>
+#include <errno.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "tcp.h"
+
+int tcp_connect(const char* const host, const int port)
+{
+    char ip[INET6_ADDRSTRLEN];
+    hostname_to_ip(host , ip);
+    printf("%s resolved to %s\n" , host, ip);
+
+    struct sockaddr_in sa;
+    int sd = socket(AF_INET, SOCK_STREAM, 0);
+    memset(&sa, '\0', sizeof(sa));
+    sa.sin_family = AF_INET;
+    sa.sin_port = htons(port);
+    inet_pton(AF_INET, ip, &sa.sin_addr);
+
+    int err = connect(sd, (struct sockaddr *) &sa, sizeof(sa));
+    if (err < 0) {
+        fprintf(stderr, "Couldn't connect %s:%d : %s\n", host, port, strerror(errno));
+        return -1;
+    }
+
+    return sd;
+}
+
+void tcp_close(const int sd)
+{
+    shutdown(sd, SHUT_RDWR);
+    close(sd);
+}
+
+int hostname_to_ip(const char* const hostname, char *ip)
+{
+    struct addrinfo hints, *servinfo; //, *p;
+    struct sockaddr_in *h;
+    int rv, ret = 0;
+
+    memset(&hints, 0, sizeof hints);
+    hints.ai_family = AF_UNSPEC; // use AF_INET6 to force IPv6
+    hints.ai_socktype = SOCK_STREAM;
+
+    if ( (rv = getaddrinfo( hostname , "http" , &hints , &servinfo)) != 0) 
+    {
+        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
+        ret = 1;
+    } else {
+        //// loop through all the results and connect to the first we can
+        //for(p = servinfo; p != NULL; p = p->ai_next)
+        //{
+        //    h = (struct sockaddr_in *) p->ai_addr;
+        //    strcpy(ip , inet_ntoa( h->sin_addr ) );
+        //}
+        h = (struct sockaddr_in *) servinfo->ai_addr;
+        strcpy(ip, inet_ntoa(h->sin_addr));
+    }
+
+    freeaddrinfo(servinfo); // all done with this structure
+    return ret;
+}
diff --git a/src/tcp.h b/src/tcp.h
new file mode 100644
index 0000000..248f941
--- /dev/null
+++ b/src/tcp.h
@@ -0,0 +1,8 @@
+#ifndef _TCP_H_
+#define _TCP_H_
+
+int hostname_to_ip(const char* const hostname, char *ip);
+int tcp_connect(const char* const host, const int port);
+void tcp_close(const int sd);
+
+#endif
diff --git a/src/tls.c b/src/tls.c
new file mode 100644
index 0000000..6b81bd3
--- /dev/null
+++ b/src/tls.c
@@ -0,0 +1,165 @@
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "tcp.h"
+#include "tls.h"
+
+int sd = 0;
+gnutls_session_t session;
+gnutls_certificate_credentials_t xcred;
+
+void tls_init(void)
+{
+    gnutls_global_init();
+    gnutls_certificate_allocate_credentials(&xcred);
+    //gnutls_certificate_set_x509_trust_file(xcred, cafile, GNUTLS_X509_FMT_PEM);
+    //gnutls_certificate_set_verify_function(xcred, _verify_certificate_callback);
+    //gnutls_certificate_set_x509_key_file (xcred, "cert.pem", "key.pem", GNUTLS_X509_FMT_PEM);
+    gnutls_init(&session, GNUTLS_CLIENT);
+
+    // Only usefull if accessing a virtual hosting server
+    //gnutls_session_set_ptr(session, (void *) "my_host_name");
+    //gnutls_server_name_set(session, GNUTLS_NAME_DNS, "my_host_name", strlen("my_host_name"));
+
+    gnutls_set_default_priority(session);
+    gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, xcred);
+}
+
+int tls_connect(const char* const host, const unsigned short port)
+{
+    int ret;
+    sd = tcp_connect(host, port);
+    if (sd < 0) {
+        tls_clean();
+        exit(101);
+    }
+
+    gnutls_transport_set_int(session, sd);
+    gnutls_handshake_set_timeout(session, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
+
+    /* Perform the TLS handshake */
+    do {
+        ret = gnutls_handshake(session);
+    }
+    while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+
+    if (ret < 0) {
+        fprintf(stderr, "TLS Handshake failed\n");
+        gnutls_perror(ret);
+        tls_clean();
+        exit(102);
+    } else {
+        char *desc;
+
+        desc = gnutls_session_get_desc(session);
+        printf("# TLS Session info: %s\n", desc);
+        gnutls_free(desc);
+    }
+
+    int sf = fcntl (sd, F_GETFL, 0);
+    if (sf == -1) {
+        perror("fcntl get");
+        return -1;
+    }
+    fcntl(sd, F_SETFL, sf | O_NONBLOCK);
+    if (sf == -1) {
+        perror("fcntl set");
+        return -1;
+    }
+
+    return sd;
+}
+
+int tls_read(int (*input_handler_callback)(const char* const sbuf, const int len))
+{
+    char sbuf[512]; /* string buffer */
+    int len = 0;  /* len read from the server */
+    len = gnutls_record_recv(session, sbuf, sizeof(sbuf));
+    if (len == 0) {
+        printf("Server has closed the TLS connection\n");
+        tls_clean();
+        return 1;
+    } else if (len < 0 && gnutls_error_is_fatal(len) == 0) {
+        fprintf(stderr, "*** Warning: %s\n", gnutls_strerror(len));
+        return 3;
+    } else if (len < 0) {
+        fprintf(stderr, "*** Error: %s\n", gnutls_strerror(len));
+        tls_clean();
+        return 2;
+    } else {
+        if (input_handler_callback(sbuf, len) != 0) {
+            return 0;
+        }
+    }
+    return 4;
+}
+
+void tls_send(char *buf, int len)
+{
+    gnutls_record_send(session, buf, len);
+}
+
+void tls_close(void) {
+    gnutls_bye(session, GNUTLS_SHUT_RDWR);
+    tls_clean();
+}
+
+void tls_clean(void)
+{
+    tcp_close(sd);
+    gnutls_deinit(session);
+    gnutls_certificate_free_credentials(xcred);
+    gnutls_global_deinit();
+}
+
+/* This function will verify the peer's certificate, and check
+ * if the hostname matches, as well as the activation, expiration dates. */
+//static int _verify_certificate_callback(gnutls_session_t session)
+//{
+//    unsigned int status;
+//    int ret, type;
+//    const char *hostname;
+//    gnutls_datum_t out;
+//
+//    /* read hostname */
+//    hostname = gnutls_session_get_ptr(session);
+//
+//    /* This verification function uses the trusted CAs in the credentials
+//     * structure. So you must have installed one or more CA certificates. */
+//    gnutls_typed_vdata_st data[2];
+//
+//    memset(data, 0, sizeof(data));
+//
+//    data[0].type = GNUTLS_DT_DNS_HOSTNAME;
+//    data[0].data = (void*)hostname;
+//
+//    data[1].type = GNUTLS_DT_KEY_PURPOSE_OID;
+//    data[1].data = (void*)GNUTLS_KP_TLS_WWW_SERVER;
+//
+//    ret = gnutls_certificate_verify_peers(session, data, 2, &status);
+//    if (ret < 0) {
+//        printf("Error\n");
+//        return GNUTLS_E_CERTIFICATE_ERROR;
+//    }
+//
+//    type = gnutls_certificate_type_get(session);
+//
+//    ret = gnutls_certificate_verification_status_print(status, type, &out, 0);
+//    if (ret < 0) {
+//        printf("Error\n");
+//        return GNUTLS_E_CERTIFICATE_ERROR;
+//    }
+//
+//    printf("%s", out.data);
+//
+//    gnutls_free(out.data);
+//
+//    if (status != 0)        /* Certificate is not trusted */
+//        return GNUTLS_E_CERTIFICATE_ERROR;
+//
+//    /* notify gnutls to continue handshake normally */
+//    return 0;
+//}
diff --git a/src/tls.h b/src/tls.h
new file mode 100644
index 0000000..ff7998d
--- /dev/null
+++ b/src/tls.h
@@ -0,0 +1,13 @@
+#ifndef _TLS_H_
+#define _TLS_H_
+
+#include <gnutls/gnutls.h>
+
+void tls_init(void);
+int tls_connect(const char* const host, const unsigned short port);
+int tls_read(int (*input_handler_callback)(const char* const sbuf, const int len));
+void tls_send(char *buf, int len);
+void tls_close(void);
+void tls_clean(void);
+
+#endif