diff options
Diffstat (limited to 'Emby.Server.Implementations/IO/SharpCifs/Smb/SmbFile.cs')
| -rw-r--r-- | Emby.Server.Implementations/IO/SharpCifs/Smb/SmbFile.cs | 3755 |
1 files changed, 3755 insertions, 0 deletions
diff --git a/Emby.Server.Implementations/IO/SharpCifs/Smb/SmbFile.cs b/Emby.Server.Implementations/IO/SharpCifs/Smb/SmbFile.cs new file mode 100644 index 000000000..151ec35c4 --- /dev/null +++ b/Emby.Server.Implementations/IO/SharpCifs/Smb/SmbFile.cs @@ -0,0 +1,3755 @@ +// This code is derived from jcifs smb client library <jcifs at samba dot org> +// Ported by J. Arturo <webmaster at komodosoft dot net> +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Text; +using SharpCifs.Dcerpc; +using SharpCifs.Dcerpc.Msrpc; +using SharpCifs.Netbios; +using SharpCifs.Util; +using SharpCifs.Util.Sharpen; + +namespace SharpCifs.Smb +{ + /// <summary>This class represents a resource on an SMB network.</summary> + /// <remarks> + /// This class represents a resource on an SMB network. Mainly these + /// resources are files and directories however an <code>SmbFile</code> + /// may also refer to servers and workgroups. If the resource is a file or + /// directory the methods of <code>SmbFile</code> follow the behavior of + /// the well known + /// <see cref="FilePath">Sharpen.FilePath</see> + /// class. One fundamental difference + /// is the usage of a URL scheme [1] to specify the target file or + /// directory. SmbFile URLs have the following syntax: + /// <blockquote><pre> + /// smb://[[[domain;]username[:password]@]server[:port]/[[share/[dir/]file]]][?[param=value[param2=value2[...]]] + /// </pre></blockquote> + /// This example: + /// <blockquote><pre> + /// smb://storage15/public/foo.txt + /// </pre></blockquote> + /// would reference the file <code>foo.txt</code> in the share + /// <code>public</code> on the server <code>storage15</code>. In addition + /// to referencing files and directories, jCIFS can also address servers, + /// and workgroups. + /// <p> + /// <font color="#800000"><i>Important: all SMB URLs that represent + /// workgroups, servers, shares, or directories require a trailing slash '/'. + /// </i></font> + /// <p> + /// When using the <tt>java.net.URL</tt> class with + /// 'smb://' URLs it is necessary to first call the static + /// <tt>jcifs.Config.registerSmbURLHandler();</tt> method. This is required + /// to register the SMB protocol handler. + /// <p> + /// The userinfo component of the SMB URL (<tt>domain;user:pass</tt>) must + /// be URL encoded if it contains reserved characters. According to RFC 2396 + /// these characters are non US-ASCII characters and most meta characters + /// however jCIFS will work correctly with anything but '@' which is used + /// to delimit the userinfo component from the server and '%' which is the + /// URL escape character itself. + /// <p> + /// The server + /// component may a traditional NetBIOS name, a DNS name, or IP + /// address. These name resolution mechanisms and their resolution order + /// can be changed (See <a href="../../../resolver.html">Setting Name + /// Resolution Properties</a>). The servername and path components are + /// not case sensitive but the domain, username, and password components + /// are. It is also likely that properties must be specified for jcifs + /// to function (See <a href="../../overview-summary.html#scp">Setting + /// JCIFS Properties</a>). Here are some examples of SMB URLs with brief + /// descriptions of what they do: + /// <p>[1] This URL scheme is based largely on the <i>SMB + /// Filesharing URL Scheme</i> IETF draft. + /// <p><table border="1" cellpadding="3" cellspacing="0" width="100%"> + /// <tr bgcolor="#ccccff"> + /// <td colspan="2"><b>SMB URL Examples</b></td> + /// <tr><td width="20%"><b>URL</b></td><td><b>Description</b></td></tr> + /// <tr><td width="20%"><code>smb://users-nyc;miallen:mypass@angus/tmp/</code></td><td> + /// This URL references a share called <code>tmp</code> on the server + /// <code>angus</code> as user <code>miallen</code> who's password is + /// <code>mypass</code>. + /// </td></tr> + /// <tr><td width="20%"> + /// <code>smb://Administrator:P%40ss@msmith1/c/WINDOWS/Desktop/foo.txt</code></td><td> + /// A relativly sophisticated example that references a file + /// <code>msmith1</code>'s desktop as user <code>Administrator</code>. Notice the '@' is URL encoded with the '%40' hexcode escape. + /// </td></tr> + /// <tr><td width="20%"><code>smb://angus/</code></td><td> + /// This references only a server. The behavior of some methods is different + /// in this context(e.g. you cannot <code>delete</code> a server) however + /// as you might expect the <code>list</code> method will list the available + /// shares on this server. + /// </td></tr> + /// <tr><td width="20%"><code>smb://myworkgroup/</code></td><td> + /// This syntactically is identical to the above example. However if + /// <code>myworkgroup</code> happends to be a workgroup(which is indeed + /// suggested by the name) the <code>list</code> method will return + /// a list of servers that have registered themselves as members of + /// <code>myworkgroup</code>. + /// </td></tr> + /// <tr><td width="20%"><code>smb://</code></td><td> + /// Just as <code>smb://server/</code> lists shares and + /// <code>smb://workgroup/</code> lists servers, the <code>smb://</code> + /// URL lists all available workgroups on a netbios LAN. Again, + /// in this context many methods are not valid and return default + /// values(e.g. <code>isHidden</code> will always return false). + /// </td></tr> + /// <tr><td width="20%"><code>smb://angus.foo.net/d/jcifs/pipes.doc</code></td><td> + /// The server name may also be a DNS name as it is in this example. See + /// <a href="../../../resolver.html">Setting Name Resolution Properties</a> + /// for details. + /// </td></tr> + /// <tr><td width="20%"><code>smb://192.168.1.15/ADMIN$/</code></td><td> + /// The server name may also be an IP address. See <a + /// href="../../../resolver.html">Setting Name Resolution Properties</a> + /// for details. + /// </td></tr> + /// <tr><td width="20%"> + /// <code>smb://domain;username:password@server/share/path/to/file.txt</code></td><td> + /// A prototypical example that uses all the fields. + /// </td></tr> + /// <tr><td width="20%"><code>smb://myworkgroup/angus/ <-- ILLEGAL </code></td><td> + /// Despite the hierarchial relationship between workgroups, servers, and + /// filesystems this example is not valid. + /// </td></tr> + /// <tr><td width="20%"> + /// <code>smb://server/share/path/to/dir <-- ILLEGAL </code></td><td> + /// URLs that represent workgroups, servers, shares, or directories require a trailing slash '/'. + /// </td></tr> + /// <tr><td width="20%"> + /// <code>smb://MYGROUP/?SERVER=192.168.10.15</code></td><td> + /// SMB URLs support some query string parameters. In this example + /// the <code>SERVER</code> parameter is used to override the + /// server name service lookup to contact the server 192.168.10.15 + /// (presumably known to be a master + /// browser) for the server list in workgroup <code>MYGROUP</code>. + /// </td></tr> + /// </table> + /// <p>A second constructor argument may be specified to augment the URL + /// for better programmatic control when processing many files under + /// a common base. This is slightly different from the corresponding + /// <code>java.io.File</code> usage; a '/' at the beginning of the second + /// parameter will still use the server component of the first parameter. The + /// examples below illustrate the resulting URLs when this second contructor + /// argument is used. + /// <p><table border="1" cellpadding="3" cellspacing="0" width="100%"> + /// <tr bgcolor="#ccccff"> + /// <td colspan="3"> + /// <b>Examples Of SMB URLs When Augmented With A Second Constructor Parameter</b></td> + /// <tr><td width="20%"> + /// <b>First Parameter</b></td><td><b>Second Parameter</b></td><td><b>Result</b></td></tr> + /// <tr><td width="20%"><code> + /// smb://host/share/a/b/ + /// </code></td><td width="20%"><code> + /// c/d/ + /// </code></td><td><code> + /// smb://host/share/a/b/c/d/ + /// </code></td></tr> + /// <tr><td width="20%"><code> + /// smb://host/share/foo/bar/ + /// </code></td><td width="20%"><code> + /// /share2/zig/zag + /// </code></td><td><code> + /// smb://host/share2/zig/zag + /// </code></td></tr> + /// <tr><td width="20%"><code> + /// smb://host/share/foo/bar/ + /// </code></td><td width="20%"><code> + /// ../zip/ + /// </code></td><td><code> + /// smb://host/share/foo/zip/ + /// </code></td></tr> + /// <tr><td width="20%"><code> + /// smb://host/share/zig/zag + /// </code></td><td width="20%"><code> + /// smb://foo/bar/ + /// </code></td><td><code> + /// smb://foo/bar/ + /// </code></td></tr> + /// <tr><td width="20%"><code> + /// smb://host/share/foo/ + /// </code></td><td width="20%"><code> + /// ../.././.././../foo/ + /// </code></td><td><code> + /// smb://host/foo/ + /// </code></td></tr> + /// <tr><td width="20%"><code> + /// smb://host/share/zig/zag + /// </code></td><td width="20%"><code> + /// / + /// </code></td><td><code> + /// smb://host/ + /// </code></td></tr> + /// <tr><td width="20%"><code> + /// smb://server/ + /// </code></td><td width="20%"><code> + /// ../ + /// </code></td><td><code> + /// smb://server/ + /// </code></td></tr> + /// <tr><td width="20%"><code> + /// smb:// + /// </code></td><td width="20%"><code> + /// myworkgroup/ + /// </code></td><td><code> + /// smb://myworkgroup/ + /// </code></td></tr> + /// <tr><td width="20%"><code> + /// smb://myworkgroup/ + /// </code></td><td width="20%"><code> + /// angus/ + /// </code></td><td><code> + /// smb://myworkgroup/angus/ <-- ILLEGAL<br />(But if you first create an <tt>SmbFile</tt> with 'smb://workgroup/' and use and use it as the first parameter to a constructor that accepts it with a second <tt>String</tt> parameter jCIFS will factor out the 'workgroup'.) + /// </code></td></tr> + /// </table> + /// <p>Instances of the <code>SmbFile</code> class are immutable; that is, + /// once created, the abstract pathname represented by an SmbFile object + /// will never change. + /// </remarks> + /// <seealso cref="FilePath">Sharpen.FilePath</seealso> + public class SmbFile : UrlConnection + { + internal const int ORdonly = 0x01; + + internal const int OWronly = 0x02; + + internal const int ORdwr = 0x03; + + internal const int OAppend = 0x04; + + internal const int OCreat = 0x0010; + + internal const int OExcl = 0x0020; + + internal const int OTrunc = 0x0040; + + /// <summary> + /// When specified as the <tt>shareAccess</tt> constructor parameter, + /// other SMB clients (including other threads making calls into jCIFS) + /// will not be permitted to access the target file and will receive "The + /// file is being accessed by another process" message. + /// </summary> + /// <remarks> + /// When specified as the <tt>shareAccess</tt> constructor parameter, + /// other SMB clients (including other threads making calls into jCIFS) + /// will not be permitted to access the target file and will receive "The + /// file is being accessed by another process" message. + /// </remarks> + public const int FileNoShare = 0x00; + + /// <summary> + /// When specified as the <tt>shareAccess</tt> constructor parameter, + /// other SMB clients will be permitted to read from the target file while + /// this file is open. + /// </summary> + /// <remarks> + /// When specified as the <tt>shareAccess</tt> constructor parameter, + /// other SMB clients will be permitted to read from the target file while + /// this file is open. This constant may be logically OR'd with other share + /// access flags. + /// </remarks> + public const int FileShareRead = 0x01; + + /// <summary> + /// When specified as the <tt>shareAccess</tt> constructor parameter, + /// other SMB clients will be permitted to write to the target file while + /// this file is open. + /// </summary> + /// <remarks> + /// When specified as the <tt>shareAccess</tt> constructor parameter, + /// other SMB clients will be permitted to write to the target file while + /// this file is open. This constant may be logically OR'd with other share + /// access flags. + /// </remarks> + public const int FileShareWrite = 0x02; + + /// <summary> + /// When specified as the <tt>shareAccess</tt> constructor parameter, + /// other SMB clients will be permitted to delete the target file while + /// this file is open. + /// </summary> + /// <remarks> + /// When specified as the <tt>shareAccess</tt> constructor parameter, + /// other SMB clients will be permitted to delete the target file while + /// this file is open. This constant may be logically OR'd with other share + /// access flags. + /// </remarks> + public const int FileShareDelete = 0x04; + + /// <summary> + /// A file with this bit on as returned by <tt>getAttributes()</tt> or set + /// with <tt>setAttributes()</tt> will be read-only + /// </summary> + public const int AttrReadonly = 0x01; + + /// <summary> + /// A file with this bit on as returned by <tt>getAttributes()</tt> or set + /// with <tt>setAttributes()</tt> will be hidden + /// </summary> + public const int AttrHidden = 0x02; + + /// <summary> + /// A file with this bit on as returned by <tt>getAttributes()</tt> or set + /// with <tt>setAttributes()</tt> will be a system file + /// </summary> + public const int AttrSystem = 0x04; + + /// <summary> + /// A file with this bit on as returned by <tt>getAttributes()</tt> is + /// a volume + /// </summary> + public const int AttrVolume = 0x08; + + /// <summary> + /// A file with this bit on as returned by <tt>getAttributes()</tt> is + /// a directory + /// </summary> + public const int AttrDirectory = 0x10; + + /// <summary> + /// A file with this bit on as returned by <tt>getAttributes()</tt> or set + /// with <tt>setAttributes()</tt> is an archived file + /// </summary> + public const int AttrArchive = 0x20; + + internal const int AttrCompressed = 0x800; + + internal const int AttrNormal = 0x080; + + internal const int AttrTemporary = 0x100; + + internal const int AttrGetMask = 0x7FFF; + + internal const int AttrSetMask = 0x30A7; + + internal const int DefaultAttrExpirationPeriod = 5000; + + internal static readonly int HashDot = ".".GetHashCode(); + + internal static readonly int HashDotDot = "..".GetHashCode(); + + //internal static LogStream log = LogStream.GetInstance(); + public LogStream Log + { + get { return LogStream.GetInstance(); } + } + + internal static long AttrExpirationPeriod; + + internal static bool IgnoreCopyToException; + + static SmbFile() + { + // Open Function Encoding + // create if the file does not exist + // fail if the file exists + // truncate if the file exists + // share access + // file attribute encoding + // extended file attribute encoding(others same as above) + /*try + { + Sharpen.Runtime.GetType("jcifs.Config"); + } + catch (TypeLoadException cnfe) + { + Sharpen.Runtime.PrintStackTrace(cnfe); + }*/ + + AttrExpirationPeriod = Config.GetLong("jcifs.smb.client.attrExpirationPeriod", DefaultAttrExpirationPeriod + ); + IgnoreCopyToException = Config.GetBoolean("jcifs.smb.client.ignoreCopyToException" + , true); + Dfs = new Dfs(); + } + + /// <summary> + /// Returned by + /// <see cref="GetType()">GetType()</see> + /// if the resource this <tt>SmbFile</tt> + /// represents is a regular file or directory. + /// </summary> + public const int TypeFilesystem = 0x01; + + /// <summary> + /// Returned by + /// <see cref="GetType()">GetType()</see> + /// if the resource this <tt>SmbFile</tt> + /// represents is a workgroup. + /// </summary> + public const int TypeWorkgroup = 0x02; + + /// <summary> + /// Returned by + /// <see cref="GetType()">GetType()</see> + /// if the resource this <tt>SmbFile</tt> + /// represents is a server. + /// </summary> + public const int TypeServer = 0x04; + + /// <summary> + /// Returned by + /// <see cref="GetType()">GetType()</see> + /// if the resource this <tt>SmbFile</tt> + /// represents is a share. + /// </summary> + public const int TypeShare = 0x08; + + /// <summary> + /// Returned by + /// <see cref="GetType()">GetType()</see> + /// if the resource this <tt>SmbFile</tt> + /// represents is a named pipe. + /// </summary> + public const int TypeNamedPipe = 0x10; + + /// <summary> + /// Returned by + /// <see cref="GetType()">GetType()</see> + /// if the resource this <tt>SmbFile</tt> + /// represents is a printer. + /// </summary> + public const int TypePrinter = 0x20; + + /// <summary> + /// Returned by + /// <see cref="GetType()">GetType()</see> + /// if the resource this <tt>SmbFile</tt> + /// represents is a communications device. + /// </summary> + public const int TypeComm = 0x40; + + private string _canon; + + private string _share; + + private long _createTime; + + private long _lastModified; + + private int _attributes; + + private long _attrExpiration; + + private long _size; + + private long _sizeExpiration; + + private bool _isExists; + + private int _shareAccess = FileShareRead | FileShareWrite | FileShareDelete; + + private bool _enableDfs = Config.GetBoolean("jcifs.smb.client.enabledfs", false); + + private SmbComBlankResponse _blankResp; + + private DfsReferral _dfsReferral; + + protected internal static Dfs Dfs; + + internal NtlmPasswordAuthentication Auth; + + internal SmbTree Tree; + + internal string Unc; + + internal int Fid; + + internal int Type; + + internal bool Opened; + + internal int TreeNum; + + public bool EnableDfs + { + get { return _enableDfs; } + set { _enableDfs = value; } + } + + /// <summary> + /// Constructs an SmbFile representing a resource on an SMB network such as + /// a file or directory. + /// </summary> + /// <remarks> + /// Constructs an SmbFile representing a resource on an SMB network such as + /// a file or directory. See the description and examples of smb URLs above. + /// </remarks> + /// <param name="url">A URL string</param> + /// <exception cref="System.UriFormatException"> + /// If the <code>parent</code> and <code>child</code> parameters + /// do not follow the prescribed syntax + /// </exception> + public SmbFile(string url) + : this(new Uri(url)) + { + } + + /// <summary> + /// Constructs an SmbFile representing a resource on an SMB network such + /// as a file or directory. + /// </summary> + /// <remarks> + /// Constructs an SmbFile representing a resource on an SMB network such + /// as a file or directory. The second parameter is a relative path from + /// the <code>parent SmbFile</code>. See the description above for examples + /// of using the second <code>name</code> parameter. + /// </remarks> + /// <param name="context">A base <code>SmbFile</code></param> + /// <param name="name">A path string relative to the <code>parent</code> paremeter</param> + /// <exception cref="System.UriFormatException"> + /// If the <code>parent</code> and <code>child</code> parameters + /// do not follow the prescribed syntax + /// </exception> + /// <exception cref="UnknownHostException">If the server or workgroup of the <tt>context</tt> file cannot be determined + /// </exception> + public SmbFile(SmbFile context, string name) + : this(context.IsWorkgroup0 + () ? new Uri("smb://" + name) : new Uri(context.Url.AbsoluteUri + name), + context.Auth) + { + + this._enableDfs = context.EnableDfs; + + if (!context.IsWorkgroup0()) + { + Addresses = context.Addresses; + + if (context._share != null) + { + Tree = context.Tree; + _dfsReferral = context._dfsReferral; + } + } + } + + /// <summary> + /// Constructs an SmbFile representing a resource on an SMB network such + /// as a file or directory. + /// </summary> + /// <remarks> + /// Constructs an SmbFile representing a resource on an SMB network such + /// as a file or directory. The second parameter is a relative path from + /// the <code>parent</code>. See the description above for examples of + /// using the second <code>chile</code> parameter. + /// </remarks> + /// <param name="context">A URL string</param> + /// <param name="name">A path string relative to the <code>context</code> paremeter</param> + /// <exception cref="System.UriFormatException"> + /// If the <code>context</code> and <code>name</code> parameters + /// do not follow the prescribed syntax + /// </exception> + /*public SmbFile(string context, string name) + : this(new Uri(new Uri(null, context), name)) + { + }*/ + + public SmbFile(string context, string name) + : this(new Uri(context + name)) + { + + } + + + /// <summary> + /// Constructs an SmbFile representing a resource on an SMB network such + /// as a file or directory. + /// </summary> + /// <remarks> + /// Constructs an SmbFile representing a resource on an SMB network such + /// as a file or directory. + /// </remarks> + /// <param name="url">A URL string</param> + /// <param name="auth">The credentials the client should use for authentication</param> + /// <exception cref="System.UriFormatException">If the <code>url</code> parameter does not follow the prescribed syntax + /// </exception> + public SmbFile(string url, NtlmPasswordAuthentication auth) + : this(new Uri(url, UriKind.RelativeOrAbsolute), + auth) + { + + } + + /// <summary>Constructs an SmbFile representing a file on an SMB network.</summary> + /// <remarks> + /// Constructs an SmbFile representing a file on an SMB network. The + /// <tt>shareAccess</tt> parameter controls what permissions other + /// clients have when trying to access the same file while this instance + /// is still open. This value is either <tt>FILE_NO_SHARE</tt> or any + /// combination of <tt>FILE_SHARE_READ</tt>, <tt>FILE_SHARE_WRITE</tt>, + /// and <tt>FILE_SHARE_DELETE</tt> logically OR'd together. + /// </remarks> + /// <param name="url">A URL string</param> + /// <param name="auth">The credentials the client should use for authentication</param> + /// <param name="shareAccess">Specifies what access other clients have while this file is open. + /// </param> + /// <exception cref="System.UriFormatException">If the <code>url</code> parameter does not follow the prescribed syntax + /// </exception> + public SmbFile(string url, NtlmPasswordAuthentication auth, int shareAccess) + : this + (new Uri(url), auth) + { + // Initially null; set by getUncPath; dir must end with '/' + // Can be null + // For getDfsPath() and getServerWithDfs() + // Cannot be null + // Initially null + // Initially null; set by getUncPath; never ends with '/' + // Initially 0; set by open() + if ((shareAccess & ~(FileShareRead | FileShareWrite | FileShareDelete)) != + 0) + { + throw new RuntimeException("Illegal shareAccess parameter"); + } + this._shareAccess = shareAccess; + } + + /// <summary> + /// Constructs an SmbFile representing a resource on an SMB network such + /// as a file or directory. + /// </summary> + /// <remarks> + /// Constructs an SmbFile representing a resource on an SMB network such + /// as a file or directory. The second parameter is a relative path from + /// the <code>context</code>. See the description above for examples of + /// using the second <code>name</code> parameter. + /// </remarks> + /// <param name="context">A URL string</param> + /// <param name="name">A path string relative to the <code>context</code> paremeter</param> + /// <param name="auth">The credentials the client should use for authentication</param> + /// <exception cref="System.UriFormatException"> + /// If the <code>context</code> and <code>name</code> parameters + /// do not follow the prescribed syntax + /// </exception> + public SmbFile(string context, string name, NtlmPasswordAuthentication auth) + : this + (new Uri(context + name) + , auth) + { + + } + + + /// <summary> + /// Constructs an SmbFile representing a resource on an SMB network such + /// as a file or directory. + /// </summary> + /// <remarks> + /// Constructs an SmbFile representing a resource on an SMB network such + /// as a file or directory. The second parameter is a relative path from + /// the <code>context</code>. See the description above for examples of + /// using the second <code>name</code> parameter. The <tt>shareAccess</tt> + /// parameter controls what permissions other clients have when trying + /// to access the same file while this instance is still open. This + /// value is either <tt>FILE_NO_SHARE</tt> or any combination + /// of <tt>FILE_SHARE_READ</tt>, <tt>FILE_SHARE_WRITE</tt>, and + /// <tt>FILE_SHARE_DELETE</tt> logically OR'd together. + /// </remarks> + /// <param name="context">A URL string</param> + /// <param name="name">A path string relative to the <code>context</code> paremeter</param> + /// <param name="auth">The credentials the client should use for authentication</param> + /// <param name="shareAccess">Specifies what access other clients have while this file is open. + /// </param> + /// <exception cref="System.UriFormatException"> + /// If the <code>context</code> and <code>name</code> parameters + /// do not follow the prescribed syntax + /// </exception> + public SmbFile(string context, string name, NtlmPasswordAuthentication auth, int + shareAccess) + : this(new Uri(context + name), auth) + { + if ((shareAccess & ~(FileShareRead | FileShareWrite | FileShareDelete)) != + 0) + { + throw new RuntimeException("Illegal shareAccess parameter"); + } + this._shareAccess = shareAccess; + } + + /// <summary> + /// Constructs an SmbFile representing a resource on an SMB network such + /// as a file or directory. + /// </summary> + /// <remarks> + /// Constructs an SmbFile representing a resource on an SMB network such + /// as a file or directory. The second parameter is a relative path from + /// the <code>context</code>. See the description above for examples of + /// using the second <code>name</code> parameter. The <tt>shareAccess</tt> + /// parameter controls what permissions other clients have when trying + /// to access the same file while this instance is still open. This + /// value is either <tt>FILE_NO_SHARE</tt> or any combination + /// of <tt>FILE_SHARE_READ</tt>, <tt>FILE_SHARE_WRITE</tt>, and + /// <tt>FILE_SHARE_DELETE</tt> logically OR'd together. + /// </remarks> + /// <param name="context">A base <code>SmbFile</code></param> + /// <param name="name">A path string relative to the <code>context</code> file path</param> + /// <param name="shareAccess">Specifies what access other clients have while this file is open. + /// </param> + /// <exception cref="System.UriFormatException"> + /// If the <code>context</code> and <code>name</code> parameters + /// do not follow the prescribed syntax + /// </exception> + /// <exception cref="UnknownHostException"></exception> + public SmbFile(SmbFile context, string name, int shareAccess) + : this(context.IsWorkgroup0() ? new Uri("smb://" + name) : new Uri( + context.Url.AbsoluteUri + name), context.Auth) + { + if ((shareAccess & ~(FileShareRead | FileShareWrite | FileShareDelete)) != + 0) + { + throw new RuntimeException("Illegal shareAccess parameter"); + } + + if (!context.IsWorkgroup0()) + { + this.Addresses = context.Addresses; + + if (context._share != null || context.Tree != null) + { + Tree = context.Tree; + _dfsReferral = context._dfsReferral; + } + } + + this._shareAccess = shareAccess; + this._enableDfs = context.EnableDfs; + } + + /// <summary> + /// Constructs an SmbFile representing a resource on an SMB network such + /// as a file or directory from a <tt>URL</tt> object. + /// </summary> + /// <remarks> + /// Constructs an SmbFile representing a resource on an SMB network such + /// as a file or directory from a <tt>URL</tt> object. + /// </remarks> + /// <param name="url">The URL of the target resource</param> + protected SmbFile(Uri url) + : this(url, new NtlmPasswordAuthentication(url.GetUserInfo + ())) + { + } + + /// <summary> + /// Constructs an SmbFile representing a resource on an SMB network such + /// as a file or directory from a <tt>URL</tt> object and an + /// <tt>NtlmPasswordAuthentication</tt> object. + /// </summary> + /// <remarks> + /// Constructs an SmbFile representing a resource on an SMB network such + /// as a file or directory from a <tt>URL</tt> object and an + /// <tt>NtlmPasswordAuthentication</tt> object. + /// </remarks> + /// <param name="url">The URL of the target resource</param> + /// <param name="auth">The credentials the client should use for authentication</param> + public SmbFile(Uri url, NtlmPasswordAuthentication auth) + { + this.Auth = auth ?? new NtlmPasswordAuthentication(url.GetUserInfo()); + Url = url; + GetUncPath0(); + } + + /// <exception cref="System.UriFormatException"></exception> + /// <exception cref="UnknownHostException"></exception> + /*internal SmbFile(Jcifs.Smb.SmbFile context, string name, int type, int attributes + , long createTime, long lastModified, long size) + : this(context.IsWorkgroup0() ? + new Uri(null, "smb://" + name + "/") : new Uri(context.url, + name + ((attributes & ATTR_DIRECTORY) > 0 ? "/" : string.Empty)))*/ + internal SmbFile(SmbFile context, string name, int type, int attributes + , long createTime, long lastModified, long size) + : this(context.IsWorkgroup0() ? + new Uri("smb://" + name + "/") : new Uri(context.Url.AbsoluteUri + + name + ((attributes & AttrDirectory) > 0 ? "/" : string.Empty))) + { + Auth = context.Auth; + if (context._share != null) + { + Tree = context.Tree; + _dfsReferral = context._dfsReferral; + } + int last = name.Length - 1; + if (name[last] == '/') + { + name = Runtime.Substring(name, 0, last); + } + if (context._share == null) + { + Unc = "\\"; + } + else + { + if (context.Unc.Equals("\\")) + { + Unc = '\\' + name; + } + else + { + Unc = context.Unc + '\\' + name; + } + } + + if (!context.IsWorkgroup0()) + { + Addresses = context.Addresses; + } + + this._enableDfs = context.EnableDfs; + + this.Type = type; + this._attributes = attributes; + this._createTime = createTime; + this._lastModified = lastModified; + this._size = size; + _isExists = true; + _attrExpiration = _sizeExpiration = Runtime.CurrentTimeMillis() + AttrExpirationPeriod; + } + + private SmbComBlankResponse Blank_resp() + { + if (_blankResp == null) + { + _blankResp = new SmbComBlankResponse(); + } + return _blankResp; + } + + /// <exception cref="SharpCifs.Smb.SmbException"></exception> + internal virtual void ResolveDfs(ServerMessageBlock request) + { + if (!_enableDfs) + { + Connect0(); + return; + } + + if (request is SmbComClose) + { + return; + } + Connect0(); + DfsReferral dr = Dfs.Resolve(Tree.Session.transport.TconHostName, Tree.Share, Unc + , Auth); + if (dr != null) + { + string service = null; + if (request != null) + { + switch (request.Command) + { + case ServerMessageBlock.SmbComTransaction: + case ServerMessageBlock.SmbComTransaction2: + { + switch (((SmbComTransaction)request).SubCommand & 0xFF) + { + case SmbComTransaction.Trans2GetDfsReferral: + { + break; + } + + default: + { + service = "A:"; + break; + } + } + break; + } + + default: + { + service = "A:"; + break; + } + } + } + DfsReferral start = dr; + SmbException se = null; + do + { + try + { + if (Log.Level >= 2) + { + Log.WriteLine("DFS redirect: " + dr); + } + UniAddress addr = UniAddress.GetByName(dr.Server); + SmbTransport trans = SmbTransport.GetSmbTransport(addr, Url.Port); + trans.Connect(); + Tree = trans.GetSmbSession(Auth).GetSmbTree(dr.Share, service); + if (dr != start && dr.Key != null) + { + dr.Map.Put(dr.Key, dr); + } + se = null; + break; + } + catch (IOException ioe) + { + if (ioe is SmbException) + { + se = (SmbException)ioe; + } + else + { + se = new SmbException(dr.Server, ioe); + } + } + dr = dr.Next; + } + while (dr != start); + if (se != null) + { + throw se; + } + if (Log.Level >= 3) + { + Log.WriteLine(dr); + } + _dfsReferral = dr; + if (dr.PathConsumed < 0) + { + dr.PathConsumed = 0; + } + else + { + if (dr.PathConsumed > Unc.Length) + { + dr.PathConsumed = Unc.Length; + } + } + string dunc = Runtime.Substring(Unc, dr.PathConsumed); + if (dunc.Equals(string.Empty)) + { + dunc = "\\"; + } + if (!dr.Path.Equals(string.Empty)) + { + dunc = "\\" + dr.Path + dunc; + } + Unc = dunc; + if (request != null && request.Path != null && request.Path.EndsWith("\\") && dunc + .EndsWith("\\") == false) + { + dunc += "\\"; + } + if (request != null) + { + request.Path = dunc; + request.Flags2 |= SmbConstants.Flags2ResolvePathsInDfs; + } + } + else + { + if (Tree.InDomainDfs && !(request is NtTransQuerySecurityDesc) && !(request is SmbComClose + ) && !(request is SmbComFindClose2)) + { + throw new SmbException(NtStatus.NtStatusNotFound, false); + } + if (request != null) + { + request.Flags2 &= ~SmbConstants.Flags2ResolvePathsInDfs; + } + } + } + + /// <exception cref="SharpCifs.Smb.SmbException"></exception> + internal virtual void Send(ServerMessageBlock request, ServerMessageBlock response + ) + { + for (; ; ) + { + ResolveDfs(request); + try + { + Tree.Send(request, response); + break; + } + catch (DfsReferral dre) + { + if (dre.ResolveHashes) + { + throw; + } + request.Reset(); + } + } + } + + internal static string QueryLookup(string query, string param) + { + char[] instr = query.ToCharArray(); + int i; + int ch; + int st; + int eq; + st = eq = 0; + for (i = 0; i < instr.Length; i++) + { + ch = instr[i]; + if (ch == '&') + { + if (eq > st) + { + string p = new string(instr, st, eq - st); + if (Runtime.EqualsIgnoreCase(p, param)) + { + eq++; + return new string(instr, eq, i - eq); + } + } + st = i + 1; + } + else + { + if (ch == '=') + { + eq = i; + } + } + } + if (eq > st) + { + string p = new string(instr, st, eq - st); + if (Runtime.EqualsIgnoreCase(p, param)) + { + eq++; + return new string(instr, eq, instr.Length - eq); + } + } + return null; + } + + internal UniAddress[] Addresses; + + internal int AddressIndex; + + /// <exception cref="UnknownHostException"></exception> + internal virtual UniAddress GetAddress() + { + if (AddressIndex == 0) + { + return GetFirstAddress(); + } + return Addresses[AddressIndex - 1]; + } + + /// <exception cref="UnknownHostException"></exception> + internal virtual UniAddress GetFirstAddress() + { + AddressIndex = 0; + string host = Url.GetHost(); + string path = Url.AbsolutePath; + string query = Url.GetQuery(); + + if (Addresses != null && Addresses.Length > 0) + { + return GetNextAddress(); + } + + if (query != null) + { + string server = QueryLookup(query, "server"); + if (!string.IsNullOrEmpty(server)) + { + Addresses = new UniAddress[1]; + Addresses[0] = UniAddress.GetByName(server); + return GetNextAddress(); + } + string address = QueryLookup(query, "address"); + if (!string.IsNullOrEmpty(address)) + { + byte[] ip = Extensions.GetAddressByName(address).GetAddressBytes(); + Addresses = new UniAddress[1]; + //addresses[0] = new UniAddress(IPAddress.Parse(host, ip)); + Addresses[0] = new UniAddress(IPAddress.Parse(host)); + return GetNextAddress(); + } + } + if (host.Length == 0) + { + try + { + NbtAddress addr = NbtAddress.GetByName(NbtAddress.MasterBrowserName, 0x01, null); + Addresses = new UniAddress[1]; + Addresses[0] = UniAddress.GetByName(addr.GetHostAddress()); + } + catch (UnknownHostException uhe) + { + NtlmPasswordAuthentication.InitDefaults(); + if (NtlmPasswordAuthentication.DefaultDomain.Equals("?")) + { + throw; + } + Addresses = UniAddress.GetAllByName(NtlmPasswordAuthentication.DefaultDomain, true + ); + } + } + else + { + if (path.Length == 0 || path.Equals("/")) + { + Addresses = UniAddress.GetAllByName(host, true); + } + else + { + Addresses = UniAddress.GetAllByName(host, false); + } + } + return GetNextAddress(); + } + + internal virtual UniAddress GetNextAddress() + { + UniAddress addr = null; + if (AddressIndex < Addresses.Length) + { + addr = Addresses[AddressIndex++]; + } + return addr; + } + + internal virtual bool HasNextAddress() + { + return AddressIndex < Addresses.Length; + } + + /// <exception cref="SharpCifs.Smb.SmbException"></exception> + internal virtual void Connect0() + { + try + { + Connect(); + } + catch (UnknownHostException uhe) + { + throw new SmbException("Failed to connect to server", uhe); + } + catch (SmbException se) + { + throw; + } + catch (IOException ioe) + { + throw new SmbException("Failed to connect to server", ioe); + } + } + + /// <exception cref="System.IO.IOException"></exception> + internal virtual void DoConnect() + { + SmbTransport trans; + UniAddress addr; + addr = GetAddress(); + + if (Tree != null && Tree.Session.transport.Address.Equals(addr)) + { + trans = Tree.Session.transport; + } + else + { + trans = SmbTransport.GetSmbTransport(addr, Url.Port); + Tree = trans.GetSmbSession(Auth).GetSmbTree(_share, null); + } + + + string hostName = GetServerWithDfs(); + if (_enableDfs) + { + Tree.InDomainDfs = Dfs.Resolve(hostName, Tree.Share, null, Auth) != null; + } + if (Tree.InDomainDfs) + { + Tree.ConnectionState = 2; + } + try + { + if (Log.Level >= 3) + { + Log.WriteLine("doConnect: " + addr); + } + Tree.TreeConnect(null, null); + } + catch (SmbAuthException sae) + { + NtlmPasswordAuthentication a; + SmbSession ssn; + if (_share == null) + { + // IPC$ - try "anonymous" credentials + ssn = trans.GetSmbSession(NtlmPasswordAuthentication.Null); + Tree = ssn.GetSmbTree(null, null); + Tree.TreeConnect(null, null); + } + else + { + if ((a = NtlmAuthenticator.RequestNtlmPasswordAuthentication(Url.ToString(), sae) + ) != null) + { + Auth = a; + ssn = trans.GetSmbSession(Auth); + Tree = ssn.GetSmbTree(_share, null); + Tree.InDomainDfs = Dfs.Resolve(hostName, Tree.Share, null, Auth) != null; + if (Tree.InDomainDfs) + { + Tree.ConnectionState = 2; + } + Tree.TreeConnect(null, null); + } + else + { + if (Log.Level >= 1 && HasNextAddress()) + { + Runtime.PrintStackTrace(sae, Log); + } + throw; + } + } + } + } + + /// <summary>It is not necessary to call this method directly.</summary> + /// <remarks> + /// It is not necessary to call this method directly. This is the + /// <tt>URLConnection</tt> implementation of <tt>connect()</tt>. + /// </remarks> + /// <exception cref="System.IO.IOException"></exception> + public void Connect() + { + SmbTransport trans; + SmbSession ssn; + UniAddress addr; + if (IsConnected()) + { + return; + } + GetUncPath0(); + GetFirstAddress(); + for (; ; ) + { + try + { + DoConnect(); + return; + } + catch (SmbAuthException sae) + { + throw; + } + catch (SmbException se) + { + // Prevents account lockout on servers with multiple IPs + if (GetNextAddress() == null) + { + throw; + } + else + { + RemoveCurrentAddress(); + } + + if (Log.Level >= 3) + { + Runtime.PrintStackTrace(se, Log); + } + } + } + } + + internal virtual bool IsConnected() + { + return Tree != null && Tree.ConnectionState == 2; + } + + /// <exception cref="SharpCifs.Smb.SmbException"></exception> + internal virtual int Open0(int flags, int access, int attrs, int options) + { + int f; + Connect0(); + if (Log.Level >= 3) + { + Log.WriteLine("open0: " + Unc); + } + if (Tree.Session.transport.HasCapability(SmbConstants.CapNtSmbs)) + { + SmbComNtCreateAndXResponse response = new SmbComNtCreateAndXResponse(); + SmbComNtCreateAndX request = new SmbComNtCreateAndX(Unc, flags, access, _shareAccess + , attrs, options, null); + if (this is SmbNamedPipe) + { + request.Flags0 |= 0x16; + request.DesiredAccess |= 0x20000; + response.IsExtended = true; + } + Send(request, response); + f = response.Fid; + _attributes = response.ExtFileAttributes & AttrGetMask; + _attrExpiration = Runtime.CurrentTimeMillis() + AttrExpirationPeriod; + _isExists = true; + } + else + { + SmbComOpenAndXResponse response = new SmbComOpenAndXResponse(); + Send(new SmbComOpenAndX(Unc, access, flags, null), response); + f = response.Fid; + } + return f; + } + + /// <exception cref="SharpCifs.Smb.SmbException"></exception> + internal virtual void Open(int flags, int access, int attrs, int options) + { + if (IsOpen()) + { + return; + } + Fid = Open0(flags, access, attrs, options); + Opened = true; + TreeNum = Tree.TreeNum; + } + + internal virtual bool IsOpen() + { + bool ans = Opened && IsConnected() && TreeNum == Tree.TreeNum; + return ans; + } + + /// <exception cref="SharpCifs.Smb.SmbException"></exception> + internal virtual void Close(int f, long lastWriteTime) + { + if (Log.Level >= 3) + { + Log.WriteLine("close: " + f); + } + Send(new SmbComClose(f, lastWriteTime), Blank_resp()); + } + + /// <exception cref="SharpCifs.Smb.SmbException"></exception> + internal virtual void Close(long lastWriteTime) + { + if (IsOpen() == false) + { + return; + } + Close(Fid, lastWriteTime); + Opened = false; + } + + /// <exception cref="SharpCifs.Smb.SmbException"></exception> + internal virtual void Close() + { + Close(0L); + } + + /// <summary> + /// Returns the <tt>NtlmPasswordAuthentication</tt> object used as + /// credentials with this file or pipe. + /// </summary> + /// <remarks> + /// Returns the <tt>NtlmPasswordAuthentication</tt> object used as + /// credentials with this file or pipe. This can be used to retrieve the + /// username for example: + /// <tt> + /// String username = f.getPrincipal().getName(); + /// </tt> + /// The <tt>Principal</tt> object returned will never be <tt>null</tt> + /// however the username can be <tt>null</tt> indication anonymous + /// credentials were used (e.g. some IPC$ services). + /// </remarks> + public virtual Principal GetPrincipal() + { + return Auth; + } + + /// <summary>Returns the last component of the target URL.</summary> + /// <remarks> + /// Returns the last component of the target URL. This will + /// effectively be the name of the file or directory represented by this + /// <code>SmbFile</code> or in the case of URLs that only specify a server + /// or workgroup, the server or workgroup will be returned. The name of + /// the root URL <code>smb://</code> is also <code>smb://</code>. If this + /// <tt>SmbFile</tt> refers to a workgroup, server, share, or directory, + /// the name will include a trailing slash '/' so that composing new + /// <tt>SmbFile</tt>s will maintain the trailing slash requirement. + /// </remarks> + /// <returns> + /// The last component of the URL associated with this SMB + /// resource or <code>smb://</code> if the resource is <code>smb://</code> + /// itself. + /// </returns> + public virtual string GetName() + { + GetUncPath0(); + if (_canon.Length > 1) + { + int i = _canon.Length - 2; + while (_canon[i] != '/') + { + i--; + } + return Runtime.Substring(_canon, i + 1); + } + if (_share != null) + { + return _share + '/'; + } + if (Url.GetHost().Length > 0) + { + return Url.GetHost() + '/'; + } + return "smb://"; + } + + /// <summary> + /// Everything but the last component of the URL representing this SMB + /// resource is effectivly it's parent. + /// </summary> + /// <remarks> + /// Everything but the last component of the URL representing this SMB + /// resource is effectivly it's parent. The root URL <code>smb://</code> + /// does not have a parent. In this case <code>smb://</code> is returned. + /// </remarks> + /// <returns> + /// The parent directory of this SMB resource or + /// <code>smb://</code> if the resource refers to the root of the URL + /// hierarchy which incedentally is also <code>smb://</code>. + /// </returns> + public virtual string GetParent() + { + string str = Url.Authority; + if (str.Length > 0) + { + StringBuilder sb = new StringBuilder("smb://"); + sb.Append(str); + GetUncPath0(); + if (_canon.Length > 1) + { + sb.Append(_canon); + } + else + { + sb.Append('/'); + } + str = sb.ToString(); + int i = str.Length - 2; + while (str[i] != '/') + { + i--; + } + return Runtime.Substring(str, 0, i + 1); + } + return "smb://"; + } + + /// <summary>Returns the full uncanonicalized URL of this SMB resource.</summary> + /// <remarks> + /// Returns the full uncanonicalized URL of this SMB resource. An + /// <code>SmbFile</code> constructed with the result of this method will + /// result in an <code>SmbFile</code> that is equal to the original. + /// </remarks> + /// <returns>The uncanonicalized full URL of this SMB resource.</returns> + public virtual string GetPath() + { + return Url.ToString(); + } + + internal virtual string GetUncPath0() + { + if (Unc == null) + { + char[] instr = Url.LocalPath.ToCharArray(); + char[] outstr = new char[instr.Length]; + int length = instr.Length; + int i; + int o; + int state; + + state = 0; + o = 0; + for (i = 0; i < length; i++) + { + switch (state) + { + case 0: + { + if (instr[i] != '/') + { + return null; + } + outstr[o++] = instr[i]; + state = 1; + break; + } + + case 1: + { + if (instr[i] == '/') + { + break; + } + if (instr[i] == '.' && ((i + 1) >= length || instr[i + 1] == '/')) + { + i++; + break; + } + if ((i + 1) < length && instr[i] == '.' && instr[i + 1] == '.' && ((i + 2) >= length + || instr[i + 2] == '/')) + { + i += 2; + if (o == 1) + { + break; + } + do + { + o--; + } + while (o > 1 && outstr[o - 1] != '/'); + break; + } + state = 2; + goto case 2; + } + + case 2: + { + if (instr[i] == '/') + { + state = 1; + } + outstr[o++] = instr[i]; + break; + } + } + } + _canon = new string(outstr, 0, o); + if (o > 1) + { + o--; + i = _canon.IndexOf('/', 1); + if (i < 0) + { + _share = Runtime.Substring(_canon, 1); + Unc = "\\"; + } + else + { + if (i == o) + { + _share = Runtime.Substring(_canon, 1, i); + Unc = "\\"; + } + else + { + _share = Runtime.Substring(_canon, 1, i); + Unc = Runtime.Substring(_canon, i, outstr[o] == '/' ? o : o + 1); + Unc = Unc.Replace('/', '\\'); + } + } + } + else + { + _share = null; + Unc = "\\"; + } + } + return Unc; + } + + /// <summary>Retuns the Windows UNC style path with backslashs intead of forward slashes. + /// </summary> + /// <remarks>Retuns the Windows UNC style path with backslashs intead of forward slashes. + /// </remarks> + /// <returns>The UNC path.</returns> + public virtual string GetUncPath() + { + GetUncPath0(); + if (_share == null) + { + return "\\\\" + Url.GetHost(); + } + return "\\\\" + Url.GetHost() + _canon.Replace('/', '\\'); + } + + /// <summary> + /// Returns the full URL of this SMB resource with '.' and '..' components + /// factored out. + /// </summary> + /// <remarks> + /// Returns the full URL of this SMB resource with '.' and '..' components + /// factored out. An <code>SmbFile</code> constructed with the result of + /// this method will result in an <code>SmbFile</code> that is equal to + /// the original. + /// </remarks> + /// <returns>The canonicalized URL of this SMB resource.</returns> + public virtual string GetCanonicalPath() + { + string str = Url.Authority; + GetUncPath0(); + if (str.Length > 0) + { + return "smb://" + Url.Authority + _canon; + } + return "smb://"; + } + + /// <summary>Retrieves the share associated with this SMB resource.</summary> + /// <remarks> + /// Retrieves the share associated with this SMB resource. In + /// the case of <code>smb://</code>, <code>smb://workgroup/</code>, + /// and <code>smb://server/</code> URLs which do not specify a share, + /// <code>null</code> will be returned. + /// </remarks> + /// <returns>The share component or <code>null</code> if there is no share</returns> + public virtual string GetShare() + { + return _share; + } + + internal virtual string GetServerWithDfs() + { + if (_dfsReferral != null) + { + return _dfsReferral.Server; + } + return GetServer(); + } + + /// <summary>Retrieve the hostname of the server for this SMB resource.</summary> + /// <remarks> + /// Retrieve the hostname of the server for this SMB resource. If this + /// <code>SmbFile</code> references a workgroup, the name of the workgroup + /// is returned. If this <code>SmbFile</code> refers to the root of this + /// SMB network hierarchy, <code>null</code> is returned. + /// </remarks> + /// <returns> + /// The server or workgroup name or <code>null</code> if this + /// <code>SmbFile</code> refers to the root <code>smb://</code> resource. + /// </returns> + public virtual string GetServer() + { + string str = Url.GetHost(); + if (str.Length == 0) + { + return null; + } + return str; + } + + /// <summary>Returns type of of object this <tt>SmbFile</tt> represents.</summary> + /// <remarks>Returns type of of object this <tt>SmbFile</tt> represents.</remarks> + /// <returns> + /// <tt>TYPE_FILESYSTEM, TYPE_WORKGROUP, TYPE_SERVER, TYPE_SHARE, + /// TYPE_PRINTER, TYPE_NAMED_PIPE</tt>, or <tt>TYPE_COMM</tt>. + /// </returns> + /// <exception cref="SharpCifs.Smb.SmbException"></exception> + public new virtual int GetType() + { + if (Type == 0) + { + if (GetUncPath0().Length > 1) + { + Type = TypeFilesystem; + } + else + { + if (_share != null) + { + // treeConnect good enough to test service type + Connect0(); + if (_share.Equals("IPC$")) + { + Type = TypeNamedPipe; + } + else + { + if (Tree.Service.Equals("LPT1:")) + { + Type = TypePrinter; + } + else + { + if (Tree.Service.Equals("COMM")) + { + Type = TypeComm; + } + else + { + Type = TypeShare; + } + } + } + } + else + { + if (string.IsNullOrEmpty(Url.Authority)) + { + Type = TypeWorkgroup; + } + else + { + UniAddress addr; + try + { + addr = GetAddress(); + } + catch (UnknownHostException uhe) + { + throw new SmbException(Url.ToString(), uhe); + } + if (addr.GetAddress() is NbtAddress) + { + int code = ((NbtAddress)addr.GetAddress()).GetNameType(); + if (code == 0x1d || code == 0x1b) + { + Type = TypeWorkgroup; + return Type; + } + } + Type = TypeServer; + } + } + } + } + return Type; + } + + /// <exception cref="UnknownHostException"></exception> + internal virtual bool IsWorkgroup0() + { + if (Type == TypeWorkgroup || Url.GetHost().Length == 0) + { + Type = TypeWorkgroup; + return true; + } + GetUncPath0(); + if (_share == null) + { + UniAddress addr = GetAddress(); + if (addr.GetAddress() is NbtAddress) + { + int code = ((NbtAddress)addr.GetAddress()).GetNameType(); + if (code == 0x1d || code == 0x1b) + { + Type = TypeWorkgroup; + return true; + } + } + Type = TypeServer; + } + return false; + } + + /// <exception cref="SharpCifs.Smb.SmbException"></exception> + internal virtual IInfo QueryPath(string path, int infoLevel) + { + Connect0(); + if (Log.Level >= 3) + { + Log.WriteLine("queryPath: " + path); + } + if (Tree.Session.transport.HasCapability(SmbConstants.CapNtSmbs)) + { + Trans2QueryPathInformationResponse response = new Trans2QueryPathInformationResponse + (infoLevel); + Send(new Trans2QueryPathInformation(path, infoLevel), response); + return response.Info; + } + else + { + SmbComQueryInformationResponse response = new SmbComQueryInformationResponse(Tree + .Session.transport.Server.ServerTimeZone * 1000 * 60L); + Send(new SmbComQueryInformation(path), response); + return response; + } + } + + /// <summary>Tests to see if the SMB resource exists.</summary> + /// <remarks> + /// Tests to see if the SMB resource exists. If the resource refers + /// only to a server, this method determines if the server exists on the + /// network and is advertising SMB services. If this resource refers to + /// a workgroup, this method determines if the workgroup name is valid on + /// the local SMB network. If this <code>SmbFile</code> refers to the root + /// <code>smb://</code> resource <code>true</code> is always returned. If + /// this <code>SmbFile</code> is a traditional file or directory, it will + /// be queried for on the specified server as expected. + /// </remarks> + /// <returns> + /// <code>true</code> if the resource exists or is alive or + /// <code>false</code> otherwise + /// </returns> + /// <exception cref="SharpCifs.Smb.SmbException"></exception> + public virtual bool Exists() + { + if (_attrExpiration > Runtime.CurrentTimeMillis()) + { + return _isExists; + } + _attributes = AttrReadonly | AttrDirectory; + _createTime = 0L; + _lastModified = 0L; + _isExists = false; + try + { + if (Url.GetHost().Length == 0) + { + } + else + { + if (_share == null) + { + if (GetType() == TypeWorkgroup) + { + UniAddress.GetByName(Url.GetHost(), true); + } + else + { + UniAddress.GetByName(Url.GetHost()).GetHostName(); + } + } + else + { + if (GetUncPath0().Length == 1 || Runtime.EqualsIgnoreCase(_share, "IPC$")) + { + Connect0(); + } + else + { + // treeConnect is good enough + IInfo info = QueryPath(GetUncPath0(), Trans2QueryPathInformationResponse.SMB_QUERY_FILE_BASIC_INFO + ); + _attributes = info.GetAttributes(); + _createTime = info.GetCreateTime(); + _lastModified = info.GetLastWriteTime(); + } + } + } + _isExists = true; + } + catch (UnknownHostException) + { + } + catch (SmbException se) + { + switch (se.GetNtStatus()) + { + case NtStatus.NtStatusNoSuchFile: + case NtStatus.NtStatusObjectNameInvalid: + case NtStatus.NtStatusObjectNameNotFound: + case NtStatus.NtStatusObjectPathNotFound: + { + break; + } + + default: + { + throw; + } + } + } + _attrExpiration = Runtime.CurrentTimeMillis() + AttrExpirationPeriod; + return _isExists; + } + + /// <summary> + /// Tests to see if the file this <code>SmbFile</code> represents can be + /// read. + /// </summary> + /// <remarks> + /// Tests to see if the file this <code>SmbFile</code> represents can be + /// read. Because any file, directory, or other resource can be read if it + /// exists, this method simply calls the <code>exists</code> method. + /// </remarks> + /// <returns><code>true</code> if the file is read-only</returns> + /// <exception cref="SharpCifs.Smb.SmbException"></exception> + public virtual bool CanRead() + { + if (GetType() == TypeNamedPipe) + { + // try opening the pipe for reading? + return true; + } + return Exists(); + } + + // try opening and catch sharing violation? + /// <summary> + /// Tests to see if the file this <code>SmbFile</code> represents + /// exists and is not marked read-only. + /// </summary> + /// <remarks> + /// Tests to see if the file this <code>SmbFile</code> represents + /// exists and is not marked read-only. By default, resources are + /// considered to be read-only and therefore for <code>smb://</code>, + /// <code>smb://workgroup/</code>, and <code>smb://server/</code> resources + /// will be read-only. + /// </remarks> + /// <returns> + /// <code>true</code> if the resource exists is not marked + /// read-only + /// </returns> + /// <exception cref="SharpCifs.Smb.SmbException"></exception> + public virtual bool CanWrite() + { + if (GetType() == TypeNamedPipe) + { + // try opening the pipe for writing? + return true; + } + return Exists() && (_attributes & AttrReadonly) == 0; + } + + /// <summary>Tests to see if the file this <code>SmbFile</code> represents is a directory. + /// </summary> + /// <remarks>Tests to see if the file this <code>SmbFile</code> represents is a directory. + /// </remarks> + /// <returns><code>true</code> if this <code>SmbFile</code> is a directory</returns> + /// <exception cref="SharpCifs.Smb.SmbException"></exception> + public virtual bool IsDirectory() + { + if (GetUncPath0().Length == 1) + { + return true; + } + if (!Exists()) + { + return false; + } + return (_attributes & AttrDirectory) == AttrDirectory; + } + + /// <summary>Tests to see if the file this <code>SmbFile</code> represents is not a directory. + /// </summary> + /// <remarks>Tests to see if the file this <code>SmbFile</code> represents is not a directory. + /// </remarks> + /// <returns><code>true</code> if this <code>SmbFile</code> is not a directory</returns> + /// <exception cref="SharpCifs.Smb.SmbException"></exception> + public virtual bool IsFile() + { + if (GetUncPath0().Length == 1) + { + return false; + } + Exists(); + return (_attributes & AttrDirectory) == 0; + } + + /// <summary> + /// Tests to see if the file this SmbFile represents is marked as + /// hidden. + /// </summary> + /// <remarks> + /// Tests to see if the file this SmbFile represents is marked as + /// hidden. This method will also return true for shares with names that + /// end with '$' such as <code>IPC$</code> or <code>C$</code>. + /// </remarks> + /// <returns><code>true</code> if the <code>SmbFile</code> is marked as being hidden</returns> + /// <exception cref="SharpCifs.Smb.SmbException"></exception> + public virtual bool IsHidden() + { + if (_share == null) + { + return false; + } + if (GetUncPath0().Length == 1) + { + if (_share.EndsWith("$")) + { + return true; + } + return false; + } + Exists(); + return (_attributes & AttrHidden) == AttrHidden; + } + + /// <summary> + /// If the path of this <code>SmbFile</code> falls within a DFS volume, + /// this method will return the referral path to which it maps. + /// </summary> + /// <remarks> + /// If the path of this <code>SmbFile</code> falls within a DFS volume, + /// this method will return the referral path to which it maps. Otherwise + /// <code>null</code> is returned. + /// </remarks> + /// <exception cref="SharpCifs.Smb.SmbException"></exception> + public virtual string GetDfsPath() + { + ResolveDfs(null); + if (_dfsReferral == null) + { + return null; + } + string path = "smb:/" + _dfsReferral.Server + "/" + _dfsReferral.Share + Unc; + path = path.Replace('\\', '/'); + if (IsDirectory()) + { + path += '/'; + } + return path; + } + + /// <summary>Retrieve the time this <code>SmbFile</code> was created.</summary> + /// <remarks> + /// Retrieve the time this <code>SmbFile</code> was created. The value + /// returned is suitable for constructing a + /// <see cref="System.DateTime">System.DateTime</see> + /// object + /// (i.e. seconds since Epoch 1970). Times should be the same as those + /// reported using the properties dialog of the Windows Explorer program. + /// For Win95/98/Me this is actually the last write time. It is currently + /// not possible to retrieve the create time from files on these systems. + /// </remarks> + /// <returns> + /// The number of milliseconds since the 00:00:00 GMT, January 1, + /// 1970 as a <code>long</code> value + /// </returns> + /// <exception cref="SharpCifs.Smb.SmbException"></exception> + public virtual long CreateTime() + { + if (GetUncPath0().Length > 1) + { + Exists(); + return _createTime; + } + return 0L; + } + + /// <summary> + /// Retrieve the last time the file represented by this + /// <code>SmbFile</code> was modified. + /// </summary> + /// <remarks> + /// Retrieve the last time the file represented by this + /// <code>SmbFile</code> was modified. The value returned is suitable for + /// constructing a + /// <see cref="System.DateTime">System.DateTime</see> + /// object (i.e. seconds since Epoch + /// 1970). Times should be the same as those reported using the properties + /// dialog of the Windows Explorer program. + /// </remarks> + /// <returns> + /// The number of milliseconds since the 00:00:00 GMT, January 1, + /// 1970 as a <code>long</code> value + /// </returns> + /// <exception cref="SharpCifs.Smb.SmbException"></exception> + public virtual long LastModified() + { + if (GetUncPath0().Length > 1) + { + Exists(); + return _lastModified; + } + return 0L; + } + + /// <summary>List the contents of this SMB resource.</summary> + /// <remarks> + /// List the contents of this SMB resource. The list returned by this + /// method will be; + /// <ul> + /// <li> files and directories contained within this resource if the + /// resource is a normal disk file directory, + /// <li> all available NetBIOS workgroups or domains if this resource is + /// the top level URL <code>smb://</code>, + /// <li> all servers registered as members of a NetBIOS workgroup if this + /// resource refers to a workgroup in a <code>smb://workgroup/</code> URL, + /// <li> all browseable shares of a server including printers, IPC + /// services, or disk volumes if this resource is a server URL in the form + /// <code>smb://server/</code>, + /// <li> or <code>null</code> if the resource cannot be resolved. + /// </ul> + /// </remarks> + /// <returns> + /// A <code>String[]</code> array of files and directories, + /// workgroups, servers, or shares depending on the context of the + /// resource URL + /// </returns> + /// <exception cref="SmbException"></exception> + public virtual string[] List() + { + return List("*", AttrDirectory | AttrHidden | AttrSystem, null, null); + } + + /// <summary>List the contents of this SMB resource.</summary> + /// <remarks> + /// List the contents of this SMB resource. The list returned will be + /// identical to the list returned by the parameterless <code>list()</code> + /// method minus filenames filtered by the specified filter. + /// </remarks> + /// <param name="filter">a filename filter to exclude filenames from the results</param> + /// <exception cref="SmbException"># @return An array of filenames</exception> + /// <exception cref="SharpCifs.Smb.SmbException"></exception> + public virtual string[] List(ISmbFilenameFilter filter) + { + return List("*", AttrDirectory | AttrHidden | AttrSystem, filter, null); + } + + /// <summary> + /// List the contents of this SMB resource as an array of + /// <code>SmbFile</code> objects. + /// </summary> + /// <remarks> + /// List the contents of this SMB resource as an array of + /// <code>SmbFile</code> objects. This method is much more efficient than + /// the regular <code>list</code> method when querying attributes of each + /// file in the result set. + /// <p> + /// The list of <code>SmbFile</code>s returned by this method will be; + /// <ul> + /// <li> files and directories contained within this resource if the + /// resource is a normal disk file directory, + /// <li> all available NetBIOS workgroups or domains if this resource is + /// the top level URL <code>smb://</code>, + /// <li> all servers registered as members of a NetBIOS workgroup if this + /// resource refers to a workgroup in a <code>smb://workgroup/</code> URL, + /// <li> all browseable shares of a server including printers, IPC + /// services, or disk volumes if this resource is a server URL in the form + /// <code>smb://server/</code>, + /// <li> or <code>null</code> if the resource cannot be resolved. + /// </ul> + /// </remarks> + /// <returns> + /// An array of <code>SmbFile</code> objects representing file + /// and directories, workgroups, servers, or shares depending on the context + /// of the resource URL + /// </returns> + /// <exception cref="SmbException"></exception> + public virtual SmbFile[] ListFiles() + { + return ListFiles("*", AttrDirectory | AttrHidden | AttrSystem, null, null); + } + + /// <summary> + /// The CIFS protocol provides for DOS "wildcards" to be used as + /// a performance enhancement. + /// </summary> + /// <remarks> + /// The CIFS protocol provides for DOS "wildcards" to be used as + /// a performance enhancement. The client does not have to filter + /// the names and the server does not have to return all directory + /// entries. + /// <p> + /// The wildcard expression may consist of two special meta + /// characters in addition to the normal filename characters. The '*' + /// character matches any number of characters in part of a name. If + /// the expression begins with one or more '?'s then exactly that + /// many characters will be matched whereas if it ends with '?'s + /// it will match that many characters <i>or less</i>. + /// <p> + /// Wildcard expressions will not filter workgroup names or server names. + /// <blockquote><pre> + /// winnt> ls c?o + /// clock.avi -rw-- 82944 Mon Oct 14 1996 1:38 AM + /// Cookies drw-- 0 Fri Nov 13 1998 9:42 PM + /// 2 items in 5ms + /// </pre></blockquote> + /// </remarks> + /// <param name="wildcard">a wildcard expression</param> + /// <exception cref="SmbException">SmbException</exception> + /// <returns> + /// An array of <code>SmbFile</code> objects representing file + /// and directories, workgroups, servers, or shares depending on the context + /// of the resource URL + /// </returns> + /// <exception cref="SmbException"></exception> + public virtual SmbFile[] ListFiles(string wildcard) + { + return ListFiles(wildcard, AttrDirectory | AttrHidden | AttrSystem, null, null + ); + } + + /// <summary>List the contents of this SMB resource.</summary> + /// <remarks> + /// List the contents of this SMB resource. The list returned will be + /// identical to the list returned by the parameterless <code>listFiles()</code> + /// method minus files filtered by the specified filename filter. + /// </remarks> + /// <param name="filter">a filter to exclude files from the results</param> + /// <returns>An array of <tt>SmbFile</tt> objects</returns> + /// <exception cref="SmbException">SmbException</exception> + /// <exception cref="SharpCifs.Smb.SmbException"></exception> + public virtual SmbFile[] ListFiles(ISmbFilenameFilter filter) + { + return ListFiles("*", AttrDirectory | AttrHidden | AttrSystem, filter, null); + } + + /// <summary>List the contents of this SMB resource.</summary> + /// <remarks> + /// List the contents of this SMB resource. The list returned will be + /// identical to the list returned by the parameterless <code>listFiles()</code> + /// method minus filenames filtered by the specified filter. + /// </remarks> + /// <param name="filter">a file filter to exclude files from the results</param> + /// <returns>An array of <tt>SmbFile</tt> objects</returns> + /// <exception cref="SharpCifs.Smb.SmbException"></exception> + public virtual SmbFile[] ListFiles(ISmbFileFilter filter) + { + return ListFiles("*", AttrDirectory | AttrHidden | AttrSystem, null, filter); + } + + /// <exception cref="SharpCifs.Smb.SmbException"></exception> + internal virtual string[] List(string wildcard, int searchAttributes, ISmbFilenameFilter + fnf, ISmbFileFilter ff) + { + List<object> list = new List<object>(); + DoEnum(list, false, wildcard, searchAttributes, fnf, ff); + + return Collections.ToArray<string>(list); //Collections.ToArray<string>(list); + } + + /// <exception cref="SharpCifs.Smb.SmbException"></exception> + internal virtual SmbFile[] ListFiles(string wildcard, int searchAttributes + , ISmbFilenameFilter fnf, ISmbFileFilter ff) + { + List<object> list = new List<object>(); + DoEnum(list, true, wildcard, searchAttributes, fnf, ff); + + return Collections.ToArray<SmbFile>(list); //Collections.ToArray<SmbFile>(list); + } + + /// <exception cref="SharpCifs.Smb.SmbException"></exception> + internal virtual void DoEnum(List<object> list, bool files, string wildcard, int searchAttributes + , ISmbFilenameFilter fnf, ISmbFileFilter ff) + { + if (ff != null && ff is DosFileFilter) + { + DosFileFilter dff = (DosFileFilter)ff; + if (dff.Wildcard != null) + { + wildcard = dff.Wildcard; + } + searchAttributes = dff.Attributes; + } + try + { + int hostlen = Url.GetHost() != null ? Url.GetHost().Length : 0; + if (hostlen == 0 || GetType() == TypeWorkgroup) + { + DoNetServerEnum(list, files, wildcard, searchAttributes, fnf, ff); + } + else + { + if (_share == null) + { + DoShareEnum(list, files, wildcard, searchAttributes, fnf, ff); + } + else + { + DoFindFirstNext(list, files, wildcard, searchAttributes, fnf, ff); + } + } + } + catch (UnknownHostException uhe) + { + throw new SmbException(Url.ToString(), uhe); + } + catch (UriFormatException mue) + { + throw new SmbException(Url.ToString(), mue); + } + } + + private void RemoveCurrentAddress() + { + if (AddressIndex >= 1) + { + UniAddress[] aux = new UniAddress[Addresses.Length - 1]; + + Array.Copy(Addresses, 1, aux, 0, Addresses.Length - 1); + + Addresses = aux; + AddressIndex--; + } + } + + /// <exception cref="SharpCifs.Smb.SmbException"></exception> + /// <exception cref="UnknownHostException"></exception> + /// <exception cref="System.UriFormatException"></exception> + internal virtual void DoShareEnum(List<object> list, bool files, string wildcard, int + searchAttributes, ISmbFilenameFilter fnf, ISmbFileFilter ff) + { + string p = Url.AbsolutePath; + IOException last = null; + IFileEntry[] entries; + UniAddress addr; + IFileEntry e; + Hashtable map; + if (p.LastIndexOf('/') != (p.Length - 1)) + { + throw new SmbException(Url + " directory must end with '/'"); + } + if (GetType() != TypeServer) + { + throw new SmbException("The requested list operations is invalid: " + Url); + } + map = new Hashtable(); + if (_enableDfs && Dfs.IsTrustedDomain(GetServer(), Auth)) + { + try + { + entries = DoDfsRootEnum(); + for (int ei = 0; ei < entries.Length; ei++) + { + e = entries[ei]; + if (map.ContainsKey(e) == false) + { + map.Put(e, e); + } + } + } + catch (IOException ioe) + { + if (Log.Level >= 4) + { + Runtime.PrintStackTrace(ioe, Log); + } + } + } + addr = GetFirstAddress(); + while (addr != null) + { + try + { + last = null; + + DoConnect(); + try + { + entries = DoMsrpcShareEnum(); + } + catch (IOException ioe) + { + if (Log.Level >= 3) + { + Runtime.PrintStackTrace(ioe, Log); + } + entries = DoNetShareEnum(); + } + for (int ei = 0; ei < entries.Length; ei++) + { + e = entries[ei]; + if (map.ContainsKey(e) == false) + { + map.Put(e, e); + } + } + break; + } + catch (IOException ioe) + { + if (Log.Level >= 3) + { + Runtime.PrintStackTrace(ioe, Log); + } + last = ioe; + + if (!(ioe is SmbAuthException)) + { + RemoveCurrentAddress(); + + addr = GetNextAddress(); + } + else + { + break; + } + } + + } + if (last != null && map.Count == 0) + { + if (last is SmbException == false) + { + throw new SmbException(Url.ToString(), last); + } + throw (SmbException)last; + } + //Iterator iter = map.Keys.Iterator(); + //while (iter.HasNext()) + foreach (var item in map.Keys) + { + e = (IFileEntry)item; + string name = e.GetName(); + if (fnf != null && fnf.Accept(this, name) == false) + { + continue; + } + if (name.Length > 0) + { + // if !files we don't need to create SmbFiles here + SmbFile f = new SmbFile(this, name, e.GetType(), AttrReadonly + | AttrDirectory, 0L, 0L, 0L); + if (ff != null && ff.Accept(f) == false) + { + continue; + } + if (files) + { + list.Add(f); + } + else + { + list.Add(name); + } + } + } + } + + /// <exception cref="System.IO.IOException"></exception> + internal virtual IFileEntry[] DoDfsRootEnum() + { + MsrpcDfsRootEnum rpc; + DcerpcHandle handle = null; + IFileEntry[] entries; + handle = DcerpcHandle.GetHandle("ncacn_np:" + GetAddress().GetHostAddress() + "[\\PIPE\\netdfs]" + , Auth); + try + { + rpc = new MsrpcDfsRootEnum(GetServer()); + handle.Sendrecv(rpc); + if (rpc.Retval != 0) + { + throw new SmbException(rpc.Retval, true); + } + return rpc.GetEntries(); + } + finally + { + try + { + handle.Close(); + } + catch (IOException ioe) + { + if (Log.Level >= 4) + { + Runtime.PrintStackTrace(ioe, Log); + } + } + } + } + + /// <exception cref="System.IO.IOException"></exception> + internal virtual IFileEntry[] DoMsrpcShareEnum() + { + MsrpcShareEnum rpc; + DcerpcHandle handle; + rpc = new MsrpcShareEnum(Url.GetHost()); + handle = DcerpcHandle.GetHandle("ncacn_np:" + GetAddress().GetHostAddress() + "[\\PIPE\\srvsvc]" + , Auth); + try + { + handle.Sendrecv(rpc); + if (rpc.Retval != 0) + { + throw new SmbException(rpc.Retval, true); + } + return rpc.GetEntries(); + } + finally + { + try + { + handle.Close(); + } + catch (IOException ioe) + { + if (Log.Level >= 4) + { + Runtime.PrintStackTrace(ioe, Log); + } + } + } + } + + /// <exception cref="SharpCifs.Smb.SmbException"></exception> + internal virtual IFileEntry[] DoNetShareEnum() + { + SmbComTransaction req = new NetShareEnum(); + SmbComTransactionResponse resp = new NetShareEnumResponse(); + Send(req, resp); + if (resp.Status != WinError.ErrorSuccess) + { + throw new SmbException(resp.Status, true); + } + return resp.Results; + } + + /// <exception cref="SharpCifs.Smb.SmbException"></exception> + /// <exception cref="UnknownHostException"></exception> + /// <exception cref="System.UriFormatException"></exception> + internal virtual void DoNetServerEnum(List<object> list, bool files, string wildcard + , int searchAttributes, ISmbFilenameFilter fnf, ISmbFileFilter ff) + { + int listType = Url.GetHost().Length == 0 ? 0 : GetType(); + SmbComTransaction req; + SmbComTransactionResponse resp; + if (listType == 0) + { + Connect0(); + req = new NetServerEnum2(Tree.Session.transport.Server.OemDomainName, NetServerEnum2 + .SvTypeDomainEnum); + resp = new NetServerEnum2Response(); + } + else + { + if (listType == TypeWorkgroup) + { + req = new NetServerEnum2(Url.GetHost(), NetServerEnum2.SvTypeAll); + resp = new NetServerEnum2Response(); + } + else + { + throw new SmbException("The requested list operations is invalid: " + Url); + } + } + bool more; + do + { + int n; + Send(req, resp); + if (resp.Status != WinError.ErrorSuccess && resp.Status != WinError.ErrorMoreData) + { + throw new SmbException(resp.Status, true); + } + more = resp.Status == WinError.ErrorMoreData; + n = more ? resp.NumEntries - 1 : resp.NumEntries; + for (int i = 0; i < n; i++) + { + IFileEntry e = resp.Results[i]; + string name = e.GetName(); + if (fnf != null && fnf.Accept(this, name) == false) + { + continue; + } + if (name.Length > 0) + { + // if !files we don't need to create SmbFiles here + SmbFile f = new SmbFile(this, name, e.GetType(), AttrReadonly + | AttrDirectory, 0L, 0L, 0L); + if (ff != null && ff.Accept(f) == false) + { + continue; + } + if (files) + { + list.Add(f); + } + else + { + list.Add(name); + } + } + } + if (GetType() != TypeWorkgroup) + { + break; + } + req.SubCommand = unchecked(SmbComTransaction.NetServerEnum3); + req.Reset(0, ((NetServerEnum2Response)resp).LastName); + resp.Reset(); + } + while (more); + } + + /// <exception cref="SharpCifs.Smb.SmbException"></exception> + /// <exception cref="UnknownHostException"></exception> + /// <exception cref="System.UriFormatException"></exception> + internal virtual void DoFindFirstNext(List<object> list, bool files, string wildcard + , int searchAttributes, ISmbFilenameFilter fnf, ISmbFileFilter ff) + { + SmbComTransaction req; + Trans2FindFirst2Response resp; + int sid; + string path = GetUncPath0(); + string p = Url.AbsolutePath; + if (p.LastIndexOf('/') != (p.Length - 1)) + { + throw new SmbException(Url + " directory must end with '/'"); + } + req = new Trans2FindFirst2(path, wildcard, searchAttributes); + resp = new Trans2FindFirst2Response(); + if (Log.Level >= 3) + { + Log.WriteLine("doFindFirstNext: " + req.Path); + } + Send(req, resp); + sid = resp.Sid; + req = new Trans2FindNext2(sid, resp.ResumeKey, resp.LastName); + resp.SubCommand = SmbComTransaction.Trans2FindNext2; + for (; ; ) + { + for (int i = 0; i < resp.NumEntries; i++) + { + IFileEntry e = resp.Results[i]; + string name = e.GetName(); + if (name.Length < 3) + { + int h = name.GetHashCode(); + if (h == HashDot || h == HashDotDot) + { + if (name.Equals(".") || name.Equals("..")) + { + continue; + } + } + } + if (fnf != null && fnf.Accept(this, name) == false) + { + continue; + } + if (name.Length > 0) + { + SmbFile f = new SmbFile(this, name, TypeFilesystem, e.GetAttributes + (), e.CreateTime(), e.LastModified(), e.Length()); + if (ff != null && ff.Accept(f) == false) + { + continue; + } + if (files) + { + list.Add(f); + } + else + { + list.Add(name); + } + } + } + if (resp.IsEndOfSearch || resp.NumEntries == 0) + { + break; + } + req.Reset(resp.ResumeKey, resp.LastName); + resp.Reset(); + Send(req, resp); + } + try + { + Send(new SmbComFindClose2(sid), Blank_resp()); + } + catch (SmbException se) + { + if (Log.Level >= 4) + { + Runtime.PrintStackTrace(se, Log); + } + } + } + + /// <summary> + /// Changes the name of the file this <code>SmbFile</code> represents to the name + /// designated by the <code>SmbFile</code> argument. + /// </summary> + /// <remarks> + /// Changes the name of the file this <code>SmbFile</code> represents to the name + /// designated by the <code>SmbFile</code> argument. + /// <p/> + /// <i>Remember: <code>SmbFile</code>s are immutible and therefore + /// the path associated with this <code>SmbFile</code> object will not + /// change). To access the renamed file it is necessary to construct a + /// new <tt>SmbFile</tt></i>. + /// </remarks> + /// <param name="dest">An <code>SmbFile</code> that represents the new pathname</param> + /// <exception cref="System.ArgumentNullException">If the <code>dest</code> argument is <code>null</code> + /// </exception> + /// <exception cref="SharpCifs.Smb.SmbException"></exception> + public virtual void RenameTo(SmbFile dest) + { + if (GetUncPath0().Length == 1 || dest.GetUncPath0().Length == 1) + { + throw new SmbException("Invalid operation for workgroups, servers, or shares"); + } + ResolveDfs(null); + dest.ResolveDfs(null); + if (!Tree.Equals(dest.Tree)) + { + throw new SmbException("Invalid operation for workgroups, servers, or shares"); + } + if (Log.Level >= 3) + { + Log.WriteLine("renameTo: " + Unc + " -> " + dest.Unc); + } + _attrExpiration = _sizeExpiration = 0; + dest._attrExpiration = 0; + Send(new SmbComRename(Unc, dest.Unc), Blank_resp()); + } + + internal class WriterThread : Thread + { + internal byte[] B; + + internal int N; + + internal long Off; + + internal bool Ready; + + internal SmbFile Dest; + + internal SmbException E; + + internal bool UseNtSmbs; + + internal SmbComWriteAndX Reqx; + + internal SmbComWrite Req; + + internal ServerMessageBlock Resp; + + /// <exception cref="SharpCifs.Smb.SmbException"></exception> + public WriterThread(SmbFile enclosing) + : base("JCIFS-WriterThread") + { + this._enclosing = enclosing; + UseNtSmbs = this._enclosing.Tree.Session.transport.HasCapability(SmbConstants.CapNtSmbs); + if (UseNtSmbs) + { + Reqx = new SmbComWriteAndX(); + Resp = new SmbComWriteAndXResponse(); + } + else + { + Req = new SmbComWrite(); + Resp = new SmbComWriteResponse(); + } + Ready = false; + } + + internal virtual void Write(byte[] b, int n, SmbFile dest, long off) + { + lock (this) + { + this.B = b; + this.N = n; + this.Dest = dest; + this.Off = off; + Ready = false; + Runtime.Notify(this); + } + } + + public override void Run() + { + lock (this) + { + try + { + for (; ; ) + { + Runtime.Notify(this); + Ready = true; + while (Ready) + { + Runtime.Wait(this); + } + if (N == -1) + { + return; + } + if (UseNtSmbs) + { + Reqx.SetParam(Dest.Fid, Off, N, B, 0, N); + Dest.Send(Reqx, Resp); + } + else + { + Req.SetParam(Dest.Fid, Off, N, B, 0, N); + Dest.Send(Req, Resp); + } + } + } + catch (SmbException e) + { + this.E = e; + } + catch (Exception x) + { + E = new SmbException("WriterThread", x); + } + Runtime.Notify(this); + } + } + + private readonly SmbFile _enclosing; + } + + /// <exception cref="SharpCifs.Smb.SmbException"></exception> + internal virtual void CopyTo0(SmbFile dest, byte[][] b, int bsize, WriterThread + w, SmbComReadAndX req, SmbComReadAndXResponse resp) + { + int i; + if (_attrExpiration < Runtime.CurrentTimeMillis()) + { + _attributes = AttrReadonly | AttrDirectory; + _createTime = 0L; + _lastModified = 0L; + _isExists = false; + IInfo info = QueryPath(GetUncPath0(), Trans2QueryPathInformationResponse.SMB_QUERY_FILE_BASIC_INFO + ); + _attributes = info.GetAttributes(); + _createTime = info.GetCreateTime(); + _lastModified = info.GetLastWriteTime(); + _isExists = true; + _attrExpiration = Runtime.CurrentTimeMillis() + AttrExpirationPeriod; + } + if (IsDirectory()) + { + SmbFile[] files; + SmbFile ndest; + string path = dest.GetUncPath0(); + if (path.Length > 1) + { + try + { + dest.Mkdir(); + dest.SetPathInformation(_attributes, _createTime, _lastModified); + } + catch (SmbException se) + { + if (se.GetNtStatus() != NtStatus.NtStatusAccessDenied && se.GetNtStatus() != NtStatus + .NtStatusObjectNameCollision) + { + throw; + } + } + } + files = ListFiles("*", AttrDirectory | AttrHidden | AttrSystem, null, null); + try + { + for (i = 0; i < files.Length; i++) + { + ndest = new SmbFile(dest, files[i].GetName(), files[i].Type, files[i]._attributes, + files[i]._createTime, files[i]._lastModified, files[i]._size); + files[i].CopyTo0(ndest, b, bsize, w, req, resp); + } + } + catch (UnknownHostException uhe) + { + throw new SmbException(Url.ToString(), uhe); + } + catch (UriFormatException mue) + { + throw new SmbException(Url.ToString(), mue); + } + } + else + { + long off; + try + { + Open(ORdonly, 0, AttrNormal, 0); + try + { + dest.Open(OCreat | OWronly | OTrunc, SmbConstants.FileWriteData | + SmbConstants.FileWriteAttributes, _attributes, 0); + } + catch (SmbAuthException sae) + { + if ((dest._attributes & AttrReadonly) != 0) + { + dest.SetPathInformation(dest._attributes & ~AttrReadonly, 0L, 0L); + dest.Open(OCreat | OWronly | OTrunc, SmbConstants.FileWriteData | + SmbConstants.FileWriteAttributes, _attributes, 0); + } + else + { + throw; + } + } + i = 0; + off = 0L; + for (; ; ) + { + req.SetParam(Fid, off, bsize); + resp.SetParam(b[i], 0); + Send(req, resp); + lock (w) + { + if (w.E != null) + { + throw w.E; + } + while (!w.Ready) + { + try + { + Runtime.Wait(w); + } + catch (Exception ie) + { + throw new SmbException(dest.Url.ToString(), ie); + } + } + if (w.E != null) + { + throw w.E; + } + if (resp.DataLength <= 0) + { + break; + } + w.Write(b[i], resp.DataLength, dest, off); + } + i = i == 1 ? 0 : 1; + off += resp.DataLength; + } + dest.Send(new Trans2SetFileInformation(dest.Fid, _attributes, _createTime, _lastModified + ), new Trans2SetFileInformationResponse()); + dest.Close(0L); + } + catch (SmbException se) + { + if (IgnoreCopyToException == false) + { + throw new SmbException("Failed to copy file from [" + ToString() + "] to [" + + dest + "]", se); + } + if (Log.Level > 1) + { + Runtime.PrintStackTrace(se, Log); + } + } + finally + { + Close(); + } + } + } + + /// <summary> + /// This method will copy the file or directory represented by this + /// <tt>SmbFile</tt> and it's sub-contents to the location specified by the + /// <tt>dest</tt> parameter. + /// </summary> + /// <remarks> + /// This method will copy the file or directory represented by this + /// <tt>SmbFile</tt> and it's sub-contents to the location specified by the + /// <tt>dest</tt> parameter. This file and the destination file do not + /// need to be on the same host. This operation does not copy extended + /// file attibutes such as ACLs but it does copy regular attributes as + /// well as create and last write times. This method is almost twice as + /// efficient as manually copying as it employs an additional write + /// thread to read and write data concurrently. + /// <p/> + /// It is not possible (nor meaningful) to copy entire workgroups or + /// servers. + /// </remarks> + /// <param name="dest">the destination file or directory</param> + /// <exception cref="SmbException">SmbException</exception> + /// <exception cref="SharpCifs.Smb.SmbException"></exception> + public virtual void CopyTo(SmbFile dest) + { + SmbComReadAndX req; + SmbComReadAndXResponse resp; + WriterThread w; + int bsize; + byte[][] b; + if (_share == null || dest._share == null) + { + throw new SmbException("Invalid operation for workgroups or servers"); + } + req = new SmbComReadAndX(); + resp = new SmbComReadAndXResponse(); + Connect0(); + dest.Connect0(); + ResolveDfs(null); + try + { + if (GetAddress().Equals(dest.GetAddress()) && _canon.RegionMatches(true, 0, dest._canon + , 0, Math.Min(_canon.Length, dest._canon.Length))) + { + throw new SmbException("Source and destination paths overlap."); + } + } + catch (UnknownHostException) + { + } + w = new WriterThread(this); + w.SetDaemon(true); + w.Start(); + SmbTransport t1 = Tree.Session.transport; + SmbTransport t2 = dest.Tree.Session.transport; + if (t1.SndBufSize < t2.SndBufSize) + { + t2.SndBufSize = t1.SndBufSize; + } + else + { + t1.SndBufSize = t2.SndBufSize; + } + bsize = Math.Min(t1.RcvBufSize - 70, t1.SndBufSize - 70); + b = new[] { new byte[bsize], new byte[bsize] }; + try + { + CopyTo0(dest, b, bsize, w, req, resp); + } + finally + { + w.Write(null, -1, null, 0); + } + } + + /// <summary> + /// This method will delete the file or directory specified by this + /// <code>SmbFile</code>. + /// </summary> + /// <remarks> + /// This method will delete the file or directory specified by this + /// <code>SmbFile</code>. If the target is a directory, the contents of + /// the directory will be deleted as well. If a file within the directory or + /// it's sub-directories is marked read-only, the read-only status will + /// be removed and the file will be deleted. + /// </remarks> + /// <exception cref="SmbException">SmbException</exception> + /// <exception cref="SharpCifs.Smb.SmbException"></exception> + public virtual void Delete() + { + Exists(); + GetUncPath0(); + Delete(Unc); + } + + /// <exception cref="SharpCifs.Smb.SmbException"></exception> + internal virtual void Delete(string fileName) + { + if (GetUncPath0().Length == 1) + { + throw new SmbException("Invalid operation for workgroups, servers, or shares"); + } + if (Runtime.CurrentTimeMillis() > _attrExpiration) + { + _attributes = AttrReadonly | AttrDirectory; + _createTime = 0L; + _lastModified = 0L; + _isExists = false; + IInfo info = QueryPath(GetUncPath0(), Trans2QueryPathInformationResponse.SMB_QUERY_FILE_BASIC_INFO + ); + _attributes = info.GetAttributes(); + _createTime = info.GetCreateTime(); + _lastModified = info.GetLastWriteTime(); + _attrExpiration = Runtime.CurrentTimeMillis() + AttrExpirationPeriod; + _isExists = true; + } + if ((_attributes & AttrReadonly) != 0) + { + SetReadWrite(); + } + if (Log.Level >= 3) + { + Log.WriteLine("delete: " + fileName); + } + if ((_attributes & AttrDirectory) != 0) + { + try + { + SmbFile[] l = ListFiles("*", AttrDirectory | AttrHidden | AttrSystem, null, null + ); + for (int i = 0; i < l.Length; i++) + { + l[i].Delete(); + } + } + catch (SmbException se) + { + if (se.GetNtStatus() != NtStatus.NtStatusNoSuchFile) + { + throw; + } + } + Send(new SmbComDeleteDirectory(fileName), Blank_resp()); + } + else + { + Send(new SmbComDelete(fileName), Blank_resp()); + } + _attrExpiration = _sizeExpiration = 0; + } + + /// <summary>Returns the length of this <tt>SmbFile</tt> in bytes.</summary> + /// <remarks> + /// Returns the length of this <tt>SmbFile</tt> in bytes. If this object + /// is a <tt>TYPE_SHARE</tt> the total capacity of the disk shared in + /// bytes is returned. If this object is a directory or a type other than + /// <tt>TYPE_SHARE</tt>, 0L is returned. + /// </remarks> + /// <returns> + /// The length of the file in bytes or 0 if this + /// <code>SmbFile</code> is not a file. + /// </returns> + /// <exception cref="SmbException">SmbException</exception> + /// <exception cref="SharpCifs.Smb.SmbException"></exception> + public virtual long Length() + { + if (_sizeExpiration > Runtime.CurrentTimeMillis()) + { + return _size; + } + if (GetType() == TypeShare) + { + Trans2QueryFsInformationResponse response; + int level = Trans2QueryFsInformationResponse.SMB_INFO_ALLOCATION; + response = new Trans2QueryFsInformationResponse(level); + Send(new Trans2QueryFsInformation(level), response); + _size = response.Info.GetCapacity(); + } + else + { + if (GetUncPath0().Length > 1 && Type != TypeNamedPipe) + { + IInfo info = QueryPath(GetUncPath0(), Trans2QueryPathInformationResponse.SMB_QUERY_FILE_STANDARD_INFO + ); + _size = info.GetSize(); + } + else + { + _size = 0L; + } + } + _sizeExpiration = Runtime.CurrentTimeMillis() + AttrExpirationPeriod; + return _size; + } + + /// <summary> + /// This method returns the free disk space in bytes of the drive this share + /// represents or the drive on which the directory or file resides. + /// </summary> + /// <remarks> + /// This method returns the free disk space in bytes of the drive this share + /// represents or the drive on which the directory or file resides. Objects + /// other than <tt>TYPE_SHARE</tt> or <tt>TYPE_FILESYSTEM</tt> will result + /// in 0L being returned. + /// </remarks> + /// <returns> + /// the free disk space in bytes of the drive on which this file or + /// directory resides + /// </returns> + /// <exception cref="SharpCifs.Smb.SmbException"></exception> + public virtual long GetDiskFreeSpace() + { + if (GetType() == TypeShare || Type == TypeFilesystem) + { + int level = Trans2QueryFsInformationResponse.SmbFsFullSizeInformation; + try + { + return QueryFsInformation(level); + } + catch (SmbException ex) + { + switch (ex.GetNtStatus()) + { + case NtStatus.NtStatusInvalidInfoClass: + case NtStatus.NtStatusUnsuccessful: + { + // NetApp Filer + // SMB_FS_FULL_SIZE_INFORMATION not supported by the server. + level = Trans2QueryFsInformationResponse.SMB_INFO_ALLOCATION; + return QueryFsInformation(level); + } + } + throw; + } + } + return 0L; + } + + /// <exception cref="SharpCifs.Smb.SmbException"></exception> + private long QueryFsInformation(int level) + { + Trans2QueryFsInformationResponse response; + response = new Trans2QueryFsInformationResponse(level); + Send(new Trans2QueryFsInformation(level), response); + if (Type == TypeShare) + { + _size = response.Info.GetCapacity(); + _sizeExpiration = Runtime.CurrentTimeMillis() + AttrExpirationPeriod; + } + return response.Info.GetFree(); + } + + /// <summary> + /// Creates a directory with the path specified by this + /// <code>SmbFile</code>. + /// </summary> + /// <remarks> + /// Creates a directory with the path specified by this + /// <code>SmbFile</code>. For this method to be successful, the target + /// must not already exist. This method will fail when + /// used with <code>smb://</code>, <code>smb://workgroup/</code>, + /// <code>smb://server/</code>, or <code>smb://server/share/</code> URLs + /// because workgroups, servers, and shares cannot be dynamically created + /// (although in the future it may be possible to create shares). + /// </remarks> + /// <exception cref="SmbException">SmbException</exception> + /// <exception cref="SharpCifs.Smb.SmbException"></exception> + public virtual void Mkdir() + { + string path = GetUncPath0(); + if (path.Length == 1) + { + throw new SmbException("Invalid operation for workgroups, servers, or shares"); + } + if (Log.Level >= 3) + { + Log.WriteLine("mkdir: " + path); + } + Send(new SmbComCreateDirectory(path), Blank_resp()); + _attrExpiration = _sizeExpiration = 0; + } + + /// <summary> + /// Creates a directory with the path specified by this <tt>SmbFile</tt> + /// and any parent directories that do not exist. + /// </summary> + /// <remarks> + /// Creates a directory with the path specified by this <tt>SmbFile</tt> + /// and any parent directories that do not exist. This method will fail + /// when used with <code>smb://</code>, <code>smb://workgroup/</code>, + /// <code>smb://server/</code>, or <code>smb://server/share/</code> URLs + /// because workgroups, servers, and shares cannot be dynamically created + /// (although in the future it may be possible to create shares). + /// </remarks> + /// <exception cref="SmbException">SmbException</exception> + /// <exception cref="SharpCifs.Smb.SmbException"></exception> + public virtual void Mkdirs() + { + SmbFile parent; + try + { + parent = new SmbFile(GetParent(), Auth); + } + catch (IOException) + { + return; + } + if (parent.Exists() == false) + { + parent.Mkdirs(); + } + Mkdir(); + } + + /// <summary>Create a new file but fail if it already exists.</summary> + /// <remarks> + /// Create a new file but fail if it already exists. The check for + /// existance of the file and it's creation are an atomic operation with + /// respect to other filesystem activities. + /// </remarks> + /// <exception cref="SharpCifs.Smb.SmbException"></exception> + public virtual void CreateNewFile() + { + if (GetUncPath0().Length == 1) + { + throw new SmbException("Invalid operation for workgroups, servers, or shares"); + } + Close(Open0(ORdwr | OCreat | OExcl, 0, AttrNormal, 0), 0L); + } + + /// <exception cref="SharpCifs.Smb.SmbException"></exception> + internal virtual void SetPathInformation(int attrs, long ctime, long mtime) + { + int f; + int dir; + Exists(); + dir = _attributes & AttrDirectory; + f = Open0(ORdonly, SmbConstants.FileWriteAttributes, dir, dir != 0 ? 0x0001 + : 0x0040); + Send(new Trans2SetFileInformation(f, attrs | dir, ctime, mtime), new Trans2SetFileInformationResponse + ()); + Close(f, 0L); + _attrExpiration = 0; + } + + /// <summary>Set the create time of the file.</summary> + /// <remarks> + /// Set the create time of the file. The time is specified as milliseconds + /// from Jan 1, 1970 which is the same as that which is returned by the + /// <tt>createTime()</tt> method. + /// <p/> + /// This method does not apply to workgroups, servers, or shares. + /// </remarks> + /// <param name="time">the create time as milliseconds since Jan 1, 1970</param> + /// <exception cref="SharpCifs.Smb.SmbException"></exception> + public virtual void SetCreateTime(long time) + { + if (GetUncPath0().Length == 1) + { + throw new SmbException("Invalid operation for workgroups, servers, or shares"); + } + SetPathInformation(0, time, 0L); + } + + /// <summary>Set the last modified time of the file.</summary> + /// <remarks> + /// Set the last modified time of the file. The time is specified as milliseconds + /// from Jan 1, 1970 which is the same as that which is returned by the + /// <tt>lastModified()</tt>, <tt>getLastModified()</tt>, and <tt>getDate()</tt> methods. + /// <p/> + /// This method does not apply to workgroups, servers, or shares. + /// </remarks> + /// <param name="time">the last modified time as milliseconds since Jan 1, 1970</param> + /// <exception cref="SharpCifs.Smb.SmbException"></exception> + public virtual void SetLastModified(long time) + { + if (GetUncPath0().Length == 1) + { + throw new SmbException("Invalid operation for workgroups, servers, or shares"); + } + SetPathInformation(0, 0L, time); + } + + /// <summary>Return the attributes of this file.</summary> + /// <remarks> + /// Return the attributes of this file. Attributes are represented as a + /// bitset that must be masked with <tt>ATTR_*</tt> constants to determine + /// if they are set or unset. The value returned is suitable for use with + /// the <tt>setAttributes()</tt> method. + /// </remarks> + /// <returns>the <tt>ATTR_*</tt> attributes associated with this file</returns> + /// <exception cref="SmbException">SmbException</exception> + /// <exception cref="SharpCifs.Smb.SmbException"></exception> + public virtual int GetAttributes() + { + if (GetUncPath0().Length == 1) + { + return 0; + } + Exists(); + return _attributes & AttrGetMask; + } + + /// <summary>Set the attributes of this file.</summary> + /// <remarks> + /// Set the attributes of this file. Attributes are composed into a + /// bitset by bitwise ORing the <tt>ATTR_*</tt> constants. Setting the + /// value returned by <tt>getAttributes</tt> will result in both files + /// having the same attributes. + /// </remarks> + /// <exception cref="SmbException">SmbException</exception> + /// <exception cref="SharpCifs.Smb.SmbException"></exception> + public virtual void SetAttributes(int attrs) + { + if (GetUncPath0().Length == 1) + { + throw new SmbException("Invalid operation for workgroups, servers, or shares"); + } + SetPathInformation(attrs & AttrSetMask, 0L, 0L); + } + + /// <summary>Make this file read-only.</summary> + /// <remarks> + /// Make this file read-only. This is shorthand for <tt>setAttributes( + /// getAttributes() | ATTR_READ_ONLY )</tt>. + /// </remarks> + /// <exception cref="SmbException">SmbException</exception> + /// <exception cref="SharpCifs.Smb.SmbException"></exception> + public virtual void SetReadOnly() + { + SetAttributes(GetAttributes() | AttrReadonly); + } + + /// <summary>Turn off the read-only attribute of this file.</summary> + /// <remarks> + /// Turn off the read-only attribute of this file. This is shorthand for + /// <tt>setAttributes( getAttributes() & ~ATTR_READONLY )</tt>. + /// </remarks> + /// <exception cref="SmbException">SmbException</exception> + /// <exception cref="SmbException"></exception> + public virtual void SetReadWrite() + { + SetAttributes(GetAttributes() & ~AttrReadonly); + } + + /// <summary> + /// Returns a + /// <see cref="System.Uri">System.Uri</see> + /// for this <code>SmbFile</code>. The + /// <code>URL</code> may be used as any other <code>URL</code> might to + /// access an SMB resource. Currently only retrieving data and information + /// is supported (i.e. no <tt>doOutput</tt>). + /// </summary> + /// <returns> + /// A new <code> + /// <see cref="System.Uri">System.Uri</see> + /// </code> for this <code>SmbFile</code> + /// </returns> + /// <exception cref="System.UriFormatException">System.UriFormatException</exception> + [Obsolete(@"Use getURL() instead")] + public virtual Uri ToUrl() + { + return Url; + } + + /// <summary> + /// Computes a hashCode for this file based on the URL string and IP + /// address if the server. + /// </summary> + /// <remarks> + /// Computes a hashCode for this file based on the URL string and IP + /// address if the server. The hashing function uses the hashcode of the + /// server address, the canonical representation of the URL, and does not + /// compare authentication information. In essance, two + /// <code>SmbFile</code> objects that refer to + /// the same file should generate the same hashcode provided it is possible + /// to make such a determination. + /// </remarks> + /// <returns>A hashcode for this abstract file</returns> + /// <exception cref="SmbException">SmbException</exception> + public override int GetHashCode() + { + int hash; + try + { + hash = GetAddress().GetHashCode(); + } + catch (UnknownHostException) + { + hash = GetServer().ToUpper().GetHashCode(); + } + GetUncPath0(); + return hash + _canon.ToUpper().GetHashCode(); + } + + protected internal virtual bool PathNamesPossiblyEqual(string path1, string path2 + ) + { + int p1; + int p2; + int l1; + int l2; + // if unsure return this method returns true + p1 = path1.LastIndexOf('/'); + p2 = path2.LastIndexOf('/'); + l1 = path1.Length - p1; + l2 = path2.Length - p2; + // anything with dots voids comparison + if (l1 > 1 && path1[p1 + 1] == '.') + { + return true; + } + if (l2 > 1 && path2[p2 + 1] == '.') + { + return true; + } + return l1 == l2 && path1.RegionMatches(true, p1, path2, p2, l1); + } + + /// <summary>Tests to see if two <code>SmbFile</code> objects are equal.</summary> + /// <remarks> + /// Tests to see if two <code>SmbFile</code> objects are equal. Two + /// SmbFile objects are equal when they reference the same SMB + /// resource. More specifically, two <code>SmbFile</code> objects are + /// equals if their server IP addresses are equal and the canonicalized + /// representation of their URLs, minus authentication parameters, are + /// case insensitivly and lexographically equal. + /// <p/> + /// For example, assuming the server <code>angus</code> resolves to the + /// <code>192.168.1.15</code> IP address, the below URLs would result in + /// <code>SmbFile</code>s that are equal. + /// <p><blockquote><pre> + /// smb://192.168.1.15/share/DIR/foo.txt + /// smb://angus/share/data/../dir/foo.txt + /// </pre></blockquote> + /// </remarks> + /// <param name="obj">Another <code>SmbFile</code> object to compare for equality</param> + /// <returns> + /// <code>true</code> if the two objects refer to the same SMB resource + /// and <code>false</code> otherwise + /// </returns> + /// <exception cref="SmbException">SmbException</exception> + public override bool Equals(object obj) + { + if (obj is SmbFile) + { + SmbFile f = (SmbFile)obj; + bool ret; + if (this == f) + { + return true; + } + if (PathNamesPossiblyEqual(Url.AbsolutePath, f.Url.AbsolutePath)) + { + GetUncPath0(); + f.GetUncPath0(); + if (Runtime.EqualsIgnoreCase(_canon, f._canon)) + { + try + { + ret = GetAddress().Equals(f.GetAddress()); + } + catch (UnknownHostException) + { + ret = Runtime.EqualsIgnoreCase(GetServer(), f.GetServer()); + } + return ret; + } + } + } + return false; + } + + /// <summary>Returns the string representation of this SmbFile object.</summary> + /// <remarks> + /// Returns the string representation of this SmbFile object. This will + /// be the same as the URL used to construct this <code>SmbFile</code>. + /// This method will return the same value + /// as <code>getPath</code>. + /// </remarks> + /// <returns>The original URL representation of this SMB resource</returns> + /// <exception cref="SmbException">SmbException</exception> + public override string ToString() + { + return Url.ToString(); + } + + /// <summary>This URLConnection method just returns the result of <tt>length()</tt>.</summary> + /// <remarks>This URLConnection method just returns the result of <tt>length()</tt>.</remarks> + /// <returns>the length of this file or 0 if it refers to a directory</returns> + public int GetContentLength() + { + try + { + return (int)(Length() & unchecked(0xFFFFFFFFL)); + } + catch (SmbException) + { + } + return 0; + } + + /// <summary>This URLConnection method just returns the result of <tt>lastModified</tt>. + /// </summary> + /// <remarks>This URLConnection method just returns the result of <tt>lastModified</tt>. + /// </remarks> + /// <returns>the last modified data as milliseconds since Jan 1, 1970</returns> + public long GetDate() + { + try + { + return LastModified(); + } + catch (SmbException) + { + } + return 0L; + } + + /// <summary>This URLConnection method just returns the result of <tt>lastModified</tt>. + /// </summary> + /// <remarks>This URLConnection method just returns the result of <tt>lastModified</tt>. + /// </remarks> + /// <returns>the last modified data as milliseconds since Jan 1, 1970</returns> + public long GetLastModified() + { + try + { + return LastModified(); + } + catch (SmbException) + { + } + return 0L; + } + + /// <summary>This URLConnection method just returns a new <tt>SmbFileInputStream</tt> created with this file. + /// </summary> + /// <remarks>This URLConnection method just returns a new <tt>SmbFileInputStream</tt> created with this file. + /// </remarks> + /// <exception cref="System.IO.IOException">thrown by <tt>SmbFileInputStream</tt> constructor + /// </exception> + public InputStream GetInputStream() + { + return new SmbFileInputStream(this); + } + + /// <summary>This URLConnection method just returns a new <tt>SmbFileOutputStream</tt> created with this file. + /// </summary> + /// <remarks>This URLConnection method just returns a new <tt>SmbFileOutputStream</tt> created with this file. + /// </remarks> + /// <exception cref="System.IO.IOException">thrown by <tt>SmbFileOutputStream</tt> constructor + /// </exception> + public OutputStream GetOutputStream() + { + return new SmbFileOutputStream(this); + } + + /// <exception cref="System.IO.IOException"></exception> + private void ProcessAces(Ace[] aces, bool resolveSids) + { + string server = GetServerWithDfs(); + int ai; + if (resolveSids) + { + Sid[] sids = new Sid[aces.Length]; + string[] names = null; + for (ai = 0; ai < aces.Length; ai++) + { + sids[ai] = aces[ai].Sid; + } + for (int off = 0; off < sids.Length; off += 64) + { + int len = sids.Length - off; + if (len > 64) + { + len = 64; + } + Sid.ResolveSids(server, Auth, sids, off, len); + } + } + else + { + for (ai = 0; ai < aces.Length; ai++) + { + aces[ai].Sid.OriginServer = server; + aces[ai].Sid.OriginAuth = Auth; + } + } + } + + /// <summary> + /// Return an array of Access Control Entry (ACE) objects representing + /// the security descriptor associated with this file or directory. + /// </summary> + /// <remarks> + /// Return an array of Access Control Entry (ACE) objects representing + /// the security descriptor associated with this file or directory. + /// If no DACL is present, null is returned. If the DACL is empty, an array with 0 elements is returned. + /// </remarks> + /// <param name="resolveSids"> + /// Attempt to resolve the SIDs within each ACE form + /// their numeric representation to their corresponding account names. + /// </param> + /// <exception cref="System.IO.IOException"></exception> + public virtual Ace[] GetSecurity(bool resolveSids) + { + int f; + Ace[] aces; + f = Open0(ORdonly, SmbConstants.ReadControl, 0, IsDirectory() ? 1 : 0); + NtTransQuerySecurityDesc request = new NtTransQuerySecurityDesc(f, 0x04); + NtTransQuerySecurityDescResponse response = new NtTransQuerySecurityDescResponse( + ); + try + { + Send(request, response); + } + finally + { + Close(f, 0L); + } + aces = response.SecurityDescriptor.Aces; + if (aces != null) + { + ProcessAces(aces, resolveSids); + } + return aces; + } + + /// <summary> + /// Return an array of Access Control Entry (ACE) objects representing + /// the share permissions on the share exporting this file or directory. + /// </summary> + /// <remarks> + /// Return an array of Access Control Entry (ACE) objects representing + /// the share permissions on the share exporting this file or directory. + /// If no DACL is present, null is returned. If the DACL is empty, an array with 0 elements is returned. + /// <p> + /// Note that this is different from calling <tt>getSecurity</tt> on a + /// share. There are actually two different ACLs for shares - the ACL on + /// the share and the ACL on the folder being shared. + /// Go to <i>Computer Management</i> + /// > <i>System Tools</i> > <i>Shared Folders</i> > <i>Shares</i> and + /// look at the <i>Properties</i> for a share. You will see two tabs - one + /// for "Share Permissions" and another for "Security". These correspond to + /// the ACLs returned by <tt>getShareSecurity</tt> and <tt>getSecurity</tt> + /// respectively. + /// </remarks> + /// <param name="resolveSids"> + /// Attempt to resolve the SIDs within each ACE form + /// their numeric representation to their corresponding account names. + /// </param> + /// <exception cref="System.IO.IOException"></exception> + public virtual Ace[] GetShareSecurity(bool resolveSids) + { + string p = Url.AbsolutePath; + MsrpcShareGetInfo rpc; + DcerpcHandle handle; + Ace[] aces; + ResolveDfs(null); + string server = GetServerWithDfs(); + rpc = new MsrpcShareGetInfo(server, Tree.Share); + handle = DcerpcHandle.GetHandle("ncacn_np:" + server + "[\\PIPE\\srvsvc]", Auth); + try + { + handle.Sendrecv(rpc); + if (rpc.Retval != 0) + { + throw new SmbException(rpc.Retval, true); + } + aces = rpc.GetSecurity(); + if (aces != null) + { + ProcessAces(aces, resolveSids); + } + } + finally + { + try + { + handle.Close(); + } + catch (IOException ioe) + { + if (Log.Level >= 1) + { + Runtime.PrintStackTrace(ioe, Log); + } + } + } + return aces; + } + + /// <summary> + /// Return an array of Access Control Entry (ACE) objects representing + /// the security descriptor associated with this file or directory. + /// </summary> + /// <remarks> + /// Return an array of Access Control Entry (ACE) objects representing + /// the security descriptor associated with this file or directory. + /// <p> + /// Initially, the SIDs within each ACE will not be resolved however when + /// <tt>getType()</tt>, <tt>getDomainName()</tt>, <tt>getAccountName()</tt>, + /// or <tt>toString()</tt> is called, the names will attempt to be + /// resolved. If the names cannot be resolved (e.g. due to temporary + /// network failure), the said methods will return default values (usually + /// <tt>S-X-Y-Z</tt> strings of fragments of). + /// <p> + /// Alternatively <tt>getSecurity(true)</tt> may be used to resolve all + /// SIDs together and detect network failures. + /// </remarks> + /// <exception cref="System.IO.IOException"></exception> + public virtual Ace[] GetSecurity() + { + return GetSecurity(false); + } + } +} |
