PageRenderTime 54ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/smb4php/smb.php

https://github.com/guruz/3rdparty
PHP | 482 lines | 384 code | 57 blank | 41 comment | 52 complexity | b3c71065cb5420e15a8bd1aeac63b00d MD5 | raw file
Possible License(s): BSD-2-Clause, Apache-2.0, BSD-3-Clause, GPL-2.0, MIT
  1. <?php
  2. ###################################################################
  3. # smb.php
  4. # This class implements a SMB stream wrapper based on 'smbclient'
  5. #
  6. # Date: lun oct 22 10:35:35 CEST 2007
  7. #
  8. # Homepage: http://www.phpclasses.org/smb4php
  9. #
  10. # Copyright (c) 2007 Victor M. Varela <vmvarela@gmail.com>
  11. #
  12. # This program is free software; you can redistribute it and/or
  13. # modify it under the terms of the GNU General Public License
  14. # as published by the Free Software Foundation; either version 2
  15. # of the License, or (at your option) any later version.
  16. #
  17. # This program is distributed in the hope that it will be useful,
  18. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  19. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  20. # GNU General Public License for more details.
  21. #
  22. ###################################################################
  23. define ('SMB4PHP_VERSION', '0.8');
  24. ###################################################################
  25. # CONFIGURATION SECTION - Change for your needs
  26. ###################################################################
  27. define ('SMB4PHP_SMBCLIENT', 'smbclient');
  28. define ('SMB4PHP_SMBOPTIONS', 'TCP_NODELAY IPTOS_LOWDELAY SO_KEEPALIVE SO_RCVBUF=8192 SO_SNDBUF=8192');
  29. define ('SMB4PHP_AUTHMODE', 'arg'); # set to 'env' to use USER enviroment variable
  30. ###################################################################
  31. # SMB - commands that does not need an instance
  32. ###################################################################
  33. $GLOBALS['__smb_cache'] = array ('stat' => array (), 'dir' => array ());
  34. class smb {
  35. function parse_url ($url) {
  36. $pu = parse_url (trim($url));
  37. foreach (array ('domain', 'user', 'pass', 'host', 'port', 'path') as $i) {
  38. if (! isset($pu[$i])) {
  39. $pu[$i] = '';
  40. }
  41. }
  42. if (count ($userdomain = explode (';', urldecode ($pu['user']))) > 1) {
  43. @list ($pu['domain'], $pu['user']) = $userdomain;
  44. }
  45. $path = preg_replace (array ('/^\//', '/\/$/'), '', urldecode ($pu['path']));
  46. list ($pu['share'], $pu['path']) = (preg_match ('/^([^\/]+)\/(.*)/', $path, $regs))
  47. ? array ($regs[1], preg_replace ('/\//', '\\', $regs[2]))
  48. : array ($path, '');
  49. $pu['type'] = $pu['path'] ? 'path' : ($pu['share'] ? 'share' : ($pu['host'] ? 'host' : '**error**'));
  50. if (! ($pu['port'] = intval(@$pu['port']))) {
  51. $pu['port'] = 139;
  52. }
  53. // decode user and password
  54. $pu['user'] = urldecode($pu['user']);
  55. $pu['pass'] = urldecode($pu['pass']);
  56. return $pu;
  57. }
  58. function look ($purl) {
  59. return smb::client ('-L ' . escapeshellarg ($purl['host']), $purl);
  60. }
  61. function execute ($command, $purl) {
  62. return smb::client ('-d 0 '
  63. . escapeshellarg ('//' . $purl['host'] . '/' . $purl['share'])
  64. . ' -c ' . escapeshellarg ($command), $purl
  65. );
  66. }
  67. function client ($params, $purl) {
  68. static $regexp = array (
  69. '^added interface ip=(.*) bcast=(.*) nmask=(.*)$' => 'skip',
  70. 'Anonymous login successful' => 'skip',
  71. '^Domain=\[(.*)\] OS=\[(.*)\] Server=\[(.*)\]$' => 'skip',
  72. '^\tSharename[ ]+Type[ ]+Comment$' => 'shares',
  73. '^\t---------[ ]+----[ ]+-------$' => 'skip',
  74. '^\tServer [ ]+Comment$' => 'servers',
  75. '^\t---------[ ]+-------$' => 'skip',
  76. '^\tWorkgroup[ ]+Master$' => 'workg',
  77. '^\t(.*)[ ]+(Disk|IPC)[ ]+IPC.*$' => 'skip',
  78. '^\tIPC\\\$(.*)[ ]+IPC' => 'skip',
  79. '^\t(.*)[ ]+(Disk)[ ]+(.*)$' => 'share',
  80. '^\t(.*)[ ]+(Printer)[ ]+(.*)$' => 'skip',
  81. '([0-9]+) blocks of size ([0-9]+)\. ([0-9]+) blocks available' => 'skip',
  82. 'Got a positive name query response from ' => 'skip',
  83. '^(session setup failed): (.*)$' => 'error',
  84. '^(.*): ERRSRV - ERRbadpw' => 'error',
  85. '^Error returning browse list: (.*)$' => 'error',
  86. '^tree connect failed: (.*)$' => 'error',
  87. '^(Connection to .* failed)$' => 'error',
  88. '^NT_STATUS_(.*) ' => 'error',
  89. '^NT_STATUS_(.*)\$' => 'error',
  90. 'ERRDOS - ERRbadpath \((.*).\)' => 'error',
  91. 'cd (.*): (.*)$' => 'error',
  92. '^cd (.*): NT_STATUS_(.*)' => 'error',
  93. '^\t(.*)$' => 'srvorwg',
  94. '^([0-9]+)[ ]+([0-9]+)[ ]+(.*)$' => 'skip',
  95. '^Job ([0-9]+) cancelled' => 'skip',
  96. '^[ ]+(.*)[ ]+([0-9]+)[ ]+(Mon|Tue|Wed|Thu|Fri|Sat|Sun)[ ](Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)[ ]+([0-9]+)[ ]+([0-9]{2}:[0-9]{2}:[0-9]{2})[ ]([0-9]{4})$' => 'files',
  97. '^message start: ERRSRV - (ERRmsgoff)' => 'error'
  98. );
  99. if (SMB4PHP_AUTHMODE == 'env') {
  100. putenv("USER={$purl['user']}%{$purl['pass']}");
  101. $auth = '';
  102. } else {
  103. $auth = ($purl['user'] <> '' ? (' -U ' . escapeshellarg ($purl['user'] . '%' . $purl['pass'])) : '');
  104. }
  105. if ($purl['domain'] <> '') {
  106. $auth .= ' -W ' . escapeshellarg ($purl['domain']);
  107. }
  108. $port = ($purl['port'] <> 139 ? ' -p ' . escapeshellarg ($purl['port']) : '');
  109. $options = '-O ' . escapeshellarg(SMB4PHP_SMBOPTIONS);
  110. // this put env is necessary to read the output of smbclient correctly
  111. $old_locale = getenv('LC_ALL');
  112. putenv('LC_ALL=en_US.UTF-8');
  113. $output = popen (SMB4PHP_SMBCLIENT." -N {$auth} {$options} {$port} {$options} {$params} 2>/dev/null", 'r');
  114. $info = array ();
  115. $info['info']= array ();
  116. $mode = '';
  117. while ($line = fgets ($output, 4096)) {
  118. list ($tag, $regs, $i) = array ('skip', array (), array ());
  119. reset ($regexp);
  120. foreach ($regexp as $r => $t) if (preg_match ('/'.$r.'/', $line, $regs)) {
  121. $tag = $t;
  122. break;
  123. }
  124. switch ($tag) {
  125. case 'skip': continue;
  126. case 'shares': $mode = 'shares'; break;
  127. case 'servers': $mode = 'servers'; break;
  128. case 'workg': $mode = 'workgroups'; break;
  129. case 'share':
  130. list($name, $type) = array (
  131. trim(substr($line, 1, 15)),
  132. trim(strtolower(substr($line, 17, 10)))
  133. );
  134. $i = ($type <> 'disk' && preg_match('/^(.*) Disk/', $line, $regs))
  135. ? array(trim($regs[1]), 'disk')
  136. : array($name, 'disk');
  137. break;
  138. case 'srvorwg':
  139. list ($name, $master) = array (
  140. strtolower(trim(substr($line,1,21))),
  141. strtolower(trim(substr($line, 22)))
  142. );
  143. $i = ($mode == 'servers') ? array ($name, "server") : array ($name, "workgroup", $master);
  144. break;
  145. case 'files':
  146. list ($attr, $name) = preg_match ("/^(.*)[ ]+([D|A|H|S|R]+)$/", trim ($regs[1]), $regs2)
  147. ? array (trim ($regs2[2]), trim ($regs2[1]))
  148. : array ('', trim ($regs[1]));
  149. list ($his, $im) = array (
  150. explode(':', $regs[6]), 1 + strpos("JanFebMarAprMayJunJulAugSepOctNovDec", $regs[4]) / 3);
  151. $i = ($name <> '.' && $name <> '..')
  152. ? array (
  153. $name,
  154. (strpos($attr,'D') === FALSE) ? 'file' : 'folder',
  155. 'attr' => $attr,
  156. 'size' => intval($regs[2]),
  157. 'time' => mktime ($his[0], $his[1], $his[2], $im, $regs[5], $regs[7])
  158. )
  159. : array();
  160. break;
  161. case 'error':
  162. if(substr($regs[0],0,22)=='NT_STATUS_NO_SUCH_FILE'){
  163. return false;
  164. }elseif(substr($regs[0],0,31)=='NT_STATUS_OBJECT_NAME_COLLISION'){
  165. return false;
  166. }elseif(substr($regs[0],0,31)=='NT_STATUS_OBJECT_PATH_NOT_FOUND'){
  167. return false;
  168. }elseif(substr($regs[0],0,29)=='NT_STATUS_FILE_IS_A_DIRECTORY'){
  169. return false;
  170. }
  171. trigger_error($regs[0].' params('.$params.')', E_USER_ERROR);
  172. }
  173. if ($i) switch ($i[1]) {
  174. case 'file':
  175. case 'folder': $info['info'][$i[0]] = $i;
  176. case 'disk':
  177. case 'server':
  178. case 'workgroup': $info[$i[1]][] = $i[0];
  179. }
  180. }
  181. pclose($output);
  182. // restore previous locale
  183. if ($old_locale===false) {
  184. putenv('LC_ALL');
  185. } else {
  186. putenv('LC_ALL='.$old_locale);
  187. }
  188. return $info;
  189. }
  190. # stats
  191. function url_stat ($url, $flags = STREAM_URL_STAT_LINK) {
  192. if ($s = smb::getstatcache($url)) {
  193. return $s;
  194. }
  195. list ($stat, $pu) = array (array (), smb::parse_url ($url));
  196. switch ($pu['type']) {
  197. case 'host':
  198. if ($o = smb::look ($pu))
  199. $stat = stat ("/tmp");
  200. else
  201. trigger_error ("url_stat(): list failed for host '{$pu['host']}'", E_USER_WARNING);
  202. break;
  203. case 'share':
  204. if ($o = smb::look ($pu)) {
  205. $found = FALSE;
  206. $lshare = strtolower ($pu['share']); # fix by Eric Leung
  207. foreach ($o['disk'] as $s) if ($lshare == strtolower($s)) {
  208. $found = TRUE;
  209. $stat = stat ("/tmp");
  210. break;
  211. }
  212. if (! $found)
  213. trigger_error ("url_stat(): disk resource '{$lshare}' not found in '{$pu['host']}'", E_USER_WARNING);
  214. }
  215. break;
  216. case 'path':
  217. if ($o = smb::execute ('dir "'.$pu['path'].'"', $pu)) {
  218. $p = explode('\\', $pu['path']);
  219. $name = $p[count($p)-1];
  220. if (isset ($o['info'][$name])) {
  221. $stat = smb::addstatcache ($url, $o['info'][$name]);
  222. } else {
  223. trigger_error ("url_stat(): path '{$pu['path']}' not found", E_USER_WARNING);
  224. }
  225. } else {
  226. return false;
  227. // trigger_error ("url_stat(): dir failed for path '{$pu['path']}'", E_USER_WARNING);
  228. }
  229. break;
  230. default: trigger_error ('error in URL', E_USER_ERROR);
  231. }
  232. return $stat;
  233. }
  234. function addstatcache ($url, $info) {
  235. $url = str_replace('//', '/', $url);
  236. $url = rtrim($url, '/');
  237. global $__smb_cache;
  238. $is_file = (strpos ($info['attr'],'D') === FALSE);
  239. $s = ($is_file) ? stat ('/etc/passwd') : stat ('/tmp');
  240. $s[7] = $s['size'] = $info['size'];
  241. $s[8] = $s[9] = $s[10] = $s['atime'] = $s['mtime'] = $s['ctime'] = $info['time'];
  242. return $__smb_cache['stat'][$url] = $s;
  243. }
  244. function getstatcache ($url) {
  245. $url = str_replace('//', '/', $url);
  246. $url = rtrim($url, '/');
  247. global $__smb_cache;
  248. return isset ($__smb_cache['stat'][$url]) ? $__smb_cache['stat'][$url] : FALSE;
  249. }
  250. function clearstatcache ($url='') {
  251. $url = str_replace('//', '/', $url);
  252. $url = rtrim($url, '/');
  253. global $__smb_cache;
  254. if ($url == '') $__smb_cache['stat'] = array (); else unset ($__smb_cache['stat'][$url]);
  255. }
  256. # commands
  257. function unlink ($url) {
  258. $pu = smb::parse_url($url);
  259. if ($pu['type'] <> 'path') trigger_error('unlink(): error in URL', E_USER_ERROR);
  260. smb::clearstatcache ($url);
  261. smb_stream_wrapper::cleardircache (dirname($url));
  262. return smb::execute ('del "'.$pu['path'].'"', $pu);
  263. }
  264. function rename ($url_from, $url_to) {
  265. list ($from, $to) = array (smb::parse_url($url_from), smb::parse_url($url_to));
  266. if ($from['host'] <> $to['host'] ||
  267. $from['share'] <> $to['share'] ||
  268. $from['user'] <> $to['user'] ||
  269. $from['pass'] <> $to['pass'] ||
  270. $from['domain'] <> $to['domain']) {
  271. trigger_error('rename(): FROM & TO must be in same server-share-user-pass-domain', E_USER_ERROR);
  272. }
  273. if ($from['type'] <> 'path' || $to['type'] <> 'path') {
  274. trigger_error('rename(): error in URL', E_USER_ERROR);
  275. }
  276. smb::clearstatcache ($url_from);
  277. return smb::execute ('rename "'.$from['path'].'" "'.$to['path'].'"', $to);
  278. }
  279. function mkdir ($url, $mode, $options) {
  280. $pu = smb::parse_url($url);
  281. if ($pu['type'] <> 'path') trigger_error('mkdir(): error in URL', E_USER_ERROR);
  282. return smb::execute ('mkdir "'.$pu['path'].'"', $pu)!==false;
  283. }
  284. function rmdir ($url) {
  285. $pu = smb::parse_url($url);
  286. if ($pu['type'] <> 'path') trigger_error('rmdir(): error in URL', E_USER_ERROR);
  287. smb::clearstatcache ($url);
  288. smb_stream_wrapper::cleardircache (dirname($url));
  289. return smb::execute ('rmdir "'.$pu['path'].'"', $pu)!==false;
  290. }
  291. }
  292. ###################################################################
  293. # SMB_STREAM_WRAPPER - class to be registered for smb:// URLs
  294. ###################################################################
  295. class smb_stream_wrapper extends smb {
  296. # variables
  297. private $stream, $url, $parsed_url = array (), $mode, $tmpfile;
  298. private $need_flush = FALSE;
  299. private $dir = array (), $dir_index = -1;
  300. # directories
  301. function dir_opendir ($url, $options) {
  302. if ($d = $this->getdircache ($url)) {
  303. $this->dir = $d;
  304. $this->dir_index = 0;
  305. return TRUE;
  306. }
  307. $pu = smb::parse_url ($url);
  308. switch ($pu['type']) {
  309. case 'host':
  310. if ($o = smb::look ($pu)) {
  311. $this->dir = $o['disk'];
  312. $this->dir_index = 0;
  313. } else {
  314. trigger_error ("dir_opendir(): list failed for host '{$pu['host']}'", E_USER_WARNING);
  315. return false;
  316. }
  317. break;
  318. case 'share':
  319. case 'path':
  320. if (is_array($o = smb::execute ('dir "'.$pu['path'].'\*"', $pu))) {
  321. $this->dir = array_keys($o['info']);
  322. $this->dir_index = 0;
  323. $this->adddircache ($url, $this->dir);
  324. if(substr($url,-1,1)=='/'){
  325. $url=substr($url,0,-1);
  326. }
  327. foreach ($o['info'] as $name => $info) {
  328. smb::addstatcache($url . '/' . $name, $info);
  329. }
  330. } else {
  331. trigger_error ("dir_opendir(): dir failed for path '".$pu['path']."'", E_USER_WARNING);
  332. return false;
  333. }
  334. break;
  335. default:
  336. trigger_error ('dir_opendir(): error in URL', E_USER_ERROR);
  337. return false;
  338. }
  339. return TRUE;
  340. }
  341. function dir_readdir () {
  342. return ($this->dir_index < count($this->dir)) ? $this->dir[$this->dir_index++] : FALSE;
  343. }
  344. function dir_rewinddir () { $this->dir_index = 0; }
  345. function dir_closedir () { $this->dir = array(); $this->dir_index = -1; return TRUE; }
  346. # cache
  347. function adddircache ($url, $content) {
  348. $url = str_replace('//', '/', $url);
  349. $url = rtrim($url, '/');
  350. global $__smb_cache;
  351. return $__smb_cache['dir'][$url] = $content;
  352. }
  353. function getdircache ($url) {
  354. $url = str_replace('//', '/', $url);
  355. $url = rtrim($url, '/');
  356. global $__smb_cache;
  357. return isset ($__smb_cache['dir'][$url]) ? $__smb_cache['dir'][$url] : FALSE;
  358. }
  359. function cleardircache ($url='') {
  360. $url = str_replace('//', '/', $url);
  361. $url = rtrim($url, '/');
  362. global $__smb_cache;
  363. if ($url == ''){
  364. $__smb_cache['dir'] = array ();
  365. }else{
  366. unset ($__smb_cache['dir'][$url]);
  367. }
  368. }
  369. # streams
  370. function stream_open ($url, $mode, $options, $opened_path) {
  371. $this->url = $url;
  372. $this->mode = $mode;
  373. $this->parsed_url = $pu = smb::parse_url($url);
  374. if ($pu['type'] <> 'path') trigger_error('stream_open(): error in URL', E_USER_ERROR);
  375. switch ($mode) {
  376. case 'r':
  377. case 'r+':
  378. case 'rb':
  379. case 'a':
  380. case 'a+': $this->tmpfile = tempnam('/tmp', 'smb.down.');
  381. smb::execute ('get "'.$pu['path'].'" "'.$this->tmpfile.'"', $pu);
  382. break;
  383. case 'w':
  384. case 'w+':
  385. case 'wb':
  386. case 'x':
  387. case 'x+': $this->cleardircache();
  388. $this->tmpfile = tempnam('/tmp', 'smb.up.');
  389. $this->need_flush=true;
  390. }
  391. $this->stream = fopen ($this->tmpfile, $mode);
  392. return TRUE;
  393. }
  394. function stream_close () { return fclose($this->stream); }
  395. function stream_read ($count) { return fread($this->stream, $count); }
  396. function stream_write ($data) { $this->need_flush = TRUE; return fwrite($this->stream, $data); }
  397. function stream_eof () { return feof($this->stream); }
  398. function stream_tell () { return ftell($this->stream); }
  399. function stream_seek ($offset, $whence=null) { return fseek($this->stream, $offset, $whence); }
  400. function stream_flush () {
  401. if ($this->mode <> 'r' && $this->need_flush) {
  402. smb::clearstatcache ($this->url);
  403. smb::execute ('put "'.$this->tmpfile.'" "'.$this->parsed_url['path'].'"', $this->parsed_url);
  404. $this->need_flush = FALSE;
  405. }
  406. }
  407. function stream_stat () { return smb::url_stat ($this->url); }
  408. function __destruct () {
  409. if ($this->tmpfile <> '') {
  410. if ($this->need_flush) $this->stream_flush ();
  411. unlink ($this->tmpfile);
  412. }
  413. }
  414. }
  415. ###################################################################
  416. # Register 'smb' protocol !
  417. ###################################################################
  418. stream_wrapper_register('smb', 'smb_stream_wrapper')
  419. or die ('Failed to register protocol');