Chris@76
|
1 <?php
|
Chris@76
|
2
|
Chris@76
|
3 /**
|
Chris@76
|
4 * Simple Machines Forum (SMF)
|
Chris@76
|
5 *
|
Chris@76
|
6 * @package SMF
|
Chris@76
|
7 * @author Simple Machines http://www.simplemachines.org
|
Chris@76
|
8 * @copyright 2011 Simple Machines
|
Chris@76
|
9 * @license http://www.simplemachines.org/about/smf/license.php BSD
|
Chris@76
|
10 *
|
Chris@76
|
11 * @version 2.0
|
Chris@76
|
12 */
|
Chris@76
|
13
|
Chris@76
|
14 if (!defined('SMF'))
|
Chris@76
|
15 die('Hacking attempt...');
|
Chris@76
|
16
|
Chris@76
|
17 /* The following functions are all within the xmlArray class, which is the xml
|
Chris@76
|
18 parser. There are more functions, but these are the ones that should be
|
Chris@76
|
19 used from outside the class:
|
Chris@76
|
20
|
Chris@76
|
21 class xmlArray(string data, bool auto_trim = false,
|
Chris@76
|
22 int error_level = error_reporting(), bool is_clone = false)
|
Chris@76
|
23 - creates a new xmlArray, which is an simple xml dom parser.
|
Chris@76
|
24 - data should be the xml data or an array of, unless is_clone is true.
|
Chris@76
|
25 - auto_trim can be used to automatically trim textual data.
|
Chris@76
|
26 - error_level specifies whether notices should be generated for
|
Chris@76
|
27 missing elements and attributes.
|
Chris@76
|
28 - if is_clone is true, the xmlArray is cloned from another - used
|
Chris@76
|
29 internally only.
|
Chris@76
|
30
|
Chris@76
|
31 string xmlArray::name()
|
Chris@76
|
32 - retrieves the name of the current element, usually ''.
|
Chris@76
|
33
|
Chris@76
|
34 string xmlArray::fetch(string path, bool get_elements = false)
|
Chris@76
|
35 - retrieves the textual value of the specified path.
|
Chris@76
|
36 - children are parsed for text, but only textual data is returned
|
Chris@76
|
37 unless get_elements is true.
|
Chris@76
|
38
|
Chris@76
|
39 xmlArray xmlArray::path(string path, bool return_set = false)
|
Chris@76
|
40 - finds any elements that match the path specified.
|
Chris@76
|
41 - will always return a set if there is more than one of the element
|
Chris@76
|
42 or return_set is true.
|
Chris@76
|
43 - returns in the form of a new xmlArray.
|
Chris@76
|
44
|
Chris@76
|
45 bool xmlArray::exists(string path)
|
Chris@76
|
46 - returns whether the specified path matches at least one element.
|
Chris@76
|
47
|
Chris@76
|
48 int xmlArray::count(string path)
|
Chris@76
|
49 - returns the number of elements the path matches.
|
Chris@76
|
50
|
Chris@76
|
51 array xmlArray::set(string path)
|
Chris@76
|
52 - returns an array of xmlArray's matching the specified path.
|
Chris@76
|
53 - this differs from ->path(path, true) in that instead of an xmlArray
|
Chris@76
|
54 of elements, an array of xmlArray's is returned for use with foreach.
|
Chris@76
|
55
|
Chris@76
|
56 string xmlArray::create_xml(string path = '.')
|
Chris@76
|
57 - returns the specified path as an xml file.
|
Chris@76
|
58 */
|
Chris@76
|
59
|
Chris@76
|
60 // An xml array. Reads in xml, allows you to access it simply. Version 1.1.
|
Chris@76
|
61 class xmlArray
|
Chris@76
|
62 {
|
Chris@76
|
63 // The array and debugging output level.
|
Chris@76
|
64 public $array, $debug_level, $trim;
|
Chris@76
|
65
|
Chris@76
|
66 // Create an xml array.
|
Chris@76
|
67 // the xml data, trim elements?, debugging output level, reserved.
|
Chris@76
|
68 //ie. $xml = new xmlArray(file('data.xml'));
|
Chris@76
|
69 public function __construct($data, $auto_trim = false, $level = null, $is_clone = false)
|
Chris@76
|
70 {
|
Chris@76
|
71 // If we're using this try to get some more memory.
|
Chris@76
|
72 @ini_set('memory_limit', '32M');
|
Chris@76
|
73
|
Chris@76
|
74 // Set the debug level.
|
Chris@76
|
75 $this->debug_level = $level !== null ? $level : error_reporting();
|
Chris@76
|
76 $this->trim = $auto_trim;
|
Chris@76
|
77
|
Chris@76
|
78 // Is the data already parsed?
|
Chris@76
|
79 if ($is_clone)
|
Chris@76
|
80 {
|
Chris@76
|
81 $this->array = $data;
|
Chris@76
|
82 return;
|
Chris@76
|
83 }
|
Chris@76
|
84
|
Chris@76
|
85 // Is the input an array? (ie. passed from file()?)
|
Chris@76
|
86 if (is_array($data))
|
Chris@76
|
87 $data = implode('', $data);
|
Chris@76
|
88
|
Chris@76
|
89 // Remove any xml declaration or doctype, and parse out comments and CDATA.
|
Chris@76
|
90 $data = preg_replace('/<!--.*?-->/s', '', $this->_to_cdata(preg_replace(array('/^<\?xml.+?\?' . '>/is', '/<!DOCTYPE[^>]+?' . '>/s'), '', $data)));
|
Chris@76
|
91
|
Chris@76
|
92 // Now parse the xml!
|
Chris@76
|
93 $this->array = $this->_parse($data);
|
Chris@76
|
94 }
|
Chris@76
|
95
|
Chris@76
|
96 // Get the root element's name.
|
Chris@76
|
97 //ie. echo $element->name();
|
Chris@76
|
98 public function name()
|
Chris@76
|
99 {
|
Chris@76
|
100 return isset($this->array['name']) ? $this->array['name'] : '';
|
Chris@76
|
101 }
|
Chris@76
|
102
|
Chris@76
|
103 // Get a specified element's value or attribute by path.
|
Chris@76
|
104 // the path to the element to fetch, whether to include elements?
|
Chris@76
|
105 //ie. $data = $xml->fetch('html/head/title');
|
Chris@76
|
106 public function fetch($path, $get_elements = false)
|
Chris@76
|
107 {
|
Chris@76
|
108 // Get the element, in array form.
|
Chris@76
|
109 $array = $this->path($path);
|
Chris@76
|
110
|
Chris@76
|
111 if ($array === false)
|
Chris@76
|
112 return false;
|
Chris@76
|
113
|
Chris@76
|
114 // Getting elements into this is a bit complicated...
|
Chris@76
|
115 if ($get_elements && !is_string($array))
|
Chris@76
|
116 {
|
Chris@76
|
117 $temp = '';
|
Chris@76
|
118
|
Chris@76
|
119 // Use the _xml() function to get the xml data.
|
Chris@76
|
120 foreach ($array->array as $val)
|
Chris@76
|
121 {
|
Chris@76
|
122 // Skip the name and any attributes.
|
Chris@76
|
123 if (is_array($val))
|
Chris@76
|
124 $temp .= $this->_xml($val, null);
|
Chris@76
|
125 }
|
Chris@76
|
126
|
Chris@76
|
127 // Just get the XML data and then take out the CDATAs.
|
Chris@76
|
128 return $this->_to_cdata($temp);
|
Chris@76
|
129 }
|
Chris@76
|
130
|
Chris@76
|
131 // Return the value - taking care to pick out all the text values.
|
Chris@76
|
132 return is_string($array) ? $array : $this->_fetch($array->array);
|
Chris@76
|
133 }
|
Chris@76
|
134
|
Chris@76
|
135 // Get an element, returns a new xmlArray.
|
Chris@76
|
136 // the path to the element to get, always return full result set? (ie. don't contract a single item.)
|
Chris@76
|
137 //ie. $element = $xml->path('html/body');
|
Chris@76
|
138 public function path($path, $return_full = false)
|
Chris@76
|
139 {
|
Chris@76
|
140 // Split up the path.
|
Chris@76
|
141 $path = explode('/', $path);
|
Chris@76
|
142
|
Chris@76
|
143 // Start with a base array.
|
Chris@76
|
144 $array = $this->array;
|
Chris@76
|
145
|
Chris@76
|
146 // For each element in the path.
|
Chris@76
|
147 foreach ($path as $el)
|
Chris@76
|
148 {
|
Chris@76
|
149 // Deal with sets....
|
Chris@76
|
150 if (strpos($el, '[') !== false)
|
Chris@76
|
151 {
|
Chris@76
|
152 $lvl = (int) substr($el, strpos($el, '[') + 1);
|
Chris@76
|
153 $el = substr($el, 0, strpos($el, '['));
|
Chris@76
|
154 }
|
Chris@76
|
155 // Find an attribute.
|
Chris@76
|
156 elseif (substr($el, 0, 1) == '@')
|
Chris@76
|
157 {
|
Chris@76
|
158 // It simplifies things if the attribute is already there ;).
|
Chris@76
|
159 if (isset($array[$el]))
|
Chris@76
|
160 return $array[$el];
|
Chris@76
|
161 else
|
Chris@76
|
162 {
|
Chris@76
|
163 if (function_exists('debug_backtrace'))
|
Chris@76
|
164 {
|
Chris@76
|
165 $trace = debug_backtrace();
|
Chris@76
|
166 $i = 0;
|
Chris@76
|
167 while ($i < count($trace) && isset($trace[$i]['class']) && $trace[$i]['class'] == get_class($this))
|
Chris@76
|
168 $i++;
|
Chris@76
|
169 $debug = ' from ' . $trace[$i - 1]['file'] . ' on line ' . $trace[$i - 1]['line'];
|
Chris@76
|
170 }
|
Chris@76
|
171 else
|
Chris@76
|
172 $debug = '';
|
Chris@76
|
173
|
Chris@76
|
174 // Cause an error.
|
Chris@76
|
175 if ($this->debug_level & E_NOTICE)
|
Chris@76
|
176 trigger_error('Undefined XML attribute: ' . substr($el, 1) . $debug, E_USER_NOTICE);
|
Chris@76
|
177 return false;
|
Chris@76
|
178 }
|
Chris@76
|
179 }
|
Chris@76
|
180 else
|
Chris@76
|
181 $lvl = null;
|
Chris@76
|
182
|
Chris@76
|
183 // Find this element.
|
Chris@76
|
184 $array = $this->_path($array, $el, $lvl);
|
Chris@76
|
185 }
|
Chris@76
|
186
|
Chris@76
|
187 // Clean up after $lvl, for $return_full.
|
Chris@76
|
188 if ($return_full && (!isset($array['name']) || substr($array['name'], -1) != ']'))
|
Chris@76
|
189 $array = array('name' => $el . '[]', $array);
|
Chris@76
|
190
|
Chris@76
|
191 // Create the right type of class...
|
Chris@76
|
192 $newClass = get_class($this);
|
Chris@76
|
193
|
Chris@76
|
194 // Return a new xmlArray for the result.
|
Chris@76
|
195 return $array === false ? false : new $newClass($array, $this->trim, $this->debug_level, true);
|
Chris@76
|
196 }
|
Chris@76
|
197
|
Chris@76
|
198 // Check if an element exists.
|
Chris@76
|
199 // the path to the element to get.
|
Chris@76
|
200 //ie. echo $xml->exists('html/body') ? 'y' : 'n';
|
Chris@76
|
201 public function exists($path)
|
Chris@76
|
202 {
|
Chris@76
|
203 // Split up the path.
|
Chris@76
|
204 $path = explode('/', $path);
|
Chris@76
|
205
|
Chris@76
|
206 // Start with a base array.
|
Chris@76
|
207 $array = $this->array;
|
Chris@76
|
208
|
Chris@76
|
209 // For each element in the path.
|
Chris@76
|
210 foreach ($path as $el)
|
Chris@76
|
211 {
|
Chris@76
|
212 // Deal with sets....
|
Chris@76
|
213 if (strpos($el, '[') !== false)
|
Chris@76
|
214 {
|
Chris@76
|
215 $lvl = (int) substr($el, strpos($el, '[') + 1);
|
Chris@76
|
216 $el = substr($el, 0, strpos($el, '['));
|
Chris@76
|
217 }
|
Chris@76
|
218 // Find an attribute.
|
Chris@76
|
219 elseif (substr($el, 0, 1) == '@')
|
Chris@76
|
220 return isset($array[$el]);
|
Chris@76
|
221 else
|
Chris@76
|
222 $lvl = null;
|
Chris@76
|
223
|
Chris@76
|
224 // Find this element.
|
Chris@76
|
225 $array = $this->_path($array, $el, $lvl, true);
|
Chris@76
|
226 }
|
Chris@76
|
227
|
Chris@76
|
228 return $array !== false;
|
Chris@76
|
229 }
|
Chris@76
|
230
|
Chris@76
|
231 // Count the number of occurances of a path.
|
Chris@76
|
232 // the path to search for.
|
Chris@76
|
233 //ie. echo $xml->count('html/head/meta');
|
Chris@76
|
234 public function count($path)
|
Chris@76
|
235 {
|
Chris@76
|
236 // Get the element, always returning a full set.
|
Chris@76
|
237 $temp = $this->path($path, true);
|
Chris@76
|
238
|
Chris@76
|
239 // Start at zero, then count up all the numeric keys.
|
Chris@76
|
240 $i = 0;
|
Chris@76
|
241 foreach ($temp->array as $item)
|
Chris@76
|
242 {
|
Chris@76
|
243 if (is_array($item))
|
Chris@76
|
244 $i++;
|
Chris@76
|
245 }
|
Chris@76
|
246
|
Chris@76
|
247 return $i;
|
Chris@76
|
248 }
|
Chris@76
|
249
|
Chris@76
|
250 // Get an array of xmlArray's for use with foreach.
|
Chris@76
|
251 // the path to search for.
|
Chris@76
|
252 //ie. foreach ($xml->set('html/body/p') as $p)
|
Chris@76
|
253 public function set($path)
|
Chris@76
|
254 {
|
Chris@76
|
255 // None as yet, just get the path.
|
Chris@76
|
256 $array = array();
|
Chris@76
|
257 $xml = $this->path($path, true);
|
Chris@76
|
258
|
Chris@76
|
259 foreach ($xml->array as $val)
|
Chris@76
|
260 {
|
Chris@76
|
261 // Skip these, they aren't elements.
|
Chris@76
|
262 if (!is_array($val) || $val['name'] == '!')
|
Chris@76
|
263 continue;
|
Chris@76
|
264
|
Chris@76
|
265 // Create the right type of class...
|
Chris@76
|
266 $newClass = get_class($this);
|
Chris@76
|
267
|
Chris@76
|
268 // Create a new xmlArray and stick it in the array.
|
Chris@76
|
269 $array[] = new $newClass($val, $this->trim, $this->debug_level, true);
|
Chris@76
|
270 }
|
Chris@76
|
271
|
Chris@76
|
272 return $array;
|
Chris@76
|
273 }
|
Chris@76
|
274
|
Chris@76
|
275 // Create an xml file from an xml array.
|
Chris@76
|
276 // the path to the element. (optional)
|
Chris@76
|
277 //ie. echo $this->create_xml()
|
Chris@76
|
278 public function create_xml($path = null)
|
Chris@76
|
279 {
|
Chris@76
|
280 // Was a path specified? If so, use that array.
|
Chris@76
|
281 if ($path !== null)
|
Chris@76
|
282 {
|
Chris@76
|
283 $path = $this->path($path);
|
Chris@76
|
284
|
Chris@76
|
285 // The path was not found
|
Chris@76
|
286 if ($path === false)
|
Chris@76
|
287 return false;
|
Chris@76
|
288
|
Chris@76
|
289 $path = $path->array;
|
Chris@76
|
290 }
|
Chris@76
|
291 // Just use the current array.
|
Chris@76
|
292 else
|
Chris@76
|
293 $path = $this->array;
|
Chris@76
|
294
|
Chris@76
|
295 // Add the xml declaration to the front.
|
Chris@76
|
296 return '<?xml version="1.0"?' . '>' . $this->_xml($path, 0);
|
Chris@76
|
297 }
|
Chris@76
|
298
|
Chris@76
|
299 // Output the xml in an array form.
|
Chris@76
|
300 // the path to output.
|
Chris@76
|
301 //ie. print_r($xml->to_array());
|
Chris@76
|
302 public function to_array($path = null)
|
Chris@76
|
303 {
|
Chris@76
|
304 // Are we doing a specific path?
|
Chris@76
|
305 if ($path !== null)
|
Chris@76
|
306 {
|
Chris@76
|
307 $path = $this->path($path);
|
Chris@76
|
308
|
Chris@76
|
309 // The path was not found
|
Chris@76
|
310 if ($path === false)
|
Chris@76
|
311 return false;
|
Chris@76
|
312
|
Chris@76
|
313 $path = $path->array;
|
Chris@76
|
314 }
|
Chris@76
|
315 // No, so just use the current array.
|
Chris@76
|
316 else
|
Chris@76
|
317 $path = $this->array;
|
Chris@76
|
318
|
Chris@76
|
319 return $this->_array($path);
|
Chris@76
|
320 }
|
Chris@76
|
321
|
Chris@76
|
322 // Parse data into an array. (privately used...)
|
Chris@76
|
323 protected function _parse($data)
|
Chris@76
|
324 {
|
Chris@76
|
325 // Start with an 'empty' array with no data.
|
Chris@76
|
326 $current = array(
|
Chris@76
|
327 );
|
Chris@76
|
328
|
Chris@76
|
329 // Loop until we're out of data.
|
Chris@76
|
330 while ($data != '')
|
Chris@76
|
331 {
|
Chris@76
|
332 // Find and remove the next tag.
|
Chris@76
|
333 preg_match('/\A<([\w\-:]+)((?:\s+.+?)?)([\s]?\/)?' . '>/', $data, $match);
|
Chris@76
|
334 if (isset($match[0]))
|
Chris@76
|
335 $data = preg_replace('/' . preg_quote($match[0], '/') . '/s', '', $data, 1);
|
Chris@76
|
336
|
Chris@76
|
337 // Didn't find a tag? Keep looping....
|
Chris@76
|
338 if (!isset($match[1]) || $match[1] == '')
|
Chris@76
|
339 {
|
Chris@76
|
340 // If there's no <, the rest is data.
|
Chris@76
|
341 if (strpos($data, '<') === false)
|
Chris@76
|
342 {
|
Chris@76
|
343 $text_value = $this->_from_cdata($data);
|
Chris@76
|
344 $data = '';
|
Chris@76
|
345
|
Chris@76
|
346 if ($text_value != '')
|
Chris@76
|
347 $current[] = array(
|
Chris@76
|
348 'name' => '!',
|
Chris@76
|
349 'value' => $text_value
|
Chris@76
|
350 );
|
Chris@76
|
351 }
|
Chris@76
|
352 // If the < isn't immediately next to the current position... more data.
|
Chris@76
|
353 elseif (strpos($data, '<') > 0)
|
Chris@76
|
354 {
|
Chris@76
|
355 $text_value = $this->_from_cdata(substr($data, 0, strpos($data, '<')));
|
Chris@76
|
356 $data = substr($data, strpos($data, '<'));
|
Chris@76
|
357
|
Chris@76
|
358 if ($text_value != '')
|
Chris@76
|
359 $current[] = array(
|
Chris@76
|
360 'name' => '!',
|
Chris@76
|
361 'value' => $text_value
|
Chris@76
|
362 );
|
Chris@76
|
363 }
|
Chris@76
|
364 // If we're looking at a </something> with no start, kill it.
|
Chris@76
|
365 elseif (strpos($data, '<') !== false && strpos($data, '<') == 0)
|
Chris@76
|
366 {
|
Chris@76
|
367 if (strpos($data, '<', 1) !== false)
|
Chris@76
|
368 {
|
Chris@76
|
369 $text_value = $this->_from_cdata(substr($data, 0, strpos($data, '<', 1)));
|
Chris@76
|
370 $data = substr($data, strpos($data, '<', 1));
|
Chris@76
|
371
|
Chris@76
|
372 if ($text_value != '')
|
Chris@76
|
373 $current[] = array(
|
Chris@76
|
374 'name' => '!',
|
Chris@76
|
375 'value' => $text_value
|
Chris@76
|
376 );
|
Chris@76
|
377 }
|
Chris@76
|
378 else
|
Chris@76
|
379 {
|
Chris@76
|
380 $text_value = $this->_from_cdata($data);
|
Chris@76
|
381 $data = '';
|
Chris@76
|
382
|
Chris@76
|
383 if ($text_value != '')
|
Chris@76
|
384 $current[] = array(
|
Chris@76
|
385 'name' => '!',
|
Chris@76
|
386 'value' => $text_value
|
Chris@76
|
387 );
|
Chris@76
|
388 }
|
Chris@76
|
389 }
|
Chris@76
|
390
|
Chris@76
|
391 // Wait for an actual occurance of an element.
|
Chris@76
|
392 continue;
|
Chris@76
|
393 }
|
Chris@76
|
394
|
Chris@76
|
395 // Create a new element in the array.
|
Chris@76
|
396 $el = &$current[];
|
Chris@76
|
397 $el['name'] = $match[1];
|
Chris@76
|
398
|
Chris@76
|
399 // If this ISN'T empty, remove the close tag and parse the inner data.
|
Chris@76
|
400 if ((!isset($match[3]) || trim($match[3]) != '/') && (!isset($match[2]) || trim($match[2]) != '/'))
|
Chris@76
|
401 {
|
Chris@76
|
402 // Because PHP 5.2.0+ seems to croak using regex, we'll have to do this the less fun way.
|
Chris@76
|
403 $last_tag_end = strpos($data, '</' . $match[1]. '>');
|
Chris@76
|
404 if ($last_tag_end === false)
|
Chris@76
|
405 continue;
|
Chris@76
|
406
|
Chris@76
|
407 $offset = 0;
|
Chris@76
|
408 while (1 == 1)
|
Chris@76
|
409 {
|
Chris@76
|
410 // Where is the next start tag?
|
Chris@76
|
411 $next_tag_start = strpos($data, '<' . $match[1], $offset);
|
Chris@76
|
412 // If the next start tag is after the last end tag then we've found the right close.
|
Chris@76
|
413 if ($next_tag_start === false || $next_tag_start > $last_tag_end)
|
Chris@76
|
414 break;
|
Chris@76
|
415
|
Chris@76
|
416 // If not then find the next ending tag.
|
Chris@76
|
417 $next_tag_end = strpos($data, '</' . $match[1]. '>', $offset);
|
Chris@76
|
418
|
Chris@76
|
419 // Didn't find one? Then just use the last and sod it.
|
Chris@76
|
420 if ($next_tag_end === false)
|
Chris@76
|
421 break;
|
Chris@76
|
422 else
|
Chris@76
|
423 {
|
Chris@76
|
424 $last_tag_end = $next_tag_end;
|
Chris@76
|
425 $offset = $next_tag_start + 1;
|
Chris@76
|
426 }
|
Chris@76
|
427 }
|
Chris@76
|
428 // Parse the insides.
|
Chris@76
|
429 $inner_match = substr($data, 0, $last_tag_end);
|
Chris@76
|
430 // Data now starts from where this section ends.
|
Chris@76
|
431 $data = substr($data, $last_tag_end + strlen('</' . $match[1]. '>'));
|
Chris@76
|
432
|
Chris@76
|
433 if (!empty($inner_match))
|
Chris@76
|
434 {
|
Chris@76
|
435 // Parse the inner data.
|
Chris@76
|
436 if (strpos($inner_match, '<') !== false)
|
Chris@76
|
437 $el += $this->_parse($inner_match);
|
Chris@76
|
438 elseif (trim($inner_match) != '')
|
Chris@76
|
439 {
|
Chris@76
|
440 $text_value = $this->_from_cdata($inner_match);
|
Chris@76
|
441 if ($text_value != '')
|
Chris@76
|
442 $el[] = array(
|
Chris@76
|
443 'name' => '!',
|
Chris@76
|
444 'value' => $text_value
|
Chris@76
|
445 );
|
Chris@76
|
446 }
|
Chris@76
|
447 }
|
Chris@76
|
448 }
|
Chris@76
|
449
|
Chris@76
|
450 // If we're dealing with attributes as well, parse them out.
|
Chris@76
|
451 if (isset($match[2]) && $match[2] != '')
|
Chris@76
|
452 {
|
Chris@76
|
453 // Find all the attribute pairs in the string.
|
Chris@76
|
454 preg_match_all('/([\w:]+)="(.+?)"/', $match[2], $attr, PREG_SET_ORDER);
|
Chris@76
|
455
|
Chris@76
|
456 // Set them as @attribute-name.
|
Chris@76
|
457 foreach ($attr as $match_attr)
|
Chris@76
|
458 $el['@' . $match_attr[1]] = $match_attr[2];
|
Chris@76
|
459 }
|
Chris@76
|
460 }
|
Chris@76
|
461
|
Chris@76
|
462 // Return the parsed array.
|
Chris@76
|
463 return $current;
|
Chris@76
|
464 }
|
Chris@76
|
465
|
Chris@76
|
466 // Get a specific element's xml. (privately used...)
|
Chris@76
|
467 protected function _xml($array, $indent)
|
Chris@76
|
468 {
|
Chris@76
|
469 $indentation = $indent !== null ? '
|
Chris@76
|
470 ' . str_repeat(' ', $indent) : '';
|
Chris@76
|
471
|
Chris@76
|
472 // This is a set of elements, with no name...
|
Chris@76
|
473 if (is_array($array) && !isset($array['name']))
|
Chris@76
|
474 {
|
Chris@76
|
475 $temp = '';
|
Chris@76
|
476 foreach ($array as $val)
|
Chris@76
|
477 $temp .= $this->_xml($val, $indent);
|
Chris@76
|
478 return $temp;
|
Chris@76
|
479 }
|
Chris@76
|
480
|
Chris@76
|
481 // This is just text!
|
Chris@76
|
482 if ($array['name'] == '!')
|
Chris@76
|
483 return $indentation . '<![CDATA[' . $array['value'] . ']]>';
|
Chris@76
|
484 elseif (substr($array['name'], -2) == '[]')
|
Chris@76
|
485 $array['name'] = substr($array['name'], 0, -2);
|
Chris@76
|
486
|
Chris@76
|
487 // Start the element.
|
Chris@76
|
488 $output = $indentation . '<' . $array['name'];
|
Chris@76
|
489
|
Chris@76
|
490 $inside_elements = false;
|
Chris@76
|
491 $output_el = '';
|
Chris@76
|
492
|
Chris@76
|
493 // Run through and recurively output all the elements or attrbutes inside this.
|
Chris@76
|
494 foreach ($array as $k => $v)
|
Chris@76
|
495 {
|
Chris@76
|
496 if (substr($k, 0, 1) == '@')
|
Chris@76
|
497 $output .= ' ' . substr($k, 1) . '="' . $v . '"';
|
Chris@76
|
498 elseif (is_array($v))
|
Chris@76
|
499 {
|
Chris@76
|
500 $output_el .= $this->_xml($v, $indent === null ? null : $indent + 1);
|
Chris@76
|
501 $inside_elements = true;
|
Chris@76
|
502 }
|
Chris@76
|
503 }
|
Chris@76
|
504
|
Chris@76
|
505 // Indent, if necessary.... then close the tag.
|
Chris@76
|
506 if ($inside_elements)
|
Chris@76
|
507 $output .= '>' . $output_el . $indentation . '</' . $array['name'] . '>';
|
Chris@76
|
508 else
|
Chris@76
|
509 $output .= ' />';
|
Chris@76
|
510
|
Chris@76
|
511 return $output;
|
Chris@76
|
512 }
|
Chris@76
|
513
|
Chris@76
|
514 // Return an element as an array...
|
Chris@76
|
515 protected function _array($array)
|
Chris@76
|
516 {
|
Chris@76
|
517 $return = array();
|
Chris@76
|
518 $text = '';
|
Chris@76
|
519 foreach ($array as $value)
|
Chris@76
|
520 {
|
Chris@76
|
521 if (!is_array($value) || !isset($value['name']))
|
Chris@76
|
522 continue;
|
Chris@76
|
523
|
Chris@76
|
524 if ($value['name'] == '!')
|
Chris@76
|
525 $text .= $value['value'];
|
Chris@76
|
526 else
|
Chris@76
|
527 $return[$value['name']] = $this->_array($value);
|
Chris@76
|
528 }
|
Chris@76
|
529
|
Chris@76
|
530 if (empty($return))
|
Chris@76
|
531 return $text;
|
Chris@76
|
532 else
|
Chris@76
|
533 return $return;
|
Chris@76
|
534 }
|
Chris@76
|
535
|
Chris@76
|
536 // Parse out CDATA tags. (htmlspecialchars them...)
|
Chris@76
|
537 function _to_cdata($data)
|
Chris@76
|
538 {
|
Chris@76
|
539 $inCdata = $inComment = false;
|
Chris@76
|
540 $output = '';
|
Chris@76
|
541
|
Chris@76
|
542 $parts = preg_split('~(<!\[CDATA\[|\]\]>|<!--|-->)~', $data, -1, PREG_SPLIT_DELIM_CAPTURE);
|
Chris@76
|
543 foreach ($parts as $part)
|
Chris@76
|
544 {
|
Chris@76
|
545 // Handle XML comments.
|
Chris@76
|
546 if (!$inCdata && $part === '<!--')
|
Chris@76
|
547 $inComment = true;
|
Chris@76
|
548 if ($inComment && $part === '-->')
|
Chris@76
|
549 $inComment = false;
|
Chris@76
|
550 elseif ($inComment)
|
Chris@76
|
551 continue;
|
Chris@76
|
552
|
Chris@76
|
553 // Handle Cdata blocks.
|
Chris@76
|
554 elseif (!$inComment && $part === '<![CDATA[')
|
Chris@76
|
555 $inCdata = true;
|
Chris@76
|
556 elseif ($inCdata && $part === ']]>')
|
Chris@76
|
557 $inCdata = false;
|
Chris@76
|
558 elseif ($inCdata)
|
Chris@76
|
559 $output .= htmlentities($part, ENT_QUOTES);
|
Chris@76
|
560
|
Chris@76
|
561 // Everything else is kept as is.
|
Chris@76
|
562 else
|
Chris@76
|
563 $output .= $part;
|
Chris@76
|
564 }
|
Chris@76
|
565
|
Chris@76
|
566 return $output;
|
Chris@76
|
567 }
|
Chris@76
|
568
|
Chris@76
|
569 // Turn the CDATAs back to normal text.
|
Chris@76
|
570 protected function _from_cdata($data)
|
Chris@76
|
571 {
|
Chris@76
|
572 // Get the HTML translation table and reverse it.
|
Chris@76
|
573 $trans_tbl = array_flip(get_html_translation_table(HTML_ENTITIES, ENT_QUOTES));
|
Chris@76
|
574
|
Chris@76
|
575 // Translate all the entities out.
|
Chris@76
|
576 $data = strtr(preg_replace('~&#(\d{1,4});~e', "chr('\$1')", $data), $trans_tbl);
|
Chris@76
|
577
|
Chris@76
|
578 return $this->trim ? trim($data) : $data;
|
Chris@76
|
579 }
|
Chris@76
|
580
|
Chris@76
|
581 // Given an array, return the text from that array. (recursive and privately used.)
|
Chris@76
|
582 protected function _fetch($array)
|
Chris@76
|
583 {
|
Chris@76
|
584 // Don't return anything if this is just a string.
|
Chris@76
|
585 if (is_string($array))
|
Chris@76
|
586 return '';
|
Chris@76
|
587
|
Chris@76
|
588 $temp = '';
|
Chris@76
|
589 foreach ($array as $text)
|
Chris@76
|
590 {
|
Chris@76
|
591 // This means it's most likely an attribute or the name itself.
|
Chris@76
|
592 if (!isset($text['name']))
|
Chris@76
|
593 continue;
|
Chris@76
|
594
|
Chris@76
|
595 // This is text!
|
Chris@76
|
596 if ($text['name'] == '!')
|
Chris@76
|
597 $temp .= $text['value'];
|
Chris@76
|
598 // Another element - dive in ;).
|
Chris@76
|
599 else
|
Chris@76
|
600 $temp .= $this->_fetch($text);
|
Chris@76
|
601 }
|
Chris@76
|
602
|
Chris@76
|
603 // Return all the bits and pieces we've put together.
|
Chris@76
|
604 return $temp;
|
Chris@76
|
605 }
|
Chris@76
|
606
|
Chris@76
|
607 // Get a specific array by path, one level down. (privately used...)
|
Chris@76
|
608 protected function _path($array, $path, $level, $no_error = false)
|
Chris@76
|
609 {
|
Chris@76
|
610 // Is $array even an array? It might be false!
|
Chris@76
|
611 if (!is_array($array))
|
Chris@76
|
612 return false;
|
Chris@76
|
613
|
Chris@76
|
614 // Asking for *no* path?
|
Chris@76
|
615 if ($path == '' || $path == '.')
|
Chris@76
|
616 return $array;
|
Chris@76
|
617 $paths = explode('|', $path);
|
Chris@76
|
618
|
Chris@76
|
619 // A * means all elements of any name.
|
Chris@76
|
620 $show_all = in_array('*', $paths);
|
Chris@76
|
621
|
Chris@76
|
622 $results = array();
|
Chris@76
|
623
|
Chris@76
|
624 // Check each element.
|
Chris@76
|
625 foreach ($array as $value)
|
Chris@76
|
626 {
|
Chris@76
|
627 if (!is_array($value) || $value['name'] === '!')
|
Chris@76
|
628 continue;
|
Chris@76
|
629
|
Chris@76
|
630 if ($show_all || in_array($value['name'], $paths))
|
Chris@76
|
631 {
|
Chris@76
|
632 // Skip elements before "the one".
|
Chris@76
|
633 if ($level !== null && $level > 0)
|
Chris@76
|
634 $level--;
|
Chris@76
|
635 else
|
Chris@76
|
636 $results[] = $value;
|
Chris@76
|
637 }
|
Chris@76
|
638 }
|
Chris@76
|
639
|
Chris@76
|
640 // No results found...
|
Chris@76
|
641 if (empty($results))
|
Chris@76
|
642 {
|
Chris@76
|
643 if (function_exists('debug_backtrace'))
|
Chris@76
|
644 {
|
Chris@76
|
645 $trace = debug_backtrace();
|
Chris@76
|
646 $i = 0;
|
Chris@76
|
647 while ($i < count($trace) && isset($trace[$i]['class']) && $trace[$i]['class'] == get_class($this))
|
Chris@76
|
648 $i++;
|
Chris@76
|
649 $debug = ' from ' . $trace[$i - 1]['file'] . ' on line ' . $trace[$i - 1]['line'];
|
Chris@76
|
650 }
|
Chris@76
|
651 else
|
Chris@76
|
652 $debug = '';
|
Chris@76
|
653
|
Chris@76
|
654 // Cause an error.
|
Chris@76
|
655 if ($this->debug_level & E_NOTICE && !$no_error)
|
Chris@76
|
656 trigger_error('Undefined XML element: ' . $path . $debug, E_USER_NOTICE);
|
Chris@76
|
657 return false;
|
Chris@76
|
658 }
|
Chris@76
|
659 // Only one result.
|
Chris@76
|
660 elseif (count($results) == 1 || $level !== null)
|
Chris@76
|
661 return $results[0];
|
Chris@76
|
662 // Return the result set.
|
Chris@76
|
663 else
|
Chris@76
|
664 return $results + array('name' => $path . '[]');
|
Chris@76
|
665 }
|
Chris@76
|
666 }
|
Chris@76
|
667
|
Chris@76
|
668 // http://www.faqs.org/rfcs/rfc959.html
|
Chris@76
|
669 if (!class_exists('ftp_connection'))
|
Chris@76
|
670 {
|
Chris@76
|
671 class ftp_connection
|
Chris@76
|
672 {
|
Chris@76
|
673 public $connection, $error, $last_message, $pasv;
|
Chris@76
|
674
|
Chris@76
|
675 // Create a new FTP connection...
|
Chris@76
|
676 public function __construct($ftp_server, $ftp_port = 21, $ftp_user = 'anonymous', $ftp_pass = 'ftpclient@simplemachines.org')
|
Chris@76
|
677 {
|
Chris@76
|
678 // Initialize variables.
|
Chris@76
|
679 $this->connection = 'no_connection';
|
Chris@76
|
680 $this->error = false;
|
Chris@76
|
681 $this->pasv = array();
|
Chris@76
|
682
|
Chris@76
|
683 if ($ftp_server !== null)
|
Chris@76
|
684 $this->connect($ftp_server, $ftp_port, $ftp_user, $ftp_pass);
|
Chris@76
|
685 }
|
Chris@76
|
686
|
Chris@76
|
687 public function connect($ftp_server, $ftp_port = 21, $ftp_user = 'anonymous', $ftp_pass = 'ftpclient@simplemachines.org')
|
Chris@76
|
688 {
|
Chris@76
|
689 if (substr($ftp_server, 0, 6) == 'ftp://')
|
Chris@76
|
690 $ftp_server = substr($ftp_server, 6);
|
Chris@76
|
691 elseif (substr($ftp_server, 0, 7) == 'ftps://')
|
Chris@76
|
692 $ftp_server = 'ssl://' . substr($ftp_server, 7);
|
Chris@76
|
693 if (substr($ftp_server, 0, 7) == 'http://')
|
Chris@76
|
694 $ftp_server = substr($ftp_server, 7);
|
Chris@76
|
695 $ftp_server = strtr($ftp_server, array('/' => '', ':' => '', '@' => ''));
|
Chris@76
|
696
|
Chris@76
|
697 // Connect to the FTP server.
|
Chris@76
|
698 $this->connection = @fsockopen($ftp_server, $ftp_port, $err, $err, 5);
|
Chris@76
|
699 if (!$this->connection)
|
Chris@76
|
700 {
|
Chris@76
|
701 $this->error = 'bad_server';
|
Chris@76
|
702 return;
|
Chris@76
|
703 }
|
Chris@76
|
704
|
Chris@76
|
705 // Get the welcome message...
|
Chris@76
|
706 if (!$this->check_response(220))
|
Chris@76
|
707 {
|
Chris@76
|
708 $this->error = 'bad_response';
|
Chris@76
|
709 return;
|
Chris@76
|
710 }
|
Chris@76
|
711
|
Chris@76
|
712 // Send the username, it should ask for a password.
|
Chris@76
|
713 fwrite($this->connection, 'USER ' . $ftp_user . "\r\n");
|
Chris@76
|
714 if (!$this->check_response(331))
|
Chris@76
|
715 {
|
Chris@76
|
716 $this->error = 'bad_username';
|
Chris@76
|
717 return;
|
Chris@76
|
718 }
|
Chris@76
|
719
|
Chris@76
|
720 // Now send the password... and hope it goes okay.
|
Chris@76
|
721 fwrite($this->connection, 'PASS ' . $ftp_pass . "\r\n");
|
Chris@76
|
722 if (!$this->check_response(230))
|
Chris@76
|
723 {
|
Chris@76
|
724 $this->error = 'bad_password';
|
Chris@76
|
725 return;
|
Chris@76
|
726 }
|
Chris@76
|
727 }
|
Chris@76
|
728
|
Chris@76
|
729 public function chdir($ftp_path)
|
Chris@76
|
730 {
|
Chris@76
|
731 if (!is_resource($this->connection))
|
Chris@76
|
732 return false;
|
Chris@76
|
733
|
Chris@76
|
734 // No slash on the end, please...
|
Chris@76
|
735 if ($ftp_path !== '/' && substr($ftp_path, -1) === '/')
|
Chris@76
|
736 $ftp_path = substr($ftp_path, 0, -1);
|
Chris@76
|
737
|
Chris@76
|
738 fwrite($this->connection, 'CWD ' . $ftp_path . "\r\n");
|
Chris@76
|
739 if (!$this->check_response(250))
|
Chris@76
|
740 {
|
Chris@76
|
741 $this->error = 'bad_path';
|
Chris@76
|
742 return false;
|
Chris@76
|
743 }
|
Chris@76
|
744
|
Chris@76
|
745 return true;
|
Chris@76
|
746 }
|
Chris@76
|
747
|
Chris@76
|
748 public function chmod($ftp_file, $chmod)
|
Chris@76
|
749 {
|
Chris@76
|
750 if (!is_resource($this->connection))
|
Chris@76
|
751 return false;
|
Chris@76
|
752
|
Chris@76
|
753 if ($ftp_file == '')
|
Chris@76
|
754 $ftp_file = '.';
|
Chris@76
|
755
|
Chris@76
|
756 // Convert the chmod value from octal (0777) to text ("777").
|
Chris@76
|
757 fwrite($this->connection, 'SITE CHMOD ' . decoct($chmod) . ' ' . $ftp_file . "\r\n");
|
Chris@76
|
758 if (!$this->check_response(200))
|
Chris@76
|
759 {
|
Chris@76
|
760 $this->error = 'bad_file';
|
Chris@76
|
761 return false;
|
Chris@76
|
762 }
|
Chris@76
|
763
|
Chris@76
|
764 return true;
|
Chris@76
|
765 }
|
Chris@76
|
766
|
Chris@76
|
767 public function unlink($ftp_file)
|
Chris@76
|
768 {
|
Chris@76
|
769 // We are actually connected, right?
|
Chris@76
|
770 if (!is_resource($this->connection))
|
Chris@76
|
771 return false;
|
Chris@76
|
772
|
Chris@76
|
773 // Delete file X.
|
Chris@76
|
774 fwrite($this->connection, 'DELE ' . $ftp_file . "\r\n");
|
Chris@76
|
775 if (!$this->check_response(250))
|
Chris@76
|
776 {
|
Chris@76
|
777 fwrite($this->connection, 'RMD ' . $ftp_file . "\r\n");
|
Chris@76
|
778
|
Chris@76
|
779 // Still no love?
|
Chris@76
|
780 if (!$this->check_response(250))
|
Chris@76
|
781 {
|
Chris@76
|
782 $this->error = 'bad_file';
|
Chris@76
|
783 return false;
|
Chris@76
|
784 }
|
Chris@76
|
785 }
|
Chris@76
|
786
|
Chris@76
|
787 return true;
|
Chris@76
|
788 }
|
Chris@76
|
789
|
Chris@76
|
790 public function check_response($desired)
|
Chris@76
|
791 {
|
Chris@76
|
792 // Wait for a response that isn't continued with -, but don't wait too long.
|
Chris@76
|
793 $time = time();
|
Chris@76
|
794 do
|
Chris@76
|
795 $this->last_message = fgets($this->connection, 1024);
|
Chris@76
|
796 while ((strlen($this->last_message) < 4 || substr($this->last_message, 0, 1) == ' ' || substr($this->last_message, 3, 1) != ' ') && time() - $time < 5);
|
Chris@76
|
797
|
Chris@76
|
798 // Was the desired response returned?
|
Chris@76
|
799 return is_array($desired) ? in_array(substr($this->last_message, 0, 3), $desired) : substr($this->last_message, 0, 3) == $desired;
|
Chris@76
|
800 }
|
Chris@76
|
801
|
Chris@76
|
802 public function passive()
|
Chris@76
|
803 {
|
Chris@76
|
804 // We can't create a passive data connection without a primary one first being there.
|
Chris@76
|
805 if (!is_resource($this->connection))
|
Chris@76
|
806 return false;
|
Chris@76
|
807
|
Chris@76
|
808 // Request a passive connection - this means, we'll talk to you, you don't talk to us.
|
Chris@76
|
809 @fwrite($this->connection, 'PASV' . "\r\n");
|
Chris@76
|
810 $time = time();
|
Chris@76
|
811 do
|
Chris@76
|
812 $response = fgets($this->connection, 1024);
|
Chris@76
|
813 while (substr($response, 3, 1) != ' ' && time() - $time < 5);
|
Chris@76
|
814
|
Chris@76
|
815 // If it's not 227, we weren't given an IP and port, which means it failed.
|
Chris@76
|
816 if (substr($response, 0, 4) != '227 ')
|
Chris@76
|
817 {
|
Chris@76
|
818 $this->error = 'bad_response';
|
Chris@76
|
819 return false;
|
Chris@76
|
820 }
|
Chris@76
|
821
|
Chris@76
|
822 // Snatch the IP and port information, or die horribly trying...
|
Chris@76
|
823 if (preg_match('~\((\d+),\s*(\d+),\s*(\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+))\)~', $response, $match) == 0)
|
Chris@76
|
824 {
|
Chris@76
|
825 $this->error = 'bad_response';
|
Chris@76
|
826 return false;
|
Chris@76
|
827 }
|
Chris@76
|
828
|
Chris@76
|
829 // This is pretty simple - store it for later use ;).
|
Chris@76
|
830 $this->pasv = array('ip' => $match[1] . '.' . $match[2] . '.' . $match[3] . '.' . $match[4], 'port' => $match[5] * 256 + $match[6]);
|
Chris@76
|
831
|
Chris@76
|
832 return true;
|
Chris@76
|
833 }
|
Chris@76
|
834
|
Chris@76
|
835 public function create_file($ftp_file)
|
Chris@76
|
836 {
|
Chris@76
|
837 // First, we have to be connected... very important.
|
Chris@76
|
838 if (!is_resource($this->connection))
|
Chris@76
|
839 return false;
|
Chris@76
|
840
|
Chris@76
|
841 // I'd like one passive mode, please!
|
Chris@76
|
842 if (!$this->passive())
|
Chris@76
|
843 return false;
|
Chris@76
|
844
|
Chris@76
|
845 // Seems logical enough, so far...
|
Chris@76
|
846 fwrite($this->connection, 'STOR ' . $ftp_file . "\r\n");
|
Chris@76
|
847
|
Chris@76
|
848 // Okay, now we connect to the data port. If it doesn't work out, it's probably "file already exists", etc.
|
Chris@76
|
849 $fp = @fsockopen($this->pasv['ip'], $this->pasv['port'], $err, $err, 5);
|
Chris@76
|
850 if (!$fp || !$this->check_response(150))
|
Chris@76
|
851 {
|
Chris@76
|
852 $this->error = 'bad_file';
|
Chris@76
|
853 @fclose($fp);
|
Chris@76
|
854 return false;
|
Chris@76
|
855 }
|
Chris@76
|
856
|
Chris@76
|
857 // This may look strange, but we're just closing it to indicate a zero-byte upload.
|
Chris@76
|
858 fclose($fp);
|
Chris@76
|
859 if (!$this->check_response(226))
|
Chris@76
|
860 {
|
Chris@76
|
861 $this->error = 'bad_response';
|
Chris@76
|
862 return false;
|
Chris@76
|
863 }
|
Chris@76
|
864
|
Chris@76
|
865 return true;
|
Chris@76
|
866 }
|
Chris@76
|
867
|
Chris@76
|
868 public function list_dir($ftp_path = '', $search = false)
|
Chris@76
|
869 {
|
Chris@76
|
870 // Are we even connected...?
|
Chris@76
|
871 if (!is_resource($this->connection))
|
Chris@76
|
872 return false;
|
Chris@76
|
873
|
Chris@76
|
874 // Passive... non-agressive...
|
Chris@76
|
875 if (!$this->passive())
|
Chris@76
|
876 return false;
|
Chris@76
|
877
|
Chris@76
|
878 // Get the listing!
|
Chris@76
|
879 fwrite($this->connection, 'LIST -1' . ($search ? 'R' : '') . ($ftp_path == '' ? '' : ' ' . $ftp_path) . "\r\n");
|
Chris@76
|
880
|
Chris@76
|
881 // Connect, assuming we've got a connection.
|
Chris@76
|
882 $fp = @fsockopen($this->pasv['ip'], $this->pasv['port'], $err, $err, 5);
|
Chris@76
|
883 if (!$fp || !$this->check_response(array(150, 125)))
|
Chris@76
|
884 {
|
Chris@76
|
885 $this->error = 'bad_response';
|
Chris@76
|
886 @fclose($fp);
|
Chris@76
|
887 return false;
|
Chris@76
|
888 }
|
Chris@76
|
889
|
Chris@76
|
890 // Read in the file listing.
|
Chris@76
|
891 $data = '';
|
Chris@76
|
892 while (!feof($fp))
|
Chris@76
|
893 $data .= fread($fp, 4096);
|
Chris@76
|
894 fclose($fp);
|
Chris@76
|
895
|
Chris@76
|
896 // Everything go okay?
|
Chris@76
|
897 if (!$this->check_response(226))
|
Chris@76
|
898 {
|
Chris@76
|
899 $this->error = 'bad_response';
|
Chris@76
|
900 return false;
|
Chris@76
|
901 }
|
Chris@76
|
902
|
Chris@76
|
903 return $data;
|
Chris@76
|
904 }
|
Chris@76
|
905
|
Chris@76
|
906 public function locate($file, $listing = null)
|
Chris@76
|
907 {
|
Chris@76
|
908 if ($listing === null)
|
Chris@76
|
909 $listing = $this->list_dir('', true);
|
Chris@76
|
910 $listing = explode("\n", $listing);
|
Chris@76
|
911
|
Chris@76
|
912 @fwrite($this->connection, 'PWD' . "\r\n");
|
Chris@76
|
913 $time = time();
|
Chris@76
|
914 do
|
Chris@76
|
915 $response = fgets($this->connection, 1024);
|
Chris@76
|
916 while ($response[3] != ' ' && time() - $time < 5);
|
Chris@76
|
917
|
Chris@76
|
918 // Check for 257!
|
Chris@76
|
919 if (preg_match('~^257 "(.+?)" ~', $response, $match) != 0)
|
Chris@76
|
920 $current_dir = strtr($match[1], array('""' => '"'));
|
Chris@76
|
921 else
|
Chris@76
|
922 $current_dir = '';
|
Chris@76
|
923
|
Chris@76
|
924 for ($i = 0, $n = count($listing); $i < $n; $i++)
|
Chris@76
|
925 {
|
Chris@76
|
926 if (trim($listing[$i]) == '' && isset($listing[$i + 1]))
|
Chris@76
|
927 {
|
Chris@76
|
928 $current_dir = substr(trim($listing[++$i]), 0, -1);
|
Chris@76
|
929 $i++;
|
Chris@76
|
930 }
|
Chris@76
|
931
|
Chris@76
|
932 // Okay, this file's name is:
|
Chris@76
|
933 $listing[$i] = $current_dir . '/' . trim(strlen($listing[$i]) > 30 ? strrchr($listing[$i], ' ') : $listing[$i]);
|
Chris@76
|
934
|
Chris@76
|
935 if ($file[0] == '*' && substr($listing[$i], -(strlen($file) - 1)) == substr($file, 1))
|
Chris@76
|
936 return $listing[$i];
|
Chris@76
|
937 if (substr($file, -1) == '*' && substr($listing[$i], 0, strlen($file) - 1) == substr($file, 0, -1))
|
Chris@76
|
938 return $listing[$i];
|
Chris@76
|
939 if (basename($listing[$i]) == $file || $listing[$i] == $file)
|
Chris@76
|
940 return $listing[$i];
|
Chris@76
|
941 }
|
Chris@76
|
942
|
Chris@76
|
943 return false;
|
Chris@76
|
944 }
|
Chris@76
|
945
|
Chris@76
|
946 public function create_dir($ftp_dir)
|
Chris@76
|
947 {
|
Chris@76
|
948 // We must be connected to the server to do something.
|
Chris@76
|
949 if (!is_resource($this->connection))
|
Chris@76
|
950 return false;
|
Chris@76
|
951
|
Chris@76
|
952 // Make this new beautiful directory!
|
Chris@76
|
953 fwrite($this->connection, 'MKD ' . $ftp_dir . "\r\n");
|
Chris@76
|
954 if (!$this->check_response(257))
|
Chris@76
|
955 {
|
Chris@76
|
956 $this->error = 'bad_file';
|
Chris@76
|
957 return false;
|
Chris@76
|
958 }
|
Chris@76
|
959
|
Chris@76
|
960 return true;
|
Chris@76
|
961 }
|
Chris@76
|
962
|
Chris@76
|
963 public function detect_path($filesystem_path, $lookup_file = null)
|
Chris@76
|
964 {
|
Chris@76
|
965 $username = '';
|
Chris@76
|
966
|
Chris@76
|
967 if (isset($_SERVER['DOCUMENT_ROOT']))
|
Chris@76
|
968 {
|
Chris@76
|
969 if (preg_match('~^/home[2]?/([^/]+?)/public_html~', $_SERVER['DOCUMENT_ROOT'], $match))
|
Chris@76
|
970 {
|
Chris@76
|
971 $username = $match[1];
|
Chris@76
|
972
|
Chris@76
|
973 $path = strtr($_SERVER['DOCUMENT_ROOT'], array('/home/' . $match[1] . '/' => '', '/home2/' . $match[1] . '/' => ''));
|
Chris@76
|
974
|
Chris@76
|
975 if (substr($path, -1) == '/')
|
Chris@76
|
976 $path = substr($path, 0, -1);
|
Chris@76
|
977
|
Chris@76
|
978 if (strlen(dirname($_SERVER['PHP_SELF'])) > 1)
|
Chris@76
|
979 $path .= dirname($_SERVER['PHP_SELF']);
|
Chris@76
|
980 }
|
Chris@76
|
981 elseif (substr($filesystem_path, 0, 9) == '/var/www/')
|
Chris@76
|
982 $path = substr($filesystem_path, 8);
|
Chris@76
|
983 else
|
Chris@76
|
984 $path = strtr(strtr($filesystem_path, array('\\' => '/')), array($_SERVER['DOCUMENT_ROOT'] => ''));
|
Chris@76
|
985 }
|
Chris@76
|
986 else
|
Chris@76
|
987 $path = '';
|
Chris@76
|
988
|
Chris@76
|
989 if (is_resource($this->connection) && $this->list_dir($path) == '')
|
Chris@76
|
990 {
|
Chris@76
|
991 $data = $this->list_dir('', true);
|
Chris@76
|
992
|
Chris@76
|
993 if ($lookup_file === null)
|
Chris@76
|
994 $lookup_file = $_SERVER['PHP_SELF'];
|
Chris@76
|
995
|
Chris@76
|
996 $found_path = dirname($this->locate('*' . basename(dirname($lookup_file)) . '/' . basename($lookup_file), $data));
|
Chris@76
|
997 if ($found_path == false)
|
Chris@76
|
998 $found_path = dirname($this->locate(basename($lookup_file)));
|
Chris@76
|
999 if ($found_path != false)
|
Chris@76
|
1000 $path = $found_path;
|
Chris@76
|
1001 }
|
Chris@76
|
1002 elseif (is_resource($this->connection))
|
Chris@76
|
1003 $found_path = true;
|
Chris@76
|
1004
|
Chris@76
|
1005 return array($username, $path, isset($found_path));
|
Chris@76
|
1006 }
|
Chris@76
|
1007
|
Chris@76
|
1008 public function close()
|
Chris@76
|
1009 {
|
Chris@76
|
1010 // Goodbye!
|
Chris@76
|
1011 fwrite($this->connection, 'QUIT' . "\r\n");
|
Chris@76
|
1012 fclose($this->connection);
|
Chris@76
|
1013
|
Chris@76
|
1014 return true;
|
Chris@76
|
1015 }
|
Chris@76
|
1016 }
|
Chris@76
|
1017 }
|
Chris@76
|
1018
|
Chris@76
|
1019 ?> |