diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..efb5642
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,58 @@
+*~
+
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+env/
+bin/
+build/
+develop-eggs/
+dist/
+eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+*.egg-info/
+.installed.cfg
+*.egg
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.coverage
+.cache
+nosetests.xml
+coverage.xml
+
+# Translations
+*.mo
+
+# Mr Developer
+.mr.developer.cfg
+.project
+.pydevproject
+
+# Rope
+.ropeproject
+
+# Django stuff:
+*.log
+*.pot
+
+# Sphinx documentation
+docs/_build/
+
+# PyCharm
+.idea
\ No newline at end of file
diff --git a/CHANGELOG b/CHANGELOG
index e52126b..d59f598 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,5 +1,102 @@
+pysmb-1.2.6, 9 Dec 2020
+=======================
+- Fix bug in SMB1 store file implmentation which generates SMB_COM_WRITE_ANDX
+ packets larger than the allowed max buffer size (#175)
+
+pysmb-1.2.5, 18 Oct 2020
+=======================
+- Fix bug in filename encoding which leads to failure for file retrieval and upload operations (#170 #171).
+- Improve resetFileAttributes() method in SMBConnection class to allow the
+ new attribute to be specified in the reset operation (#172).
+
+pysmb-1.2.4, 6 Oct 2020
+=======================
+- Remove dependency on pycrypto as it is no longer under active maintenance
+
+pysmb-1.2.3, 6 Oct 2020
+=======================
+- Fix bug in session key generation during session negotiation (#166)
+- Fix bug in SMB message signing which leads to operation failures with Samba services.
+
+pysmb-1.2.2, 5 Sep 2020
+=======================
+- Improve SMB URL handlers to support specifying server's machine name and IP
+ address. (#162)
+- Improvements to documentation on SMB URLs (#160)
+
+pysmb-1.2.1, 17 May 2020
+========================
+- Fix bug in deleteFiles() method which can fail for certain search patterns.
+
+pysmb-1.2.0, 17 May 2020
+=========================
+- Add new parameter, delete_matching_folders, to deleteFiles() method to
+ support deletion of child folders that match the search pattern.
+
+pysmb-1.1.29, 16 May 2020
+=========================
+- Fix unhandled exception for short NBNS queries #149
+- Fix wildcard file deletion with servers on SMB2 protocol #33
+
+pysmb-1.1.28, 23 Nov 2019
+========================
+- SharedFile instances returned from the listPath() method now has a new
+ file_id attribute which represents the file reference number given by the SMB server.
+
+pysmb-1.1.27, 9 Jan 2019
+========================
+- Remove support for SMB-2.1 dialect as it seems to have issues with Windows 2008 R2
+
+pysmb-1.1.26, 5 Jan 2019
+========================
+- Prevents OperationError from being raised when listPath() operation does not
+ return any matching file results.
+- SMBConnection is now a context manager #122.
+
+pysmb-1.1.25, 28 July 2018
+========================
+- Fix buggy support for search parameter in listPath() method. Add
+ SMB_FILE_ATTRIBUTE_INCL_NORMAL bit constant to include 'normal' files with
+ other file types in the returned result. From now on, pysmb defines a 'normal' file
+ as a file entry that is not read-only, not hidden, not system, not archive and
+ not a directory; it ignores other attributes like compression, indexed, sparse,
+ temporary and encryption. listPath() method will now include 'normal' files
+ using the default search parameter.
+- Add isNormal property to SharedFile class to support test if the file is a
+ 'normal' file (according to pysmb definition of 'normal' file).
+
+pysmb-1.1.24, 19 July 2018
+========================
+- Improve listPath implementation for SMB1
+- Support for STATUS_PENDING responses across all SMB2 operations.
+
+pysmb-1.1.23, 5 May 2018
+========================
+- Fix bug in listShares() method which fails when the remote server has many shares.
+- Improve echo() method to test and fail if the provided data to echo is not a bytes object.
+- Fix bug in listPath() method where the path to query is not properly terminated.
+
+pysmb-1.1.22, 17 Sep 2017
+========================
+- Fix bug in getAttributes() method which should return only the filename
+ instead of the entire path for the filename property for the return result.
+
+pysmb-1.1.21, 9 Sep 2017
+========================
+- Fix bug where timestamp values for SMB1 getAttributes() response are not
+ converted properly from FILETIME to epoch time values.
+
+pysmb-1.1.20, 13 Aug 2017
+=========================
+- Add getSecurity() method to support security descriptors query via SMB2
+- Improve retrieveFile() and retrieveFileFromOffset() methods to allow file
+ retrievals over SMB2 even when the file is being locked on the server.
+- Silently discards NMB SESSION_KEEPALIVE packets instead of raising warnings.
+- SMB sessionID will be sent in ECHO requests to conform to SMB2 specs.
+- Fix type errors for MD4 functions in python3.
+
pysmb-1.1.19, 13 Nov 2016
-========================
+=========================
- Ignore STATUS_PENDING during delete and file store operations
pysmb-1.1.18, 9 Apr 2016
diff --git a/LICENSE b/LICENSE
index d183506..a02f43a 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,5 +1,5 @@
-Copyright (C) 2001-2015 Michael Teo
+Copyright (C) 2001-2020 Michael Teo
This software is provided 'as-is', without any express or implied warranty.
In no event will the author be held liable for any damages arising from the
diff --git a/MANIFEST b/MANIFEST
new file mode 100644
index 0000000..172ef7f
--- /dev/null
+++ b/MANIFEST
@@ -0,0 +1,134 @@
+CHANGELOG
+LICENSE
+README.txt
+setup.py
+docs/doctrees/environment.pickle
+docs/doctrees/extending.doctree
+docs/doctrees/index.doctree
+docs/doctrees/api/nmb_NBNSProtocol.doctree
+docs/doctrees/api/nmb_NetBIOS.doctree
+docs/doctrees/api/smb_SMBConnection.doctree
+docs/doctrees/api/smb_SMBHandler.doctree
+docs/doctrees/api/smb_SMBProtocolFactory.doctree
+docs/doctrees/api/smb_SharedDevice.doctree
+docs/doctrees/api/smb_SharedFile.doctree
+docs/doctrees/api/smb_exceptions.doctree
+docs/html/.buildinfo
+docs/html/extending.html
+docs/html/genindex.html
+docs/html/index.html
+docs/html/objects.inv
+docs/html/search.html
+docs/html/searchindex.js
+docs/html/_modules/index.html
+docs/html/_modules/nmb/NetBIOS.html
+docs/html/_modules/nmb/NetBIOSProtocol.html
+docs/html/_modules/smb/SMBConnection.html
+docs/html/_modules/smb/SMBProtocol.html
+docs/html/_modules/smb/base.html
+docs/html/_modules/smb/smb_structs.html
+docs/html/_sources/extending.txt
+docs/html/_sources/index.txt
+docs/html/_sources/api/nmb_NBNSProtocol.txt
+docs/html/_sources/api/nmb_NetBIOS.txt
+docs/html/_sources/api/smb_SMBConnection.txt
+docs/html/_sources/api/smb_SMBHandler.txt
+docs/html/_sources/api/smb_SMBProtocolFactory.txt
+docs/html/_sources/api/smb_SharedDevice.txt
+docs/html/_sources/api/smb_SharedFile.txt
+docs/html/_sources/api/smb_exceptions.txt
+docs/html/_static/ajax-loader.gif
+docs/html/_static/basic.css
+docs/html/_static/comment-bright.png
+docs/html/_static/comment-close.png
+docs/html/_static/comment.png
+docs/html/_static/contents.png
+docs/html/_static/doctools.js
+docs/html/_static/down-pressed.png
+docs/html/_static/down.png
+docs/html/_static/file.png
+docs/html/_static/jquery.js
+docs/html/_static/minus.png
+docs/html/_static/navigation.png
+docs/html/_static/plus.png
+docs/html/_static/pygments.css
+docs/html/_static/searchtools.js
+docs/html/_static/sphinxdoc.css
+docs/html/_static/underscore.js
+docs/html/_static/up-pressed.png
+docs/html/_static/up.png
+docs/html/_static/websupport.js
+docs/html/api/nmb_NBNSProtocol.html
+docs/html/api/nmb_NetBIOS.html
+docs/html/api/smb_SMBConnection.html
+docs/html/api/smb_SMBHandler.html
+docs/html/api/smb_SMBProtocolFactory.html
+docs/html/api/smb_SharedDevice.html
+docs/html/api/smb_SharedFile.html
+docs/html/api/smb_exceptions.html
+nmb/NetBIOS.py
+nmb/NetBIOSProtocol.py
+nmb/__init__.py
+nmb/base.py
+nmb/nmb_constants.py
+nmb/nmb_structs.py
+nmb/utils.py
+smb/SMBConnection.py
+smb/SMBHandler.py
+smb/SMBProtocol.py
+smb/__init__.py
+smb/base.py
+smb/ntlm.py
+smb/securityblob.py
+smb/smb_constants.py
+smb/smb_structs.py
+smb/utils/README.txt
+smb/utils/U32.py
+smb/utils/__init__.py
+smb/utils/md4.py
+smb/utils/pyDes.py
+sphinx/Makefile
+sphinx/make.bat
+sphinx/source/conf.py
+sphinx/source/extending.rst
+sphinx/source/index.rst
+sphinx/source/api/nmb_NBNSProtocol.rst
+sphinx/source/api/nmb_NetBIOS.rst
+sphinx/source/api/smb_SMBConnection.rst
+sphinx/source/api/smb_SMBHandler.rst
+sphinx/source/api/smb_SMBProtocolFactory.rst
+sphinx/source/api/smb_SharedDevice.rst
+sphinx/source/api/smb_SharedFile.rst
+sphinx/source/api/smb_exceptions.rst
+tests/README_1st.txt
+tests/__init__.py
+tests/connection.ini
+tests/smbtest.zip
+tests/test_ntlm.py
+tests/test_securityblob.py
+tests/NetBIOSTests/__init__.py
+tests/NetBIOSTests/test_queryname.py
+tests/NetBIOSTwistedTests/__init__.py
+tests/NetBIOSTwistedTests/test_queryname.py
+tests/SMBConnectionTests/__init__.py
+tests/SMBConnectionTests/test_SMBHandler.py
+tests/SMBConnectionTests/test_auth.py
+tests/SMBConnectionTests/test_createdeletedirectory.py
+tests/SMBConnectionTests/test_echo.py
+tests/SMBConnectionTests/test_listpath.py
+tests/SMBConnectionTests/test_listshares.py
+tests/SMBConnectionTests/test_rename.py
+tests/SMBConnectionTests/test_retrievefile.py
+tests/SMBConnectionTests/test_storefile.py
+tests/SMBConnectionTests/util.py
+tests/SMBTwistedTests/__init__.py
+tests/SMBTwistedTests/test_auth.py
+tests/SMBTwistedTests/test_createdeletedirectory.py
+tests/SMBTwistedTests/test_echo.py
+tests/SMBTwistedTests/test_listpath.py
+tests/SMBTwistedTests/test_listshares.py
+tests/SMBTwistedTests/test_rename.py
+tests/SMBTwistedTests/test_retrievefile.py
+tests/SMBTwistedTests/test_storefile.py
+tests/SMBTwistedTests/util.py
+tests/SupportFiles/binary.dat
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000..b22628d
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1,11 @@
+include LICENSE
+include CHANGELOG
+recursive-include python2 *
+recursive-exclude python2 *.pyc
+recursive-exclude python2 *~
+recursive-include python3 *
+recursive-exclude python3 *.pyc
+recursive-exclude python3 *~
+recursive-include sphinx *
+recursive-include docs *
+recursive-exclude docs *.zip
diff --git a/PKG-INFO b/PKG-INFO
deleted file mode 100644
index ba886a1..0000000
--- a/PKG-INFO
+++ /dev/null
@@ -1,26 +0,0 @@
-Metadata-Version: 1.1
-Name: pysmb
-Version: 1.1.19
-Summary: pysmb is an experimental SMB/CIFS library written in Python to support file sharing between Windows and Linux machines
-Home-page: https://miketeo.net/projects/pysmb
-Author: Michael Teo
-Author-email: miketeo@miketeo.net
-License: zlib/libpng
-Description: pysmb is an experimental SMB/CIFS library written in Python. It implements the client-side SMB/CIFS protocol which allows your Python application to access and transfer files to/from SMB/CIFS shared folders like your Windows file sharing and Samba folders.
-Keywords: windows samba cifs sharing ftp smb linux
-Platform: UNKNOWN
-Classifier: Development Status :: 5 - Production/Stable
-Classifier: Environment :: Win32 (MS Windows)
-Classifier: Environment :: Console
-Classifier: License :: OSI Approved :: zlib/libpng License
-Classifier: Operating System :: Microsoft :: Windows
-Classifier: Operating System :: POSIX
-Classifier: Programming Language :: Python
-Classifier: Programming Language :: Python :: 2.4
-Classifier: Programming Language :: Python :: 2.5
-Classifier: Programming Language :: Python :: 2.6
-Classifier: Programming Language :: Python :: 2.7
-Classifier: Programming Language :: Python :: 3
-Classifier: Topic :: Communications :: File Sharing
-Classifier: Topic :: Software Development :: Libraries :: Python Modules
-Classifier: Topic :: System :: Networking
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..9c23246
--- /dev/null
+++ b/README.md
@@ -0,0 +1,8 @@
+pysmb
+=====
+
+pysmb is an experimental SMB/CIFS library written in Python. It implements the client-side SMB/CIFS protocol (SMB1 and SMB2) which allows your Python application to access and transfer files to/from SMB/CIFS shared folders like your Windows file sharing and Samba folders.
+
+* Primary Project Website: https://miketeo.net/blog/projects/pysmb
+* Documentation: http://pysmb.readthedocs.io/
+* Issue Tracker: Please use the [issue tracker on github](https://github.com/miketeo/pysmb/issues).
diff --git a/docs/doctrees/api/nmb_NBNSProtocol.doctree b/docs/doctrees/api/nmb_NBNSProtocol.doctree
index 63a5cd0..b69c5c9 100644
Binary files a/docs/doctrees/api/nmb_NBNSProtocol.doctree and b/docs/doctrees/api/nmb_NBNSProtocol.doctree differ
diff --git a/docs/doctrees/api/nmb_NetBIOS.doctree b/docs/doctrees/api/nmb_NetBIOS.doctree
index 9b279b3..fc1e88e 100644
Binary files a/docs/doctrees/api/nmb_NetBIOS.doctree and b/docs/doctrees/api/nmb_NetBIOS.doctree differ
diff --git a/docs/doctrees/api/smb_SMBConnection.doctree b/docs/doctrees/api/smb_SMBConnection.doctree
index 383d6bf..593caf4 100644
Binary files a/docs/doctrees/api/smb_SMBConnection.doctree and b/docs/doctrees/api/smb_SMBConnection.doctree differ
diff --git a/docs/doctrees/api/smb_SMBHandler.doctree b/docs/doctrees/api/smb_SMBHandler.doctree
index 2fb06a4..2481234 100644
Binary files a/docs/doctrees/api/smb_SMBHandler.doctree and b/docs/doctrees/api/smb_SMBHandler.doctree differ
diff --git a/docs/doctrees/api/smb_SMBProtocolFactory.doctree b/docs/doctrees/api/smb_SMBProtocolFactory.doctree
index d424daa..dccca6e 100644
Binary files a/docs/doctrees/api/smb_SMBProtocolFactory.doctree and b/docs/doctrees/api/smb_SMBProtocolFactory.doctree differ
diff --git a/docs/doctrees/api/smb_SharedDevice.doctree b/docs/doctrees/api/smb_SharedDevice.doctree
index eb0fa96..637a4fc 100644
Binary files a/docs/doctrees/api/smb_SharedDevice.doctree and b/docs/doctrees/api/smb_SharedDevice.doctree differ
diff --git a/docs/doctrees/api/smb_SharedFile.doctree b/docs/doctrees/api/smb_SharedFile.doctree
index faa32e5..68050e3 100644
Binary files a/docs/doctrees/api/smb_SharedFile.doctree and b/docs/doctrees/api/smb_SharedFile.doctree differ
diff --git a/docs/doctrees/api/smb_security_descriptors.doctree b/docs/doctrees/api/smb_security_descriptors.doctree
new file mode 100644
index 0000000..734f4f6
Binary files /dev/null and b/docs/doctrees/api/smb_security_descriptors.doctree differ
diff --git a/docs/doctrees/environment.pickle b/docs/doctrees/environment.pickle
index 987c4bf..cba0814 100644
Binary files a/docs/doctrees/environment.pickle and b/docs/doctrees/environment.pickle differ
diff --git a/docs/doctrees/extending.doctree b/docs/doctrees/extending.doctree
index dc0b3fc..4031973 100644
Binary files a/docs/doctrees/extending.doctree and b/docs/doctrees/extending.doctree differ
diff --git a/docs/doctrees/index.doctree b/docs/doctrees/index.doctree
index 807f40d..cf4fb9f 100644
Binary files a/docs/doctrees/index.doctree and b/docs/doctrees/index.doctree differ
diff --git a/docs/doctrees/upgrading.doctree b/docs/doctrees/upgrading.doctree
new file mode 100644
index 0000000..d6f3158
Binary files /dev/null and b/docs/doctrees/upgrading.doctree differ
diff --git a/docs/html/.buildinfo b/docs/html/.buildinfo
index bf80f8a..5069b78 100644
--- a/docs/html/.buildinfo
+++ b/docs/html/.buildinfo
@@ -1,4 +1,4 @@
# Sphinx build info version 1
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
-config: 8ec856095809db2990831edf01ebc5a4
+config: 25ba8f2e92503ade60b28e1a69a6901b
tags: 645f666f9bcd5a90fca523b33c5a78b7
diff --git a/docs/html/_modules/index.html b/docs/html/_modules/index.html
index c518f1e..d2a25a5 100644
--- a/docs/html/_modules/index.html
+++ b/docs/html/_modules/index.html
@@ -6,7 +6,7 @@
- Overview: module code — pysmb 1.1.18 documentation
+ Overview: module code — pysmb 1.2.1 documentation
@@ -14,7 +14,7 @@
-
+
@@ -84,6 +87,7 @@
Create a new SMBConnection instance. *username* and *password* are the user credentials required to authenticate the underlying SMB connection with the remote server.
+ *password* can be a string or a callable returning a string. File operations can only be proceeded after the connection has been authenticated successfully. Note that you need to call *connect* method to actually establish the SMB connection to the remote server and perform authentication.
@@ -135,6 +139,15 @@
total_sent=total_sent+sent#
+ # Support for "with" context
+ #
+ def__enter__(self):
+ returnself
+
+ def__exit__(self,*args):
+ self.close()
+
+ ## Misc Properties#
@@ -216,15 +229,23 @@
returnresults
[docs]deflistPath(self,service_name,path,
- search=SMB_FILE_ATTRIBUTE_READONLY|SMB_FILE_ATTRIBUTE_HIDDEN|SMB_FILE_ATTRIBUTE_SYSTEM|SMB_FILE_ATTRIBUTE_DIRECTORY|SMB_FILE_ATTRIBUTE_ARCHIVE,
+ search=SMB_FILE_ATTRIBUTE_READONLY|SMB_FILE_ATTRIBUTE_HIDDEN|SMB_FILE_ATTRIBUTE_SYSTEM|SMB_FILE_ATTRIBUTE_DIRECTORY|SMB_FILE_ATTRIBUTE_ARCHIVE|SMB_FILE_ATTRIBUTE_INCL_NORMAL,pattern='*',timeout=30):""" Retrieve a directory listing of files/folders at *path*
+
+ For simplicity, pysmb defines a "normal" file as a file entry that is not read-only, not hidden, not system, not archive and not a directory.
+ It ignores other attributes like compression, indexed, sparse, temporary and encryption.
+
+ Note that the default search parameter will query for all read-only (SMB_FILE_ATTRIBUTE_READONLY), hidden (SMB_FILE_ATTRIBUTE_HIDDEN),
+ system (SMB_FILE_ATTRIBUTE_SYSTEM), archive (SMB_FILE_ATTRIBUTE_ARCHIVE), normal (SMB_FILE_ATTRIBUTE_INCL_NORMAL) files
+ and directories (SMB_FILE_ATTRIBUTE_DIRECTORY).
+ If you do not need to include "normal" files in the result, define your own search parameter without the SMB_FILE_ATTRIBUTE_INCL_NORMAL constant.
+ SMB_FILE_ATTRIBUTE_NORMAL should be used by itself and not be used with other bit constants. :param string/unicode service_name: the name of the shared folder for the *path* :param string/unicode path: path relative to the *service_name* where we are interested to learn about its files/sub-folders. :param integer search: integer value made up from a bitwise-OR of *SMB_FILE_ATTRIBUTE_xxx* bits (see smb_constants.py).
- The default *search* value will query for all read-only, hidden, system, archive files and directories. :param string/unicode pattern: the filter to apply to the results before returning to the client. :return: A list of :doc:`smb.base.SharedFile<smb_SharedFile>` instances. """
@@ -308,6 +329,37 @@
self.is_busy=Truetry:self._getAttributes(service_name,path,cb,eb,timeout)
+ whileself.is_busy:
+ self._pollForNetBIOSPacket(timeout)
+ finally:
+ self.is_busy=False
+
+ returnresults[0]
+
+
[docs]defgetSecurity(self,service_name,path,timeout=30):
+ """
+ Retrieve the security descriptor of the file at *path* on the *service_name*.
+
+ :param string/unicode service_name: the name of the shared folder for the *path*
+ :param string/unicode path: Path of the file on the remote server. If the file cannot be opened for reading, an :doc:`OperationFailure<smb_exceptions>` will be raised.
+ :return: A :class:`smb.security_descriptors.SecurityDescriptor` instance containing the security information of the file.
+ """
+ ifnotself.sock:
+ raiseNotConnectedError('Not connected to server')
+
+ results=[]
+
+ defcb(info):
+ self.is_busy=False
+ results.append(info)
+
+ defeb(failure):
+ self.is_busy=False
+ raisefailure
+
+ self.is_busy=True
+ try:
+ self._getSecurity(service_name,path,cb,eb,timeout)whileself.is_busy:self._pollForNetBIOSPacket(timeout)finally:
@@ -413,9 +465,11 @@
returnresults[0]
[docs]defdeleteFiles(self,service_name,path_file_pattern,delete_matching_folders=False,timeout=30):""" Delete one or more regular files. It supports the use of wildcards in file names, allowing for deletion of multiple files in a single request.
+
+ If delete_matching_folders is True, immediate sub-folders that match the path_file_pattern will be deleted recursively. :param string/unicode service_name: Contains the name of the shared folder. :param string/unicode path_file_pattern: The pathname of the file(s) to be deleted, relative to the service_name.
@@ -435,7 +489,7 @@
self.is_busy=Truetry:
- self._deleteFiles(service_name,path_file_pattern,cb,eb,timeout=timeout)
+ self._deleteFiles(service_name,path_file_pattern,delete_matching_folders,cb,eb,timeout=timeout)whileself.is_busy:self._pollForNetBIOSPacket(timeout)finally:
@@ -558,7 +612,7 @@
""" Send an echo command containing *data* to the remote SMB/CIFS server. The remote SMB/CIFS will reply with the same *data*.
- :param string data: Data to send to the remote server.
+ :param bytes data: Data to send to the remote server. Must be a bytes object. :return: The *data* parameter """ifnotself.sock:
@@ -656,12 +710,15 @@
[docs]deflistPath(self,service_name,path,
- search=SMB_FILE_ATTRIBUTE_READONLY|SMB_FILE_ATTRIBUTE_HIDDEN|SMB_FILE_ATTRIBUTE_SYSTEM|SMB_FILE_ATTRIBUTE_DIRECTORY|SMB_FILE_ATTRIBUTE_ARCHIVE,
+ search=SMB_FILE_ATTRIBUTE_READONLY|SMB_FILE_ATTRIBUTE_HIDDEN|SMB_FILE_ATTRIBUTE_SYSTEM|SMB_FILE_ATTRIBUTE_DIRECTORY|SMB_FILE_ATTRIBUTE_ARCHIVE|SMB_FILE_ATTRIBUTE_INCL_NORMAL,pattern='*',timeout=30):""" Retrieve a directory listing of files/folders at *path*
+
+ For simplicity, pysmb defines a "normal" file as a file entry that is not read-only, not hidden, not system, not archive and not a directory.
+ It ignores other attributes like compression, indexed, sparse, temporary and encryption.
+
+ Note that the default search parameter will query for all read-only (SMB_FILE_ATTRIBUTE_READONLY), hidden (SMB_FILE_ATTRIBUTE_HIDDEN),
+ system (SMB_FILE_ATTRIBUTE_SYSTEM), archive (SMB_FILE_ATTRIBUTE_ARCHIVE), normal (SMB_FILE_ATTRIBUTE_INCL_NORMAL) files
+ and directories (SMB_FILE_ATTRIBUTE_DIRECTORY).
+ If you do not need to include "normal" files in the result, define your own search parameter without the SMB_FILE_ATTRIBUTE_INCL_NORMAL constant.
+ SMB_FILE_ATTRIBUTE_NORMAL should be used by itself and not be used with other bit constants. :param string/unicode service_name: the name of the shared folder for the *path* :param string/unicode path: path relative to the *service_name* where we are interested to learn about its files/sub-folders. :param integer search: integer value made up from a bitwise-OR of *SMB_FILE_ATTRIBUTE_xxx* bits (see smb_constants.py).
- The default *search* value will query for all read-only, hidden, system, archive files and directories. :param string/unicode pattern: the filter to apply to the results before returning to the client. :param integer/float timeout: Number of seconds that pysmb will wait before raising *SMBTimeout* via the returned *Deferred* instance's *errback* method. :return: A *twisted.internet.defer.Deferred* instance. The callback function will be called with a list of :doc:`smb.base.SharedFile<smb_SharedFile>` instances.
@@ -431,7 +442,7 @@
""" Send an echo command containing *data* to the remote SMB/CIFS server. The remote SMB/CIFS will reply with the same *data*.
- :param string data: Data to send to the remote server.
+ :param bytes data: Data to send to the remote server. Must be a bytes object. :param integer/float timeout: Number of seconds that pysmb will wait before raising *SMBTimeout* via the returned *Deferred* instance's *errback* method. :return: A *twisted.internet.defer.Deferred* instance. The callback function will be called with the *data* parameter. """
@@ -475,12 +486,15 @@
@@ -68,6 +71,7 @@
fromsmb2_constantsimport*fromsmb_structsimport*fromsmb2_structsimport*
+from.security_descriptorsimportSecurityDescriptorfromnmb.baseimportNMBSessionfromutilsimportconvertFILETIMEtoEpochimportntlm,securityblob
@@ -122,13 +126,14 @@
def__init__(self,username,password,my_name,remote_name,domain='',use_ntlm_v2=True,sign_options=SIGN_WHEN_REQUIRED,is_direct_tcp=False):NMBSession.__init__(self,my_name,remote_name,is_direct_tcp=is_direct_tcp)self.username=_convert_to_unicode(username)
- self.password=_convert_to_unicode(password)
+ self._password=passwordself.domain=_convert_to_unicode(domain)self.sign_options=sign_optionsself.is_direct_tcp=is_direct_tcpself.use_ntlm_v2=use_ntlm_v2#: Similar to LMAuthenticationPolicy and NTAuthenticationPolicy as described in [MS-CIFS] 3.2.1.1self.smb_message=SMBMessage()self.is_using_smb2=False#: Are we communicating using SMB2 protocol? self.smb_message will be a SMB2Message instance if this flag is True
+ self.async_requests={}#: AsyncID mapped to _PendingRequest instanceself.pending_requests={}#: MID mapped to _PendingRequest instanceself.connected_trees={}#: Share name mapped to TIDself.next_rpc_call_id=1#: Next RPC callID value. Not used directly in SMB message. Usually encapsulated in sub-commands under SMB_COM_TRANSACTION or SMB_COM_TRANSACTION2 messages
@@ -167,6 +172,10 @@
(self.use_ntlm_v2and'v2')or'v1',(SUPPORT_EXTENDED_SECURITYand'with')or'without')
+ @property
+ defpassword(self):
+ password=self._password()ifcallable(self._password)elseself._password
+ return_convert_to_unicode(password)## NMBSession Methods
@@ -236,6 +245,7 @@
self._listShares=self._listShares_SMB1self._listPath=self._listPath_SMB1self._listSnapshots=self._listSnapshots_SMB1
+ self._getSecurity=self._getSecurity_SMB1self._getAttributes=self._getAttributes_SMB1self._retrieveFile=self._retrieveFile_SMB1self._retrieveFileFromOffset=self._retrieveFileFromOffset_SMB1
@@ -259,6 +269,7 @@
self._listPath=self._listPath_SMB2self._listSnapshots=self._listSnapshots_SMB2self._getAttributes=self._getAttributes_SMB2
+ self._getSecurity=self._getSecurity_SMB2self._retrieveFile=self._retrieveFile_SMB2self._retrieveFileFromOffset=self._retrieveFileFromOffset_SMB2self._storeFile=self._storeFile_SMB2
@@ -282,7 +293,7 @@
ifsmb_message.mid==0:smb_message.mid=self._getNextMID_SMB2()
- ifsmb_message.command!=SMB2_COM_NEGOTIATEandsmb_message.command!=SMB2_COM_ECHO:
+ ifsmb_message.command!=SMB2_COM_NEGOTIATE:smb_message.session_id=self.session_idifself.is_signing_active:
@@ -319,6 +330,19 @@
ifresult==securityblob.RESULT_ACCEPT_COMPLETED:self.has_authenticated=Trueself.log.info('Authentication (on SMB2) successful!')
+
+ # [MS-SMB2]: 3.2.5.3.1
+ # If the security subsystem indicates that the session was established by an anonymous user,
+ # Session.SigningRequired MUST be set to FALSE.
+ # If the SMB2_SESSION_FLAG_IS_GUEST bit is set in the SessionFlags field of the
+ # SMB2 SESSION_SETUP Response and if Session.SigningRequired is TRUE, this indicates a SESSION_SETUP
+ # failure and the connection MUST be terminated. If the SMB2_SESSION_FLAG_IS_GUEST bit is set in the SessionFlags
+ # field of the SMB2 SESSION_SETUP Response and if RequireMessageSigning is FALSE, Session.SigningRequired
+ # MUST be set to FALSE.
+ ifmessage.payload.isGuestSessionormessage.payload.isAnonymousSession:
+ self.is_signing_active=False
+ self.log.info('Signing disabled because session is guest/anonymous')
+
self.onAuthOK()else:raiseProtocolError('SMB2_COM_SESSION_SETUP status is 0 but security blob negResult value is %d'%result,message.raw_data,message)
@@ -332,18 +356,58 @@
self._handleSessionChallenge(message,ntlm_token)except(securityblob.BadSecurityBlobError,securityblob.UnsupportedSecurityProvider),ex:raiseProtocolError(str(ex),message.raw_data,message)
- elifmessage.status==0xc000006d:# STATUS_LOGON_FAILURE
+ elif(message.status==0xc000006d# STATUS_LOGON_FAILURE
+ ormessage.status==0xc0000064# STATUS_NO_SUCH_USER
+ ormessage.status==0xc000006a):# STATUS_WRONG_PASSWORDself.has_authenticated=Falseself.log.info('Authentication (on SMB2) failed. Please check username and password.')self.onAuthFailed()
+ elif(message.status==0xc0000193# STATUS_ACCOUNT_EXPIRED
+ ormessage.status==0xC0000071):# STATUS_PASSWORD_EXPIRED
+ self.has_authenticated=False
+ self.log.info('Authentication (on SMB2) failed. Account or password has expired.')
+ self.onAuthFailed()
+ elifmessage.status==0xc0000234:# STATUS_ACCOUNT_LOCKED_OUT
+ self.has_authenticated=False
+ self.log.info('Authentication (on SMB2) failed. Account has been locked due to too many invalid logon attempts.')
+ self.onAuthFailed()
+ elifmessage.status==0xc0000072:# STATUS_ACCOUNT_DISABLED
+ self.has_authenticated=False
+ self.log.info('Authentication (on SMB2) failed. Account has been disabled.')
+ self.onAuthFailed()
+ elif(message.status==0xc000006f# STATUS_INVALID_LOGON_HOURS
+ ormessage.status==0xc000015b# STATUS_LOGON_TYPE_NOT_GRANTED
+ ormessage.status==0xc0000070):# STATUS_INVALID_WORKSTATION
+ self.has_authenticated=False
+ self.log.info('Authentication (on SMB2) failed. Not allowed.')
+ self.onAuthFailed()
+ elifmessage.status==0xc000018c:# STATUS_TRUSTED_DOMAIN_FAILURE
+ self.has_authenticated=False
+ self.log.info('Authentication (on SMB2) failed. Domain not trusted.')
+ self.onAuthFailed()
+ elifmessage.status==0xc000018d:# STATUS_TRUSTED_RELATIONSHIP_FAILURE
+ self.has_authenticated=False
+ self.log.info('Authentication (on SMB2) failed. Workstation not trusted.')
+ self.onAuthFailed()else:raiseProtocolError('Unknown status value (0x%08X) in SMB_COM_SESSION_SETUP_ANDX (with extended security)'%message.status,message.raw_data,message)
- req=self.pending_requests.pop(message.mid,None)
- ifreq:
- req.callback(message,**req.kwargs)
- returnTrue
+ ifmessage.isAsync:
+ ifmessage.status==0x00000103:# STATUS_PENDING
+ req=self.pending_requests.pop(message.mid,None)
+ ifreq:
+ self.async_requests[message.async_id]=req
+ else:# All other status including SUCCESS
+ req=self.async_requests.pop(message.async_id,None)
+ ifreq:
+ req.callback(message,**req.kwargs)
+ returnTrue
+ else:
+ req=self.pending_requests.pop(message.mid,None)
+ ifreq:
+ req.callback(message,**req.kwargs)
+ returnTruedef_updateServerInfo_SMB2(self,payload):
@@ -383,7 +447,8 @@
lm_challenge_response,session_key,self.username,
- self.domain)
+ self.domain,
+ self.my_name)ifself.log.isEnabledFor(logging.DEBUG):self.log.debug('NT challenge response is "%s" (%d bytes)',binascii.hexlify(nt_challenge_response),len(nt_challenge_response))
@@ -432,7 +497,7 @@
m.tid=tidself._sendSMBMessage(m)
- self.pending_requests[m.mid]=_PendingRequest(m.mid,expiry_time,connectSrvSvcCB,errback)
+ self.pending_requests[m.mid]=_PendingRequest(m.mid,expiry_time,connectSrvSvcCB,errback,tid=tid)messages_history.append(m)defconnectSrvSvcCB(create_message,**kwargs):
@@ -454,9 +519,9 @@
01 00 00 00""".replace(' ','').replace('\n',''))m=SMB2Message(SMB2WriteRequest(create_message.payload.fid,data_bytes,0))
- m.tid=create_message.tid
+ m.tid=kwargs['tid']self._sendSMBMessage(m)
- self.pending_requests[m.mid]=_PendingRequest(m.mid,expiry_time,rpcBindCB,errback,fid=create_message.payload.fid)
+ self.pending_requests[m.mid]=_PendingRequest(m.mid,expiry_time,rpcBindCB,errback,tid=kwargs['tid'],fid=create_message.payload.fid)messages_history.append(m)else:errback(OperationFailure('Failed to list shares: Unable to locate Server Service RPC endpoint',messages_history))
@@ -465,12 +530,12 @@
messages_history.append(trans_message)iftrans_message.status==0:m=SMB2Message(SMB2ReadRequest(kwargs['fid'],read_len=1024,read_offset=0))
- m.tid=trans_message.tid
+ m.tid=kwargs['tid']self._sendSMBMessage(m)
- self.pending_requests[m.mid]=_PendingRequest(m.mid,expiry_time,rpcReadCB,errback,fid=kwargs['fid'])
+ self.pending_requests[m.mid]=_PendingRequest(m.mid,expiry_time,rpcReadCB,errback,tid=kwargs['tid'],fid=kwargs['fid'])messages_history.append(m)else:
- closeFid(trans_message.tid,kwargs['fid'],error='Failed to list shares: Unable to read from Server Service RPC endpoint')
+ closeFid(kwargs['tid'],kwargs['fid'],error='Failed to list shares: Unable to read from Server Service RPC endpoint')defrpcReadCB(read_message,**kwargs):messages_history.append(read_message)
@@ -498,12 +563,12 @@
00 00 00 00 ff ff ff ff 08 00 02 00 00 00 00 00""".replace(' ','').replace('\n',''))m=SMB2Message(SMB2IoctlRequest(kwargs['fid'],0x0011C017,flags=0x01,max_out_size=8196,in_data=data_bytes))
- m.tid=read_message.tid
+ m.tid=kwargs['tid']self._sendSMBMessage(m)
- self.pending_requests[m.mid]=_PendingRequest(m.mid,expiry_time,listShareResultsCB,errback,fid=kwargs['fid'])
+ self.pending_requests[m.mid]=_PendingRequest(m.mid,expiry_time,listShareResultsCB,errback,tid=kwargs['tid'],fid=kwargs['fid'])messages_history.append(m)else:
- closeFid(read_message.tid,kwargs['fid'],error='Failed to list shares: Unable to bind to Server Service RPC endpoint')
+ closeFid(kwargs['tid'],kwargs['fid'],error='Failed to list shares: Unable to bind to Server Service RPC endpoint')deflistShareResultsCB(result_message,**kwargs):messages_history.append(result_message)
@@ -512,13 +577,11 @@
data_bytes=result_message.payload.out_dataiford(data_bytes[3])&0x02==0:
- sendReadRequest(result_message.tid,kwargs['fid'],data_bytes)
- else:
- decodeResults(result_message.tid,kwargs['fid'],data_bytes)
- elifresult_message.status==0x0103:# STATUS_PENDING
- self.pending_requests[result_message.mid]=_PendingRequest(result_message.mid,expiry_time,listShareResultsCB,errback,fid=kwargs['fid'])
- else:
- closeFid(result_message.tid,kwargs['fid'])
+ sendReadRequest(kwargs['tid'],kwargs['fid'],data_bytes)
+ else:
+ decodeResults(kwargs['tid'],kwargs['fid'],data_bytes)
+ else:
+ closeFid(kwargs['tid'],kwargs['fid'])errback(OperationFailure('Failed to list shares: Unable to retrieve shared device list',messages_history))defdecodeResults(tid,fid,data_bytes):
@@ -557,20 +620,19 @@
m.tid=tidself._sendSMBMessage(m)self.pending_requests[m.mid]=_PendingRequest(m.mid,int(time.time())+timeout,readCB,errback,
- fid=fid,data_bytes=data_bytes)
+ tid=tid,fid=fid,data_bytes=data_bytes)defreadCB(read_message,**kwargs):messages_history.append(read_message)ifread_message.status==0:
- data_len=read_message.payload.data_lengthdata_bytes=read_message.payload.dataiford(data_bytes[3])&0x02==0:
- sendReadRequest(read_message.tid,kwargs['fid'],kwargs['data_bytes']+data_bytes[24:data_len-24])
- else:
- decodeResults(read_message.tid,kwargs['fid'],kwargs['data_bytes']+data_bytes[24:data_len-24])
- else:
- closeFid(read_message.tid,kwargs['fid'])
+ sendReadRequest(kwargs['tid'],kwargs['fid'],kwargs['data_bytes']+data_bytes[24:])
+ else:
+ decodeResults(kwargs['tid'],kwargs['fid'],kwargs['data_bytes']+data_bytes[24:])
+ else:
+ closeFid(kwargs['tid'],kwargs['fid'])errback(OperationFailure('Failed to list shares: Unable to retrieve shared device list',messages_history))defcloseFid(tid,fid,results=None,error=None):
@@ -635,39 +697,44 @@
create_context_data=create_context_data))m.tid=tidself._sendSMBMessage(m)
- self.pending_requests[m.mid]=_PendingRequest(m.mid,expiry_time,createCB,errback)
+ self.pending_requests[m.mid]=_PendingRequest(m.mid,expiry_time,createCB,errback,tid=tid)messages_history.append(m)defcreateCB(create_message,**kwargs):messages_history.append(create_message)ifcreate_message.status==0:
- sendQuery(create_message.tid,create_message.payload.fid,'')
+ sendQuery(kwargs['tid'],create_message.payload.fid,'')
+ elifcreate_message.status==0xC0000034L:# [MS-ERREF]: STATUS_OBJECT_NAME_INVALID
+ errback(OperationFailure('Failed to list %s on %s: Path not found'%(path,service_name),messages_history))else:errback(OperationFailure('Failed to list %s on %s: Unable to open directory'%(path,service_name),messages_history))defsendQuery(tid,fid,data_buf):m=SMB2Message(SMB2QueryDirectoryRequest(fid,pattern,
- info_class=0x03,# FileBothDirectoryInformation
+ info_class=0x25,# FileIdBothDirectoryInformationflags=0,output_buf_len=self.max_transact_size))m.tid=tidself._sendSMBMessage(m)
- self.pending_requests[m.mid]=_PendingRequest(m.mid,expiry_time,queryCB,errback,fid=fid,data_buf=data_buf)
+ self.pending_requests[m.mid]=_PendingRequest(m.mid,expiry_time,queryCB,errback,tid=tid,fid=fid,data_buf=data_buf)messages_history.append(m)defqueryCB(query_message,**kwargs):messages_history.append(query_message)ifquery_message.status==0:data_buf=decodeQueryStruct(kwargs['data_buf']+query_message.payload.data)
- sendQuery(query_message.tid,kwargs['fid'],data_buf)
+ sendQuery(kwargs['tid'],kwargs['fid'],data_buf)
+ elifquery_message.status==0xC000000FL:# [MS-ERREF]: STATUS_NO_SUCH_FILE
+ # If there are no matching files, we just treat as success instead of failing
+ closeFid(kwargs['tid'],kwargs['fid'],results=results)elifquery_message.status==0x80000006L:# STATUS_NO_MORE_FILES
- closeFid(query_message.tid,kwargs['fid'],results=results)
- else:
- closeFid(query_message.tid,kwargs['fid'],error=query_message.status)
+ closeFid(kwargs['tid'],kwargs['fid'],results=results)
+ else:
+ closeFid(kwargs['tid'],kwargs['fid'],error=query_message.status)defdecodeQueryStruct(data_bytes):
- # SMB_FIND_FILE_BOTH_DIRECTORY_INFO structure. See [MS-CIFS]: 2.2.8.1.7 and [MS-SMB]: 2.2.8.1.1
- info_format='<IIQQQQQQIIIBB24s'
+ # FileIdBothDirectoryInformation structure. See [MS-SMB]: 2.2.8.1.3 and [MS-FSCC]: 2.4.17
+ info_format='<IIQQQQQQIIIBB24sHQ'info_size=struct.calcsize(info_format)data_length=len(data_bytes)
@@ -679,17 +746,24 @@
next_offset,_, \
create_time,last_access_time,last_write_time,last_attr_change_time, \
file_size,alloc_size,file_attributes,filename_length,ea_size, \
- short_name_length,_,short_name=struct.unpack(info_format,data_bytes[offset:offset+info_size])
+ short_name_length,_,short_name,_,file_id=struct.unpack(info_format,data_bytes[offset:offset+info_size])offset2=offset+info_sizeifoffset2+filename_length>data_length:returndata_bytes[offset:]filename=data_bytes[offset2:offset2+filename_length].decode('UTF-16LE')
- short_name=short_name.decode('UTF-16LE')
- results.append(SharedFile(convertFILETIMEtoEpoch(create_time),convertFILETIMEtoEpoch(last_access_time),
- convertFILETIMEtoEpoch(last_write_time),convertFILETIMEtoEpoch(last_attr_change_time),
- file_size,alloc_size,file_attributes,short_name,filename))
+ short_name=short_name[:short_name_length].decode('UTF-16LE')
+
+ accept_result=False
+ if(file_attributes&0xff)in(0x00,ATTR_NORMAL):# Only the first 8-bits are compared. We ignore other bits like temp, compressed, encryption, sparse, indexed, etc
+ accept_result=(search==SMB_FILE_ATTRIBUTE_NORMAL)or(search&SMB_FILE_ATTRIBUTE_INCL_NORMAL)
+ else:
+ accept_result=(file_attributes&search)>0
+ ifaccept_result:
+ results.append(SharedFile(convertFILETIMEtoEpoch(create_time),convertFILETIMEtoEpoch(last_access_time),
+ convertFILETIMEtoEpoch(last_write_time),convertFILETIMEtoEpoch(last_attr_change_time),
+ file_size,alloc_size,file_attributes,short_name,filename,file_id))ifnext_offset:offset+=next_offset
@@ -708,7 +782,11 @@
ifkwargs['results']isnotNone:callback(kwargs['results'])elifkwargs['error']isnotNone:
- errback(OperationFailure('Failed to list %s on %s: Query failed with errorcode 0x%08x'%(path,service_name,kwargs['error']),messages_history))
+ ifkwargs['error']==0xC000000F:# [MS-ERREF]: STATUS_NO_SUCH_FILE
+ # Remote server returns STATUS_NO_SUCH_FILE error so we assume that the search returns no matching files
+ callback([])
+ else:
+ errback(OperationFailure('Failed to list %s on %s: Query failed with errorcode 0x%08x'%(path,service_name,kwargs['error']),messages_history))ifnotself.connected_trees.has_key(service_name):defconnectCB(connect_message,**kwargs):
@@ -758,17 +836,18 @@
create_context_data=create_context_data))m.tid=tidself._sendSMBMessage(m)
- self.pending_requests[m.mid]=_PendingRequest(m.mid,expiry_time,createCB,errback)
+ self.pending_requests[m.mid]=_PendingRequest(m.mid,expiry_time,createCB,errback,tid=tid)messages_history.append(m)defcreateCB(create_message,**kwargs):messages_history.append(create_message)ifcreate_message.status==0:p=create_message.payload
+ filename=self._extractLastPathComponent(unicode(path))info=SharedFile(p.create_time,p.lastaccess_time,p.lastwrite_time,p.change_time,p.file_size,p.allocation_size,p.file_attributes,
- unicode(path),unicode(path))
- closeFid(create_message.tid,p.fid,info=info)
+ filename,filename)
+ closeFid(kwargs['tid'],p.fid,info=info)else:errback(OperationFailure('Failed to get attributes for %s on %s: Unable to open remote file object'%(path,service_name),messages_history))
@@ -793,6 +872,87 @@
sendCreate(connect_message.tid)else:errback(OperationFailure('Failed to get attributes for %s on %s: Unable to connect to shared device'%(path,service_name),messages_history))
+
+ m=SMB2Message(SMB2TreeConnectRequest(r'\\%s\%s'%(self.remote_name.upper(),service_name)))
+ self._sendSMBMessage(m)
+ self.pending_requests[m.mid]=_PendingRequest(m.mid,expiry_time,connectCB,errback,path=service_name)
+ messages_history.append(m)
+ else:
+ sendCreate(self.connected_trees[service_name])
+
+ def_getSecurity_SMB2(self,service_name,path,callback,errback,timeout=30):
+ ifnotself.has_authenticated:
+ raiseNotReadyError('SMB connection not authenticated')
+
+ expiry_time=time.time()+timeout
+ path=path.replace('/','\\')
+ ifpath.startswith('\\'):
+ path=path[1:]
+ ifpath.endswith('\\'):
+ path=path[:-1]
+ messages_history=[]
+ results=[]
+
+ defsendCreate(tid):
+ m=SMB2Message(SMB2CreateRequest(path,
+ file_attributes=0,
+ access_mask=FILE_READ_DATA|FILE_READ_EA|FILE_READ_ATTRIBUTES|READ_CONTROL|SYNCHRONIZE,
+ share_access=FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
+ oplock=SMB2_OPLOCK_LEVEL_NONE,
+ impersonation=SEC_IMPERSONATE,
+ create_options=0,
+ create_disp=FILE_OPEN))
+ m.tid=tid
+ self._sendSMBMessage(m)
+ self.pending_requests[m.mid]=_PendingRequest(m.mid,expiry_time,createCB,errback,tid=tid)
+ messages_history.append(m)
+
+ defcreateCB(create_message,**kwargs):
+ messages_history.append(create_message)
+ ifcreate_message.status==0:
+ m=SMB2Message(SMB2QueryInfoRequest(create_message.payload.fid,
+ flags=0,
+ additional_info=OWNER_SECURITY_INFORMATION|GROUP_SECURITY_INFORMATION|DACL_SECURITY_INFORMATION,
+ info_type=SMB2_INFO_SECURITY,
+ file_info_class=0,# [MS-SMB2] 2.2.37, 3.2.4.12
+ input_buf='',
+ output_buf_len=self.max_transact_size))
+ m.tid=kwargs['tid']
+ self._sendSMBMessage(m)
+ self.pending_requests[m.mid]=_PendingRequest(m.mid,expiry_time,queryCB,errback,tid=kwargs['tid'],fid=create_message.payload.fid)
+ messages_history.append(m)
+ else:
+ errback(OperationFailure('Failed to get the security descriptor of %s on %s: Unable to open file or directory'%(path,service_name),messages_history))
+
+ defqueryCB(query_message,**kwargs):
+ messages_history.append(query_message)
+ ifquery_message.status==0:
+ security=SecurityDescriptor.from_bytes(query_message.payload.data)
+ closeFid(kwargs['tid'],kwargs['fid'],result=security)
+ else:
+ closeFid(kwargs['tid'],kwargs['fid'],error=query_message.status)
+
+ defcloseFid(tid,fid,result=None,error=None):
+ m=SMB2Message(SMB2CloseRequest(fid))
+ m.tid=tid
+ self._sendSMBMessage(m)
+ self.pending_requests[m.mid]=_PendingRequest(m.mid,expiry_time,closeCB,errback,result=result,error=error)
+ messages_history.append(m)
+
+ defcloseCB(close_message,**kwargs):
+ ifkwargs['result']isnotNone:
+ callback(kwargs['result'])
+ elifkwargs['error']isnotNone:
+ errback(OperationFailure('Failed to get the security descriptor of %s on %s: Query failed with errorcode 0x%08x'%(path,service_name,kwargs['error']),messages_history))
+
+ ifnotself.connected_trees.has_key(service_name):
+ defconnectCB(connect_message,**kwargs):
+ messages_history.append(connect_message)
+ ifconnect_message.status==0:
+ self.connected_trees[service_name]=connect_message.tid
+ sendCreate(connect_message.tid)
+ else:
+ errback(OperationFailure('Failed to get the security descriptor of %s on %s: Unable to connect to shared device'%(path,service_name),messages_history))m=SMB2Message(SMB2TreeConnectRequest(r'\\%s\%s'%(self.remote_name.upper(),service_name)))self._sendSMBMessage(m)
@@ -829,7 +989,7 @@
m=SMB2Message(SMB2CreateRequest(path,file_attributes=0,access_mask=FILE_READ_DATA|FILE_READ_EA|FILE_READ_ATTRIBUTES|READ_CONTROL|SYNCHRONIZE,
- share_access=FILE_SHARE_READ,
+ share_access=FILE_SHARE_READ|FILE_SHARE_WRITE,oplock=SMB2_OPLOCK_LEVEL_NONE,impersonation=SEC_IMPERSONATE,create_options=FILE_SEQUENTIAL_ONLY|FILE_NON_DIRECTORY_FILE,
@@ -850,13 +1010,15 @@
file_info_class=0x16,# FileStreamInformation [MS-FSCC] 2.4input_buf='',output_buf_len=4096))
- m.tid=create_message.tid
+ m.tid=kwargs['tid']self._sendSMBMessage(m)self.pending_requests[m.mid]=_PendingRequest(m.mid,expiry_time,infoCB,errback,
- fid=create_message.payload.fid,file_attributes=create_message.payload.file_attributes)
+ tid=kwargs['tid'],
+ fid=create_message.payload.fid,
+ file_attributes=create_message.payload.file_attributes)messages_history.append(m)else:
- errback(OperationFailure('Failed to list %s on %s: Unable to open file'%(path,service_name),messages_history))
+ errback(OperationFailure('Failed to retrieve %s on %s: Unable to open file'%(path,service_name),messages_history))definfoCB(info_message,**kwargs):messages_history.append(info_message)
@@ -871,9 +1033,9 @@
remaining_len=file_lenifstarting_offset+remaining_len>file_len:remaining_len=file_len-starting_offset
- sendRead(info_message.tid,kwargs['fid'],starting_offset,remaining_len,0,kwargs['file_attributes'])
- else:
- errback(OperationFailure('Failed to list %s on %s: Unable to retrieve information on file'%(path,service_name),messages_history))
+ sendRead(kwargs['tid'],kwargs['fid'],starting_offset,remaining_len,0,kwargs['file_attributes'])
+ else:
+ errback(OperationFailure('Failed to retrieve %s on %s: Unable to retrieve information on file'%(path,service_name),messages_history))defsendRead(tid,fid,offset,remaining_len,read_len,file_attributes):read_count=min(self.max_read_size,remaining_len)
@@ -881,7 +1043,7 @@
m.tid=tidself._sendSMBMessage(m)self.pending_requests[m.mid]=_PendingRequest(m.mid,int(time.time())+timeout,readCB,errback,
- fid=fid,offset=offset,
+ tid=tid,fid=fid,offset=offset,remaining_len=remaining_len,read_len=read_len,file_attributes=file_attributes)
@@ -895,12 +1057,12 @@
remaining_len=kwargs['remaining_len']-data_lenifremaining_len>0:
- sendRead(read_message.tid,kwargs['fid'],kwargs['offset']+data_len,remaining_len,kwargs['read_len']+data_len,kwargs['file_attributes'])
- else:
- closeFid(read_message.tid,kwargs['fid'],ret=(file_obj,kwargs['file_attributes'],kwargs['read_len']+data_len))
+ sendRead(kwargs['tid'],kwargs['fid'],kwargs['offset']+data_len,remaining_len,kwargs['read_len']+data_len,kwargs['file_attributes'])
+ else:
+ closeFid(kwargs['tid'],kwargs['fid'],ret=(file_obj,kwargs['file_attributes'],kwargs['read_len']+data_len))else:messages_history.append(read_message)
- closeFid(read_message.tid,kwargs['fid'],error=read_message.status)
+ closeFid(kwargs['tid'],kwargs['fid'],error=read_message.status)defcloseFid(tid,fid,ret=None,error=None):m=SMB2Message(SMB2CloseRequest(fid))
@@ -938,6 +1100,7 @@
ifnotself.has_authenticated:raiseNotReadyError('SMB connection not authenticated')
+ expiry_time=time.time()+timeoutpath=path.replace('/','\\')ifpath.startswith('\\'):path=path[1:]
@@ -971,6 +1134,7 @@
messages_history.append(m)defcreateCB(create_message,**kwargs):
+ create_message.tid=kwargs['tid']messages_history.append(create_message)ifcreate_message.status==0:sendWrite(create_message.tid,create_message.payload.fid,starting_offset)
@@ -985,17 +1149,17 @@
m=SMB2Message(SMB2WriteRequest(fid,data,offset))m.tid=tidself._sendSMBMessage(m)
- self.pending_requests[m.mid]=_PendingRequest(m.mid,int(time.time())+timeout,writeCB,errback,fid=fid,offset=offset+data_len)
+ self.pending_requests[m.mid]=_PendingRequest(m.mid,int(time.time())+timeout,writeCB,errback,tid=tid,fid=fid,offset=offset+data_len)else:closeFid(tid,fid,offset=offset)defwriteCB(write_message,**kwargs):# To avoid crazy memory usage when saving large files, we do not save every write_message in messages_history.ifwrite_message.status==0:
- sendWrite(write_message.tid,kwargs['fid'],kwargs['offset'])
+ sendWrite(kwargs['tid'],kwargs['fid'],kwargs['offset'])else:messages_history.append(write_message)
- closeFid(write_message.tid,kwargs['fid'])
+ closeFid(kwargs['tid'],kwargs['fid'])errback(OperationFailure('Failed to store %s on %s: Write failed'%(path,service_name),messages_history))defcloseFid(tid,fid,error=None,offset=None):
@@ -1028,16 +1192,99 @@
sendCreate(self.connected_trees[service_name])
- def_deleteFiles_SMB2(self,service_name,path_file_pattern,callback,errback,timeout=30):
+ def_deleteFiles_SMB2(self,service_name,path_file_pattern,delete_matching_folders,callback,errback,timeout=30):ifnotself.has_authenticated:raiseNotReadyError('SMB connection not authenticated')expiry_time=time.time()+timeout
+ pattern=Nonepath=path_file_pattern.replace('/','\\')ifpath.startswith('\\'):path=path[1:]ifpath.endswith('\\'):path=path[:-1]
+ else:
+ path_components=path.split('\\')
+ ifpath_components[-1].find('*')>-1orpath_components[-1].find('?')>-1:
+ path='\\'.join(path_components[:-1])
+ pattern=path_components[-1]
+ messages_history,files_queue=[],[]
+
+ ifpatternisNone:
+ path_components=path.split('\\')
+ iflen(path_components)>1:
+ files_queue.append(('\\'.join(path_components[:-1]),path_components[-1]))
+ else:
+ files_queue.append(('',path))
+
+ defdeleteCB(path):
+ iffiles_queue:
+ p,filename=files_queue.pop(0)
+ iffilename:
+ ifp:
+ filename=p+'\\'+filename
+ self._deleteFiles_SMB2__del(service_name,self.connected_trees[service_name],filename,deleteCB,errback,timeout)
+ else:
+ self._deleteDirectory_SMB2(service_name,p,deleteCB,errback,timeout)
+ else:
+ callback(path_file_pattern)
+
+ deflistCB(files_list):
+ files_queue.extend(files_list)
+ deleteCB(None)
+
+ ifnotself.connected_trees.has_key(service_name):
+ defconnectCB(connect_message,**kwargs):
+ messages_history.append(connect_message)
+ ifconnect_message.status==0:
+ self.connected_trees[service_name]=connect_message.tid
+ iffiles_queue:
+ deleteCB(None)
+ else:
+ self._deleteFiles_SMB2__list(service_name,path,pattern,delete_matching_folders,listCB,errback,timeout)
+ else:
+ errback(OperationFailure('Failed to delete %s on %s: Unable to connect to shared device'%(path,service_name),messages_history))
+
+ m=SMB2Message(SMB2TreeConnectRequest(r'\\%s\%s'%(self.remote_name.upper(),service_name)))
+ self._sendSMBMessage(m)
+ self.pending_requests[m.mid]=_PendingRequest(m.mid,expiry_time,connectCB,errback,path=service_name)
+ messages_history.append(m)
+ else:
+ iffiles_queue:
+ deleteCB(None)
+ else:
+ self._deleteFiles_SMB2__list(service_name,path,pattern,delete_matching_folders,listCB,errback,timeout)
+
+ def_deleteFiles_SMB2__list(self,service_name,path,pattern,delete_matching_folders,callback,errback,timeout=30):
+ folder_queue=[]
+ files_list=[]
+ current_path=[path]
+ search=SMB_FILE_ATTRIBUTE_READONLY|SMB_FILE_ATTRIBUTE_HIDDEN|SMB_FILE_ATTRIBUTE_SYSTEM|SMB_FILE_ATTRIBUTE_DIRECTORY|SMB_FILE_ATTRIBUTE_ARCHIVE|SMB_FILE_ATTRIBUTE_INCL_NORMAL
+
+ deflistCB(results):
+ files=[]
+ forfinfilter(lambdax:x.filenamenotin['.','..'],results):
+ iff.isDirectory:
+ ifdelete_matching_folders:
+ folder_queue.append(current_path[0]+'\\'+f.filename)
+ else:
+ files.append((current_path[0],f.filename))
+ ifcurrent_path[0]!=pathanddelete_matching_folders:
+ files.append((current_path[0],None))
+
+ iffiles:
+ files_list[0:0]=files
+
+ iffolder_queue:
+ p=folder_queue.pop()
+ current_path[0]=p
+ self._listPath_SMB2(service_name,current_path[0],listCB,errback,search=search,pattern='*',timeout=30)
+ else:
+ callback(files_list)
+
+ self._listPath_SMB2(service_name,path,listCB,errback,search=search,pattern=pattern,timeout=timeout)
+
+ def_deleteFiles_SMB2__del(self,service_name,tid,path,callback,errback,timeout=30):messages_history=[]defsendCreate(tid):
@@ -1064,9 +1311,14 @@
messages_history.append(m)defcreateCB(open_message,**kwargs):
+ open_message.tid=kwargs['tid']messages_history.append(open_message)ifopen_message.status==0:sendDelete(open_message.tid,open_message.payload.fid)
+ elifopen_message.status==0xC0000034L:# [MS-ERREF]: STATUS_OBJECT_NAME_NOT_FOUND
+ callback(path)
+ elifopen_message.status==0xC00000BAL:# [MS-ERREF]: STATUS_FILE_IS_A_DIRECTORY
+ errback(OperationFailure('Failed to delete %s on %s: Cannot delete a folder. Please use deleteDirectory() method or append "/*" to your path if you wish to delete all files in the folder.'%(path,service_name),messages_history))else:errback(OperationFailure('Failed to delete %s on %s: Unable to open file'%(path,service_name),messages_history))
@@ -1076,22 +1328,18 @@
info_type=SMB2_INFO_FILE,file_info_class=0x0d,# SMB2_FILE_DISPOSITION_INFOdata='\x01'))
- '''
- Resources:
- https://msdn.microsoft.com/en-us/library/cc246560.aspx
- https://msdn.microsoft.com/en-us/library/cc232098.aspx
- '''
- m.tid=tid
- self._sendSMBMessage(m)
- self.pending_requests[m.mid]=_PendingRequest(m.mid,int(time.time())+timeout,deleteCB,errback,fid=fid)
+ # [MS-SMB2]: 2.2.39, [MS-FSCC]: 2.4.11
+ m.tid=tid
+ self._sendSMBMessage(m)
+ self.pending_requests[m.mid]=_PendingRequest(m.mid,int(time.time())+timeout,deleteCB,errback,tid=tid,fid=fid)messages_history.append(m)defdeleteCB(delete_message,**kwargs):messages_history.append(delete_message)ifdelete_message.status==0:
- closeFid(delete_message.tid,kwargs['fid'],status=0)
- else:
- closeFid(delete_message.tid,kwargs['fid'],status=delete_message.status)
+ closeFid(kwargs['tid'],kwargs['fid'],status=0)
+ else:
+ closeFid(kwargs['tid'],kwargs['fid'],status=delete_message.status)defcloseFid(tid,fid,status=None):m=SMB2Message(SMB2CloseRequest(fid))
@@ -1102,25 +1350,11 @@
defcloseCB(close_message,**kwargs):ifkwargs['status']==0:
- callback(path_file_pattern)
+ callback(path)else:errback(OperationFailure('Failed to delete %s on %s: Delete failed'%(path,service_name),messages_history))
- ifnotself.connected_trees.has_key(service_name):
- defconnectCB(connect_message,**kwargs):
- messages_history.append(connect_message)
- ifconnect_message.status==0:
- self.connected_trees[service_name]=connect_message.tid
- sendCreate(connect_message.tid)
- else:
- errback(OperationFailure('Failed to delete %s on %s: Unable to connect to shared device'%(path,service_name),messages_history))
-
- m=SMB2Message(SMB2TreeConnectRequest(r'\\%s\%s'%(self.remote_name.upper(),service_name)))
- self._sendSMBMessage(m)
- self.pending_requests[m.mid]=_PendingRequest(m.mid,expiry_time,connectCB,errback,path=service_name)
- messages_history.append(m)
- else:
- sendCreate(self.connected_trees[service_name])
+ sendCreate(tid)def_resetFileAttributes_SMB2(self,service_name,path_file_pattern,callback,errback,timeout=30):ifnotself.has_authenticated:
@@ -1161,7 +1395,7 @@
defcreateCB(open_message,**kwargs):messages_history.append(open_message)ifopen_message.status==0:
- sendReset(open_message.tid,open_message.payload.fid)
+ sendReset(kwargs['tid'],open_message.payload.fid)else:errback(OperationFailure('Failed to reset attributes of %s on %s: Unable to open file'%(path,service_name),messages_history))
@@ -1171,24 +1405,18 @@
info_type=SMB2_INFO_FILE,file_info_class=4,# FileBasicInformationdata=struct.pack('qqqqii',0,0,0,0,0x80,0)))# FILE_ATTRIBUTE_NORMAL
- '''
- Resources:
- https://msdn.microsoft.com/en-us/library/cc246560.aspx
- https://msdn.microsoft.com/en-us/library/cc232064.aspx
- https://msdn.microsoft.com/en-us/library/cc232094.aspx
- https://msdn.microsoft.com/en-us/library/cc232110.aspx
- '''
- m.tid=tid
- self._sendSMBMessage(m)
- self.pending_requests[m.mid]=_PendingRequest(m.mid,int(time.time())+timeout,resetCB,errback,fid=fid)
+ # [MS-SMB2]: 2.2.39, [MS-FSCC]: 2.4, [MS-FSCC]: 2.4.7, [MS-FSCC]: 2.6
+ m.tid=tid
+ self._sendSMBMessage(m)
+ self.pending_requests[m.mid]=_PendingRequest(m.mid,int(time.time())+timeout,resetCB,errback,tid=tid,fid=fid)messages_history.append(m)defresetCB(reset_message,**kwargs):messages_history.append(reset_message)ifreset_message.status==0:
- closeFid(reset_message.tid,kwargs['fid'],status=0)
- else:
- closeFid(reset_message.tid,kwargs['fid'],status=reset_message.status)
+ closeFid(kwargs['tid'],kwargs['fid'],status=0)
+ else:
+ closeFid(kwargs['tid'],kwargs['fid'],status=reset_message.status)defcloseFid(tid,fid,status=None):m=SMB2Message(SMB2CloseRequest(fid))
@@ -1251,13 +1479,13 @@
create_context_data=create_context_data))m.tid=tidself._sendSMBMessage(m)
- self.pending_requests[m.mid]=_PendingRequest(m.mid,int(time.time())+timeout,createCB,errback)
+ self.pending_requests[m.mid]=_PendingRequest(m.mid,int(time.time())+timeout,createCB,errback,tid=tid)messages_history.append(m)defcreateCB(create_message,**kwargs):messages_history.append(create_message)ifcreate_message.status==0:
- closeFid(create_message.tid,create_message.payload.fid)
+ closeFid(kwargs['tid'],create_message.payload.fid)else:errback(OperationFailure('Failed to create directory %s on %s: Create failed'%(path,service_name),messages_history))
@@ -1325,7 +1553,7 @@
defcreateCB(open_message,**kwargs):messages_history.append(open_message)ifopen_message.status==0:
- sendDelete(open_message.tid,open_message.payload.fid)
+ sendDelete(kwargs['tid'],open_message.payload.fid)else:errback(OperationFailure('Failed to delete %s on %s: Unable to open directory'%(path,service_name),messages_history))
@@ -1337,15 +1565,15 @@
data='\x01'))m.tid=tidself._sendSMBMessage(m)
- self.pending_requests[m.mid]=_PendingRequest(m.mid,int(time.time())+timeout,deleteCB,errback,fid=fid)
+ self.pending_requests[m.mid]=_PendingRequest(m.mid,int(time.time())+timeout,deleteCB,errback,tid=tid,fid=fid)messages_history.append(m)defdeleteCB(delete_message,**kwargs):messages_history.append(delete_message)ifdelete_message.status==0:
- closeFid(delete_message.tid,kwargs['fid'],status=0)
- else:
- closeFid(delete_message.tid,kwargs['fid'],status=delete_message.status)
+ closeFid(kwargs['tid'],kwargs['fid'],status=0)
+ else:
+ closeFid(kwargs['tid'],kwargs['fid'],status=delete_message.status)defcloseFid(tid,fid,status=None):m=SMB2Message(SMB2CloseRequest(fid))
@@ -1421,7 +1649,7 @@
defcreateCB(create_message,**kwargs):messages_history.append(create_message)ifcreate_message.status==0:
- sendRename(create_message.tid,create_message.payload.fid)
+ sendRename(kwargs['tid'],create_message.payload.fid)else:errback(OperationFailure('Failed to rename %s on %s: Unable to open file/directory'%(old_path,service_name),messages_history))
@@ -1434,15 +1662,15 @@
data=data))m.tid=tidself._sendSMBMessage(m)
- self.pending_requests[m.mid]=_PendingRequest(m.mid,int(time.time())+timeout,renameCB,errback,fid=fid)
+ self.pending_requests[m.mid]=_PendingRequest(m.mid,int(time.time())+timeout,renameCB,errback,tid=tid,fid=fid)messages_history.append(m)defrenameCB(rename_message,**kwargs):messages_history.append(rename_message)ifrename_message.status==0:
- closeFid(rename_message.tid,kwargs['fid'],status=0)
- else:
- closeFid(rename_message.tid,kwargs['fid'],status=rename_message.status)
+ closeFid(kwargs['tid'],kwargs['fid'],status=0)
+ else:
+ closeFid(kwargs['tid'],kwargs['fid'],status=rename_message.status)defcloseFid(tid,fid,status=None):m=SMB2Message(SMB2CloseRequest(fid))
@@ -1509,7 +1737,7 @@
defcreateCB(create_message,**kwargs):messages_history.append(create_message)ifcreate_message.status==0:
- sendEnumSnapshots(create_message.tid,create_message.payload.fid)
+ sendEnumSnapshots(kwargs['tid'],create_message.payload.fid)else:errback(OperationFailure('Failed to list snapshots %s on %s: Unable to open file/directory'%(old_path,service_name),messages_history))
@@ -1650,9 +1878,38 @@
self._handleSessionChallenge(message,ntlm_token)except(securityblob.BadSecurityBlobError,securityblob.UnsupportedSecurityProvider),ex:raiseProtocolError(str(ex),message.raw_data,message)
- elifmessage.status.internal_value==0xc000006d:# STATUS_LOGON_FAILURE
+ elif(message.status.internal_value==0xc000006d# STATUS_LOGON_FAILURE
+ ormessage.status.internal_value==0xc0000064# STATUS_NO_SUCH_USER
+ ormessage.status.internal_value==0xc000006a):# STATUS_WRONG_PASSWORDself.has_authenticated=False
- self.log.info('Authentication (with extended security) failed. Please check username and password. You may need to enable/disable NTLMv2 authentication.')
+ self.log.info('Authentication (with extended security) failed. Please check username and password.')
+ self.onAuthFailed()
+ elif(message.status.internal_value==0xc0000193# STATUS_ACCOUNT_EXPIRED
+ ormessage.status.internal_value==0xC0000071):# STATUS_PASSWORD_EXPIRED
+ self.has_authenticated=False
+ self.log.info('Authentication (with extended security) failed. Account or password has expired.')
+ self.onAuthFailed()
+ elifmessage.status.internal_value==0xc0000234:# STATUS_ACCOUNT_LOCKED_OUT
+ self.has_authenticated=False
+ self.log.info('Authentication (with extended security) failed. Account has been locked due to too many invalid logon attempts.')
+ self.onAuthFailed()
+ elifmessage.status.internal_value==0xc0000072:# STATUS_ACCOUNT_DISABLED
+ self.has_authenticated=False
+ self.log.info('Authentication (with extended security) failed. Account has been disabled.')
+ self.onAuthFailed()
+ elif(message.status.internal_value==0xc000006f# STATUS_INVALID_LOGON_HOURS
+ ormessage.status.internal_value==0xc000015b# STATUS_LOGON_TYPE_NOT_GRANTED
+ ormessage.status.internal_value==0xc0000070):# STATUS_INVALID_WORKSTATION
+ self.has_authenticated=False
+ self.log.info('Authentication (with extended security) failed. Not allowed.')
+ self.onAuthFailed()
+ elifmessage.status.internal_value==0xc000018c:# STATUS_TRUSTED_DOMAIN_FAILURE
+ self.has_authenticated=False
+ self.log.info('Authentication (with extended security) failed. Domain not trusted.')
+ self.onAuthFailed()
+ elifmessage.status.internal_value==0xc000018d:# STATUS_TRUSTED_RELATIONSHIP_FAILURE
+ self.has_authenticated=False
+ self.log.info('Authentication (with extended security) failed. Workstation not trusted.')self.onAuthFailed()else:raiseProtocolError('Unknown status value (0x%08X) in SMB_COM_SESSION_SETUP_ANDX (with extended security)'%message.status.internal_value,
@@ -1719,7 +1976,8 @@
lm_challenge_response,session_key,self.username,
- self.domain)
+ self.domain,
+ self.my_name)ifself.log.isEnabledFor(logging.DEBUG):self.log.debug('NT challenge response is "%s" (%d bytes)',binascii.hexlify(nt_challenge_response),len(nt_challenge_response))
@@ -1913,13 +2171,12 @@
defreadCB(read_message,**kwargs):messages_history.append(read_message)ifnotread_message.status.hasError:
- data_len=read_message.payload.data_lengthdata_bytes=read_message.payload.dataiford(data_bytes[3])&0x02==0:
- sendReadRequest(read_message.tid,kwargs['fid'],kwargs['data_bytes']+data_bytes[24:data_len-24])
- else:
- decodeResults(read_message.tid,kwargs['fid'],kwargs['data_bytes']+data_bytes[24:data_len-24])
+ sendReadRequest(read_message.tid,kwargs['fid'],kwargs['data_bytes']+data_bytes[24:])
+ else:
+ decodeResults(read_message.tid,kwargs['fid'],kwargs['data_bytes']+data_bytes[24:])else:closeFid(read_message.tid,kwargs['fid'])errback(OperationFailure('Failed to list shares: Unable to retrieve shared device list',messages_history))
@@ -1958,15 +2215,15 @@
setup_bytes=struct.pack('<H',0x0001)# TRANS2_FIND_FIRST2 sub-command. See [MS-CIFS]: 2.2.6.2.1params_bytes= \
struct.pack('<HHHHI',
- search,# SearchAttributes
+ search&0xFFFF,# SearchAttributes (need to restrict the values due to introduction of SMB_FILE_ATTRIBUTE_INCL_NORMAL)100,# SearchCount0x0006,# Flags: SMB_FIND_CLOSE_AT_EOS | SMB_FIND_RETURN_RESUME_KEYS0x0104,# InfoLevel: SMB_FIND_FILE_BOTH_DIRECTORY_INFO
- 0x0000)# SearchStorageType
+ 0x0000)# SearchStorageType (seems to be ignored by Windows)ifsupport_dfs:params_bytes+=("\\"+self.remote_name+"\\"+service_name+path+pattern+'\0').encode('UTF-16LE')else:
- params_bytes+=(path+pattern).encode('UTF-16LE')
+ params_bytes+=(path+pattern+'\0').encode('UTF-16LE')m=SMBMessage(ComTransaction2Request(max_params_count=10,max_data_count=16644,
@@ -2002,9 +2259,16 @@
filename=data_bytes[offset2:offset2+filename_length].decode('UTF-16LE')short_name=short_name.decode('UTF-16LE')
- results.append(SharedFile(convertFILETIMEtoEpoch(create_time),convertFILETIMEtoEpoch(last_access_time),
- convertFILETIMEtoEpoch(last_write_time),convertFILETIMEtoEpoch(last_attr_change_time),
- file_size,alloc_size,file_attributes,short_name,filename))
+
+ accept_result=False
+ if(file_attributes&0xff)in(0x00,ATTR_NORMAL):# Only the first 8-bits are compared. We ignore other bits like temp, compressed, encryption, sparse, indexed, etc
+ accept_result=(search==SMB_FILE_ATTRIBUTE_NORMAL)or(search&SMB_FILE_ATTRIBUTE_INCL_NORMAL)
+ else:
+ accept_result=(file_attributes&search)>0
+ ifaccept_result:
+ results.append(SharedFile(convertFILETIMEtoEpoch(create_time),convertFILETIMEtoEpoch(last_access_time),
+ convertFILETIMEtoEpoch(last_write_time),convertFILETIMEtoEpoch(last_attr_change_time),
+ file_size,alloc_size,file_attributes,short_name,filename))ifnext_offset:offset+=next_offset
@@ -2044,11 +2308,15 @@
elifend_of_search:callback(results)else:
- sendFindNext(find_message.tid,sid,last_name_offset,kwargs.get('support_dfs',False))
- else:
- errback(OperationFailure('Failed to list %s on %s: Unable to retrieve file list'%(path,service_name),messages_history))
-
- defsendFindNext(tid,sid,resume_key,support_dfs=False):
+ sendFindNext(find_message.tid,sid,0,results[-1].filename,kwargs.get('support_dfs',False))
+ else:
+ iffind_message.status.internal_value==0xC000000F:# [MS-ERREF]: STATUS_NO_SUCH_FILE
+ # Remote server returns STATUS_NO_SUCH_FILE error so we assume that the search returns no matching files
+ callback([])
+ else:
+ errback(OperationFailure('Failed to list %s on %s: Unable to retrieve file list'%(path,service_name),messages_history))
+
+ defsendFindNext(tid,sid,resume_key,resume_file,support_dfs=False):setup_bytes=struct.pack('<H',0x0002)# TRANS2_FIND_NEXT2 sub-command. See [MS-CIFS]: 2.2.6.3.1params_bytes= \
struct.pack('<HHHIH',
@@ -2056,11 +2324,8 @@
100,# SearchCount0x0104,# InfoLevel: SMB_FIND_FILE_BOTH_DIRECTORY_INFOresume_key,# ResumeKey
- 0x000a)# Flags: SMB_FIND_RETURN_RESUME_KEYS | SMB_FIND_CLOSE_AT_EOS | SMB_FIND_RETURN_RESUME_KEYS
- ifsupport_dfs:
- params_bytes+=("\\"+self.remote_name+"\\"+service_name+path+pattern+'\0').encode('UTF-16LE')
- else:
- params_bytes+=(path+pattern).encode('UTF-16LE')
+ 0x0006)# Flags: SMB_FIND_RETURN_RESUME_KEYS | SMB_FIND_CLOSE_AT_EOS
+ params_bytes+=(resume_file+'\0').encode('UTF-16LE')m=SMBMessage(ComTransaction2Request(max_params_count=10,max_data_count=16644,
@@ -2106,7 +2371,7 @@
elifend_of_search:callback(results)else:
- sendFindNext(find_message.tid,kwargs['sid'],last_name_offset,kwargs.get('support_dfs',False))
+ sendFindNext(find_message.tid,kwargs['sid'],0,results[-1].filename,kwargs.get('support_dfs',False))else:errback(OperationFailure('Failed to list %s on %s: Unable to retrieve file list'%(path,service_name),messages_history))
@@ -2184,9 +2449,10 @@
info_size=struct.calcsize(info_format)create_time,last_access_time,last_write_time,last_attr_change_time, \
file_attributes,_,alloc_size,file_size=struct.unpack(info_format,query_message.payload.data_bytes[:info_size])
-
- info=SharedFile(create_time,last_access_time,last_write_time,last_attr_change_time,
- file_size,alloc_size,file_attributes,unicode(path),unicode(path))
+ filename=self._extractLastPathComponent(unicode(path))
+
+ info=SharedFile(convertFILETIMEtoEpoch(create_time),convertFILETIMEtoEpoch(last_access_time),convertFILETIMEtoEpoch(last_write_time),convertFILETIMEtoEpoch(last_attr_change_time),
+ file_size,alloc_size,file_attributes,filename,filename)callback(info)else:errback(OperationFailure('Failed to get attributes for %s on %s: Read failed'%(path,service_name),messages_history))
@@ -2206,6 +2472,9 @@
messages_history.append(m)else:sendQuery(self.connected_trees[service_name])
+
+ def_getSecurity_SMB1(self,service_name,path_file_pattern,callback,errback,timeout=30):
+ raiseNotReadyError('getSecurity is not yet implemented for SMB1')def_retrieveFile_SMB1(self,service_name,path,file_obj,callback,errback,timeout=30):returnself._retrieveFileFromOffset(service_name,path,file_obj,callback,errback,0L,-1L,timeout)
@@ -2374,11 +2643,100 @@
else:sendOpen(self.connected_trees[service_name])
- def_deleteFiles_SMB1(self,service_name,path_file_pattern,callback,errback,timeout=30):
+ def_deleteFiles_SMB1(self,service_name,path_file_pattern,delete_matching_folders,callback,errback,timeout=30):ifnotself.has_authenticated:raiseNotReadyError('SMB connection not authenticated')
+ expiry_time=time.time()+timeout
+ pattern=Nonepath=path_file_pattern.replace('/','\\')
+ ifpath.startswith('\\'):
+ path=path[1:]
+ ifpath.endswith('\\'):
+ path=path[:-1]
+ else:
+ path_components=path.split('\\')
+ ifpath_components[-1].find('*')>-1orpath_components[-1].find('?')>-1:
+ path='\\'.join(path_components[:-1])
+ pattern=path_components[-1]
+ messages_history,files_queue=[],[]
+
+ ifpatternisNone:
+ path_components=path.split('\\')
+ iflen(path_components)>1:
+ files_queue.append(('\\'.join(path_components[:-1]),path_components[-1]))
+ else:
+ files_queue.append(('',path))
+
+ defdeleteCB(path):
+ iffiles_queue:
+ p,filename=files_queue.pop(0)
+ iffilename:
+ ifp:
+ filename=p+'\\'+filename
+ self._deleteFiles_SMB1__del(service_name,self.connected_trees[service_name],filename,deleteCB,errback,timeout)
+ else:
+ self._deleteDirectory_SMB1(service_name,p,deleteCB,errback,timeout=30)
+ else:
+ callback(path_file_pattern)
+
+ deflistCB(files_list):
+ files_queue.extend(files_list)
+ deleteCB(None)
+
+ ifnotself.connected_trees.has_key(service_name):
+ defconnectCB(connect_message,**kwargs):
+ messages_history.append(connect_message)
+ ifnotconnect_message.status.hasError:
+ self.connected_trees[service_name]=connect_message.tid
+ iffiles_queue:
+ deleteCB(None)
+ else:
+ self._deleteFiles_SMB1__list(service_name,path,pattern,delete_matching_folders,listCB,errback,timeout)
+ else:
+ errback(OperationFailure('Failed to delete %s on %s: Unable to connect to shared device'%(path,service_name),messages_history))
+
+ m=SMBMessage(ComTreeConnectAndxRequest(r'\\%s\%s'%(self.remote_name.upper(),service_name),SERVICE_ANY,''))
+ self._sendSMBMessage(m)
+ self.pending_requests[m.mid]=_PendingRequest(m.mid,expiry_time,connectCB,errback,path=service_name)
+ messages_history.append(m)
+ else:
+ iffiles_queue:
+ deleteCB(None)
+ else:
+ self._deleteFiles_SMB1__list(service_name,path,pattern,delete_matching_folders,listCB,errback,timeout)
+
+ def_deleteFiles_SMB1__list(self,service_name,path,pattern,delete_matching_folders,callback,errback,timeout=30):
+ folder_queue=[]
+ files_list=[]
+ current_path=[path]
+ search=SMB_FILE_ATTRIBUTE_READONLY|SMB_FILE_ATTRIBUTE_HIDDEN|SMB_FILE_ATTRIBUTE_SYSTEM|SMB_FILE_ATTRIBUTE_DIRECTORY|SMB_FILE_ATTRIBUTE_ARCHIVE|SMB_FILE_ATTRIBUTE_INCL_NORMAL
+
+ deflistCB(results):
+ files=[]
+ forfinfilter(lambdax:x.filenamenotin['.','..'],results):
+ iff.isDirectory:
+ ifdelete_matching_folders:
+ folder_queue.append(current_path[0]+'\\'+f.filename)
+ else:
+ files.append((current_path[0],f.filename))
+ ifcurrent_path[0]!=pathanddelete_matching_folders:
+ files.append((current_path[0],None))
+
+ iffiles:
+ files_list[0:0]=files
+
+ iffolder_queue:
+ p=folder_queue.pop()
+ current_path[0]=p
+ self._listPath_SMB1(service_name,current_path[0],listCB,errback,search=search,pattern='*',timeout=30)
+ else:
+ callback(files_list)
+
+ self._listPath_SMB1(service_name,path,listCB,errback,search=search,pattern=pattern,timeout=timeout)
+
+
+ def_deleteFiles_SMB1__del(self,service_name,tid,path,callback,errback,timeout=30):messages_history=[]defsendDelete(tid):
@@ -2392,9 +2750,79 @@
defdeleteCB(delete_message,**kwargs):messages_history.append(delete_message)ifnotdelete_message.status.hasError:
+ callback(path)
+ elifdelete_message.status.internal_value==0xC000000FL:# [MS-ERREF]: STATUS_NO_SUCH_FILE
+ # If there are no matching files, we just treat as success instead of failingcallback(path_file_pattern)
- else:
- errback(OperationFailure('Failed to store %s on %s: Delete failed'%(path,service_name),messages_history))
+ elifdelete_message.status.internal_value==0xC00000BAL:# [MS-ERREF]: STATUS_FILE_IS_A_DIRECTORY
+ errback(OperationFailure('Failed to delete %s on %s: Cannot delete a folder. Please use deleteDirectory() method or append "/*" to your path if you wish to delete all files in the folder.'%(path,service_name),messages_history))
+ elifdelete_message.status.internal_value==0xC0000034L:# [MS-ERREF]: STATUS_OBJECT_NAME_INVALID
+ errback(OperationFailure('Failed to delete %s on %s: Path not found'%(path,service_name),messages_history))
+ else:
+ errback(OperationFailure('Failed to delete %s on %s: Delete failed'%(path,service_name),messages_history))
+
+ sendDelete(tid)
+
+ def_resetFileAttributes_SMB1(self,service_name,path_file_pattern,callback,errback,timeout=30):
+ raiseNotReadyError('resetFileAttributes is not yet implemented for SMB1')
+
+ def_createDirectory_SMB1(self,service_name,path,callback,errback,timeout=30):
+ ifnotself.has_authenticated:
+ raiseNotReadyError('SMB connection not authenticated')
+
+ path=path.replace('/','\\')
+ messages_history=[]
+
+ defsendCreate(tid):
+ m=SMBMessage(ComCreateDirectoryRequest(path))
+ m.tid=tid
+ self._sendSMBMessage(m)
+ self.pending_requests[m.mid]=_PendingRequest(m.mid,int(time.time())+timeout,createCB,errback)
+ messages_history.append(m)
+
+ defcreateCB(create_message,**kwargs):
+ messages_history.append(create_message)
+ ifnotcreate_message.status.hasError:
+ callback(path)
+ else:
+ errback(OperationFailure('Failed to create directory %s on %s: Create failed'%(path,service_name),messages_history))
+
+ ifnotself.connected_trees.has_key(service_name):
+ defconnectCB(connect_message,**kwargs):
+ messages_history.append(connect_message)
+ ifnotconnect_message.status.hasError:
+ self.connected_trees[service_name]=connect_message.tid
+ sendCreate(connect_message.tid)
+ else:
+ errback(OperationFailure('Failed to create directory %s on %s: Unable to connect to shared device'%(path,service_name),messages_history))
+
+ m=SMBMessage(ComTreeConnectAndxRequest(r'\\%s\%s'%(self.remote_name.upper(),service_name),SERVICE_ANY,''))
+ self._sendSMBMessage(m)
+ self.pending_requests[m.mid]=_PendingRequest(m.mid,int(time.time())+timeout,connectCB,errback,path=service_name)
+ messages_history.append(m)
+ else:
+ sendCreate(self.connected_trees[service_name])
+
+ def_deleteDirectory_SMB1(self,service_name,path,callback,errback,timeout=30):
+ ifnotself.has_authenticated:
+ raiseNotReadyError('SMB connection not authenticated')
+
+ path=path.replace('/','\\')
+ messages_history=[]
+
+ defsendDelete(tid):
+ m=SMBMessage(ComDeleteDirectoryRequest(path))
+ m.tid=tid
+ self._sendSMBMessage(m)
+ self.pending_requests[m.mid]=_PendingRequest(m.mid,int(time.time())+timeout,deleteCB,errback)
+ messages_history.append(m)
+
+ defdeleteCB(delete_message,**kwargs):
+ messages_history.append(delete_message)
+ ifnotdelete_message.status.hasError:
+ callback(path)
+ else:
+ errback(OperationFailure('Failed to delete directory %s on %s: Delete failed'%(path,service_name),messages_history))ifnotself.connected_trees.has_key(service_name):defconnectCB(connect_message,**kwargs):
@@ -2403,84 +2831,7 @@
self.connected_trees[service_name]=connect_message.tidsendDelete(connect_message.tid)else:
- errback(OperationFailure('Failed to delete %s on %s: Unable to connect to shared device'%(path,service_name),messages_history))
-
- m=SMBMessage(ComTreeConnectAndxRequest(r'\\%s\%s'%(self.remote_name.upper(),service_name),SERVICE_ANY,''))
- self._sendSMBMessage(m)
- self.pending_requests[m.mid]=_PendingRequest(m.mid,int(time.time())+timeout,connectCB,errback,path=service_name)
- messages_history.append(m)
- else:
- sendDelete(self.connected_trees[service_name])
-
- def_resetFileAttributes_SMB1(self,service_name,path_file_pattern,callback,errback,timeout=30):
- raiseNotReadyError('resetFileAttributes is not yet implemented for SMB1')
-
- def_createDirectory_SMB1(self,service_name,path,callback,errback,timeout=30):
- ifnotself.has_authenticated:
- raiseNotReadyError('SMB connection not authenticated')
-
- path=path.replace('/','\\')
- messages_history=[]
-
- defsendCreate(tid):
- m=SMBMessage(ComCreateDirectoryRequest(path))
- m.tid=tid
- self._sendSMBMessage(m)
- self.pending_requests[m.mid]=_PendingRequest(m.mid,int(time.time())+timeout,createCB,errback)
- messages_history.append(m)
-
- defcreateCB(create_message,**kwargs):
- messages_history.append(create_message)
- ifnotcreate_message.status.hasError:
- callback(path)
- else:
- errback(OperationFailure('Failed to create directory %s on %s: Create failed'%(path,service_name),messages_history))
-
- ifnotself.connected_trees.has_key(service_name):
- defconnectCB(connect_message,**kwargs):
- messages_history.append(connect_message)
- ifnotconnect_message.status.hasError:
- self.connected_trees[service_name]=connect_message.tid
- sendCreate(connect_message.tid)
- else:
- errback(OperationFailure('Failed to create directory %s on %s: Unable to connect to shared device'%(path,service_name),messages_history))
-
- m=SMBMessage(ComTreeConnectAndxRequest(r'\\%s\%s'%(self.remote_name.upper(),service_name),SERVICE_ANY,''))
- self._sendSMBMessage(m)
- self.pending_requests[m.mid]=_PendingRequest(m.mid,int(time.time())+timeout,connectCB,errback,path=service_name)
- messages_history.append(m)
- else:
- sendCreate(self.connected_trees[service_name])
-
- def_deleteDirectory_SMB1(self,service_name,path,callback,errback,timeout=30):
- ifnotself.has_authenticated:
- raiseNotReadyError('SMB connection not authenticated')
-
- path=path.replace('/','\\')
- messages_history=[]
-
- defsendDelete(tid):
- m=SMBMessage(ComDeleteDirectoryRequest(path))
- m.tid=tid
- self._sendSMBMessage(m)
- self.pending_requests[m.mid]=_PendingRequest(m.mid,int(time.time())+timeout,deleteCB,errback)
- messages_history.append(m)
-
- defdeleteCB(delete_message,**kwargs):
- messages_history.append(delete_message)
- ifnotdelete_message.status.hasError:
- callback(path)
- else:
- errback(OperationFailure('Failed to delete directory %s on %s: Delete failed'%(path,service_name),messages_history))
-
- ifnotself.connected_trees.has_key(service_name):
- defconnectCB(connect_message,**kwargs):
- messages_history.append(connect_message)
- ifnotconnect_message.status.hasError:
- self.connected_trees[service_name]=connect_message.tid
- sendDelete(connect_message.tid)
- else:
- errback(OperationFailure('Failed to delete %s on %s: Unable to connect to shared device'%(path,service_name),messages_history))
+ errback(OperationFailure('Failed to delete directory %s on %s: Unable to connect to shared device'%(path,service_name),messages_history))m=SMBMessage(ComTreeConnectAndxRequest(r'\\%s\%s'%(self.remote_name.upper(),service_name),SERVICE_ANY,''))self._sendSMBMessage(m)
@@ -2615,6 +2966,9 @@
def_echo_SMB1(self,data,callback,errback,timeout=30):messages_history=[]
+ ifnotisinstance(data,type(b'')):
+ raiseTypeError('Echo data must be %s not %s'%(type(b'').__name__,type(data).__name__))
+
defechoCB(echo_message,**kwargs):messages_history.append(echo_message)ifnotecho_message.status.hasError:
@@ -2627,10 +2981,18 @@
self.pending_requests[m.mid]=_PendingRequest(m.mid,int(time.time())+timeout,echoCB,errback)messages_history.append(m)
+ def_extractLastPathComponent(self,path):
+ returnpath.replace('\\','/').split('/')[-1]
+
[docs]classSharedDevice:""" Contains information about a single shared device on the remote server.
+
+ The following attributes are available:
+
+ * name : An unicode string containing the name of the shared device
+ * comments : An unicode string containing the user description of the shared device """# The following constants are taken from [MS-SRVS]: 2.2.2.4
@@ -2686,18 +3048,32 @@
If you encounter *SharedFile* instance where its short_name attribute is empty but the filename attribute contains a short name which does not correspond to any files/folders on your remote shared device, it could be that the original filename on the file/folder entry on the shared device contains one of these prohibited characters: "\/[]:+|<>=;?,* (see [MS-CIFS]: 2.2.1.1.1 for more details).
+
+ The following attributes are available:
+
+ * create_time : Float value in number of seconds since 1970-01-01 00:00:00 to the time of creation of this file resource on the remote server
+ * last_access_time : Float value in number of seconds since 1970-01-01 00:00:00 to the time of last access of this file resource on the remote server
+ * last_write_time : Float value in number of seconds since 1970-01-01 00:00:00 to the time of last modification of this file resource on the remote server
+ * last_attr_change_time : Float value in number of seconds since 1970-01-01 00:00:00 to the time of last attribute change of this file resource on the remote server
+ * file_size : File size in number of bytes
+ * alloc_size : Total number of bytes allocated to store this file
+ * file_attributes : A SMB_EXT_FILE_ATTR integer value. See [MS-CIFS]: 2.2.1.2.3. You can perform bit-wise tests to determine the status of the file using the ATTR_xxx constants in smb_constants.py.
+ * short_name : Unicode string containing the short name of this file (usually in 8.3 notation)
+ * filename : Unicode string containing the long filename of this file. Each OS has a limit to the length of this file name. On Windows, it is 256 characters.
+ * file_id : Long value representing the file reference number for the file. If the remote system does not support this field, this field will be None or 0. See [MS-FSCC]: 2.4.17 """
- def__init__(self,create_time,last_access_time,last_write_time,last_attr_change_time,file_size,alloc_size,file_attributes,short_name,filename):
+ def__init__(self,create_time,last_access_time,last_write_time,last_attr_change_time,file_size,alloc_size,file_attributes,short_name,filename,file_id=None):self.create_time=create_time#: Float value in number of seconds since 1970-01-01 00:00:00 to the time of creation of this file resource on the remote serverself.last_access_time=last_access_time#: Float value in number of seconds since 1970-01-01 00:00:00 to the time of last access of this file resource on the remote serverself.last_write_time=last_write_time#: Float value in number of seconds since 1970-01-01 00:00:00 to the time of last modification of this file resource on the remote serverself.last_attr_change_time=last_attr_change_time#: Float value in number of seconds since 1970-01-01 00:00:00 to the time of last attribute change of this file resource on the remote serverself.file_size=file_size#: File size in number of bytesself.alloc_size=alloc_size#: Total number of bytes allocated to store this file
- self.file_attributes=file_attributes#: A SMB_EXT_FILE_ATTR integer value. See [MS-CIFS]: 2.2.1.2.3
+ self.file_attributes=file_attributes#: A SMB_EXT_FILE_ATTR integer value. See [MS-CIFS]: 2.2.1.2.3. You can perform bit-wise tests to determine the status of the file using the ATTR_xxx constants in smb_constants.py.self.short_name=short_name#: Unicode string containing the short name of this file (usually in 8.3 notation)self.filename=filename#: Unicode string containing the long filename of this file. Each OS has a limit to the length of this file name. On Windows, it is 256 characters.
+ self.file_id=file_id#: Long value representing the file reference number for the file. If the remote system does not support this field, this field will be None or 0. See [MS-FSCC]: 2.4.17@propertydefisDirectory(self):
@@ -2708,6 +3084,16 @@
defisReadOnly(self):"""A convenient property to return True if this file resource is read-only on the remote server"""returnbool(self.file_attributes&ATTR_READONLY)
+
+ @property
+ defisNormal(self):
+ """
+ A convenient property to return True if this is a normal file.
+
+ Note that pysmb defines a normal file as a file entry that is not read-only, not hidden, not system, not archive and not a directory.
+ It ignores other attributes like compression, indexed, sparse, temporary and encryption.
+ """
+ return(self.file_attributes==ATTR_NORMAL)or((self.file_attributes&0xff)==0)def__unicode__(self):returnu'Shared file: %s (FileSize:%d bytes, isDirectory:%s)'%(self.filename,self.file_size,self.isDirectory)
[docs]classSID(object):
+ """
+ A Windows security identifier. Represents a single principal, such a
+ user or a group, as a sequence of numbers consisting of the revision,
+ identifier authority, and a variable-length list of subauthorities.
+
+ See [MS-DTYP]: 2.4.2
+ """
+ def__init__(self,revision,identifier_authority,subauthorities):
+ #: Revision, should always be 1.
+ self.revision=revision
+ #: An integer representing the identifier authority.
+ self.identifier_authority=identifier_authority
+ #: A list of integers representing all subauthorities.
+ self.subauthorities=subauthorities
+
+ def__str__(self):
+ """
+ String representation, as specified in [MS-DTYP]: 2.4.2.1
+ """
+ ifself.identifier_authority>=2**32:
+ id_auth='%#x'%(self.identifier_authority,)
+ else:
+ id_auth=self.identifier_authority
+ auths=[self.revision,id_auth]+self.subauthorities
+ return'S-'+'-'.join(str(subauth)forsubauthinauths)
+
+ def__repr__(self):
+ return'SID(%r)'%(str(self),)
+
+ @classmethod
+ deffrom_bytes(cls,data,return_tail=False):
+ revision,subauth_count=struct.unpack('<BB',data[:2])
+ identifier_authority=struct.unpack('>Q','\x00\x00'+data[2:8])[0]
+ subauth_data=data[8:]
+ subauthorities=[struct.unpack('<L',subauth_data[4*i:4*(i+1)])[0]
+ foriinrange(subauth_count)]
+ sid=cls(revision,identifier_authority,subauthorities)
+ ifreturn_tail:
+ returnsid,subauth_data[4*subauth_count:]
+ returnsid
+
+
+
[docs]classACE(object):
+ """
+ Represents a single access control entry.
+
+ See [MS-DTYP]: 2.4.4
+ """
+ HEADER_FORMAT='<BBH'
+
+ def__init__(self,type_,flags,mask,sid,additional_data):
+ #: An integer representing the type of the ACE. One of the
+ #: ``ACE_TYPE_*`` constants. Corresponds to the ``AceType`` field
+ #: from [MS-DTYP] 2.4.4.1.
+ self.type=type_
+ #: An integer bitmask with ACE flags, corresponds to the
+ #: ``AceFlags`` field.
+ self.flags=flags
+ #: An integer representing the ``ACCESS_MASK`` as specified in
+ #: [MS-DTYP] 2.4.3.
+ self.mask=mask
+ #: The :class:`SID` of a trustee.
+ self.sid=sid
+ #: A dictionary of additional fields present in the ACE, depending
+ #: on the type. The following fields can be present:
+ #:
+ #: * ``flags``
+ #: * ``object_type``
+ #: * ``inherited_object_type``
+ #: * ``application_data``
+ #: * ``attribute_data``
+ self.additional_data=additional_data
+
+ def__repr__(self):
+ return"ACE(type=%#04x, flags=%#04x, mask=%#010x, sid=%s)"%(
+ self.type,self.flags,self.mask,self.sid,
+ )
+
+ @property
+ defisInheritOnly(self):
+ """Convenience property which indicates if this ACE is inherit
+ only, meaning that it doesn't apply to the object itself."""
+ returnbool(self.flags&ACE_FLAG_INHERIT_ONLY)
+
+ @classmethod
+ deffrom_bytes(cls,data):
+ header_size=struct.calcsize(cls.HEADER_FORMAT)
+ header=data[:header_size]
+ type_,flags,size=struct.unpack(cls.HEADER_FORMAT,header)
+
+ assertlen(data)>=size
+
+ body=data[header_size:size]
+ additional_data={}
+
+ # In all ACE types, the mask immediately follows the header.
+ mask=struct.unpack('<I',body[:4])[0]
+ body=body[4:]
+
+ # All OBJECT-type ACEs contain additional flags, and two GUIDs as
+ # the following fields.
+ iftype_in(ACE_TYPE_ACCESS_ALLOWED_OBJECT,
+ ACE_TYPE_ACCESS_DENIED_OBJECT,
+ ACE_TYPE_ACCESS_ALLOWED_CALLBACK_OBJECT,
+ ACE_TYPE_ACCESS_DENIED_CALLBACK_OBJECT,
+ ACE_TYPE_SYSTEM_AUDIT_OBJECT,
+ ACE_TYPE_SYSTEM_AUDIT_CALLBACK_OBJECT):
+ additional_data['flags']=struct.unpack('<I',body[:4])[0]
+ additional_data['object_type']=body[4:20]
+ additional_data['inherited_object_type']=body[20:36]
+ body=body[36:]
+
+ # Then the SID in all types.
+ sid,body=SID.from_bytes(body,return_tail=True)
+
+ # CALLBACK-type ACEs (and for some obscure reason,
+ # SYSTEM_AUDIT_OBJECT) have a final tail of application data.
+ iftype_in(ACE_TYPE_ACCESS_ALLOWED_CALLBACK,
+ ACE_TYPE_ACCESS_DENIED_CALLBACK,
+ ACE_TYPE_ACCESS_ALLOWED_CALLBACK_OBJECT,
+ ACE_TYPE_ACCESS_DENIED_CALLBACK_OBJECT,
+ ACE_TYPE_SYSTEM_AUDIT_OBJECT,
+ ACE_TYPE_SYSTEM_AUDIT_CALLBACK,
+ ACE_TYPE_SYSTEM_AUDIT_CALLBACK_OBJECT):
+ additional_data['application_data']=body
+
+ # SYSTEM_RESOURCE_ATTRIBUTE ACEs have a tail of attribute data.
+ iftype_==ACE_TYPE_SYSTEM_RESOURCE_ATTRIBUTE:
+ additional_data['attribute_data']=body
+
+ returncls(type_,flags,mask,sid,additional_data)
+
+
+
[docs]classACL(object):
+ """
+ Access control list, encapsulating a sequence of access control
+ entries.
+
+ See [MS-DTYP]: 2.4.5
+ """
+ HEADER_FORMAT='<BBHHH'
+
+ def__init__(self,revision,aces):
+ #: Integer value of the revision.
+ self.revision=revision
+ #: List of :class:`ACE` instances.
+ self.aces=aces
+
+ def__repr__(self):
+ return"ACL(%r)"%(self.aces,)
+
+ @classmethod
+ deffrom_bytes(cls,data):
+ revision=None
+ aces=[]
+
+ header_size=struct.calcsize(cls.HEADER_FORMAT)
+ header,remaining=data[:header_size],data[header_size:]
+ revision,sbz1,size,count,sbz2=struct.unpack(cls.HEADER_FORMAT,header)
+
+ assertlen(data)>=size
+
+ foriinrange(count):
+ ace_size=struct.unpack('<H',remaining[2:4])[0]
+ ace_data,remaining=remaining[:ace_size],remaining[ace_size:]
+ aces.append(ACE.from_bytes(ace_data))
+
+ returncls(revision,aces)
+
+
+
[docs]classSecurityDescriptor(object):
+ """
+ Represents a security descriptor.
+
+ See [MS-DTYP]: 2.4.6
+ """
+
+ HEADER_FORMAT='<BBHIIII'
+
+ def__init__(self,flags,owner,group,dacl,sacl):
+ #: Integer bitmask of control flags. Corresponds to the
+ #: ``Control`` field in [MS-DTYP] 2.4.6.
+ self.flags=flags
+ #: Instance of :class:`SID` representing the owner user.
+ self.owner=owner
+ #: Instance of :class:`SID` representing the owner group.
+ self.group=group
+ #: Instance of :class:`ACL` representing the discretionary access
+ #: control list, which specifies access restrictions of an object.
+ self.dacl=dacl
+ #: Instance of :class:`ACL` representing the system access control
+ #: list, which specifies audit logging of an object.
+ self.sacl=sacl
+
+ @classmethod
+ deffrom_bytes(cls,data):
+ owner=None
+ group=None
+ dacl=None
+ sacl=None
+
+ header=data[:struct.calcsize(cls.HEADER_FORMAT)]
+ (revision,sbz1,flags,owner_offset,group_offset,sacl_offset,
+ dacl_offset)=struct.unpack(cls.HEADER_FORMAT,header)
+
+ assertrevision==1
+ assertflags&SECURITY_DESCRIPTOR_SELF_RELATIVE
+ foroffsetin(owner_offset,group_offset,sacl_offset,dacl_offset):
+ assert0<=offset<len(data)
+
+ ifowner_offset:
+ owner=SID.from_bytes(data[owner_offset:])
+ ifgroup_offset:
+ group=SID.from_bytes(data[group_offset:])
+ ifdacl_offset:
+ dacl=ACL.from_bytes(data[dacl_offset:])
+ ifsacl_offset:
+ sacl=ACL.from_bytes(data[sacl_offset:])
+
+ returncls(flags,owner,group,dacl,sacl)
diff --git a/docs/html/_sources/api/smb_SMBHandler.txt b/docs/html/_sources/api/smb_SMBHandler.txt
index da106fe..9300de1 100644
--- a/docs/html/_sources/api/smb_SMBHandler.txt
+++ b/docs/html/_sources/api/smb_SMBHandler.txt
@@ -6,8 +6,12 @@
Notes
-----
-* Note that you need to pass in a valid hostname or IP address for the host component of the URL.
- Do not use the Windows/NetBIOS machine name for the host component.
+* The host component of the URL must be one of the following:
+
+ * A fully-qualified hostname that can be resolved by your local DNS service. Example: myserver.test.com
+ * An IP address. Example: 192.168.1.1
+ * A comma-separated string "," where ** is the Windows/NetBIOS machine name for remote SMB service, and ** is the service's IP address. Example: MYSERVER,192.168.1.1
+
* The first component of the path in the URL points to the name of the shared folder.
Subsequent path components will point to the directory/folder of the file.
* You can retrieve and upload files, but you cannot delete files/folders or create folders.
@@ -16,7 +20,7 @@
Example
-------
-The following code snippet illustrates file retrieval.::
+The following code snippet illustrates file retrieval with Python 2.::
# -*- coding: utf-8 -*-
import urllib2
@@ -34,7 +38,7 @@
# Process fh2 like a file-like object and then close it.
fh2.close()
-The following code snippet illustrates file upload. You need to provide a file-like object for the *data* parameter in the *open()* method::
+The following code snippet illustrates file upload with Python 2. You need to provide a file-like object for the *data* parameter in the *open()* method::
import urllib2
from smb.SMBHandler import SMBHandler
@@ -46,3 +50,34 @@
# Reading from fh will only return an empty string
fh.close()
+
+
+The following code snippet illustrates file retrieval with Python 3.::
+
+ import urllib
+ from smb.SMBHandler import SMBHandler
+
+ director = urllib.request.build_opener(SMBHandler)
+ fh = director.open('smb://myuserID:mypassword@192.168.1.1/sharedfolder/rfc1001.txt')
+
+ # Process fh like a file-like object and then close it.
+ fh.close()
+
+ # For paths/files with unicode characters, simply pass in the URL as an unicode string
+ fh2 = director.open(u'smb://myuserID:mypassword@192.168.1.1/sharedfolder/测试文件夹/垃圾文件.dat')
+
+ # Process fh2 like a file-like object and then close it.
+ fh2.close()
+
+The following code snippet illustrates file upload with Python 3. You need to provide a file-like object for the *data* parameter in the *open()* method::
+
+ import urllib
+ from smb.SMBHandler import SMBHandler
+
+ file_fh = open('local_file.dat', 'rb')
+
+ director = urllib.request.build_opener(SMBHandler)
+ fh = director.open('smb://myuserID:mypassword@192.168.1.1/sharedfolder/upload_file.dat', data = file_fh)
+
+ # Reading from fh will only return an empty string
+ fh.close()
diff --git a/docs/html/_sources/api/smb_security_descriptors.txt b/docs/html/_sources/api/smb_security_descriptors.txt
new file mode 100644
index 0000000..0f048fe
--- /dev/null
+++ b/docs/html/_sources/api/smb_security_descriptors.txt
@@ -0,0 +1,23 @@
+
+Security Descriptors
+====================
+
+.. module:: smb.security_descriptors
+ :synopsis: Data structures used in Windows security descriptors.
+
+This module implements security descriptors, and associated data
+structures, as specified in `[MS-DTYP]`_.
+
+.. autoclass:: SID
+ :members:
+
+.. autoclass:: ACE
+ :members:
+
+.. autoclass:: ACL
+ :members:
+
+.. autoclass:: SecurityDescriptor
+ :members:
+
+.. _[MS-DTYP]: https://msdn.microsoft.com/en-us/library/cc230273.aspx
diff --git a/docs/html/_sources/index.txt b/docs/html/_sources/index.txt
index 7638fcd..e3eb1ac 100644
--- a/docs/html/_sources/index.txt
+++ b/docs/html/_sources/index.txt
@@ -8,9 +8,9 @@
pysmb is a pure Python implementation of the client-side SMB/CIFS protocol (SMB1 and SMB2) which is the underlying protocol
that facilitates file sharing and printing between Windows machines, as well as with Linux machines via the Samba server application.
-pysmb is developed in Python 2.4.6, Python 2.7.1 and Python 3.2.3 and has been tested against shared folders on Windows XP SP3, Windows Vista, Windows 7 and Samba 3.x.
+pysmb is developed in Python 2.7.x and Python 3.5.x and has been tested against shared folders on Windows XP SP3, Windows Vista, Windows 7 and Samba 3.x.
-The latest version of pysmb is always available at the pysmb project page at `miketeo.net `_.
+The latest version of pysmb is always available at the pysmb project page at `miketeo.net `_.
License
-------
@@ -90,6 +90,8 @@
As a software developer who is looking to modify pysmb so that you can integrate it to other network frameworks:
* Read :doc:`extending`
+If you are upgrading from older pysmb versions:
+ * Read :doc:`upgrading`
Indices and tables
@@ -101,6 +103,7 @@
api/*
extending
+ upgrading
* :ref:`genindex`
* :ref:`search`
diff --git a/docs/html/_sources/upgrading.txt b/docs/html/_sources/upgrading.txt
new file mode 100644
index 0000000..8d5e7cd
--- /dev/null
+++ b/docs/html/_sources/upgrading.txt
@@ -0,0 +1,63 @@
+Upgrading from older pysmb versions
+====================================
+
+This page documents the improvements and changes to the API that could be incompatible with previous releases.
+
+pysmb 1.2.0
+-----------
+- Add new `delete_matching_folders` parameter to `deleteFiles()` method in SMBProtocolFactory and SMBConnection
+ class to support deletion of sub-folders. If you are passing timeout parameter to the `deleteFiles()` method
+ in your application, please switch to using named parameter for timeout.
+
+pysmb 1.1.28
+------------
+- SharedFile instances returned from the `listPath()` method now has a new property
+ `file_id` attribute which represents the file reference number given by the remote SMB server.
+
+pysmb 1.1.26
+------------
+- SMBConnection class can now be used as a context manager
+
+pysmb 1.1.25
+------------
+- SharedFile class has a new property `isNormal` which will be True if the file is a
+ 'normal' file. pysmb defines a 'normal' file as a file entry that is not
+ read-only, not hidden, not system, not archive and not a directory;
+ it ignores other attributes like compression, indexed, sparse, temporary and encryption.
+- `listPath()` method in SMBProtocolFactory and SMBConnection class will now include
+ 'normal' files by default if you do not specify the `search` parameter.
+
+pysmb 1.1.20
+------------
+- A new method `getSecurity()` was added to SMBConnection and SMBProtocolFactory class.
+
+pysmb 1.1.15
+------------
+- Add new `truncate` parameter to `storeFileFromOffset()` in SMBProtocolFactory and SMBConnection
+ class to support truncation of the file before writing. If you are passing timeout parameter
+ to the `storeFileFromOffset()` method in your application, please switch to using named parameter for timeout.
+
+pysmb 1.1.11
+------------
+- A new method `storeFileFromOffset()` was added to SMBConnection and SMBProtocolFactory class.
+
+pysmb 1.1.10
+------------
+- A new method `getAttributes()` was added to SMBConnection and SMBProtocolFactory class
+- SharedFile class has a new property `isReadOnly` to indicate the file is read-only on the remote filesystem.
+
+pysmb 1.1.2
+-----------
+- `queryIPForName()` method in nmb.NetBIOS and nmb.NBNSProtocol class will now return only the server machine name and ignore workgroup names.
+
+pysmb 1.0.3
+-----------
+- Two new methods were added to NBNSProtocol class: `queryIPForName()` and `NetBIOS.queryIPForName()`
+ to support querying for a machine's NetBIOS name at the given IP address.
+- A new method `retrieveFileFromOffset()` was added to SMBProtocolFactory and SMBConnection
+ to support finer control of file retrieval operation.
+
+pysmb 1.0.0
+-----------
+pysmb was completely rewritten in version 1.0.0.
+If you are upgrading from pysmb 0.x, you most likely have to rewrite your application for the new 1.x API.
diff --git a/docs/html/api/nmb_NBNSProtocol.html b/docs/html/api/nmb_NBNSProtocol.html
index 263abd9..0ecd94d 100644
--- a/docs/html/api/nmb_NBNSProtocol.html
+++ b/docs/html/api/nmb_NBNSProtocol.html
@@ -6,7 +6,7 @@
- NBNSProtocol Class — pysmb 1.1.18 documentation
+ NBNSProtocol Class — pysmb 1.2.1 documentation
@@ -14,7 +14,7 @@
-
+
@@ -35,12 +35,15 @@
index
username and password are the user credentials required to authenticate the underlying SMB connection with the remote server.
+password can be a string or a callable returning a string.
File operations can only be proceeded after the connection has been authenticated successfully.
Note that you need to call connect method to actually establish the SMB connection to the remote server and perform authentication.
The default TCP port for most SMB/CIFS servers using NetBIOS over TCP/IP is 139.
@@ -244,8 +248,9 @@
Retrieve a directory listing of files/folders at path
+
For simplicity, pysmb defines a “normal” file as a file entry that is not read-only, not hidden, not system, not archive and not a directory.
+It ignores other attributes like compression, indexed, sparse, temporary and encryption.
+
Note that the default search parameter will query for all read-only (SMB_FILE_ATTRIBUTE_READONLY), hidden (SMB_FILE_ATTRIBUTE_HIDDEN),
+system (SMB_FILE_ATTRIBUTE_SYSTEM), archive (SMB_FILE_ATTRIBUTE_ARCHIVE), normal (SMB_FILE_ATTRIBUTE_INCL_NORMAL) files
+and directories (SMB_FILE_ATTRIBUTE_DIRECTORY).
+If you do not need to include “normal” files in the result, define your own search parameter without the SMB_FILE_ATTRIBUTE_INCL_NORMAL constant.
+SMB_FILE_ATTRIBUTE_NORMAL should be used by itself and not be used with other bit constants.
@@ -313,8 +346,7 @@
Parameters:
service_name (string/unicode) – the name of the shared folder for the path
path (string/unicode) – path relative to the service_name where we are interested to learn about its files/sub-folders.
-
search (integer) – integer value made up from a bitwise-OR of SMB_FILE_ATTRIBUTE_xxx bits (see smb_constants.py).
-The default search value will query for all read-only, hidden, system, archive files and directories.
+
search (integer) – integer value made up from a bitwise-OR of SMB_FILE_ATTRIBUTE_xxx bits (see smb_constants.py).
pattern (string/unicode) – the filter to apply to the results before returning to the client.
Note that you need to pass in a valid hostname or IP address for the host component of the URL.
-Do not use the Windows/NetBIOS machine name for the host component.
+
The host component of the URL must be one of the following:
+
A fully-qualified hostname that can be resolved by your local DNS service. Example: myserver.test.com
+
An IP address. Example: 192.168.1.1
+
A comma-separated string “<NBName>,<IP>” where <NBName> is the Windows/NetBIOS machine name for remote SMB service, and <IP> is the service’s IP address. Example: MYSERVER,192.168.1.1
+
+
The first component of the path in the URL points to the name of the shared folder.
Subsequent path components will point to the directory/folder of the file.
You can retrieve and upload files, but you cannot delete files/folders or create folders.
@@ -104,7 +111,7 @@
The following code snippet illustrates file upload. You need to provide a file-like object for the data parameter in the open() method:
+
The following code snippet illustrates file upload with Python 2. You need to provide a file-like object for the data parameter in the open() method:
importurllib2fromsmb.SMBHandlerimportSMBHandlerfile_fh=open('local_file.dat','rb')director=urllib2.build_opener(SMBHandler)
+fh=director.open('smb://myuserID:mypassword@192.168.1.1/sharedfolder/upload_file.dat',data=file_fh)
+
+# Reading from fh will only return an empty string
+fh.close()
+
+
+
The following code snippet illustrates file retrieval with Python 3.:
+
importurllib
+fromsmb.SMBHandlerimportSMBHandler
+
+director=urllib.request.build_opener(SMBHandler)
+fh=director.open('smb://myuserID:mypassword@192.168.1.1/sharedfolder/rfc1001.txt')
+
+# Process fh like a file-like object and then close it.
+fh.close()
+
+# For paths/files with unicode characters, simply pass in the URL as an unicode string
+fh2=director.open(u'smb://myuserID:mypassword@192.168.1.1/sharedfolder/测试文件夹/垃圾文件.dat')
+
+# Process fh2 like a file-like object and then close it.
+fh2.close()
+
+
+
The following code snippet illustrates file upload with Python 3. You need to provide a file-like object for the data parameter in the open() method:
+
importurllib
+fromsmb.SMBHandlerimportSMBHandler
+
+file_fh=open('local_file.dat','rb')
+
+director=urllib.request.build_opener(SMBHandler)fh=director.open('smb://myuserID:mypassword@192.168.1.1/sharedfolder/upload_file.dat',data=file_fh)# Reading from fh will only return an empty string
@@ -151,16 +188,19 @@
index
Retrieve a directory listing of files/folders at path
+
For simplicity, pysmb defines a “normal” file as a file entry that is not read-only, not hidden, not system, not archive and not a directory.
+It ignores other attributes like compression, indexed, sparse, temporary and encryption.
+
Note that the default search parameter will query for all read-only (SMB_FILE_ATTRIBUTE_READONLY), hidden (SMB_FILE_ATTRIBUTE_HIDDEN),
+system (SMB_FILE_ATTRIBUTE_SYSTEM), archive (SMB_FILE_ATTRIBUTE_ARCHIVE), normal (SMB_FILE_ATTRIBUTE_INCL_NORMAL) files
+and directories (SMB_FILE_ATTRIBUTE_DIRECTORY).
+If you do not need to include “normal” files in the result, define your own search parameter without the SMB_FILE_ATTRIBUTE_INCL_NORMAL constant.
+SMB_FILE_ATTRIBUTE_NORMAL should be used by itself and not be used with other bit constants.
@@ -349,8 +359,7 @@
Parameters:
service_name (string/unicode) – the name of the shared folder for the path
path (string/unicode) – path relative to the service_name where we are interested to learn about its files/sub-folders.
-
search (integer) – integer value made up from a bitwise-OR of SMB_FILE_ATTRIBUTE_xxx bits (see smb_constants.py).
-The default search value will query for all read-only, hidden, system, archive files and directories.
+
search (integer) – integer value made up from a bitwise-OR of SMB_FILE_ATTRIBUTE_xxx bits (see smb_constants.py).
pattern (string/unicode) – the filter to apply to the results before returning to the client.
timeout (integer/float) – Number of seconds that pysmb will wait before raising SMBTimeout via the returned Deferred instance’s errback method.
Contain information about a file/folder entry that is shared on the shared device.
As an application developer, you should not need to instantiate a SharedFile instance directly in your application.
These SharedFile instances are usually returned via a call to listPath method in smb.SMBProtocol.SMBProtocolFactory.
If you encounter SharedFile instance where its short_name attribute is empty but the filename attribute contains a short name which does not correspond
to any files/folders on your remote shared device, it could be that the original filename on the file/folder entry on the shared device contains
one of these prohibited characters: “/[]:+|<>=;?,* (see [MS-CIFS]: 2.2.1.1.1 for more details).
+
The following attributes are available:
+
+
create_time : Float value in number of seconds since 1970-01-01 00:00:00 to the time of creation of this file resource on the remote server
+
last_access_time : Float value in number of seconds since 1970-01-01 00:00:00 to the time of last access of this file resource on the remote server
+
last_write_time : Float value in number of seconds since 1970-01-01 00:00:00 to the time of last modification of this file resource on the remote server
+
last_attr_change_time : Float value in number of seconds since 1970-01-01 00:00:00 to the time of last attribute change of this file resource on the remote server
+
file_size : File size in number of bytes
+
alloc_size : Total number of bytes allocated to store this file
+
file_attributes : A SMB_EXT_FILE_ATTR integer value. See [MS-CIFS]: 2.2.1.2.3. You can perform bit-wise tests to determine the status of the file using the ATTR_xxx constants in smb_constants.py.
+
short_name : Unicode string containing the short name of this file (usually in 8.3 notation)
+
filename : Unicode string containing the long filename of this file. Each OS has a limit to the length of this file name. On Windows, it is 256 characters.
+
file_id : Long value representing the file reference number for the file. If the remote system does not support this field, this field will be None or 0. See [MS-FSCC]: 2.4.17
A SMB_EXT_FILE_ATTR integer value. See [MS-CIFS]: 2.2.1.2.3
+
A SMB_EXT_FILE_ATTR integer value. See [MS-CIFS]: 2.2.1.2.3. You can perform bit-wise tests to determine the status of the file using the ATTR_xxx constants in smb_constants.py.
Long value representing the file reference number for the file. If the remote system does not support this field, this field will be None or 0. See [MS-FSCC]: 2.4.17
A convenient property to return True if this is a normal file.
+
Note that pysmb defines a normal file as a file entry that is not read-only, not hidden, not system, not archive and not a directory.
+It ignores other attributes like compression, indexed, sparse, temporary and encryption.
A Windows security identifier. Represents a single principal, such a
+user or a group, as a sequence of numbers consisting of the revision,
+identifier authority, and a variable-length list of subauthorities.