Fix #6679 - SMB file browsing
authorAlan Knowles <alan@roojs.com>
Tue, 27 Apr 2021 09:52:08 +0000 (17:52 +0800)
committerAlan Knowles <alan@roojs.com>
Tue, 27 Apr 2021 09:52:08 +0000 (17:52 +0800)
DB/DataObject.php
File/Smb.php [new file with mode: 0644]
File/Smb/Dir.php [new file with mode: 0644]
File/Smb/Exception.php [new file with mode: 0644]
File/Smb/File.php [new file with mode: 0644]

index 79b442b..911734a 100644 (file)
@@ -4300,6 +4300,14 @@ class DB_DataObject extends DB_DataObject_Overload
         return $ret;
     }
 
+    /**
+     * simple version of toArray that can be used in fetchAll
+     */
+    function toArrayResult($format = '%s')
+    {
+        return $this->toArray($format, 0);
+    }
+    
     /**
      * validate the values of the object (usually prior to inserting/updating..)
      *
diff --git a/File/Smb.php b/File/Smb.php
new file mode 100644 (file)
index 0000000..973e3b7
--- /dev/null
@@ -0,0 +1,82 @@
+<?php
+
+/**
+ *
+ * An alpha interface to samba browsing with the libsmb client.
+ *
+ *
+ */
+
+require_once 'File/Smb/Dir.php';
+
+class File_Smb  extends File_Smb_Dir  {
+    
+    
+    const FILE = 1;
+    const DIR = 2;
+    const SHARE = 4;
+    
+    
+    
+    
+    /**
+     * static map of server => [ user , pass ]
+     */
+    static $auth = array();
+    
+    static $connection = array();
+    
+    /**
+     * constructor
+     * 
+     * @param:  $con (string)  - connection string:   USER\\WORKGROUP%PASS@/SERVER/SHARE - we use unix paths here (converted to smb by lib.)
+     *  
+     * 
+     */
+    
+    
+    function __construct($con)
+    {
+        
+        $lr = explode("@", $con);
+        $bits = explode("/", $lr[1]);
+        $this->server  = $bits[0];
+        $this->path = $bits[1];
+        $this->name = $bits[1];
+        $this->type = self::DIR  + self::SHARE;
+        
+        
+        $bb = explode('%', $lr[0]);
+        $u = $bb[0];
+        $ws = null;
+        $pass = $bb[1];
+        if (strpos('\\', $bb[0]) > -1) {
+            list($u,$ws) = explode("\\", $bb[0]);
+        }
+        
+        $auth = File_Smb::$auth[$this->server] = array($ws, $u, $pass);
+        
+        if (!isset(File_Smb::$connection[$this->server])) {
+            $con = File_Smb::$connection[$this->server] = smbclient_state_new();
+            //print_R(array('connect', $auth[0], $auth[1], $auth[2]));
+            
+            smbclient_state_init($con , $auth[0], $auth[1], $auth[2]);
+        }
+        
+   
+        $this->resource = File_Smb::$connection[$this->server];
+        
+        
+    }
+    
+    function ctorDir($path)
+    {
+        $p = substr($path, strlen($this->path)+1);
+        $ret = new File_Smb_Dir($this, $p,  basename($p));
+        
+        return $ret;
+    }
+    
+    
+    
+}
diff --git a/File/Smb/Dir.php b/File/Smb/Dir.php
new file mode 100644 (file)
index 0000000..f7dbf7d
--- /dev/null
@@ -0,0 +1,179 @@
+<?php
+
+
+class File_Smb_Dir {
+    
+    
+    var $server;
+    var $resource; // smblicent resource
+    var $path; // full path excluding server, including share.
+    var $type;
+    var $name;
+    var $namehash; // hash of name
+    
+    var $perm_denied  = false;
+    var $ino;  //inode number ****
+    var $mode; //inode protection mode
+    var $nlink;        //number of links
+    var $uid;  //userid of owner *
+    var $gid;  //groupid of owner *
+    var $rdev; //device type, if inode device
+    var $size; //size in bytes
+    var $atime;        //time of last access (Unix timestamp)
+    var $mtime;        //time of last modification (Unix timestamp)
+    var $ctime;        //time of last inode change (Unix timestamp)
+    var $blksize;      //blocksize of filesystem IO **
+    var $blocks;
+    
+    
+    var $created_dt;
+    var $updated_dt;
+    var $accessed_dt;
+    
+    
+    /**
+     * constructor
+     * 
+     * @param:  $dir File_Smb_Dir
+     * @param: string $sub - directory;
+     *  
+     * 
+     */
+    
+    
+    function __construct($dir, $sub, $base = false)
+    {
+        
+        
+        $this->server  = $dir->server;
+        $this->path = $dir->path . '/' . $sub;
+        $this->name = $base === false ? $sub : $base; // for overriding ...
+        $this->namehash = sha1($this->name);
+        $this->type = File_Smb::DIR;
+        $this->resource = File_Smb::$connection[$this->server];
+        $this->stat();
+     
+        
+    }
+    
+    function stat()
+    {
+        //if (!is_readable('smb://' . $this->server . '/'. $this->path
+        set_error_handler(function($errno, $errstr, $errfile, $errline)  {
+            if (preg_match('/Permission denied/', $errstr)) {
+                $this->perm_denied = true;
+                return;
+            }
+
+        });
+                 
+        $ar = smbclient_stat($this->resource, 'smb://' . $this->server . '/'. $this->path);
+        
+        restore_error_handler();
+        if ($ar == false) {
+            return;
+        }
+
+        foreach($ar as $k=>$v) {
+            if (!is_numeric($k)) {
+                $this->$k = $v;
+            }
+        }
+        if (isset($this->ctime)) {
+            $this->created_dt = date("Y-m-d H:i:s", $this->ctime);
+        }
+        if (isset($this->mtime)) {
+            $this->updated_dt = date("Y-m-d H:i:s", $this->mtime);
+        }
+        if (isset($this->atime)) {
+            $this->accessed_dt = date("Y-m-d H:i:s", $this->atime);
+        }
+        $acls = explode(',',smbclient_getxattr($this->resource, 'smb://' . $this->server . '/'. $this->path, 'system.nt_sec_desc.*+'));
+        $this->acls = array();
+        foreach($acls as $a) {
+            $aa = explode(":", $a);
+            if ($aa[0] == 'ACL') {
+                if (!in_array($aa[1], $this->acls)) {
+                    $this->acls[] = $aa[1];
+                }
+                
+            } else {
+                $this->{strtolower($aa[0])} = $aa[1];
+            }
+            
+            
+        }
+
+    }
+    
+    /**
+     * return the list of files in a folder.
+     */
+    
+    function dir()
+    {
+        require_once 'File/Smb/File.php'; 
+        set_error_handler(array($this, 'error_handler'));
+        // fixme - path compoenent should be uuencoded..
+        $dh = smbclient_opendir($this->resource, 'smb://' . $this->server . '/'. $this->path);
+        restore_error_handler();
+
+        if (!$dh) {
+            throw new Exception("Directory failed to open");
+            //return array();
+        }
+        
+        $ret = array();
+        
+        while (($e = smbclient_readdir($this->resource,$dh)) !== false) {
+           //  print_R($e);
+            switch($e['type']) {
+                
+                case 'file':
+                    $ret[] = new File_Smb_File($this, $e['name']);
+                    break;
+                
+                case 'directory':
+                    if ($e['name'] == '.' || $e['name'] == '..') {
+                        break;
+                    }
+                    $ret[] = new File_Smb_Dir($this, $e['name']);
+                    break;
+                
+                case 'workgroup':
+                case 'server':
+                case 'file share':
+                case 'printer share':
+                case 'communication share':
+                case 'IPC share':
+                case 'link':
+                case 'unknown':
+                    echo "Unknown share type?\n";
+                    break;
+            }
+        }
+        //print_R($ret);exit;
+        
+        return $ret;
+            
+        
+        
+    }
+    
+    function error_handler($errno, $errstr, $errfile, $errline) {
+        require_once 'File/Smb/Exception.php';
+        restore_error_handler();
+        switch($errno) {
+            case 2:
+                throw new File_Smb_ExceptionNotExist($errstr, $errno);
+            case 13:
+                throw new File_Smb_ExceptionPermDenied($errstr, $errno);
+            
+        }
+        
+        
+    }
+    
+    
+}
\ No newline at end of file
diff --git a/File/Smb/Exception.php b/File/Smb/Exception.php
new file mode 100644 (file)
index 0000000..2c8c0cb
--- /dev/null
@@ -0,0 +1,4 @@
+<?php
+
+class File_Smb_ExceptionNotExist extends Exception {};
+class File_Smb_ExceptionPermDenied extends Exception {};
\ No newline at end of file
diff --git a/File/Smb/File.php b/File/Smb/File.php
new file mode 100644 (file)
index 0000000..8ee6d60
--- /dev/null
@@ -0,0 +1,60 @@
+<?php
+
+require_once 'File/Smb.php';
+
+class File_Smb_File extends File_Smb {
+    
+    
+    var $server;
+    var $resource; // smblicent resource
+    var $path; // full path excluding server, including share.
+    var $type;
+    
+
+    /**
+     * constructor
+     * 
+     * @param:  $con (string)  - connection string:   USER\WORKGROUP@PASS/SERVER/SHARE - we use unix paths here (converted to smb by lib.)
+     *  
+     * 
+     */
+    
+    
+    function __construct($dir, $sub)
+    {
+        
+
+        $this->server  = $dir->server;
+        $this->path = $dir->path . '/' . $sub;
+        $this->name = $sub;
+        $this->namehash = sha1($this->name);
+        $this->type = File_Smb::FILE;
+        $this->resource = File_Smb::$connection[$this->server];
+        $this->stat();
+
+        
+    }
+    
+    function download($target)
+    {
+        $fh =  smbclient_open( $this->resource, 'smb://' . $this->server . '/'. $this->path, 'r');
+        if ($fh === false) {
+            throw new Exception("SMB download : {$this->path} open Failed");
+        }
+        $fw = fopen($target, 'w');
+        if (!$fw) {
+            throw new Exception("SMB download: {$this->path} open write target Failed");
+        }
+        while (true) {
+            $str = smbclient_read($this->resource, $fh, 4096);
+            if (strlen($str) ==0 ) {
+                break;
+            }
+            fputs($fw,$str);
+        }
+        fclose($fw);
+        smbclient_close($this->resource, $fh);
+        
+    }
+    
+}
\ No newline at end of file