/** * Initialize torrent context before calculating hash. * * @param ctx context to initialize */ void bt_init(torrent_ctx* ctx) { memset(ctx, 0, sizeof(torrent_ctx)); ctx->piece_length = BT_MIN_HASH_LENGTH; assert(BT_MIN_HASH_LENGTH == bt_default_piece_length(0)); #ifdef USE_OPENSSL { /* get the methods of the selected SHA1 algorithm */ rhash_hash_info *sha1_info = &rhash_info_table[3]; assert(sha1_info->info->hash_id == RHASH_SHA1); assert(sha1_info->context_size <= (sizeof(sha1_ctx) + sizeof(unsigned long))); ctx->sha_init = sha1_info->init; ctx->sha_update = sha1_info->update; ctx->sha_final = sha1_info->final; } #endif SHA1_INIT(ctx); }
/** * Add a file info into the batch of files of given torrent. * * @param ctx torrent algorithm context * @param path file path * @param filesize file size * @return non-zero on success, zero on fail */ int bt_add_file(torrent_ctx *ctx, const char* path, uint64_t filesize) { size_t len = strlen(path); bt_file_info* info = (bt_file_info*)malloc(sizeof(uint64_t) + len + 1); if(info == NULL) { ctx->error = 1; return 0; } info->size = filesize; memcpy(info->path, path, len + 1); if(!bt_vector_add_ptr(&ctx->files, info)) return 0; /* recalculate piece length (but only if hashing not started yet) */ if(ctx->piece_count == 0 && ctx->index == 0) { /* note: in case of batch of files should use a total batch size */ ctx->piece_length = bt_default_piece_length(filesize); } return 1; }
/** * 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); }
RHASH_API size_t rhash_torrent_get_default_piece_length(uint64_t total_size) { return bt_default_piece_length(total_size); }