diff --git a/librtmp/amf.c b/librtmp/amf.c
index ce84f81..a25bc04 100644
--- a/librtmp/amf.c
+++ b/librtmp/amf.c
@@ -610,6 +610,9 @@ AMFProp_Decode(AMFObjectProperty *prop, const char *pBuffer, int nSize,
       return -1;
     }
 
+  if (*pBuffer == AMF_NULL)
+    bDecodeName = 0;
+
   if (bDecodeName && nSize < 4)
     {				/* at least name (length + at least 1 byte) and 1 byte of data */
       RTMP_Log(RTMP_LOGDEBUG,
@@ -801,8 +804,8 @@ AMFProp_Dump(AMFObjectProperty *prop)
     }
   else
     {
-      name.av_val = "no-name.";
-      name.av_len = sizeof("no-name.") - 1;
+      name.av_val = "no-name";
+      name.av_len = sizeof("no-name") - 1;
     }
   if (name.av_len > 18)
     name.av_len = 18;
diff --git a/librtmp/dh.h b/librtmp/dh.h
index 9959532..e29587b 100644
--- a/librtmp/dh.h
+++ b/librtmp/dh.h
@@ -61,7 +61,7 @@ static int MDH_generate_key(MDH *dh)
   MP_set(&dh->ctx.P, dh->p);
   MP_set(&dh->ctx.G, dh->g);
   dh->ctx.len = 128;
-  dhm_make_public(&dh->ctx, 1024, out, 1, havege_rand, &RTMP_TLS_ctx->hs);
+  dhm_make_public(&dh->ctx, 1024, out, 1, havege_random, &RTMP_TLS_ctx->hs);
   MP_new(dh->pub_key);
   MP_new(dh->priv_key);
   MP_set(dh->pub_key, &dh->ctx.GX);
diff --git a/librtmp/handshake.h b/librtmp/handshake.h
index 0438486..102ba82 100644
--- a/librtmp/handshake.h
+++ b/librtmp/handshake.h
@@ -965,8 +965,18 @@ HandShake(RTMP * r, int FP9HandShake)
     __FUNCTION__);
   RTMP_LogHex(RTMP_LOGDEBUG, reply, RTMP_SIG_SIZE);
 #endif
-  if (!WriteN(r, (char *)reply, RTMP_SIG_SIZE))
-    return FALSE;
+  if (r->Link.CombineConnectPacket)
+    {
+      char *HandshakeResponse = malloc(RTMP_SIG_SIZE);
+      memcpy(HandshakeResponse, (char *) reply, RTMP_SIG_SIZE);
+      r->Link.HandshakeResponse.av_val = HandshakeResponse;
+      r->Link.HandshakeResponse.av_len = RTMP_SIG_SIZE;
+    }
+  else
+    {
+      if (!WriteN(r, (char *) reply, RTMP_SIG_SIZE))
+        return FALSE;
+    }
 
   /* 2nd part of handshake */
   if (ReadN(r, (char *)serversig, RTMP_SIG_SIZE) != RTMP_SIG_SIZE)
diff --git a/librtmp/hashswf.c b/librtmp/hashswf.c
index 9f4e2c0..eeed34c 100644
--- a/librtmp/hashswf.c
+++ b/librtmp/hashswf.c
@@ -70,7 +70,7 @@ extern TLS_CTX RTMP_TLS_ctx;
 
 #endif /* CRYPTO */
 
-#define	AGENT	"Mozilla/5.0"
+#define	AGENT	"Mozilla/5.0 (Windows NT 5.1; rv:8.0) Gecko/20100101 Firefox/8.0"
 
 HTTPResult
 HTTP_get(struct HTTP_ctx *http, const char *url, HTTP_read_callback *cb)
@@ -528,7 +528,7 @@ RTMP_HashSWF(const char *url, unsigned int *size, unsigned char *hash,
 
 	  if (strncmp(buf, "url: ", 5))
 	    continue;
-	  if (strncmp(buf + 5, url, hlen))
+	  if (strncmp(buf + 5, url, strlen(buf + 5) - 1))
 	    continue;
 	  r1 = strrchr(buf, '/');
 	  i = strlen(r1);
diff --git a/librtmp/log.c b/librtmp/log.c
index 0012985..856e3e4 100644
--- a/librtmp/log.c
+++ b/librtmp/log.c
@@ -52,8 +52,8 @@ static void rtmp_log_default(int level, const char *format, va_list vl)
 	vsnprintf(str, MAX_PRINT_LEN-1, format, vl);
 
 	/* Filter out 'no-name' */
-	if ( RTMP_debuglevel<RTMP_LOGALL && strstr(str, "no-name" ) != NULL )
-		return;
+	if (RTMP_debuglevel < RTMP_LOGDEBUG && strstr(str, "no-name") != NULL)
+	  return;
 
 	if ( !fmsg ) fmsg = stderr;
 
diff --git a/librtmp/rtmp.c b/librtmp/rtmp.c
index 52d0254..bef37aa 100644
--- a/librtmp/rtmp.c
+++ b/librtmp/rtmp.c
@@ -27,6 +27,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <assert.h>
+#include <math.h>
 
 #include "rtmp_sys.h"
 #include "log.h"
@@ -45,6 +46,7 @@ TLS_CTX RTMP_TLS_ctx;
 
 #define RTMP_SIG_SIZE 1536
 #define RTMP_LARGE_HEADER_SIZE 12
+#define HEX2BIN(a) (((a)&0x40)?((a)&0xf)+9:((a)&0xf))
 
 static const int packetSize[] = { 12, 8, 4, 1 };
 
@@ -97,6 +99,9 @@ static int SendFCSubscribe(RTMP *r, AVal *subscribepath);
 static int SendPlay(RTMP *r);
 static int SendBytesReceived(RTMP *r);
 static int SendUsherToken(RTMP *r, AVal *usherToken);
+static int SendInvoke(RTMP *r, AVal *Command, int queue);
+static int SendGetStreamLength(RTMP *r);
+static int strsplit(char *src, int srclen, char delim, char ***params);
 
 #if 0				/* unused */
 static int SendBGHasStream(RTMP *r, double dId, AVal *playpath);
@@ -259,6 +264,8 @@ RTMP_Init(RTMP *r)
   r->m_fVideoCodecs = 252.0;
   r->Link.timeout = 30;
   r->Link.swfAge = 30;
+  r->Link.CombineConnectPacket = TRUE;
+  r->Link.ConnectPacket = FALSE;
 }
 
 void
@@ -337,6 +344,7 @@ RTMP_SetupStream(RTMP *r,
 		 AVal *flashVer,
 		 AVal *subscribepath,
 		 AVal *usherToken,
+		 AVal *WeebToken,
 		 int dStart,
 		 int dStop, int bLiveStream, long int timeout)
 {
@@ -359,6 +367,8 @@ RTMP_SetupStream(RTMP *r,
     RTMP_Log(RTMP_LOGDEBUG, "subscribepath : %s", subscribepath->av_val);
   if (usherToken && usherToken->av_val)
     RTMP_Log(RTMP_LOGDEBUG, "NetStream.Authenticate.UsherToken : %s", usherToken->av_val);
+  if (WeebToken && WeebToken->av_val)
+    RTMP_Log(RTMP_LOGDEBUG, "WeebToken: %s", WeebToken->av_val);
   if (flashVer && flashVer->av_val)
     RTMP_Log(RTMP_LOGDEBUG, "flashVer : %s", flashVer->av_val);
   if (dStart > 0)
@@ -426,6 +436,8 @@ RTMP_SetupStream(RTMP *r,
     r->Link.subscribepath = *subscribepath;
   if (usherToken && usherToken->av_len)
     r->Link.usherToken = *usherToken;
+  if (WeebToken && WeebToken->av_len)
+    r->Link.WeebToken = *WeebToken;
   r->Link.seekTime = dStart;
   r->Link.stopTime = dStop;
   if (bLiveStream)
@@ -483,14 +495,22 @@ static struct urlopt {
   	"Stream is live, no seeking possible" },
   { AVC("subscribe"), OFF(Link.subscribepath), OPT_STR, 0,
   	"Stream to subscribe to" },
-  { AVC("jtv"), OFF(Link.usherToken),          OPT_STR, 0,
-	"Justin.tv authentication token" },
-  { AVC("token"),     OFF(Link.token),	       OPT_STR, 0,
+  { AVC("jtv"),       OFF(Link.usherToken),    OPT_STR, 0,
+        "Justin.tv authentication token"},
+  { AVC("weeb"),      OFF(Link.WeebToken),     OPT_STR, 0,
+        "Weeb.tv authentication token"},
+  { AVC("token"),     OFF(Link.token),         OPT_STR, 0,
   	"Key for SecureToken response" },
   { AVC("swfVfy"),    OFF(Link.lFlags),        OPT_BOOL, RTMP_LF_SWFV,
   	"Perform SWF Verification" },
   { AVC("swfAge"),    OFF(Link.swfAge),        OPT_INT, 0,
   	"Number of days to use cached SWF hash" },
+#ifdef CRYPTO
+  { AVC("swfsize"),   OFF(Link.swfSize),       OPT_INT, 0,
+        "Size of the decompressed SWF file"},
+  { AVC("swfhash"),   OFF(Link.swfHash),       OPT_STR, 0,
+        "SHA256 hash of the decompressed SWF file"},
+#endif
   { AVC("start"),     OFF(Link.seekTime),      OPT_INT, 0,
   	"Stream start position in milliseconds" },
   { AVC("stop"),      OFF(Link.stopTime),      OPT_INT, 0,
@@ -751,9 +771,16 @@ int RTMP_SetupURL(RTMP *r, char *url)
     }
 
 #ifdef CRYPTO
-  if ((r->Link.lFlags & RTMP_LF_SWFV) && r->Link.swfUrl.av_len)
-    RTMP_HashSWF(r->Link.swfUrl.av_val, &r->Link.SWFSize,
-	  (unsigned char *)r->Link.SWFHash, r->Link.swfAge);
+  RTMP_Log(RTMP_LOGDEBUG, "Khalsa: %d %d %s\n", r->Link.swfSize, r->Link.swfHash.av_len, r->Link.swfHash.av_val);
+  if (r->Link.swfSize && r->Link.swfHash.av_len)
+    {
+      int i, j = 0;
+      for (i = 0; i < r->Link.swfHash.av_len; i += 2)
+        r->Link.SWFHash[j++] = (HEX2BIN(r->Link.swfHash.av_val[i]) << 4) | HEX2BIN(r->Link.swfHash.av_val[i + 1]);
+      r->Link.SWFSize = (uint32_t) r->Link.swfSize;
+    }
+  else if ((r->Link.lFlags & RTMP_LF_SWFV) && r->Link.swfUrl.av_len)
+    RTMP_HashSWF(r->Link.swfUrl.av_val, &r->Link.SWFSize, (unsigned char *) r->Link.SWFHash, r->Link.swfAge);
 #endif
 
   if (r->Link.port == 0)
@@ -854,6 +881,8 @@ RTMP_Connect0(RTMP *r, struct sockaddr * service)
   }
 
   setsockopt(r->m_sb.sb_socket, IPPROTO_TCP, TCP_NODELAY, (char *) &on, sizeof(on));
+  if (r->Link.protocol & RTMP_FEATURE_HTTP)
+    setsockopt(r->m_sb.sb_socket, SOL_SOCKET, SO_KEEPALIVE, (char *) &on, sizeof (on));
 
   return TRUE;
 }
@@ -1308,8 +1337,24 @@ ReadN(RTMP *r, char *buffer, int n)
 		  return 0;
 		}
 	    }
-	  if (r->m_resplen && !r->m_sb.sb_size)
-	    RTMPSockBuf_Fill(&r->m_sb);
+
+          // Try to fill the whole buffer. previous buffer needs to be consumed
+          // completely before receiving new data.
+          if (r->m_resplen && (r->m_sb.sb_size <= 0))
+            {
+              do
+                {
+                  nBytes = RTMPSockBuf_Fill(&r->m_sb);
+                  if (nBytes == -1)
+                    {
+                      if (!r->m_sb.sb_timedout)
+                        RTMP_Close(r);
+                      return 0;
+                    }
+                }
+              while (r->m_resplen && (r->m_sb.sb_size < r->m_resplen) && (nBytes > 0));
+            }
+
           avail = r->m_sb.sb_size;
 	  if (avail > r->m_resplen)
 	    avail = r->m_resplen;
@@ -1336,10 +1381,9 @@ ReadN(RTMP *r, char *buffer, int n)
 	  r->m_sb.sb_size -= nRead;
 	  nBytes = nRead;
 	  r->m_nBytesIn += nRead;
-	  if (r->m_bSendCounter
-	      && r->m_nBytesIn > ( r->m_nBytesInSent + r->m_nClientBW / 10))
-	    if (!SendBytesReceived(r))
-	        return FALSE;
+          if (r->m_bSendCounter && r->m_nBytesIn > (r->m_nBytesInSent + r->m_nClientBW / 10))
+            if (!SendBytesReceived(r))
+              return FALSE;
 	}
       /*RTMP_Log(RTMP_LOGDEBUG, "%s: %d bytes\n", __FUNCTION__, nBytes); */
 #ifdef _DEBUG
@@ -1390,6 +1434,16 @@ WriteN(RTMP *r, const char *buffer, int n)
     }
 #endif
 
+  if (r->Link.ConnectPacket)
+    {
+      char *ConnectPacket = malloc(r->Link.HandshakeResponse.av_len + n);
+      memcpy(ConnectPacket, r->Link.HandshakeResponse.av_val, r->Link.HandshakeResponse.av_len);
+      memcpy(ConnectPacket + r->Link.HandshakeResponse.av_len, ptr, n);
+      ptr = ConnectPacket;
+      n += r->Link.HandshakeResponse.av_len;
+      r->Link.ConnectPacket = FALSE;
+    }
+
   while (n > 0)
     {
       int nBytes;
@@ -1455,6 +1509,9 @@ SendConnectPacket(RTMP *r, RTMPPacket *cp)
   char pbuf[4096], *pend = pbuf + sizeof(pbuf);
   char *enc;
 
+  if (r->Link.CombineConnectPacket)
+    r->Link.ConnectPacket = TRUE;
+
   if (cp)
     return RTMP_SendPacket(r, cp, TRUE);
 
@@ -1667,7 +1724,7 @@ SendUsherToken(RTMP *r, AVal *usherToken)
   packet.m_hasAbsTimestamp = 0;
   packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;
 
-  RTMP_Log(RTMP_LOGDEBUG, "UsherToken: %s", usherToken->av_val);
+  RTMP_Log(RTMP_LOGDEBUG, "UsherToken: %.*s", usherToken->av_len, usherToken->av_val);
   enc = packet.m_body;
   enc = AMF_EncodeString(enc, pend, &av_NetStream_Authenticate_UsherToken);
   enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes);
@@ -2096,10 +2153,8 @@ SendPlay(RTMP *r)
     enc = AMF_EncodeNumber(enc, pend, -1000.0);
   else
     {
-      if (r->Link.seekTime > 0.0)
-	enc = AMF_EncodeNumber(enc, pend, r->Link.seekTime);	/* resume from here */
-      else
-	enc = AMF_EncodeNumber(enc, pend, 0.0);	/*-2000.0);*/ /* recorded as default, -2000.0 is not reliable since that freezes the player if the stream is not found */
+      if (r->Link.seekTime > 0.0 || r->Link.stopTime)
+        enc = AMF_EncodeNumber(enc, pend, r->Link.seekTime); /* resume from here */
     }
   if (!enc)
     return FALSE;
@@ -2215,7 +2270,7 @@ RTMP_SendCtrl(RTMP *r, short nType, unsigned int nObject, unsigned int nTime)
   int nSize;
   char *buf;
 
-  RTMP_Log(RTMP_LOGDEBUG, "sending ctrl. type: 0x%04x", (unsigned short)nType);
+  RTMP_Log(RTMP_LOGDEBUG, "sending ctrl, type: 0x%04x", (unsigned short)nType);
 
   packet.m_nChannel = 0x02;	/* control channel (ping) */
   packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM;
@@ -2247,8 +2302,8 @@ RTMP_SendCtrl(RTMP *r, short nType, unsigned int nObject, unsigned int nTime)
     }
   else if (nType == 0x1A)
     {
-	  *buf = nObject & 0xff;
-	}
+      *buf = nObject & 0xff;
+    }
   else
     {
       if (nSize > 2)
@@ -2305,6 +2360,7 @@ AV_clear(RTMP_METHOD *vals, int num)
   free(vals);
 }
 
+SAVC(onBWCheck);
 SAVC(onBWDone);
 SAVC(onFCSubscribe);
 SAVC(onFCUnsubscribe);
@@ -2314,24 +2370,26 @@ SAVC(_error);
 SAVC(close);
 SAVC(code);
 SAVC(level);
+SAVC(description);
 SAVC(onStatus);
 SAVC(playlist_ready);
 static const AVal av_NetStream_Failed = AVC("NetStream.Failed");
 static const AVal av_NetStream_Play_Failed = AVC("NetStream.Play.Failed");
-static const AVal av_NetStream_Play_StreamNotFound =
-AVC("NetStream.Play.StreamNotFound");
-static const AVal av_NetConnection_Connect_InvalidApp =
-AVC("NetConnection.Connect.InvalidApp");
+static const AVal av_NetStream_Play_StreamNotFound = AVC("NetStream.Play.StreamNotFound");
+static const AVal av_NetConnection_Connect_InvalidApp = AVC("NetConnection.Connect.InvalidApp");
 static const AVal av_NetStream_Play_Start = AVC("NetStream.Play.Start");
 static const AVal av_NetStream_Play_Complete = AVC("NetStream.Play.Complete");
 static const AVal av_NetStream_Play_Stop = AVC("NetStream.Play.Stop");
 static const AVal av_NetStream_Seek_Notify = AVC("NetStream.Seek.Notify");
 static const AVal av_NetStream_Pause_Notify = AVC("NetStream.Pause.Notify");
-static const AVal av_NetStream_Play_PublishNotify =
-AVC("NetStream.Play.PublishNotify");
-static const AVal av_NetStream_Play_UnpublishNotify =
-AVC("NetStream.Play.UnpublishNotify");
+static const AVal av_NetStream_Play_PublishNotify = AVC("NetStream.Play.PublishNotify");
+static const AVal av_NetStream_Play_UnpublishNotify = AVC("NetStream.Play.UnpublishNotify");
 static const AVal av_NetStream_Publish_Start = AVC("NetStream.Publish.Start");
+static const AVal av_NetConnection_confStream = AVC("NetConnection.confStream");
+static const AVal av_verifyClient = AVC("verifyClient");
+static const AVal av_sendStatus = AVC("sendStatus");
+static const AVal av_getStreamLength = AVC("getStreamLength");
+static const AVal av_ReceiveCheckPublicStatus = AVC("ReceiveCheckPublicStatus");
 
 /* Returns 0 for OK/Failed/error, 1 for 'Stop or Complete' */
 static int
@@ -2341,6 +2399,11 @@ HandleInvoke(RTMP *r, const char *body, unsigned int nBodySize)
   AVal method;
   double txn;
   int ret = 0, nRes;
+  char pbuf[256], *pend = pbuf + sizeof (pbuf), *enc, **params = NULL;
+  char *host = r->Link.hostname.av_len ? r->Link.hostname.av_val : "";
+  char *pageUrl = r->Link.pageUrl.av_len ? r->Link.pageUrl.av_val : "";
+  int param_count;
+  AVal av_Command, av_Response;
   if (body[0] != 0x02)		/* make sure it is a string method name we start with */
     {
       RTMP_Log(RTMP_LOGWARNING, "%s, Sanity failed. no string method in invoke packet",
@@ -2402,23 +2465,137 @@ HandleInvoke(RTMP *r, const char *body, unsigned int nBodySize)
 	      RTMP_SendServerBW(r);
 	      RTMP_SendCtrl(r, 3, 0, 300);
 	    }
-	  RTMP_SendCreateStream(r);
-
-	  if (!(r->Link.protocol & RTMP_FEATURE_WRITE))
-	    {
-	      /* Authenticate on Justin.tv legacy servers before sending FCSubscribe */
-	      if (r->Link.usherToken.av_len)
-	        SendUsherToken(r, &r->Link.usherToken);
-	      /* Send the FCSubscribe if live stream or if subscribepath is set */
-	      if (r->Link.subscribepath.av_len)
-	        SendFCSubscribe(r, &r->Link.subscribepath);
-	      else if (r->Link.lFlags & RTMP_LF_LIVE)
-	        SendFCSubscribe(r, &r->Link.playpath);
-	    }
-	}
+          if (strstr(host, "tv-stream.to") || strstr(pageUrl, "tv-stream.to"))
+            {
+              static char auth[] = {'h', 0xC2, 0xA7, '4', 'j', 'h', 'H', '4', '3', 'd'};
+              AVal av_auth;
+              SAVC(requestAccess);
+              av_auth.av_val = auth;
+              av_auth.av_len = sizeof (auth);
+
+              enc = pbuf;
+              enc = AMF_EncodeString(enc, pend, &av_requestAccess);
+              enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes);
+              *enc++ = AMF_NULL;
+              enc = AMF_EncodeString(enc, pend, &av_auth);
+              av_Command.av_val = pbuf;
+              av_Command.av_len = enc - pbuf;
+              SendInvoke(r, &av_Command, FALSE);
+
+              SAVC(getConnectionCount);
+              enc = pbuf;
+              enc = AMF_EncodeString(enc, pend, &av_getConnectionCount);
+              enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes);
+              *enc++ = AMF_NULL;
+              av_Command.av_val = pbuf;
+              av_Command.av_len = enc - pbuf;
+              SendInvoke(r, &av_Command, FALSE);
+
+              SendGetStreamLength(r);
+            }
+          else if (strstr(host, "jampo.com.ua") || strstr(pageUrl, "jampo.com.ua"))
+            {
+              SendGetStreamLength(r);
+            }
+          else if (strstr(host, "streamscene.cc") || strstr(pageUrl, "streamscene.cc")
+                   || strstr(host, "tsboard.tv") || strstr(pageUrl, "teamstream.in"))
+            {
+              SAVC(r);
+              enc = pbuf;
+              enc = AMF_EncodeString(enc, pend, &av_r);
+              enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes);
+              *enc++ = AMF_NULL;
+              av_Command.av_val = pbuf;
+              av_Command.av_len = enc - pbuf;
+              SendInvoke(r, &av_Command, FALSE);
+
+              SendGetStreamLength(r);
+            }
+          else if (strstr(host, "chaturbate.com") || strstr(pageUrl, "chaturbate.com"))
+            {
+              AVal av_ModelName;
+              SAVC(CheckPublicStatus);
+
+              if (strlen(pageUrl) > 7)
+                {
+                  strsplit(pageUrl + 7, FALSE, '/', &params);
+                  av_ModelName.av_val = params[1];
+                  av_ModelName.av_len = strlen(params[1]);
+
+                  enc = pbuf;
+                  enc = AMF_EncodeString(enc, pend, &av_CheckPublicStatus);
+                  enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes);
+                  *enc++ = AMF_NULL;
+                  enc = AMF_EncodeString(enc, pend, &av_ModelName);
+                  av_Command.av_val = pbuf;
+                  av_Command.av_len = enc - pbuf;
+
+                  SendInvoke(r, &av_Command, FALSE);
+                }
+              else
+                {
+                  RTMP_Log(RTMP_LOGERROR, "you must specify the pageUrl");
+                  RTMP_Close(r);
+                }
+            }
+          /* Weeb.tv specific authentication */
+          else if (r->Link.WeebToken.av_len)
+            {
+              AVal av_Token, av_Username, av_Password;
+              SAVC(determineAccess);
+
+              param_count = strsplit(r->Link.WeebToken.av_val, FALSE, ';', &params);
+              if (param_count >= 1)
+                {
+                  av_Token.av_val = params[0];
+                  av_Token.av_len = strlen(params[0]);
+                }
+              if (param_count >= 2)
+                {
+                  av_Username.av_val = params[1];
+                  av_Username.av_len = strlen(params[1]);
+                }
+              if (param_count >= 3)
+                {
+                  av_Password.av_val = params[2];
+                  av_Password.av_len = strlen(params[2]);
+                }
+
+              enc = pbuf;
+              enc = AMF_EncodeString(enc, pend, &av_determineAccess);
+              enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes);
+              *enc++ = AMF_NULL;
+              enc = AMF_EncodeString(enc, pend, &av_Token);
+              enc = AMF_EncodeString(enc, pend, &av_Username);
+              enc = AMF_EncodeString(enc, pend, &av_Password);
+              av_Command.av_val = pbuf;
+              av_Command.av_len = enc - pbuf;
+
+              RTMP_Log(RTMP_LOGDEBUG, "WeebToken: %s", r->Link.WeebToken.av_val);
+              SendInvoke(r, &av_Command, FALSE);
+            }
+          else
+            RTMP_SendCreateStream(r);
+        }
+      else if (AVMATCH(&methodInvoked, &av_getStreamLength))
+        {
+          RTMP_SendCreateStream(r);
+        }
       else if (AVMATCH(&methodInvoked, &av_createStream))
-	{
-	  r->m_stream_id = (int)AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 3));
+        {
+          r->m_stream_id = (int) AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 3));
+
+          if (!(r->Link.protocol & RTMP_FEATURE_WRITE))
+            {
+              /* Authenticate on Justin.tv legacy servers before sending FCSubscribe */
+              if (r->Link.usherToken.av_len)
+                SendUsherToken(r, &r->Link.usherToken);
+              /* Send the FCSubscribe if live stream or if subscribepath is set */
+              if (r->Link.subscribepath.av_len)
+                SendFCSubscribe(r, &r->Link.subscribepath);
+              else if ((r->Link.lFlags & RTMP_LF_LIVE) && (!r->Link.WeebToken.av_len))
+                SendFCSubscribe(r, &r->Link.playpath);
+            }
 
 	  if (r->Link.protocol & RTMP_FEATURE_WRITE)
 	    {
@@ -2441,7 +2618,7 @@ HandleInvoke(RTMP *r, const char *body, unsigned int nBodySize)
     }
   else if (AVMATCH(&method, &av_onBWDone))
     {
-	  if (!r->m_nBWCheckCounter)
+      if (!r->m_nBWCheckCounter)
         SendCheckBW(r);
     }
   else if (AVMATCH(&method, &av_onFCSubscribe))
@@ -2457,7 +2634,7 @@ HandleInvoke(RTMP *r, const char *body, unsigned int nBodySize)
     {
       SendPong(r, txn);
     }
-  else if (AVMATCH(&method, &av__onbwcheck))
+  else if (AVMATCH(&method, &av__onbwcheck) || AVMATCH(&method, &av_onBWCheck))
     {
       SendCheckBWResult(r, txn);
     }
@@ -2473,20 +2650,63 @@ HandleInvoke(RTMP *r, const char *body, unsigned int nBodySize)
     }
   else if (AVMATCH(&method, &av__error))
     {
-      RTMP_Log(RTMP_LOGERROR, "rtmp server sent error");
+      double code = 0;
+      unsigned int parsedPort;
+      AMFObject obj2;
+      AMFObjectProperty p;
+      AVal redirect;
+      SAVC(ex);
+      SAVC(redirect);
+
+      AMFProp_GetObject(AMF_GetProp(&obj, NULL, 3), &obj2);
+      if (RTMP_FindFirstMatchingProperty(&obj2, &av_ex, &p))
+        {
+          AMFProp_GetObject(&p, &obj2);
+          if (RTMP_FindFirstMatchingProperty(&obj2, &av_code, &p))
+            code = AMFProp_GetNumber(&p);
+          if (code == 302 && RTMP_FindFirstMatchingProperty(&obj2, &av_redirect, &p))
+            {
+              AMFProp_GetString(&p, &redirect);
+              r->Link.redirected = TRUE;
+
+              char *url = malloc(redirect.av_len + sizeof ("/playpath"));
+              strncpy(url, redirect.av_val, redirect.av_len);
+              url[redirect.av_len] = '\0';
+              r->Link.tcUrl.av_val = url;
+              r->Link.tcUrl.av_len = redirect.av_len;
+              strcat(url, "/playpath");
+              RTMP_ParseURL(url, &r->Link.protocol, &r->Link.hostname, &parsedPort, &r->Link.playpath0, &r->Link.app);
+              r->Link.port = parsedPort;
+            }
+        }
+      if (r->Link.redirected)
+        RTMP_Log(RTMP_LOGINFO, "rtmp server sent redirect");
+      else
+        RTMP_Log(RTMP_LOGERROR, "rtmp server sent error");
     }
   else if (AVMATCH(&method, &av_close))
     {
-      RTMP_Log(RTMP_LOGERROR, "rtmp server requested close");
-      RTMP_Close(r);
+      if (r->Link.redirected)
+        {
+          RTMP_Log(RTMP_LOGINFO, "trying to connect with redirected url");
+          RTMP_Close(r);
+          r->Link.redirected = FALSE;
+          RTMP_Connect(r, NULL);
+        }
+      else
+        {
+          RTMP_Log(RTMP_LOGERROR, "rtmp server requested close");
+          RTMP_Close(r);
+        }
     }
   else if (AVMATCH(&method, &av_onStatus))
     {
       AMFObject obj2;
-      AVal code, level;
+      AVal code, level, description;
       AMFProp_GetObject(AMF_GetProp(&obj, NULL, 3), &obj2);
       AMFProp_GetString(AMF_GetProp(&obj2, &av_code, -1), &code);
       AMFProp_GetString(AMF_GetProp(&obj2, &av_level, -1), &level);
+      AMFProp_GetString(AMF_GetProp(&obj2, &av_description, -1), &description);
 
       RTMP_Log(RTMP_LOGDEBUG, "%s, onStatus: %s", __FUNCTION__, code.av_val);
       if (AVMATCH(&code, &av_NetStream_Failed)
@@ -2550,6 +2770,45 @@ HandleInvoke(RTMP *r, const char *body, unsigned int nBodySize)
 	    r->m_pausing = 3;
 	  }
 	}
+
+      else if (AVMATCH(&code, &av_NetConnection_confStream))
+        {
+#ifdef CRYPTO
+          static const char hexdig[] = "0123456789abcdef";
+          SAVC(cf_stream);
+          int i;
+          char hash_hex[33] = {0};
+          unsigned char hash[16];
+          AVal auth;
+          param_count = strsplit(description.av_val, description.av_len, ':', &params);
+          if (param_count >= 3)
+            {
+              char *buf = malloc(strlen(params[0]) + r->Link.playpath.av_len + 1);
+              strcpy(buf, params[0]);
+              strncat(buf, r->Link.playpath.av_val, r->Link.playpath.av_len);
+              md5_hash((unsigned char *) buf, strlen(buf), hash);
+              for (i = 0; i < 16; i++)
+                {
+                  hash_hex[i * 2] = hexdig[0x0f & (hash[i] >> 4)];
+                  hash_hex[i * 2 + 1] = hexdig[0x0f & (hash[i])];
+                }
+              auth.av_val = &hash_hex[atoi(params[1]) - 1];
+              auth.av_len = atoi(params[2]);
+              RTMP_Log(RTMP_LOGDEBUG, "Khalsa: %.*s", auth.av_len, auth.av_val);
+
+              enc = pbuf;
+              enc = AMF_EncodeString(enc, pend, &av_cf_stream);
+              enc = AMF_EncodeNumber(enc, pend, txn);
+              *enc++ = AMF_NULL;
+              enc = AMF_EncodeString(enc, pend, &auth);
+              av_Command.av_val = pbuf;
+              av_Command.av_len = enc - pbuf;
+
+              SendInvoke(r, &av_Command, FALSE);
+              free(buf);
+            }
+#endif
+        }
     }
   else if (AVMATCH(&method, &av_playlist_ready))
     {
@@ -2563,6 +2822,74 @@ HandleInvoke(RTMP *r, const char *body, unsigned int nBodySize)
 	    }
         }
     }
+  else if (AVMATCH(&method, &av_verifyClient))
+    {
+      double VerificationNumber = AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 3));
+      RTMP_Log(RTMP_LOGDEBUG, "VerificationNumber: %.2f", VerificationNumber);
+
+      enc = pbuf;
+      enc = AMF_EncodeString(enc, pend, &av__result);
+      enc = AMF_EncodeNumber(enc, pend, txn);
+      *enc++ = AMF_NULL;
+      enc = AMF_EncodeNumber(enc, pend, exp(atan(sqrt(VerificationNumber))) + 1);
+      av_Response.av_val = pbuf;
+      av_Response.av_len = enc - pbuf;
+
+      AMF_Decode(&obj, av_Response.av_val, av_Response.av_len, FALSE);
+      AMF_Dump(&obj);
+      SendInvoke(r, &av_Response, FALSE);
+    }
+  else if (AVMATCH(&method, &av_sendStatus))
+    {
+      if (r->Link.WeebToken.av_len)
+        {
+          AVal av_Authorized = AVC("User.hasAccess");
+          AVal av_TransferLimit = AVC("User.noPremium.limited");
+          AVal av_UserLimit = AVC("User.noPremium.tooManyUsers");
+          AVal av_TimeLeft = AVC("timeLeft");
+          AVal av_Status, av_ReconnectionTime;
+
+          AMFObject Status;
+          AMFProp_GetObject(AMF_GetProp(&obj, NULL, 3), &Status);
+          AMFProp_GetString(AMF_GetProp(&Status, &av_code, -1), &av_Status);
+          RTMP_Log(RTMP_LOGINFO, "%.*s", av_Status.av_len, av_Status.av_val);
+          if (AVMATCH(&av_Status, &av_Authorized))
+            {
+              RTMP_Log(RTMP_LOGINFO, "Weeb.tv authentication successful");
+              RTMP_SendCreateStream(r);
+            }
+          else if (AVMATCH(&av_Status, &av_UserLimit))
+            {
+              RTMP_Log(RTMP_LOGINFO, "No free slots available");
+              RTMP_Close(r);
+            }
+          else if (AVMATCH(&av_Status, &av_TransferLimit))
+            {
+              AMFProp_GetString(AMF_GetProp(&Status, &av_TimeLeft, -1), &av_ReconnectionTime);
+              RTMP_Log(RTMP_LOGINFO, "Viewing limit exceeded. try again in %.*s minutes.", av_ReconnectionTime.av_len, av_ReconnectionTime.av_val);
+              RTMP_Close(r);
+            }
+        }
+    }
+  else if (AVMATCH(&method, &av_ReceiveCheckPublicStatus))
+    {
+      AVal Status;
+      AMFProp_GetString(AMF_GetProp(&obj, NULL, 3), &Status);
+      strsplit(Status.av_val, Status.av_len, ',', &params);
+      if (strcmp(params[0], "0") == 0)
+        {
+          RTMP_Log(RTMP_LOGINFO, "Model status is %s", params[1]);
+          RTMP_Close(r);
+        }
+      else
+        {
+          AVal Playpath;
+          Playpath.av_val = params[1];
+          Playpath.av_len = strlen(params[1]);
+          RTMP_ParsePlaypath(&Playpath, &r->Link.playpath);
+          RTMP_SendCreateStream(r);
+        }
+    }
   else
     {
 
@@ -2748,7 +3075,7 @@ HandleCtrl(RTMP *r, const RTMPPacket *packet)
   unsigned int tmp;
   if (packet->m_body && packet->m_nBodySize >= 2)
     nType = AMF_DecodeInt16(packet->m_body);
-  RTMP_Log(RTMP_LOGDEBUG, "%s, received ctrl. type: %d, len: %d", __FUNCTION__, nType,
+  RTMP_Log(RTMP_LOGDEBUG, "%s, received ctrl, type: %d, len: %d", __FUNCTION__, nType,
       packet->m_nBodySize);
   /*RTMP_LogHex(packet.m_body, packet.m_nBodySize); */
 
@@ -2856,15 +3183,15 @@ HandleCtrl(RTMP *r, const RTMPPacket *packet)
       RTMP_Log(RTMP_LOGDEBUG, "%s, SWFVerification ping received: ", __FUNCTION__);
       if (packet->m_nBodySize > 2 && packet->m_body[2] > 0x01)
 	{
-	  RTMP_Log(RTMP_LOGERROR,
-            "%s: SWFVerification Type %d request not supported! Patches welcome...",
-	    __FUNCTION__, packet->m_body[2]);
+          RTMP_Log(RTMP_LOGERROR,
+                   "%s: SWFVerification Type %d request not supported, attempting to use SWFVerification Type 1! Patches welcome...",
+                   __FUNCTION__, packet->m_body[2]);
 	}
 #ifdef CRYPTO
       /*RTMP_LogHex(packet.m_body, packet.m_nBodySize); */
 
       /* respond with HMAC SHA256 of decompressed SWF, key is the 30byte player key, also the last 30 bytes of the server handshake are applied */
-      else if (r->Link.SWFSize)
+      if (r->Link.SWFSize)
 	{
 	  RTMP_SendCtrl(r, 0x1B, 0, 0);
 	}
@@ -3142,8 +3469,18 @@ HandShake(RTMP *r, int FP9HandShake)
       serversig[4], serversig[5], serversig[6], serversig[7]);
 
   /* 2nd part of handshake */
-  if (!WriteN(r, serversig, RTMP_SIG_SIZE))
-    return FALSE;
+  if (r->Link.CombineConnectPacket)
+    {
+      char *HandshakeResponse = malloc(RTMP_SIG_SIZE);
+      memcpy(HandshakeResponse, (char *) serversig, RTMP_SIG_SIZE);
+      r->Link.HandshakeResponse.av_val = HandshakeResponse;
+      r->Link.HandshakeResponse.av_len = RTMP_SIG_SIZE;
+    }
+  else
+    {
+      if (!WriteN(r, (char *) serversig, RTMP_SIG_SIZE))
+        return FALSE;
+    }
 
   if (ReadN(r, serversig, RTMP_SIG_SIZE) != RTMP_SIG_SIZE)
     return FALSE;
@@ -3709,12 +4046,11 @@ HTTP_Post(RTMP *r, RTMPTCmd cmd, const char *buf, int len)
   char hbuf[512];
   int hlen = snprintf(hbuf, sizeof(hbuf), "POST /%s%s/%d HTTP/1.1\r\n"
     "Host: %.*s:%d\r\n"
-    "Accept: */*\r\n"
-    "User-Agent: Shockwave Flash\n"
-    "Connection: Keep-Alive\n"
+    "User-Agent: Shockwave Flash\r\n"
+    "Connection: Keep-Alive\r\n"
     "Cache-Control: no-cache\r\n"
-    "Content-type: application/x-fcs\r\n"
-    "Content-length: %d\r\n\r\n", RTMPT_cmds[cmd],
+    "Content-Type: application/x-fcs\r\n"
+    "Content-Length: %d\r\n\r\n", RTMPT_cmds[cmd],
     r->m_clientID.av_val ? r->m_clientID.av_val : "",
     r->m_msgCounter, r->Link.hostname.av_len, r->Link.hostname.av_val,
     r->Link.port, len);
@@ -3749,6 +4085,14 @@ HTTP_read(RTMP *r, int fill)
   if (!ptr)
     return -1;
   ptr += 4;
+  int resplen = r->m_sb.sb_size - (ptr - r->m_sb.sb_start);
+  if (hlen < 4096)
+    while (resplen < hlen)
+      {
+        if (RTMPSockBuf_Fill(&r->m_sb) == -1)
+          return -1;
+        resplen = r->m_sb.sb_size - (ptr - r->m_sb.sb_start);
+      }
   r->m_sb.sb_size -= ptr - r->m_sb.sb_start;
   r->m_sb.sb_start = ptr;
   r->m_unackd--;
@@ -4301,13 +4645,21 @@ fail:
 		  r->m_read.status = nRead;
 		  goto fail;
 		}
-	      /* buffer overflow, fix buffer and give up */
-	      if (r->m_read.buf < mybuf || r->m_read.buf > end) {
-	      	mybuf = realloc(mybuf, cnt + nRead);
-		memcpy(mybuf+cnt, r->m_read.buf, nRead);
-		r->m_read.buf = mybuf+cnt+nRead;
-	        break;
-	      }
+              /* buffer overflow, fix buffer and give up */
+              if (r->m_read.buf < mybuf || r->m_read.buf > end)
+                {
+                  if (!cnt)
+                    {
+                      mybuf = realloc(mybuf, sizeof (flvHeader) + cnt + nRead);
+                      memcpy(mybuf, flvHeader, sizeof (flvHeader));
+                      cnt += sizeof (flvHeader);
+                    }
+                  else
+                    mybuf = realloc(mybuf, cnt + nRead);
+                  memcpy(mybuf + cnt, r->m_read.buf, nRead);
+                  r->m_read.buf = mybuf + cnt + nRead;
+                  break;
+                }
 	      cnt += nRead;
 	      r->m_read.buf += nRead;
 	      r->m_read.buflen -= nRead;
@@ -4458,3 +4810,90 @@ RTMP_Write(RTMP *r, const char *buf, int size)
     }
   return size+s2;
 }
+
+static int
+SendInvoke(RTMP *r, AVal *Command, int queue)
+{
+  RTMPPacket packet;
+  char pbuf[512], *enc;
+
+  packet.m_nChannel = 0x03; /* control channel (invoke) */
+  packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM;
+  packet.m_packetType = RTMP_PACKET_TYPE_INVOKE;
+  packet.m_nTimeStamp = 0;
+  packet.m_nInfoField2 = 0;
+  packet.m_hasAbsTimestamp = 0;
+  packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;
+
+  enc = packet.m_body;
+  if (Command->av_len)
+    {
+      memcpy(enc, Command->av_val, Command->av_len);
+      enc += Command->av_len;
+    }
+  else
+    return FALSE;
+  packet.m_nBodySize = enc - packet.m_body;
+
+  return RTMP_SendPacket(r, &packet, queue);
+}
+
+static int
+strsplit(char *src, int srclen, char delim, char ***params)
+{
+  char *sptr, *srcbeg, *srcend, *dstr;
+  int count = 1, i = 0, len = 0;
+
+  if (src == NULL)
+    return 0;
+  if (!srclen)
+    srclen = strlen(src);
+  srcbeg = src;
+  srcend = srcbeg + srclen;
+  sptr = srcbeg;
+
+  /* count the delimiters */
+  while (sptr < srcend)
+    {
+      if (*sptr++ == delim)
+        count++;
+    }
+  sptr = srcbeg;
+  *params = calloc(count, sizeof (size_t));
+  char **param = *params;
+
+  for (i = 0; i < (count - 1); i++)
+    {
+      dstr = strchr(sptr, delim);
+      len = dstr - sptr;
+      param[i] = calloc(len + 1, sizeof (char));
+      strncpy(param[i], sptr, len);
+      sptr += len + 1;
+    }
+
+  /* copy the last string */
+  if (sptr <= srcend)
+    {
+      len = srclen - (sptr - srcbeg);
+      param[i] = calloc(len + 1, sizeof (char));
+      strncpy(param[i], sptr, len);
+    }
+  return count;
+}
+
+static int
+SendGetStreamLength(RTMP *r)
+{
+  char pbuf[256], *pend = pbuf + sizeof (pbuf), *enc;
+  AVal av_Command;
+
+  enc = pbuf;
+  enc = AMF_EncodeString(enc, pend, &av_getStreamLength);
+  enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes);
+  *enc++ = AMF_NULL;
+  enc = AMF_EncodeString(enc, pend, &r->Link.playpath);
+  av_Command.av_val = pbuf;
+  av_Command.av_len = enc - pbuf;
+
+  return SendInvoke(r, &av_Command, TRUE);
+}
diff --git a/librtmp/rtmp.h b/librtmp/rtmp.h
index 6b2ae5b..411b488 100644
--- a/librtmp/rtmp.h
+++ b/librtmp/rtmp.h
@@ -150,12 +150,14 @@ extern "C"
     AVal playpath;	/* passed in explicitly */
     AVal tcUrl;
     AVal swfUrl;
+    AVal swfHash;
     AVal pageUrl;
     AVal app;
     AVal auth;
     AVal flashVer;
     AVal subscribepath;
     AVal usherToken;
+    AVal WeebToken;
     AVal token;
     AMFObject extras;
     int edepth;
@@ -172,9 +174,15 @@ extern "C"
     int lFlags;
 
     int swfAge;
+    int swfSize;
 
     int protocol;
+    int ConnectPacket;
+    int CombineConnectPacket;
+    int redirected;
     int timeout;		/* connection timeout in seconds */
+    AVal Extras;
+    AVal HandshakeResponse;
 
     unsigned short socksport;
     unsigned short port;
@@ -299,6 +307,7 @@ extern "C"
 			AVal *flashVer,
 			AVal *subscribepath,
 			AVal *usherToken,
+			AVal *WeebToken,
 			int dStart,
 			int dStop, int bLiveStream, long int timeout);
 
diff --git a/librtmp/rtmp_sys.h b/librtmp/rtmp_sys.h
index c3fd4a6..1bfb562 100644
--- a/librtmp/rtmp_sys.h
+++ b/librtmp/rtmp_sys.h
@@ -64,6 +64,7 @@
 #include <polarssl/net.h>
 #include <polarssl/ssl.h>
 #include <polarssl/havege.h>
+#include <polarssl/md5.h>
 typedef struct tls_ctx {
 	havege_state hs;
 	ssl_session ssn;
@@ -71,7 +72,7 @@ typedef struct tls_ctx {
 #define TLS_CTX tls_ctx *
 #define TLS_client(ctx,s)	s = malloc(sizeof(ssl_context)); ssl_init(s);\
 	ssl_set_endpoint(s, SSL_IS_CLIENT); ssl_set_authmode(s, SSL_VERIFY_NONE);\
-	ssl_set_rng(s, havege_rand, &ctx->hs);\
+	ssl_set_rng(s, havege_random, &ctx->hs);\
 	ssl_set_ciphersuites(s, ssl_default_ciphersuites);\
 	ssl_set_session(s, 1, 600, &ctx->ssn)
 #define TLS_setfd(s,fd)	ssl_set_bio(s, net_recv, &fd, net_send, &fd)
@@ -80,6 +81,7 @@ typedef struct tls_ctx {
 #define TLS_write(s,b,l)	ssl_write(s,(unsigned char *)b,l)
 #define TLS_shutdown(s)	ssl_close_notify(s)
 #define TLS_close(s)	ssl_free(s); free(s)
+#define md5_hash(i, ilen, o) md5(i, ilen, o)
 
 #elif defined(USE_GNUTLS)
 #include <gnutls/gnutls.h>
@@ -95,6 +97,8 @@ typedef struct tls_ctx {
 #define TLS_write(s,b,l)	gnutls_record_send(s,b,l)
 #define TLS_shutdown(s)	gnutls_bye(s, GNUTLS_SHUT_RDWR)
 #define TLS_close(s)	gnutls_deinit(s)
+#define md5_hash(i, ilen, o) gnutls_digest_algorithm_t algorithm = GNUTLS_DIG_MD5;\
+                             gnutls_hash_fast(algorithm, i, ilen, o);
 
 #else	/* USE_OPENSSL */
 #define TLS_CTX	SSL_CTX *
@@ -105,6 +109,7 @@ typedef struct tls_ctx {
 #define TLS_write(s,b,l)	SSL_write(s,b,l)
 #define TLS_shutdown(s)	SSL_shutdown(s)
 #define TLS_close(s)	SSL_free(s)
+#define md5_hash(i, ilen, o) MD5(i, ilen, o)
 
 #endif
 #endif
diff --git a/rtmpdump.c b/rtmpdump.c
index e52f7d4..7bb0890 100644
--- a/rtmpdump.c
+++ b/rtmpdump.c
@@ -701,6 +701,8 @@ void usage(char *prog)
 	  RTMP_LogPrintf
 	    ("--jtv|-j JSON           Authentication token for Justin.tv legacy servers\n");
 	  RTMP_LogPrintf
+	    ("--weeb|-J string        Authentication token for weeb.tv servers\n");
+	  RTMP_LogPrintf
 	    ("--hashes|-#             Display progress with hashes, not with the byte counter\n");
 	  RTMP_LogPrintf
 	    ("--buffer|-b             Buffer time in milliseconds (default: %u)\n",
@@ -747,7 +749,8 @@ main(int argc, char **argv)
   AVal hostname = { 0, 0 };
   AVal playpath = { 0, 0 };
   AVal subscribepath = { 0, 0 };
-  AVal usherToken = { 0, 0 }; //Justin.tv auth token
+  AVal usherToken = { 0, 0 }; // Justin.tv auth token
+  AVal WeebToken = { 0, 0 };  // Weeb.tv auth token
   int port = -1;
   int protocol = RTMP_PROTOCOL_UNDEFINED;
   int retries = 0;
@@ -852,12 +855,13 @@ main(int argc, char **argv)
     {"quiet", 0, NULL, 'q'},
     {"verbose", 0, NULL, 'V'},
     {"jtv", 1, NULL, 'j'},
+    {"weeb", 1, NULL, 'J'},
     {0, 0, 0, 0}
   };
 
   while ((opt =
 	  getopt_long(argc, argv,
-		      "hVveqzRr:s:t:p:a:b:f:o:u:C:n:c:l:y:Ym:k:d:A:B:T:w:x:W:X:S:#j:",
+		      "hVveqzr:s:t:p:a:b:f:o:u:C:n:c:l:y:Ym:k:d:A:B:T:w:x:W:X:S:#j:J:",
 		      longopts, NULL)) != -1)
     {
       switch (opt)
@@ -1070,6 +1074,9 @@ main(int argc, char **argv)
 	case 'j':
 	  STR2AVAL(usherToken, optarg);
 	  break;
+	case 'J':
+	  STR2AVAL(WeebToken, optarg);
+	  break;
 	default:
 	  RTMP_LogPrintf("unknown option: %c\n", opt);
 	  usage(argv[0]);
@@ -1161,14 +1168,14 @@ main(int argc, char **argv)
 
   if (tcUrl.av_len == 0)
     {
-	  tcUrl.av_len = strlen(RTMPProtocolStringsLower[protocol]) +
-	  	hostname.av_len + app.av_len + sizeof("://:65535/");
+      tcUrl.av_len = strlen(RTMPProtocolStringsLower[protocol]) +
+              hostname.av_len + app.av_len + sizeof ("://:65535/");
       tcUrl.av_val = (char *) malloc(tcUrl.av_len);
-	  if (!tcUrl.av_val)
-	    return RD_FAILED;
+      if (!tcUrl.av_val)
+        return RD_FAILED;
       tcUrl.av_len = snprintf(tcUrl.av_val, tcUrl.av_len, "%s://%.*s:%d/%.*s",
-	  	   RTMPProtocolStringsLower[protocol], hostname.av_len,
-		   hostname.av_val, port, app.av_len, app.av_val);
+                              RTMPProtocolStringsLower[protocol], hostname.av_len,
+                              hostname.av_val, port, app.av_len, app.av_val);
     }
 
   int first = 1;
@@ -1187,7 +1194,7 @@ main(int argc, char **argv)
 
   RTMP_SetupStream(&rtmp, protocol, &hostname, port, &sockshost, &playpath,
 		   &tcUrl, &swfUrl, &pageUrl, &app, &auth, &swfHash, swfSize,
-		   &flashVer, &subscribepath, &usherToken, dSeek, dStopOffset, bLiveStream, timeout);
+		   &flashVer, &subscribepath, &usherToken, &WeebToken, dSeek, dStopOffset, bLiveStream, timeout);
 
   /* Try to keep the stream moving if it pauses on us */
   if (!bLiveStream && !bRealtimeStream && !(protocol & RTMP_FEATURE_HTTP))
diff --git a/rtmpgw.c b/rtmpgw.c
index 0cf56bb..cd4396d 100644
--- a/rtmpgw.c
+++ b/rtmpgw.c
@@ -95,7 +95,8 @@ typedef struct
   AVal flashVer;
   AVal token;
   AVal subscribepath;
-  AVal usherToken; //Justin.tv auth token
+  AVal usherToken; // Justin.tv auth token
+  AVal WeebToken;  // Weeb.tv auth token
   AVal sockshost;
   AMFObject extras;
   int edepth;
@@ -553,7 +554,7 @@ void processTCPrequest(STREAMING_SERVER * server,	// server socket and state (ou
   RTMP_Init(&rtmp);
   RTMP_SetBufferMS(&rtmp, req.bufferTime);
   RTMP_SetupStream(&rtmp, req.protocol, &req.hostname, req.rtmpport, &req.sockshost,
-		   &req.playpath, &req.tcUrl, &req.swfUrl, &req.pageUrl, &req.app, &req.auth, &req.swfHash, req.swfSize, &req.flashVer, &req.subscribepath, &req.usherToken, dSeek, req.dStopOffset,
+		   &req.playpath, &req.tcUrl, &req.swfUrl, &req.pageUrl, &req.app, &req.auth, &req.swfHash, req.swfSize, &req.flashVer, &req.subscribepath, &req.usherToken, &req.WeebToken, dSeek, req.dStopOffset,
 		   req.bLiveStream, req.timeout);
   /* backward compatibility, we always sent this as true before */
   if (req.auth.av_len)
@@ -957,6 +958,9 @@ ParseOption(char opt, char *arg, RTMP_REQUEST * req)
     case 'j':
       STR2AVAL(req->usherToken, arg);
       break;
+    case 'J':
+      STR2AVAL(req->WeebToken, arg);
+      break;
     default:
       RTMP_LogPrintf("unknown option: %c, arg: %s\n", opt, arg);
       return FALSE;
@@ -1028,6 +1032,7 @@ main(int argc, char **argv)
     {"quiet", 0, NULL, 'q'},
     {"verbose", 0, NULL, 'V'},
     {"jtv", 1, NULL, 'j'},
+    {"weeb", 1, NULL, 'J'},
     {0, 0, 0, 0}
   };
 
@@ -1040,7 +1045,7 @@ main(int argc, char **argv)
 
   while ((opt =
 	  getopt_long(argc, argv,
-		      "hvqVzr:s:t:p:a:f:u:n:c:l:y:m:d:D:A:B:T:g:w:x:W:X:S:j:", longopts,
+		      "hvqVzr:s:t:p:a:f:u:n:c:l:y:m:d:D:A:B:T:g:w:x:W:X:S:j:J:", longopts,
 		      NULL)) != -1)
     {
       switch (opt)
@@ -1103,6 +1108,8 @@ main(int argc, char **argv)
 	  RTMP_LogPrintf
 	    ("--jtv|-j JSON           Authentication token for Justin.tv legacy servers\n");
 	  RTMP_LogPrintf
+	    ("--weeb|-J string        Authentication token for weeb.tv servers\n");
+	  RTMP_LogPrintf
 	    ("--buffer|-b             Buffer time in milliseconds (default: %u)\n\n",
 	     defaultRTMPRequest.bufferTime);
 
diff --git a/rtmpsrv.c b/rtmpsrv.c
index 9aa62f3..9ec8f23 100644
--- a/rtmpsrv.c
+++ b/rtmpsrv.c
@@ -96,9 +96,20 @@ STREAMING_SERVER *rtmpServer = 0;	// server structure pointer
 STREAMING_SERVER *startStreaming(const char *address, int port);
 void stopStreaming(STREAMING_SERVER * server);
 void AVreplace(AVal *src, const AVal *orig, const AVal *repl);
+char *strreplace(char *srcstr, int srclen, char *orig, char *repl);
+int file_exists(const char *fname);
+int SendCheckBWResponse(RTMP *r, int oldMethodType, int onBWDoneInit);
+AVal AVcopy(AVal src);
+AVal StripParams(AVal *src);
 
 static const AVal av_dquote = AVC("\"");
 static const AVal av_escdquote = AVC("\\\"");
+#ifdef WIN32
+static const AVal av_caret = AVC("^");
+static const AVal av_esccaret = AVC("^^");
+static const AVal av_pipe = AVC("|");
+static const AVal av_escpipe = AVC("^|");
+#endif
 
 typedef struct
 {
@@ -167,6 +178,10 @@ SAVC(level);
 SAVC(code);
 SAVC(description);
 SAVC(secureToken);
+SAVC(_checkbw);
+SAVC(_onbwdone);
+SAVC(checkBandwidth);
+SAVC(onBWDone);
 
 static int
 SendConnectResult(RTMP *r, double txn)
@@ -190,7 +205,7 @@ SendConnectResult(RTMP *r, double txn)
   enc = AMF_EncodeNumber(enc, pend, txn);
   *enc++ = AMF_OBJECT;
 
-  STR2AVAL(av, "FMS/3,5,1,525");
+  STR2AVAL(av, "FMS/3,5,7,7009");
   enc = AMF_EncodeNamedString(enc, pend, &av_fmsVer, &av);
   enc = AMF_EncodeNamedNumber(enc, pend, &av_capabilities, 31.0);
   enc = AMF_EncodeNamedNumber(enc, pend, &av_mode, 1.0);
@@ -212,7 +227,7 @@ SendConnectResult(RTMP *r, double txn)
   enc = AMF_EncodeNamedString(enc, pend, &av_secureToken, &av);
 #endif
   STR2AVAL(p.p_name, "version");
-  STR2AVAL(p.p_vu.p_aval, "3,5,1,525");
+  STR2AVAL(p.p_vu.p_aval, "3,5,7,7009");
   p.p_type = AMF_STRING;
   obj.o_num = 1;
   obj.o_props = &p;
@@ -268,7 +283,7 @@ static int
 SendPlayStart(RTMP *r)
 {
   RTMPPacket packet;
-  char pbuf[512], *pend = pbuf+sizeof(pbuf);
+  char pbuf[1024], *pend = pbuf + sizeof (pbuf);
 
   packet.m_nChannel = 0x03;     // control channel (invoke)
   packet.m_headerType = 1; /* RTMP_PACKET_SIZE_MEDIUM; */
@@ -300,7 +315,7 @@ static int
 SendPlayStop(RTMP *r)
 {
   RTMPPacket packet;
-  char pbuf[512], *pend = pbuf+sizeof(pbuf);
+  char pbuf[1024], *pend = pbuf + sizeof (pbuf);
 
   packet.m_nChannel = 0x03;     // control channel (invoke)
   packet.m_headerType = 1; /* RTMP_PACKET_SIZE_MEDIUM; */
@@ -328,6 +343,49 @@ SendPlayStop(RTMP *r)
   return RTMP_SendPacket(r, &packet, FALSE);
 }
 
+int
+SendCheckBWResponse(RTMP *r, int oldMethodType, int onBWDoneInit)
+{
+  RTMPPacket packet;
+  char pbuf[256], *pend = pbuf + sizeof (pbuf);
+  char *enc;
+
+  packet.m_nChannel = 0x03; /* control channel (invoke) */
+  packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM;
+  packet.m_packetType = RTMP_PACKET_TYPE_INVOKE;
+  packet.m_nTimeStamp = 0;
+  packet.m_nInfoField2 = 0;
+  packet.m_hasAbsTimestamp = 0;
+  packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;
+
+  enc = packet.m_body;
+  if (oldMethodType)
+    {
+      enc = AMF_EncodeString(enc, pend, &av__onbwdone);
+      enc = AMF_EncodeNumber(enc, pend, 0);
+      *enc++ = AMF_NULL;
+      enc = AMF_EncodeNumber(enc, pend, 10240);
+      enc = AMF_EncodeNumber(enc, pend, 10240);
+    }
+  else
+    {
+      enc = AMF_EncodeString(enc, pend, &av_onBWDone);
+      enc = AMF_EncodeNumber(enc, pend, 0);
+      *enc++ = AMF_NULL;
+      if (!onBWDoneInit)
+        {
+          enc = AMF_EncodeNumber(enc, pend, 10240);
+          enc = AMF_EncodeNumber(enc, pend, 10240);
+          enc = AMF_EncodeNumber(enc, pend, 0);
+          enc = AMF_EncodeNumber(enc, pend, 0);
+        }
+    }
+
+  packet.m_nBodySize = enc - packet.m_body;
+
+  return RTMP_SendPacket(r, &packet, FALSE);
+}
+
 static void
 spawn_dumper(int argc, AVal *av, char *cmd)
 {
@@ -568,6 +626,7 @@ ServeInvoke(STREAMING_SERVER *server, RTMP * r, RTMPPacket *packet, unsigned int
 	  server->arglen += countAMF(&r->Link.extras, &server->argc);
 	}
       SendConnectResult(r, txn);
+      SendCheckBWResponse(r, FALSE, TRUE);
     }
   else if (AVMATCH(&method, &av_createStream))
     {
@@ -582,10 +641,22 @@ ServeInvoke(STREAMING_SERVER *server, RTMP * r, RTMPPacket *packet, unsigned int
       AVal usherToken;
       AMFProp_GetString(AMF_GetProp(&obj, NULL, 3), &usherToken);
       AVreplace(&usherToken, &av_dquote, &av_escdquote);
+#ifdef WIN32
+      AVreplace(&usherToken, &av_caret, &av_esccaret);
+      AVreplace(&usherToken, &av_pipe, &av_escpipe);
+#endif
       server->arglen += 6 + usherToken.av_len;
       server->argc += 2;
       r->Link.usherToken = usherToken;
     }
+  else if (AVMATCH(&method, &av__checkbw))
+    {
+      SendCheckBWResponse(r, TRUE, FALSE);
+    }
+  else if (AVMATCH(&method, &av_checkBandwidth))
+    {
+      SendCheckBWResponse(r, FALSE, FALSE);
+    }
   else if (AVMATCH(&method, &av_play))
     {
       char *file, *p, *q, *cmd, *ptr;
@@ -599,6 +670,17 @@ ServeInvoke(STREAMING_SERVER *server, RTMP * r, RTMPPacket *packet, unsigned int
       if (obj.o_num > 5)
 	r->Link.length = AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 5));
       */
+      double StartFlag = 0;
+      AMFObjectProperty *Start = AMF_GetProp(&obj, NULL, 4);
+      if (!(Start->p_type == AMF_INVALID))
+        StartFlag = AMFProp_GetNumber(Start);
+      r->Link.app = AVcopy(r->Link.app);
+      if (StartFlag == -1000 || strstr(r->Link.app.av_val, "live"))
+        {
+          StartFlag = -1000;
+          server->arglen += 7;
+          server->argc += 1;
+        }
       if (r->Link.tcUrl.av_len)
 	{
 	  len = server->arglen + r->Link.playpath.av_len + 4 +
@@ -616,6 +698,7 @@ ServeInvoke(STREAMING_SERVER *server, RTMP * r, RTMPPacket *packet, unsigned int
 	  argv[argc].av_val = ptr + 1;
 	  argv[argc++].av_len = 2;
 	  argv[argc].av_val = ptr + 5;
+	  r->Link.tcUrl = StripParams(&r->Link.tcUrl);
 	  ptr += sprintf(ptr," -r \"%s\"", r->Link.tcUrl.av_val);
 	  argv[argc++].av_len = r->Link.tcUrl.av_len;
 
@@ -640,6 +723,7 @@ ServeInvoke(STREAMING_SERVER *server, RTMP * r, RTMPPacket *packet, unsigned int
 	      argv[argc].av_val = ptr + 1;
 	      argv[argc++].av_len = 2;
 	      argv[argc].av_val = ptr + 5;
+	      r->Link.swfUrl = StripParams(&r->Link.swfUrl);
 	      ptr += sprintf(ptr, " -W \"%s\"", r->Link.swfUrl.av_val);
 	      argv[argc++].av_len = r->Link.swfUrl.av_len;
 	    }
@@ -662,10 +746,17 @@ ServeInvoke(STREAMING_SERVER *server, RTMP * r, RTMPPacket *packet, unsigned int
 	      r->Link.usherToken.av_val = NULL;
 	      r->Link.usherToken.av_len = 0;
 	    }
-	  if (r->Link.extras.o_num) {
-	    ptr = dumpAMF(&r->Link.extras, ptr, argv, &argc);
-	    AMF_Reset(&r->Link.extras);
-	  }
+          if (StartFlag == -1000)
+            {
+              argv[argc].av_val = ptr + 1;
+              argv[argc++].av_len = 6;
+              ptr += sprintf(ptr, " --live");
+            }
+          if (r->Link.extras.o_num)
+            {
+              ptr = dumpAMF(&r->Link.extras, ptr, argv, &argc);
+              AMF_Reset(&r->Link.extras);
+            }
 	  argv[argc].av_val = ptr + 1;
 	  argv[argc++].av_len = 2;
 	  argv[argc].av_val = ptr + 5;
@@ -673,7 +764,13 @@ ServeInvoke(STREAMING_SERVER *server, RTMP * r, RTMPPacket *packet, unsigned int
 	    r->Link.playpath.av_len, r->Link.playpath.av_val);
 	  argv[argc++].av_len = r->Link.playpath.av_len;
 
-	  av = r->Link.playpath;
+          if (r->Link.playpath.av_len)
+            av = r->Link.playpath;
+          else
+            {
+              av.av_val = "file";
+              av.av_len = 4;
+            }
 	  /* strip trailing URL parameters */
 	  q = memchr(av.av_val, '?', av.av_len);
 	  if (q)
@@ -725,7 +822,30 @@ ServeInvoke(STREAMING_SERVER *server, RTMP * r, RTMPPacket *packet, unsigned int
 	  argv[argc++].av_len = 2;
 	  argv[argc].av_val = file;
 	  argv[argc].av_len = av.av_len;
-	  ptr += sprintf(ptr, " -o %s", file);
+#ifdef VLC
+          char *vlc;
+          int didAlloc = FALSE;
+
+          if (getenv("VLC"))
+            vlc = getenv("VLC");
+          else if (getenv("ProgramFiles"))
+            {
+              vlc = malloc(512 * sizeof (char));
+              didAlloc = TRUE;
+              char *ProgramFiles = getenv("ProgramFiles");
+              sprintf(vlc, "%s%s", ProgramFiles, " (x86)\\VideoLAN\\VLC\\vlc.exe");
+              if (!file_exists(vlc))
+                sprintf(vlc, "%s%s", ProgramFiles, "\\VideoLAN\\VLC\\vlc.exe");
+            }
+          else
+            vlc = "C:\\Program Files\\VideoLAN\\VLC\\vlc.exe";
+
+          ptr += sprintf(ptr, " | %s -", vlc);
+          if (didAlloc)
+            free(vlc);
+#else
+          ptr += sprintf(ptr, " -o %s", file);
+#endif
 	  now = RTMP_GetTime();
 	  if (now - server->filetime < DUPTIME && AVMATCH(&argv[argc], &server->filename))
 	    {
@@ -739,7 +859,23 @@ ServeInvoke(STREAMING_SERVER *server, RTMP * r, RTMPPacket *packet, unsigned int
 	      server->filetime = now;
 	      free(server->filename.av_val);
 	      server->filename = argv[argc++];
-	      spawn_dumper(argc, argv, cmd);
+#ifdef VLC
+              FILE *vlc_cmdfile = fopen("VLC.bat", "w");
+              char *vlc_batchcmd = strreplace(cmd, 0, "%", "%%");
+              fprintf(vlc_cmdfile, "%s\n", vlc_batchcmd);
+              fclose(vlc_cmdfile);
+              free(vlc_batchcmd);
+              spawn_dumper(argc, argv, "VLC.bat");
+#else
+              spawn_dumper(argc, argv, cmd);
+#endif
+
+#ifdef WIN32
+              // Dump command to batch file
+              FILE *cmdfile = fopen("Command.bat", "a");
+              fprintf(cmdfile, "%s\n", cmd);
+              fclose(cmdfile);
+#endif
 	    }
 
 	  free(cmd);
@@ -1178,3 +1314,115 @@ AVreplace(AVal *src, const AVal *orig, const AVal *repl)
   src->av_val = dest;
   src->av_len = dptr - dest;
 }
+
+char *
+strreplace(char *srcstr, int srclen, char *orig, char *repl)
+{
+  char *ptr = NULL, *sptr = srcstr;
+  int origlen = strlen(orig);
+  int repllen = strlen(repl);
+  if (!srclen)
+    srclen = strlen(srcstr);
+  char *srcend = srcstr + srclen;
+  int dstbuffer = srclen / origlen * repllen;
+  if (dstbuffer < srclen)
+    dstbuffer = srclen;
+  char *dststr = calloc(dstbuffer + 1, sizeof (char));
+  char *dptr = dststr;
+
+  if ((ptr = strstr(srcstr, orig)))
+    {
+      while (ptr < srcend && (ptr = strstr(sptr, orig)))
+        {
+          int len = ptr - sptr;
+          memcpy(dptr, sptr, len);
+          sptr += len + origlen;
+          dptr += len;
+          memcpy(dptr, repl, repllen);
+          dptr += repllen;
+        }
+      memcpy(dptr, sptr, srcend - sptr);
+      return dststr;
+    }
+
+  memcpy(dststr, srcstr, srclen);
+  return dststr;
+}
+
+AVal
+StripParams(AVal *src)
+{
+  AVal str;
+  if (src->av_val)
+    {
+      str.av_val = calloc(src->av_len + 1, sizeof (char));
+      strncpy(str.av_val, src->av_val, src->av_len);
+      str.av_len = src->av_len;
+      char *start = str.av_val;
+      char *end = start + str.av_len;
+      char *ptr = start;
+
+      while (ptr < end)
+        {
+          if (*ptr == '?')
+            {
+              str.av_len = ptr - start;
+              break;
+            }
+          ptr++;
+        }
+      memset(start + str.av_len, 0, 1);
+
+      char *dynamic = strstr(start, "[[DYNAMIC]]");
+      if (dynamic)
+        {
+          dynamic -= 1;
+          memset(dynamic, 0, 1);
+          str.av_len = dynamic - start;
+          end = start + str.av_len;
+        }
+
+      char *import = strstr(start, "[[IMPORT]]");
+      if (import)
+        {
+          str.av_val = import + 11;
+          strcpy(start, "http://");
+          str.av_val = strcat(start, str.av_val);
+          str.av_len = strlen(str.av_val);
+        }
+      return str;
+    }
+  str = *src;
+  return str;
+}
+
+int
+file_exists(const char *fname)
+{
+  FILE *file;
+  if ((file = fopen(fname, "r")))
+    {
+      fclose(file);
+      return TRUE;
+    }
+  return FALSE;
+}
+
+AVal
+AVcopy(AVal src)
+{
+  AVal dst;
+  if (src.av_len)
+    {
+      dst.av_val = malloc(src.av_len + 1);
+      memcpy(dst.av_val, src.av_val, src.av_len);
+      dst.av_val[src.av_len] = '\0';
+      dst.av_len = src.av_len;
+    }
+  else
+    {
+      dst.av_val = NULL;
+      dst.av_len = 0;
+    }
+  return dst;
+}
diff --git a/rtmpsuck.c b/rtmpsuck.c
index e886179..e80c686 100644
--- a/rtmpsuck.c
+++ b/rtmpsuck.c
@@ -143,15 +143,18 @@ SAVC(onStatus);
 SAVC(close);
 static const AVal av_NetStream_Failed = AVC("NetStream.Failed");
 static const AVal av_NetStream_Play_Failed = AVC("NetStream.Play.Failed");
-static const AVal av_NetStream_Play_StreamNotFound =
-AVC("NetStream.Play.StreamNotFound");
-static const AVal av_NetConnection_Connect_InvalidApp =
-AVC("NetConnection.Connect.InvalidApp");
+static const AVal av_NetStream_Play_StreamNotFound = AVC("NetStream.Play.StreamNotFound");
+static const AVal av_NetConnection_Connect_InvalidApp = AVC("NetConnection.Connect.InvalidApp");
 static const AVal av_NetStream_Play_Start = AVC("NetStream.Play.Start");
 static const AVal av_NetStream_Play_Complete = AVC("NetStream.Play.Complete");
 static const AVal av_NetStream_Play_Stop = AVC("NetStream.Play.Stop");
+static const AVal av_NetStream_Authenticate_UsherToken = AVC("NetStream.Authenticate.UsherToken");
 
 static const char *cst[] = { "client", "server" };
+char *dumpAMF(AMFObject *obj, char *ptr);
+char *strreplace(char *srcstr, int srclen, char *orig, char *repl);
+AVal AVcopy(AVal src);
+AVal StripParams(AVal *src);
 
 // Returns 0 for OK/Failed/error, 1 for 'Stop or Complete'
 int
@@ -198,26 +201,28 @@ ServeInvoke(STREAMING_SERVER *server, int which, RTMPPacket *pack, const char *b
           if (cobj.o_props[i].p_type == AMF_STRING)
             {
               pval = cobj.o_props[i].p_vu.p_aval;
-              RTMP_LogPrintf("%.*s: %.*s\n", pname.av_len, pname.av_val, pval.av_len, pval.av_val);
+              RTMP_LogPrintf("%10.*s : %.*s\n", pname.av_len, pname.av_val, pval.av_len, pval.av_val);
             }
           if (AVMATCH(&pname, &av_app))
             {
-              server->rc.Link.app = pval;
+              server->rc.Link.app = AVcopy(pval);
               pval.av_val = NULL;
             }
           else if (AVMATCH(&pname, &av_flashVer))
             {
-              server->rc.Link.flashVer = pval;
+              server->rc.Link.flashVer = AVcopy(pval);
               pval.av_val = NULL;
             }
           else if (AVMATCH(&pname, &av_swfUrl))
             {
 #ifdef CRYPTO
               if (pval.av_val)
-	        RTMP_HashSWF(pval.av_val, &server->rc.Link.SWFSize,
-		  (unsigned char *)server->rc.Link.SWFHash, 30);
+                {
+                  AVal swfUrl = StripParams(&pval);
+                  RTMP_HashSWF(swfUrl.av_val, &server->rc.Link.SWFSize, (unsigned char *) server->rc.Link.SWFHash, 30);
+                }
 #endif
-              server->rc.Link.swfUrl = pval;
+              server->rc.Link.swfUrl = AVcopy(pval);
               pval.av_val = NULL;
             }
           else if (AVMATCH(&pname, &av_tcUrl))
@@ -225,7 +230,7 @@ ServeInvoke(STREAMING_SERVER *server, int which, RTMPPacket *pack, const char *b
               char *r1 = NULL, *r2;
               int len;
 
-              server->rc.Link.tcUrl = pval;
+              server->rc.Link.tcUrl = AVcopy(pval);
               if ((pval.av_val[0] | 0x40) == 'r' &&
                   (pval.av_val[1] | 0x40) == 't' &&
                   (pval.av_val[2] | 0x40) == 'm' &&
@@ -267,7 +272,7 @@ ServeInvoke(STREAMING_SERVER *server, int which, RTMPPacket *pack, const char *b
             }
           else if (AVMATCH(&pname, &av_pageUrl))
             {
-              server->rc.Link.pageUrl = pval;
+              server->rc.Link.pageUrl = AVcopy(pval);
               pval.av_val = NULL;
             }
           else if (AVMATCH(&pname, &av_audioCodecs))
@@ -287,14 +292,21 @@ ServeInvoke(STREAMING_SERVER *server, int which, RTMPPacket *pack, const char *b
           if (pval.av_val)
             free(pval.av_val);
         }
+
       if (obj.o_num > 3)
         {
-          if (AMFProp_GetBoolean(&obj.o_props[3]))
-            server->rc.Link.lFlags |= RTMP_LF_AUTH;
-          if (obj.o_num > 4)
-          {
-            AMFProp_GetString(&obj.o_props[4], &server->rc.Link.auth);
-          }
+          int i = obj.o_num - 3;
+          server->rc.Link.extras.o_num = i;
+          server->rc.Link.extras.o_props = malloc(i * sizeof (AMFObjectProperty));
+          memcpy(server->rc.Link.extras.o_props, obj.o_props + 3, i * sizeof (AMFObjectProperty));
+          obj.o_num = 3;
+        }
+
+      if (server->rc.Link.extras.o_num)
+        {
+          server->rc.Link.Extras.av_val = calloc(1024, sizeof (char));
+          dumpAMF(&server->rc.Link.extras, server->rc.Link.Extras.av_val);
+          server->rc.Link.Extras.av_len = strlen(server->rc.Link.Extras.av_val);
         }
 
       if (!RTMP_Connect(&server->rc, pack))
@@ -303,6 +315,16 @@ ServeInvoke(STREAMING_SERVER *server, int which, RTMPPacket *pack, const char *b
           return 1;
         }
       server->rc.m_bSendCounter = FALSE;
+
+      if (server->rc.Link.extras.o_props)
+        {
+          AMF_Reset(&server->rc.Link.extras);
+        }
+    }
+  else if (AVMATCH(&method, &av_NetStream_Authenticate_UsherToken))
+    {
+      AMFProp_GetString(AMF_GetProp(&obj, NULL, 3), &server->rc.Link.usherToken);
+      RTMP_LogPrintf("%10s : %.*s\n", "usherToken", server->rc.Link.usherToken.av_len, server->rc.Link.usherToken.av_val);
     }
   else if (AVMATCH(&method, &av_play))
     {
@@ -323,6 +345,14 @@ ServeInvoke(STREAMING_SERVER *server, int which, RTMPPacket *pack, const char *b
       if (!av.av_val)
         goto out;
 
+      double StartFlag = 0;
+      AMFObjectProperty *Start = AMF_GetProp(&obj, NULL, 4);
+      if (!(Start->p_type == AMF_INVALID))
+        StartFlag = AMFProp_GetNumber(Start);
+      if (StartFlag == -1000 || strstr(server->rc.Link.app.av_val, "live"))
+        StartFlag = -1000;
+      RTMP_LogPrintf("%10s : %s\n", "live", (StartFlag == -1000) ? "yes" : "no");
+
       /* check for duplicates */
       for (fl = server->f_head; fl; fl=fl->f_next)
         {
@@ -372,9 +402,51 @@ ServeInvoke(STREAMING_SERVER *server, int which, RTMPPacket *pack, const char *b
       for (p=file; *p; p++)
         if (*p == ':')
           *p = '_';
-      RTMP_LogPrintf("Playpath: %.*s\nSaving as: %s\n",
-        server->rc.Link.playpath.av_len, server->rc.Link.playpath.av_val,
-        file);
+      RTMP_LogPrintf("%10s : %.*s\n%10s : %s\n", "Playpath", server->rc.Link.playpath.av_len,
+                     server->rc.Link.playpath.av_val, "Saving as", file);
+
+#ifdef WIN32
+      // Dump command to batch file
+      char *cmd = NULL, *ptr = NULL;
+      AVal swfUrl, tcUrl;
+
+      cmd = calloc(2048, sizeof (char));
+      ptr = cmd;
+      tcUrl = StripParams(&server->rc.Link.tcUrl);
+      swfUrl = StripParams(&server->rc.Link.swfUrl);
+      ptr += sprintf(ptr, "rtmpdump -r \"%.*s\" -a \"%.*s\" -f \"%.*s\" -W \"%.*s\" -p \"%.*s\"",
+                     tcUrl.av_len, tcUrl.av_val,
+                     server->rc.Link.app.av_len, server->rc.Link.app.av_val,
+                     server->rc.Link.flashVer.av_len, server->rc.Link.flashVer.av_val,
+                     swfUrl.av_len, swfUrl.av_val,
+                     server->rc.Link.pageUrl.av_len, server->rc.Link.pageUrl.av_val);
+
+      if (server->rc.Link.usherToken.av_val)
+        {
+          char *usherToken = strreplace(server->rc.Link.usherToken.av_val, server->rc.Link.usherToken.av_len, "\"", "\\\"");
+          usherToken = strreplace(usherToken, 0, "^", "^^");
+          usherToken = strreplace(usherToken, 0, "|", "^|");
+          ptr += sprintf(ptr, " --jtv \"%s\"", usherToken);
+          free(usherToken);
+        }
+
+      if (server->rc.Link.Extras.av_len)
+        {
+          ptr += sprintf(ptr, "%.*s", server->rc.Link.Extras.av_len, server->rc.Link.Extras.av_val);
+        }
+
+      if (StartFlag == -1000)
+        ptr += sprintf(ptr, "%s", " --live");
+      ptr += sprintf(ptr, " -y \"%.*s\"", server->rc.Link.playpath.av_len, server->rc.Link.playpath.av_val);
+      ptr += sprintf(ptr, " -o \"%s.flv\"\n", file);
+
+      FILE *cmdfile = fopen("Command.bat", "a");
+      fprintf(cmdfile, "%s", cmd);
+      fclose(cmdfile);
+
+      free(cmd);
+#endif
+
       out = fopen(file, "wb");
       free(file);
       if (!out)
@@ -1196,3 +1268,146 @@ main(int argc, char **argv)
 #endif
   return nStatus;
 }
+
+char *
+dumpAMF(AMFObject *obj, char *ptr)
+{
+  int i;
+  const char opt[] = "NBSO Z";
+
+  for (i = 0; i < obj->o_num; i++)
+    {
+      AMFObjectProperty *p = &obj->o_props[i];
+      if (p->p_type > 5)
+        continue;
+      ptr += sprintf(ptr, " -C ");
+      if (p->p_name.av_val)
+        *ptr++ = 'N';
+      *ptr++ = opt[p->p_type];
+      *ptr++ = ':';
+      if (p->p_name.av_val)
+        ptr += sprintf(ptr, "%.*s:", p->p_name.av_len, p->p_name.av_val);
+      switch (p->p_type)
+        {
+        case AMF_BOOLEAN:
+          *ptr++ = p->p_vu.p_number != 0 ? '1' : '0';
+          break;
+        case AMF_STRING:
+          memcpy(ptr, p->p_vu.p_aval.av_val, p->p_vu.p_aval.av_len);
+          ptr += p->p_vu.p_aval.av_len;
+          break;
+        case AMF_NUMBER:
+          ptr += sprintf(ptr, "%f", p->p_vu.p_number);
+          break;
+        case AMF_OBJECT:
+          *ptr++ = '1';
+          ptr = dumpAMF(&p->p_vu.p_object, ptr);
+          ptr += sprintf(ptr, " -C O:0");
+          break;
+        case AMF_NULL:
+        default:
+          break;
+        }
+    }
+  return ptr;
+}
+
+char *
+strreplace(char *srcstr, int srclen, char *orig, char *repl)
+{
+  char *ptr = NULL, *sptr = srcstr;
+  int origlen = strlen(orig);
+  int repllen = strlen(repl);
+  if (!srclen)
+    srclen = strlen(srcstr);
+  char *srcend = srcstr + srclen;
+  int dstbuffer = srclen / origlen * repllen;
+  if (dstbuffer < srclen)
+    dstbuffer = srclen;
+  char *dststr = calloc(dstbuffer + 1, sizeof (char));
+  char *dptr = dststr;
+
+  if ((ptr = strstr(srcstr, orig)))
+    {
+      while (ptr < srcend && (ptr = strstr(sptr, orig)))
+        {
+          int len = ptr - sptr;
+          memcpy(dptr, sptr, len);
+          sptr += len + origlen;
+          dptr += len;
+          memcpy(dptr, repl, repllen);
+          dptr += repllen;
+        }
+      memcpy(dptr, sptr, srcend - sptr);
+      return dststr;
+    }
+
+  memcpy(dststr, srcstr, srclen);
+  return dststr;
+}
+
+AVal
+StripParams(AVal *src)
+{
+  AVal str;
+  if (src->av_val)
+    {
+      str.av_val = calloc(src->av_len + 1, sizeof (char));
+      strncpy(str.av_val, src->av_val, src->av_len);
+      str.av_len = src->av_len;
+      char *start = str.av_val;
+      char *end = start + str.av_len;
+      char *ptr = start;
+
+      while (ptr < end)
+        {
+          if (*ptr == '?')
+            {
+              str.av_len = ptr - start;
+              break;
+            }
+          ptr++;
+        }
+      memset(start + str.av_len, 0, 1);
+
+      char *dynamic = strstr(start, "[[DYNAMIC]]");
+      if (dynamic)
+        {
+          dynamic -= 1;
+          memset(dynamic, 0, 1);
+          str.av_len = dynamic - start;
+          end = start + str.av_len;
+        }
+
+      char *import = strstr(start, "[[IMPORT]]");
+      if (import)
+        {
+          str.av_val = import + 11;
+          strcpy(start, "http://");
+          str.av_val = strcat(start, str.av_val);
+          str.av_len = strlen(str.av_val);
+        }
+      return str;
+    }
+  str = *src;
+  return str;
+}
+
+AVal
+AVcopy(AVal src)
+{
+  AVal dst;
+  if (src.av_len)
+    {
+      dst.av_val = malloc(src.av_len + 1);
+      memcpy(dst.av_val, src.av_val, src.av_len);
+      dst.av_val[src.av_len] = '\0';
+      dst.av_len = src.av_len;
+    }
+  else
+    {
+      dst.av_val = NULL;
+      dst.av_len = 0;
+    }
+  return dst;
+}
