PageRenderTime 76ms CodeModel.GetById 39ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/ajaxplorer-init-collection/AjaXplorer-3.2.3/plugins/access.smb/smb.php

https://bitbucket.org/pombredanne/blingnode
PHP | 505 lines | 395 code | 60 blank | 50 comment | 69 complexity | ccd458d6ad2e81dfe311055f43df3f46 MD5 | raw file
Possible License(s): LGPL-3.0, MPL-2.0-no-copyleft-exception, LGPL-2.0, BSD-3-Clause, BSD-2-Clause, Apache-2.0, LGPL-2.1
  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])) $pu[$i] = '';
  39. if (count ($userdomain = explode (';', urldecode ($pu['user']))) > 1)
  40. @list ($pu['domain'], $pu['user']) = $userdomain;
  41. $path = preg_replace (array ('/^\//', '/\/$/'), '', urldecode ($pu['path']));
  42. list ($pu['share'], $pu['path']) = (preg_match ('/^([^\/]+)\/(.*)/', $path, $regs))
  43. ? array ($regs[1], preg_replace ('/\//', '\\', $regs[2]))
  44. : array ($path, '');
  45. $pu['type'] = $pu['path'] ? 'path' : ($pu['share'] ? 'share' : ($pu['host'] ? 'host' : '**error**'));
  46. if (! ($pu['port'] = intval(@$pu['port']))) $pu['port'] = 139;
  47. return $pu;
  48. }
  49. function look ($purl) {
  50. return smb::client ('-L ' . escapeshellarg ($purl['host']), $purl);
  51. }
  52. function execute ($command, $purl) {
  53. return smb::client ('-d 0 '
  54. . escapeshellarg ('//' . $purl['host'] . '/' . $purl['share'])
  55. . ' -c ' . escapeshellarg ($command), $purl
  56. );
  57. }
  58. function client ($params, $purl) {
  59. //var_dump($params);
  60. static $regexp = array (
  61. '^added interface ip=(.*) bcast=(.*) nmask=(.*)$' => 'skip',
  62. 'Anonymous login successful' => 'skip',
  63. '^Domain=\[(.*)\] OS=\[(.*)\] Server=\[(.*)\]$' => 'skip',
  64. '^\tSharename[ ]+Type[ ]+Comment$' => 'shares',
  65. '^\t---------[ ]+----[ ]+-------$' => 'skip',
  66. '^\tServer [ ]+Comment$' => 'servers',
  67. '^\t---------[ ]+-------$' => 'skip',
  68. '^\tWorkgroup[ ]+Master$' => 'workg',
  69. '^\t(.*)[ ]+(Disk|IPC)[ ]+IPC.*$' => 'skip',
  70. '^\tIPC\\\$(.*)[ ]+IPC' => 'skip',
  71. '^\t(.*)[ ]+(Disk)[ ]+(.*)$' => 'share',
  72. '^\t(.*)[ ]+(Printer)[ ]+(.*)$' => 'skip',
  73. '([0-9]+) blocks of size ([0-9]+)\. ([0-9]+) blocks available' => 'skip',
  74. 'Got a positive name query response from ' => 'skip',
  75. '^(session setup failed): (.*)$' => 'error',
  76. '^(.*): ERRSRV - ERRbadpw' => 'error',
  77. '^Error returning browse list: (.*)$' => 'error',
  78. '^tree connect failed: (.*)$' => 'error',
  79. '^(Connection to .* failed)$' => 'error',
  80. '^NT_STATUS_(.*) ' => 'error',
  81. '^NT_STATUS_(.*)\$' => 'error',
  82. 'ERRDOS - ERRbadpath \((.*).\)' => 'error',
  83. 'cd (.*): (.*)$' => 'error',
  84. '^cd (.*): NT_STATUS_(.*)' => 'error',
  85. '^\t(.*)$' => 'srvorwg',
  86. '^([0-9]+)[ ]+([0-9]+)[ ]+(.*)$' => 'skip',
  87. '^Job ([0-9]+) cancelled' => 'skip',
  88. '^[ ]+(.*)[ ]+([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',
  89. '^message start: ERRSRV - (ERRmsgoff)' => 'error'
  90. );
  91. if (SMB4PHP_AUTHMODE == 'env') {
  92. putenv("USER={$purl['user']}%{$purl['pass']}");
  93. $auth = '';
  94. } else {
  95. $auth = ($purl['user'] <> '' ? (' -U ' . escapeshellarg ($purl['user'] . '%' . $purl['pass'])) : '');
  96. }
  97. if ($purl['domain'] <> '') {
  98. $auth .= ' -W ' . escapeshellarg ($purl['domain']);
  99. }
  100. $port = ($purl['port'] <> 139 ? ' -p ' . escapeshellarg ($purl['port']) : '');
  101. $options = '-O ' . escapeshellarg(SMB4PHP_SMBOPTIONS);
  102. AJXP_Logger::debug("SMBCLIENT", "{$options} {$params}");
  103. $output = popen (SMB4PHP_SMBCLIENT." -N {$auth} {$options} {$port} {$options} {$params} 2>/dev/null", 'r');
  104. $info = array ();
  105. while ($line = fgets ($output, 4096)) {
  106. list ($tag, $regs, $i) = array ('skip', array (), array ());
  107. reset ($regexp);
  108. foreach ($regexp as $r => $t) if (preg_match ('/'.$r.'/', $line, $regs)) {
  109. $tag = $t;
  110. break;
  111. }
  112. switch ($tag) {
  113. case 'skip': continue;
  114. case 'shares': $mode = 'shares'; break;
  115. case 'servers': $mode = 'servers'; break;
  116. case 'workg': $mode = 'workgroups'; break;
  117. case 'share':
  118. list($name, $type) = array (
  119. trim(substr($line, 1, 15)),
  120. trim(strtolower(substr($line, 17, 10)))
  121. );
  122. $i = ($type <> 'disk' && preg_match('/^(.*) Disk/', $line, $regs))
  123. ? array(trim($regs[1]), 'disk')
  124. : array($name, 'disk');
  125. break;
  126. case 'srvorwg':
  127. list ($name, $master) = array (
  128. strtolower(trim(substr($line,1,21))),
  129. strtolower(trim(substr($line, 22)))
  130. );
  131. $i = ($mode == 'servers') ? array ($name, "server") : array ($name, "workgroup", $master);
  132. break;
  133. case 'files':
  134. list ($attr, $name) = preg_match ("/^(.*)[ ]+([D|A|H|S|R]+)$/", trim ($regs[1]), $regs2)
  135. ? array (trim ($regs2[2]), trim ($regs2[1]))
  136. : array ('', trim ($regs[1]));
  137. list ($his, $im) = array (
  138. explode(':', $regs[6]), 1 + strpos("JanFebMarAprMayJunJulAugSepOctNovDec", $regs[4]) / 3);
  139. $i = ($name <> '.' && $name <> '..')
  140. ? array (
  141. $name,
  142. (strpos($attr,'D') === FALSE) ? 'file' : 'folder',
  143. 'attr' => $attr,
  144. 'size' => intval($regs[2]),
  145. 'time' => mktime ($his[0], $his[1], $his[2], $im, $regs[5], $regs[7])
  146. )
  147. : array();
  148. break;
  149. case 'error':
  150. if(strstr($regs[1], "NO_SUCH_FILE") == 0){
  151. return "NOT_FOUND";
  152. }
  153. trigger_error($regs[1], E_USER_ERROR);
  154. }
  155. if ($i) switch ($i[1]) {
  156. case 'file':
  157. case 'folder': $info['info'][$i[0]] = $i;
  158. case 'disk':
  159. case 'server':
  160. case 'workgroup': $info[$i[1]][] = $i[0];
  161. }
  162. }
  163. pclose($output);
  164. return $info;
  165. }
  166. # stats
  167. function url_stat ($url, $flags = STREAM_URL_STAT_LINK) {
  168. if ($s = smb::getstatcache($url)) {
  169. AJXP_Logger::debug("Using statcache for $url");
  170. return $s;
  171. }
  172. AJXP_Logger::debug("Getting statcache for $url");
  173. list ($stat, $pu) = array (array (), smb::parse_url ($url));
  174. switch ($pu['type'])
  175. {
  176. case 'host':
  177. if ($o = smb::look ($pu))
  178. $stat = stat ("/tmp");
  179. else
  180. trigger_error ("url_stat(): list failed for host '{$host}'", E_USER_WARNING);
  181. break;
  182. case 'share':
  183. if ($o = smb::look ($pu)) {
  184. $found = FALSE;
  185. $lshare = strtolower ($pu['share']); # fix by Eric Leung
  186. if(is_array($o) && isSet($o['disk']) && is_array($o['disk'])){
  187. foreach ($o['disk'] as $s) if ($lshare == strtolower($s)) {
  188. $found = TRUE;
  189. $stat = stat ("/tmp");
  190. break;
  191. }
  192. }
  193. if (! $found)
  194. //trigger_error ("url_stat(): disk resource '{$share}' not found in '{$host}'", E_USER_WARNING);
  195. return null;
  196. }
  197. break;
  198. case 'path':
  199. $o = smb::execute ('dir "'.$pu['path'].'"', $pu);
  200. if ($o != null) {
  201. if($o == "NOT_FOUND"){
  202. return null;
  203. }
  204. $p = explode ("\\", $pu['path']);
  205. $name = $p[count($p)-1];
  206. if (isset ($o['info'][$name])) {
  207. $stat = smb::addstatcache ($url, $o['info'][$name]);
  208. } else {
  209. trigger_error ("url_stat(): path '{$pu['path']}' not found", E_USER_WARNING);
  210. }
  211. } else {
  212. trigger_error ("url_stat(): dir failed for path '{$pu['path']}'", E_USER_WARNING);
  213. }
  214. break;
  215. default: trigger_error ('error in URL', E_USER_ERROR);
  216. }
  217. return $stat;
  218. }
  219. function addstatcache ($url, $info) {
  220. global $__smb_cache;
  221. $url = smb::cleanUrl($url);
  222. $is_file = (strpos ($info['attr'],'D') === FALSE);
  223. $s = ($is_file) ? stat ('/etc/passwd') : stat ('/tmp');
  224. if($is_file){
  225. $s[2] = $s['mode'] = 0666;
  226. $s[2] = $s['mode'] |= 0100000;
  227. }
  228. $s[7] = $s['size'] = $info['size'];
  229. $s[8] = $s[9] = $s[10] = $s['atime'] = $s['mtime'] = $s['ctime'] = $info['time'];
  230. return $__smb_cache['stat'][$url] = $s;
  231. }
  232. function getstatcache ($url) {
  233. global $__smb_cache;
  234. $url = smb::cleanUrl($url);
  235. return isset ($__smb_cache['stat'][$url]) ? $__smb_cache['stat'][$url] : FALSE;
  236. }
  237. function clearstatcache ($url='') {
  238. global $__smb_cache;
  239. $url = smb::cleanUrl($url);
  240. if ($url == '') $__smb_cache['stat'] = array (); else unset ($__smb_cache['stat'][$url]);
  241. }
  242. static function cleanUrl($url){
  243. $url = str_replace("smb://", "smb:/__/__", $url);
  244. while (strstr($url, "//")!==FALSE) {
  245. $url = str_replace("//", "/", $url);
  246. }
  247. $url = str_replace("smb:/__/__", "smb://", $url);
  248. return $url;
  249. }
  250. # commands
  251. function unlink ($url) {
  252. $pu = smb::parse_url($url);
  253. if ($pu['type'] <> 'path') trigger_error('unlink(): error in URL', E_USER_ERROR);
  254. smb::clearstatcache ($url);
  255. smb::execute ('del "'.$pu['path'].'"', $pu);
  256. return true;
  257. }
  258. function rename ($url_from, $url_to) {
  259. list ($from, $to) = array (smb::parse_url($url_from), smb::parse_url($url_to));
  260. if ($from['host'] <> $to['host'] ||
  261. $from['share'] <> $to['share'] ||
  262. $from['user'] <> $to['user'] ||
  263. $from['pass'] <> $to['pass'] ||
  264. $from['domain'] <> $to['domain']) {
  265. trigger_error('rename(): FROM & TO must be in same server-share-user-pass-domain', E_USER_ERROR);
  266. }
  267. if ($from['type'] <> 'path' || $to['type'] <> 'path') {
  268. trigger_error('rename(): error in URL', E_USER_ERROR);
  269. }
  270. smb::clearstatcache ($url_from);
  271. return smb::execute ('rename "'.$from['path'].'" "'.$to['path'].'"', $to);
  272. }
  273. function mkdir ($url, $mode, $options) {
  274. $pu = smb::parse_url($url);
  275. if ($pu['type'] <> 'path') trigger_error('mkdir(): error in URL', E_USER_ERROR);
  276. return smb::execute ('mkdir "'.$pu['path'].'"', $pu);
  277. }
  278. function rmdir ($url) {
  279. $pu = smb::parse_url($url);
  280. if ($pu['type'] <> 'path') trigger_error('rmdir(): error in URL', E_USER_ERROR);
  281. smb::clearstatcache ($url);
  282. return smb::execute ('rmdir "'.$pu['path'].'"', $pu);
  283. }
  284. }
  285. ###################################################################
  286. # SMB_STREAM_WRAPPER - class to be registered for smb:// URLs
  287. ###################################################################
  288. class smb_stream_wrapper extends smb {
  289. # variables
  290. var $stream, $url, $parsed_url = array (), $mode, $tmpfile;
  291. var $need_flush = FALSE;
  292. var $dir = array (), $dir_index = -1;
  293. # directories
  294. function dir_opendir ($url, $options) {
  295. $d = $this->getdircache ($url);
  296. if (is_array($d)) {
  297. $this->dir = $d;
  298. $this->dir_index = 0;
  299. return TRUE;
  300. }
  301. $pu = smb::parse_url ($url);
  302. switch ($pu['type']) {
  303. case 'host':
  304. if ($o = smb::look ($pu)) {
  305. $this->dir = $o['disk'];
  306. $this->dir_index = 0;
  307. } else {
  308. trigger_error ("dir_opendir(): list failed for host '{$pu['host']}'", E_USER_WARNING);
  309. }
  310. break;
  311. case 'share':
  312. case 'path':
  313. $o = smb::execute ('dir "'.$pu['path'].'\*"', $pu);
  314. if (is_array($o)) {
  315. if(isSet($o['info'])){
  316. $this->dir = array_keys($o['info']);
  317. $this->dir_index = 0;
  318. $this->adddircache ($url, $this->dir);
  319. foreach ($o['info'] as $name => $info) {
  320. AJXP_Logger::debug("Adding to statcache ".$url.'/'.$name);
  321. //smb::addstatcache($url . '/' . urlencode($name), $info);
  322. smb::addstatcache($url .'/'. $name, $info);
  323. }
  324. }else{
  325. $this->dir = array();
  326. $this->dir_index = 0;
  327. $this->adddircache($url, $this->dir);
  328. }
  329. } else {
  330. trigger_error ("dir_opendir(): dir failed for path '{$path}'", E_USER_WARNING);
  331. }
  332. break;
  333. default:
  334. trigger_error ('dir_opendir(): error in URL', E_USER_ERROR);
  335. }
  336. return TRUE;
  337. }
  338. function dir_readdir () { return ($this->dir_index < count($this->dir)) ? $this->dir[$this->dir_index++] : FALSE; }
  339. function dir_rewinddir () { $this->dir_index = 0; }
  340. function dir_closedir () { $this->dir = array(); $this->dir_index = -1; return TRUE; }
  341. # cache
  342. function adddircache ($url, $content) {
  343. global $__smb_cache;
  344. $url = smb::cleanUrl($url);
  345. AJXP_Logger::debug("Adding to dir cache", array("url"=>$url));
  346. return $__smb_cache['dir'][$url] = $content;
  347. }
  348. function getdircache ($url) {
  349. global $__smb_cache;
  350. $url = smb::cleanUrl($url);
  351. AJXP_Logger::debug("Testing dir cache", array("url"=>$url));
  352. return isset ($__smb_cache['dir'][$url]) ? $__smb_cache['dir'][$url] : FALSE;
  353. }
  354. function cleardircache ($url='') {
  355. global $__smb_cache;
  356. $url = smb::cleanUrl($url);
  357. if ($url == '') $__smb_cache['dir'] = array (); else unset ($__smb_cache['dir'][$url]);
  358. }
  359. # streams
  360. function stream_open ($url, $mode, $options, $opened_path) {
  361. $this->url = $url;
  362. $this->mode = $mode;
  363. $this->defer_stream_read;
  364. $this->parsed_url = $pu = smb::parse_url($url);
  365. if ($pu['type'] <> 'path') trigger_error('stream_open(): error in URL', E_USER_ERROR);
  366. switch ($mode) {
  367. case 'r':
  368. case 'r+':
  369. case 'rb':
  370. case 'a':
  371. case 'a+':
  372. // REFERENCE STREAM BUT DO NOT OPEN IT UNTIL READING IS REALLY NECESSARY!
  373. /*
  374. $this->tmpfile = tempnam('/tmp', 'smb.down.');
  375. smb::execute ('get "'.$pu['path'].'" "'.$this->tmpfile.'"', $pu);
  376. $this->stream = fopen ($this->tmpfile, $mode);
  377. */
  378. $this->defer_stream_read = true;
  379. break;
  380. case 'w':
  381. case 'w+':
  382. case 'wb':
  383. case 'x':
  384. case 'x+':
  385. $this->cleardircache();
  386. $this->tmpfile = tempnam('/tmp', 'smb.up.');
  387. $this->stream = fopen ($this->tmpfile, $mode);
  388. $this->need_flush = TRUE;
  389. }
  390. //$this->stream = fopen ($this->tmpfile, $mode);
  391. return TRUE;
  392. }
  393. function stream_close () {
  394. if(isSet($this->stream)){
  395. return fclose($this->stream);
  396. }else{
  397. // Stream was in fact never opened!
  398. return true;
  399. }
  400. }
  401. function stream_read ($count) { return fread($this->getStream(), $count); }
  402. function stream_write ($data) {
  403. $this->need_flush = TRUE;
  404. return fwrite($this->getStream(), $data);
  405. }
  406. function stream_eof () { return feof($this->getStream()); }
  407. function stream_tell () { return ftell($this->getStream()); }
  408. function stream_seek ($offset, $whence=null) { return fseek($this->getStream(), $offset, $whence); }
  409. function stream_flush () {
  410. if ($this->mode <> 'r' && $this->need_flush) {
  411. smb::clearstatcache ($this->url);
  412. smb::execute ('put "'.$this->tmpfile.'" "'.$this->parsed_url['path'].'"', $this->parsed_url);
  413. $this->need_flush = FALSE;
  414. }
  415. }
  416. function stream_stat () { return smb::url_stat ($this->url); }
  417. function __destruct () {
  418. if ($this->tmpfile <> '') {
  419. if ($this->need_flush) $this->stream_flush ();
  420. unlink ($this->tmpfile);
  421. }
  422. }
  423. private function getStream(){
  424. if(isSet($this->stream)){
  425. return $this->stream;
  426. }
  427. if(isSet($this->defer_stream_read)){
  428. $this->tmpfile = tempnam('/tmp', 'smb.down');
  429. AJXP_Logger::debug("Creating real tmp file now");
  430. smb::execute ('get "'.$this->parsed_url['path'].'" "'.$this->tmpfile.'"', $this->parsed_url);
  431. $this->stream = fopen($this->tmpfile, $this->mode);
  432. }
  433. return $this->stream;
  434. }
  435. }
  436. ###################################################################
  437. # Register 'smb' protocol !
  438. ###################################################################
  439. stream_wrapper_register('smb', 'smb_stream_wrapper')
  440. or die ('Failed to register protocol');
  441. ?>