$ git clone https://tcclient.ion.nu/tc_client.git
commit 1ab52a908fdcf30077bbb328e59e242ec145dd6c
Author: Alicia <...>
Date:   Sat Aug 1 00:22:06 2015 +0200

    irchack: added support for captchas.

diff --git a/ChangeLog b/ChangeLog
index 110da90..02d51dc 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -4,7 +4,8 @@ Send account username in AMF0 "connect" command at startup regardless of whether
 If an RTMP chunk already has a buffer allocated when a packet sets the length, free it and start over.
 Added support for captchas.
 irchack: added a check for whether tc_client is installed or should be run from the source directory (should have been added along with the 'install' target)
-tc_client-gtk and camviewer: added support for captchas
+tc_client-gtk and camviewer: added support for captchas.
+irchack: added support for captchas.
 0.34:
 Fixed memory alignment in rtmp/amf code (for CPU architectures that are picky about it)
 Print version info when called with -v/--version (contributed by Jade)
diff --git a/utilities/irchack/irchack.c b/utilities/irchack/irchack.c
index a78cb32..932df6b 100644
--- a/utilities/irchack/irchack.c
+++ b/utilities/irchack/irchack.c
@@ -82,6 +82,20 @@ int findcolor_ansi(char* irc, char** end)
   return -1;
 }
 
+int read_line(int fd, char* buf, int buflen)
+{
+  int r;
+  int len=0;
+  while(len<buflen-1)
+  {
+    if((r=read(fd, &buf[len], 1))!=1 || buf[len]=='\r' || buf[len]=='\n'){break;}
+    ++len;
+  }
+  if(r!=1){return 0;}
+  buf[len]=0;
+  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)
@@ -108,18 +122,9 @@ int main(int argc, char** argv)
     char* pass=0;
     char* acc_user=0;
     char* acc_pass=0;
-    int len;
     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(!read_line(sock, buf, 2048)){break;}
       if(!strncmp(buf, "USER ", 5))
       {
         acc_user=&buf[5];
@@ -213,7 +218,6 @@ char session(int sock, const char* nick, const char* channel, const char* pass,
   pfd[1].events=POLLIN;
   pfd[1].revents=0;
   char buf[2048];
-  int len;
   char joins=0;
   while(1)
   {
@@ -221,15 +225,7 @@ char session(int sock, const char* nick, const char* channel, const char* pass,
     if(pfd[0].revents)
     {
       pfd[0].revents=0;
-      len=0;
-      int r;
-      while(len<2047)
-      {
-        if((r=read(tc_out[0], &buf[len], 1))!=1 || buf[len]=='\r' || buf[len]=='\n'){break;}
-        ++len;
-      }
-      if(r!=1){break;}
-      buf[len]=0;
+      if(!read_line(tc_out[0], buf, 2048)){break;}
 printf("Got from tc_client: '%s'\n", buf);
       if(!strncmp(buf, "Currently online: ", 18))
       {
@@ -256,6 +252,20 @@ printf("Got from tc_client: '%s'\n", buf);
         dprintf(sock, ":irchack 332 %s #%s :%s\n", nick, channel, &buf[12]);
         continue;
       }
+      if(!strncmp(buf, "Captcha: ", 9))
+      {
+        dprintf(sock, ":captcha!user@host PRIVMSG %s :%s (reply to this when done)\n", nick, buf);
+        while(1)
+        {
+          if(!read_line(sock, buf, 2048)){break;}
+          if(!strncmp(buf, "PRIVMSG captcha ", 16))
+          {
+            write(tc_in[1], "\n", 1);
+            break;
+          }
+        }
+        continue;
+      }
       if(!strcmp(buf, "Password required"))
       {
         dprintf(sock, ":irchack 475 %s :Cannot join %s without the correct password\n", channel, channel);
@@ -298,14 +308,7 @@ printf("Got from tc_client: '%s'\n", buf);
             end[0]=0;
             dprintf(sock, ":irchack 367 %s #%s %s!%s@* irchack 0\n", nick, channel, user, buf);
           }
-          len=0;
-          while(len<2047)
-          {
-            if((r=read(tc_out[0], &buf[len], 1))!=1 || buf[len]=='\r' || buf[len]=='\n'){break;}
-            ++len;
-          }
-          if(r!=1){return 1;}
-          buf[len]=0;
+          if(!read_line(tc_out[0], buf, 2048)){return 1;}
         }
         dprintf(sock, ":irchack 368 %s #%s :End of Channel Ban List.\n", nick, channel);
         continue;
@@ -373,15 +376,7 @@ printf("Got from tc_client: '%s'\n", buf);
     if(pfd[1].revents)
     {
       pfd[1].revents=0;
-      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(!read_line(sock, buf, 2048)){break;}
 printf("Got from IRC client: '%s'\n", buf);
       if(!strncasecmp(buf, "PRIVMSG ", 8))
       {