/** * Generate torrent file content * @see http://wiki.theory.org/BitTorrentSpecification * * @param ctx the torrent algorithm context */ static void bt_generate_torrent(torrent_ctx *ctx) { uint64_t total_size = 0; size_t info_start_pos; assert(ctx->content.str == NULL); if(ctx->piece_length == 0) { if(ctx->files.size == 1) { total_size = ((bt_file_info*)ctx->files.array[0])->size; } ctx->piece_length = bt_default_piece_length(total_size); } /* write torrent header to the ctx->torrent string buffer */ if((ctx->options & BT_OPT_INFOHASH_ONLY) == 0) { bt_str_append(ctx, "d"); if(ctx->announce) { bt_bencode_str(ctx, "8:announce", ctx->announce); } if(ctx->program_name) { bt_bencode_str(ctx, "10:created by", ctx->program_name); } bt_bencode_int(ctx, "13:creation date", (uint64_t)time(NULL)); } bt_str_append(ctx, "8:encoding5:UTF-8"); bt_str_append(ctx, "4:infod"); /* start info dictionary */ info_start_pos = ctx->content.length - 1; if(ctx->files.size > 1) { size_t i; /* process batch torrent */ bt_str_append(ctx, "5:filesl"); /* start list of files */ /* write length and path for each file in the batch */ for(i = 0; i < ctx->files.size; i++) { bt_file_info_append(ctx, "d6:length", "4:pathl", (bt_file_info*)ctx->files.array[i]); bt_str_append(ctx, "ee"); } /* note: get_batch_name modifies path, so should be called here */ bt_bencode_str(ctx, "e4:name", get_batch_name( ((bt_file_info*)ctx->files.array[0])->path)); } else if(ctx->files.size > 0) { /* write size and basename of the first file */ /* in the non-batch mode other files are ignored */ bt_file_info_append(ctx, "6:length", "4:name", (bt_file_info*)ctx->files.array[0]); } bt_bencode_int(ctx, "12:piece length", ctx->piece_length); bt_str_append(ctx, "6:pieces"); bt_bencode_pieces(ctx); if(ctx->options & BT_OPT_PRIVATE) { bt_str_append(ctx, "7:privatei1e"); } bt_str_append(ctx, "ee"); /* calculate BTIH */ SHA1_INIT(ctx); SHA1_UPDATE(ctx, (unsigned char*)ctx->content.str + info_start_pos, ctx->content.length - info_start_pos - 1); SHA1_FINAL(ctx, ctx->btih); }
/** * Generate torrent file content * @see http://wiki.theory.org/BitTorrentSpecification * * @param ctx the torrent algorithm context */ static void make_torrent(torrent_ctx *ctx) { uint64_t total_size = 0; size_t info_start_pos; assert(ctx->torrent == NULL); assert(ctx->files.size <= 1); ctx->torrent = str_new(); if(ctx->piece_length == 0) { if(ctx->files.size == 1) { total_size = ((file_n_size_info*)ctx->files.array[0])->size; } ctx->piece_length = torrent_default_piece_length(total_size); } /* write torrent header to the ctx->torrent string bufer */ if((ctx->flags & BT_OPT_INFOHASH_ONLY) == 0) { str_append(ctx->torrent, "d"); if(ctx->announce) { str_append(ctx->torrent, "8:announce"); bt_bencode_str(ctx->torrent, ctx->announce); } if(ctx->program_name) { str_append(ctx->torrent, "10:created by"); bt_bencode_str(ctx->torrent, ctx->program_name); } str_append(ctx->torrent, "13:creation date"); bt_bencode_int(ctx->torrent, (uint64_t)time(NULL)); } str_append(ctx->torrent, "8:encoding5:UTF-8"); str_append(ctx->torrent, "4:infod"); /* start info dictionary */ info_start_pos = ctx->torrent->len - 1; if(ctx->files.size == 1) { file_n_size_info* f = (file_n_size_info*)ctx->files.array[0]; str_append(ctx->torrent, "6:length"); bt_bencode_int(ctx->torrent, f->size); /* note: for one file f->path must be a basename */ str_append(ctx->torrent, "4:name"); bt_bencode_str(ctx->torrent, f->path); } str_append(ctx->torrent, "12:piece length"); bt_bencode_int(ctx->torrent, ctx->piece_length); str_append(ctx->torrent, "6:pieces"); bt_bencode_pieces(ctx->torrent, ctx); if(ctx->flags & BT_OPT_PRIVATE) { str_append(ctx->torrent, "7:privatei1e"); } str_append(ctx->torrent, "ee"); SHA1_INIT(ctx); SHA1_UPDATE(ctx, (unsigned char*)ctx->torrent->str + info_start_pos, ctx->torrent->len - info_start_pos - 1); SHA1_FINAL(ctx, ctx->btih); }