Changeset 65 for trunk

Show
Ignore:
Timestamp:
23.10.2006 12:46:20 (2 years ago)
Author:
m
Message:

Added changes from Bug #9093 to Decode.php. Added test cases. Added AllTest?.php.

Location:
trunk
Files:
1 added
2 modified

Legend:

Unmodified
Added
Removed
  • trunk/File/Bittorrent/Decode.php

    r62 r65  
    44// | Decode and Encode data in Bittorrent format                          | 
    55// +----------------------------------------------------------------------+ 
    6 // | Copyright (C) 2004-2005 Markus Tacker <m@tacker.org>                 | 
     6// | Copyright (C) 2004-2006 Markus Tacker <m@tacker.org>                 | 
    77// +----------------------------------------------------------------------+ 
    88// | This library is free software; you can redistribute it and/or        | 
     
    4040* @category File 
    4141* @author Markus Tacker <m@tacker.org> 
     42* @author Robin H. Johnson <robbat2@gentoo.org> 
    4243* @version $Id$ 
    4344*/ 
     
    7374* @category File 
    7475* @author Markus Tacker <m@tacker.org> 
     76* @author Robin H. Johnson <robbat2@gentoo.org> 
    7577*/ 
    7678class File_Bittorrent_Decode 
     
    165167        $this->_position  = 0; 
    166168        $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; 
    168175    } 
    169176 
     
    239246            } 
    240247        // In case the torrent contains only on file 
    241         } elseif(isset($this->decoded['info']['name']))  { 
     248        } elseif (isset($this->decoded['info']['name']))  { 
    242249                $this->files[] = array( 
    243250                   'filename' => $this->decoded['info']['name'], 
     
    316323    function _decode_dict() 
    317324    { 
     325        $return = array(); 
     326        $ended = false; 
     327        $lastkey = NULL; 
    318328        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            } 
    320338            $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            } 
    321349            $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            } 
    322355            $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; 
    323361        } 
    324362        $this->_position++; 
     
    338376    function _decode_string() 
    339377    { 
     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        } 
    340384        // Find position of colon 
    341385        // 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.'); 
    343388            return false; 
    344389        } 
    345390        // Get length of string 
    346391        $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        } 
    347396        // 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        } 
    349402        // Move Pointer after string 
    350403        $this->_position = $pos_colon + $str_length + 1; 
     
    365418    { 
    366419        $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        } 
    367437        // The return value showld be automatically casted to float if the intval would 
    368438        // overflow. The "+ 0" accomplishes exactly that, using the internal casting 
     
    389459        $return = array(); 
    390460        $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; 
    392468            $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            } 
    393475            $return[] = $val; 
    394476        } 
     
    407489        if (empty($this->_source)) return false; 
    408490        if ($this->_position >= $this->_source_length) return false; 
    409         return $this->_source{$this->_position}; 
     491        return substr($this->_source, $this->_position, 1); 
    410492    } 
    411493 
  • trunk/Tests/FileBittorrent.php

    r62 r65  
    44// | Decode and Encode data in Bittorrent format                          | 
    55// +----------------------------------------------------------------------+ 
    6 // | Copyright (C) 2004-2005 Markus Tacker <m@tacker.org>                 | 
     6// | Copyright (C) 2004-2006 Markus Tacker <m@tacker.org>                 | 
    77// +----------------------------------------------------------------------+ 
    88// | This library is free software; you can redistribute it and/or        | 
     
    2929    * @category File 
    3030    * @author Markus Tacker <m@tacker.org> 
     31    * @author Robin H. Johnson <robbat2@gentoo.org> 
    3132    * @version $Id$ 
    3233    */ 
     
    4243    * @category File 
    4344    * @author Markus Tacker <m@tacker.org> 
     45    * @author Robin H. Johnson <robbat2@gentoo.org> 
    4446    * @version $Id$ 
    4547    */ 
     
    5557            $this->assertEquals($File_Bittorrent_Decode->info_hash, substr($bt[3], strpos($bt[3], ':') + 2)); 
    5658        } 
     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        } 
    57119    } 
    58120