$ git clone https://tcclient.ion.nu/tc_client.git
commit 351b4f77fab643c8b2823540b958d6b596bc5c11
Author: Alicia <...>
Date:   Tue Apr 7 06:49:00 2015 +0200

    Version 0.3

diff --git a/ChangeLog b/ChangeLog
index acf024e..79cf358 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,2 +1,6 @@
+0.3:
+Handle 'joins', 'join', 'nick' and 'quit' messages from the server (contributed by Jade)
+Moved the AMF message writing functions into a separate source file.
+Added irchack.c, a simple application that acts as an IRC server mirroring a tinychat channel using tc_client.
 0.2:
 Added support for non-ascii characters (sent as ISO-8859-1 to/from the server)
diff --git a/Makefile b/Makefile
index c2adc8d..cc34c31 100644
--- a/Makefile
+++ b/Makefile
@@ -1,12 +1,12 @@
-VERSION=0.2
+VERSION=0.3
 CFLAGS=-g3 -Wall $(shell curl-config --cflags)
 LIBS=-g3 $(shell curl-config --libs)
 
-tc_client: client.o amfparser.o rtmp.o numlist.o
+tc_client: client.o amfparser.o rtmp.o numlist.o amfwriter.o
  $(CC) $(LDFLAGS) $^ $(LIBS) -o $@
 
 clean:
- rm -f client.o amfparser.o rtmp.o numlist.o tc_client
+ rm -f client.o amfparser.o rtmp.o numlist.o amfwriter.o tc_client
 
 tarball:
- tar -cJf tc_client-$(VERSION).tar.xz --transform='s|^|tc_client-$(VERSION)/|' Makefile client.c amfparser.c rtmp.c numlist.c amfparser.h rtmp.h numlist.h LICENSE README ChangeLog
+ tar -cJf tc_client-$(VERSION).tar.xz --transform='s|^|tc_client-$(VERSION)/|' Makefile client.c amfparser.c rtmp.c numlist.c amfwriter.c amfparser.h rtmp.h numlist.h amfwriter.h LICENSE README ChangeLog irchack.c
diff --git a/README b/README
index d2293ce..9c4cb28 100644
--- a/README
+++ b/README
@@ -1,11 +1,17 @@
-This is a pre-release of tc_client, it's still not ready for a release beyond sharing it with a small group, most significantly because the channel is hardcoded.
-
-Some other notes about tc_client in its current state (things that should be fixed):
+Some notes about tc_client in its current state (things that should be fixed):
 *there is no real user interface, to send a message you just type it and it might get cut off by incoming messages, it'll be ugly.
-*PMs can be sent by /msg <nickname> <message> and replies appear similarly 
-*you can't log in with an account and thus not to mod stuff, I don't know how this works protocol-wise
+*PMs can be sent by /msg <nickname> <message> and replies appear similarly. Note however that PMs sent this way are currently sent to everyone in the channel, but the flash client ignores them.
+*you can't log in with an account and thus not do mod stuff, I don't know how this works protocol-wise
 
 Some things that will probably never change:
 *tc_client can't view people's webcams or listen to mics
 *tc_client can't stream/broadcast your webcam/mic
 *tc_client probably won't ever play youtube videos (although you can see when one is launched and get the video ID because it is sent as a message and can then open it yourself)
+
+Current commands sent by the TC servers that tc_client doesn't know how to handle:
+notice
+topic
+joinsdone
+avons (list of people currently on cam)
+pros
+oper
diff --git a/amfwriter.c b/amfwriter.c
index 3fc07dc..38845f3 100644
--- a/amfwriter.c
+++ b/amfwriter.c
@@ -1,6 +1,6 @@
 /*
     tc_client, a simple non-flash client for tinychat(.com)
-    Copyright (C) 2014-2015  alicia@ion.nu
+    Copyright (C) 2014  alicia@ion.nu
 
     This program is free software: you can redistribute it and/or modify
     it under the terms of the GNU Affero General Public License as published by
@@ -18,89 +18,119 @@
 #include <string.h>
 #include <stdint.h>
 #include <unistd.h>
-#include "endian.h"
+#include <arpa/inet.h>
 #include "rtmp.h"
 #include "amfwriter.h"
 
-void amfinit(struct rtmp* msg, unsigned int chunkid)
+extern unsigned int flip(unsigned int bits, int bytecount);
+
+void amfinit(struct amfmsg* msg)
+{
+  msg->len=0;
+  msg->buf=malloc(sizeof(struct rtmph));
+}
+
+void amfsend(struct amfmsg* msg, int sock)
 {
-  msg->type=RTMP_AMF0;
-  msg->chunkid=chunkid;
-  msg->length=0;
-  msg->msgid=0;
+  struct rtmph* head=(struct rtmph*)msg->buf;
+  head->streamid=3;
+  head->fmt=0;
+  head->timestamp=0; // Time is irrelevant
+  head->length=flip(msg->len, 3);
+  head->type=20;
+  head->msgid=0;
+  // Send 128 bytes at a time separated by 0xc3 (because apparently that's something RTMP requires)
+// printf("Writing %i-byte message\n", msg->len);
+  write(sock, msg->buf, sizeof(struct rtmph));
+  unsigned char* pos=msg->buf+sizeof(struct rtmph);
+  while(msg->len>0)
+  {
+    int w;
+    if(msg->len>128)
+    {
+      w=write(sock, pos, 128);
+      w+=write(sock, "\xc3", 1);
+      msg->len-=128;
+    }else{
+      w=write(sock, pos, msg->len);
+      msg->len=0;
+    }
+// printf("Wrote %i bytes\n", w);
+    pos+=128;
+  }
+  free(msg->buf);
   msg->buf=0;
 }
 
-void amfnum(struct rtmp* msg, double v)
+void amfnum(struct amfmsg* msg, double v)
 {
-  unsigned long long* endian=(void*)&v;
-  *endian=be64(*endian);
-  int offset=msg->length;
-  msg->length+=1+sizeof(double);
-  msg->buf=realloc(msg->buf, msg->length);
+  int offset=sizeof(struct rtmph)+msg->len;
+  msg->len+=1+sizeof(double);
+  msg->buf=realloc(msg->buf, sizeof(struct rtmph)+msg->len);
   unsigned char* type=msg->buf+offset;
   type[0]='\x00';
-  memcpy(msg->buf+offset+1, &v, sizeof(v));
+  double* value=(double*)(msg->buf+offset+1);
+  *value=v;
 }
 
-void amfbool(struct rtmp* msg, char v)
+void amfbool(struct amfmsg* msg, char v)
 {
-  int offset=msg->length;
-  msg->length+=2;
-  msg->buf=realloc(msg->buf, msg->length);
+  int offset=sizeof(struct rtmph)+msg->len;
+  msg->len+=2;
+  msg->buf=realloc(msg->buf, sizeof(struct rtmph)+msg->len);
   unsigned char* x=msg->buf+offset;
   x[0]='\x01';
   x[1]=!!v;
 }
 
-void amfstring(struct rtmp* msg, const char* string)
+void amfstring(struct amfmsg* msg, char* string)
 {
   int len=strlen(string);
-  int offset=msg->length;
-  msg->length+=3+len;
-  msg->buf=realloc(msg->buf, msg->length);
+  int offset=sizeof(struct rtmph)+msg->len;
+  msg->len+=3+len;
+  msg->buf=realloc(msg->buf, sizeof(struct rtmph)+msg->len);
   unsigned char* type=msg->buf+offset;
   type[0]='\x02';
   uint16_t* strlength=(uint16_t*)(msg->buf+offset+1);
-  *strlength=be16(len);
+  *strlength=htons(len);
   memcpy(msg->buf+offset+3, string, len);
 }
 
-void amfobjstart(struct rtmp* msg)
+void amfobjstart(struct amfmsg* msg)
 {
-  int offset=msg->length;
-  msg->length+=1;
-  msg->buf=realloc(msg->buf, msg->length);
+  int offset=sizeof(struct rtmph)+msg->len;
+  msg->len+=1;
+  msg->buf=realloc(msg->buf, sizeof(struct rtmph)+msg->len);
   unsigned char* type=msg->buf+offset;
   type[0]='\x03';
 }
 
-void amfobjitem(struct rtmp* msg, char* name)
+void amfobjitem(struct amfmsg* msg, char* name)
 {
   int len=strlen(name);
-  int offset=msg->length;
-  msg->length+=2+len;
-  msg->buf=realloc(msg->buf, msg->length);
+  int offset=sizeof(struct rtmph)+msg->len;
+  msg->len+=2+len;
+  msg->buf=realloc(msg->buf, sizeof(struct rtmph)+msg->len);
   uint16_t* strlength=(uint16_t*)(msg->buf+offset);
-  *strlength=be16(len);
+  *strlength=htons(len);
   memcpy(msg->buf+offset+2, name, len);
 }
 
-void amfobjend(struct rtmp* msg)
+void amfobjend(struct amfmsg* msg)
 {
   amfobjitem(msg, "");
-  int offset=msg->length;
-  msg->length+=1;
-  msg->buf=realloc(msg->buf, msg->length);
+  int offset=sizeof(struct rtmph)+msg->len;
+  msg->len+=1;
+  msg->buf=realloc(msg->buf, sizeof(struct rtmph)+msg->len);
   unsigned char* type=msg->buf+offset;
   type[0]='\x09';
 }
 
-void amfnull(struct rtmp* msg)
+void amfnull(struct amfmsg* msg)
 {
-  int offset=msg->length;
-  msg->length+=1;
-  msg->buf=realloc(msg->buf, msg->length);
+  int offset=sizeof(struct rtmph)+msg->len;
+  msg->len+=1;
+  msg->buf=realloc(msg->buf, sizeof(struct rtmph)+msg->len);
   unsigned char* type=msg->buf+offset;
   type[0]='\x05';
 }
diff --git a/amfwriter.h b/amfwriter.h
index 62f735e..329b4ae 100644
--- a/amfwriter.h
+++ b/amfwriter.h
@@ -1,6 +1,6 @@
 /*
     tc_client, a simple non-flash client for tinychat(.com)
-    Copyright (C) 2014-2015  alicia@ion.nu
+    Copyright (C) 2014  alicia@ion.nu
 
     This program is free software: you can redistribute it and/or modify
     it under the terms of the GNU Affero General Public License as published by
@@ -15,13 +15,18 @@
     along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
-extern void amfinit(struct rtmp* msg, unsigned int streamid);
-extern void amfnum(struct rtmp* msg, double v);
-extern void amfbool(struct rtmp* msg, char v);
-extern void amfstring(struct rtmp* msg, const char* string);
-extern void amfobjstart(struct rtmp* msg);
-extern void amfobjitem(struct rtmp* msg, char* name);
-extern void amfobjend(struct rtmp* msg);
-extern void amfnull(struct rtmp* msg);
+struct amfmsg
+{
+  unsigned int len;
+  unsigned char* buf;
+};
 
-#define amfsend(rtmp,sock) rtmp_send(sock,rtmp); free((rtmp)->buf); (rtmp)->buf=0
+extern void amfinit(struct amfmsg* msg);
+extern void amfsend(struct amfmsg* msg, int sock);
+extern void amfnum(struct amfmsg* msg, double v);
+extern void amfbool(struct amfmsg* msg, char v);
+extern void amfstring(struct amfmsg* msg, char* string);
+extern void amfobjstart(struct amfmsg* msg);
+extern void amfobjitem(struct amfmsg* msg, char* name);
+extern void amfobjend(struct amfmsg* msg);
+extern void amfnull(struct amfmsg* msg);
diff --git a/client.c b/client.c
index 0f95878..8497b16 100644
--- a/client.c
+++ b/client.c
@@ -27,12 +27,7 @@
 #include "rtmp.h"
 #include "amfparser.h"
 #include "numlist.h"
-
-struct amfmsg
-{
-  unsigned int len;
-  unsigned char* buf;
-};
+#include "amfwriter.h"
 
 unsigned int flip(unsigned int bits, int bytecount)
 {
@@ -47,117 +42,6 @@ unsigned int flip(unsigned int bits, int bytecount)
   return ret;
 }
 
-void amfinit(struct amfmsg* msg)
-{
-  msg->len=0;
-  msg->buf=malloc(sizeof(struct rtmph));
-}
-
-void amfsend(struct amfmsg* msg, int sock)
-{
-  struct rtmph* head=(struct rtmph*)msg->buf;
-  head->streamid=3;
-  head->fmt=0;
-  head->timestamp=0; // Time is irrelevant
-  head->length=flip(msg->len, 3);
-  head->type=20;
-  head->msgid=0;
-  // Send 128 bytes at a time separated by 0xc3 (because apparently that's something RTMP requires)
-// printf("Writing %i-byte message\n", msg->len);
-  write(sock, msg->buf, sizeof(struct rtmph));
-  unsigned char* pos=msg->buf+sizeof(struct rtmph);
-  while(msg->len>0)
-  {
-    int w;
-    if(msg->len>128)
-    {
-      w=write(sock, pos, 128);
-      w+=write(sock, "\xc3", 1);
-      msg->len-=128;
-    }else{
-      w=write(sock, pos, msg->len);
-      msg->len=0;
-    }
-// printf("Wrote %i bytes\n", w);
-    pos+=128;
-  }
-  free(msg->buf);
-  msg->buf=0;
-}
-
-void amfnum(struct amfmsg* msg, double v)
-{
-  int offset=sizeof(struct rtmph)+msg->len;
-  msg->len+=1+sizeof(double);
-  msg->buf=realloc(msg->buf, sizeof(struct rtmph)+msg->len);
-  unsigned char* type=msg->buf+offset;
-  type[0]='\x00';
-  double* value=(double*)(msg->buf+offset+1);
-  *value=v;
-}
-
-void amfbool(struct amfmsg* msg, char v)
-{
-  int offset=sizeof(struct rtmph)+msg->len;
-  msg->len+=2;
-  msg->buf=realloc(msg->buf, sizeof(struct rtmph)+msg->len);
-  unsigned char* x=msg->buf+offset;
-  x[0]='\x01';
-  x[1]=!!v;
-}
-
-void amfstring(struct amfmsg* msg, char* string)
-{
-  int len=strlen(string);
-  int offset=sizeof(struct rtmph)+msg->len;
-  msg->len+=3+len;
-  msg->buf=realloc(msg->buf, sizeof(struct rtmph)+msg->len);
-  unsigned char* type=msg->buf+offset;
-  type[0]='\x02';
-  uint16_t* strlength=(uint16_t*)(msg->buf+offset+1);
-  *strlength=htons(len);
-  memcpy(msg->buf+offset+3, string, len);
-}
-
-void amfobjstart(struct amfmsg* msg)
-{
-  int offset=sizeof(struct rtmph)+msg->len;
-  msg->len+=1;
-  msg->buf=realloc(msg->buf, sizeof(struct rtmph)+msg->len);
-  unsigned char* type=msg->buf+offset;
-  type[0]='\x03';
-}
-
-void amfobjitem(struct amfmsg* msg, char* name)
-{
-  int len=strlen(name);
-  int offset=sizeof(struct rtmph)+msg->len;
-  msg->len+=2+len;
-  msg->buf=realloc(msg->buf, sizeof(struct rtmph)+msg->len);
-  uint16_t* strlength=(uint16_t*)(msg->buf+offset);
-  *strlength=htons(len);
-  memcpy(msg->buf+offset+2, name, len);
-}
-
-void amfobjend(struct amfmsg* msg)
-{
-  amfobjitem(msg, "");
-  int offset=sizeof(struct rtmph)+msg->len;
-  msg->len+=1;
-  msg->buf=realloc(msg->buf, sizeof(struct rtmph)+msg->len);
-  unsigned char* type=msg->buf+offset;
-  type[0]='\x09';
-}
-
-void amfnull(struct amfmsg* msg)
-{
-  int offset=sizeof(struct rtmph)+msg->len;
-  msg->len+=1;
-  msg->buf=realloc(msg->buf, sizeof(struct rtmph)+msg->len);
-  unsigned char* type=msg->buf+offset;
-  type[0]='\x05';
-}
-
 void b_read(int sock, void* buf, size_t len)
 {
   while(len>0)
@@ -254,7 +138,7 @@ int main(int argc, char** argv)
   setlocale(LC_ALL, "");
   if(argc<3)
    {
-    printf("Usage: %s <roomname> <nickname> [password]\n", argv[0]);
+    printf("Usage: %s <channelname> <nickname> [password]\n", argv[0]);
     return 1;
   }
   char* channel=argv[1];
@@ -299,10 +183,10 @@ int main(int argc, char** argv)
     amfstring(&amf, "tinyconf");
 
     amfobjitem(&amf, "flashVer");
-    amfstring(&amf, "LNX 11,2,202,424");
+    amfstring(&amf, "no flash");
 
     amfobjitem(&amf, "swfUrl");
-    amfstring(&amf, "http://tinychat.com/embed/Tinychat-11.1-1.0.0.0602.swf?version=1.0.0.0602/[[DYNAMIC]]/8");
+    amfstring(&amf, "no flash");
 
     amfobjitem(&amf, "tcUrl");
     char str[strlen("rtmp://:/tinyconf0")+strlen(server)+strlen(port)];
@@ -331,16 +215,16 @@ int main(int argc, char** argv)
 
     amfobjitem(&amf, "objectEncoding");
     amfnum(&amf, 0);
-    amfobjend(&amf);
-    amfstring(&amf, channel);
-    amfstring(&amf, "none");
-    amfstring(&amf, "show"); // This item is called roomtype in the same HTTP response that gives us the server (IP+port) to connect to
-    amfstring(&amf, "tinychat");
-    amfstring(&amf, "");
-    amfsend(&amf, sock);
-
-    unsigned char buf[2048];
-    int len=read(sock, buf, 2048);
+  amfobjend(&amf);
+  amfstring(&amf, channel);
+  amfstring(&amf, "none");
+  amfstring(&amf, "show"); // This item is called roomtype in the same HTTP response that gives us the server (IP+port) to connect to
+  amfstring(&amf, "tinychat");
+  amfstring(&amf, "");
+  amfsend(&amf, sock);
+
+  unsigned char buf[2048];
+  int len=read(sock, buf, 2048);
 //  printf("Received %i byte response\n", len);
 /* Debugging
   int f=open("output", O_WRONLY|O_CREAT|O_TRUNC, 0644);
@@ -348,56 +232,38 @@ int main(int argc, char** argv)
   close(f);
 */
 
-/* As they appear in the flash client:
-    char* colors[]={
-      "#1965b6,en",
-      "#32a5d9,en",
-      "#7db257,en",
-      "#a78901,en",
-      "#9d5bb5,en",
-      "#5c1a7a,en",
-      "#c53332,en",
-      "#821615,en",
-      "#a08f23,en",
-      "#487d21,en",
-      "#c356a3,en",
-      "#1d82eb,en",
-      "#919104,en",
-      "#00a990,en",
-      "#b9807f,en",
-      "#7bb224,en"
-    };
+/* Disabled for now
+  char* colors[]={ // Sorted like a rainbow
+    "#821615,en",
+    "#c53332,en",
+    "#a08f23,en",
+    "#a78901,en",
+    "#919104,en",
+    "#7bb224,en",
+    "#7db257,en",
+    "#487d21,en",
+    "#00a990,en",
+    "#32a5d9,en",
+    "#1d82eb,en",
+    "#1965b6,en",
+    "#5c1a7a,en",
+    "#9d5bb5,en",
+    "#c356a3,en",
+    "#b9807f,en"
+  };
+  unsigned int currentcolor=0;
 */
-    char* colors[]={ // Sorted like a rainbow
-      "#821615,en",
-      "#c53332,en",
-      "#a08f23,en",
-      "#a78901,en",
-      "#919104,en",
-      "#7bb224,en",
-      "#7db257,en",
-      "#487d21,en",
-      "#00a990,en",
-      "#32a5d9,en",
-      "#1d82eb,en",
-      "#1965b6,en",
-      "#5c1a7a,en",
-      "#9d5bb5,en",
-      "#c356a3,en",
-      "#b9807f,en"
-    };
-    unsigned int currentcolor=0;
 
 //  int outnum=2; (Debugging, number for output filenames)
-    struct pollfd pfd[2];
-    pfd[0].fd=0;
-    pfd[0].events=POLLIN;
-    pfd[0].revents=0;
-    pfd[1].fd=sock;
-    pfd[1].events=POLLIN;
-    pfd[1].revents=0;
-    while(1)
-    {
+  struct pollfd pfd[2];
+  pfd[0].fd=0;
+  pfd[0].events=POLLIN;
+  pfd[0].revents=0;
+  pfd[1].fd=sock;
+  pfd[1].events=POLLIN;
+  pfd[1].revents=0;
+  while(1)
+  {
     // Poll for input, very crude chat UI
     poll(pfd, 2, -1);
     if(pfd[0].revents) // Got input, send a privmsg command
@@ -415,10 +281,11 @@ int main(int argc, char** argv)
       amfnum(&amf, 0);
       amfnull(&amf);
       amfstring(&amf, msg);
-      // The alternating color thing is an unnecessary but fun feature
-      amfstring(&amf, colors[currentcolor%16]);
+      // amfstring(&amf, colors[currentcolor%16]); // Alternating colors, fun but annoying in the long run
+      amfstring(&amf, "#000000,en");
+// TODO: for PMs, add a string like "n<numeric ID>-<nick>" to avoid broadcasting private messages to everyone connected (although the flash client ignores PMs sent to others)
       amfsend(&amf, sock);
-      ++currentcolor;
+      // ++currentcolor;
       free(msg);
       continue;
     }
@@ -470,7 +337,7 @@ int main(int argc, char** argv)
         amfsend(&amf, sock);
       }
       // Items for privmsg: 0=cmd, 2=channel, 3=msg, 4=color/lang, 5=nick
-      if(amfin->itemcount>0 && amfin->items[0].type==AMF_STRING && amf_comparestrings_c(&amfin->items[0].string, "privmsg") && amfin->items[3].type==AMF_STRING && amfin->items[5].type==AMF_STRING)
+      else if(amfin->itemcount>5 && amfin->items[0].type==AMF_STRING && amf_comparestrings_c(&amfin->items[0].string, "privmsg") && amfin->items[3].type==AMF_STRING && amfin->items[5].type==AMF_STRING)
       {
         wchar_t* msg=fromnumlist(amfin->items[3].string.string);
         // Timestamps, e.g. "[hh:mm] name: message"
@@ -478,7 +345,52 @@ int main(int argc, char** argv)
         struct tm* t=localtime(&timestamp);
         printf("[%02i:%02i] %s: %ls\n", t->tm_hour, t->tm_min, amfin->items[5].string.string, msg);
         free(msg);
+        fflush(stdout);
+      }
+      // users on channel entry.  there's also a "joinsdone" command for some reason...
+      else if(amfin->itemcount>3 && amfin->items[0].type==AMF_STRING && amf_comparestrings_c(&amfin->items[0].string, "joins"))
+      {
+        printf("Currently online: ");
+        int i;
+        for(i = 3; i < amfin->itemcount-1; i+=2)
+        {
+          // a "numeric" id precedes each nick, i.e. i is the id, i+1 is the nick
+          if(amfin->items[i+1].type==AMF_STRING)
+          {
+            printf("%s%s", (i==3?"":", "), amfin->items[i+1].string.string);
+          }
+        }
+        printf("\n");
+        fflush(stdout);
+      }
+      // join
+      else if(amfin->itemcount==4 && amfin->items[0].type==AMF_STRING && amf_comparestrings_c(&amfin->items[0].string, "join") && amfin->items[3].type==AMF_STRING)
+      {
+        // Timestamps, e.g. "[hh:mm] name: message"
+        time_t timestamp=time(0);
+        struct tm* t=localtime(&timestamp);
+        printf("[%02i:%02i] %s entered the channel\n", t->tm_hour, t->tm_min, amfin->items[3].string.string);
+        fflush(stdout);
+      }
+      // part
+      else if(amfin->itemcount==4 && amfin->items[0].type==AMF_STRING && amf_comparestrings_c(&amfin->items[0].string, "quit") && amfin->items[3].type==AMF_STRING)
+      {
+        // Timestamps, e.g. "[hh:mm] name: message"
+        time_t timestamp=time(0);
+        struct tm* t=localtime(&timestamp);
+        printf("[%02i:%02i] %s left the channel\n", t->tm_hour, t->tm_min, amfin->items[2].string.string);
+        fflush(stdout);
+      }
+      // nick
+      else if(amfin->itemcount==5 && amfin->items[0].type==AMF_STRING && amf_comparestrings_c(&amfin->items[0].string, "nick") && amfin->items[2].type==AMF_STRING && amfin->items[3].type==AMF_STRING)
+      {
+        // Timestamps, e.g. "[hh:mm] name: message"
+        time_t timestamp=time(0);
+        struct tm* t=localtime(&timestamp);
+        printf("[%02i:%02i] %s changed nickname to %s\n", t->tm_hour, t->tm_min, amfin->items[2].string.string, amfin->items[3].string.string);
+        fflush(stdout);
       }
+      // else{printf("Unknown command...\n"); printamf(amfin);} // (Debugging)
       amf_free(amfin);
     }
 //    ++outnum; (Debugging)
diff --git a/irchack.c b/irchack.c
index fa527f8..b3ab620 100644
--- a/irchack.c
+++ b/irchack.c
@@ -1,7 +1,6 @@
 /*
     irchack, a simple application to reuse IRC clients as user interfaces for tc_client
-    Copyright (C) 2014-2015  alicia@ion.nu
-    Copyright (C) 2015  Jade Lea
+    Copyright (C) 2014  alicia@ion.nu
 
     This program is free software: you can redistribute it and/or modify
     it under the terms of the GNU Affero General Public License as published by
@@ -21,177 +20,68 @@
 #include <string.h>
 #include <stdlib.h>
 #include <stdio.h>
-#include <sys/socket.h>
-#include <ctype.h>
-#include <signal.h>
 
-#ifdef __ANDROID__
-// Android has no dprintf, so we make our own
-#include <stdarg.h>
-size_t dprintf(int fd, const char* fmt, ...)
+int main()
 {
-  va_list va;
-  va_start(va, fmt);
-  int len=vsnprintf(0, 0, fmt, va);
-  va_end(va);
-  char buf[len+1];
-  va_start(va, fmt);
-  vsnprintf(buf, len+1, fmt, va);
-  va_end(va);
-  buf[len]=0;
-  write(fd, buf, len);
-  return len;
-}
-#endif
-
-// ANSI colors and their IRC equivalents
-struct color{const char* ansi; const char* irc;};
-struct color colortable[]={
-  {"31",   "\x03""05"},
-  {"31;1", "\x03""04"},
-  {"33",   "\x03""07"},
-  {"33",   "\x03""07"},
-  {"33;1", "\x03""08"},
-  {"32;1", "\x03""09"},
-  {"32;1", "\x03""09"},
-  {"32",   "\x03""03"},
-  {"36",   "\x03""10"},
-  {"34;1", "\x03""12"},
-  {"34;1", "\x03""12"},
-  {"34",   "\x03""02"},
-  {"35",   "\x03""06"},
-  {"35;1", "\x03""13"},
-  {"35;1", "\x03""13"},
-  {"35;1", "\x03""13"}
-};
-
-const char* findcolor_irc(const char* ansi)
-{
-  int len;
-  for(len=0; ansi[len]&&ansi[len]!='m'; ++len);
-  int i;
-  for(i=0; i<16; ++i)
-  {
-    if(!strncmp(colortable[i].ansi, ansi, len) && !colortable[i].ansi[len])
-    {
-      return colortable[i].irc;
-    }
-  }
-  return 0;
-}
-
-int findcolor_ansi(char* irc, char** end)
-{
-  char color[4];
-  strncpy(color, irc, 3);
-  color[3]=0;
-  if(!isdigit(color[1])){*end=&irc[1]; return -1;}
-  if(!isdigit(color[2])){*end=&irc[2]; color[2]=color[1]; color[1]='0';}else{*end=&irc[3];}
-  int i;
-  for(i=0; i<16; ++i)
-  {
-    if(!strcmp(colortable[i].irc, color))
-    {
-      return i;
-    }
-  }
-  return -1;
-}
-
-extern char session(int sock, const char* nick, const char* channel, const char* pass, const char* acc_user, const char* acc_pass);
-
-int main(int argc, char** argv)
-{
-  int port=(argc>1?atoi(argv[1]):6667);
   struct sockaddr_in addr;
   memset(&addr, 0, sizeof(addr));
   addr.sin_family=AF_INET;
-  addr.sin_addr.s_addr=htonl(0x7f000001); // 127.0.0.1
-  addr.sin_port=htons(port);
+  addr.sin_addr.s_addr=0;
+  addr.sin_port=htons(6667);
   int lsock=socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
   if(bind(lsock, (struct sockaddr*)&addr, sizeof(addr))){perror("bind"); return 1;}
   listen(lsock, 1);
-  printf("Done! Open an IRC client and connect to localhost on port %i\n", port);
-  signal(SIGCHLD, SIG_IGN);
-  int sock;
-  while((sock=accept(lsock, 0, 0))>-1)
+  int sock=accept(lsock, 0, 0);
+  close(lsock);
+  char buf[2048];
+  char* nick=0;
+  char* channel=0;
+  char* pass=0;
+  int len;
+  while(!nick || !channel) // Init loop, wait for USER, NICK and JOIN, well not USER
   {
-    if(fork()){continue;}
-    char buf[2048];
-    char* nick=0;
-    char* channel=0;
-    char* pass=0;
-    char* acc_user=0;
-    char* acc_pass=0;
-    int len;
+    len=0;
     while(1)
     {
-      len=0;
-      int r;
-      while(len<2047)
-      {
-        if((r=read(sock, &buf[len], 1))!=1 || buf[len]=='\r' || buf[len]=='\n'){break;}
-        ++len;
-      }
-      if(r!=1){break;}
-      buf[len]=0;
-      if(!strncmp(buf, "USER ", 5))
-      {
-        acc_user=&buf[5];
-        char* end=strchr(acc_user, ' ');
-        if(end){end[0]=0;}
-        acc_user=strdup(acc_user);
-      }
-      else if(!strncmp(buf, "PASS ", 5))
-      {
-        acc_pass=&buf[5];
-        if(acc_pass[0]==':'){acc_pass=&acc_pass[1];}
-        acc_pass=strdup(acc_pass);
-      }
-      else if(!strncmp(buf, "NICK ", 5))
-      {
-        char* newnick=&buf[5];
-        if(newnick[0]==':'){newnick=&nick[1];}
-        if(!nick)
-        {
-          dprintf(sock, ":irchack 001 %s :Welcome\n", newnick);
-        }else{
-          dprintf(sock, ":%s!user@host NICK :%s\n", nick, newnick);
-        }
-        free(nick);
-        nick=strdup(newnick);
-      }
-      else if(nick && !strncmp(buf, "JOIN ", 5))
-      {
-        free(channel);
-        channel=&buf[5];
-        if(channel[0]==':'){channel=&channel[1];}
-        free(pass);
-        pass=strchr(channel, ' ');
-        if(pass)
-        {
-          pass[0]=0;
-          pass=&pass[1];
-          if(pass[0]==':'){pass=strdup(&pass[1]);}
-        }
-        if(channel[0]=='#'){channel=&channel[1];}
-        channel=strdup(channel);
-        if(!session(sock, nick, channel, pass, acc_user, acc_pass)){break;}
-      }
-      else if(!strncmp(buf, "PING ", 5))
+      if(read(sock, &buf[len], 1)!=1 || buf[len]=='\r' || buf[len]=='\n'){break;}
+      ++len;
+    }
+    buf[len]=0;
+    if(!strncmp(buf, "NICK ", 5))
+    {
+      char* newnick=&buf[5];
+      if(newnick[0]==':'){newnick=&nick[1];}
+      if(!nick)
       {
-        dprintf(sock, ":irchack PONG %s\n", &buf[5]);
+        dprintf(sock, ":irchack 001 %s :Welcome\n", newnick);
+      }else{
+        dprintf(sock, ":%s!user@host NICK :%s\n", nick, newnick);
       }
+      free(nick);
+      nick=strdup(newnick);
+    }
+    else if(!strncmp(buf, "JOIN ", 5))
+    {
+      free(channel);
+      channel=&buf[5];
+      if(channel[0]==':'){channel=&channel[1];}
+      free(pass);
+      pass=strchr(channel, ' ');
+      if(pass)
+      {
+        pass[0]=0;
+        pass=&pass[1];
+        if(pass[0]==':'){pass=strdup(&pass[1]);}
+      }
+      if(channel[0]=='#'){channel=&channel[1];}
+      channel=strdup(channel);
+    }
+    else if(!strncmp(buf, "PING ", 5))
+    {
+      dprintf(sock, ":irchack PONG %s\n", &buf[5]);
     }
-    shutdown(sock, SHUT_RDWR);
-    _exit(0);
   }
-  close(lsock);
-  return 0;
-}
-
-char session(int sock, const char* nick, const char* channel, const char* pass, const char* acc_user, const char* acc_pass)
-{
+  // We now have what we need to launch tc_client
   printf("Nick: %s\n", nick);
   printf("Channel: %s\n", channel);
   printf("Password: %s\n", pass);
@@ -205,17 +95,13 @@ char session(int sock, const char* nick, const char* channel, const char* pass,
     close(tc_out[0]);
     dup2(tc_in[0], 0);
     dup2(tc_out[1], 1);
-    if(acc_user && acc_pass)
-    {
-      execl("./tc_client", "./tc_client", "-u", acc_user, "-p", acc_pass, channel, nick, pass, (char*)0);
-    }else{
-      execl("./tc_client", "./tc_client", channel, nick, pass, (char*)0);
-    }
-    perror("Failed to exec tc_client");
+    execl("./tc_client", "./tc_client", channel, nick, pass, (char*)0);
+    printf("Failed to exec tc_client\n");
     _exit(1);
   }
   close(tc_in[0]);
   close(tc_out[1]);
+  dprintf(tc_in[1], ":%s!user@host JOIN %s\n", nick, channel);
   struct pollfd pfd[2];
   pfd[0].fd=tc_out[0];
   pfd[0].events=POLLIN;
@@ -223,9 +109,6 @@ char session(int sock, const char* nick, const char* channel, const char* pass,
   pfd[1].fd=sock;
   pfd[1].events=POLLIN;
   pfd[1].revents=0;
-  char buf[2048];
-  int len;
-  char joins=0;
   while(1)
   {
     poll(pfd, 2, -1);
@@ -233,13 +116,11 @@ char session(int sock, const char* nick, const char* channel, const char* pass,
     {
       pfd[0].revents=0;
       len=0;
-      int r;
-      while(len<2047)
+      while(1)
       {
-        if((r=read(tc_out[0], &buf[len], 1))!=1 || buf[len]=='\r' || buf[len]=='\n'){break;}
+        if(read(tc_out[0], &buf[len], 1)!=1 || buf[len]=='\r' || buf[len]=='\n'){break;}
         ++len;
       }
-      if(r!=1){break;}
       buf[len]=0;
 printf("Got from tc_client: '%s'\n", buf);
       if(!strncmp(buf, "Currently online: ", 18))
@@ -254,63 +135,10 @@ printf("Got from tc_client: '%s'\n", buf);
           user=next;
         }
         write(sock, "\n", 1);
-        joins=1;
-        continue;
-      }
-      else if(joins)
-      {
         dprintf(sock, ":irchack 366 %s #%s :End of /NAMES list.\n", nick, channel);
-        joins=0;
-      }
-      if(!strncmp(buf, "Room topic: ", 12))
-      {
-        dprintf(sock, ":irchack 332 %s #%s :%s\n", nick, channel, &buf[12]);
-        continue;
-      }
-      if(!strcmp(buf, "Password required"))
-      {
-        dprintf(sock, ":irchack 475 %s :Cannot join %s without the correct password\n", channel, channel);
-        close(tc_in[1]);
-        close(tc_out[0]);
-        return 1;
-      }
-      if(!strncmp(buf, "Guest ID: ", 10))
-      {
-        dprintf(sock, ":%s!user@host NICK :guest-%s\n", nick, &buf[10]);
-        continue;
-      }
-      if(!strncmp(buf, "No such nick: ", 14))
-      {
-        dprintf(sock, ":irchack 401 %s %s :No such nick/channel\n", nick, &buf[14]);
-        continue;
-      }
-      char* space=strchr(buf, ' ');
-      if(space && !strcmp(space, " is a moderator."))
-      {
-        space[0]=0;
-        dprintf(sock, ":irchack MODE #%s +o %s\n", channel, buf);
-        continue;
-      }
-      if(space && !strcmp(space, " is no longer a moderator."))
-      {
-        space[0]=0;
-        dprintf(sock, ":irchack MODE #%s -o %s\n", channel, buf);
         continue;
       }
       if(buf[0]!='['){continue;} // Beyond this we only care about timestamped lines
-      // Translate ANSI escape codes to IRC color code instead
-      char* ansi;
-      const char* color="";
-      while((ansi=strstr(buf, "\x1b[")))
-      {
-        int len;
-        for(len=0; ansi[len]&&ansi[len]!='m'; ++len);
-        if(ansi[len]=='m'){++len;}
-        const char* c=findcolor_irc(&ansi[2]);
-        if(c){color=c;}
-        memmove(ansi, &ansi[len], strlen(&ansi[len])+1);
-      }
-
       char* name=strchr(buf, ' ');
       if(!name){continue;}
       name[0]=0;
@@ -326,11 +154,11 @@ printf("Got from tc_client: '%s'\n", buf);
           msg=strchr(&msg[5], ' ');
           if(!msg){continue;}
           msg=&msg[1];
-          dprintf(sock, ":%s!user@host PRIVMSG %s :%s%s\n", name, nick, color, msg);
+          dprintf(sock, ":%s!user@host PRIVMSG %s :%s\n", name, nick, msg);
         }else{ // Regular channel message
-          dprintf(sock, ":%s!user@host PRIVMSG #%s :%s%s\n", name, channel, color, msg);
+          dprintf(sock, ":%s!user@host PRIVMSG #%s :%s\n", name, channel, msg);
         }
-      }else{ // action, parse the actions and send them as JOINs, NICKs and QUITs etc. instead
+      }else{ // action, TODO: parse the actions and send them as JOINs, NICKs and QUITs etc. instead
         msg[0]=0;
         msg=&msg[1];
         if(!strcmp(msg, "entered the channel"))
@@ -355,13 +183,13 @@ printf("Got from tc_client: '%s'\n", buf);
     {
       pfd[1].revents=0;
       len=0;
-      int r;
-      while(len<2047)
+      while(1)
       {
-        if((r=read(sock, &buf[len], 1))!=1 || buf[len]=='\r' || buf[len]=='\n'){break;}
+        if(read(sock, &buf[len], 1)!=1 || buf[len]=='\r' || buf[len]=='\n'){break;}
         ++len;
       }
-      if(r!=1){break;}
+      if(len<=0){continue;}
+      while(len>0 && (buf[len-1]=='\n'||buf[len-1]=='\r')){--len;}
       buf[len]=0;
 printf("Got from IRC client: '%s'\n", buf);
       if(!strncmp(buf, "PRIVMSG ", 8))
@@ -372,21 +200,6 @@ printf("Got from IRC client: '%s'\n", buf);
         msg[0]=0;
         msg=&msg[1];
         if(msg[0]==':'){msg=&msg[1];}
-        char* color;
-        while((color=strchr(msg, '\x03')))
-        {
-          char* end;
-          int c=findcolor_ansi(color, &end);
-          if(c!=-1){dprintf(tc_in[1], "/color %i\n", c);}
-          memmove(color, end, strlen(end)+1);
-        }
-        if(!strncmp(msg, "\x01""ACTION ", 8)) // Translate '/me'
-        {
-          msg=&msg[7];
-          msg[0]='*';
-          char* end=strchr(msg, '\x01');
-          if(end){end[0]='*';}
-        }
         if(target[0]=='#' && !strcmp(&target[1], channel))
         {
           dprintf(tc_in[1], "%s\n", msg);
@@ -394,21 +207,13 @@ printf("Got from IRC client: '%s'\n", buf);
           dprintf(tc_in[1], "/msg %s %s\n", target, msg);
         }
       }
-      else if(!strncmp(buf, "NICK ", 5))
-      {
-        char* nick=&buf[5];
-        if(nick[0]==':'){nick=&nick[1];}
-        dprintf(tc_in[1], "/nick %s\n", nick);
-      }
       else if(!strncmp(buf, "PING ", 5))
       {
         dprintf(sock, ":irchack PONG %s\n", &buf[5]);
       }
-      else if(!strncmp(buf, "QUIT ", 5)){break;}
     }
   }
 
-  close(tc_in[1]);
-  close(tc_out[0]);
+  shutdown(sock, SHUT_RDWR);
   return 0;
 }