1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35:
36: class Horde_Vfs_Smb extends Horde_Vfs_Base
37: {
38: 39: 40: 41: 42:
43: protected $_credentials = array('username', 'password');
44:
45: 46: 47: 48: 49:
50: protected function _connect()
51: {
52: try {
53: $this->_command('', array('quit'));
54: } catch (Horde_Vfs_Exception $e) {
55: throw new Horde_Vfs_Exception('Authentication to the SMB server failed.');
56: }
57: }
58:
59: 60: 61: 62: 63: 64: 65: 66: 67:
68: public function size($path, $name)
69: {
70: $file = $this->readFile($path, $name);
71: return filesize($file);
72: }
73:
74: 75: 76: 77: 78: 79: 80: 81:
82: public function read($path, $name)
83: {
84: $file = $this->readFile($path, $name);
85: $size = filesize($file);
86: return ($size === 0)
87: ? ''
88: : file_get_contents($file);
89: }
90:
91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102:
103: public function readFile($path, $name)
104: {
105:
106:
107: if (!($localFile = Horde_Util::getTempFile('vfs'))) {
108: throw new Horde_Vfs_Exception('Unable to create temporary file.');
109: }
110:
111: list($path, $name) = $this->_escapeShellCommand($path, $name);
112: $cmd = array('get \"' . $name . '\" ' . $localFile);
113: $this->_command($path, $cmd);
114: if (!file_exists($localFile)) {
115: throw new Horde_Vfs_Exception(sprintf('Unable to open VFS file "%s".', $this->_getPath($path, $name)));
116: }
117:
118: clearstatcache();
119:
120: return $localFile;
121: }
122:
123: 124: 125: 126: 127: 128: 129: 130:
131: public function readStream($path, $name)
132: {
133: return fopen($this->readFile($path, $name),OS_WINDOWS ? 'rb' : 'r');
134: }
135:
136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146:
147: public function write($path, $name, $tmpFile, $autocreate = false)
148: {
149:
150: $name = str_replace('"', "'", $name);
151:
152: list($path, $name) = $this->_escapeShellCommand($path, $name);
153: $cmd = array('put \"' . $tmpFile . '\" \"' . $name . '\"');
154:
155: if ($autocreate) {
156: $this->autocreatePath($path);
157: }
158:
159: $this->_command($path, $cmd);
160: }
161:
162: 163: 164: 165: 166: 167: 168: 169: 170: 171:
172: public function writeData($path, $name, $data, $autocreate = false)
173: {
174: $tmpFile = Horde_Util::getTempFile('vfs');
175: file_put_contents($tmpFile, $data);
176: try {
177: $this->write($path, $name, $tmpFile, $autocreate);
178: unlink($tmpFile);
179: } catch (Horde_Vfs_Exception $e) {
180: unlink($tmpFile);
181: throw $e;
182: }
183: }
184:
185: 186: 187: 188: 189: 190: 191: 192:
193: public function deleteFile($path, $name)
194: {
195:
196:
197: if (substr($path, -1) != '/') {
198: $path .= '/';
199: }
200:
201: list($path, $name) = $this->_escapeShellCommand($path, $name);
202: $cmd = array('del \"' . $name . '\"');
203: $this->_command($path, $cmd);
204: }
205:
206: 207: 208: 209: 210: 211: 212: 213:
214: public function isFolder($path, $name)
215: {
216: list($path, $name) = $this->_escapeShellCommand($path, $name);
217: $cmd = array('quit');
218: try {
219: $this->_command($this->_getPath($path, $name), array('quit'));
220: return true;
221: } catch (Horde_Vfs_Exception $e) {
222: return false;
223: }
224: }
225:
226: 227: 228: 229: 230: 231: 232: 233: 234:
235: public function deleteFolder($path, $name, $recursive = false)
236: {
237:
238:
239: if (substr($path, -1) != '/') {
240: $path .= '/';
241: }
242:
243: if (!$this->isFolder($path, $name)) {
244: throw new Horde_Vfs_Exception(sprintf('"%s" is not a directory.', $path . '/' . $name));
245: }
246:
247: $file_list = $this->listFolder($this->_getPath($path, $name));
248:
249: if ($file_list && !$recursive) {
250: throw new Horde_Vfs_Exception(sprintf('Unable to delete "%s", the directory is not empty.', $this->_getPath($path, $name)));
251: }
252:
253: foreach ($file_list as $file) {
254: if ($file['type'] == '**dir') {
255: $this->deleteFolder($this->_getPath($path, $name), $file['name'], $recursive);
256: } else {
257: $this->deleteFile($this->_getPath($path, $name), $file['name']);
258: }
259: }
260:
261:
262: list($path, $name) = $this->_escapeShellCommand($path, $name);
263: $cmd = array('rmdir \"' . $name . '\"');
264:
265: try {
266: $this->_command($path, $cmd);
267: } catch (Horde_Vfs_Exception $e) {
268: throw new Horde_Vfs_Exception(sprintf('Unable to delete VFS folder "%s".', $this->_getPath($path, $name)));
269: }
270: }
271:
272: 273: 274: 275: 276: 277: 278: 279: 280: 281:
282: public function rename($oldpath, $oldname, $newpath, $newname)
283: {
284: $this->autocreatePath($newpath);
285:
286:
287:
288: $oldname = str_replace('"', "'", trim($oldname, '/'));
289: $newname = str_replace('"', "'", trim($newname, '/'));
290:
291: if (empty($oldname)) {
292: throw new Horde_Vfs_Exception('Unable to rename VFS file to same name.');
293: }
294:
295: 296:
297: if (!empty($oldpath)) {
298: $oldpath .= '/';
299: }
300: if (!empty($newpath)) {
301: $newpath .= '/';
302: }
303:
304: list($file, $name) = $this->_escapeShellCommand($oldname, $newname);
305: $cmd = array('rename \"' . str_replace('/', '\\\\', $oldpath) . $file . '\" \"' .
306: str_replace('/', '\\\\', $newpath) . $name . '\"');
307:
308: try {
309: $this->_command('', $cmd);
310: } catch (Horde_Vfs_Exception $e) {
311: throw new Horde_Vfs_Exception(sprintf('Unable to rename VFS file "%s".', $this->_getPath($oldpath, $oldname)));
312: }
313: }
314:
315: 316: 317: 318: 319: 320: 321: 322:
323: public function createFolder($path, $name)
324: {
325:
326:
327: if (substr($path, -1) != '/') {
328: $path .= '/';
329: }
330:
331:
332: $name = str_replace('"', "'", $name);
333:
334: list($dir, $mkdir) = $this->_escapeShellCommand($path, $name);
335: $cmd = array('mkdir \"' . $mkdir . '\"');
336:
337: try {
338: $this->_command($dir, $cmd);
339: } catch (Horde_Vfs_Exception $e) {
340: throw new Horde_Vfs_Exception(sprintf('Unable to create VFS folder "%s".', $this->_getPath($path, $name)));
341: }
342: }
343:
344: 345: 346: 347: 348: 349: 350: 351: 352: 353: 354: 355: 356: 357:
358: public function listFolder($path = '', $filter = null, $dotfiles = true,
359: $dironly = false, $recursive = false)
360: {
361: list($path) = $this->_escapeShellCommand($path);
362: return $this->parseListing($this->_command($path, array('ls')), $filter, $dotfiles, $dironly);
363: }
364:
365: 366:
367: public function parseListing($res, $filter, $dotfiles, $dironly)
368: {
369: $num_lines = count($res);
370: $files = array();
371: for ($r = 0; $r < $num_lines; $r++) {
372:
373: if (!preg_match('/^ (.+?) +([A-Z]*) +(\d+) (\w\w\w \w\w\w [ \d]\d \d\d:\d\d:\d\d \d\d\d\d)$/', $res[$r], $match)) {
374: continue;
375: }
376:
377:
378: if ($match[1] == '.' || $match[1] == '..') {
379: continue;
380: }
381:
382: $my_name = $match[1];
383:
384:
385: if (!$dotfiles && substr($my_name, 0, 1) == '.') {
386: continue;
387: }
388:
389: $my_size = $match[3];
390: $ext_name = explode('.', $my_name);
391:
392: if ((strpos($match[2], 'D') !== false)) {
393: $my_type = '**dir';
394: $my_size = -1;
395: } else {
396: $my_type = Horde_String::lower($ext_name[count($ext_name) - 1]);
397: }
398: $my_date = strtotime($match[4]);
399: $filedata = array('owner' => '',
400: 'group' => '',
401: 'perms' => '',
402: 'name' => $my_name,
403: 'type' => $my_type,
404: 'date' => $my_date,
405: 'size' => $my_size);
406:
407: if ($this->_filterMatch($filter, $my_name)) {
408: unset($file);
409: continue;
410: }
411: if ($dironly && $my_type !== '**dir') {
412: unset($file);
413: continue;
414: }
415:
416: $files[$filedata['name']] = $filedata;
417: }
418:
419: return $files;
420: }
421:
422: 423: 424: 425: 426: 427: 428: 429: 430: 431: 432:
433: public function listFolders($path = '', $filter = null, $dotfolders = true)
434: {
435:
436: $folder = array(
437: 'val' => dirname($path),
438: 'abbrev' => '..',
439: 'label' => '..'
440: );
441: $folders = array($folder['val'] => $folder);
442:
443: $folderList = $this->listFolder($path, null, $dotfolders, true);
444: foreach ($folderList as $files) {
445: $folders[$folder['val']] = array(
446: 'val' => $this->_getPath($path, $files['name']),
447: 'abbrev' => $files['name'],
448: 'label' => $folder['val']
449: );
450: }
451:
452: ksort($folders);
453: return $folders;
454: }
455:
456: 457: 458: 459: 460: 461: 462: 463: 464: 465:
466: public function copy($path, $name, $dest, $autocreate = false)
467: {
468: $orig = $this->_getPath($path, $name);
469: if (preg_match('|^' . preg_quote($orig) . '/?$|', $dest)) {
470: throw new Horde_Vfs_Exception('Cannot copy file(s) - source and destination are the same.');
471: }
472:
473: if ($autocreate) {
474: $this->autocreatePath($dest);
475: }
476:
477: foreach ($this->listFolder($dest, null, true) as $file) {
478: if ($file['name'] == $name) {
479: throw new Horde_Vfs_Exception(sprintf('%s already exists.', $this->_getPath($dest, $name)));
480: }
481: }
482:
483: if ($this->isFolder($path, $name)) {
484: $this->_copyRecursive($path, $name, $dest);
485: } else {
486: try {
487: $this->write($dest, $name, $this->readFile($path, $name));
488: } catch (Horde_Vfs_Exception $e) {
489: throw new Horde_Vfs_Exception(sprintf('Copy failed: %s', $this->_getPath($dest, $name)));
490: }
491: }
492: }
493:
494: 495: 496: 497: 498: 499: 500: 501: 502: 503:
504: public function move($path, $name, $dest, $autocreate = false)
505: {
506: $orig = $this->_getPath($path, $name);
507: if (preg_match('|^' . preg_quote($orig) . '/?$|', $dest)) {
508: throw new Horde_Vfs_Exception('Cannot copy file(s) - destination is within source.');
509: }
510:
511: if ($autocreate) {
512: $this->autocreatePath($dest);
513: }
514:
515: foreach ($this->listFolder($dest, null, true) as $file) {
516: if ($file['name'] == $name) {
517: throw new Horde_Vfs_Exception(sprintf('%s already exists.', $this->_getPath($dest, $name)));
518: }
519: }
520:
521: try {
522: $this->rename($path, $name, $dest, $name);
523: } catch (Horde_Vfs_Exception $e) {
524: throw new Horde_Vfs_Exception(sprintf('Failed to move to "%s".', $this->_getPath($dest, $name)));
525: }
526: }
527:
528: 529: 530: 531: 532: 533: 534: 535:
536: protected function _escapeShellCommand()
537: {
538: $ret = array();
539: $args = func_get_args();
540: foreach ($args as $arg) {
541: $ret[] = str_replace(array(';', '\\'), array('\;', '\\\\'), $arg);
542: }
543: return $ret;
544: }
545:
546: 547: 548: 549: 550: 551: 552: 553:
554: protected function _execute($cmd)
555: {
556: $cmd = str_replace('"-U%"', '-N', $cmd);
557: $proc = proc_open(
558: $cmd,
559: array(1 => array('pipe', 'w'), 2 => array('pipe', 'w')),
560: $pipes);
561: if (!is_resource($proc)) {
562:
563: throw new Horde_Vfs_Exception('Failed to call proc_open().');
564: }
565: $out = explode("\n", stream_get_contents($pipes[1]));
566: $error = explode("\n", stream_get_contents($pipes[2]));
567: $ret = proc_close($proc);
568:
569:
570:
571:
572: if ($ret != 0) {
573: $err = '';
574: foreach ($error as $line) {
575: if (strpos($line, 'Usage:') === 0) {
576: $err = 'Command syntax incorrect';
577: break;
578: }
579: if (strpos($line, 'ERRSRV') !== false ||
580: strpos($line, 'ERRDOS') !== false) {
581: $err = preg_replace('/.*\((.+)\).*/', '\\1', $line);
582: if (!$err) {
583: $err = $line;
584: }
585: break;
586: }
587: }
588: if (!$err) {
589: $err = $out ? $out[count($out) - 1] : $ret;
590: }
591:
592: throw new Horde_Vfs_Exception($err);
593: }
594:
595:
596: $err = '';
597: foreach ($out as $line) {
598: if (strpos($line, 'NT_STATUS_NO_SUCH_FILE') !== false ||
599: strpos($line, 'NT_STATUS_OBJECT_NAME_NOT_FOUND') !== false) {
600: $err = Horde_Vfs_Translation::t("No such file");
601: break;
602: } elseif (strpos($line, 'NT_STATUS_ACCESS_DENIED') !== false) {
603: $err = Horde_Vfs_Translation::t("Permission Denied");
604: break;
605: }
606: }
607:
608: if ($err) {
609: throw new Horde_Vfs_Exception($err);
610: }
611:
612: return $out;
613: }
614:
615: 616: 617: 618: 619: 620: 621: 622: 623: 624:
625: protected function _command($path, $cmd)
626: {
627: list($share) = $this->_escapeShellCommand($this->_params['share']);
628: putenv('PASSWD=' . $this->_params['password']);
629: $ipoption = (isset($this->_params['ipaddress'])) ? (' -I ' . $this->_params['ipaddress']) : null;
630: $fullcmd = $this->_params['smbclient'] .
631: ' "//' . $this->_params['hostspec'] . '/' . $share . '"' .
632: ' "-p' . $this->_params['port'] . '"' .
633: ' "-U' . $this->_params['username'] . '"' .
634: ' -D "' . $path . '" ' .
635: $ipoption .
636: ' -c "';
637: foreach ($cmd as $c) {
638: $fullcmd .= $c . ";";
639: }
640: $fullcmd .= '"';
641:
642: return $this->_execute($fullcmd);
643: }
644:
645: }
646: