Changeset 13781 for trunk/libtransmission/torrent-magnet.c
- Timestamp:
- Jan 7, 2013, 6:16:34 PM (9 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/libtransmission/torrent-magnet.c
r13722 r13781 42 42 enum 43 43 { 44 45 44 /* don't ask for the same metadata piece more than this often */ 45 MIN_REPEAT_INTERVAL_SECS = 3 46 46 }; 47 47 48 48 struct metadata_node 49 49 { 50 51 50 time_t requestedAt; 51 int piece; 52 52 }; 53 53 54 54 struct tr_incomplete_metadata 55 55 { 56 57 58 59 60 61 62 56 uint8_t * metadata; 57 int metadata_size; 58 int pieceCount; 59 60 /** sorted from least to most recently requested */ 61 struct metadata_node * piecesNeeded; 62 int piecesNeededCount; 63 63 }; 64 64 … … 66 66 incompleteMetadataFree (struct tr_incomplete_metadata * m) 67 67 { 68 69 70 68 tr_free (m->metadata); 69 tr_free (m->piecesNeeded); 70 tr_free (m); 71 71 } 72 72 … … 74 74 tr_torrentSetMetadataSizeHint (tr_torrent * tor, int size) 75 75 { 76 if (!tr_torrentHasMetadata (tor)) 77 { 78 if (tor->incompleteMetadata == NULL) 79 { 80 int i; 81 struct tr_incomplete_metadata * m; 82 const int n = (size + (METADATA_PIECE_SIZE - 1)) / METADATA_PIECE_SIZE; 83 dbgmsg (tor, "metadata is %d bytes in %d pieces", size, n); 84 85 m = tr_new (struct tr_incomplete_metadata, 1); 86 m->pieceCount = n; 87 m->metadata = tr_new (uint8_t, size); 88 m->metadata_size = size; 89 m->piecesNeededCount = n; 90 m->piecesNeeded = tr_new (struct metadata_node, n); 91 92 for (i=0; i<n; ++i) { 93 m->piecesNeeded[i].piece = i; 94 m->piecesNeeded[i].requestedAt = 0; 76 if (!tr_torrentHasMetadata (tor)) 77 { 78 if (tor->incompleteMetadata == NULL) 79 { 80 int i; 81 struct tr_incomplete_metadata * m; 82 const int n = (size + (METADATA_PIECE_SIZE - 1)) / METADATA_PIECE_SIZE; 83 dbgmsg (tor, "metadata is %d bytes in %d pieces", size, n); 84 85 m = tr_new (struct tr_incomplete_metadata, 1); 86 m->pieceCount = n; 87 m->metadata = tr_new (uint8_t, size); 88 m->metadata_size = size; 89 m->piecesNeededCount = n; 90 m->piecesNeeded = tr_new (struct metadata_node, n); 91 92 for (i=0; i<n; ++i) 93 { 94 m->piecesNeeded[i].piece = i; 95 m->piecesNeeded[i].requestedAt = 0; 95 96 } 96 97 97 98 tor->incompleteMetadata = m; 98 99 } 99 100 } … … 103 104 findInfoDictOffset (const tr_torrent * tor) 104 105 { 105 106 107 108 109 110 111 { 112 113 114 115 { 116 117 118 106 size_t fileLen; 107 uint8_t * fileContents; 108 int offset = 0; 109 110 /* load the file, and find the info dict's offset inside the file */ 111 if ((fileContents = tr_loadFile (tor->info.torrent, &fileLen))) 112 { 113 tr_variant top; 114 115 if (!tr_variantFromBenc (&top, fileContents, fileLen)) 116 { 117 tr_variant * infoDict; 118 119 if (tr_variantDictFindDict (&top, TR_KEY_info, &infoDict)) 119 120 { 120 121 122 123 124 121 int infoLen; 122 char * infoContents = tr_variantToStr (infoDict, TR_VARIANT_FMT_BENC, &infoLen); 123 const uint8_t * i = (const uint8_t*) tr_memmem ((char*)fileContents, fileLen, infoContents, infoLen); 124 offset = i != NULL ? i - fileContents : 0; 125 tr_free (infoContents); 125 126 } 126 127 127 128 } 129 130 131 } 132 133 128 tr_variantFree (&top); 129 } 130 131 tr_free (fileContents); 132 } 133 134 return offset; 134 135 } 135 136 … … 137 138 ensureInfoDictOffsetIsCached (tr_torrent * tor) 138 139 { 139 140 141 142 { 143 144 140 assert (tr_torrentHasMetadata (tor)); 141 142 if (!tor->infoDictOffsetIsCached) 143 { 144 tor->infoDictOffset = findInfoDictOffset (tor); 145 tor->infoDictOffsetIsCached = true; 145 146 } 146 147 } … … 149 150 tr_torrentGetMetadataPiece (tr_torrent * tor, int piece, int * len) 150 151 { 151 152 153 154 155 156 157 158 { 159 160 161 162 163 164 165 166 167 168 { 169 170 171 152 char * ret = NULL; 153 154 assert (tr_isTorrent (tor)); 155 assert (piece >= 0); 156 assert (len != NULL); 157 158 if (tr_torrentHasMetadata (tor)) 159 { 160 FILE * fp; 161 162 ensureInfoDictOffsetIsCached (tor); 163 164 assert (tor->infoDictLength > 0); 165 assert (tor->infoDictOffset >= 0); 166 167 fp = fopen (tor->info.torrent, "rb"); 168 if (fp != NULL) 169 { 170 const int o = piece * METADATA_PIECE_SIZE; 171 172 if (!fseek (fp, tor->infoDictOffset + o, SEEK_SET)) 172 173 { 173 174 175 176 177 174 const int l = o + METADATA_PIECE_SIZE <= tor->infoDictLength 175 ? METADATA_PIECE_SIZE 176 : tor->infoDictLength - o; 177 178 if (0<l && l<=METADATA_PIECE_SIZE) 178 179 { 179 180 181 180 char * buf = tr_new (char, l); 181 const int n = fread (buf, 1, l, fp); 182 if (n == l) 182 183 { 183 184 185 184 *len = l; 185 ret = buf; 186 buf = NULL; 186 187 } 187 188 188 189 tr_free (buf); 189 190 } 190 191 } 191 192 192 193 fclose (fp); 193 194 } 194 195 } … … 200 201 tr_torrentSetMetadataPiece (tr_torrent * tor, int piece, const void * data, int len) 201 202 { 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 { 237 238 239 240 241 242 243 244 245 246 { 247 248 249 250 251 203 int i; 204 struct tr_incomplete_metadata * m; 205 const int offset = piece * METADATA_PIECE_SIZE; 206 207 assert (tr_isTorrent (tor)); 208 209 dbgmsg (tor, "got metadata piece %d", piece); 210 211 /* are we set up to download metadata? */ 212 m = tor->incompleteMetadata; 213 if (m == NULL) 214 return; 215 216 /* does this data pass the smell test? */ 217 if (offset + len > m->metadata_size) 218 return; 219 220 /* do we need this piece? */ 221 for (i=0; i<m->piecesNeededCount; ++i) 222 if (m->piecesNeeded[i].piece == piece) 223 break; 224 if (i==m->piecesNeededCount) 225 return; 226 227 memcpy (m->metadata + offset, data, len); 228 229 tr_removeElementFromArray (m->piecesNeeded, i, 230 sizeof (struct metadata_node), 231 m->piecesNeededCount--); 232 233 dbgmsg (tor, "saving metainfo piece %d... %d remain", piece, m->piecesNeededCount); 234 235 /* are we done? */ 236 if (m->piecesNeededCount == 0) 237 { 238 bool success = false; 239 bool checksumPassed = false; 240 bool metainfoParsed = false; 241 uint8_t sha1[SHA_DIGEST_LENGTH]; 242 243 /* we've got a complete set of metainfo... see if it passes the checksum test */ 244 dbgmsg (tor, "metainfo piece %d was the last one", piece); 245 tr_sha1 (sha1, m->metadata, m->metadata_size, NULL); 246 if ((checksumPassed = !memcmp (sha1, tor->info.hash, SHA_DIGEST_LENGTH))) 247 { 248 /* checksum passed; now try to parse it as benc */ 249 tr_variant infoDict; 250 const int err = tr_variantFromBenc (&infoDict, m->metadata, m->metadata_size); 251 dbgmsg (tor, "err is %d", err); 252 if ((metainfoParsed = !err)) 252 253 { 253 254 255 256 257 254 /* yay we have bencoded metainfo... merge it into our .torrent file */ 255 tr_variant newMetainfo; 256 char * path = tr_strdup (tor->info.torrent); 257 258 if (!tr_variantFromFile (&newMetainfo, TR_VARIANT_FMT_BENC, path)) 258 259 { 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 260 bool hasInfo; 261 tr_info info; 262 int infoDictLength; 263 264 /* remove any old .torrent and .resume files */ 265 remove (path); 266 tr_torrentRemoveResume (tor); 267 268 dbgmsg (tor, "Saving completed metadata to \"%s\"", path); 269 tr_variantMergeDicts (tr_variantDictAddDict (&newMetainfo, TR_KEY_info, 0), &infoDict); 270 271 memset (&info, 0, sizeof (tr_info)); 272 success = tr_metainfoParse (tor->session, &newMetainfo, &info, &hasInfo, &infoDictLength); 273 274 if (success && !tr_getBlockSize (info.pieceSize)) 274 275 { 275 276 276 tr_torrentSetLocalError (tor, "%s", _("Magnet torrent's metadata is not usable")); 277 success = false; 277 278 } 278 279 279 280 if (success) 280 281 { 281 282 283 284 285 286 287 288 289 282 /* keep the new info */ 283 tor->info = info; 284 tor->infoDictLength = infoDictLength; 285 286 /* save the new .torrent file */ 287 tr_variantToFile (&newMetainfo, TR_VARIANT_FMT_BENC, tor->info.torrent); 288 tr_sessionSetTorrentFile (tor->session, tor->info.hashString, tor->info.torrent); 289 tr_torrentGotNewInfoDict (tor); 290 tr_torrentSetDirty (tor); 290 291 } 291 292 292 293 tr_variantFree (&newMetainfo); 293 294 } 294 295 295 296 296 tr_variantFree (&infoDict); 297 tr_free (path); 297 298 } 298 299 } 299 300 300 301 { 302 303 301 if (success) 302 { 303 incompleteMetadataFree (tor->incompleteMetadata); 304 tor->incompleteMetadata = NULL; 304 305 } 305 306 else /* drat. */ 306 307 { 307 308 308 const int n = m->pieceCount; 309 for (i=0; i<n; ++i) 309 310 { 310 311 311 m->piecesNeeded[i].piece = i; 312 m->piecesNeeded[i].requestedAt = 0; 312 313 } 313 314 315 316 314 m->piecesNeededCount = n; 315 dbgmsg (tor, "metadata error; trying again. %d pieces left", n); 316 317 tr_err ("magnet status: checksum passed %d, metainfo parsed %d", 317 318 (int)checksumPassed, (int)metainfoParsed); 318 319 } … … 323 324 tr_torrentGetNextMetadataRequest (tr_torrent * tor, time_t now, int * setme_piece) 324 325 { 325 bool have_request = false; 326 struct tr_incomplete_metadata * m; 327 328 assert (tr_isTorrent (tor)); 329 330 m = tor->incompleteMetadata; 331 332 if ((m != NULL) 333 && (m->piecesNeededCount > 0) 334 && (m->piecesNeeded[0].requestedAt + MIN_REPEAT_INTERVAL_SECS < now)) 335 { 336 int i; 337 const int piece = m->piecesNeeded[0].piece; 338 339 tr_removeElementFromArray (m->piecesNeeded, 0, 340 sizeof (struct metadata_node), 341 m->piecesNeededCount--); 342 343 i = m->piecesNeededCount++; 344 m->piecesNeeded[i].piece = piece; 345 m->piecesNeeded[i].requestedAt = now; 346 347 dbgmsg (tor, "next piece to request: %d", piece); 348 *setme_piece = piece; 349 have_request = true; 350 } 351 352 return have_request; 326 bool have_request = false; 327 struct tr_incomplete_metadata * m; 328 329 assert (tr_isTorrent (tor)); 330 331 m = tor->incompleteMetadata; 332 333 if ((m != NULL) && (m->piecesNeededCount > 0) 334 && (m->piecesNeeded[0].requestedAt + MIN_REPEAT_INTERVAL_SECS < now)) 335 { 336 int i; 337 const int piece = m->piecesNeeded[0].piece; 338 339 tr_removeElementFromArray (m->piecesNeeded, 0, 340 sizeof (struct metadata_node), 341 m->piecesNeededCount--); 342 343 i = m->piecesNeededCount++; 344 m->piecesNeeded[i].piece = piece; 345 m->piecesNeeded[i].requestedAt = now; 346 347 dbgmsg (tor, "next piece to request: %d", piece); 348 *setme_piece = piece; 349 have_request = true; 350 } 351 352 return have_request; 353 353 } 354 354 … … 356 356 tr_torrentGetMetadataPercent (const tr_torrent * tor) 357 357 { 358 double ret; 359 360 if (tr_torrentHasMetadata (tor)) 361 ret = 1.0; 362 else { 363 const struct tr_incomplete_metadata * m = tor->incompleteMetadata; 364 if (!m || !m->pieceCount) 365 ret = 0.0; 366 else 367 ret = (m->pieceCount - m->piecesNeededCount) / (double)m->pieceCount; 368 } 369 370 return ret; 371 } 372 373 char* 358 double ret; 359 360 if (tr_torrentHasMetadata (tor)) 361 { 362 ret = 1.0; 363 } 364 else 365 { 366 const struct tr_incomplete_metadata * m = tor->incompleteMetadata; 367 368 if (!m || !m->pieceCount) 369 ret = 0.0; 370 else 371 ret = (m->pieceCount - m->piecesNeededCount) / (double)m->pieceCount; 372 } 373 374 return ret; 375 } 376 377 char * 374 378 tr_torrentInfoGetMagnetLink (const tr_info * inf) 375 379 { 376 377 378 379 380 381 382 383 384 { 385 386 387 } 388 389 390 { 391 392 393 } 394 395 396 } 380 unsigned int i; 381 const char * name; 382 struct evbuffer * s = evbuffer_new (); 383 384 evbuffer_add_printf (s, "magnet:?xt=urn:btih:%s", inf->hashString); 385 386 name = inf->name; 387 if (name && *name) 388 { 389 evbuffer_add_printf (s, "%s", "&dn="); 390 tr_http_escape (s, name, -1, true); 391 } 392 393 for (i=0; i<inf->trackerCount; ++i) 394 { 395 evbuffer_add_printf (s, "%s", "&tr="); 396 tr_http_escape (s, inf->trackers[i].announce, -1, true); 397 } 398 399 return evbuffer_free_to_str (s); 400 }
Note: See TracChangeset
for help on using the changeset viewer.