All of lore.kernel.org
 help / color / mirror / Atom feed
From: Yann E. MORIN <yann.morin.1998@free.fr>
To: buildroot@busybox.net
Subject: [Buildroot] [PATCH v8 15/28] rtmpdump: Add KSV patch
Date: Sat, 17 May 2014 22:11:25 +0200	[thread overview]
Message-ID: <20140517201125.GG3459@free.fr> (raw)
In-Reply-To: <1400342276-10303-16-git-send-email-bernd.kuhls@t-online.de>

Bernd, All,

On 2014-05-17 17:57 +0200, Bernd Kuhls spake thusly:
> Signed-off-by: Bernd Kuhls <bernd.kuhls@t-online.de>
> ---
>  package/rtmpdump/rtmpdump-0001-ksv.patch | 3397 ++++++++++++++++++++++++++++++

Wee... Huge patch without any explanation.

What is this 'KSV' stuff?
What does the patch do?
Why is it needed?

Regards,
Yann E. MORIN.

>  1 file changed, 3397 insertions(+)
>  create mode 100644 package/rtmpdump/rtmpdump-0001-ksv.patch
> 
> diff --git a/package/rtmpdump/rtmpdump-0001-ksv.patch b/package/rtmpdump/rtmpdump-0001-ksv.patch
> new file mode 100644
> index 0000000..2536187
> --- /dev/null
> +++ b/package/rtmpdump/rtmpdump-0001-ksv.patch
> @@ -0,0 +1,3397 @@
> +Several fixes made or collected by KSV:
> +http://stream-recorder.com/forum/customized-rtmpdump-binaries-patch-file-t16103.html
> +
> +This patch file is the -p1 converted version of Patch.diff, contained in
> +rtmpdump-2.4.zip, downloaded from https://github.com/K-S-V/Scripts/releases
> +It includes "Update 21/03/2014" as latest update.
> +
> +Signed-off-by: Bernd Kuhls <bernd.kuhls@t-online.de>
> +
> +diff -uNr librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497.org/librtmp/amf.c librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497/librtmp/amf.c
> +--- librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497.org/librtmp/amf.c	2014-03-02 19:20:23.000000000 +0100
> ++++ librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497/librtmp/amf.c	2014-05-04 17:55:17.513338440 +0200
> +@@ -618,6 +618,9 @@
> +       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,
> +@@ -729,13 +732,13 @@
> +       }
> +     case AMF_DATE:
> +       {
> +-	RTMP_Log(RTMP_LOGDEBUG, "AMF_DATE");
> +-
> + 	if (nSize < 10)
> + 	  return -1;
> + 
> + 	prop->p_vu.p_number = AMF_DecodeNumber(pBuffer);
> + 	prop->p_UTCoffset = AMF_DecodeInt16(pBuffer + 8);
> ++        RTMP_Log(RTMP_LOGDEBUG, "AMF_DATE: %f, UTC offset: %d", prop->p_vu.p_number,
> ++                 prop->p_UTCoffset);
> + 
> + 	nSize -= 10;
> + 	break;
> +@@ -807,8 +810,8 @@
> +     }
> +   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;
> +@@ -1068,17 +1071,18 @@
> + 
> + 	  /*std::string str = className; */
> + 
> +-	  RTMP_Log(RTMP_LOGDEBUG,
> +-	      "Class name: %s, externalizable: %d, dynamic: %d, classMembers: %d",
> +-	      cd.cd_name.av_val, cd.cd_externalizable, cd.cd_dynamic,
> +-	      cd.cd_num);
> ++          RTMP_Log(RTMP_LOGDEBUG, "Class name: %.*s, externalizable: %d, dynamic: %d, classMembers: %d",
> ++                   cd.cd_name.av_len, cd.cd_name.av_val, cd.cd_externalizable, cd.cd_dynamic, cd.cd_num);
> + 
> + 	  for (i = 0; i < cd.cd_num; i++)
> +-	    {
> +-	      AVal memberName;
> +-	      len = AMF3ReadString(pBuffer, &memberName);
> +-	      RTMP_Log(RTMP_LOGDEBUG, "Member: %s", memberName.av_val);
> +-	      AMF3CD_AddProp(&cd, &memberName);
> ++            {
> ++              AVal memberName = {NULL, 0};
> ++              len = AMF3ReadString(pBuffer, &memberName);
> ++              if (memberName.av_val)
> ++                {
> ++                  RTMP_Log(RTMP_LOGDEBUG, "Member: %s", memberName.av_val);
> ++                  AMF3CD_AddProp(&cd, &memberName);
> ++                }
> + 	      nSize -= len;
> + 	      pBuffer += len;
> + 	    }
> +@@ -1259,7 +1263,8 @@
> + {
> +   if (!(cd->cd_num & 0x0f))
> +     cd->cd_props = realloc(cd->cd_props, (cd->cd_num + 16) * sizeof(AVal));
> +-  cd->cd_props[cd->cd_num++] = *prop;
> ++  if (cd->cd_props)
> ++    cd->cd_props[cd->cd_num++] = *prop;
> + }
> + 
> + AVal *
> +diff -uNr librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497.org/librtmp/handshake.h librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497/librtmp/handshake.h
> +--- librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497.org/librtmp/handshake.h	2014-03-02 19:20:23.000000000 +0100
> ++++ librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497/librtmp/handshake.h	2014-05-04 17:55:17.517338389 +0200
> +@@ -707,7 +707,7 @@
> +   uint32_t uptime;
> + 
> +   uint8_t clientbuf[RTMP_SIG_SIZE + 4], *clientsig=clientbuf+4;
> +-  uint8_t serversig[RTMP_SIG_SIZE], client2[RTMP_SIG_SIZE], *reply;
> ++  uint8_t serversig[RTMP_SIG_SIZE], serversig1[RTMP_SIG_SIZE], client2[RTMP_SIG_SIZE], *reply;
> +   uint8_t type;
> +   getoff *getdh = NULL, *getdig = NULL;
> + 
> +@@ -760,7 +760,7 @@
> + #else
> +   ip = (int32_t *)(clientsig+8);
> +   for (i = 2; i < RTMP_SIG_SIZE/4; i++)
> +-    *ip++ = rand();
> ++    *ip++ = ((rand() & 0xFFFF) << 16) | (rand() & 0xFFFF);
> + #endif
> + 
> +   /* set handshake digest */
> +@@ -825,6 +825,8 @@
> + 
> +   if (ReadN(r, (char *)serversig, RTMP_SIG_SIZE) != RTMP_SIG_SIZE)
> +     return FALSE;
> ++  if (ReadN(r, (char *) serversig1, RTMP_SIG_SIZE) != RTMP_SIG_SIZE)
> ++    return FALSE;
> + 
> +   /* decode server response */
> +   memcpy(&uptime, serversig, 4);
> +@@ -834,7 +836,7 @@
> +   RTMP_Log(RTMP_LOGDEBUG, "%s: FMS Version   : %d.%d.%d.%d", __FUNCTION__, serversig[4],
> +       serversig[5], serversig[6], serversig[7]);
> + 
> +-  if (FP9HandShake && type == 3 && !serversig[4])
> ++  if (FP9HandShake && type == 3 && (!serversig[4] || !serversig1[4]))
> +     FP9HandShake = FALSE;
> + 
> + #ifdef _DEBUG
> +@@ -914,7 +916,7 @@
> + #else
> +       ip = (int32_t *)reply;
> +       for (i = 0; i < RTMP_SIG_SIZE/4; i++)
> +-        *ip++ = rand();
> ++        *ip++ = ((rand() & 0xFFFF) << 16) | (rand() & 0xFFFF);
> + #endif
> +       /* calculate response now */
> +       signatureResp = reply+RTMP_SIG_SIZE-SHA256_DIGEST_LENGTH;
> +@@ -965,16 +967,22 @@
> +     __FUNCTION__);
> +   RTMP_LogHex(RTMP_LOGDEBUG, reply, RTMP_SIG_SIZE);
> + #endif
> +-  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)
> +-    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;
> ++    }
> + 
> + #ifdef _DEBUG
> +   RTMP_Log(RTMP_LOGDEBUG, "%s: 2nd handshake: ", __FUNCTION__);
> +-  RTMP_LogHex(RTMP_LOGDEBUG, serversig, RTMP_SIG_SIZE);
> ++  RTMP_LogHex(RTMP_LOGDEBUG, serversig1, RTMP_SIG_SIZE);
> + #endif
> + 
> +   if (FP9HandShake)
> +@@ -982,21 +990,21 @@
> +       uint8_t signature[SHA256_DIGEST_LENGTH];
> +       uint8_t digest[SHA256_DIGEST_LENGTH];
> + 
> +-      if (serversig[4] == 0 && serversig[5] == 0 && serversig[6] == 0
> +-	  && serversig[7] == 0)
> ++      if (serversig1[4] == 0 && serversig1[5] == 0 && serversig1[6] == 0
> ++	  && serversig1[7] == 0)
> + 	{
> + 	  RTMP_Log(RTMP_LOGDEBUG,
> + 	      "%s: Wait, did the server just refuse signed authentication?",
> + 	      __FUNCTION__);
> + 	}
> +       RTMP_Log(RTMP_LOGDEBUG, "%s: Server sent signature:", __FUNCTION__);
> +-      RTMP_LogHex(RTMP_LOGDEBUG, &serversig[RTMP_SIG_SIZE - SHA256_DIGEST_LENGTH],
> ++      RTMP_LogHex(RTMP_LOGDEBUG, &serversig1[RTMP_SIG_SIZE - SHA256_DIGEST_LENGTH],
> + 	     SHA256_DIGEST_LENGTH);
> + 
> +       /* verify server response */
> +       HMACsha256(&clientsig[digestPosClient], SHA256_DIGEST_LENGTH,
> + 		 GenuineFMSKey, sizeof(GenuineFMSKey), digest);
> +-      HMACsha256(serversig, RTMP_SIG_SIZE - SHA256_DIGEST_LENGTH, digest,
> ++      HMACsha256(serversig1, RTMP_SIG_SIZE - SHA256_DIGEST_LENGTH, digest,
> + 		 SHA256_DIGEST_LENGTH, signature);
> + 
> +       /* show some information */
> +@@ -1024,7 +1032,7 @@
> +       RTMP_Log(RTMP_LOGDEBUG, "%s: Signature calculated:", __FUNCTION__);
> +       RTMP_LogHex(RTMP_LOGDEBUG, signature, SHA256_DIGEST_LENGTH);
> +       if (memcmp
> +-	  (signature, &serversig[RTMP_SIG_SIZE - SHA256_DIGEST_LENGTH],
> ++	  (signature, &serversig1[RTMP_SIG_SIZE - SHA256_DIGEST_LENGTH],
> + 	   SHA256_DIGEST_LENGTH) != 0)
> + 	{
> + 	  RTMP_Log(RTMP_LOGWARNING, "%s: Server not genuine Adobe!", __FUNCTION__);
> +@@ -1057,7 +1065,7 @@
> +     }
> +   else
> +     {
> +-      if (memcmp(serversig, clientsig, RTMP_SIG_SIZE) != 0)
> ++      if (memcmp(serversig1, clientsig, RTMP_SIG_SIZE) != 0)
> + 	{
> + 	  RTMP_Log(RTMP_LOGWARNING, "%s: client signature does not match!",
> + 	      __FUNCTION__);
> +@@ -1099,7 +1107,7 @@
> +     {
> +       encrypted = FALSE;
> +     }
> +-  else if (type == 6 || type == 8)
> ++  else if (type == 6 || type == 8 || type == 9)
> +     {
> +       offalg = 1;
> +       encrypted = TRUE;
> +@@ -1148,7 +1156,7 @@
> + #else
> +   ip = (int32_t *)(serversig+8);
> +   for (i = 2; i < RTMP_SIG_SIZE/4; i++)
> +-    *ip++ = rand();
> ++    *ip++ = ((rand() & 0xFFFF) << 16) | (rand() & 0xFFFF);
> + #endif
> + 
> +   /* set handshake digest */
> +diff -uNr librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497.org/librtmp/hashswf.c librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497/librtmp/hashswf.c
> +--- librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497.org/librtmp/hashswf.c	2014-03-02 19:20:23.000000000 +0100
> ++++ librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497/librtmp/hashswf.c	2014-05-04 17:55:17.517338389 +0200
> +@@ -70,7 +70,7 @@
> + 
> + #endif /* CRYPTO */
> + 
> +-#define	AGENT	"Mozilla/5.0"
> ++#define	AGENT	"Mozilla/5.0 (Windows NT 5.1; rv:21.0) Gecko/20100101 Firefox/21.0"
> + 
> + HTTPResult
> + HTTP_get(struct HTTP_ctx *http, const char *url, HTTP_read_callback *cb)
> +@@ -116,6 +116,8 @@
> + 
> +   host = p1 + 3;
> +   path = strchr(host, '/');
> ++  if (!path)
> ++    return HTTPRES_BAD_REQUEST;
> +   hlen = path - host;
> +   strncpy(hbuf, host, hlen);
> +   hbuf[hlen] = '\0';
> +@@ -200,7 +202,7 @@
> +     }
> + 
> +   p1 = strchr(sb.sb_buf, ' ');
> +-  rc = atoi(p1 + 1);
> ++  rc = p1 ? atoi(p1 + 1) : 400;
> +   http->status = rc;
> + 
> +   if (rc >= 300)
> +@@ -528,9 +530,11 @@
> + 
> + 	  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, '/');
> ++          if (!r1)
> ++            continue;
> + 	  i = strlen(r1);
> + 	  r1[--i] = '\0';
> + 	  if (strncmp(r1, file, i))
> +@@ -640,7 +644,7 @@
> + 	  HMAC_finish(in.ctx, hash, hlen);
> + 	  *size = in.size;
> + 
> +-	  fprintf(f, "date: %s\n", date);
> ++          fprintf(f, "date: %s\n", date[0] ? date : cctim);
> + 	  fprintf(f, "size: %08x\n", in.size);
> + 	  fprintf(f, "hash: ");
> + 	  for (i = 0; i < SHA256_DIGEST_LENGTH; i++)
> +diff -uNr librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497.org/librtmp/log.c librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497/librtmp/log.c
> +--- librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497.org/librtmp/log.c	2014-03-02 19:20:23.000000000 +0100
> ++++ librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497/librtmp/log.c	2014-05-04 17:55:17.517338389 +0200
> +@@ -52,8 +52,8 @@
> + 	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 -uNr librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497.org/librtmp/Makefile librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497/librtmp/Makefile
> +--- librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497.org/librtmp/Makefile	2014-03-02 19:20:23.000000000 +0100
> ++++ librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497/librtmp/Makefile	2014-05-04 17:55:17.513338440 +0200
> +@@ -26,7 +26,7 @@
> + REQ_OPENSSL=libssl,libcrypto
> + PUB_GNUTLS=-lgmp
> + LIBZ=-lz
> +-LIBS_posix=
> ++LIBS_posix=-lm
> + LIBS_darwin=
> + LIBS_mingw=-lws2_32 -lwinmm -lgdi32
> + LIB_GNUTLS=-lgnutls -lhogweed -lnettle -lgmp $(LIBZ)
> +diff -uNr librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497.org/librtmp/parseurl.c librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497/librtmp/parseurl.c
> +--- librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497.org/librtmp/parseurl.c	2014-03-02 19:20:23.000000000 +0100
> ++++ librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497/librtmp/parseurl.c	2014-05-04 17:55:17.517338389 +0200
> +@@ -34,6 +34,7 @@
> + 	AVal *playpath, AVal *app)
> + {
> + 	char *p, *end, *col, *ques, *slash;
> ++	int doubleSlash = FALSE;
> + 
> + 	RTMP_Log(RTMP_LOGDEBUG, "Parsing...");
> + 
> +@@ -140,11 +141,19 @@
> + 	char *slash2, *slash3 = NULL, *slash4 = NULL;
> + 	int applen, appnamelen;
> + 
> +-	slash2 = strchr(p, '/');
> +-	if(slash2)
> +-		slash3 = strchr(slash2+1, '/');
> +-	if(slash3)
> +-		slash4 = strchr(slash3+1, '/');
> ++    if ((slash2 = strstr(p, "//")))
> ++      {
> ++        doubleSlash = TRUE;
> ++        slash2 += 1;
> ++      }
> ++    else
> ++      {
> ++        slash2 = strchr(p, '/');
> ++        if (slash2)
> ++          slash3 = strchr(slash2 + 1, '/');
> ++        if (slash3)
> ++          slash4 = strchr(slash3 + 1, '/');
> ++      }
> + 
> + 	applen = end-p; /* ondemand, pass all parameters as app */
> + 	appnamelen = applen; /* ondemand length */
> +@@ -168,6 +177,8 @@
> + 		applen = appnamelen;
> + 	}
> + 
> ++	if ((!ques) && doubleSlash)
> ++	  applen -= 1;
> + 	app->av_val = p;
> + 	app->av_len = applen;
> + 	RTMP_Log(RTMP_LOGDEBUG, "Parsed app     : %.*s", applen, p);
> +diff -uNr librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497.org/librtmp/rtmp.c librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497/librtmp/rtmp.c
> +--- librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497.org/librtmp/rtmp.c	2014-03-02 19:20:23.000000000 +0100
> ++++ librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497/librtmp/rtmp.c	2014-05-04 17:55:17.517338389 +0200
> +@@ -28,6 +28,7 @@
> + #include <string.h>
> + #include <assert.h>
> + #include <time.h>
> ++#include <math.h>
> + 
> + #include "rtmp_sys.h"
> + #include "log.h"
> +@@ -68,6 +69,7 @@
> + 
> + #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 };
> + 
> +@@ -108,17 +110,21 @@
> +   RTMPT_OPEN=0, RTMPT_SEND, RTMPT_IDLE, RTMPT_CLOSE
> + } RTMPTCmd;
> + 
> ++static int ConnectSocket(RTMP *r);
> + static int DumpMetaData(AMFObject *obj);
> + static int HandShake(RTMP *r, int FP9HandShake);
> + static int SocksNegotiate(RTMP *r);
> + 
> ++static int SendBytesReceived(RTMP *r);
> ++static int SendCommand(RTMP *r, char *method, int queue);
> + static int SendConnectPacket(RTMP *r, RTMPPacket *cp);
> + static int SendCheckBW(RTMP *r);
> + static int SendCheckBWResult(RTMP *r, double txn);
> + static int SendDeleteStream(RTMP *r, double dStreamId);
> + static int SendFCSubscribe(RTMP *r, AVal *subscribepath);
> ++static int SendGetStreamLength(RTMP *r);
> ++static int SendInvoke(RTMP *r, AVal *command, int queue);
> + static int SendPlay(RTMP *r);
> +-static int SendBytesReceived(RTMP *r);
> + static int SendUsherToken(RTMP *r, AVal *usherToken);
> + 
> + #if 0				/* unused */
> +@@ -336,10 +342,13 @@
> +   r->m_nClientBW = 2500000;
> +   r->m_nClientBW2 = 2;
> +   r->m_nServerBW = 2500000;
> +-  r->m_fAudioCodecs = 3191.0;
> ++  r->m_fAudioCodecs = 3575.0;
> +   r->m_fVideoCodecs = 252.0;
> ++  r->m_fEncoding = 3.0;
> +   r->Link.timeout = 30;
> +   r->Link.swfAge = 30;
> ++  r->Link.CombineConnectPacket = TRUE;
> ++  r->Link.ConnectPacket = FALSE;
> + }
> + 
> + void
> +@@ -357,6 +366,8 @@
> + int
> + RTMP_IsConnected(RTMP *r)
> + {
> ++  if (r->m_sb.sb_size > 0)
> ++    return TRUE;
> +   return r->m_sb.sb_socket != -1;
> + }
> + 
> +@@ -443,6 +454,7 @@
> + 		 AVal *flashVer,
> + 		 AVal *subscribepath,
> + 		 AVal *usherToken,
> ++		 AVal *WeebToken,
> + 		 int dStart,
> + 		 int dStop, int bLiveStream, long int timeout)
> + {
> +@@ -465,6 +477,8 @@
> +     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)
> +@@ -513,6 +527,8 @@
> +     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)
> +@@ -570,14 +586,22 @@
> +   	"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,
> +@@ -765,7 +789,7 @@
> +   if (!ret)
> +     return ret;
> +   r->Link.port = port;
> +-  r->Link.playpath = r->Link.playpath0;
> ++  r->Link.playpath = AVcopy(r->Link.playpath0);
> + 
> +   while (ptr) {
> +     *ptr++ = '\0';
> +@@ -842,9 +866,16 @@
> +     }
> + 
> + #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", 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
> + 
> +   SocksSetup(r, &r->Link.sockshost);
> +@@ -947,6 +978,8 @@
> +   }
> + 
> +   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;
> + }
> +@@ -1397,41 +1430,96 @@
> +   ptr = buffer;
> +   while (n > 0)
> +     {
> +-      int nBytes = 0, nRead;
> ++      int nBytes = 0, nRead, status = 0, retries = 0;
> +       if (r->Link.protocol & RTMP_FEATURE_HTTP)
> +         {
> +-	  int refill = 0;
> +-	  while (!r->m_resplen)
> +-	    {
> +-	      int ret;
> +-	      if (r->m_sb.sb_size < 13 || refill)
> +-	        {
> +-		  if (!r->m_unackd)
> +-		    HTTP_Post(r, RTMPT_IDLE, "", 1);
> +-		  if (RTMPSockBuf_Fill(&r->m_sb) < 1)
> +-		    {
> +-		      if (!r->m_sb.sb_timedout)
> +-		        RTMP_Close(r);
> +-		      return 0;
> +-		    }
> +-		}
> +-	      if ((ret = HTTP_read(r, 0)) == -1)
> +-		{
> +-		  RTMP_Log(RTMP_LOGDEBUG, "%s, No valid HTTP response found", __FUNCTION__);
> +-		  RTMP_Close(r);
> +-		  return 0;
> +-		}
> +-              else if (ret == -2)
> ++          while (!r->m_resplen)
> ++            {
> ++              /* Refill if socket buffer is empty */
> ++              if (!r->m_sb.sb_size)
> +                 {
> +-                  refill = 1;
> ++                  if (retries > 30)
> ++                    {
> ++                      RTMP_Close(r);
> ++                      return 0;
> ++                    }
> ++
> ++                  if (!r->m_unackd)
> ++                    {
> ++                      if (retries > 0)
> ++                        {
> ++                          HTTP_Post(r, RTMPT_IDLE, "", 1);
> ++                          r->m_unackd = TRUE;
> ++                        }
> ++                      retries++;
> ++
> ++                      if (!r->m_bPlaying)
> ++                        sleep(.25);
> ++                    }
> ++
> ++                  RTMP_Log(RTMP_LOGDEBUG, "Trying to fill HTTP buffer, Retries: %d", retries);
> ++                  status = RTMPSockBuf_Fill(&r->m_sb);
> ++                  /* Reconnect socket when closed by some moronic servers after
> ++                   * every HTTP data packet */
> ++                  if (status < 1)
> ++                    {
> ++                      /* Close connection on connection reset */
> ++                      if (status == -1)
> ++                        {
> ++                          RTMP_Close(r);
> ++                          return 0;
> ++                        }
> ++
> ++                      RTMP_Log(RTMP_LOGDEBUG, "Reconnecting socket, Status: %d", status);
> ++                      if (ConnectSocket(r))
> ++                        {
> ++                          HTTP_Post(r, RTMPT_IDLE, "", 1);
> ++                          r->m_unackd = TRUE;
> ++                          retries++;
> ++                        }
> ++                      else
> ++                        {
> ++                          RTMP_Close(r);
> ++                          return 0;
> ++                        }
> ++                    }
> +                 }
> +-              else
> ++
> ++              RTMP_Log(RTMP_LOGDEBUG, "Trying to read HTTP response, Bytes Available: %d", r->m_sb.sb_size);
> ++              status = HTTP_read(r, 0);
> ++              if (status == -1)
> +                 {
> +-                  refill = 0;
> ++                  RTMP_Log(RTMP_LOGDEBUG, "%s, No valid HTTP response found", __FUNCTION__);
> ++                  RTMP_Close(r);
> ++                  return 0;
> +                 }
> +-	    }
> +-	  if (r->m_resplen && !r->m_sb.sb_size)
> +-	    RTMPSockBuf_Fill(&r->m_sb);
> ++              else if (status == -2)
> ++                {
> ++                  if (RTMPSockBuf_Fill(&r->m_sb) < 1)
> ++                    if (!r->m_sb.sb_timedout)
> ++                      {
> ++                        RTMP_Close(r);
> ++                        return 0;
> ++                      }
> ++                }
> ++              else if (status == -3)
> ++                {
> ++                  RTMP_Close(r);
> ++                  return 0;
> ++                }
> ++              else
> ++                r->m_unackd = FALSE;
> ++            }
> ++
> ++          /* Refill when there is still some data to be read and socket buffer
> ++           * is empty */
> ++          if (r->m_resplen && (!r->m_sb.sb_size))
> ++            {
> ++              if (RTMPSockBuf_Fill(&r->m_sb) < 1)
> ++                if (!r->m_sb.sb_timedout)
> ++                  RTMP_Close(r);
> ++            }
> ++
> +           avail = r->m_sb.sb_size;
> + 	  if (avail > r->m_resplen)
> + 	    avail = r->m_resplen;
> +@@ -1458,10 +1546,9 @@
> + 	  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
> +@@ -1472,7 +1559,8 @@
> + 	{
> + 	  RTMP_Log(RTMP_LOGDEBUG, "%s, RTMP socket closed by peer", __FUNCTION__);
> + 	  /*goto again; */
> +-	  RTMP_Close(r);
> ++          if (!r->m_sb.sb_timedout)
> ++            RTMP_Close(r);
> + 	  break;
> + 	}
> + 
> +@@ -1497,6 +1585,7 @@
> + WriteN(RTMP *r, const char *buffer, int n)
> + {
> +   const char *ptr = buffer;
> ++  char *ConnectPacket = 0;
> + #ifdef CRYPTO
> +   char *encrypted = 0;
> +   char buf[RTMP_BUFFER_CACHE_SIZE];
> +@@ -1512,6 +1601,15 @@
> +     }
> + #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;
> ++    }
> ++
> +   while (n > 0)
> +     {
> +       int nBytes;
> +@@ -1548,6 +1646,14 @@
> +     free(encrypted);
> + #endif
> + 
> ++  if (r->Link.ConnectPacket)
> ++    {
> ++      if (r->Link.HandshakeResponse.av_val)
> ++        free(r->Link.HandshakeResponse.av_val);
> ++      free(ConnectPacket);
> ++      r->Link.ConnectPacket = FALSE;
> ++    }
> ++
> +   return n == 0;
> + }
> + 
> +@@ -1577,6 +1683,9 @@
> +   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);
> + 
> +@@ -1625,7 +1734,7 @@
> +       enc = AMF_EncodeNamedBoolean(enc, pend, &av_fpad, FALSE);
> +       if (!enc)
> + 	return FALSE;
> +-      enc = AMF_EncodeNamedNumber(enc, pend, &av_capabilities, 15.0);
> ++      enc = AMF_EncodeNamedNumber(enc, pend, &av_capabilities, 239.0);
> +       if (!enc)
> + 	return FALSE;
> +       enc = AMF_EncodeNamedNumber(enc, pend, &av_audioCodecs, r->m_fAudioCodecs);
> +@@ -1789,7 +1898,7 @@
> +   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);
> +@@ -2095,6 +2204,7 @@
> + }
> + 
> + SAVC(_checkbw);
> ++SAVC(checkBandwidth);
> + 
> + static int
> + SendCheckBW(RTMP *r)
> +@@ -2112,7 +2222,7 @@
> +   packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;
> + 
> +   enc = packet.m_body;
> +-  enc = AMF_EncodeString(enc, pend, &av__checkbw);
> ++  enc = AMF_EncodeString(enc, pend, &av_checkBandwidth);
> +   enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes);
> +   *enc++ = AMF_NULL;
> + 
> +@@ -2219,10 +2329,8 @@
> +     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;
> +@@ -2338,7 +2446,7 @@
> +   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;
> +@@ -2370,8 +2478,8 @@
> +     }
> +   else if (nType == 0x1A)
> +     {
> +-	  *buf = nObject & 0xff;
> +-	}
> ++      *buf = nObject & 0xff;
> ++    }
> +   else
> +     {
> +       if (nSize > 2)
> +@@ -2885,6 +2993,7 @@
> + #endif
> + 
> + 
> ++SAVC(onBWCheck);
> + SAVC(onBWDone);
> + SAVC(onFCSubscribe);
> + SAVC(onFCUnsubscribe);
> +@@ -2897,24 +3006,24 @@
> + SAVC(description);
> + SAVC(onStatus);
> + SAVC(playlist_ready);
> ++SAVC(cps);
> ++SAVC(getStreamLength);
> ++SAVC(sendStatus);
> ++SAVC(verifyClient);
> + 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_Connect_Rejected =
> +-AVC("NetConnection.Connect.Rejected");
> ++static const AVal av_NetConnection_Connect_Rejected = AVC("NetConnection.Connect.Rejected");
> ++static const AVal av_NetConnection_confStream = AVC("NetConnection.confStream");
> + 
> + /* Returns 0 for OK/Failed/error, 1 for 'Stop or Complete' */
> + static int
> +@@ -2924,6 +3033,11 @@
> +   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",
> +@@ -2985,46 +3099,221 @@
> + 	      RTMP_SendServerBW(r);
> + 	      RTMP_SendCtrl(r, 3, 0, 300);
> + 	    }
> +-	  RTMP_SendCreateStream(r);
> ++          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);
> ++
> ++              SendCommand(r, "getConnectionCount", FALSE);
> ++              SendGetStreamLength(r);
> ++              RTMP_SendCreateStream(r);
> ++            }
> ++          else if (strstr(host, "featve.com") || strstr(pageUrl, "featve.com"))
> ++            {
> ++              AVal av_auth = AVC("yes");
> ++              SAVC(youCannotPlayMe);
> + 
> +-	  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);
> +-	    }
> +-	}
> ++              enc = pbuf;
> ++              enc = AMF_EncodeString(enc, pend, &av_youCannotPlayMe);
> ++              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);
> ++
> ++              RTMP_SendCreateStream(r);
> ++            }
> ++          else if (strstr(host, "wfctv.com") || strstr(pageUrl, "wfctv.com"))
> ++            {
> ++              AVal av_auth1 = AVC("zoivid");
> ++              AVal av_auth2 = AVC("yePi4jee");
> ++              SAVC(stream_login);
> ++
> ++              enc = pbuf;
> ++              enc = AMF_EncodeString(enc, pend, &av_stream_login);
> ++              enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes);
> ++              *enc++ = AMF_NULL;
> ++              enc = AMF_EncodeString(enc, pend, &av_auth1);
> ++              enc = AMF_EncodeString(enc, pend, &av_auth2);
> ++              av_Command.av_val = pbuf;
> ++              av_Command.av_len = enc - pbuf;
> ++              SendInvoke(r, &av_Command, FALSE);
> ++
> ++              RTMP_SendCreateStream(r);
> ++            }
> ++          else if (strstr(host, "streamscene.cc") || strstr(pageUrl, "streamscene.cc")
> ++                   || strstr(host, "tsboard.tv") || strstr(pageUrl, "teamstream.in")
> ++                   || strstr(host, "hdstreams.tv") || strstr(pageUrl, "teamstream.to")
> ++                   || strstr(pageUrl, "istreams.to"))
> ++            {
> ++              SendCommand(r, "r", FALSE);
> ++              SendGetStreamLength(r);
> ++              RTMP_SendCreateStream(r);
> ++            }
> ++          else if (strstr(host, "pc3oot.us.to"))
> ++            {
> ++              SendCommand(r, "StreamPiraten", TRUE);
> ++              SendGetStreamLength(r);
> ++              RTMP_SendCreateStream(r);
> ++            }
> ++          else if (strstr(pageUrl, "axcast.com"))
> ++            {
> ++              SendCommand(r, "requestData", FALSE);
> ++              RTMP_SendCreateStream(r);
> ++            }
> ++          else if (strstr(pageUrl, "dhmediahosting.com"))
> ++            {
> ++              SendCommand(r, "netStreamEnable", FALSE);
> ++              RTMP_SendCreateStream(r);
> ++            }
> ++          else if (strstr(pageUrl, "ezcast.tv"))
> ++            {
> ++              SendCommand(r, "jaSakamCarevataKerka", TRUE);
> ++              RTMP_SendCreateStream(r);
> ++            }
> ++          else if (strstr(pageUrl, "liveflash.tv"))
> ++            {
> ++              SendCommand(r, "kaskatija", TRUE);
> ++              RTMP_SendCreateStream(r);
> ++            }
> ++          else if (strstr(pageUrl, "mips.tv"))
> ++            {
> ++              SendCommand(r, "gaolVanus", TRUE);
> ++              RTMP_SendCreateStream(r);
> ++            }
> ++          else if (strstr(pageUrl, "ucaster.eu"))
> ++            {
> ++              SendCommand(r, "vujkoMiLazarBarakovOdMokrino", TRUE);
> ++              RTMP_SendCreateStream(r);
> ++            }
> ++          else if (strstr(pageUrl, "yukons.net"))
> ++            {
> ++              SendCommand(r, "trxuwaaLahRKnaechb", TRUE);
> ++              RTMP_SendCreateStream(r);
> ++            }
> ++          else if (strstr(pageUrl, "yycast.com"))
> ++            {
> ++              SendCommand(r, "trajkoProkopiev", TRUE);
> ++              RTMP_SendCreateStream(r);
> ++            }
> ++          else if ((strstr(host, "highwebmedia.com") || strstr(pageUrl, "chaturbate.com"))
> ++                   && (!strstr(host, "origin")))
> ++            {
> ++              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_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)
> +-	    {
> +-	      SendPublish(r);
> +-	    }
> +-	  else
> +-	    {
> +-	      if (r->Link.lFlags & RTMP_LF_PLST)
> +-	        SendPlaylist(r);
> +-	      SendPlay(r);
> +-	      RTMP_SendCtrl(r, 3, r->m_stream_id, r->m_nBufferMS);
> +-	    }
> +-	}
> ++          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)
> ++            {
> ++              SendPublish(r);
> ++            }
> ++          else
> ++            {
> ++              if (r->Link.lFlags & RTMP_LF_PLST)
> ++                SendPlaylist(r);
> ++              SendPlay(r);
> ++              RTMP_SendCtrl(r, 3, r->m_stream_id, r->m_nBufferMS);
> ++            }
> ++        }
> +       else if (AVMATCH(&methodInvoked, &av_play) ||
> +-      	AVMATCH(&methodInvoked, &av_publish))
> +-	{
> +-	  r->m_bPlaying = TRUE;
> +-	}
> ++               AVMATCH(&methodInvoked, &av_publish))
> ++        {
> ++          r->m_bPlaying = TRUE;
> ++        }
> +       free(methodInvoked.av_val);
> +     }
> +   else if (AVMATCH(&method, &av_onBWDone))
> +     {
> +-	  if (!r->m_nBWCheckCounter)
> ++      if (!r->m_nBWCheckCounter)
> +         SendCheckBW(r);
> +     }
> +   else if (AVMATCH(&method, &av_onFCSubscribe))
> +@@ -3048,21 +3337,22 @@
> +     {
> +       int i;
> +       for (i = 0; i < r->m_numCalls; i++)
> +-	if (AVMATCH(&r->m_methodCalls[i].name, &av__checkbw))
> +-	  {
> +-	    AV_erase(r->m_methodCalls, &r->m_numCalls, i, TRUE);
> +-	    break;
> +-	  }
> ++        if (AVMATCH(&r->m_methodCalls[i].name, &av__checkbw))
> ++          {
> ++            AV_erase(r->m_methodCalls, &r->m_numCalls, i, TRUE);
> ++            break;
> ++          }
> +     }
> +   else if (AVMATCH(&method, &av__error))
> +     {
> ++      int handled = FALSE;
> + #ifdef CRYPTO
> +       AVal methodInvoked = {0};
> +       int i;
> + 
> +       if (r->Link.protocol & RTMP_FEATURE_WRITE)
> +         {
> +-          for (i=0; i<r->m_numCalls; i++)
> ++          for (i = 0; i < r->m_numCalls; i++)
> +             {
> +               if (r->m_methodCalls[i].num == txn)
> +                 {
> +@@ -3074,12 +3364,12 @@
> +           if (!methodInvoked.av_val)
> +             {
> +               RTMP_Log(RTMP_LOGDEBUG, "%s, received result id %f without matching request",
> +-                    __FUNCTION__, txn);
> ++                       __FUNCTION__, txn);
> +               goto leave;
> +             }
> + 
> +           RTMP_Log(RTMP_LOGDEBUG, "%s, received error for method call <%s>", __FUNCTION__,
> +-          methodInvoked.av_val);
> ++                   methodInvoked.av_val);
> + 
> +           if (AVMATCH(&methodInvoked, &av_connect))
> +             {
> +@@ -3093,20 +3383,65 @@
> +               /* if PublisherAuth returns 1, then reconnect */
> +               PublisherAuth(r, &description);
> +             }
> +-        }
> +-      else
> +-        {
> +-          RTMP_Log(RTMP_LOGERROR, "rtmp server sent error");
> ++          handled = TRUE;
> +         }
> +       free(methodInvoked.av_val);
> +-#else
> +-      RTMP_Log(RTMP_LOGERROR, "rtmp server sent error");
> + #endif
> ++      double code = 0.0;
> ++      unsigned int parsedPort = 0;
> ++      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 *playpath = "//playpath";
> ++              int len = redirect.av_len + strlen(playpath);
> ++              char *url = malloc(len + 1);
> ++              memcpy(url, redirect.av_val, redirect.av_len);
> ++              memcpy(url + redirect.av_len, playpath, strlen(playpath));
> ++              url[len] = '\0';
> ++              r->Link.tcUrl.av_val = url;
> ++              r->Link.tcUrl.av_len = redirect.av_len;
> ++              RTMP_ParseURL(url, &r->Link.protocol, &r->Link.hostname, &parsedPort, &r->Link.playpath0, &r->Link.app);
> ++              if (parsedPort)
> ++                r->Link.port = parsedPort;
> ++            }
> ++        }
> ++      if (r->Link.redirected)
> ++        {
> ++          handled = TRUE;
> ++          RTMP_Log(RTMP_LOGINFO, "rtmp server sent redirect");
> ++        }
> ++
> ++      if (!handled)
> ++        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)
> ++        {
> ++          r->Link.redirected = FALSE;
> ++          RTMP_Close(r);
> ++          RTMP_Log(RTMP_LOGINFO, "trying to connect with redirected url");
> ++          RTMP_Connect(r, NULL);
> ++        }
> ++      else
> ++        {
> ++          RTMP_Log(RTMP_LOGERROR, "rtmp server requested close");
> ++          RTMP_Close(r);
> ++        }
> + #ifdef CRYPTO
> +       if ((r->Link.protocol & RTMP_FEATURE_WRITE) &&
> +               !(r->Link.pFlags & RTMP_PUB_CLEAN) &&
> +@@ -3127,16 +3462,18 @@
> +   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)
> +-	  || AVMATCH(&code, &av_NetStream_Play_Failed)
> +-	  || AVMATCH(&code, &av_NetStream_Play_StreamNotFound)
> +-	  || AVMATCH(&code, &av_NetConnection_Connect_InvalidApp))
> ++          || AVMATCH(&code, &av_NetStream_Play_Failed)
> ++          || AVMATCH(&code, &av_NetStream_Play_StreamNotFound)
> ++          || AVMATCH(&code, &av_NetConnection_Connect_Rejected)
> ++          || AVMATCH(&code, &av_NetConnection_Connect_InvalidApp))
> + 	{
> + 	  r->m_stream_id = -1;
> + 	  RTMP_Close(r);
> +@@ -3194,6 +3531,46 @@
> + 	    r->m_pausing = 3;
> + 	  }
> + 	}
> ++
> ++      else if (AVMATCH(&code, &av_NetConnection_confStream))
> ++        {
> ++#ifdef CRYPTO
> ++          static const char hexdig[] = "0123456789abcdef";
> ++          AVal auth;
> ++          SAVC(cf_stream);
> ++          int i;
> ++          char hash_hex[33] = {0};
> ++          unsigned char hash[16];
> ++
> ++          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))
> +     {
> +@@ -3207,6 +3584,85 @@
> + 	    }
> +         }
> +     }
> ++  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_cps))
> ++    {
> ++      int Status = AMFProp_GetBoolean(AMF_GetProp(&obj, NULL, 3));
> ++      if (Status == FALSE)
> ++        {
> ++          AVal Message;
> ++          AMFProp_GetString(AMF_GetProp(&obj, NULL, 4), &Message);
> ++          RTMP_Log(RTMP_LOGINFO, "Model status is %.*s", Message.av_len, Message.av_val);
> ++          RTMP_Close(r);
> ++        }
> ++      else
> ++        {
> ++          AVal Playpath, Server;
> ++          AMFProp_GetString(AMF_GetProp(&obj, NULL, 5), &Playpath);
> ++          AMFProp_GetString(AMF_GetProp(&obj, NULL, 6), &Server);
> ++          if (strncasecmp(&Playpath.av_val[Playpath.av_len - 4], ".mp4", 4) != 0)
> ++            {
> ++              char *playpath = calloc(Server.av_len + Playpath.av_len + 25, sizeof (char));
> ++              strcat(playpath, "rtmp://");
> ++              strncat(playpath, Server.av_val, Server.av_len);
> ++              strcat(playpath, "/live-origin/");
> ++              strncat(playpath, Playpath.av_val, Playpath.av_len);
> ++              strcat(playpath, ".mp4");
> ++              Playpath.av_val = playpath;
> ++              Playpath.av_len = strlen(playpath);
> ++            }
> ++          RTMP_ParsePlaypath(&Playpath, &r->Link.playpath);
> ++          RTMP_SendCreateStream(r);
> ++        }
> ++    }
> +   else
> +     {
> + 
> +@@ -3232,7 +3688,8 @@
> + 	  return TRUE;
> + 	}
> + 
> +-      if (prop->p_type == AMF_OBJECT || prop->p_type == AMF_ECMA_ARRAY)
> ++      if (prop->p_type == AMF_OBJECT || prop->p_type == AMF_ECMA_ARRAY
> ++          || prop->p_type == AMF_STRICT_ARRAY)
> + 	{
> + 	  if (RTMP_FindFirstMatchingProperty(&prop->p_vu.p_object, name, p))
> + 	    return TRUE;
> +@@ -3258,7 +3715,8 @@
> + 	  return TRUE;
> + 	}
> + 
> +-      if (prop->p_type == AMF_OBJECT)
> ++      if (prop->p_type == AMF_OBJECT || prop->p_type == AMF_ECMA_ARRAY
> ++          || prop->p_type == AMF_STRICT_ARRAY)
> + 	{
> + 	  if (RTMP_FindPrefixProperty(&prop->p_vu.p_object, name, p))
> + 	    return TRUE;
> +@@ -3292,6 +3750,7 @@
> + 	  snprintf(str, 255, "%s",
> + 		   prop->p_vu.p_number != 0. ? "TRUE" : "FALSE");
> + 	  break;
> ++        case AMF_NULL:
> + 	case AMF_STRING:
> + 	  len = snprintf(str, 255, "%.*s", prop->p_vu.p_aval.av_len,
> + 		   prop->p_vu.p_aval.av_val);
> +@@ -3307,7 +3766,7 @@
> + 	}
> +       if (str[0] && prop->p_name.av_len)
> + 	{
> +-	  RTMP_Log(RTMP_LOGINFO, "  %-22.*s%s", prop->p_name.av_len,
> ++          RTMP_Log(RTMP_LOGINFO, "  %-24.*s%s", prop->p_name.av_len,
> + 		    prop->p_name.av_val, str);
> + 	}
> +     }
> +@@ -3389,7 +3848,7 @@
> +   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); */
> + 
> +@@ -3498,15 +3957,15 @@
> +       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);
> + 	}
> +@@ -3811,8 +4270,18 @@
> +       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;
> +@@ -4263,8 +4732,13 @@
> + {
> +   int nBytes;
> + 
> +-  if (!sb->sb_size)
> +-    sb->sb_start = sb->sb_buf;
> ++  /* Copy unprocessed bytes to the start of buffer to make optimum use of
> ++   * available buffer */
> ++  if (sb->sb_start != sb->sb_buf)
> ++    {
> ++      memcpy(sb->sb_buf, sb->sb_start, sb->sb_size);
> ++      sb->sb_start = sb->sb_buf;
> ++    }
> + 
> +   while (1)
> +     {
> +@@ -4278,6 +4752,8 @@
> + #endif
> + 	{
> + 	  nBytes = recv(sb->sb_socket, sb->sb_start + sb->sb_size, nBytes, 0);
> ++          if (!nBytes)
> ++            RTMP_Log(RTMP_LOGDEBUG, "Socket closed by server, nBytes: %d", nBytes);
> + 	}
> +       if (nBytes != -1)
> + 	{
> +@@ -4417,21 +4893,19 @@
> + 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\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],
> +-    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);
> ++  int hlen = snprintf(hbuf, sizeof (hbuf), "POST /%s%s/%d HTTP/1.1\r\n"
> ++                      "Content-Type: application/x-fcs\r\n"
> ++                      "User-Agent: Shockwave Flash\r\n"
> ++                      "Host: %.*s:%d\r\n"
> ++                      "Content-Length: %d\r\n"
> ++                      "Connection: Keep-Alive\r\n"
> ++                      "Cache-Control: no-cache\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);
> +   RTMPSockBuf_Send(&r->m_sb, hbuf, hlen);
> +   hlen = RTMPSockBuf_Send(&r->m_sb, buf, len);
> +   r->m_msgCounter++;
> +-  r->m_unackd++;
> +   return hlen;
> + }
> + 
> +@@ -4441,22 +4915,17 @@
> +   char *ptr;
> +   int hlen;
> + 
> +-restart:
> +   if (fill)
> +     RTMPSockBuf_Fill(&r->m_sb);
> +-  if (r->m_sb.sb_size < 13) {
> +-    if (fill)
> +-      goto restart;
> ++
> ++  /* Check if socket buffer is empty or HTTP header isn't completely received */
> ++  memset(r->m_sb.sb_start + r->m_sb.sb_size, '\0', 1);
> ++  if ((!r->m_sb.sb_size) || (!strstr(r->m_sb.sb_start, "\r\n\r\n")))
> +     return -2;
> +-  }
> ++
> +   if (strncmp(r->m_sb.sb_start, "HTTP/1.1 200 ", 13))
> +     return -1;
> +   r->m_sb.sb_start[r->m_sb.sb_size] = '\0';
> +-  if (!strstr(r->m_sb.sb_start, "\r\n\r\n")) {
> +-    if (fill)
> +-      goto restart;
> +-    return -2;
> +-  }
> + 
> +   ptr = r->m_sb.sb_start + sizeof("HTTP/1.1 200");
> +   while ((ptr = strstr(ptr, "Content-"))) {
> +@@ -4464,21 +4933,31 @@
> +     ptr += 8;
> +   }
> +   if (!ptr)
> +-    return -1;
> +-  hlen = atoi(ptr+16);
> ++    {
> ++      ptr = r->m_sb.sb_start + sizeof ("HTTP/1.1 200");
> ++      RTMP_Log(RTMP_LOGDEBUG, "No Content-Length header found, assuming continuous stream");
> ++      hlen = 2147483648UL; // 2 GB
> ++    }
> ++  else
> ++    hlen = atoi(ptr + 16);
> +   ptr = strstr(ptr+16, "\r\n\r\n");
> +   if (!ptr)
> +     return -1;
> +   ptr += 4;
> +-  if (ptr + (r->m_clientID.av_val ? 1 : hlen) > r->m_sb.sb_start + r->m_sb.sb_size)
> +-    {
> +-      if (fill)
> +-        goto restart;
> +-      return -2;
> +-    }
> +   r->m_sb.sb_size -= ptr - r->m_sb.sb_start;
> +   r->m_sb.sb_start = ptr;
> +-  r->m_unackd--;
> ++
> ++  /* Stop processing if content length is 0 */
> ++  if (!hlen)
> ++    return -3;
> ++
> ++  /* Refill buffer if no payload is received */
> ++  if (hlen && (!r->m_sb.sb_size))
> ++    {
> ++      RTMPSockBuf_Fill(&r->m_sb);
> ++      ptr = r->m_sb.sb_buf;
> ++      r->m_sb.sb_start = ptr;
> ++    }
> + 
> +   if (!r->m_clientID.av_val)
> +     {
> +@@ -4498,10 +4977,17 @@
> +       r->m_sb.sb_start++;
> +       r->m_sb.sb_size--;
> +     }
> ++
> ++  /* Following values shouldn't be negative in any case */
> ++  if (r->m_resplen < 0)
> ++    r->m_resplen = 0;
> ++  if (r->m_sb.sb_size < 0)
> ++    r->m_sb.sb_size = 0;
> ++
> +   return 0;
> + }
> + 
> +-#define MAX_IGNORED_FRAMES	50
> ++#define MAX_IGNORED_FRAMES	100
> + 
> + /* Read from the stream until we get a media packet.
> +  * Returns -3 if Play.Close/Stop, -2 if fatal error, -1 if no more media
> +@@ -4569,162 +5055,156 @@
> + #endif
> + 
> +       if (r->m_read.flags & RTMP_READ_RESUME)
> +-	{
> +-	  /* check the header if we get one */
> +-	  if (packet.m_nTimeStamp == 0)
> +-	    {
> +-	      if (r->m_read.nMetaHeaderSize > 0
> +-		  && packet.m_packetType == RTMP_PACKET_TYPE_INFO)
> +-		{
> +-		  AMFObject metaObj;
> +-		  int nRes =
> +-		    AMF_Decode(&metaObj, packetBody, nPacketLen, FALSE);
> +-		  if (nRes >= 0)
> +-		    {
> +-		      AVal metastring;
> +-		      AMFProp_GetString(AMF_GetProp(&metaObj, NULL, 0),
> +-					&metastring);
> +-
> +-		      if (AVMATCH(&metastring, &av_onMetaData))
> +-			{
> +-			  /* compare */
> +-			  if ((r->m_read.nMetaHeaderSize != nPacketLen) ||
> +-			      (memcmp
> +-			       (r->m_read.metaHeader, packetBody,
> +-				r->m_read.nMetaHeaderSize) != 0))
> +-			    {
> +-			      ret = RTMP_READ_ERROR;
> +-			    }
> +-			}
> +-		      AMF_Reset(&metaObj);
> +-		      if (ret == RTMP_READ_ERROR)
> +-			break;
> +-		    }
> +-		}
> ++        {
> ++          RTMP_Log(RTMP_LOGDEBUG2, "Received timestamp: %d, type %d",
> ++                   packet.m_nTimeStamp, packet.m_packetType);
> ++          if (packet.m_nTimeStamp > 0 && r->m_read.nResumeDriftTS > 0)
> ++            packet.m_nTimeStamp -= r->m_read.nResumeDriftTS;
> ++          RTMP_Log(RTMP_LOGDEBUG2, "Adjusted timestamp: %d", packet.m_nTimeStamp);
> ++
> ++          /* check the header if we get one */
> ++          if (r->m_read.nMetaHeaderSize > 0
> ++              && packet.m_packetType == RTMP_PACKET_TYPE_INFO)
> ++            {
> ++              AMFObject metaObj;
> ++              int nRes = AMF_Decode(&metaObj, packetBody, nPacketLen, FALSE);
> ++              if (nRes >= 0)
> ++                {
> ++                  AVal metastring;
> ++                  AMFProp_GetString(AMF_GetProp(&metaObj, NULL, 0), &metastring);
> + 
> +-	      /* check first keyframe to make sure we got the right position
> +-	       * in the stream! (the first non ignored frame)
> +-	       */
> +-	      if (r->m_read.nInitialFrameSize > 0)
> +-		{
> +-		  /* video or audio data */
> +-		  if (packet.m_packetType == r->m_read.initialFrameType
> +-		      && r->m_read.nInitialFrameSize == nPacketLen)
> +-		    {
> +-		      /* we don't compare the sizes since the packet can
> +-		       * contain several FLV packets, just make sure the
> +-		       * first frame is our keyframe (which we are going
> +-		       * to rewrite)
> +-		       */
> +-		      if (memcmp
> +-			  (r->m_read.initialFrame, packetBody,
> +-			   r->m_read.nInitialFrameSize) == 0)
> +-			{
> +-			  RTMP_Log(RTMP_LOGDEBUG, "Checked keyframe successfully!");
> +-			  r->m_read.flags |= RTMP_READ_GOTKF;
> +-			  /* ignore it! (what about audio data after it? it is
> +-			   * handled by ignoring all 0ms frames, see below)
> +-			   */
> +-			  ret = RTMP_READ_IGNORE;
> +-			  break;
> +-			}
> +-		    }
> ++                  if (AVMATCH(&metastring, &av_onMetaData))
> ++                    {
> ++                      /* compare */
> ++                      if ((r->m_read.nMetaHeaderSize != nPacketLen) ||
> ++                          (memcmp(r->m_read.metaHeader, packetBody, r->m_read.nMetaHeaderSize) != 0))
> ++                        {
> ++                          ret = RTMP_READ_ERROR;
> ++                        }
> ++                    }
> ++                  AMF_Reset(&metaObj);
> ++                  if (ret == RTMP_READ_ERROR)
> ++                    break;
> ++                }
> ++            }
> + 
> +-		  /* hande FLV streams, even though the server resends the
> +-		   * keyframe as an extra video packet it is also included
> +-		   * in the first FLV stream chunk and we have to compare
> +-		   * it and filter it out !!
> +-		   */
> +-		  if (packet.m_packetType == RTMP_PACKET_TYPE_FLASH_VIDEO)
> +-		    {
> +-		      /* basically we have to find the keyframe with the
> +-		       * correct TS being nResumeTS
> +-		       */
> +-		      unsigned int pos = 0;
> +-		      uint32_t ts = 0;
> +-
> +-		      while (pos + 11 < nPacketLen)
> +-			{
> +-			  /* size without header (11) and prevTagSize (4) */
> +-			  uint32_t dataSize =
> +-			    AMF_DecodeInt24(packetBody + pos + 1);
> +-			  ts = AMF_DecodeInt24(packetBody + pos + 4);
> +-			  ts |= (packetBody[pos + 7] << 24);
> ++          /* check first keyframe to make sure we got the right position
> ++           * in the stream! (the first non ignored frame)
> ++           */
> ++          RTMP_Log(RTMP_LOGDEBUG2, "Required packet length: %d, Packet length: %d",
> ++                   r->m_read.nInitialFrameSize, nPacketLen);
> ++          if (r->m_read.nInitialFrameSize > 0)
> ++            {
> ++              /* video or audio data */
> ++              if (packet.m_packetType == r->m_read.initialFrameType
> ++                  && r->m_read.nInitialFrameSize == nPacketLen)
> ++                {
> ++                  /* we don't compare the sizes since the packet can
> ++                   * contain several FLV packets, just make sure the
> ++                   * first frame is our keyframe (which we are going
> ++                   * to rewrite)
> ++                   */
> ++                  RTMP_Log(RTMP_LOGDEBUG2, "Comparing keyframe data");
> ++                  if (memcmp(r->m_read.initialFrame, packetBody,
> ++                             r->m_read.nInitialFrameSize) == 0)
> ++                    {
> ++                      RTMP_Log(RTMP_LOGDEBUG, "Checked keyframe successfully!");
> ++                      r->m_read.flags |= RTMP_READ_GOTKF;
> ++                      r->m_read.nResumeDriftTS = packet.m_nTimeStamp;
> ++                      /* ignore it! (what about audio data after it? it is
> ++                       * handled by ignoring all 0ms frames, see below)
> ++                       */
> ++                      ret = RTMP_READ_IGNORE;
> ++                      break;
> ++                    }
> ++                }
> ++
> ++              /* hande FLV streams, even though the server resends the
> ++               * keyframe as an extra video packet it is also included
> ++               * in the first FLV stream chunk and we have to compare
> ++               * it and filter it out !!
> ++               */
> ++              if (packet.m_packetType == RTMP_PACKET_TYPE_FLASH_VIDEO)
> ++                {
> ++                  /* basically we have to find the keyframe with the
> ++                   * correct TS being nResumeTS
> ++                   */
> ++                  unsigned int pos = 0;
> ++                  uint32_t ts = 0;
> ++
> ++                  while (pos + 11 < nPacketLen)
> ++                    {
> ++                      /* size without header (11) and prevTagSize (4) */
> ++                      uint32_t dataSize = AMF_DecodeInt24(packetBody + pos + 1);
> ++                      ts = AMF_DecodeInt24(packetBody + pos + 4);
> ++                      ts |= (packetBody[pos + 7] << 24);
> + 
> + #ifdef _DEBUG
> +-			  RTMP_Log(RTMP_LOGDEBUG,
> +-			      "keyframe search: FLV Packet: type %02X, dataSize: %d, timeStamp: %d ms",
> +-			      packetBody[pos], dataSize, ts);
> +-#endif
> +-			  /* ok, is it a keyframe?:
> +-			   * well doesn't work for audio!
> +-			   */
> +-			  if (packetBody[pos /*6928, test 0 */ ] ==
> +-			      r->m_read.initialFrameType
> +-			      /* && (packetBody[11]&0xf0) == 0x10 */ )
> +-			    {
> +-			      if (ts == r->m_read.nResumeTS)
> +-				{
> +-				  RTMP_Log(RTMP_LOGDEBUG,
> +-				      "Found keyframe with resume-keyframe timestamp!");
> +-				  if (r->m_read.nInitialFrameSize != dataSize
> +-				      || memcmp(r->m_read.initialFrame,
> +-						packetBody + pos + 11,
> +-						r->m_read.
> +-						nInitialFrameSize) != 0)
> +-				    {
> +-				      RTMP_Log(RTMP_LOGERROR,
> +-					  "FLV Stream: Keyframe doesn't match!");
> +-				      ret = RTMP_READ_ERROR;
> +-				      break;
> +-				    }
> +-				  r->m_read.flags |= RTMP_READ_GOTFLVK;
> +-
> +-				  /* skip this packet?
> +-				   * check whether skippable:
> +-				   */
> +-				  if (pos + 11 + dataSize + 4 > nPacketLen)
> +-				    {
> +-				      RTMP_Log(RTMP_LOGWARNING,
> +-					  "Non skipable packet since it doesn't end with chunk, stream corrupt!");
> +-				      ret = RTMP_READ_ERROR;
> +-				      break;
> +-				    }
> +-				  packetBody += (pos + 11 + dataSize + 4);
> +-				  nPacketLen -= (pos + 11 + dataSize + 4);
> +-
> +-				  goto stopKeyframeSearch;
> +-
> +-				}
> +-			      else if (r->m_read.nResumeTS < ts)
> +-				{
> +-				  /* the timestamp ts will only increase with
> +-				   * further packets, wait for seek
> +-				   */
> +-				  goto stopKeyframeSearch;
> +-				}
> +-			    }
> +-			  pos += (11 + dataSize + 4);
> +-			}
> +-		      if (ts < r->m_read.nResumeTS)
> +-			{
> +-			  RTMP_Log(RTMP_LOGERROR,
> +-			      "First packet does not contain keyframe, all "
> +-			      "timestamps are smaller than the keyframe "
> +-			      "timestamp; probably the resume seek failed?");
> +-			}
> +-		    stopKeyframeSearch:
> +-		      ;
> +-		      if (!(r->m_read.flags & RTMP_READ_GOTFLVK))
> +-			{
> +-			  RTMP_Log(RTMP_LOGERROR,
> +-			      "Couldn't find the seeked keyframe in this chunk!");
> +-			  ret = RTMP_READ_IGNORE;
> +-			  break;
> +-			}
> +-		    }
> +-		}
> +-	    }
> ++                      RTMP_Log(RTMP_LOGDEBUG,
> ++                               "keyframe search: FLV Packet: type %02X, dataSize: %d, timeStamp: %d ms",
> ++                               packetBody[pos], dataSize, ts);
> ++#endif
> ++                      /* ok, is it a keyframe?:
> ++                       * well doesn't work for audio!
> ++                       */
> ++                      if (packetBody[pos /*6928, test 0 */ ] == r->m_read.initialFrameType
> ++                          /* && (packetBody[11]&0xf0) == 0x10 */)
> ++                        {
> ++                          if (ts == r->m_read.nResumeTS)
> ++                            {
> ++                              RTMP_Log(RTMP_LOGDEBUG, "Found keyframe with resume-keyframe timestamp!");
> ++                              if (r->m_read.nInitialFrameSize != dataSize ||
> ++                                  memcmp(r->m_read.initialFrame, packetBody + pos + 11,
> ++                                         r->m_read.nInitialFrameSize) != 0)
> ++                                {
> ++                                  RTMP_Log(RTMP_LOGERROR, "FLV Stream: Keyframe doesn't match!");
> ++                                  ret = RTMP_READ_ERROR;
> ++                                  break;
> ++                                }
> ++                              r->m_read.flags |= RTMP_READ_GOTFLVK;
> ++
> ++                              /* skip this packet?
> ++                               * check whether skippable:
> ++                               */
> ++                              if (pos + 11 + dataSize + 4 > nPacketLen)
> ++                                {
> ++                                  RTMP_Log(RTMP_LOGWARNING, "Non skipable packet since it doesn't "
> ++                                           "end with chunk, stream corrupt!");
> ++                                  ret = RTMP_READ_ERROR;
> ++                                  break;
> ++                                }
> ++                              packetBody += (pos + 11 + dataSize + 4);
> ++                              nPacketLen -= (pos + 11 + dataSize + 4);
> ++
> ++                              goto stopKeyframeSearch;
> ++
> ++                            }
> ++                          else if (r->m_read.nResumeTS < ts)
> ++                            {
> ++                              /* the timestamp ts will only increase with
> ++                               * further packets, wait for seek
> ++                               */
> ++                              goto stopKeyframeSearch;
> ++                            }
> ++                        }
> ++                      pos += (11 + dataSize + 4);
> ++                    }
> ++                  if (ts < r->m_read.nResumeTS)
> ++                    {
> ++                      RTMP_Log(RTMP_LOGERROR,
> ++                               "First packet does not contain keyframe, all "
> ++                               "timestamps are smaller than the keyframe "
> ++                               "timestamp; probably the resume seek failed?");
> ++                    }
> ++                stopKeyframeSearch:
> ++                  if (!(r->m_read.flags & RTMP_READ_GOTFLVK))
> ++                    {
> ++                      RTMP_Log(RTMP_LOGERROR, "Couldn't find the seeked keyframe in this chunk!");
> ++                      ret = RTMP_READ_IGNORE;
> ++                      break;
> ++                    }
> ++                }
> ++            }
> + 
> + 	  if (packet.m_nTimeStamp > 0
> + 	      && (r->m_read.flags & (RTMP_READ_GOTKF|RTMP_READ_GOTFLVK)))
> +@@ -4984,7 +5464,7 @@
> +   0x00, 0x00, 0x00, 0x00
> + };
> + 
> +-#define HEADERBUF	(128*1024)
> ++#define HEADERBUF	(1024*1024)
> + int
> + RTMP_Read(RTMP *r, char *buf, int size)
> + {
> +@@ -5187,3 +5667,284 @@
> +     }
> +   return size+s2;
> + }
> ++
> ++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;
> ++}
> ++
> ++static int
> ++ConnectSocket(RTMP *r)
> ++{
> ++  int on = 1;
> ++  struct sockaddr_in service;
> ++  if (!r->Link.hostname.av_len)
> ++    return FALSE;
> ++
> ++  memset(&service, 0, sizeof (struct sockaddr_in));
> ++  service.sin_family = AF_INET;
> ++
> ++  if (r->Link.socksport)
> ++    {
> ++      /* Connect via SOCKS */
> ++      if (!add_addr_info(&service, &r->Link.sockshost, r->Link.socksport))
> ++        return FALSE;
> ++    }
> ++  else
> ++    {
> ++      /* Connect directly */
> ++      if (!add_addr_info(&service, &r->Link.hostname, r->Link.port))
> ++        return FALSE;
> ++    }
> ++
> ++  r->m_sb.sb_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
> ++  if (r->m_sb.sb_socket != -1)
> ++    {
> ++      if (connect(r->m_sb.sb_socket, (struct sockaddr *) &service, sizeof (struct sockaddr)) < 0)
> ++        {
> ++          int err = GetSockError();
> ++          RTMP_Log(RTMP_LOGERROR, "%s, failed to connect socket. %d (%s)",
> ++                   __FUNCTION__, err, strerror(err));
> ++          RTMP_Close(r);
> ++          return FALSE;
> ++        }
> ++
> ++      if (r->Link.socksport)
> ++        {
> ++          RTMP_Log(RTMP_LOGDEBUG, "%s ... SOCKS negotiation", __FUNCTION__);
> ++          if (!SocksNegotiate(r))
> ++            {
> ++              RTMP_Log(RTMP_LOGERROR, "%s, SOCKS negotiation failed.", __FUNCTION__);
> ++              RTMP_Close(r);
> ++              return FALSE;
> ++            }
> ++        }
> ++    }
> ++  else
> ++    {
> ++      RTMP_Log(RTMP_LOGERROR, "%s, failed to create socket. Error: %d",
> ++               __FUNCTION__, GetSockError());
> ++      return FALSE;
> ++    }
> ++
> ++  /* set timeout */
> ++  SET_RCVTIMEO(tv, r->Link.timeout);
> ++  if (setsockopt(r->m_sb.sb_socket, SOL_SOCKET, SO_RCVTIMEO, (char *) &tv, sizeof (tv)))
> ++    {
> ++      RTMP_Log(RTMP_LOGERROR, "%s, Setting socket timeout to %d failed!",
> ++               __FUNCTION__, r->Link.timeout);
> ++    }
> ++
> ++  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;
> ++}
> ++
> ++static int
> ++SendCommand(RTMP *r, char *method, int queue)
> ++{
> ++  char pbuf[256], *pend = pbuf + sizeof (pbuf), *enc;
> ++  AVal av_command, methodName;
> ++
> ++  enc = pbuf;
> ++  methodName.av_val = method;
> ++  methodName.av_len = strlen(method);
> ++  enc = AMF_EncodeString(enc, pend, &methodName);
> ++  enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes);
> ++  *enc++ = AMF_NULL;
> ++  av_command.av_val = pbuf;
> ++  av_command.av_len = enc - pbuf;
> ++
> ++  return SendInvoke(r, &av_command, queue);
> ++}
> ++
> ++static int
> ++SendGetStreamLength(RTMP *r)
> ++{
> ++  char pbuf[256], *pend = pbuf + sizeof (pbuf), *enc;
> ++  AVal av_Command;
> ++  SAVC(getStreamLength);
> ++
> ++  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);
> ++}
> ++
> ++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);
> ++}
> ++
> ++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;
> ++}
> ++
> ++char *
> ++strreplace(char *srcstr, int srclen, char *orig, char *repl, int didAlloc)
> ++{
> ++  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);
> ++      if (didAlloc)
> ++        free(srcstr);
> ++      return dststr;
> ++    }
> ++
> ++  memcpy(dststr, srcstr, srclen);
> ++  if (didAlloc)
> ++    free(srcstr);
> ++  return dststr;
> ++}
> ++
> ++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 = malloc(count * sizeof (size_t));
> ++  char **param = *params;
> ++
> ++  for (i = 0; i < (count - 1); i++)
> ++    {
> ++      dstr = strchr(sptr, delim);
> ++      len = dstr - sptr;
> ++      param[i] = malloc((len + 1) * sizeof (char));
> ++      memcpy(param[i], sptr, len);
> ++      *(param[i] + len) = '\0';
> ++      sptr += len + 1;
> ++    }
> ++
> ++  /* copy the last string */
> ++  if (sptr <= srcend)
> ++    {
> ++      len = srclen - (sptr - srcbeg);
> ++      param[i] = malloc((len + 1) * sizeof (char));
> ++      memcpy(param[i], sptr, len);
> ++      *(param[i] + len) = '\0';
> ++    }
> ++  return count;
> ++}
> +diff -uNr librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497.org/librtmp/rtmp.h librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497/librtmp/rtmp.h
> +--- librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497.org/librtmp/rtmp.h	2014-03-02 19:20:23.000000000 +0100
> ++++ librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497/librtmp/rtmp.h	2014-05-04 17:55:17.517338389 +0200
> +@@ -150,12 +150,14 @@
> +     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;
> +     AVal pubUser;
> +     AVal pubPasswd;
> +@@ -174,9 +176,15 @@
> +     int lFlags;
> + 
> +     int swfAge;
> ++    int swfSize;
> + 
> +     int protocol;
> ++    int ConnectPacket;
> ++    int CombineConnectPacket;
> ++    int redirected;
> +     int timeout;		/* connection timeout in seconds */
> ++    AVal Extras;
> ++    AVal HandshakeResponse;
> + 
> + #define RTMP_PUB_NAME   0x0001  /* send login to server */
> + #define RTMP_PUB_RESP   0x0002  /* send salted password hash */
> +@@ -224,6 +232,7 @@
> +     /* if bResume == TRUE */
> +     uint8_t initialFrameType;
> +     uint32_t nResumeTS;
> ++    uint32_t nResumeDriftTS;
> +     char *metaHeader;
> +     char *initialFrame;
> +     uint32_t nMetaHeaderSize;
> +@@ -310,6 +319,7 @@
> + 			AVal *flashVer,
> + 			AVal *subscribepath,
> + 			AVal *usherToken,
> ++			AVal *WeebToken,
> + 			int dStart,
> + 			int dStop, int bLiveStream, long int timeout);
> + 
> +@@ -375,6 +385,11 @@
> +   int RTMP_HashSWF(const char *url, unsigned int *size, unsigned char *hash,
> + 		   int age);
> + 
> ++  AVal AVcopy(AVal src);
> ++  AVal StripParams(AVal *src);
> ++  char *strreplace(char *srcstr, int srclen, char *orig, char *repl, int didAlloc);
> ++  int strsplit(char *src, int srclen, char delim, char ***params);
> ++
> + #ifdef __cplusplus
> + };
> + #endif
> +diff -uNr librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497.org/librtmp/rtmp_sys.h librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497/librtmp/rtmp_sys.h
> +--- librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497.org/librtmp/rtmp_sys.h	2014-03-02 19:20:23.000000000 +0100
> ++++ librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497/librtmp/rtmp_sys.h	2014-05-04 17:55:17.517338389 +0200
> +@@ -65,6 +65,7 @@
> + #include <polarssl/net.h>
> + #include <polarssl/ssl.h>
> + #include <polarssl/havege.h>
> ++#include <polarssl/md5.h>
> + #if POLARSSL_VERSION_NUMBER < 0x01010000
> + #define havege_random	havege_rand
> + #endif
> +@@ -105,6 +106,7 @@
> + #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>
> +@@ -122,6 +124,8 @@
> + #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 *
> +@@ -134,6 +138,7 @@
> + #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 -uNr librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497.org/Makefile librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497/Makefile
> +--- librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497.org/Makefile	2014-03-02 19:20:23.000000000 +0100
> ++++ librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497/Makefile	2014-05-04 17:55:17.513338440 +0200
> +@@ -32,7 +32,7 @@
> + SBINDIR=$(DESTDIR)$(sbindir)
> + MANDIR=$(DESTDIR)$(mandir)
> + 
> +-LIBS_posix=
> ++LIBS_posix=-lm
> + LIBS_darwin=
> + LIBS_mingw=-lws2_32 -lwinmm -lgdi32
> + LIB_RTMP=-Llibrtmp -lrtmp
> +diff -uNr librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497.org/rtmpdump.c librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497/rtmpdump.c
> +--- librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497.org/rtmpdump.c	2014-03-02 19:20:23.000000000 +0100
> ++++ librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497/rtmpdump.c	2014-05-04 17:55:17.517338389 +0200
> +@@ -283,6 +283,7 @@
> +   uint8_t dataType;
> +   int bAudioOnly;
> +   off_t size;
> ++  char *syncbuf, *p;
> + 
> +   fseek(file, 0, SEEK_END);
> +   size = ftello(file);
> +@@ -293,8 +294,8 @@
> + 
> +   bAudioOnly = (dataType & 0x4) && !(dataType & 0x1);
> + 
> +-  RTMP_Log(RTMP_LOGDEBUG, "bAudioOnly: %d, size: %llu", bAudioOnly,
> +-      (unsigned long long) size);
> ++  RTMP_Log(RTMP_LOGDEBUG, "bAudioOnly: %d, size: %lu", bAudioOnly,
> ++           (unsigned long) size);
> + 
> +   // ok, we have to get the timestamp of the last keyframe (only keyframes are seekable) / last audio frame (audio only streams)
> + 
> +@@ -326,6 +327,51 @@
> +       prevTagSize = AMF_DecodeInt32(buffer);
> +       //RTMP_Log(RTMP_LOGDEBUG, "Last packet: prevTagSize: %d", prevTagSize);
> + 
> ++      if (prevTagSize <= 0 || prevTagSize > size - 4 - 13)
> ++        {
> ++          /* Last packet was not fully received - try to sync to last tag */
> ++          prevTagSize = 0;
> ++          tsize = size > 0x100000 ? 0x100000 : size; /* 1MB should be enough for 3500K bitrates */
> ++          if (tsize > 13 + 15)
> ++            {
> ++              tsize -= 13; // do not read header
> ++              syncbuf = (char *) malloc(tsize);
> ++              if (syncbuf)
> ++                {
> ++                  fseeko(file, size - tsize, SEEK_SET);
> ++                  if (fread(syncbuf, 1, tsize, file) == tsize)
> ++                    {
> ++                      p = syncbuf + tsize;
> ++                      while (p >= syncbuf + 15)
> ++                        {
> ++                          /* Check for StreamID */
> ++                          if (AMF_DecodeInt24(p - 7) == 0)
> ++                            {
> ++                              /* Check for Audio/Video/Script */
> ++                              dataType = p[-15] & 0x1F;
> ++                              if (dataType == 8 || dataType == 9 || dataType == 18)
> ++                                {
> ++                                  prevTagSize = AMF_DecodeInt24(p - 14);
> ++                                  if ((prevTagSize < tsize) && (p + prevTagSize + 11 <= syncbuf + tsize - 4)
> ++                                      && (AMF_DecodeInt32(p - 4 + prevTagSize) == prevTagSize + 11))
> ++                                    {
> ++                                      prevTagSize = syncbuf + tsize - p + 15;
> ++                                      RTMP_Log(RTMP_LOGDEBUG, "Sync success - found last tag at 0x%x", (uint32_t) (size - prevTagSize));
> ++                                      prevTagSize -= 4;
> ++                                      tsize = 0;
> ++                                      break;
> ++                                    }
> ++                                  else
> ++                                    prevTagSize = 0;
> ++                                }
> ++                            }
> ++                          --p;
> ++                        }
> ++                    }
> ++                  free(syncbuf);
> ++                }
> ++            }
> ++        }
> +       if (prevTagSize == 0)
> + 	{
> + 	  RTMP_Log(RTMP_LOGERROR, "Couldn't find keyframe to resume from!");
> +@@ -705,6 +751,8 @@
> + 	  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",
> +@@ -751,7 +799,8 @@
> +   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;
> +@@ -858,12 +907,13 @@
> +     {"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:i:p:a:b:f:o:u:C:n:c:l:y:Ym:k:d:A:B:T:w:x:W:X:S:#j:",
> ++                      "hVveqzRr:s:t:i: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)
> +@@ -995,7 +1045,7 @@
> + 		  port = parsedPort;
> + 		if (playpath.av_len == 0 && parsedPlaypath.av_len)
> + 		  {
> +-		    playpath = parsedPlaypath;
> ++                    playpath = AVcopy(parsedPlaypath);
> + 		  }
> + 		if (protocol == RTMP_PROTOCOL_UNDEFINED)
> + 		  protocol = parsedProtocol;
> +@@ -1079,6 +1129,9 @@
> + 	case 'j':
> + 	  STR2AVAL(usherToken, optarg);
> + 	  break;
> ++	case 'J':
> ++	  STR2AVAL(WeebToken, optarg);
> ++	  break;
> + 	default:
> + 	  RTMP_LogPrintf("unknown option: %c\n", opt);
> + 	  usage(argv[0]);
> +@@ -1170,14 +1223,14 @@
> + 
> +   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;
> +@@ -1197,8 +1250,8 @@
> +   if (!fullUrl.av_len)
> +     {
> +       RTMP_SetupStream(&rtmp, protocol, &hostname, port, &sockshost, &playpath,
> +-		       &tcUrl, &swfUrl, &pageUrl, &app, &auth, &swfHash, swfSize,
> +-		       &flashVer, &subscribepath, &usherToken, dSeek, dStopOffset, bLiveStream, timeout);
> ++                       &tcUrl, &swfUrl, &pageUrl, &app, &auth, &swfHash, swfSize,
> ++                       &flashVer, &subscribepath, &usherToken, &WeebToken, dSeek, dStopOffset, bLiveStream, timeout);
> +     }
> +   else
> +     {
> +diff -uNr librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497.org/rtmpgw.c librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497/rtmpgw.c
> +--- librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497.org/rtmpgw.c	2014-03-02 19:20:23.000000000 +0100
> ++++ librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497/rtmpgw.c	2014-05-04 17:55:17.525338289 +0200
> +@@ -96,7 +96,8 @@
> +   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;
> +@@ -556,8 +557,8 @@
> +   if (!req.fullUrl.av_len)
> +     {
> +       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.bLiveStream, req.timeout);
> ++                       &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);
> +     }
> +   else
> +     {
> +@@ -972,6 +973,9 @@
> +     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;
> +@@ -1044,6 +1048,7 @@
> +     {"quiet", 0, NULL, 'q'},
> +     {"verbose", 0, NULL, 'V'},
> +     {"jtv", 1, NULL, 'j'},
> ++    {"weeb", 1, NULL, 'J'},
> +     {0, 0, 0, 0}
> +   };
> + 
> +@@ -1056,7 +1061,7 @@
> + 
> +   while ((opt =
> + 	  getopt_long(argc, argv,
> +-		      "hvqVzr:s:t:i: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:i: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)
> +@@ -1121,6 +1126,8 @@
> + 	  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 -uNr librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497.org/rtmpsrv.c librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497/rtmpsrv.c
> +--- librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497.org/rtmpsrv.c	2014-03-02 19:20:23.000000000 +0100
> ++++ librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497/rtmpsrv.c	2014-05-04 17:55:17.525338289 +0200
> +@@ -25,9 +25,13 @@
> +  */
> + 
> + #include <stdlib.h>
> ++#ifdef __MINGW_H
> ++#include <unistd.h>
> ++#endif
> + #include <string.h>
> + #include <math.h>
> + #include <limits.h>
> ++#include <time.h>
> + 
> + #include <signal.h>
> + #include <getopt.h>
> +@@ -94,12 +98,19 @@
> + STREAMING_SERVER *rtmpServer = 0;	// server structure pointer
> + void *sslCtx = NULL;
> + 
> ++int file_exists(const char *fname);
> + STREAMING_SERVER *startStreaming(const char *address, int port);
> + void stopStreaming(STREAMING_SERVER * server);
> + void AVreplace(AVal *src, const AVal *orig, const AVal *repl);
> + 
> + 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
> + {
> +@@ -168,6 +179,12 @@
> + SAVC(code);
> + SAVC(description);
> + SAVC(secureToken);
> ++SAVC(_checkbw);
> ++SAVC(_onbwdone);
> ++SAVC(checkBandwidth);
> ++SAVC(onBWDone);
> ++SAVC(FCSubscribe);
> ++SAVC(onFCSubscribe);
> + 
> + static int
> + SendConnectResult(RTMP *r, double txn)
> +@@ -191,7 +208,7 @@
> +   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);
> +@@ -213,7 +230,7 @@
> +   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;
> +@@ -234,7 +251,7 @@
> + SendResultNumber(RTMP *r, double txn, double ID)
> + {
> +   RTMPPacket packet;
> +-  char pbuf[256], *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; */
> +@@ -264,12 +281,13 @@
> + SAVC(details);
> + SAVC(clientid);
> + static const AVal av_NetStream_Authenticate_UsherToken = AVC("NetStream.Authenticate.UsherToken");
> ++static const AVal av_FCSubscribe_message = AVC("FCSubscribe to stream");
> + 
> + 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; */
> +@@ -301,7 +319,7 @@
> + 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; */
> +@@ -329,6 +347,83 @@
> +   return RTMP_SendPacket(r, &packet, FALSE);
> + }
> + 
> ++static int
> ++SendCheckBWResponse(RTMP *r, int oldMethodType, int onBWDoneInit)
> ++{
> ++  RTMPPacket packet;
> ++  char pbuf[1024], *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, 0);
> ++    }
> ++  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, 0);
> ++          enc = AMF_EncodeNumber(enc, pend, 0);
> ++          enc = AMF_EncodeNumber(enc, pend, 20);
> ++        }
> ++    }
> ++
> ++  packet.m_nBodySize = enc - packet.m_body;
> ++
> ++  return RTMP_SendPacket(r, &packet, FALSE);
> ++}
> ++
> ++static int
> ++SendOnFCSubscribe(RTMP *r)
> ++{
> ++  RTMPPacket packet;
> ++  char pbuf[1024], *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;
> ++  enc = AMF_EncodeString(enc, pend, &av_onFCSubscribe);
> ++  enc = AMF_EncodeNumber(enc, pend, 0);
> ++  *enc++ = AMF_NULL;
> ++
> ++  *enc++ = AMF_OBJECT;
> ++  enc = AMF_EncodeNamedString(enc, pend, &av_level, &av_status);
> ++  enc = AMF_EncodeNamedString(enc, pend, &av_code, &av_NetStream_Play_Start);
> ++  enc = AMF_EncodeNamedString(enc, pend, &av_description, &av_FCSubscribe_message);
> ++  enc = AMF_EncodeNamedNumber(enc, pend, &av_clientid, 0);
> ++  *enc++ = 0;
> ++  *enc++ = 0;
> ++  *enc++ = AMF_OBJECT_END;
> ++
> ++  packet.m_nBodySize = enc - packet.m_body;
> ++
> ++  return RTMP_SendPacket(r, &packet, FALSE);
> ++}
> ++
> + static void
> + spawn_dumper(int argc, AVal *av, char *cmd)
> + {
> +@@ -389,6 +484,8 @@
> + 	  len += 40;
> + 	  break;
> + 	case AMF_OBJECT:
> ++        case AMF_ECMA_ARRAY:
> ++        case AMF_STRICT_ARRAY:
> + 	  len += 9;
> + 	  len += countAMF(&p->p_vu.p_object, argc);
> + 	  (*argc) += 2;
> +@@ -404,12 +501,14 @@
> + static char *
> + dumpAMF(AMFObject *obj, char *ptr, AVal *argv, int *argc)
> + {
> +-  int i, len, ac = *argc;
> ++  int i, ac = *argc;
> +   const char opt[] = "NBSO Z";
> + 
> +-  for (i=0, len=0; i < obj->o_num; i++)
> ++  for (i = 0; i < obj->o_num; i++)
> +     {
> +       AMFObjectProperty *p = &obj->o_props[i];
> ++      if ((p->p_type == AMF_ECMA_ARRAY) || (p->p_type == AMF_STRICT_ARRAY))
> ++        p->p_type = AMF_OBJECT;
> +       argv[ac].av_val = ptr+1;
> +       argv[ac++].av_len = 2;
> +       ptr += sprintf(ptr, " -C ");
> +@@ -569,6 +668,7 @@
> + 	  server->arglen += countAMF(&r->Link.extras, &server->argc);
> + 	}
> +       SendConnectResult(r, txn);
> ++      SendCheckBWResponse(r, FALSE, TRUE);
> +     }
> +   else if (AVMATCH(&method, &av_createStream))
> +     {
> +@@ -583,10 +683,26 @@
> +       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_FCSubscribe))
> ++    {
> ++      SendOnFCSubscribe(r);
> ++    }
> +   else if (AVMATCH(&method, &av_play))
> +     {
> +       char *file, *p, *q, *cmd, *ptr;
> +@@ -600,6 +716,17 @@
> +       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 || (r->Link.app.av_val && 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 +
> +@@ -617,6 +744,7 @@
> + 	  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;
> + 
> +@@ -641,6 +769,7 @@
> + 	      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;
> + 	    }
> +@@ -663,10 +792,17 @@
> + 	      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;
> +@@ -674,7 +810,13 @@
> + 	    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)
> +@@ -708,25 +850,82 @@
> + 
> + 	  memcpy(file, av.av_val, av.av_len);
> + 	  file[av.av_len] = '\0';
> +-	  for (p=file; *p; p++)
> +-	    if (*p == ':')
> +-	      *p = '_';
> + 
> +-	  /* Add extension if none present */
> +-	  if (file[av.av_len - 4] != '.')
> +-	    {
> +-	      av.av_len += 4;
> +-	    }
> +-	  /* Always use flv extension, regardless of original */
> +-	  if (strcmp(file+av.av_len-4, ".flv"))
> +-	    {
> +-	      strcpy(file+av.av_len-4, ".flv");
> +-	    }
> ++          if (strlen(file) < 128)
> ++            {
> ++              /* Add extension if none present */
> ++              if (file[av.av_len - 4] != '.')
> ++                {
> ++                  av.av_len += 4;
> ++                }
> ++
> ++              /* Always use flv extension, regardless of original */
> ++              if (strcmp(file + av.av_len - 4, ".flv"))
> ++                {
> ++                  strcpy(file + av.av_len - 4, ".flv");
> ++                }
> ++
> ++              /* Remove invalid characters from filename */
> ++              file = strreplace(file, 0, ":", "_", TRUE);
> ++              file = strreplace(file, 0, "&", "_", TRUE);
> ++              file = strreplace(file, 0, "^", "_", TRUE);
> ++              file = strreplace(file, 0, "|", "_", TRUE);
> ++            }
> ++          else
> ++            {
> ++              /* Filename too long - generate unique name */
> ++              strcpy(file, "vXXXXXX");
> ++              mktemp(file);
> ++              strcat(file, ".flv");
> ++            }
> ++
> ++          /* Add timestamp to the filename */
> ++          char *filename, *pfilename, timestamp[21];
> ++          int filename_len, timestamp_len;
> ++          time_t current_time;
> ++
> ++          time(&current_time);
> ++          timestamp_len = strftime(&timestamp[0], sizeof (timestamp), "%Y-%m-%d_%I-%M-%S_", localtime(&current_time));
> ++          timestamp[timestamp_len] = '\0';
> ++          filename_len = strlen(file);
> ++          filename = malloc(timestamp_len + filename_len + 1);
> ++          pfilename = filename;
> ++          memcpy(pfilename, timestamp, timestamp_len);
> ++          pfilename += timestamp_len;
> ++          memcpy(pfilename, file, filename_len);
> ++          pfilename += filename_len;
> ++          *pfilename++ = '\0';
> ++          file = filename;
> ++
> + 	  argv[argc].av_val = ptr + 1;
> + 	  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 + 1))
> ++                sprintf(vlc + 1, "%s%s", ProgramFiles, "\\VideoLAN\\VLC\\vlc.exe");
> ++              strcpy(vlc + strlen(vlc), "\" -");
> ++            }
> ++          else
> ++            vlc = "vlc -";
> ++
> ++          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))
> + 	    {
> +@@ -740,7 +939,21 @@
> + 	      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, "%", "%%", FALSE);
> ++              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
> ++
> ++              /* Save command to text file */
> ++              FILE *cmdfile = fopen("Command.txt", "a");
> ++              fprintf(cmdfile, "%s\n", cmd);
> ++              fclose(cmdfile);
> + 	    }
> + 
> + 	  free(cmd);
> +@@ -859,12 +1072,18 @@
> + 	{
> + 	case 'q':
> + 	  RTMP_LogPrintf("Exiting\n");
> +-	  stopStreaming(rtmpServer);
> +-	  exit(0);
> ++          if (rtmpServer)
> ++            stopStreaming(rtmpServer);
> + 	  break;
> + 	default:
> + 	  RTMP_LogPrintf("Unknown command \'%c\', ignoring\n", ich);
> + 	}
> ++      sleep(1);
> ++      if (rtmpServer && (rtmpServer->state == STREAMING_STOPPED))
> ++        {
> ++          RTMP_Log(RTMP_LOGDEBUG, "Exiting text UI thread");
> ++          break;
> ++        }
> +     }
> +   TFRET();
> + }
> +@@ -1052,7 +1271,6 @@
> +     }
> + }
> + 
> +-
> + void
> + sigIntHandler(int sig)
> + {
> +@@ -1189,3 +1407,15 @@
> +   src->av_val = dest;
> +   src->av_len = dptr - dest;
> + }
> ++
> ++int
> ++file_exists(const char *fname)
> ++{
> ++  FILE *file;
> ++  if ((file = fopen(fname, "r")))
> ++    {
> ++      fclose(file);
> ++      return TRUE;
> ++    }
> ++  return FALSE;
> ++}
> +diff -uNr librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497.org/rtmpsuck.c librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497/rtmpsuck.c
> +--- librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497.org/rtmpsuck.c	2014-03-02 19:20:23.000000000 +0100
> ++++ librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497/rtmpsuck.c	2014-05-04 17:55:17.525338289 +0200
> +@@ -25,10 +25,13 @@
> +  */
> + 
> + #include <stdlib.h>
> ++#ifdef __MINGW_H
> ++#include <unistd.h>
> ++#endif
> + #include <string.h>
> + #include <math.h>
> + #include <limits.h>
> +-
> ++#include <time.h>
> + #include <signal.h>
> + #include <getopt.h>
> + 
> +@@ -141,18 +144,21 @@
> + SAVC(secureToken);
> + SAVC(onStatus);
> + SAVC(close);
> ++SAVC(play2);
> + 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_NetConnection_Connect_Rejected = AVC("NetConnection.Connect.Rejected");
> + 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);
> ++
> + // Returns 0 for OK/Failed/error, 1 for 'Stop or Complete'
> + int
> + ServeInvoke(STREAMING_SERVER *server, int which, RTMPPacket *pack, const char *body)
> +@@ -198,26 +204,28 @@
> +           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 +233,7 @@
> +               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 +275,7 @@
> +             }
> +           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 +295,21 @@
> +           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(2048, 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 +318,37 @@
> +           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))
> ++    {
> ++      AVal usherToken = {0};
> ++      AMFProp_GetString(AMF_GetProp(&obj, NULL, 3), &usherToken);
> ++      server->rc.Link.usherToken = AVcopy(usherToken);
> ++      RTMP_LogPrintf("%10s : %.*s\n", "usherToken", server->rc.Link.usherToken.av_len, server->rc.Link.usherToken.av_val);
> ++    }
> ++  else if (AVMATCH(&method, &av_play2))
> ++    {
> ++      RTMP_Log(RTMP_LOGDEBUG, "%s: Detected play2 request\n", __FUNCTION__);
> ++      if (body && nBodySize > 0)
> ++        {
> ++          char* pCmd = (char*) body;
> ++          char* pEnd = pCmd + nBodySize - 4;
> ++          while (pCmd < pEnd)
> ++            {
> ++              if (pCmd[0] == 'p' && pCmd[1] == 'l' && pCmd[2] == 'a' && pCmd[3] == 'y' && pCmd[4] == '2')
> ++                {
> ++                  /* Disable bitrate transition by sending invalid command */
> ++                  pCmd[4] = 'z';
> ++                  break;
> ++                }
> ++              ++pCmd;
> ++            }
> ++        }
> +     }
> +   else if (AVMATCH(&method, &av_play))
> +     {
> +@@ -323,6 +369,14 @@
> +       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 || (server->rc.Link.app.av_val && 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)
> +         {
> +@@ -362,19 +416,104 @@
> +       /* hope there aren't more than 255 dups */
> +       if (count)
> +         flen += 2;
> +-      file = malloc(flen+1);
> ++      file = malloc(flen + 5);
> + 
> +       memcpy(file, av.av_val, av.av_len);
> +       if (count)
> +         sprintf(file+av.av_len, "%02x", count);
> +       else
> +         file[av.av_len] = '\0';
> +-      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);
> ++
> ++      if (strlen(file) < 128)
> ++        {
> ++          /* Add extension if none present */
> ++          if (file[av.av_len - 4] != '.')
> ++            {
> ++              av.av_len += 4;
> ++            }
> ++
> ++          /* Always use flv extension, regardless of original */
> ++          if (strcmp(file + av.av_len - 4, ".flv"))
> ++            {
> ++              strcpy(file + av.av_len - 4, ".flv");
> ++            }
> ++
> ++          /* Remove invalid characters from filename */
> ++          file = strreplace(file, 0, ":", "_", TRUE);
> ++          file = strreplace(file, 0, "&", "_", TRUE);
> ++          file = strreplace(file, 0, "^", "_", TRUE);
> ++          file = strreplace(file, 0, "|", "_", TRUE);
> ++        }
> ++      else
> ++        {
> ++          /* Filename too long - generate unique name */
> ++          strcpy(file, "vXXXXXX");
> ++          mktemp(file);
> ++          strcat(file, ".flv");
> ++        }
> ++
> ++      /* Add timestamp to the filename */
> ++      char *filename, *pfilename, timestamp[21];
> ++      int filename_len, timestamp_len;
> ++      time_t current_time;
> ++
> ++      time(&current_time);
> ++      timestamp_len = strftime(&timestamp[0], sizeof (timestamp), "%Y-%m-%d_%I-%M-%S_", localtime(&current_time));
> ++      timestamp[timestamp_len] = '\0';
> ++      filename_len = strlen(file);
> ++      filename = malloc(timestamp_len + filename_len + 1);
> ++      pfilename = filename;
> ++      memcpy(pfilename, timestamp, timestamp_len);
> ++      pfilename += timestamp_len;
> ++      memcpy(pfilename, file, filename_len);
> ++      pfilename += filename_len;
> ++      *pfilename++ = '\0';
> ++      file = filename;
> ++
> ++      RTMP_LogPrintf("%10s : %.*s\n%10s : %s\n", "Playpath", server->rc.Link.playpath.av_len,
> ++                     server->rc.Link.playpath.av_val, "Saving as", file);
> ++
> ++      /* Save command to text file */
> ++      char *cmd = NULL, *ptr = NULL;
> ++      AVal swfUrl, tcUrl;
> ++
> ++      cmd = calloc(4096, 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, "\"", "\\\"", TRUE);
> ++#ifdef WIN32
> ++          usherToken = strreplace(usherToken, 0, "^", "^^", TRUE);
> ++          usherToken = strreplace(usherToken, 0, "|", "^|", TRUE);
> ++#endif
> ++          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\"\n", file);
> ++
> ++      FILE *cmdfile = fopen("Command.txt", "a");
> ++      fprintf(cmdfile, "%s", cmd);
> ++      fclose(cmdfile);
> ++      free(cmd);
> ++
> +       out = fopen(file, "wb");
> +       free(file);
> +       if (!out)
> +@@ -407,9 +546,10 @@
> + 
> +       RTMP_Log(RTMP_LOGDEBUG, "%s, onStatus: %s", __FUNCTION__, code.av_val);
> +       if (AVMATCH(&code, &av_NetStream_Failed)
> +-	  || AVMATCH(&code, &av_NetStream_Play_Failed)
> +-	  || AVMATCH(&code, &av_NetStream_Play_StreamNotFound)
> +-	  || AVMATCH(&code, &av_NetConnection_Connect_InvalidApp))
> ++          || AVMATCH(&code, &av_NetStream_Play_Failed)
> ++          || AVMATCH(&code, &av_NetStream_Play_StreamNotFound)
> ++          || AVMATCH(&code, &av_NetConnection_Connect_Rejected)
> ++          || AVMATCH(&code, &av_NetConnection_Connect_InvalidApp))
> + 	{
> + 	  ret = 1;
> + 	}
> +@@ -719,13 +859,18 @@
> + 	{
> + 	case 'q':
> + 	  RTMP_LogPrintf("Exiting\n");
> +-	  stopStreaming(rtmpServer);
> +-          free(rtmpServer);
> +-	  exit(0);
> ++          if (rtmpServer)
> ++            stopStreaming(rtmpServer);
> + 	  break;
> + 	default:
> + 	  RTMP_LogPrintf("Unknown command \'%c\', ignoring\n", ich);
> + 	}
> ++      sleep(1);
> ++      if (rtmpServer && (rtmpServer->state == STREAMING_STOPPED))
> ++        {
> ++          RTMP_Log(RTMP_LOGDEBUG, "Exiting text UI thread");
> ++          break;
> ++        }
> +     }
> +   TFRET();
> + }
> +@@ -1123,7 +1268,6 @@
> +     }
> + }
> + 
> +-
> + void
> + sigIntHandler(int sig)
> + {
> +@@ -1196,3 +1340,48 @@
> + #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 == AMF_ECMA_ARRAY) || (p->p_type == AMF_STRICT_ARRAY))
> ++        p->p_type = AMF_OBJECT;
> ++      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;
> ++}
> +diff -uNr librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497.org/thread.c librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497/thread.c
> +--- librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497.org/thread.c	2014-03-02 19:20:23.000000000 +0100
> ++++ librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497/thread.c	2014-05-04 17:55:17.525338289 +0200
> +@@ -32,7 +32,7 @@
> +   HANDLE thd;
> + 
> +   thd = (HANDLE) _beginthread(routine, 0, args);
> +-  if (thd == -1L)
> ++  if (thd == INVALID_HANDLE_VALUE)
> +     RTMP_LogPrintf("%s, _beginthread failed with %d\n", __FUNCTION__, errno);
> + 
> +   return thd;
> -- 
> 1.7.10.4
> 
> _______________________________________________
> buildroot mailing list
> buildroot at busybox.net
> http://lists.busybox.net/mailman/listinfo/buildroot

-- 
.-----------------.--------------------.------------------.--------------------.
|  Yann E. MORIN  | Real-Time Embedded | /"\ ASCII RIBBON | Erics' conspiracy: |
| +33 662 376 056 | Software  Designer | \ / CAMPAIGN     |  ___               |
| +33 223 225 172 `------------.-------:  X  AGAINST      |  \e/  There is no  |
| http://ymorin.is-a-geek.org/ | _/*\_ | / \ HTML MAIL    |   v   conspiracy.  |
'------------------------------^-------^------------------^--------------------'

  reply	other threads:[~2014-05-17 20:11 UTC|newest]

Thread overview: 68+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-05-17 15:57 [Buildroot] [PATCH v8 00/28] xbmc: bump version to 13.0-Gotham Bernd Kuhls
2014-05-17 15:57 ` [Buildroot] [PATCH v8 01/28] xproto_dri3proto: New package Bernd Kuhls
2014-05-17 15:57 ` [Buildroot] [PATCH v8 02/28] mesa3d: Bump version to 10.2-rc3 Bernd Kuhls
2014-05-17 15:57 ` [Buildroot] [PATCH v8 03/28] mesa3d: Depend on xorg meta package instead of xserver_xorg-server Bernd Kuhls
2014-05-17 19:56   ` Yann E. MORIN
2014-05-17 20:50     ` Bernd Kuhls
2014-05-17 21:07       ` Yann E. MORIN
2014-05-17 15:57 ` [Buildroot] [PATCH v8 04/28] mesa3d: add dependency xproto_presentproto Bernd Kuhls
2014-05-17 15:57 ` [Buildroot] [PATCH v8 05/28] mesa3d: Remove dependency for the libxml2 module of host-python Bernd Kuhls
2014-05-17 15:57 ` [Buildroot] [PATCH v8 06/28] mesa3d: Add dri3 support Bernd Kuhls
2014-05-17 15:57 ` [Buildroot] [PATCH v8 07/28] mesa3d: dri2 does not need udev support Bernd Kuhls
2014-05-17 15:57 ` [Buildroot] [PATCH v8 08/28] libva: new package Bernd Kuhls
2014-05-17 15:57 ` [Buildroot] [PATCH v8 09/28] libva-intel-driver: " Bernd Kuhls
2014-05-17 15:57 ` [Buildroot] [PATCH v8 10/28] ffmpeg: Add libva support Bernd Kuhls
2014-05-17 15:57 ` [Buildroot] [PATCH v8 11/28] libglu: new package Bernd Kuhls
2014-05-17 15:57 ` [Buildroot] [PATCH v8 12/28] libglew: " Bernd Kuhls
2014-05-17 15:57 ` [Buildroot] [PATCH v8 13/28] sdl: Add new dependency libglu for opengl support Bernd Kuhls
2014-05-17 20:00   ` Yann E. MORIN
2014-05-17 15:57 ` [Buildroot] [PATCH v8 14/28] xdriver_xf86-video-intel: Bump version to 2.99.911 Bernd Kuhls
2014-05-17 20:06   ` Yann E. MORIN
2014-05-17 20:51     ` Bernd Kuhls
2014-05-17 21:09       ` Yann E. MORIN
2014-05-17 21:17         ` Bernd Kuhls
2014-05-17 15:57 ` [Buildroot] [PATCH v8 15/28] rtmpdump: Add KSV patch Bernd Kuhls
2014-05-17 20:11   ` Yann E. MORIN [this message]
2014-05-17 21:15     ` Bernd Kuhls
2014-05-17 15:57 ` [Buildroot] [PATCH v8 16/28] xbmc: bump version to 13.0 Bernd Kuhls
2014-05-18 16:54   ` Bernd Kuhls
2014-05-17 15:57 ` [Buildroot] [PATCH v8 17/28] xbmc-pvr-addons: Version bump to xbmc Gotham-compatible addon Bernd Kuhls
2014-05-17 20:15   ` Yann E. MORIN
2014-05-17 20:51     ` Bernd Kuhls
2014-05-17 15:57 ` [Buildroot] [PATCH v8 18/28] xbmc-addon-xvdr: " Bernd Kuhls
2014-05-17 20:16   ` Yann E. MORIN
2014-05-17 20:52     ` Bernd Kuhls
2014-05-17 15:57 ` [Buildroot] [PATCH v8 19/28] xbmc: Add host-gettext dependency Bernd Kuhls
2014-05-17 20:29   ` Yann E. MORIN
2014-05-17 20:52     ` Bernd Kuhls
2014-05-17 15:57 ` [Buildroot] [PATCH v8 20/28] xbmc: Allow compilation with uClibc Bernd Kuhls
2014-05-17 20:40   ` Yann E. MORIN
2014-05-17 20:47     ` Bernd Kuhls
2014-05-17 20:56       ` Yann E. MORIN
2014-05-17 15:57 ` [Buildroot] [PATCH v8 21/28] xbmc: Add X.org/OpenGL support Bernd Kuhls
2014-05-17 20:55   ` Yann E. MORIN
2014-05-17 21:01     ` Bernd Kuhls
2014-05-17 21:17       ` Yann E. MORIN
2014-05-18 11:58     ` Bernd Kuhls
2014-05-18 16:29       ` Yann E. MORIN
2014-05-18 16:52         ` Bernd Kuhls
2014-05-18 17:07           ` Yann E. MORIN
2014-05-18 16:58         ` Bernd Kuhls
2014-05-18 17:29           ` Yann E. MORIN
2014-05-18 17:40             ` Bernd Kuhls
2014-05-20 16:33             ` Arnout Vandecappelle
2014-05-20 16:49               ` Yann E. MORIN
2014-05-20 21:10                 ` Peter Korsgaard
2014-05-20 23:16                 ` Arnout Vandecappelle
2014-05-21  7:39                   ` Bernd Kuhls
2014-05-17 15:57 ` [Buildroot] [PATCH v8 22/28] xbmc: Add VA-API support Bernd Kuhls
2014-05-17 21:01   ` Yann E. MORIN
2014-05-17 15:57 ` [Buildroot] [PATCH v8 23/28] xbmc: Add alsa support Bernd Kuhls
2014-05-17 15:57 ` [Buildroot] [PATCH v8 24/28] xbmc: Add lame support Bernd Kuhls
2014-05-17 15:57 ` [Buildroot] [PATCH v8 25/28] xbmc: Disable broken rsxs screensaver Bernd Kuhls
2014-05-17 15:57 ` [Buildroot] [PATCH v8 26/28] xbmc: Add option for Goom screensaver Bernd Kuhls
2014-05-17 21:04   ` Yann E. MORIN
2014-05-17 15:57 ` [Buildroot] [PATCH v8 27/28] xbmc: add nasm/yasm dependency Bernd Kuhls
2014-05-17 21:06   ` Yann E. MORIN
2014-05-17 15:57 ` [Buildroot] [PATCH v8 28/28] xbmc: Fix TexturePacker compile Bernd Kuhls
2014-05-17 21:13   ` Yann E. MORIN

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20140517201125.GG3459@free.fr \
    --to=yann.morin.1998@free.fr \
    --cc=buildroot@busybox.net \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.