hashswf.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666
  1. /*
  2. * Copyright (C) 2009-2010 Howard Chu
  3. *
  4. * This file is part of librtmp.
  5. *
  6. * librtmp is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU Lesser General Public License as
  8. * published by the Free Software Foundation; either version 2.1,
  9. * or (at your option) any later version.
  10. *
  11. * librtmp is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public License
  17. * along with librtmp see the file COPYING. If not, write to
  18. * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  19. * Boston, MA 02110-1301, USA.
  20. * http://www.gnu.org/copyleft/lgpl.html
  21. */
  22. #include "rtmp_sys.h"
  23. #include "log.h"
  24. #include "http.h"
  25. #ifdef CRYPTO
  26. #ifdef __APPLE__
  27. #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
  28. #endif
  29. #ifdef USE_POLARSSL
  30. #include <polarssl/sha2.h>
  31. #ifndef SHA256_DIGEST_LENGTH
  32. #define SHA256_DIGEST_LENGTH 32
  33. #endif
  34. #define HMAC_CTX sha2_context
  35. #define HMAC_setup(ctx, key, len) sha2_hmac_starts(&ctx, (unsigned char *)key, len, 0)
  36. #define HMAC_crunch(ctx, buf, len) sha2_hmac_update(&ctx, buf, len)
  37. #define HMAC_finish(ctx, dig, dlen) dlen = SHA256_DIGEST_LENGTH; sha2_hmac_finish(&ctx, dig)
  38. #define HMAC_close(ctx)
  39. #elif defined(USE_GNUTLS)
  40. #include <nettle/hmac.h>
  41. #ifndef SHA256_DIGEST_LENGTH
  42. #define SHA256_DIGEST_LENGTH 32
  43. #endif
  44. #undef HMAC_CTX
  45. #define HMAC_CTX struct hmac_sha256_ctx
  46. #define HMAC_setup(ctx, key, len) hmac_sha256_set_key(&ctx, len, key)
  47. #define HMAC_crunch(ctx, buf, len) hmac_sha256_update(&ctx, len, buf)
  48. #define HMAC_finish(ctx, dig, dlen) dlen = SHA256_DIGEST_LENGTH; hmac_sha256_digest(&ctx, SHA256_DIGEST_LENGTH, dig)
  49. #define HMAC_close(ctx)
  50. #else /* USE_OPENSSL */
  51. #include <openssl/ssl.h>
  52. #include <openssl/sha.h>
  53. #include <openssl/hmac.h>
  54. #include <openssl/rc4.h>
  55. #define HMAC_setup(ctx, key, len) HMAC_CTX_init(&ctx); HMAC_Init_ex(&ctx, (unsigned char *)key, len, EVP_sha256(), 0)
  56. #define HMAC_crunch(ctx, buf, len) HMAC_Update(&ctx, (unsigned char *)buf, len)
  57. #define HMAC_finish(ctx, dig, dlen) HMAC_Final(&ctx, (unsigned char *)dig, &dlen);
  58. #define HMAC_close(ctx) HMAC_CTX_cleanup(&ctx)
  59. #endif
  60. extern void RTMP_TLS_Init();
  61. extern TLS_CTX RTMP_TLS_ctx;
  62. #include <zlib.h>
  63. #endif /* CRYPTO */
  64. #define AGENT "Mozilla/5.0"
  65. HTTPResult
  66. HTTP_get(struct HTTP_ctx *http, const char *url, HTTP_read_callback *cb)
  67. {
  68. char *host, *path;
  69. char *p1, *p2;
  70. char hbuf[256];
  71. int port = 80;
  72. #ifdef CRYPTO
  73. int ssl = 0;
  74. #endif
  75. int hlen, flen = 0;
  76. int rc, i;
  77. int len_known;
  78. HTTPResult ret = HTTPRES_OK;
  79. struct sockaddr_in sa;
  80. RTMPSockBuf sb = {0};
  81. http->status = -1;
  82. memset(&sa, 0, sizeof(struct sockaddr_in));
  83. sa.sin_family = AF_INET;
  84. /* we only handle http here */
  85. if (strncasecmp(url, "http", 4))
  86. return HTTPRES_BAD_REQUEST;
  87. if (url[4] == 's')
  88. {
  89. #ifdef CRYPTO
  90. ssl = 1;
  91. port = 443;
  92. if (!RTMP_TLS_ctx)
  93. RTMP_TLS_Init();
  94. #else
  95. return HTTPRES_BAD_REQUEST;
  96. #endif
  97. }
  98. p1 = strchr(url + 4, ':');
  99. if (!p1 || strncmp(p1, "://", 3))
  100. return HTTPRES_BAD_REQUEST;
  101. host = p1 + 3;
  102. path = strchr(host, '/');
  103. hlen = path - host;
  104. strncpy(hbuf, host, hlen);
  105. hbuf[hlen] = '\0';
  106. host = hbuf;
  107. p1 = strrchr(host, ':');
  108. if (p1)
  109. {
  110. *p1++ = '\0';
  111. port = atoi(p1);
  112. }
  113. sa.sin_addr.s_addr = inet_addr(host);
  114. if (sa.sin_addr.s_addr == INADDR_NONE)
  115. {
  116. struct hostent *hp = gethostbyname(host);
  117. if (!hp || !hp->h_addr)
  118. return HTTPRES_LOST_CONNECTION;
  119. sa.sin_addr = *(struct in_addr *)hp->h_addr;
  120. }
  121. sa.sin_port = htons(port);
  122. sb.sb_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  123. if (sb.sb_socket == INVALID_SOCKET)
  124. return HTTPRES_LOST_CONNECTION;
  125. i =
  126. sprintf(sb.sb_buf,
  127. "GET %s HTTP/1.0\r\nUser-Agent: %s\r\nHost: %s\r\nReferer: %.*s\r\n",
  128. path, AGENT, host, (int)(path - url + 1), url);
  129. if (http->date[0])
  130. i += sprintf(sb.sb_buf + i, "If-Modified-Since: %s\r\n", http->date);
  131. i += sprintf(sb.sb_buf + i, "\r\n");
  132. if (connect
  133. (sb.sb_socket, (struct sockaddr *)&sa, sizeof(struct sockaddr)) < 0)
  134. {
  135. ret = HTTPRES_LOST_CONNECTION;
  136. goto leave;
  137. }
  138. #ifdef CRYPTO
  139. if (ssl)
  140. {
  141. #ifdef NO_SSL
  142. RTMP_Log(RTMP_LOGERROR, "%s, No SSL/TLS support", __FUNCTION__);
  143. ret = HTTPRES_BAD_REQUEST;
  144. goto leave;
  145. #else
  146. TLS_client(RTMP_TLS_ctx, sb.sb_ssl);
  147. TLS_setfd(sb.sb_ssl, sb.sb_socket);
  148. if (TLS_connect(sb.sb_ssl) < 0)
  149. {
  150. RTMP_Log(RTMP_LOGERROR, "%s, TLS_Connect failed", __FUNCTION__);
  151. ret = HTTPRES_LOST_CONNECTION;
  152. goto leave;
  153. }
  154. #endif
  155. }
  156. #endif
  157. RTMPSockBuf_Send(&sb, sb.sb_buf, i);
  158. /* set timeout */
  159. #define HTTP_TIMEOUT 5
  160. {
  161. SET_RCVTIMEO(tv, HTTP_TIMEOUT);
  162. if (setsockopt
  163. (sb.sb_socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv)))
  164. {
  165. RTMP_Log(RTMP_LOGERROR, "%s, Setting socket timeout to %ds failed!",
  166. __FUNCTION__, HTTP_TIMEOUT);
  167. }
  168. }
  169. sb.sb_size = 0;
  170. sb.sb_timedout = FALSE;
  171. if (RTMPSockBuf_Fill(&sb) < 1)
  172. {
  173. ret = HTTPRES_LOST_CONNECTION;
  174. goto leave;
  175. }
  176. if (strncmp(sb.sb_buf, "HTTP/1", 6))
  177. {
  178. ret = HTTPRES_BAD_REQUEST;
  179. goto leave;
  180. }
  181. p1 = strchr(sb.sb_buf, ' ');
  182. rc = atoi(p1 + 1);
  183. http->status = rc;
  184. if (rc >= 300)
  185. {
  186. if (rc == 304)
  187. {
  188. ret = HTTPRES_OK_NOT_MODIFIED;
  189. goto leave;
  190. }
  191. else if (rc == 404)
  192. ret = HTTPRES_NOT_FOUND;
  193. else if (rc >= 500)
  194. ret = HTTPRES_SERVER_ERROR;
  195. else if (rc >= 400)
  196. ret = HTTPRES_BAD_REQUEST;
  197. else
  198. ret = HTTPRES_REDIRECTED;
  199. }
  200. p1 = memchr(sb.sb_buf, '\n', sb.sb_size);
  201. if (!p1)
  202. {
  203. ret = HTTPRES_BAD_REQUEST;
  204. goto leave;
  205. }
  206. sb.sb_start = p1 + 1;
  207. sb.sb_size -= sb.sb_start - sb.sb_buf;
  208. while ((p2 = memchr(sb.sb_start, '\r', sb.sb_size)))
  209. {
  210. if (*sb.sb_start == '\r')
  211. {
  212. sb.sb_start += 2;
  213. sb.sb_size -= 2;
  214. break;
  215. }
  216. else if (!strncasecmp
  217. (sb.sb_start, "Content-Length: ", sizeof("Content-Length: ") - 1))
  218. {
  219. flen = atoi(sb.sb_start + sizeof("Content-Length: ") - 1);
  220. }
  221. else if (!strncasecmp
  222. (sb.sb_start, "Last-Modified: ", sizeof("Last-Modified: ") - 1))
  223. {
  224. *p2 = '\0';
  225. strcpy(http->date, sb.sb_start + sizeof("Last-Modified: ") - 1);
  226. }
  227. p2 += 2;
  228. sb.sb_size -= p2 - sb.sb_start;
  229. sb.sb_start = p2;
  230. if (sb.sb_size < 1)
  231. {
  232. if (RTMPSockBuf_Fill(&sb) < 1)
  233. {
  234. ret = HTTPRES_LOST_CONNECTION;
  235. goto leave;
  236. }
  237. }
  238. }
  239. len_known = flen > 0;
  240. while ((!len_known || flen > 0) &&
  241. (sb.sb_size > 0 || RTMPSockBuf_Fill(&sb) > 0))
  242. {
  243. cb(sb.sb_start, 1, sb.sb_size, http->data);
  244. if (len_known)
  245. flen -= sb.sb_size;
  246. http->size += sb.sb_size;
  247. sb.sb_size = 0;
  248. }
  249. if (flen > 0)
  250. ret = HTTPRES_LOST_CONNECTION;
  251. leave:
  252. RTMPSockBuf_Close(&sb);
  253. return ret;
  254. }
  255. #ifdef CRYPTO
  256. #define CHUNK 16384
  257. struct info
  258. {
  259. z_stream *zs;
  260. HMAC_CTX ctx;
  261. int first;
  262. int zlib;
  263. int size;
  264. };
  265. static size_t
  266. swfcrunch(void *ptr, size_t size, size_t nmemb, void *stream)
  267. {
  268. struct info *i = stream;
  269. char *p = ptr;
  270. size_t len = size * nmemb;
  271. if (i->first)
  272. {
  273. i->first = 0;
  274. /* compressed? */
  275. if (!strncmp(p, "CWS", 3))
  276. {
  277. *p = 'F';
  278. i->zlib = 1;
  279. }
  280. HMAC_crunch(i->ctx, (unsigned char *)p, 8);
  281. p += 8;
  282. len -= 8;
  283. i->size = 8;
  284. }
  285. if (i->zlib)
  286. {
  287. unsigned char out[CHUNK];
  288. i->zs->next_in = (unsigned char *)p;
  289. i->zs->avail_in = len;
  290. do
  291. {
  292. i->zs->avail_out = CHUNK;
  293. i->zs->next_out = out;
  294. inflate(i->zs, Z_NO_FLUSH);
  295. len = CHUNK - i->zs->avail_out;
  296. i->size += len;
  297. HMAC_crunch(i->ctx, out, len);
  298. }
  299. while (i->zs->avail_out == 0);
  300. }
  301. else
  302. {
  303. i->size += len;
  304. HMAC_crunch(i->ctx, (unsigned char *)p, len);
  305. }
  306. return size * nmemb;
  307. }
  308. static int tzoff;
  309. static int tzchecked;
  310. #define JAN02_1980 318340800
  311. static const char *monthtab[12] = { "Jan", "Feb", "Mar",
  312. "Apr", "May", "Jun",
  313. "Jul", "Aug", "Sep",
  314. "Oct", "Nov", "Dec"
  315. };
  316. static const char *days[] =
  317. { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
  318. /* Parse an HTTP datestamp into Unix time */
  319. static time_t
  320. make_unix_time(char *s)
  321. {
  322. struct tm time;
  323. int i, ysub = 1900, fmt = 0;
  324. char *month;
  325. char *n;
  326. time_t res;
  327. if (s[3] != ' ')
  328. {
  329. fmt = 1;
  330. if (s[3] != ',')
  331. ysub = 0;
  332. }
  333. for (n = s; *n; ++n)
  334. if (*n == '-' || *n == ':')
  335. *n = ' ';
  336. time.tm_mon = 0;
  337. n = strchr(s, ' ');
  338. if (fmt)
  339. {
  340. /* Day, DD-MMM-YYYY HH:MM:SS GMT */
  341. time.tm_mday = strtol(n + 1, &n, 0);
  342. month = n + 1;
  343. n = strchr(month, ' ');
  344. time.tm_year = strtol(n + 1, &n, 0);
  345. time.tm_hour = strtol(n + 1, &n, 0);
  346. time.tm_min = strtol(n + 1, &n, 0);
  347. time.tm_sec = strtol(n + 1, NULL, 0);
  348. }
  349. else
  350. {
  351. /* Unix ctime() format. Does not conform to HTTP spec. */
  352. /* Day MMM DD HH:MM:SS YYYY */
  353. month = n + 1;
  354. n = strchr(month, ' ');
  355. while (isspace(*n))
  356. n++;
  357. time.tm_mday = strtol(n, &n, 0);
  358. time.tm_hour = strtol(n + 1, &n, 0);
  359. time.tm_min = strtol(n + 1, &n, 0);
  360. time.tm_sec = strtol(n + 1, &n, 0);
  361. time.tm_year = strtol(n + 1, NULL, 0);
  362. }
  363. if (time.tm_year > 100)
  364. time.tm_year -= ysub;
  365. for (i = 0; i < 12; i++)
  366. if (!strncasecmp(month, monthtab[i], 3))
  367. {
  368. time.tm_mon = i;
  369. break;
  370. }
  371. time.tm_isdst = 0; /* daylight saving is never in effect in GMT */
  372. /* this is normally the value of extern int timezone, but some
  373. * braindead C libraries don't provide it.
  374. */
  375. if (!tzchecked)
  376. {
  377. struct tm *tc;
  378. time_t then = JAN02_1980;
  379. tc = localtime(&then);
  380. tzoff = (12 - tc->tm_hour) * 3600 + tc->tm_min * 60 + tc->tm_sec;
  381. tzchecked = 1;
  382. }
  383. res = mktime(&time);
  384. /* Unfortunately, mktime() assumes the input is in local time,
  385. * not GMT, so we have to correct it here.
  386. */
  387. if (res != -1)
  388. res += tzoff;
  389. return res;
  390. }
  391. /* Convert a Unix time to a network time string
  392. * Weekday, DD-MMM-YYYY HH:MM:SS GMT
  393. */
  394. static void
  395. strtime(time_t * t, char *s)
  396. {
  397. struct tm *tm;
  398. tm = gmtime((time_t *) t);
  399. sprintf(s, "%s, %02d %s %d %02d:%02d:%02d GMT",
  400. days[tm->tm_wday], tm->tm_mday, monthtab[tm->tm_mon],
  401. tm->tm_year + 1900, tm->tm_hour, tm->tm_min, tm->tm_sec);
  402. }
  403. #define HEX2BIN(a) (((a)&0x40)?((a)&0xf)+9:((a)&0xf))
  404. int
  405. RTMP_HashSWF(const char *url, unsigned int *size, unsigned char *hash,
  406. int age)
  407. {
  408. FILE *f = NULL;
  409. char *path, date[64], cctim[64];
  410. long pos = 0;
  411. time_t ctim = -1, cnow;
  412. int i, got = 0, ret = 0;
  413. unsigned int hlen;
  414. struct info in = { 0 };
  415. struct HTTP_ctx http = { 0 };
  416. HTTPResult httpres;
  417. z_stream zs = { 0 };
  418. AVal home, hpre;
  419. date[0] = '\0';
  420. #ifdef _WIN32
  421. #ifdef XBMC4XBOX
  422. hpre.av_val = "Q:";
  423. hpre.av_len = 2;
  424. home.av_val = "\\UserData";
  425. #else
  426. hpre.av_val = getenv("HOMEDRIVE");
  427. hpre.av_len = strlen(hpre.av_val);
  428. home.av_val = getenv("HOMEPATH");
  429. #endif
  430. #define DIRSEP "\\"
  431. #else /* !_WIN32 */
  432. hpre.av_val = "";
  433. hpre.av_len = 0;
  434. home.av_val = getenv("HOME");
  435. #define DIRSEP "/"
  436. #endif
  437. if (!home.av_val)
  438. home.av_val = ".";
  439. home.av_len = strlen(home.av_val);
  440. /* SWF hash info is cached in a fixed-format file.
  441. * url: <url of SWF file>
  442. * ctim: HTTP datestamp of when we last checked it.
  443. * date: HTTP datestamp of the SWF's last modification.
  444. * size: SWF size in hex
  445. * hash: SWF hash in hex
  446. *
  447. * These fields must be present in this order. All fields
  448. * besides URL are fixed size.
  449. */
  450. path = malloc(hpre.av_len + home.av_len + sizeof(DIRSEP ".swfinfo"));
  451. sprintf(path, "%s%s" DIRSEP ".swfinfo", hpre.av_val, home.av_val);
  452. f = fopen(path, "r+");
  453. while (f)
  454. {
  455. char buf[4096], *file, *p;
  456. file = strchr(url, '/');
  457. if (!file)
  458. break;
  459. file += 2;
  460. file = strchr(file, '/');
  461. if (!file)
  462. break;
  463. file++;
  464. hlen = file - url;
  465. p = strrchr(file, '/');
  466. if (p)
  467. file = p;
  468. else
  469. file--;
  470. while (fgets(buf, sizeof(buf), f))
  471. {
  472. char *r1;
  473. got = 0;
  474. if (strncmp(buf, "url: ", 5))
  475. continue;
  476. if (strncmp(buf + 5, url, hlen))
  477. continue;
  478. r1 = strrchr(buf, '/');
  479. i = strlen(r1);
  480. r1[--i] = '\0';
  481. if (strncmp(r1, file, i))
  482. continue;
  483. pos = ftell(f);
  484. while (got < 4 && fgets(buf, sizeof(buf), f))
  485. {
  486. if (!strncmp(buf, "size: ", 6))
  487. {
  488. *size = strtol(buf + 6, NULL, 16);
  489. got++;
  490. }
  491. else if (!strncmp(buf, "hash: ", 6))
  492. {
  493. unsigned char *ptr = hash, *in = (unsigned char *)buf + 6;
  494. int l = strlen((char *)in) - 1;
  495. for (i = 0; i < l; i += 2)
  496. *ptr++ = (HEX2BIN(in[i]) << 4) | HEX2BIN(in[i + 1]);
  497. got++;
  498. }
  499. else if (!strncmp(buf, "date: ", 6))
  500. {
  501. buf[strlen(buf) - 1] = '\0';
  502. strncpy(date, buf + 6, sizeof(date));
  503. got++;
  504. }
  505. else if (!strncmp(buf, "ctim: ", 6))
  506. {
  507. buf[strlen(buf) - 1] = '\0';
  508. ctim = make_unix_time(buf + 6);
  509. got++;
  510. }
  511. else if (!strncmp(buf, "url: ", 5))
  512. break;
  513. }
  514. break;
  515. }
  516. break;
  517. }
  518. cnow = time(NULL);
  519. /* If we got a cache time, see if it's young enough to use directly */
  520. if (age && ctim > 0)
  521. {
  522. ctim = cnow - ctim;
  523. ctim /= 3600 * 24; /* seconds to days */
  524. if (ctim < age) /* ok, it's new enough */
  525. goto out;
  526. }
  527. in.first = 1;
  528. HMAC_setup(in.ctx, "Genuine Adobe Flash Player 001", 30);
  529. inflateInit(&zs);
  530. in.zs = &zs;
  531. http.date = date;
  532. http.data = &in;
  533. httpres = HTTP_get(&http, url, swfcrunch);
  534. inflateEnd(&zs);
  535. if (httpres != HTTPRES_OK && httpres != HTTPRES_OK_NOT_MODIFIED)
  536. {
  537. ret = -1;
  538. if (httpres == HTTPRES_LOST_CONNECTION)
  539. RTMP_Log(RTMP_LOGERROR, "%s: connection lost while downloading swfurl %s",
  540. __FUNCTION__, url);
  541. else if (httpres == HTTPRES_NOT_FOUND)
  542. RTMP_Log(RTMP_LOGERROR, "%s: swfurl %s not found", __FUNCTION__, url);
  543. else
  544. RTMP_Log(RTMP_LOGERROR, "%s: couldn't contact swfurl %s (HTTP error %d)",
  545. __FUNCTION__, url, http.status);
  546. }
  547. else
  548. {
  549. if (got && pos)
  550. fseek(f, pos, SEEK_SET);
  551. else
  552. {
  553. char *q;
  554. if (!f)
  555. f = fopen(path, "w");
  556. if (!f)
  557. {
  558. int err = errno;
  559. RTMP_Log(RTMP_LOGERROR,
  560. "%s: couldn't open %s for writing, errno %d (%s)",
  561. __FUNCTION__, path, err, strerror(err));
  562. ret = -1;
  563. goto out;
  564. }
  565. fseek(f, 0, SEEK_END);
  566. q = strchr(url, '?');
  567. if (q)
  568. i = q - url;
  569. else
  570. i = strlen(url);
  571. fprintf(f, "url: %.*s\n", i, url);
  572. }
  573. strtime(&cnow, cctim);
  574. fprintf(f, "ctim: %s\n", cctim);
  575. if (!in.first)
  576. {
  577. HMAC_finish(in.ctx, hash, hlen);
  578. *size = in.size;
  579. fprintf(f, "date: %s\n", date);
  580. fprintf(f, "size: %08x\n", in.size);
  581. fprintf(f, "hash: ");
  582. for (i = 0; i < SHA256_DIGEST_LENGTH; i++)
  583. fprintf(f, "%02x", hash[i]);
  584. fprintf(f, "\n");
  585. }
  586. }
  587. HMAC_close(in.ctx);
  588. out:
  589. free(path);
  590. if (f)
  591. fclose(f);
  592. return ret;
  593. }
  594. #else
  595. int
  596. RTMP_HashSWF(const char *url, unsigned int *size, unsigned char *hash,
  597. int age)
  598. {
  599. (void)url;
  600. (void)size;
  601. (void)hash;
  602. (void)age;
  603. return -1;
  604. }
  605. #endif