A clone of btpd with my configuration changes.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

306 lines
7.6 KiB

  1. #include <sys/types.h>
  2. #include <sys/stat.h>
  3. #include <dirent.h>
  4. #include <string.h>
  5. #include <unistd.h>
  6. #include "btpd.h"
  7. HTBL_TYPE(numtbl, tlib, unsigned, num, nchain);
  8. HTBL_TYPE(hashtbl, tlib, uint8_t, hash, hchain);
  9. static unsigned m_nextnum;
  10. static unsigned m_ntlibs;
  11. static struct numtbl *m_numtbl;
  12. static struct hashtbl *m_hashtbl;
  13. unsigned
  14. tlib_count(void)
  15. {
  16. return m_ntlibs;
  17. }
  18. struct tlib *
  19. tlib_by_num(unsigned num)
  20. {
  21. return numtbl_find(m_numtbl, &num);
  22. }
  23. struct tlib *
  24. tlib_by_hash(const uint8_t *hash)
  25. {
  26. return hashtbl_find(m_hashtbl, hash);
  27. }
  28. void
  29. tlib_kill(struct tlib *tl)
  30. {
  31. numtbl_remove(m_numtbl, &tl->num);
  32. hashtbl_remove(m_hashtbl, tl->hash);
  33. free(tl);
  34. m_ntlibs--;
  35. }
  36. struct tlib *
  37. tlib_create(const uint8_t *hash)
  38. {
  39. struct tlib *tl = btpd_calloc(1, sizeof(*tl));
  40. char hex[SHAHEXSIZE];
  41. bin2hex(hash, hex, 20);
  42. tl->num = m_nextnum;
  43. bcopy(hash, tl->hash, 20);
  44. m_nextnum++;
  45. m_ntlibs++;
  46. numtbl_insert(m_numtbl, tl);
  47. hashtbl_insert(m_hashtbl, tl);
  48. return tl;
  49. }
  50. int
  51. tlib_del(struct tlib *tl)
  52. {
  53. char relpath[RELPATH_SIZE];
  54. char cmd[PATH_MAX];
  55. assert(tl->tp == NULL);
  56. snprintf(cmd, PATH_MAX, "rm -r torrents/%s",
  57. bin2hex(tl->hash, relpath, 20));
  58. system(cmd);
  59. tlib_kill(tl);
  60. return 0;
  61. }
  62. static void
  63. dct_subst_save(FILE *fp, const char *dct1, const char *dct2)
  64. {
  65. fprintf(fp, "d");
  66. const char *k1 = benc_first(dct1), *k2 = benc_first(dct2);
  67. const char *val, *str, *rest;
  68. size_t len;
  69. while (k1 != NULL && k2 != NULL) {
  70. int test = benc_strcmp(k1, k2);
  71. if (test < 0) {
  72. str = benc_mem(k1, &len, &val);
  73. fprintf(fp, "%d:%.*s", (int)len, (int)len, str);
  74. fwrite(val, 1, benc_length(val), fp);
  75. k1 = benc_next(val);
  76. } else {
  77. str = benc_mem(k2, &len, &val);
  78. fprintf(fp, "%d:%.*s", (int)len, (int)len, str);
  79. fwrite(val, 1, benc_length(val), fp);
  80. k2 = benc_next(val);
  81. if (test == 0)
  82. k1 = benc_next(benc_next(k1));
  83. }
  84. }
  85. rest = k1 != NULL ? k1 : k2;
  86. while (rest != NULL) {
  87. str = benc_mem(rest, &len, &val);
  88. fprintf(fp, "%d:%.*s", (int)len, (int)len, str);
  89. fwrite(val, 1, benc_length(val), fp);
  90. rest = benc_next(val);
  91. }
  92. fprintf(fp, "e");
  93. }
  94. static int
  95. valid_info(char *buf, size_t len)
  96. {
  97. size_t slen;
  98. const char *info;
  99. if (benc_validate(buf, len) != 0)
  100. return 0;
  101. if ((info = benc_dget_dct(buf, "info")) == NULL)
  102. return 0;
  103. if (benc_dget_mem(info, "name", &slen) == NULL || slen == 0)
  104. return 0;
  105. if ((benc_dget_mem(info, "dir", &slen) == NULL ||
  106. (slen == 0 || slen >= PATH_MAX)))
  107. return 0;
  108. return 1;
  109. }
  110. static void
  111. load_info(struct tlib *tl, const char *path)
  112. {
  113. size_t size = 1 << 14;
  114. char buf[size], *p = buf;
  115. const char *info;
  116. if ((errno = read_whole_file((void **)&p, &size, path)) != 0) {
  117. btpd_log(BTPD_L_ERROR, "couldn't load '%s' (%s).\n", path,
  118. strerror(errno));
  119. return;
  120. }
  121. if (!valid_info(buf, size)) {
  122. btpd_log(BTPD_L_ERROR, "bad info file '%s'.\n", path);
  123. return;
  124. }
  125. info = benc_dget_dct(buf, "info");
  126. tl->name = benc_dget_str(info, "name", NULL);
  127. tl->dir = benc_dget_str(info, "dir", NULL);
  128. tl->tot_up = benc_dget_int(info, "total upload");
  129. tl->tot_down = benc_dget_int(info, "total download");
  130. tl->content_size = benc_dget_int(info, "content size");
  131. tl->content_have = benc_dget_int(info, "content have");
  132. if (tl->name == NULL || tl->dir == NULL)
  133. btpd_err("Out of memory.\n");
  134. }
  135. static void
  136. save_info(struct tlib *tl)
  137. {
  138. FILE *fp;
  139. char relpath[SHAHEXSIZE], path[PATH_MAX], wpath[PATH_MAX];
  140. char *old = NULL;
  141. size_t size = 1 << 14;
  142. struct io_buffer iob = buf_init(1 << 10);
  143. buf_print(&iob,
  144. "d4:infod"
  145. "12:content havei%llde12:content sizei%llde"
  146. "3:dir%d:%s4:name%d:%s"
  147. "14:total downloadi%llde12:total uploadi%llde"
  148. "ee",
  149. tl->content_have, tl->content_size,
  150. (int)strlen(tl->dir), tl->dir, (int)strlen(tl->name), tl->name,
  151. tl->tot_down, tl->tot_up);
  152. if (iob.error)
  153. btpd_err("Out of memory.\n");
  154. if ((errno = read_whole_file((void **)&old, &size, path)) != 0
  155. && errno != ENOENT)
  156. btpd_log(BTPD_L_ERROR, "couldn't load '%s' (%s).\n", path,
  157. strerror(errno));
  158. bin2hex(tl->hash, relpath, 20);
  159. snprintf(path, PATH_MAX, "torrents/%s/info", relpath);
  160. snprintf(wpath, PATH_MAX, "%s.write", path);
  161. if ((fp = fopen(wpath, "w")) == NULL)
  162. btpd_err("failed to open '%s' (%s).\n", wpath, strerror(errno));
  163. if (old != NULL) {
  164. dct_subst_save(fp, old, iob.buf);
  165. free(old);
  166. } else
  167. dct_subst_save(fp, "de", iob.buf);
  168. buf_free(&iob);
  169. if (ferror(fp) || fclose(fp) != 0)
  170. btpd_err("failed to write '%s'.\n", wpath);
  171. if (rename(wpath, path) != 0)
  172. btpd_err("failed to rename: '%s' -> '%s' (%s).\n", wpath, path,
  173. strerror(errno));
  174. }
  175. void
  176. tlib_update_info(struct tlib *tl)
  177. {
  178. assert(tl->tp != NULL);
  179. tl->tot_down += tl->tp->net->downloaded;
  180. tl->tot_up += tl->tp->net->uploaded;
  181. tl->content_have = cm_content(tl->tp);
  182. tl->content_size = tl->tp->total_length;
  183. save_info(tl);
  184. }
  185. static void
  186. write_torrent(const char *mi, size_t mi_size, const char *path)
  187. {
  188. FILE *fp;
  189. if ((fp = fopen(path, "w")) == NULL)
  190. goto err;
  191. if (fwrite(mi, mi_size, 1, fp) != 1) {
  192. errno = EIO;
  193. goto err;
  194. }
  195. if (fclose(fp) != 0)
  196. goto err;
  197. return;
  198. err:
  199. btpd_err("failed to write metainfo '%s' (%s).\n", path, strerror(errno));
  200. }
  201. struct tlib *
  202. tlib_add(const uint8_t *hash, const char *mi, size_t mi_size,
  203. const char *content, char *name)
  204. {
  205. struct tlib *tl = tlib_create(hash);
  206. char relpath[RELPATH_SIZE], file[PATH_MAX];
  207. bin2hex(hash, relpath, 20);
  208. if (name == NULL)
  209. if ((name = mi_name(mi)) == NULL)
  210. btpd_err("out of memory.\n");
  211. tl->content_size = mi_total_length(mi);
  212. tl->name = name;
  213. tl->dir = strdup(content);
  214. if (tl->name == NULL || tl->dir == NULL)
  215. btpd_err("out of memory.\n");
  216. snprintf(file, PATH_MAX, "torrents/%s", relpath);
  217. if (mkdir(file, 0777) != 0)
  218. btpd_err("failed to create dir '%s' (%s).\n", file, strerror(errno));
  219. snprintf(file, PATH_MAX, "torrents/%s/torrent", relpath);
  220. write_torrent(mi, mi_size, file);
  221. save_info(tl);
  222. return tl;
  223. }
  224. static int
  225. num_test(const void *k1, const void *k2)
  226. {
  227. return *(const unsigned *)k1 == *(const unsigned *)k2;
  228. }
  229. static uint32_t
  230. num_hash(const void *k)
  231. {
  232. return *(const unsigned *)k;
  233. }
  234. static int
  235. id_test(const void *k1, const void *k2)
  236. {
  237. return bcmp(k1, k2, 20) == 0;
  238. }
  239. static uint32_t
  240. id_hash(const void *k)
  241. {
  242. return net_read32(k + 16);
  243. }
  244. void
  245. tlib_put_all(struct tlib **v)
  246. {
  247. hashtbl_tov(m_hashtbl, v);
  248. }
  249. void
  250. tlib_init(void)
  251. {
  252. DIR *dirp;
  253. struct dirent *dp;
  254. uint8_t hash[20];
  255. char file[PATH_MAX];
  256. m_numtbl = numtbl_create(num_test, num_hash);
  257. m_hashtbl = hashtbl_create(id_test, id_hash);
  258. if (m_numtbl == NULL || m_hashtbl == NULL)
  259. btpd_err("Out of memory.\n");
  260. if ((dirp = opendir("torrents")) == NULL)
  261. btpd_err("couldn't open the torrents directory.\n");
  262. while ((dp = readdir(dirp)) != NULL) {
  263. if (dp->d_namlen == 40 && ishex(dp->d_name)) {
  264. struct tlib * tl = tlib_create(hex2bin(dp->d_name, hash, 20));
  265. snprintf(file, PATH_MAX, "torrents/%s/info", dp->d_name);
  266. load_info(tl, file);
  267. }
  268. }
  269. closedir(dirp);
  270. }