$ git clone http://tcclient.ion.nu/tc_client.git
commit f0b1b786971d35cebfea30b58e4c9353ed8b786e
Author: Alicia <...>
Date:   Tue Apr 7 06:49:01 2015 +0200

    Version 0.24

diff --git a/ChangeLog b/ChangeLog
index 1c9f9f1..d5c1eaa 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+0.24:
+Fixed endianness of RTMP set-chunk-size handling.
+modbot: fixed a bug where '!wrongrequest' would claim no request was found when (and only when) the request was last in queue.
+modbot: added the options -d/--daemon and -l/--log plus better handling of -h/--help.
+modbot: moved and changed the note about '!approve entire queue' overuse to a note about making sure none of the videos were inappropriate in the "Queue approved" message.
+camviewer: fixed a bug introduced in 0.23 where audio data was handled improperly, resulting in audio/video data being missed.
+camviewer: ignore 0-byte audio/video packets.
 0.23:
 Added the moderator commands /mute and /push2talk (contributed by Jade)
 Set tc_client as a dependency of utils.
diff --git a/Makefile b/Makefile
index e780924..9735d2d 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-VERSION=0.23
+VERSION=0.24
 CFLAGS=-g3 -Wall $(shell curl-config --cflags)
 LIBS=-g3 $(shell curl-config --libs)
 ifneq ($(wildcard config.mk),)
diff --git a/rtmp.c b/rtmp.c
index 443a484..4cb900d 100644
--- a/rtmp.c
+++ b/rtmp.c
@@ -114,6 +114,7 @@ char rtmp_get(int sock, struct rtmp* rtmp)
     if(chunk->type==RTMP_SET_PACKET_SIZE)
     {
       memcpy(&chunksize_in, chunk->buf, sizeof(unsigned int));
+      chunksize_in=be32(chunksize_in);
 //      printf("Server set chunk size to %u (packet size: %u)\n", chunksize_in, chunk->length);
     }
 // printf("Got chunk: chunkid=%u, type=%u, length=%u, streamid=%u\n", chunk->id, chunk->type, chunk->length, chunk->streamid);
diff --git a/utilities/camviewer/camviewer.c b/utilities/camviewer/camviewer.c
index 2b25607..a1a8a6b 100644
--- a/utilities/camviewer/camviewer.c
+++ b/utilities/camviewer/camviewer.c
@@ -139,8 +139,10 @@ gboolean handledata(GIOChannel* channel, GIOCondition condition, gpointer datap)
     char* sizestr=strchr(&buf[7], ' ');
     if(!sizestr){return 1;}
     unsigned int size=strtoul(&sizestr[1], 0, 0);
+    if(!size){return 1;}
     unsigned char frameinfo;
     read(tc_client[0], &frameinfo, 1);
+    --size; // For the byte we read above
     unsigned char buf[size];
     unsigned int pos=0;
     while(pos<size)
@@ -160,6 +162,7 @@ gboolean handledata(GIOChannel* channel, GIOCondition condition, gpointer datap)
     if(!strcmp(data->cams[i].id, &buf[7])){cam=&data->cams[i]; break;}
   }
   unsigned int size=strtoul(&sizestr[1], 0, 0);
+  if(!size){return 1;}
   // Mostly ignore the first byte (contains frame type (e.g. keyframe etc.) in 4 bits and codec in the other 4)
   --size;
   AVPacket pkt;
diff --git a/utilities/modbot/modbot.c b/utilities/modbot/modbot.c
index 54a3ac5..5967088 100644
--- a/utilities/modbot/modbot.c
+++ b/utilities/modbot/modbot.c
@@ -150,6 +150,40 @@ void playnext(int x)
 
 int main(int argc, char** argv)
 {
+  // Handle arguments (-d, -l, -h additions, the rest are handled by tc_client)
+  char daemon=0;
+  char* logfile=0;
+  unsigned int i;
+  for(i=1; i<argc; ++i)
+  {
+    if(!strcmp(argv[i], "-d") || !strcmp(argv[i], "--daemon"))
+    {
+      daemon=1;
+      // Remove non-tc_client argument
+      --argc;
+      memmove(&argv[i], &argv[i+1], sizeof(char*)*(argc-i));
+      argv[argc]=0;
+      --i;
+    }
+    else if(i+1<argc && (!strcmp(argv[i], "-l") || !strcmp(argv[i], "--log")))
+    {
+      logfile=argv[i+1];
+      // Remove non-tc_client argument
+      argc-=2;
+      memmove(&argv[i], &argv[i+2], sizeof(char*)*(argc-i));
+      argv[argc]=0;
+      --i;
+    }
+    else if(!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help"))
+    {
+      printf("Additional options for modbot:\n"
+             "-d/--daemon     = daemonize after startup\n"
+             "-l/--log <file> = log output into <file>\n"
+             "\n");
+      execv("./tc_client", argv);
+      return 1;
+    }
+  }
   int in[2];
   int out[2];
   pipe(in);
@@ -204,6 +238,21 @@ int main(int argc, char** argv)
     }
     len=0;
     // printf("Got line '%s'\n", buf);
+    // Note: daemonizing and setting up logging here to avoid interfering with account password entry
+    if(daemon)
+    {
+      if(fork()){return 0;}
+      daemon=0;
+      if(!logfile){logfile="/dev/null";} // Prevent writing to stdout as a daemon
+    }
+    if(logfile)
+    {
+      int f=open(logfile, O_WRONLY|O_CREAT|O_APPEND, 0600);
+      dup2(f, 1);
+      dup2(f, 2);
+      close(f);
+      logfile=0;
+    }
     char* space=strchr(buf, ' ');
     if(!space){continue;}
     if(!strcmp(space, " is a moderator."))
@@ -301,10 +350,11 @@ int main(int argc, char** argv)
             {
               queue_del(&queue, queue.items[i].video);
               if(!playing && i==0){playnext(0);}
+              i=1; // distinguish from just having reached the front of the queue
               break;
             }
           }
-          if(i==queue.itemcount)
+          if(!i)
           {
             say(pm, "I can't find any request by you, sorry\n");
           }
@@ -458,9 +508,9 @@ int main(int argc, char** argv)
               {
                 list_save(&goodvids, "goodvids.txt");
                 if(!playing){playnext(0);} // Next in queue just got approved, so play it
-                else{say(pm, "Queue approved\n");}
+                else{say(pm, "Queue approved. Make sure none of the videos were inappropriate\n");}
               }else{
-                say(0, "%s: there is nothing in the queue that isn't already approved, please do not overuse this function\n", nick);
+                say(pm, "The queue is already approved (or empty)\n");
               }
               continue;
             }else{