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