Verify bittorrent .torrent metainfo files.
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

278 lignes
7.2KB

  1. #ifdef HTTP_TORRENT
  2. #include "metainfo_http.h"
  3. #include <string.h>
  4. #include <errno.h>
  5. #include <stdlib.h>
  6. #include <stdbool.h>
  7. #include <time.h>
  8. #include <sys/ioctl.h>
  9. #include <curl/curl.h>
  10. #include "metainfo.h"
  11. #include "opts.h"
  12. struct http_metainfo;
  13. typedef void (*progress_fn)(struct http_metainfo* mi);
  14. struct http_metainfo {
  15. int64_t c_size, max_size;
  16. char *data;
  17. int err;
  18. struct {
  19. struct winsize wsize;
  20. /* If -1, this is a chunked transfer */
  21. off_t cont_len;
  22. time_t last_upd_ms;
  23. int upd_count;
  24. progress_fn fn;
  25. bool show;
  26. struct timespec dlstart_time;
  27. } progress;
  28. };
  29. static void progress_known(struct http_metainfo* h_meta)
  30. {
  31. /* "[#####] 14%" */
  32. unsigned short cols;;
  33. struct timespec now;
  34. time_t nowms;
  35. int bars_count;
  36. float prcnt;
  37. if (h_meta == NULL) {
  38. /* Clear line at end */
  39. fprintf(stderr, "\033[1G\033[2K");
  40. fflush(stderr);
  41. return;
  42. }
  43. cols = h_meta->progress.wsize.ws_col;
  44. if (cols < 8)
  45. return;
  46. char line[cols + 1];
  47. /* 1 ms resoltion on my machine */
  48. clock_gettime(CLOCK_MONOTONIC_COARSE, &now);
  49. nowms = now.tv_sec * 1000 + now.tv_nsec / 1000000;
  50. if (nowms - h_meta->progress.last_upd_ms < 500)
  51. return;
  52. h_meta->progress.last_upd_ms = nowms;
  53. prcnt = h_meta->c_size / (float)h_meta->progress.cont_len;
  54. bars_count = (cols - 8) * prcnt;
  55. line[0] = '[';
  56. memset(&line[1], '#', bars_count);
  57. sprintf(&line[bars_count + 1], "]% .0f%%", prcnt * 100);
  58. fprintf(stderr, "\033[2K\033[1G%s", line);
  59. fflush(stderr);
  60. }
  61. static void progress_unknown(struct http_metainfo* h_meta)
  62. {
  63. /* "[ / ] Downloading..." */
  64. const char pchar[] = "/-\\|";
  65. struct timespec now;
  66. time_t nowms;
  67. unsigned short cols;
  68. if (h_meta == NULL) {
  69. /* Clear line at end */
  70. fprintf(stderr, "\033[2K\033[1G");
  71. fflush(stderr);
  72. return;
  73. }
  74. cols = h_meta->progress.wsize.ws_col;
  75. if (cols < 20)
  76. return;
  77. /* 1 ms resoltion on my machine */
  78. clock_gettime(CLOCK_MONOTONIC_COARSE, &now);
  79. nowms = now.tv_sec * 1000 + now.tv_nsec / 1000000;
  80. if (nowms - h_meta->progress.last_upd_ms < 500)
  81. return;
  82. h_meta->progress.last_upd_ms = nowms;
  83. fprintf(stderr, "\033[2K\033[1G[ %c ] Downloading...",
  84. pchar[h_meta->progress.upd_count++ % (sizeof(pchar) - 1)]);
  85. fflush(stderr);
  86. }
  87. static size_t metainfo_read_http_headercb(char *buf, size_t size, size_t n,
  88. void *data) {
  89. struct http_metainfo *h_meta = (struct http_metainfo*)data;
  90. const char *cl_header = "content-length";
  91. char *sep = memchr(buf, ':', size * n);
  92. if (!sep || (sep - buf) != strlen(cl_header))
  93. goto end;
  94. if (strncmp(cl_header, buf, strlen(cl_header)) == 0) {
  95. char *endp;
  96. int64_t len;
  97. sep += 2;
  98. errno = 0;
  99. len = strtoll(sep, &endp, 10);
  100. if (sep != endp && errno == 0) {
  101. if (len > MAX_TORRENT_SIZE) {
  102. h_meta->err = EFBIG;
  103. return 0;
  104. }
  105. h_meta->max_size = len;
  106. h_meta->progress.cont_len = len;
  107. if (!opt_silent)
  108. h_meta->progress.fn = progress_known;
  109. }
  110. }
  111. end:
  112. return size * n;
  113. }
  114. static int metainfo_http_progress(struct http_metainfo* h_meta) {
  115. struct timespec now;
  116. if (h_meta->progress.fn) {
  117. if (!h_meta->progress.show) {
  118. time_t diffms;
  119. clock_gettime(CLOCK_MONOTONIC_COARSE, &now);
  120. diffms = (now.tv_sec - h_meta->progress.dlstart_time.tv_sec) * 1000 +
  121. (now.tv_nsec - h_meta->progress.dlstart_time.tv_nsec) / 1000000;
  122. if (diffms >= 1000) /* Show progress after 1 second of downloading */
  123. h_meta->progress.show = true;
  124. }
  125. if (h_meta->progress.show)
  126. h_meta->progress.fn(h_meta);
  127. }
  128. return CURL_PROGRESSFUNC_CONTINUE;
  129. }
  130. static int connect_cb(void* data, char*, char*, int, int) {
  131. struct http_metainfo* h_meta = data;
  132. if (!opt_silent)
  133. clock_gettime(CLOCK_MONOTONIC_COARSE, &h_meta->progress.dlstart_time);
  134. return CURL_PREREQFUNC_OK;
  135. }
  136. static size_t metainfo_read_http_writecb(char *ptr, size_t size, size_t n,
  137. void *data) {
  138. struct http_metainfo *h_meta = (struct http_metainfo*)data;
  139. size_t bytes = size * n;
  140. if (h_meta->max_size > MAX_TORRENT_SIZE) {
  141. h_meta->err = EFBIG;
  142. goto fail; /* Stop processing if too large */
  143. }
  144. if (h_meta->max_size == -1) {
  145. /* If no content-length, make a dinamic array */
  146. h_meta->max_size = 0;
  147. } else if (!h_meta->data) {
  148. /* We have content-size, and we haven't alloced yet */
  149. h_meta->data = malloc(h_meta->max_size);
  150. }
  151. size_t free_space = h_meta->max_size - h_meta->c_size;
  152. if (bytes > free_space) {
  153. while (bytes > free_space) {
  154. if (h_meta->max_size == 0)
  155. h_meta->max_size = 2048;
  156. h_meta->max_size *= 2;
  157. free_space = h_meta->max_size - h_meta->c_size;
  158. }
  159. void *n_data = realloc(h_meta->data, h_meta->max_size);
  160. if (!n_data) {
  161. h_meta->err = ENOMEM;
  162. goto fail;
  163. }
  164. h_meta->data = n_data;
  165. }
  166. if (h_meta->c_size + bytes > MAX_TORRENT_SIZE) {
  167. h_meta->err = EFBIG;
  168. goto fail;
  169. }
  170. memcpy(&h_meta->data[h_meta->c_size], ptr, bytes);
  171. h_meta->c_size += bytes;
  172. metainfo_http_progress(h_meta);
  173. return bytes;
  174. fail:
  175. if (h_meta->data)
  176. free(h_meta->data);
  177. return 0;
  178. }
  179. /*
  180. * Download the file in memory, and return the pointer to it (which needs to be
  181. * freed) in out_contents and the size in out_size. If the file is too big,
  182. * fail. Returns 0 on success and an errno on fail.
  183. */
  184. int metainfo_read_http(const char* url, char** out_contents, int* out_size) {
  185. char errbuf[CURL_ERROR_SIZE];
  186. CURL *curl = curl_easy_init();
  187. CURLcode res;
  188. struct http_metainfo h_meta = {
  189. .c_size = 0,
  190. .max_size = -1,
  191. .data = NULL,
  192. .progress.cont_len = -1,
  193. .progress.fn = opt_silent ? NULL : progress_unknown,
  194. };
  195. if (!curl) {
  196. return ENOMEM;
  197. }
  198. if (!opt_silent) {
  199. ioctl(0, TIOCGWINSZ, &h_meta.progress.wsize);
  200. }
  201. curl_easy_setopt(curl, CURLOPT_HEADERDATA, &h_meta);
  202. curl_easy_setopt(curl, CURLOPT_WRITEDATA, &h_meta);
  203. curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, metainfo_read_http_headercb);
  204. curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, metainfo_read_http_writecb);
  205. curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errbuf);
  206. curl_easy_setopt(curl, CURLOPT_PREREQFUNCTION, connect_cb);
  207. curl_easy_setopt(curl, CURLOPT_PREREQDATA, &h_meta);
  208. curl_easy_setopt(curl, CURLOPT_URL, url);
  209. //curl_easy_setopt(curl, CURLOPT_MAX_RECV_SPEED_LARGE, 1024);
  210. res = curl_easy_perform(curl);
  211. curl_easy_cleanup(curl);
  212. if (h_meta.progress.fn && h_meta.progress.show) {
  213. h_meta.progress.fn(NULL);
  214. }
  215. if (res) {
  216. fprintf(stderr, "libCurl error: %s\n", errbuf);
  217. return h_meta.err;
  218. }
  219. *out_contents = h_meta.data;
  220. *out_size = h_meta.c_size; /* caller will free */
  221. return 0;
  222. }
  223. #endif