666 lines
15 KiB
C
666 lines
15 KiB
C
|
/*
|
||
|
* Copyright (C) 2009-2010 Howard Chu
|
||
|
*
|
||
|
* This file is part of librtmp.
|
||
|
*
|
||
|
* librtmp is free software; you can redistribute it and/or modify
|
||
|
* it under the terms of the GNU Lesser General Public License as
|
||
|
* published by the Free Software Foundation; either version 2.1,
|
||
|
* or (at your option) any later version.
|
||
|
*
|
||
|
* librtmp is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
* GNU General Public License for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU Lesser General Public License
|
||
|
* along with librtmp see the file COPYING. If not, write to
|
||
|
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||
|
* Boston, MA 02110-1301, USA.
|
||
|
* http://www.gnu.org/copyleft/lgpl.html
|
||
|
*/
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
#include <ctype.h>
|
||
|
#include <time.h>
|
||
|
|
||
|
#include "rtmp_sys.h"
|
||
|
#include "log.h"
|
||
|
#include "http.h"
|
||
|
|
||
|
#ifdef CRYPTO
|
||
|
#ifdef USE_POLARSSL
|
||
|
#include <polarssl/sha2.h>
|
||
|
#ifndef SHA256_DIGEST_LENGTH
|
||
|
#define SHA256_DIGEST_LENGTH 32
|
||
|
#endif
|
||
|
#define HMAC_CTX sha2_context
|
||
|
#define HMAC_setup(ctx, key, len) sha2_hmac_starts(&ctx, (unsigned char *)key, len, 0)
|
||
|
#define HMAC_crunch(ctx, buf, len) sha2_hmac_update(&ctx, buf, len)
|
||
|
#define HMAC_finish(ctx, dig, dlen) dlen = SHA256_DIGEST_LENGTH; sha2_hmac_finish(&ctx, dig)
|
||
|
#define HMAC_close(ctx)
|
||
|
#elif defined(USE_GNUTLS)
|
||
|
#include <nettle/hmac.h>
|
||
|
#ifndef SHA256_DIGEST_LENGTH
|
||
|
#define SHA256_DIGEST_LENGTH 32
|
||
|
#endif
|
||
|
#undef HMAC_CTX
|
||
|
#define HMAC_CTX struct hmac_sha256_ctx
|
||
|
#define HMAC_setup(ctx, key, len) hmac_sha256_set_key(&ctx, len, key)
|
||
|
#define HMAC_crunch(ctx, buf, len) hmac_sha256_update(&ctx, len, buf)
|
||
|
#define HMAC_finish(ctx, dig, dlen) dlen = SHA256_DIGEST_LENGTH; hmac_sha256_digest(&ctx, SHA256_DIGEST_LENGTH, dig)
|
||
|
#define HMAC_close(ctx)
|
||
|
#else /* USE_OPENSSL */
|
||
|
#include <openssl/ssl.h>
|
||
|
#include <openssl/sha.h>
|
||
|
#include <openssl/hmac.h>
|
||
|
#include <openssl/rc4.h>
|
||
|
#define HMAC_setup(ctx, key, len) HMAC_CTX_init(&ctx); HMAC_Init_ex(&ctx, (unsigned char *)key, len, EVP_sha256(), 0)
|
||
|
#define HMAC_crunch(ctx, buf, len) HMAC_Update(&ctx, (unsigned char *)buf, len)
|
||
|
#define HMAC_finish(ctx, dig, dlen) HMAC_Final(&ctx, (unsigned char *)dig, &dlen);
|
||
|
#define HMAC_close(ctx) HMAC_CTX_cleanup(&ctx)
|
||
|
#endif
|
||
|
|
||
|
extern void RTMP_TLS_Init();
|
||
|
extern TLS_CTX RTMP_TLS_ctx;
|
||
|
|
||
|
#include <zlib.h>
|
||
|
|
||
|
#endif /* CRYPTO */
|
||
|
|
||
|
#define AGENT "Mozilla/5.0"
|
||
|
|
||
|
HTTPResult
|
||
|
HTTP_get(struct HTTP_ctx *http, const char *url, HTTP_read_callback *cb)
|
||
|
{
|
||
|
char *host, *path;
|
||
|
char *p1, *p2;
|
||
|
char hbuf[256];
|
||
|
int port = 80;
|
||
|
#ifdef CRYPTO
|
||
|
int ssl = 0;
|
||
|
#endif
|
||
|
int hlen, flen = 0;
|
||
|
int rc, i;
|
||
|
int len_known;
|
||
|
HTTPResult ret = HTTPRES_OK;
|
||
|
struct sockaddr_in sa;
|
||
|
RTMPSockBuf sb = {0};
|
||
|
|
||
|
http->status = -1;
|
||
|
|
||
|
memset(&sa, 0, sizeof(struct sockaddr_in));
|
||
|
sa.sin_family = AF_INET;
|
||
|
|
||
|
/* we only handle http here */
|
||
|
if (strncasecmp(url, "http", 4))
|
||
|
return HTTPRES_BAD_REQUEST;
|
||
|
|
||
|
if (url[4] == 's')
|
||
|
{
|
||
|
#ifdef CRYPTO
|
||
|
ssl = 1;
|
||
|
port = 443;
|
||
|
if (!RTMP_TLS_ctx)
|
||
|
RTMP_TLS_Init();
|
||
|
#else
|
||
|
return HTTPRES_BAD_REQUEST;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
p1 = strchr(url + 4, ':');
|
||
|
if (!p1 || strncmp(p1, "://", 3))
|
||
|
return HTTPRES_BAD_REQUEST;
|
||
|
|
||
|
host = p1 + 3;
|
||
|
path = strchr(host, '/');
|
||
|
hlen = path - host;
|
||
|
strncpy(hbuf, host, hlen);
|
||
|
hbuf[hlen] = '\0';
|
||
|
host = hbuf;
|
||
|
p1 = strrchr(host, ':');
|
||
|
if (p1)
|
||
|
{
|
||
|
*p1++ = '\0';
|
||
|
port = atoi(p1);
|
||
|
}
|
||
|
|
||
|
sa.sin_addr.s_addr = inet_addr(host);
|
||
|
if (sa.sin_addr.s_addr == INADDR_NONE)
|
||
|
{
|
||
|
struct hostent *hp = gethostbyname(host);
|
||
|
if (!hp || !hp->h_addr)
|
||
|
return HTTPRES_LOST_CONNECTION;
|
||
|
sa.sin_addr = *(struct in_addr *)hp->h_addr;
|
||
|
}
|
||
|
sa.sin_port = htons(port);
|
||
|
sb.sb_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||
|
if (sb.sb_socket == -1)
|
||
|
return HTTPRES_LOST_CONNECTION;
|
||
|
i =
|
||
|
sprintf(sb.sb_buf,
|
||
|
"GET %s HTTP/1.0\r\nUser-Agent: %s\r\nHost: %s\r\nReferer: %.*s\r\n",
|
||
|
path, AGENT, host, (int)(path - url + 1), url);
|
||
|
if (http->date[0])
|
||
|
i += sprintf(sb.sb_buf + i, "If-Modified-Since: %s\r\n", http->date);
|
||
|
i += sprintf(sb.sb_buf + i, "\r\n");
|
||
|
|
||
|
if (connect
|
||
|
(sb.sb_socket, (struct sockaddr *)&sa, sizeof(struct sockaddr)) < 0)
|
||
|
{
|
||
|
ret = HTTPRES_LOST_CONNECTION;
|
||
|
goto leave;
|
||
|
}
|
||
|
#ifdef CRYPTO
|
||
|
if (ssl)
|
||
|
{
|
||
|
#ifdef NO_SSL
|
||
|
RTMP_Log(RTMP_LOGERROR, "%s, No SSL/TLS support", __FUNCTION__);
|
||
|
ret = HTTPRES_BAD_REQUEST;
|
||
|
goto leave;
|
||
|
#else
|
||
|
TLS_client(RTMP_TLS_ctx, sb.sb_ssl);
|
||
|
TLS_setfd(sb.sb_ssl, sb.sb_socket);
|
||
|
if (TLS_connect(sb.sb_ssl) < 0)
|
||
|
{
|
||
|
RTMP_Log(RTMP_LOGERROR, "%s, TLS_Connect failed", __FUNCTION__);
|
||
|
ret = HTTPRES_LOST_CONNECTION;
|
||
|
goto leave;
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
#endif
|
||
|
RTMPSockBuf_Send(&sb, sb.sb_buf, i);
|
||
|
|
||
|
/* set timeout */
|
||
|
#define HTTP_TIMEOUT 5
|
||
|
{
|
||
|
SET_RCVTIMEO(tv, HTTP_TIMEOUT);
|
||
|
if (setsockopt
|
||
|
(sb.sb_socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv)))
|
||
|
{
|
||
|
RTMP_Log(RTMP_LOGERROR, "%s, Setting socket timeout to %ds failed!",
|
||
|
__FUNCTION__, HTTP_TIMEOUT);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
sb.sb_size = 0;
|
||
|
sb.sb_timedout = FALSE;
|
||
|
if (RTMPSockBuf_Fill(&sb) < 1)
|
||
|
{
|
||
|
ret = HTTPRES_LOST_CONNECTION;
|
||
|
goto leave;
|
||
|
}
|
||
|
if (strncmp(sb.sb_buf, "HTTP/1", 6))
|
||
|
{
|
||
|
ret = HTTPRES_BAD_REQUEST;
|
||
|
goto leave;
|
||
|
}
|
||
|
|
||
|
p1 = strchr(sb.sb_buf, ' ');
|
||
|
rc = atoi(p1 + 1);
|
||
|
http->status = rc;
|
||
|
|
||
|
if (rc >= 300)
|
||
|
{
|
||
|
if (rc == 304)
|
||
|
{
|
||
|
ret = HTTPRES_OK_NOT_MODIFIED;
|
||
|
goto leave;
|
||
|
}
|
||
|
else if (rc == 404)
|
||
|
ret = HTTPRES_NOT_FOUND;
|
||
|
else if (rc >= 500)
|
||
|
ret = HTTPRES_SERVER_ERROR;
|
||
|
else if (rc >= 400)
|
||
|
ret = HTTPRES_BAD_REQUEST;
|
||
|
else
|
||
|
ret = HTTPRES_REDIRECTED;
|
||
|
}
|
||
|
|
||
|
p1 = memchr(sb.sb_buf, '\n', sb.sb_size);
|
||
|
if (!p1)
|
||
|
{
|
||
|
ret = HTTPRES_BAD_REQUEST;
|
||
|
goto leave;
|
||
|
}
|
||
|
sb.sb_start = p1 + 1;
|
||
|
sb.sb_size -= sb.sb_start - sb.sb_buf;
|
||
|
|
||
|
while ((p2 = memchr(sb.sb_start, '\r', sb.sb_size)))
|
||
|
{
|
||
|
if (*sb.sb_start == '\r')
|
||
|
{
|
||
|
sb.sb_start += 2;
|
||
|
sb.sb_size -= 2;
|
||
|
break;
|
||
|
}
|
||
|
else
|
||
|
if (!strncasecmp
|
||
|
(sb.sb_start, "Content-Length: ", sizeof("Content-Length: ") - 1))
|
||
|
{
|
||
|
flen = atoi(sb.sb_start + sizeof("Content-Length: ") - 1);
|
||
|
}
|
||
|
else
|
||
|
if (!strncasecmp
|
||
|
(sb.sb_start, "Last-Modified: ", sizeof("Last-Modified: ") - 1))
|
||
|
{
|
||
|
*p2 = '\0';
|
||
|
strcpy(http->date, sb.sb_start + sizeof("Last-Modified: ") - 1);
|
||
|
}
|
||
|
p2 += 2;
|
||
|
sb.sb_size -= p2 - sb.sb_start;
|
||
|
sb.sb_start = p2;
|
||
|
if (sb.sb_size < 1)
|
||
|
{
|
||
|
if (RTMPSockBuf_Fill(&sb) < 1)
|
||
|
{
|
||
|
ret = HTTPRES_LOST_CONNECTION;
|
||
|
goto leave;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
len_known = flen > 0;
|
||
|
while ((!len_known || flen > 0) &&
|
||
|
(sb.sb_size > 0 || RTMPSockBuf_Fill(&sb) > 0))
|
||
|
{
|
||
|
cb(sb.sb_start, 1, sb.sb_size, http->data);
|
||
|
if (len_known)
|
||
|
flen -= sb.sb_size;
|
||
|
http->size += sb.sb_size;
|
||
|
sb.sb_size = 0;
|
||
|
}
|
||
|
|
||
|
if (flen > 0)
|
||
|
ret = HTTPRES_LOST_CONNECTION;
|
||
|
|
||
|
leave:
|
||
|
RTMPSockBuf_Close(&sb);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
#ifdef CRYPTO
|
||
|
|
||
|
#define CHUNK 16384
|
||
|
|
||
|
struct info
|
||
|
{
|
||
|
z_stream *zs;
|
||
|
HMAC_CTX ctx;
|
||
|
int first;
|
||
|
int zlib;
|
||
|
int size;
|
||
|
};
|
||
|
|
||
|
static size_t
|
||
|
swfcrunch(void *ptr, size_t size, size_t nmemb, void *stream)
|
||
|
{
|
||
|
struct info *i = stream;
|
||
|
char *p = ptr;
|
||
|
size_t len = size * nmemb;
|
||
|
|
||
|
if (i->first)
|
||
|
{
|
||
|
i->first = 0;
|
||
|
/* compressed? */
|
||
|
if (!strncmp(p, "CWS", 3))
|
||
|
{
|
||
|
*p = 'F';
|
||
|
i->zlib = 1;
|
||
|
}
|
||
|
HMAC_crunch(i->ctx, (unsigned char *)p, 8);
|
||
|
p += 8;
|
||
|
len -= 8;
|
||
|
i->size = 8;
|
||
|
}
|
||
|
|
||
|
if (i->zlib)
|
||
|
{
|
||
|
unsigned char out[CHUNK];
|
||
|
i->zs->next_in = (unsigned char *)p;
|
||
|
i->zs->avail_in = len;
|
||
|
do
|
||
|
{
|
||
|
i->zs->avail_out = CHUNK;
|
||
|
i->zs->next_out = out;
|
||
|
inflate(i->zs, Z_NO_FLUSH);
|
||
|
len = CHUNK - i->zs->avail_out;
|
||
|
i->size += len;
|
||
|
HMAC_crunch(i->ctx, out, len);
|
||
|
}
|
||
|
while (i->zs->avail_out == 0);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
i->size += len;
|
||
|
HMAC_crunch(i->ctx, (unsigned char *)p, len);
|
||
|
}
|
||
|
return size * nmemb;
|
||
|
}
|
||
|
|
||
|
static int tzoff;
|
||
|
static int tzchecked;
|
||
|
|
||
|
#define JAN02_1980 318340800
|
||
|
|
||
|
static const char *monthtab[12] = { "Jan", "Feb", "Mar",
|
||
|
"Apr", "May", "Jun",
|
||
|
"Jul", "Aug", "Sep",
|
||
|
"Oct", "Nov", "Dec"
|
||
|
};
|
||
|
static const char *days[] =
|
||
|
{ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
|
||
|
|
||
|
/* Parse an HTTP datestamp into Unix time */
|
||
|
static time_t
|
||
|
make_unix_time(char *s)
|
||
|
{
|
||
|
struct tm time;
|
||
|
int i, ysub = 1900, fmt = 0;
|
||
|
char *month;
|
||
|
char *n;
|
||
|
time_t res;
|
||
|
|
||
|
if (s[3] != ' ')
|
||
|
{
|
||
|
fmt = 1;
|
||
|
if (s[3] != ',')
|
||
|
ysub = 0;
|
||
|
}
|
||
|
for (n = s; *n; ++n)
|
||
|
if (*n == '-' || *n == ':')
|
||
|
*n = ' ';
|
||
|
|
||
|
time.tm_mon = 0;
|
||
|
n = strchr(s, ' ');
|
||
|
if (fmt)
|
||
|
{
|
||
|
/* Day, DD-MMM-YYYY HH:MM:SS GMT */
|
||
|
time.tm_mday = strtol(n + 1, &n, 0);
|
||
|
month = n + 1;
|
||
|
n = strchr(month, ' ');
|
||
|
time.tm_year = strtol(n + 1, &n, 0);
|
||
|
time.tm_hour = strtol(n + 1, &n, 0);
|
||
|
time.tm_min = strtol(n + 1, &n, 0);
|
||
|
time.tm_sec = strtol(n + 1, NULL, 0);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/* Unix ctime() format. Does not conform to HTTP spec. */
|
||
|
/* Day MMM DD HH:MM:SS YYYY */
|
||
|
month = n + 1;
|
||
|
n = strchr(month, ' ');
|
||
|
while (isspace(*n))
|
||
|
n++;
|
||
|
time.tm_mday = strtol(n, &n, 0);
|
||
|
time.tm_hour = strtol(n + 1, &n, 0);
|
||
|
time.tm_min = strtol(n + 1, &n, 0);
|
||
|
time.tm_sec = strtol(n + 1, &n, 0);
|
||
|
time.tm_year = strtol(n + 1, NULL, 0);
|
||
|
}
|
||
|
if (time.tm_year > 100)
|
||
|
time.tm_year -= ysub;
|
||
|
|
||
|
for (i = 0; i < 12; i++)
|
||
|
if (!strncasecmp(month, monthtab[i], 3))
|
||
|
{
|
||
|
time.tm_mon = i;
|
||
|
break;
|
||
|
}
|
||
|
time.tm_isdst = 0; /* daylight saving is never in effect in GMT */
|
||
|
|
||
|
/* this is normally the value of extern int timezone, but some
|
||
|
* braindead C libraries don't provide it.
|
||
|
*/
|
||
|
if (!tzchecked)
|
||
|
{
|
||
|
struct tm *tc;
|
||
|
time_t then = JAN02_1980;
|
||
|
tc = localtime(&then);
|
||
|
tzoff = (12 - tc->tm_hour) * 3600 + tc->tm_min * 60 + tc->tm_sec;
|
||
|
tzchecked = 1;
|
||
|
}
|
||
|
res = mktime(&time);
|
||
|
/* Unfortunately, mktime() assumes the input is in local time,
|
||
|
* not GMT, so we have to correct it here.
|
||
|
*/
|
||
|
if (res != -1)
|
||
|
res += tzoff;
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
/* Convert a Unix time to a network time string
|
||
|
* Weekday, DD-MMM-YYYY HH:MM:SS GMT
|
||
|
*/
|
||
|
static void
|
||
|
strtime(time_t * t, char *s)
|
||
|
{
|
||
|
struct tm *tm;
|
||
|
|
||
|
tm = gmtime((time_t *) t);
|
||
|
sprintf(s, "%s, %02d %s %d %02d:%02d:%02d GMT",
|
||
|
days[tm->tm_wday], tm->tm_mday, monthtab[tm->tm_mon],
|
||
|
tm->tm_year + 1900, tm->tm_hour, tm->tm_min, tm->tm_sec);
|
||
|
}
|
||
|
|
||
|
#define HEX2BIN(a) (((a)&0x40)?((a)&0xf)+9:((a)&0xf))
|
||
|
|
||
|
int
|
||
|
RTMP_HashSWF(const char *url, unsigned int *size, unsigned char *hash,
|
||
|
int age)
|
||
|
{
|
||
|
FILE *f = NULL;
|
||
|
char *path, date[64], cctim[64];
|
||
|
long pos = 0;
|
||
|
time_t ctim = -1, cnow;
|
||
|
int i, got = 0, ret = 0;
|
||
|
unsigned int hlen;
|
||
|
struct info in = { 0 };
|
||
|
struct HTTP_ctx http = { 0 };
|
||
|
HTTPResult httpres;
|
||
|
z_stream zs = { 0 };
|
||
|
AVal home, hpre;
|
||
|
|
||
|
date[0] = '\0';
|
||
|
#ifdef _WIN32
|
||
|
#ifdef XBMC4XBOX
|
||
|
hpre.av_val = "Q:";
|
||
|
hpre.av_len = 2;
|
||
|
home.av_val = "\\UserData";
|
||
|
#else
|
||
|
hpre.av_val = getenv("HOMEDRIVE");
|
||
|
hpre.av_len = strlen(hpre.av_val);
|
||
|
home.av_val = getenv("HOMEPATH");
|
||
|
#endif
|
||
|
#define DIRSEP "\\"
|
||
|
|
||
|
#else /* !_WIN32 */
|
||
|
hpre.av_val = "";
|
||
|
hpre.av_len = 0;
|
||
|
home.av_val = getenv("HOME");
|
||
|
#define DIRSEP "/"
|
||
|
#endif
|
||
|
if (!home.av_val)
|
||
|
home.av_val = ".";
|
||
|
home.av_len = strlen(home.av_val);
|
||
|
|
||
|
/* SWF hash info is cached in a fixed-format file.
|
||
|
* url: <url of SWF file>
|
||
|
* ctim: HTTP datestamp of when we last checked it.
|
||
|
* date: HTTP datestamp of the SWF's last modification.
|
||
|
* size: SWF size in hex
|
||
|
* hash: SWF hash in hex
|
||
|
*
|
||
|
* These fields must be present in this order. All fields
|
||
|
* besides URL are fixed size.
|
||
|
*/
|
||
|
path = malloc(hpre.av_len + home.av_len + sizeof(DIRSEP ".swfinfo"));
|
||
|
sprintf(path, "%s%s" DIRSEP ".swfinfo", hpre.av_val, home.av_val);
|
||
|
|
||
|
f = fopen(path, "r+");
|
||
|
while (f)
|
||
|
{
|
||
|
char buf[4096], *file, *p;
|
||
|
|
||
|
file = strchr(url, '/');
|
||
|
if (!file)
|
||
|
break;
|
||
|
file += 2;
|
||
|
file = strchr(file, '/');
|
||
|
if (!file)
|
||
|
break;
|
||
|
file++;
|
||
|
hlen = file - url;
|
||
|
p = strrchr(file, '/');
|
||
|
if (p)
|
||
|
file = p;
|
||
|
else
|
||
|
file--;
|
||
|
|
||
|
while (fgets(buf, sizeof(buf), f))
|
||
|
{
|
||
|
char *r1;
|
||
|
|
||
|
got = 0;
|
||
|
|
||
|
if (strncmp(buf, "url: ", 5))
|
||
|
continue;
|
||
|
if (strncmp(buf + 5, url, hlen))
|
||
|
continue;
|
||
|
r1 = strrchr(buf, '/');
|
||
|
i = strlen(r1);
|
||
|
r1[--i] = '\0';
|
||
|
if (strncmp(r1, file, i))
|
||
|
continue;
|
||
|
pos = ftell(f);
|
||
|
while (got < 4 && fgets(buf, sizeof(buf), f))
|
||
|
{
|
||
|
if (!strncmp(buf, "size: ", 6))
|
||
|
{
|
||
|
*size = strtol(buf + 6, NULL, 16);
|
||
|
got++;
|
||
|
}
|
||
|
else if (!strncmp(buf, "hash: ", 6))
|
||
|
{
|
||
|
unsigned char *ptr = hash, *in = (unsigned char *)buf + 6;
|
||
|
int l = strlen((char *)in) - 1;
|
||
|
for (i = 0; i < l; i += 2)
|
||
|
*ptr++ = (HEX2BIN(in[i]) << 4) | HEX2BIN(in[i + 1]);
|
||
|
got++;
|
||
|
}
|
||
|
else if (!strncmp(buf, "date: ", 6))
|
||
|
{
|
||
|
buf[strlen(buf) - 1] = '\0';
|
||
|
strncpy(date, buf + 6, sizeof(date));
|
||
|
got++;
|
||
|
}
|
||
|
else if (!strncmp(buf, "ctim: ", 6))
|
||
|
{
|
||
|
buf[strlen(buf) - 1] = '\0';
|
||
|
ctim = make_unix_time(buf + 6);
|
||
|
got++;
|
||
|
}
|
||
|
else if (!strncmp(buf, "url: ", 5))
|
||
|
break;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
cnow = time(NULL);
|
||
|
/* If we got a cache time, see if it's young enough to use directly */
|
||
|
if (age && ctim > 0)
|
||
|
{
|
||
|
ctim = cnow - ctim;
|
||
|
ctim /= 3600 * 24; /* seconds to days */
|
||
|
if (ctim < age) /* ok, it's new enough */
|
||
|
goto out;
|
||
|
}
|
||
|
|
||
|
in.first = 1;
|
||
|
HMAC_setup(in.ctx, "Genuine Adobe Flash Player 001", 30);
|
||
|
inflateInit(&zs);
|
||
|
in.zs = &zs;
|
||
|
|
||
|
http.date = date;
|
||
|
http.data = ∈
|
||
|
|
||
|
httpres = HTTP_get(&http, url, swfcrunch);
|
||
|
|
||
|
inflateEnd(&zs);
|
||
|
|
||
|
if (httpres != HTTPRES_OK && httpres != HTTPRES_OK_NOT_MODIFIED)
|
||
|
{
|
||
|
ret = -1;
|
||
|
if (httpres == HTTPRES_LOST_CONNECTION)
|
||
|
RTMP_Log(RTMP_LOGERROR, "%s: connection lost while downloading swfurl %s",
|
||
|
__FUNCTION__, url);
|
||
|
else if (httpres == HTTPRES_NOT_FOUND)
|
||
|
RTMP_Log(RTMP_LOGERROR, "%s: swfurl %s not found", __FUNCTION__, url);
|
||
|
else
|
||
|
RTMP_Log(RTMP_LOGERROR, "%s: couldn't contact swfurl %s (HTTP error %d)",
|
||
|
__FUNCTION__, url, http.status);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (got && pos)
|
||
|
fseek(f, pos, SEEK_SET);
|
||
|
else
|
||
|
{
|
||
|
char *q;
|
||
|
if (!f)
|
||
|
f = fopen(path, "w");
|
||
|
if (!f)
|
||
|
{
|
||
|
int err = errno;
|
||
|
RTMP_Log(RTMP_LOGERROR,
|
||
|
"%s: couldn't open %s for writing, errno %d (%s)",
|
||
|
__FUNCTION__, path, err, strerror(err));
|
||
|
ret = -1;
|
||
|
goto out;
|
||
|
}
|
||
|
fseek(f, 0, SEEK_END);
|
||
|
q = strchr(url, '?');
|
||
|
if (q)
|
||
|
i = q - url;
|
||
|
else
|
||
|
i = strlen(url);
|
||
|
|
||
|
fprintf(f, "url: %.*s\n", i, url);
|
||
|
}
|
||
|
strtime(&cnow, cctim);
|
||
|
fprintf(f, "ctim: %s\n", cctim);
|
||
|
|
||
|
if (!in.first)
|
||
|
{
|
||
|
HMAC_finish(in.ctx, hash, hlen);
|
||
|
*size = in.size;
|
||
|
|
||
|
fprintf(f, "date: %s\n", date);
|
||
|
fprintf(f, "size: %08x\n", in.size);
|
||
|
fprintf(f, "hash: ");
|
||
|
for (i = 0; i < SHA256_DIGEST_LENGTH; i++)
|
||
|
fprintf(f, "%02x", hash[i]);
|
||
|
fprintf(f, "\n");
|
||
|
}
|
||
|
}
|
||
|
HMAC_close(in.ctx);
|
||
|
out:
|
||
|
free(path);
|
||
|
if (f)
|
||
|
fclose(f);
|
||
|
return ret;
|
||
|
}
|
||
|
#else
|
||
|
int
|
||
|
RTMP_HashSWF(const char *url, unsigned int *size, unsigned char *hash,
|
||
|
int age)
|
||
|
{
|
||
|
return -1;
|
||
|
}
|
||
|
#endif
|