Subversion Repositories svnkaklik

Rev

Details | Last modification | View Log

Rev Author Line No. Line
36 kaklik 1
<?php
2
 
3
/*
4
 
5
Programming info
6
 
7
All functions output a small array, which we'll call $return for now.
8
 
9
$return[0] is the data expected of the function
10
$return[1] is the offset over the whole bencoded data of the next
11
          piece of data.
12
 
13
numberdecode returns [0] as the integer read, and [1]-1 points to the
14
symbol that was interprented as the end of the interger (either "e" or
15
":").
16
numberdecode is used for integer decodes both for i11e and 11:hello there
17
so it is tolerant of the ending symbol.
18
 
19
decodelist returns $return[0] as an integer indexed array like you would use in C
20
for all the entries. $return[1]-1 is the "e" that ends the list, so [1] is the next
21
useful byte.
22
 
23
decodeDict returns $return[0] as an array of text-indexed entries. For example,
24
$return[0]["announce"] = "http://www.whatever.com:6969/announce";
25
$return[1]-1 again points to the "e" that ends the dictionary.
26
 
27
decodeEntry returns [0] as an integer in the case $offset points to
28
i12345e or a string if $offset points to 11:hello there style strings.
29
It also calls decodeDict or decodeList if it encounters a d or an l.
30
 
31
 
32
Known bugs:
33
- The program doesn't pay attention to the string it's working on.
34
 A zero-sized or truncated data block will cause string offset errors
35
 before they get rejected by the decoder. This is worked around by
36
 suppressing errors.
37
 
38
*/
39
 
40
// Protect our namespace using a class
41
class BDecode
42
{
43
 
44
    function numberdecode($wholefile, $start)
45
    {
46
        $ret[0] = 0;
47
        $offset = $start;
48
 
49
        // Funky handling of negative numbers and zero
50
        $negative = false;
51
        if ($wholefile[$offset] == '-')
52
        {
53
             $negative = true;
54
             $offset++;
55
        }
56
        if ($wholefile[$offset] == '0')
57
        {
58
            $offset++;
59
            if ($negative)
60
            {
61
                return array(false);
62
            }
63
            if ($wholefile[$offset] == ':' || $wholefile[$offset] == 'e')
64
            {
65
                $offset++;
66
                $ret[0] = 0;
67
                $ret[1] = $offset;
68
                return $ret;
69
            }
70
            return array(false);
71
        }
72
        while (true)
73
        {
74
            if ($wholefile[$offset] >= '0' && $wholefile[$offset] <= '9')
75
            {
76
                $ret[0] *= 10;
77
            //Added 2005.02.21 - VisiGod
78
            //Changing the type of variable from integer to double to prevent a numeric overflow
79
                settype($ret[0],"double");
80
            //Added 2005.02.21 - VisiGod
81
                $ret[0] += ord($wholefile[$offset]) - ord("0");
82
                $offset++;
83
            }
84
            // Tolerate : or e because this is a multiuse function
85
            else if ($wholefile[$offset] == 'e' || $wholefile[$offset] == ':')
86
            {
87
                $ret[1] = $offset+1;
88
                if ($negative)
89
                {
90
                    if ($ret[0] == 0)
91
                    {
92
                        return array(false);
93
                    }
94
                    $ret[0] = - $ret[0];
95
                }
96
                return $ret;
97
            }
98
            else
99
            {
100
                return array(false);
101
            }
102
        }
103
        return array(false);
104
    }
105
 
106
    function decodeEntry($wholefile, $offset=0)
107
    {
108
        if ($wholefile[$offset] == 'd')
109
        {
110
            return $this->decodeDict($wholefile, $offset);
111
        }
112
        if ($wholefile[$offset] == 'l')
113
        {
114
            return $this->decodelist($wholefile, $offset);
115
        }
116
        if ($wholefile[$offset] == "i")
117
        {
118
            $offset++;
119
            return $this->numberdecode($wholefile, $offset);
120
        }
121
        // String value: decode number, then grab substring
122
        $info = $this->numberdecode($wholefile, $offset);
123
 
124
        if ($info[0] === false)
125
        {
126
            return array(false);
127
        }
128
 
129
        $ret[0] = substr($wholefile, $info[1], $info[0]);
130
        $ret[1] = $info[1]+strlen($ret[0]);
131
 
132
        return $ret;
133
    }
134
 
135
    function decodeList($wholefile, $start)
136
    {
137
        $offset = $start+1;
138
        $i = 0;
139
        if ($wholefile[$start] != 'l')
140
        {
141
            return array(false);
142
        }
143
 
144
        $ret = array();
145
 
146
        while (true)
147
        {
148
            if ($wholefile[$offset] == 'e')
149
            {
150
                break;
151
            }
152
 
153
            $value = $this->decodeEntry($wholefile, $offset);
154
 
155
            if ($value[0] === false)
156
            {
157
                return array(false);
158
            }
159
 
160
            $ret[$i] = $value[0];
161
            $offset = $value[1];
162
            $i ++;
163
        }
164
 
165
        // The empy list is an empty array. Seems fine.
166
        $final[0] = $ret;
167
        $final[1] = $offset+1;
168
 
169
        return $final;
170
    }
171
 
172
// Tries to construct an array
173
    function decodeDict($wholefile, $start=0)
174
    {
175
        $offset = $start;
176
 
177
        if ($wholefile[$offset] == 'l')
178
        {
179
            return $this->decodeList($wholefile, $start);
180
        }
181
        if ($wholefile[$offset] != 'd')
182
        {
183
            return false;
184
        }
185
 
186
        $ret = array();
187
        $offset++;
188
 
189
        while (true)
190
        {
191
            if ($wholefile[$offset] == 'e')
192
            {
193
                $offset++;
194
                break;
195
            }
196
            $left = $this->decodeEntry($wholefile, $offset);
197
            if (!$left[0])
198
            {
199
                return false;
200
            }
201
            $offset = $left[1];
202
            if ($wholefile[$offset] == 'd')
203
            {
204
                // Recurse
205
 
206
                $value = $this->decodedict($wholefile, $offset);
207
 
208
                if (!$value[0])
209
                {
210
                    return false;
211
                }
212
 
213
                $ret[addslashes($left[0])] = $value[0];
214
                $offset= $value[1];
215
 
216
                continue;
217
            }
218
            else if ($wholefile[$offset] == 'l')
219
            {
220
                $value = $this->decodeList($wholefile, $offset);
221
 
222
                if (!$value[0] && is_bool($value[0]))
223
                {
224
                    return false;
225
                }
226
 
227
                $ret[addslashes($left[0])] = $value[0];
228
                $offset = $value[1];
229
            }
230
            else
231
            {
232
                $value = $this->decodeEntry($wholefile, $offset);
233
 
234
                if ($value[0] === false)
235
                {
236
                    return false;
237
                }
238
 
239
                $ret[addslashes($left[0])] = $value[0];
240
                $offset = $value[1];
241
            }
242
        }
243
        if (empty($ret))
244
        {
245
            $final[0] = true;
246
        }
247
        else
248
        {
249
            $final[0] = $ret;
250
        }
251
 
252
        $final[1] = $offset;
253
 
254
        return $final;
255
    }
256
 
257
} // End of class declaration.
258
 
259
// Use this function. eg:  BDecode("d8:announce44:http://www. ... e");
260
function BDecode($wholefile)
261
{
262
    $decoder = new BDecode;
263
    $return = $decoder->decodeEntry($wholefile);
264
 
265
    return $return[0];
266
}
267
 
268
?>