[ Index ] |
|
Code source de Horde 3.1.3 |
1 <?php 2 /** 3 * Stateless VFS implementation for a SMB server, based on smbclient. 4 * 5 * Required values for $params: 6 * <pre> 7 * 'username' - The username with which to connect to the SMB server. 8 * 'password' - The password with which to connect to the SMB server. 9 * 'hostspec' - The SMB server to connect to. 10 * 'port' - The SMB port number to connect to. 11 * 'share' - The share to access on the SMB server. 12 * 'smbclient' - The path to the 'smbclient' executable. 13 * </pre> 14 * 15 * Optional values for $params: 16 * <pre> 17 * 'ipaddress' - The address of the server to connect to. 18 * </pre> 19 * 20 * Functions not implemented: 21 * - changePermissions(): The SMB permission style does not fit with the 22 * module. 23 * 24 * $Horde: framework/VFS/VFS/smb.php,v 1.11.2.8 2006/05/31 04:50:02 slusarz Exp $ 25 * 26 * Codebase copyright 2002 Paul Gareau <paul@xhawk.net>. Adapted with 27 * permission by Patrice Levesque <wayne@ptaff.ca> from phpsmb-0.8 code, and 28 * converted to the LGPL. Please do not taunt original author, contact 29 * Patrice Levesque or dev@lists.horde.org. 30 * 31 * See the enclosed file COPYING for license information (LGPL). If you 32 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html. 33 * 34 * @author Paul Gareau <paul@xhawk.net> 35 * @author Patrice Levesque <wayne@ptaff.ca> 36 * @since Horde 3.1 37 * @package VFS 38 */ 39 class VFS_smb extends VFS { 40 41 /** 42 * List of additional credentials required for this VFS backend. 43 * 44 * @var array 45 */ 46 var $_credentials = array('username', 'password'); 47 48 /** 49 * List of permissions and if they can be changed in this VFS backend. 50 * 51 * @var array 52 */ 53 var $_permissions = array( 54 'owner' => array('read' => false, 'write' => false, 'execute' => false), 55 'group' => array('read' => false, 'write' => false, 'execute' => false), 56 'all' => array('read' => false, 'write' => false, 'execute' => false)); 57 58 /** 59 * Authenticates a user on the SMB server and share. 60 * 61 * @access private 62 * 63 * @return boolean|PEAR_Error True on success or a PEAR_Error on failure. 64 */ 65 function _connect() 66 { 67 $cmd = array('quit'); 68 $err = $this->_command('', $cmd); 69 if (is_a($err, 'PEAR_Error')) { 70 return PEAR::raiseError(_("Authentication to the SMB server failed.")); 71 } 72 return true; 73 } 74 75 /** 76 * Retrieves a file from the VFS. 77 * 78 * @param string $path The pathname to the file. 79 * @param string $name The filename to retrieve. 80 * 81 * @return string The file data. 82 */ 83 function read($path, $name) 84 { 85 list($path, $name) = $this->_escapeShellCommand($path, $name); 86 $temp = $this->_getTempFile(); 87 $cmd = array('get \"' . $name . '\" ' . $temp); 88 $err = $this->_command($path, $cmd); 89 if (is_a($err, 'PEAR_Error')) { 90 return $err; 91 } 92 if (!file_exists($temp)) { 93 return PEAR::raiseError(sprintf(_("Unable to open VFS file \"%s\"."), $this->_getPath($path, $name))); 94 } 95 if (function_exists('file_get_contents')) { 96 $file = file_get_contents($temp); 97 } else { 98 $fp = fopen($temp, 'r'); 99 $file = fread($fp, filesize($temp)); 100 fclose($fp); 101 } 102 unlink($temp); 103 if ($file) { 104 return $file; 105 } else { 106 return PEAR::raiseError(sprintf(_("Unable to open VFS file \"%s\"."), $this->_getPath($path, $name))); 107 } 108 } 109 110 /** 111 * Stores a file in the VFS. 112 * 113 * @param string $path The path to store the file in. 114 * @param string $name The filename to use. 115 * @param string $tmpFile The temporary file containing the data to be 116 * stored. 117 * @param boolean $autocreate Automatically create directories? 118 * 119 * @return boolean|PEAR_Error True on success or a PEAR_Error on failure. 120 */ 121 function write($path, $name, $tmpFile, $autocreate = false) 122 { 123 // Double quotes not allowed in SMB filename. 124 $name = str_replace('"', "'", $name); 125 126 list($path, $name) = $this->_escapeShellCommand($path, $name); 127 $cmd = array('put \"' . $tmpFile . '\" \"' . $name . '\"'); 128 // do we need to first autocreate the directory? 129 if ($autocreate) { 130 $result = $this->autocreatePath($path); 131 if (is_a($result, 'PEAR_Error')) { 132 return $result; 133 } 134 } 135 $err = $this->_command($path, $cmd); 136 if (is_a($err, 'PEAR_Error')) { 137 return $err; 138 } 139 return true; 140 } 141 142 /** 143 * Stores a file in the VFS from raw data. 144 * 145 * @param string $path The path to store the file in. 146 * @param string $name The filename to use. 147 * @param string $data The file data. 148 * @param boolean $autocreate Automatically create directories? 149 * 150 * @return boolean|PEAR_Error True on success or a PEAR_Error on failure. 151 */ 152 function writeData($path, $name, $data, $autocreate = false) 153 { 154 $tmpFile = $this->_getTempFile(); 155 $fp = fopen($tmpFile, 'wb'); 156 fwrite($fp, $data); 157 fclose($fp); 158 $result = $this->write($path, $name, $tmpFile, $autocreate); 159 unlink($tmpFile); 160 return $result; 161 } 162 163 /** 164 * Deletes a file from the VFS. 165 * 166 * @param string $path The path to delete the file from. 167 * @param string $name The filename to use. 168 * 169 * @return boolean|PEAR_Error True on success or a PEAR_Error on failure. 170 */ 171 function deleteFile($path, $name) 172 { 173 list($path, $name) = $this->_escapeShellCommand($path, $name); 174 $cmd = array('del \"' . $name . '\"'); 175 $err = $this->_command($path, $cmd); 176 if (is_a($err, 'PEAR_Error')) { 177 return $err; 178 } 179 return true; 180 } 181 182 /** 183 * Checks if a given pathname is a folder. 184 * 185 * @param string $path The path to the folder. 186 * @param string $name The file or folder name. 187 * 188 * @return boolean True if it is a folder, false otherwise. 189 */ 190 function isFolder($path, $name) 191 { 192 list($path, $name) = $this->_escapeShellCommand($path, $name); 193 $cmd = array('quit'); 194 $err = $this->_command($this->_getPath($path, $name), $cmd); 195 if (is_a($err, 'PEAR_Error')) { 196 return false; 197 } 198 return true; 199 } 200 201 /** 202 * Deletes a folder from the VFS. 203 * 204 * @param string $path The path to delete the folder from. 205 * @param string $name The name of the folder to delete. 206 * @param boolean $recursive Force a recursive delete? 207 * 208 * @return boolean|PEAR_Error True on success or a PEAR_Error on failure. 209 */ 210 function deleteFolder($path, $name, $recursive = false) 211 { 212 $isDir = false; 213 $dirCheck = $this->listFolder($path); 214 foreach ($dirCheck as $file) { 215 if ($file['name'] == $name && $file['type'] == '**dir') { 216 $isDir = true; 217 break; 218 } 219 } 220 221 if (!$isDir) { 222 return PEAR::raiseError(sprintf(_("\"%s\" is not a directory."), $path . '/' . $name)); 223 } 224 225 $file_list = $this->listFolder($this->_getPath($path, $name)); 226 if (is_a($file_list, 'PEAR_Error')) { 227 return $file_list; 228 } 229 230 if ($file_list && !$recursive) { 231 return PEAR::raiseError(sprintf(_("Unable to delete \"%s\", the directory is not empty."), 232 $this->_getPath($path, $name))); 233 } 234 235 foreach ($file_list as $file) { 236 if ($file['type'] == '**dir') { 237 $result = $this->deleteFolder($this->_getPath($path, $name), $file['name'], $recursive); 238 } else { 239 $result = $this->deleteFile($this->_getPath($path, $name), $file['name']); 240 } 241 if (is_a($result, 'PEAR_Error')) { 242 return $result; 243 } 244 } 245 246 // Really delete the folder. 247 list($path, $name) = $this->_escapeShellCommand($path, $name); 248 $cmd = array('rmdir \"' . $name . '\"'); 249 $err = $this->_command($path, $cmd); 250 if (is_a($err, 'PEAR_Error')) { 251 return PEAR::raiseError(sprintf(_("Unable to delete VFS folder \"%s\"."), $this->_getPath($path, $name))); 252 } else { 253 return true; 254 } 255 } 256 257 /** 258 * Renames a file in the VFS. 259 * 260 * @param string $oldpath The old path to the file. 261 * @param string $oldname The old filename. 262 * @param string $newpath The new path of the file. 263 * @param string $newname The new filename. 264 * 265 * @return boolean|PEAR_Error True on success or a PEAR_Error on failure. 266 */ 267 function rename($oldpath, $oldname, $newpath, $newname) 268 { 269 if (is_a($result = $this->autocreatePath($newpath), 'PEAR_Error')) { 270 return $result; 271 } 272 273 // Double quotes not allowed in SMB filename. 274 $newname = str_replace('"', "'", $newname); 275 276 list($file, $name) = $this->_escapeShellCommand($oldname, $newname); 277 $cmd = array('rename \"' . str_replace('/', '\\\\', $oldpath) . '\\' . $file . '\" \"' . 278 str_replace('/', '\\\\', $newpath) . '\\' . $name . '\"'); 279 if (is_a($err = $this->_command('', $cmd), 'PEAR_Error')) { 280 return PEAR::raiseError(sprintf(_("Unable to rename VFS file \"%s\"."), $this->_getPath($path, $name))); 281 } 282 283 return true; 284 } 285 286 /** 287 * Creates a folder on the VFS. 288 * 289 * @param string $path The path of directory to create folder. 290 * @param string $name The name of the new folder. 291 * 292 * @return boolean|PEAR_Error True on success or a PEAR_Error on failure. 293 */ 294 function createFolder($path, $name) 295 { 296 // Double quotes not allowed in SMB filename. 297 $name = str_replace('"', "'", $name); 298 299 list($dir, $mkdir) = $this->_escapeShellCommand($path, $name); 300 $cmd = array('mkdir \"' . $mkdir . '\"'); 301 $err = $this->_command($dir, $cmd); 302 if (is_a($err, 'PEAR_Error')) { 303 return PEAR::raiseError(sprintf(_("Unable to create VFS folder \"%s\"."), $this->_getPath($path, $name))); 304 } 305 return true; 306 } 307 308 /** 309 * Returns an unsorted file list. 310 * 311 * @param string $path The path of the directory to get the file list 312 * for. 313 * @param mixed $filter Hash of items to filter based on filename. 314 * @param boolean $dotfiles Show dotfiles? This is irrelevant with 315 * smbclient. 316 * @param boolean $dironly Show directories only? 317 * 318 * @return boolean|PEAR_Error File list on success or a PEAR_Error on 319 * failure. 320 */ 321 function listFolder($path = '', $filter = null, $dotfiles = true, $dironly = false) 322 { 323 list($path) = $this->_escapeShellCommand($path); 324 $cmd = array('ls'); 325 $res = $this->_command($path, $cmd); 326 if (is_a($res, 'PEAR_Error')) { 327 return $res; 328 } 329 $num_lines = count($res); 330 $files = array(); 331 for ($r = 0; $r < $num_lines; $r++) { 332 // Match file listing. 333 if (preg_match('/^(\s\s.+\s{6,})/', $res[$r])) { 334 // Split into columns at every six spaces 335 $split1 = preg_split('/\s{6,}/', trim($res[$r])); 336 // If the file name isn't . or .. 337 if ($split1[0] != '.' && $split1[0] != '..') { 338 if (isset($split1[2])) { 339 // If there is a small file size, inf could be split 340 // into 3 cols. 341 $split1[1] .= ' ' . $split1[2]; 342 } 343 // Split file inf at every one or more spaces. 344 $split2 = preg_split('/\s+/', $split1[1]); 345 if (is_numeric($split2[0])) { 346 // If there is no file attr, shift cols over. 347 array_unshift($split2, ''); 348 } 349 $my_name = $split1[0]; 350 351 // Filter out dotfiles if they aren't wanted. 352 if (!$dotfiles && substr($my_name, 0, 1) == '.') { 353 continue; 354 } 355 356 $my_size = $split2[1]; 357 $ext_name = explode('.', $my_name); 358 359 if ((strpos($split2[0], 'D') !== false)) { 360 $my_type = '**dir'; 361 $my_size = -1; 362 } else { 363 $my_type = VFS::strtolower($ext_name[count($ext_name) - 1]); 364 } 365 $my_date = strtotime($split2[4] . ' ' . $split2[3] . ' ' . 366 $split2[6] . ' ' . $split2[5]); 367 $filedata = array('owner' => '', 368 'group' => '', 369 'perms' => '', 370 'name' => $my_name, 371 'type' => $my_type, 372 'date' => $my_date, 373 'size' => $my_size); 374 // watch for filters and dironly 375 if ($this->_filterMatch($filter, $my_name)) { 376 unset($file); 377 continue; 378 } 379 if ($dironly && $my_type !== '**dir') { 380 unset($file); 381 continue; 382 } 383 384 $files[$filedata['name']] = $filedata; 385 } 386 } 387 } 388 return $files; 389 } 390 391 /** 392 * Returns a sorted list of folders in specified directory. 393 * 394 * @param string $path The path of the directory to get the 395 * directory list for. 396 * @param mixed $filter Hash of items to filter based on folderlist. 397 * @param boolean $dotfolders Include dotfolders? Irrelevant for SMB. 398 * 399 * @return boolean|PEAR_Error Folder list on success or a PEAR_Error on 400 * failure. 401 */ 402 function listFolders($path = '', $filter = null, $dotfolders = true) 403 { 404 $folders = array(); 405 $folder = array(); 406 407 $folderList = $this->listFolder($path, null, $dotfolders, true); 408 if (is_a($folderList, 'PEAR_Error')) { 409 return $folderList; 410 } 411 412 // dirname will strip last component from path, even on a directory 413 $folder['val'] = dirname($path); 414 $folder['abbrev'] = '..'; 415 $folder['label'] = '..'; 416 417 $folders[$folder['val']] = $folder; 418 419 foreach ($folderList as $files) { 420 $folder['val'] = $this->_getPath($path, $files['name']); 421 $folder['abbrev'] = $files['name']; 422 $folder['label'] = $folder['val']; 423 424 $folders[$folder['val']] = $folder; 425 } 426 427 ksort($folders); 428 return $folders; 429 } 430 431 /** 432 * Copies a file through the backend. 433 * 434 * @param string $path The path to store the file in. 435 * @param string $name The filename to use. 436 * @param string $dest The destination of the file. 437 * 438 * @return boolean|PEAR_Error True on success or a PEAR_Error on failure. 439 */ 440 function copy($path, $name, $dest) 441 { 442 $orig = $this->_getPath($path, $name); 443 if (preg_match('|^' . preg_quote($orig) . '/?$|', $dest)) { 444 return PEAR::raiseError(_("Cannot copy file(s) - source and destination are the same.")); 445 } 446 447 $fileCheck = $this->listFolder($dest, null, true); 448 foreach ($fileCheck as $file) { 449 if ($file['name'] == $name) { 450 return PEAR::raiseError(sprintf(_("%s already exists."), 451 $this->_getPath($dest, $name))); 452 } 453 } 454 455 $isDir = false; 456 $dirCheck = $this->listFolder($path, null, false); 457 foreach ($dirCheck as $file) { 458 if ($file['name'] == $name && $file['type'] == '**dir') { 459 $isDir = true; 460 break; 461 } 462 } 463 464 if ($isDir) { 465 $result = $this->createFolder($dest, $name); 466 467 if (is_a($result, 'PEAR_Error')) { 468 return $result; 469 } 470 471 $file_list = $this->listFolder($orig); 472 foreach ($file_list as $file) { 473 $result = $this->copy($orig, 474 $file['name'], 475 $this->_getPath($dest, $name)); 476 if (is_a($result, 'PEAR_Error')) { 477 return $result; 478 } 479 } 480 } else { 481 $tmpFile = $this->_createTempFile($path, $name); 482 if (is_a($tmpFile, 'PEAR_Error')) { 483 return PEAR::raiseError(sprintf(_("Failed to retrieve: %s"), $orig)); 484 } 485 486 $res = $this->write($dest, $name, $tmpFile); 487 unlink($tmpFile); 488 if (is_a($res, 'PEAR_Error')) { 489 return PEAR::raiseError(sprintf(_("Copy failed: %s"), 490 $this->_getPath($dest, $name))); 491 } 492 } 493 494 return true; 495 } 496 497 /** 498 * Moves a file through the backend. 499 * 500 * @param string $path The path to store the file in. 501 * @param string $name The filename to use. 502 * @param string $dest The destination of the file. 503 * 504 * @return boolean|PEAR_Error True on success or a PEAR_Error on failure. 505 */ 506 function move($path, $name, $dest) 507 { 508 $orig = $this->_getPath($path, $name); 509 if (preg_match('|^' . preg_quote($orig) . '/?$|', $dest)) { 510 return PEAR::raiseError(_("Cannot move file(s) - destination is within source.")); 511 } 512 513 $fileCheck = $this->listFolder($dest, null, true); 514 foreach ($fileCheck as $file) { 515 if ($file['name'] == $name) { 516 return PEAR::raiseError(sprintf(_("%s already exists."), 517 $this->_getPath($dest, $name))); 518 } 519 } 520 521 $err = $this->rename($path, $name, $dest, $name); 522 if (is_a($err, 'PEAR_Error')) { 523 return PEAR::raiseError(sprintf(_("Failed to move to \"%s\"."), 524 $this->_getPath($dest, $name))); 525 } 526 return true; 527 } 528 529 /** 530 * Returns the full path of an item. 531 * 532 * @param string $path The path of directory of the item. 533 * @param string $name The name of the item. 534 * 535 * @return mixed Full path when $path isset and just $name when not set. 536 */ 537 function _getPath($path, $name) 538 { 539 if ($path !== '') { 540 return ($path . '/' . $name); 541 } 542 return $name; 543 } 544 545 /** 546 * Replacement for escapeshellcmd(), variable length args, as we only want 547 * certain characters escaped. 548 * 549 * @access private 550 * 551 * @param array $array Strings to escape. 552 * 553 * @return array 554 */ 555 function _escapeShellCommand() 556 { 557 $ret = array(); 558 $args = func_get_args(); 559 foreach ($args as $arg) { 560 $ret[] = str_replace(array(';', '\\'), array('\;', '\\\\'), $arg); 561 } 562 return $ret; 563 } 564 565 /** 566 * Executes a command and returns output lines in array. 567 * 568 * @access private 569 * 570 * @param string $cmd Command to be executed 571 * 572 * @return mixed Array on success, false on failure. 573 */ 574 function _execute($cmd) 575 { 576 $cmd = str_replace('"-U%"', '-N', $cmd); 577 exec($cmd, $out, $ret); 578 579 // In some cases, (like trying to delete a nonexistant file), 580 // smbclient will return success (at least on 2.2.7 version I'm 581 // testing on). So try to match error strings, even after success. 582 if ($ret != 0) { 583 $err = ''; 584 foreach ($out as $line) { 585 if (strpos($line, 'Usage:') === 0) { 586 $err = 'Command syntax incorrect'; 587 break; 588 } 589 if (strpos($line, 'ERRSRV') !== false || 590 strpos($line, 'ERRDOS') !== false) { 591 $err = preg_replace('/.*\((.+)\).*/', '\\1', $line); 592 if (!$err) { 593 $err = $line; 594 } 595 break; 596 } 597 } 598 if (!$err) { 599 $err = $out ? $out[count($out) - 1] : $ret; 600 } 601 return PEAR::raiseError($err); 602 } 603 604 // Check for errors even on success. 605 $err = ''; 606 foreach ($out as $line) { 607 if (strpos($line, 'NT_STATUS_NO_SUCH_FILE') !== false || 608 strpos($line, 'NT_STATUS_OBJECT_NAME_NOT_FOUND') !== false) { 609 $err = _("No such file"); 610 break; 611 } elseif (strpos($line, 'NT_STATUS_ACCESS_DENIED') !== false) { 612 $err = _("Permission Denied"); 613 break; 614 } 615 } 616 617 if ($err) { 618 return PEAR::raiseError($err); 619 } 620 621 return $out; 622 } 623 624 /** 625 * Executes SMB commands - without authentication - and returns output 626 * lines in array. 627 * 628 * @access private 629 * 630 * @param array $path Base path for command. 631 * @param array $cmd Commands to be executed. 632 * 633 * @return mixed Array on success, false on failure. 634 */ 635 function _command($path, $cmd) 636 { 637 list($share) = $this->_escapeShellCommand($this->_params['share']); 638 putenv('PASSWD=' . $this->_params['password']); 639 $ipoption = (isset($this->_params['ipaddress'])) ? (' -I ' . $this->_params['ipaddress']) : null; 640 $fullcmd = $this->_params['smbclient'] . 641 ' "//' . $this->_params['hostspec'] . '/' . $share . '"' . 642 ' "-p' . $this->_params['port'] . '"' . 643 ' "-U' . $this->_params['username'] . '"' . 644 ' -D "' . $path . '" ' . 645 $ipoption . 646 ' -c "'; 647 foreach ($cmd as $c) { 648 $fullcmd .= $c . ";"; 649 } 650 $fullcmd .= '"'; 651 return $this->_execute($fullcmd); 652 } 653 654 /** 655 * Retrieves a file from the VFS and stores it to a temporary file. 656 * 657 * @access private 658 * 659 * @param string $path The pathname to the file. 660 * @param string $name The filename to retrieve. 661 * 662 * @return mixed The temporary filename or a PEAR_Error on failure. 663 */ 664 function _createTempFile($path, $name) 665 { 666 list($path, $name) = $this->_escapeShellCommand($path, $name); 667 $temp = $this->_getTempFile(); 668 $cmd = array('get \"' . $name . '\" ' . $temp); 669 $err = $this->_command($path, $cmd); 670 if (is_a($err, 'PEAR_Error')) { 671 return $err; 672 } 673 if (!file_exists($temp)) { 674 return PEAR::raiseError(sprintf(_("Unable to open VFS file \"%s\"."), $this->_getPath($path, $name))); 675 } 676 return $temp; 677 } 678 679 }
titre
Description
Corps
titre
Description
Corps
titre
Description
Corps
titre
Corps
Généré le : Sun Feb 25 18:01:28 2007 | par Balluche grâce à PHPXref 0.7 |