The Cache: Technology Expert's Forum
 
*
Welcome, Guest. Please login or register. September 16, 2019, 05:20:58 PM

Login with username, password and session length


Pages: [1]
  Print  
Author Topic: PHP SMB Wrapper: Hot stuff  (Read 4574 times)
perkiset
Olde World Hacker
Administrator
Lifer
*****
Offline Offline

Posts: 10096



View Profile
« on: February 17, 2011, 12:04:42 AM »

I found this code and wanted to repost it, it's freakin' great. On top of that, I did not realize how easy it is to implement file stream wrappers - I thought they had to be done in C and compiled into the PHP instance. This is a great little tutorial and gear spinner.

Simply include/require the code, and you now have an SMB: wrapper for file streams. Here's a usage example:

<?php

require "./class.smbwrapper.php";

// Wide open, guest OK share, looking for a file named "fileName.doc" in folder "dir" on share "shareName" at 10.10.1.7:
$buff file_get_contents('smb://10.10.1.7/shareName/dir/fileName.doc');

// Similar, but the share is username/password protected:
$buff file_get_contents('smb://username:password@10.10.1.7/shareName/dir/fileName.doc');

?>

Takes a BOATLOAD of hassle out of talking Linux boxes to Windows boxes in PHP. CAVEAT: You need to have SAMBA installed on the calling machine because it will make use of smbclient.

Here's the code. Enjoy!


<?php
###################################################################
# smb.php
# This class implements a SMB stream wrapper based on 'smbclient'
#
# Date: lun oct 22 10:35:35 CEST 2007
#
# Homepage: http://www.phpclasses.org/smb4php
#
# Copyright (c) 2007 Victor M. Varela <vmvarela@gmail.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#  
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#  
###################################################################

define ('SMB4PHP_VERSION''0.8');

###################################################################
# CONFIGURATION SECTION - Change for your needs
###################################################################

define ('SMB4PHP_SMBCLIENT''smbclient');
define ('SMB4PHP_SMBOPTIONS''TCP_NODELAY IPTOS_LOWDELAY SO_KEEPALIVE SO_RCVBUF=8192 SO_SNDBUF=8192');
define ('SMB4PHP_AUTHMODE''arg'); # set to 'env' to use USER enviroment variable

###################################################################
# SMB - commands that does not need an instance
###################################################################

$GLOBALS['__smb_cache'] = array ('stat' => array (), 'dir' => array ());

class 
smb {

    function 
parse_url ($url) {
        
$pu parse_url (trim($url));
        foreach (array (
'domain''user''pass''host''port''path') as $i)
            if (! isset(
$pu[$i])) $pu[$i] = '';
        if (
count ($userdomain split (';'urldecode ($pu['user']))) > 1)
            @list (
$pu['domain'], $pu['user']) = $userdomain;
        
$path preg_replace (array ('/^\//''/\/$/'), ''urldecode ($pu['path']));
        list (
$pu['share'], $pu['path']) = (preg_match ('/^([^\/]+)\/(.*)/'$path$regs))
          ? array (
$regs[1], preg_replace ('/\//''\\'$regs[2]))
          : array (
$path'');
        
$pu['type'] = $pu['path'] ? 'path' : ($pu['share'] ? 'share' : ($pu['host'] ? 'host' '**error**'));
        if (! (
$pu['port'] = intval(@$pu['port']))) $pu['port'] = 139;
        return 
$pu;
    }


    function 
look ($purl) {
        return 
smb::client ('-L ' escapeshellarg ($purl['host']), $purl);
    }


    function 
execute ($command$purl) {
        return 
smb::client ('-d 0 '
              
escapeshellarg ('//' $purl['host'] . '/' $purl['share'])
              . 
' -c ' escapeshellarg ($command), $purl
        
);
    }

    function 
client ($params$purl) {

        static 
$regexp = array (
        
'^added interface ip=(.*) bcast=(.*) nmask=(.*)$' => 'skip',
        
'Anonymous login successful' => 'skip',
        
'^Domain=\[(.*)\] OS=\[(.*)\] Server=\[(.*)\]$' => 'skip',
        
'^\tSharename[ ]+Type[ ]+Comment$' => 'shares',
        
'^\t---------[ ]+----[ ]+-------$' => 'skip',
        
'^\tServer   [ ]+Comment$' => 'servers',
        
'^\t---------[ ]+-------$' => 'skip',
        
'^\tWorkgroup[ ]+Master$' => 'workg',
        
'^\t(.*)[ ]+(Disk|IPC)[ ]+IPC.*$' => 'skip',
        
'^\tIPC\\\$(.*)[ ]+IPC' => 'skip',
        
'^\t(.*)[ ]+(Disk)[ ]+(.*)$' => 'share',
        
'^\t(.*)[ ]+(Printer)[ ]+(.*)$' => 'skip',
        
'([0-9]+) blocks of size ([0-9]+)\. ([0-9]+) blocks available' => 'skip',
        
'Got a positive name query response from ' => 'skip',
        
'^(session setup failed): (.*)$' => 'error',
        
'^(.*): ERRSRV - ERRbadpw' => 'error',
        
'^Error returning browse list: (.*)$' => 'error',
        
'^tree connect failed: (.*)$' => 'error',
        
'^(Connection to .* failed)$' => 'error',
        
'^NT_STATUS_(.*) ' => 'error',
        
'^NT_STATUS_(.*)\$' => 'error',
        
'ERRDOS - ERRbadpath \((.*).\)' => 'error',
        
'cd (.*): (.*)$' => 'error',
        
'^cd (.*): NT_STATUS_(.*)' => 'error',
        
'^\t(.*)$' => 'srvorwg',
        
'^([0-9]+)[ ]+([0-9]+)[ ]+(.*)$' => 'skip',
        
'^Job ([0-9]+) cancelled' => 'skip',
        
'^[ ]+(.*)[ ]+([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',
        
'^message start: ERRSRV - (ERRmsgoff)' => 'error'
        
);

        if (
SMB4PHP_AUTHMODE == 'env') {
            
putenv("USER={$purl['user']}%{$purl['pass']}");
            
$auth '';
        } else {
            
$auth = ($purl['user'] <> '' ? (' -U ' escapeshellarg ($purl['user'] . '%' $purl['pass'])) : '');
        }
        if (
$purl['domain'] <> '') {
            
$auth .= ' -W ' escapeshellarg ($purl['domain']);
        }
        
$port = ($purl['port'] <> 139 ' -p ' escapeshellarg ($purl['port']) : '');
        
$options '-O ' escapeshellarg(SMB4PHP_SMBOPTIONS);
        
$output popen (SMB4PHP_SMBCLIENT." -N {$auth} {$options} {$port} {$options} {$params} 2>/dev/null"'r');
        
$info = array ();
        while (
$line fgets ($output4096)) {
            list (
$tag$regs$i) = array ('skip', array (), array ());
            
reset ($regexp);
            foreach (
$regexp as $r => $t) if (preg_match ('/'.$r.'/'$line$regs)) {
                
$tag $t;
                break;
            }
            switch (
$tag) {
                case 
'skip':    continue;
                case 
'shares':  $mode 'shares';     break;
                case 
'servers'$mode 'servers';    break;
                case 
'workg':   $mode 'workgroups'; break;
                case 
'share':
                    list(
$name$type) = array (
                        
trim(substr($line115)),
                        
trim(strtolower(substr($line1710)))
                    );
                    
$i = ($type <> 'disk' && preg_match('/^(.*) Disk/'$line$regs))
                        ? array(
trim($regs[1]), 'disk')
                        : array(
$name'disk');
                    break;
                case 
'srvorwg':
                    list (
$name$master) = array (
                        
strtolower(trim(substr($line,1,21))),
                        
strtolower(trim(substr($line22)))
                    );
                    
$i = ($mode == 'servers') ? array ($name"server") : array ($name"workgroup"$master);
                    break;
                case 
'files':
                    list (
$attr$name) = preg_match ("/^(.*)[ ]+([D|A|H|S|R]+)$/"trim ($regs[1]), $regs2)
                        ? array (
trim ($regs2[2]), trim ($regs2[1]))
                        : array (
''trim ($regs[1]));
                    list (
$his$im) = array (
                    
split(':'$regs[6]), strpos("JanFebMarAprMayJunJulAugSepOctNovDec"$regs[4]) / 3);
                    
$i = ($name <> '.' && $name <> '..')
                        ? array (
                            
$name,
                            (
strpos($attr,'D') === FALSE) ? 'file' 'folder',
                            
'attr' => $attr,
                            
'size' => intval($regs[2]),
                            
'time' => mktime ($his[0], $his[1], $his[2], $im$regs[5], $regs[7])
                          )
                        : array();
                    break;
                case 
'error':   trigger_error($regs[1], E_USER_ERROR);
            }
            if (
$i) switch ($i[1]) {
                case 
'file':
                case 
'folder':    $info['info'][$i[0]] = $i;
                case 
'disk':
                case 
'server':
                case 
'workgroup'$info[$i[1]][] = $i[0];
            }
        }
        
pclose($output);
        return 
$info;
    }


    
# stats

    
function url_stat ($url$flags STREAM_URL_STAT_LINK) {
        if (
$s smb::getstatcache($url)) { return $s; }
        list (
$stat$pu) = array (array (), smb::parse_url ($url));
        switch (
$pu['type']) {
            case 
'host':
                if (
$o smb::look ($pu))
                   
$stat stat ("/tmp");
                else
                   
trigger_error ("url_stat(): list failed for host '{$host}'"E_USER_WARNING);
                break;
            case 
'share':
                if (
$o smb::look ($pu)) {
                   
$found FALSE;
                   
$lshare strtolower ($pu['share']);  # fix by Eric Leung
                   
foreach ($o['disk'] as $s) if ($lshare == strtolower($s)) {
                       
$found TRUE;
                       
$stat stat ("/tmp");
                       break;
                   }
                   if (! 
$found)
                      
trigger_error ("url_stat(): disk resource '{$share}' not found in '{$host}'"E_USER_WARNING);
                }
                break;
            case 
'path':
                if (
$o smb::execute ('dir "'.$pu['path'].'"'$pu)) {
                    
$p split ("[\\]"$pu['path']);
                    
$name $p[count($p)-1];
                    if (isset (
$o['info'][$name])) {
                       
$stat smb::addstatcache ($url$o['info'][$name]);
                    } else {
                       
trigger_error ("url_stat(): path '{$pu['path']}' not found"E_USER_WARNING);
                    }
                } else {
                    
trigger_error ("url_stat(): dir failed for path '{$pu['path']}'"E_USER_WARNING);
                }
                break;
            default: 
trigger_error ('error in URL'E_USER_ERROR);
        }
        return 
$stat;
    }

    function 
addstatcache ($url$info) {
        global 
$__smb_cache;
        
$is_file = (strpos ($info['attr'],'D') === FALSE);
        
$s = ($is_file) ? stat ('/etc/passwd') : stat ('/tmp');
        
$s[7] = $s['size'] = $info['size'];
        
$s[8] = $s[9] = $s[10] = $s['atime'] = $s['mtime'] = $s['ctime'] = $info['time'];
        return 
$__smb_cache['stat'][$url] = $s;
    }

    function 
getstatcache ($url) {
        global 
$__smb_cache;
        return isset (
$__smb_cache['stat'][$url]) ? $__smb_cache['stat'][$url] : FALSE;
    }

    function 
clearstatcache ($url='') {
        global 
$__smb_cache;
        if (
$url == ''$__smb_cache['stat'] = array (); else unset ($__smb_cache['stat'][$url]);
    }


    
# commands

    
function unlink ($url) {
        
$pu smb::parse_url($url);
        if (
$pu['type'] <> 'path'trigger_error('unlink(): error in URL'E_USER_ERROR);
        
smb::clearstatcache ($url);
        return 
smb::execute ('del "'.$pu['path'].'"'$pu);
    }

    function 
rename ($url_from$url_to) {
        list (
$from$to) = array (smb::parse_url($url_from), smb::parse_url($url_to));
        if (
$from['host'] <> $to['host'] ||
            
$from['share'] <> $to['share'] ||
            
$from['user'] <> $to['user'] ||
            
$from['pass'] <> $to['pass'] ||
            
$from['domain'] <> $to['domain']) {
            
trigger_error('rename(): FROM & TO must be in same server-share-user-pass-domain'E_USER_ERROR);
        }
        if (
$from['type'] <> 'path' || $to['type'] <> 'path') {
            
trigger_error('rename(): error in URL'E_USER_ERROR);
        }
        
smb::clearstatcache ($url_from);
        return 
smb::execute ('rename "'.$from['path'].'" "'.$to['path'].'"'$to);
    }

    function 
mkdir ($url$mode$options) {
        
$pu smb::parse_url($url);
        if (
$pu['type'] <> 'path'trigger_error('mkdir(): error in URL'E_USER_ERROR);
        return 
smb::execute ('mkdir "'.$pu['path'].'"'$pu);
    }

    function 
rmdir ($url) {
        
$pu smb::parse_url($url);
        if (
$pu['type'] <> 'path'trigger_error('rmdir(): error in URL'E_USER_ERROR);
        
smb::clearstatcache ($url);
        return 
smb::execute ('rmdir "'.$pu['path'].'"'$pu);
    }

}

###################################################################
# SMB_STREAM_WRAPPER - class to be registered for smb:// URLs
###################################################################

class smb_stream_wrapper extends smb {

    
# variables

    
var $stream$url$parsed_url = array (), $mode$tmpfile;
    var 
$need_flush FALSE;
    var 
$dir = array (), $dir_index = -1;


    
# directories

    
function dir_opendir ($url$options) {
        if (
$d $this->getdircache ($url)) {
            
$this->dir $d;
            
$this->dir_index 0;
            return 
TRUE;
        }
        
$pu smb::parse_url ($url);
        switch (
$pu['type']) {
            case 
'host':
                if (
$o smb::look ($pu)) {
                   
$this->dir $o['disk'];
                   
$this->dir_index 0;
                } else {
                   
trigger_error ("dir_opendir(): list failed for host '{$pu['host']}'"E_USER_WARNING);
                }
                break;
            case 
'share':
            case 
'path':
                if (
$o smb::execute ('dir "'.$pu['path'].'\*"'$pu)) {
                   
$this->dir array_keys($o['info']);
                   
$this->dir_index 0;
                   
$this->adddircache ($url$this->dir);
                   foreach (
$o['info'] as $name => $info) {
                       
smb::addstatcache($url '/' urlencode($name), $info);
                   }
                } else {
                   
trigger_error ("dir_opendir(): dir failed for path '{$path}'"E_USER_WARNING);
                }
                break;
            default:
                
trigger_error ('dir_opendir(): error in URL'E_USER_ERROR);
        }
        return 
TRUE;
    }

    function 
dir_readdir () { return ($this->dir_index count($this->dir)) ? $this->dir[$this->dir_index++] : FALSE; }

    function 
dir_rewinddir () { $this->dir_index 0; }

    function 
dir_closedir () { $this->dir = array(); $this->dir_index = -1; return TRUE; }


    
# cache

    
function adddircache ($url$content) {
        global 
$__smb_cache;
        return 
$__smb_cache['dir'][$url] = $content;
    }

    function 
getdircache ($url) {
        global 
$__smb_cache;
        return isset (
$__smb_cache['dir'][$url]) ? $__smb_cache['dir'][$url] : FALSE;
    }

    function 
cleardircache ($url='') {
        global 
$__smb_cache;
        if (
$url == ''$__smb_cache['dir'] = array (); else unset ($__smb_cache['dir'][$url]);
    }


    
# streams

    
function stream_open ($url$mode$options$opened_path) {
        
$this->url $url;
        
$this->mode $mode;
        
$this->parsed_url $pu smb::parse_url($url);
        if (
$pu['type'] <> 'path'trigger_error('stream_open(): error in URL'E_USER_ERROR);
        switch (
$mode) {
            case 
'r':
            case 
'r+':
            case 
'rb':
            case 
'a':
            case 
'a+':  $this->tmpfile tempnam('/tmp''smb.down.');
                        
smb::execute ('get "'.$pu['path'].'" "'.$this->tmpfile.'"'$pu);
                        break;
            case 
'w':
            case 
'w+':
            case 
'wb':
            case 
'x':
            case 
'x+':  $this->cleardircache();
                        
$this->tmpfile tempnam('/tmp''smb.up.');
        }
        
$this->stream fopen ($this->tmpfile$mode);
        return 
TRUE;
    }

    function 
stream_close () { return fclose($this->stream); }

    function 
stream_read ($count) { return fread($this->stream$count); }

    function 
stream_write ($data) { $this->need_flush TRUE; return fwrite($this->stream$data); }

    function 
stream_eof () { return feof($this->stream); }

    function 
stream_tell () { return ftell($this->stream); }

    function 
stream_seek ($offset$whence=null) { return fseek($this->stream$offset$whence); }

    function 
stream_flush () {
        if (
$this->mode <> 'r' && $this->need_flush) {
            
smb::clearstatcache ($this->url);
            
smb::execute ('put "'.$this->tmpfile.'" "'.$this->parsed_url['path'].'"'$this->parsed_url);
            
$this->need_flush FALSE;
        }
    }

    function 
stream_stat () { return smb::url_stat ($this->url); }

    function 
__destruct () {
        if (
$this->tmpfile <> '') {
            if (
$this->need_flush$this->stream_flush ();
            
unlink ($this->tmpfile);

        }
    }

}

###################################################################
# Register 'smb' protocol !
###################################################################

stream_wrapper_register('smb''smb_stream_wrapper')
    or die (
'Failed to register protocol');

?>
« Last Edit: February 17, 2011, 12:33:56 AM by perkiset » Logged

It is now believed, that after having lived in one compound with 3 wives and never leaving the house for 5 years, Bin Laden called the U.S. Navy Seals himself.
herbacious
Journeyman
***
Offline Offline

Posts: 51


View Profile
« Reply #1 on: February 18, 2011, 02:37:00 PM »

v nice Smiley
Logged
Pages: [1]
  Print  
 
Jump to:  

Perkiset's Place Home   Best of The Cache   phpMyIDE: MySQL Stored Procedures, Functions & Triggers
Politics @ Perkiset's   Pinkhat's Perspective   
cache
mart
coder
programmers
ajax
php
javascript
Powered by MySQL Powered by PHP Powered by SMF 1.1.2 | SMF © 2006-2007, Simple Machines LLC
Seo4Smf v0.2 © Webmaster's Talks


Valid XHTML 1.0! Valid CSS!