- Timestamp:
- 23.10.2006 12:46:20 (2 years ago)
- Location:
- trunk
- Files:
-
- 1 added
- 2 modified
-
File/Bittorrent/Decode.php (modified) (10 diffs)
-
Tests/AllTests.php (added)
-
Tests/FileBittorrent.php (modified) (4 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/File/Bittorrent/Decode.php
r62 r65 4 4 // | Decode and Encode data in Bittorrent format | 5 5 // +----------------------------------------------------------------------+ 6 // | Copyright (C) 2004-200 5Markus Tacker <m@tacker.org> |6 // | Copyright (C) 2004-2006 Markus Tacker <m@tacker.org> | 7 7 // +----------------------------------------------------------------------+ 8 8 // | This library is free software; you can redistribute it and/or | … … 40 40 * @category File 41 41 * @author Markus Tacker <m@tacker.org> 42 * @author Robin H. Johnson <robbat2@gentoo.org> 42 43 * @version $Id$ 43 44 */ … … 73 74 * @category File 74 75 * @author Markus Tacker <m@tacker.org> 76 * @author Robin H. Johnson <robbat2@gentoo.org> 75 77 */ 76 78 class File_Bittorrent_Decode … … 165 167 $this->_position = 0; 166 168 $this->_source_length = strlen($this->_source); 167 return $this->_bdecode(); 169 $result = $this->_bdecode(); 170 if ($this->_position < $this->_source_length) { 171 $this->last_error = PEAR::raiseError('File_Bittorrent_Decode::decode() - Trailing garbage in file.'); 172 return false; 173 } 174 return $result; 168 175 } 169 176 … … 239 246 } 240 247 // In case the torrent contains only on file 241 } elseif (isset($this->decoded['info']['name'])) {248 } elseif (isset($this->decoded['info']['name'])) { 242 249 $this->files[] = array( 243 250 'filename' => $this->decoded['info']['name'], … … 316 323 function _decode_dict() 317 324 { 325 $return = array(); 326 $ended = false; 327 $lastkey = NULL; 318 328 while ($char = $this->_getChar()) { 319 if ($char == 'e') break; 329 if ($char == 'e') { 330 $ended = true; 331 break; 332 } 333 if (!ctype_digit($char)) { 334 $this->last_error = PEAR::raiseError('File_Bittorrent_Decode::_decode_dict() - Invalid dictionary key.'); 335 $return = false; 336 break; 337 } 320 338 $key = $this->_decode_string(); 339 if (isset($return[$key])) { 340 $this->last_error = PEAR::raiseError('File_Bittorrent_Decode::_decode_dict() - Duplicate dictionary key.'); 341 $return = false; 342 break; 343 } 344 if ($key < $lastkey) { 345 $this->last_error = PEAR::raiseError('File_Bittorrent_Decode::_decode_dict() - Missorted dictionary key.'); 346 $return = false; 347 break; 348 } 321 349 $val = $this->_bdecode(); 350 if ($val === false) { 351 $this->last_error = PEAR::raiseError('File_Bittorrent_Decode::_decode_dict() - Invalid value.'); 352 $return = false; 353 break; 354 } 322 355 $return[$key] = $val; 356 $lastkey = $key; 357 } 358 if (!$ended) { 359 $this->last_error = PEAR::raiseError('File_Bittorrent_Decode::_decode_dict() - Unterminated dictionary.'); 360 $return = false; 323 361 } 324 362 $this->_position++; … … 338 376 function _decode_string() 339 377 { 378 // Check for bad leading zero 379 if (substr($this->_source, $this->_position, 1) == '0' and 380 substr($this->_source, $this->_position + 1, 1) != ':') { 381 $this->last_error = PEAR::raiseError('File_Bittorrent_Decode::_decode_string() - Leading zero in string length.'); 382 return false; 383 } 340 384 // Find position of colon 341 385 // Supress error message if colon is not found which may be caused by a corrupted or wrong encoded string 342 if(!$pos_colon = @strpos($this->_source, ':', $this->_position)) { 386 if (!$pos_colon = @strpos($this->_source, ':', $this->_position)) { 387 $this->last_error = PEAR::raiseError('File_Bittorrent_Decode::_decode_string() - Colon not found.'); 343 388 return false; 344 389 } 345 390 // Get length of string 346 391 $str_length = intval(substr($this->_source, $this->_position, $pos_colon)); 392 if ($str_length + $pos_colon + 1 > $this->_source_length) { 393 $this->last_error = PEAR::raiseError('File_Bittorrent_Decode::_decode_string() - Input too short for string length.'); 394 return false; 395 } 347 396 // Get string 348 $return = substr($this->_source, $pos_colon + 1, $str_length); 397 if ($str_length === 0) { 398 $return = ''; 399 } else { 400 $return = substr($this->_source, $pos_colon + 1, $str_length); 401 } 349 402 // Move Pointer after string 350 403 $this->_position = $pos_colon + $str_length + 1; … … 365 418 { 366 419 $pos_e = strpos($this->_source, 'e', $this->_position); 420 $p = $this->_position; 421 if ($p === $pos_e) { 422 $this->last_error = PEAR::raiseError('File_Bittorrent_Decode::_decode_int() - Empty integer.'); 423 return false; 424 } 425 if (substr($this->_source, $this->_position, 1) == '-') $p++; 426 if (substr($this->_source, $p, 1) == '0' and 427 ($p != $this->_position or $pos_e > $p+1)) { 428 $this->last_error = PEAR::raiseError('File_Bittorrent_Decode::_decode_int() - Leading zero in integer.'); 429 return false; 430 } 431 for ($i = $p; $i < $pos_e-1; $i++) { 432 if (!ctype_digit(substr($this->_source, $i, 1))) { 433 $this->last_error = PEAR::raiseError('File_Bittorrent_Decode::_decode_int() - Non-digit characters in integer.'); 434 return false; 435 } 436 } 367 437 // The return value showld be automatically casted to float if the intval would 368 438 // overflow. The "+ 0" accomplishes exactly that, using the internal casting … … 389 459 $return = array(); 390 460 $char = $this->_getChar(); 391 while ($this->_source{$this->_position} != 'e') { 461 $p1 = $p2 = 0; 462 if ($char === false) { 463 $this->last_error = PEAR::raiseError('File_Bittorrent_Decode::_decode_list() - Unterminated list.'); 464 return false; 465 } 466 while ($char !== false && substr($this->_source, $this->_position, 1) != 'e') { 467 $p1 = $this->_position; 392 468 $val = $this->_bdecode(); 469 $p2 = $this->_position; 470 // Empty does not work here 471 if($p1 == $p2) { 472 $this->last_error = PEAR::raiseError('File_Bittorrent_Decode::_decode_list() - Unterminated list.'); 473 return false; 474 } 393 475 $return[] = $val; 394 476 } … … 407 489 if (empty($this->_source)) return false; 408 490 if ($this->_position >= $this->_source_length) return false; 409 return $this->_source{$this->_position};491 return substr($this->_source, $this->_position, 1); 410 492 } 411 493 -
trunk/Tests/FileBittorrent.php
r62 r65 4 4 // | Decode and Encode data in Bittorrent format | 5 5 // +----------------------------------------------------------------------+ 6 // | Copyright (C) 2004-200 5Markus Tacker <m@tacker.org> |6 // | Copyright (C) 2004-2006 Markus Tacker <m@tacker.org> | 7 7 // +----------------------------------------------------------------------+ 8 8 // | This library is free software; you can redistribute it and/or | … … 29 29 * @category File 30 30 * @author Markus Tacker <m@tacker.org> 31 * @author Robin H. Johnson <robbat2@gentoo.org> 31 32 * @version $Id$ 32 33 */ … … 42 43 * @category File 43 44 * @author Markus Tacker <m@tacker.org> 45 * @author Robin H. Johnson <robbat2@gentoo.org> 44 46 * @version $Id$ 45 47 */ … … 55 57 $this->assertEquals($File_Bittorrent_Decode->info_hash, substr($bt[3], strpos($bt[3], ':') + 2)); 56 58 } 59 60 public function testDecode() 61 { 62 $test_data = array( 63 '0:0:' => false, // data past end of first correct bencoded string 64 'ie' => false, // empty integer 65 'i341foo382e' => false, // malformed integer 66 'i4e' => 4, 67 'i0e' => 0, 68 'i123456789e' => 123456789, 69 'i-10e' => -10, 70 'i-0e' => false, // negative zero integer 71 'i123' => false, // unterminated integer 72 '' => false, // empty string 73 'i6easd' => false, // integer with trailing garbage 74 '35208734823ljdahflajhdf' => false, // garbage looking vaguely like a string, with large count 75 '2:abfdjslhfld' => false, // string with trailing garbage 76 '0:' => '', 77 '3:abc' => 'abc', 78 '10:1234567890' => '1234567890', 79 '02:xy' => false, // string with extra leading zero in count 80 'l' => false, // unclosed empty list 81 'le' => array(), 82 'leanfdldjfh' => false, // empty list with trailing garbage 83 'l0:0:0:e' => array('', '', ''), 84 'relwjhrlewjh' => false, // complete garbage 85 'li1ei2ei3ee' => array( 1, 2, 3 ), 86 'l3:asd2:xye' => array( 'asd', 'xy' ), 87 'll5:Alice3:Bobeli2ei3eee' => array ( array( 'Alice', 'Bob' ), array( 2, 3 ) ), 88 'd' => false, // unclosed empty dict 89 'defoobar' => false, // empty dict with trailing garbage 90 'de' => array(), 91 'd1:a0:e' => array('a'=>''), 92 'd3:agei25e4:eyes4:bluee' => array('age' => 25, 'eyes' => 'blue' ), 93 'd8:spam.mp3d6:author5:Alice6:lengthi100000eee' => array('spam.mp3' => array( 'author' => 'Alice', 'length' => 100000 )), 94 'd3:fooe' => false, // dict with odd number of elements 95 'di1e0:e' => false, // dict with integer key 96 'd1:b0:1:a0:e' => false, // missorted keys 97 'd1:a0:1:a0:e' => false, // duplicate keys 98 'i03e' => false, // integer with leading zero 99 'l01:ae' => false, // list with string with leading zero in count 100 '9999:x' => false, // string shorter than count 101 'l0:' => false, // unclosed list with content 102 'd0:0:' => false, // unclosed dict with content 103 'd0:' => false, // unclosed dict with odd number of elements 104 '00:' => false, // zero-length string with extra leading zero in count 105 'l-3:e' => false, // list with negative-length string 106 'i-03e' => false, // negative integer with leading zero 107 'di0e0:e' => false, // dictionary with integer key 108 'd8:announceldi0e0:eee' => false, // nested dictionary with integer key 109 'd8:announcedi0e0:e18:azureus_propertiesi0ee' => false, // nested dictionary with integer key #2 110 ); 111 // Thanks to IsoHunt.com for the last 3 testcases of bad data seen in their system. 112 113 $File_Bittorrent_Decode = new File_Bittorrent_Decode; 114 ini_set('mbstring.internal_encoding','ASCII'); 115 foreach($test_data as $ti => $to) { 116 $this->assertEquals($to, $File_Bittorrent_Decode->decode($ti)); 117 } 118 } 57 119 } 58 120