gt:             e = (FileEntry)iter.next();
context: /* jcifs smb client library in Java
   * Copyright (C) 2000  "Michael B. Allen" <jcifs at samba dot org>
   * 
   * 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
   */
  
  package jcifs.smb;
  
  import java.net.URLConnection;
  import java.net.URL;
  import java.net.MalformedURLException;
  import java.net.UnknownHostException;
  import java.io.IOException;
  import java.io.InputStream;
  import java.io.OutputStream;
  import java.util.ArrayList;
  import java.util.HashMap;
  import java.util.Iterator;
  import java.security.Principal;
  import jcifs.Config;
  import jcifs.util.LogStream;
  import jcifs.UniAddress;
  import jcifs.netbios.NbtAddress;
  import jcifs.dcerpc.*;
  import jcifs.dcerpc.msrpc.*;
  
  import java.util.Date;
  
  /**
   * 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 {@link java.io.File} 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/ &lt;-- 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 &lt;-- 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/ &lt;-- 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.
   *
   * @see       java.io.File
   */
  
  public class SmbFile extends URLConnection implements SmbConstants {
  
      static final int O_RDONLY = 0x01;
      static final int O_WRONLY = 0x02;
      static final int O_RDWR   = 0x03;
      static final int O_APPEND = 0x04;
  
      // Open Function Encoding
      // create if the file does not exist
      static final int O_CREAT  = 0x0010;
      // fail if the file exists
      static final int O_EXCL   = 0x0020;
      // truncate if the file exists
      static final int O_TRUNC  = 0x0040;
  
      // share access
  /**
   * 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.
   */
      public static final int FILE_NO_SHARE     = 0x00;
  /**
   * 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.
   */
      public static final int FILE_SHARE_READ   = 0x01;
  /**
   * 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.
   */
      public static final int FILE_SHARE_WRITE  = 0x02;
  /**
   * 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.
   */
      public static final int FILE_SHARE_DELETE = 0x04;
  
      // file attribute encoding
  /** 
   * A file with this bit on as returned by <tt>getAttributes()</tt> or set
   * with <tt>setAttributes()</tt> will be read-only
   */ 
      public static final int ATTR_READONLY   = 0x01;
  /** 
   * A file with this bit on as returned by <tt>getAttributes()</tt> or set
   * with <tt>setAttributes()</tt> will be hidden
   */
      public static final int ATTR_HIDDEN     = 0x02;
  /**
   * A file with this bit on as returned by <tt>getAttributes()</tt> or set
   * with <tt>setAttributes()</tt> will be a system file
   */
      public static final int ATTR_SYSTEM     = 0x04;
  /**
   * A file with this bit on as returned by <tt>getAttributes()</tt> is
   * a volume
   */
      public static final int ATTR_VOLUME     = 0x08;
  /**
   * A file with this bit on as returned by <tt>getAttributes()</tt> is
   * a directory
   */
      public static final int ATTR_DIRECTORY  = 0x10;
  /**
   * A file with this bit on as returned by <tt>getAttributes()</tt> or set
   * with <tt>setAttributes()</tt> is an archived file
   */
      public static final int ATTR_ARCHIVE    = 0x20;
  
      // extended file attribute encoding(others same as above)
      static final int ATTR_COMPRESSED       = 0x800;
      static final int ATTR_NORMAL           = 0x080;
      static final int ATTR_TEMPORARY        = 0x100;
  
      static final int ATTR_GET_MASK = 0x7FFF; /* orig 0x7fff */
      static final int ATTR_SET_MASK = 0x30A7; /* orig 0x0027 */
  
      static final int DEFAULT_ATTR_EXPIRATION_PERIOD = 5000;
  
      static final int HASH_DOT     = ".".hashCode();
      static final int HASH_DOT_DOT = "..".hashCode();
  
      static LogStream log = LogStream.getInstance();
      static long attrExpirationPeriod;
      static boolean ignoreCopyToException;
  
      static {
  
          try {
              Class.forName( "jcifs.Config" );
          } catch( ClassNotFoundException cnfe ) {
              cnfe.printStackTrace();
          }
          attrExpirationPeriod = Config.getLong( "jcifs.smb.client.attrExpirationPeriod", DEFAULT_ATTR_EXPIRATION_PERIOD );
          ignoreCopyToException = Config.getBoolean( "jcifs.smb.client.ignoreCopyToException", true );
          dfs = new Dfs();
      }
  
      /**
       * Returned by {@link #getType()} if the resource this <tt>SmbFile</tt>
       * represents is a regular file or directory.
       */
      public static final int TYPE_FILESYSTEM = 0x01;
      /**
       * Returned by {@link #getType()} if the resource this <tt>SmbFile</tt>
       * represents is a workgroup.
       */
      public static final int TYPE_WORKGROUP = 0x02;
      /**
       * Returned by {@link #getType()} if the resource this <tt>SmbFile</tt>
       * represents is a server.
       */
      public static final int TYPE_SERVER = 0x04;
      /**
       * Returned by {@link #getType()} if the resource this <tt>SmbFile</tt>
       * represents is a share.
       */
      public static final int TYPE_SHARE = 0x08;
      /**
       * Returned by {@link #getType()} if the resource this <tt>SmbFile</tt>
       * represents is a named pipe.
       */
      public static final int TYPE_NAMED_PIPE = 0x10;
      /**
       * Returned by {@link #getType()} if the resource this <tt>SmbFile</tt>
       * represents is a printer.
       */
      public static final int TYPE_PRINTER = 0x20;
      /**
       * Returned by {@link #getType()} if the resource this <tt>SmbFile</tt>
       * represents is a communications device.
       */
      public static final int TYPE_COMM = 0x40;
  
  
      private String canon;            // Initially null; set by getUncPath; dir must end with '/'
      private String share;            // Can be null
      private long createTime;
      private long lastModified;
      private int attributes;
      private long attrExpiration;
      private long size;
      private long sizeExpiration;
      private boolean isExists;
      private int shareAccess = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
      private SmbComBlankResponse blank_resp = null;
      private DfsReferral dfsReferral = null;  // For getDfsPath() and getServerWithDfs()
  
      protected static Dfs dfs;
  
      NtlmPasswordAuthentication auth; // Cannot be null
      SmbTree tree = null;             // Initially null
      String unc;                      // Initially null; set by getUncPath; never ends with '/'
      int fid;                         // Initially 0; set by open()
      int type;
      boolean opened;
      int tree_num;
  
      private boolean forceNewSmbTransport = false;
  
      /**
       * Added by TijsH as a workaround for the stuck at port 139 issue
       * It forces re-negotiating the SMB port one time when this SmbFile connects.
       */
      public void forceNewConnectionOnNewSmbTransport() throws IOException {
          if (this.tree != null) { // make sure this.connect() creates a new connection.
              throw new IOException("Can only call forceNewConnectionOnNewSmbTransport once on a new SmbFile object.");
          }
          this.forceNewSmbTransport = true; // forces this.doConnect() to re-negotiate
          this.connect();
      }
  
      /**
       * Added by TijsH to get the current TCP port.
       * Note: only valid after an action, e.g. SmbFile.exists()
       */
      public int getPortOnlyValidAfterAnAction() {
          if (this.tree != null && this.tree.session != null && this.tree.session.transport != null) {
              return this.tree.session.transport.port;
          }
          return 0;
      }
  
      /**
   * 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.
   *
   * @param   url A URL string
   * @throws  MalformedURLException
   *          If the <code>parent</code> and <code>child</code> parameters
   *          do not follow the prescribed syntax
   */
  
      public SmbFile( String url ) throws MalformedURLException {
          this( new URL( null, url, Handler.SMB_HANDLER ));
      }
  
  /**
   * 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.
   *
   * @param   context A base <code>SmbFile</code>
   * @param   name A path string relative to the <code>parent</code> paremeter
   * @throws  MalformedURLException
   *          If the <code>parent</code> and <code>child</code> parameters
   *          do not follow the prescribed syntax
   * @throws  UnknownHostException
   *          If the server or workgroup of the <tt>context</tt> file cannot be determined
   */
  
      public SmbFile( SmbFile context, String name )
                  throws MalformedURLException, UnknownHostException {
          this( context.isWorkgroup0() ?
              new URL( null, "smb://" + name, Handler.SMB_HANDLER ) :
              new URL( context.url, name, Handler.SMB_HANDLER ), context.auth );
      }
  
  /**
   * 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.
   *
   * @param   context A URL string
   * @param   name A path string relative to the <code>context</code> paremeter
   * @throws  MalformedURLException
   *          If the <code>context</code> and <code>name</code> parameters
   *          do not follow the prescribed syntax
   */
  
      public SmbFile( String context, String name ) throws MalformedURLException {
          this( new URL( new URL( null, context, Handler.SMB_HANDLER ),
                  name, Handler.SMB_HANDLER ));
      }
  
  /**
   * Constructs an SmbFile representing a resource on an SMB network such
   * as a file or directory.
   *
   * @param   url A URL string
   * @param   auth The credentials the client should use for authentication
   * @throws  MalformedURLException
   *          If the <code>url</code> parameter does not follow the prescribed syntax
   */
      public SmbFile( String url, NtlmPasswordAuthentication auth )
                      throws MalformedURLException {
          this( new URL( null, url, Handler.SMB_HANDLER ), auth );
      }
  /**
   * 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.
   *
   * @param   url A URL string
   * @param   auth The credentials the client should use for authentication
   * @param   shareAccess Specifies what access other clients have while this file is open.
   * @throws  MalformedURLException
   *          If the <code>url</code> parameter does not follow the prescribed syntax
   */
      public SmbFile( String url, NtlmPasswordAuthentication auth, int shareAccess )
                      throws MalformedURLException {
          this( new URL( null, url, Handler.SMB_HANDLER ), auth );
          if ((shareAccess & ~(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE)) != 0) {
              throw new RuntimeException( "Illegal shareAccess parameter" );
          }
          this.shareAccess = shareAccess;
      }
  /**
   * 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.
   *
   * @param   context A URL string
   * @param   name A path string relative to the <code>context</code> paremeter
   * @param   auth The credentials the client should use for authentication
   * @throws  MalformedURLException
   *          If the <code>context</code> and <code>name</code> parameters
   *          do not follow the prescribed syntax
   */
      public SmbFile( String context, String name, NtlmPasswordAuthentication auth )
                      throws MalformedURLException {
          this( new URL( new URL( null, context, Handler.SMB_HANDLER ), name, Handler.SMB_HANDLER ), auth );
      }
  /**
   * 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.
   *
   * @param   context A URL string
   * @param   name A path string relative to the <code>context</code> paremeter
   * @param   auth The credentials the client should use for authentication
   * @param   shareAccess Specifies what access other clients have while this file is open.
   * @throws  MalformedURLException
   *          If the <code>context</code> and <code>name</code> parameters
   *          do not follow the prescribed syntax
   */
      public SmbFile( String context, String name, NtlmPasswordAuthentication auth, int shareAccess )
                      throws MalformedURLException {
          this( new URL( new URL( null, context, Handler.SMB_HANDLER ), name, Handler.SMB_HANDLER ), auth );
          if ((shareAccess & ~(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE)) != 0) {
              throw new RuntimeException( "Illegal shareAccess parameter" );
          }
          this.shareAccess = shareAccess;
      }
  /**
   * 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.
   *
   * @param   context A base <code>SmbFile</code>
   * @param   name A path string relative to the <code>context</code> file path
   * @param   shareAccess Specifies what access other clients have while this file is open.
   * @throws  MalformedURLException
   *          If the <code>context</code> and <code>name</code> parameters
   *          do not follow the prescribed syntax
   */
      public SmbFile( SmbFile context, String name, int shareAccess )
                      throws MalformedURLException, UnknownHostException {
          this( context.isWorkgroup0() ?
              new URL( null, "smb://" + name, Handler.SMB_HANDLER ) :
              new URL( context.url, name, Handler.SMB_HANDLER ), context.auth );
          if ((shareAccess & ~(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE)) != 0) {
              throw new RuntimeException( "Illegal shareAccess parameter" );
          }
          this.shareAccess = shareAccess;
      }
  /**
   * Constructs an SmbFile representing a resource on an SMB network such
   * as a file or directory from a <tt>URL</tt> object.
   *
   * @param   url The URL of the target resource
   */
      public SmbFile( URL url ) {
          this( url, new NtlmPasswordAuthentication( url.getUserInfo() ));
      }
  /**
   * 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.
   *
   * @param   url The URL of the target resource
   * @param   auth The credentials the client should use for authentication
   */
      public SmbFile( URL url, NtlmPasswordAuthentication auth ) {
          super( url );
          this.auth = auth == null ? new NtlmPasswordAuthentication( url.getUserInfo() ) : auth;
  
          getUncPath0();
      }
      SmbFile( SmbFile context, String name, int type,
                  int attributes, long createTime, long lastModified, long size )
                  throws MalformedURLException, UnknownHostException {
          this( context.isWorkgroup0() ?
              new URL( null, "smb://" + name + "/", Handler.SMB_HANDLER ) :
              new URL( context.url, name + (( attributes & ATTR_DIRECTORY ) > 0 ? "/" : "" )));
  
          /* why was this removed before? DFS? copyTo? Am I going around in circles? */
          auth = context.auth;
  
  
          if( context.share != null ) {
              this.tree = context.tree;
              this.dfsReferral = context.dfsReferral;
          }
          int last = name.length() - 1;
          if( name.charAt( last ) == '/' ) {
              name = name.substring( 0, last );
          }
          if( context.share == null ) {
              this.unc = "\\";
          } else if( context.unc.equals( "\\" )) {
              this.unc = '\\' + name;
          } else {
              this.unc = context.unc + '\\' + name;
          }
      /* why? am I going around in circles?
       *  this.type = type == TYPE_WORKGROUP ? 0 : type;
       */
          this.type = type;
          this.attributes = attributes;
          this.createTime = createTime;
          this.lastModified = lastModified;
          this.size = size;
          isExists = true;
  
          attrExpiration = sizeExpiration =
                  System.currentTimeMillis() + attrExpirationPeriod;
      }
  
      private SmbComBlankResponse blank_resp() {
          if( blank_resp == null ) {
              blank_resp = new SmbComBlankResponse();
          }
          return blank_resp;
      }
      void resolveDfs(ServerMessageBlock request) throws SmbException {
          if (request instanceof 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.SMB_COM_TRANSACTION:
                      case ServerMessageBlock.SMB_COM_TRANSACTION2:
                          switch( ((SmbComTransaction)request).subCommand & 0xFF ) {
                              case SmbComTransaction.TRANS2_GET_DFS_REFERRAL:
                                  break;
                              default:
                                  service = "A:";
                          }
                          break;
                      default:
                          service = "A:";
                  }
              }
  
              DfsReferral start = dr;
              SmbException se = null;
  
              do {
                  try {
                      if (log.level >= 2)
                          log.println("DFS redirect: " + dr);
  
                      UniAddress addr = UniAddress.getByName(dr.server);
                      SmbTransport trans = SmbTransport.getSmbTransport(addr, url.getPort());
                      /* This is a key point. This is where we set the "tree" of this file which
                       * is like changing the rug out from underneath our feet.
                       */
  /* Technically we should also try to authenticate here but that means doing the session setup and tree connect separately. For now a simple connect will at least tell us if the host is alive. That should be sufficient for 99% of the cases. We can revisit this again for 2.0.
   */
                      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 instanceof 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.println( dr );
  
              dfsReferral = dr;
              if (dr.pathConsumed < 0) {
                  dr.pathConsumed = 0;
              } else if (dr.pathConsumed > unc.length()) {
                  dr.pathConsumed = unc.length();
              }
              String dunc = unc.substring(dr.pathConsumed);
              if (dunc.equals(""))
                  dunc = "\\";
              if (!dr.path.equals(""))
                  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 |= ServerMessageBlock.FLAGS2_RESOLVE_PATHS_IN_DFS;
              }
          } else if (tree.inDomainDfs &&
                          !(request instanceof NtTransQuerySecurityDesc) &&
                          !(request instanceof SmbComClose) &&
                          !(request instanceof SmbComFindClose2)) {
              throw new SmbException(NtStatus.NT_STATUS_NOT_FOUND, false);
          } else {
              if (request != null)
                  request.flags2 &= ~ServerMessageBlock.FLAGS2_RESOLVE_PATHS_IN_DFS;
          }
      }
      void send( ServerMessageBlock request,
                      ServerMessageBlock response ) throws SmbException {
          for( ;; ) {
              resolveDfs(request);
              try {
                  tree.send( request, response );
                  break;
              } catch( DfsReferral dre ) {
                  if( dre.resolveHashes ) {
                      throw dre;
                  }
                  request.reset();
              }
          }
      }
  
      static String queryLookup( String query, String param ) {
          char in[] = query.toCharArray();
          int i, ch, st, eq;
  
          st = eq = 0;
          for( i = 0; i < in.length; i++) {
              ch = in[i];
              if( ch == '&' ) {
                  if( eq > st ) {
                      String p = new String( in, st, eq - st );
                      if( p.equalsIgnoreCase( param )) {
                          eq++;
                          return new String( in, eq, i - eq );
                      }
                  }
                  st = i + 1;
              } else if( ch == '=' ) {
                  eq = i;
              }
          }
          if( eq > st ) {
              String p = new String( in, st, eq - st );
              if( p.equalsIgnoreCase( param )) {
                  eq++;
                  return new String( in, eq, in.length - eq );
              }
          }
  
          return null;
      }
  
  UniAddress[] addresses;
  int addressIndex;
  
      UniAddress getAddress() throws UnknownHostException {
          if (addressIndex == 0)
              return getFirstAddress();
          return addresses[addressIndex - 1];
      }
      UniAddress getFirstAddress() throws UnknownHostException {
          addressIndex = 0;
  
          String host = url.getHost();
          String path = url.getPath();
          String query = url.getQuery();
  
          if( query != null ) {
              String server = queryLookup( query, "server" );
              if( server != null && server.length() > 0 ) {
                  addresses = new UniAddress[1];
                  addresses[0] = UniAddress.getByName( server );
                  return getNextAddress();
              }
              String address = queryLookup(query, "address");
              if (address != null && address.length() > 0) {
                  byte[] ip = java.net.InetAddress.getByName(address).getAddress();
                  addresses = new UniAddress[1];
                  addresses[0] = new UniAddress(java.net.InetAddress.getByAddress(host, ip));
                  return getNextAddress();
              }
          }
  
          if (host.length() == 0) {
              try {
                  NbtAddress addr = NbtAddress.getByName(
                          NbtAddress.MASTER_BROWSER_NAME, 0x01, null);
                  addresses = new UniAddress[1];
                  addresses[0] = UniAddress.getByName( addr.getHostAddress() );
              } catch( UnknownHostException uhe ) {
                  NtlmPasswordAuthentication.initDefaults();
                  if( NtlmPasswordAuthentication.DEFAULT_DOMAIN.equals( "?" )) {
                      throw uhe;
                  }
                  addresses = UniAddress.getAllByName( NtlmPasswordAuthentication.DEFAULT_DOMAIN, true );
              }
          } else if( path.length() == 0 || path.equals( "/" )) {
              addresses = UniAddress.getAllByName( host, true );
          } else {
              addresses = UniAddress.getAllByName(host, false);
          }
  
          return getNextAddress();
      }
      UniAddress getNextAddress() {
          UniAddress addr = null;
          if (addressIndex < addresses.length)
              addr = addresses[addressIndex++];
          return addr;
      }
      boolean hasNextAddress() {
          return addressIndex < addresses.length;
      }
      void connect0() throws SmbException {
          try {
              connect();
          } catch( UnknownHostException uhe ) {
              throw new SmbException( "Failed to connect to server", uhe );
          } catch( SmbException se ) {
              throw se;
          } catch( IOException ioe ) {
              throw new SmbException( "Failed to connect to server", ioe );
          }
      }
      void doConnect() throws IOException {
          SmbTransport trans;
          UniAddress addr;
  
          addr = getAddress();
          if (tree != null) {
              trans = tree.session.transport;
          } else {
              // Added by TijsH as a workaround for the stuck at port 139 issue
              if (this.forceNewSmbTransport) {
                  SmbTransport.removeSmbTransport(addr);
                  this.forceNewSmbTransport = false;
              }
              // End of Added by TijsH as a workaround for the stuck at port 139 issue
              trans = SmbTransport.getSmbTransport(addr, url.getPort());
              tree = trans.getSmbSession(auth).getSmbTree(share, null);
          }
  
          String hostName = getServerWithDfs();
          tree.inDomainDfs = dfs.resolve(hostName, tree.share, null, auth) != null;
          if (tree.inDomainDfs) {
              tree.connectionState = 2;
          }
  
          try {
              if( log.level >= 3 )
                  log.println( "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())
                      sae.printStackTrace(log);
                  throw sae;
              }
          }
      }
  /**
   * It is not necessary to call this method directly. This is the
   * <tt>URLConnection</tt> implementation of <tt>connect()</tt>.
   */
      public void connect() throws IOException {
          if (isConnected() && tree.session.transport.tconHostName == null) {
              /* Tree thinks it is connected but transport disconnected
               * under it, reset tree to reflect the truth.
               */
              tree.treeDisconnect(true);
          }
  
          if( isConnected() ) {
              return;
          }
  
          getUncPath0();
          getFirstAddress();
          for ( ;; ) {
              try {
                  doConnect();
                  return;
              } catch(SmbAuthException sae) {
                  throw sae; // Prevents account lockout on servers with multiple IPs
              } catch(SmbException se) {
                  if (getNextAddress() == null) 
                      throw se;
                  if (log.level >= 3)
                      se.printStackTrace(log);
              }
          }
      }
      boolean isConnected() {
          return tree != null && tree.connectionState == 2;
      }
      int open0( int flags, int access, int attrs, int options ) throws SmbException {
          int f;
  
          connect0();
  
          if( log.level >= 3 )
              log.println( "open0: " + unc );
  
          /*
           * NT Create AndX / Open AndX Request / Response
           */
  
          if( tree.session.transport.hasCapability( ServerMessageBlock.CAP_NT_SMBS )) {
              SmbComNTCreateAndXResponse response = new SmbComNTCreateAndXResponse();
  SmbComNTCreateAndX request = new SmbComNTCreateAndX( unc, flags, access, shareAccess, attrs, options, null );
  if (this instanceof SmbNamedPipe) {
      request.flags0 |= 0x16;
      request.desiredAccess |= 0x20000;
      response.isExtended = true;
  }
              send( request, response );
              f = response.fid;
              attributes = response.extFileAttributes & ATTR_GET_MASK;
              attrExpiration = System.currentTimeMillis() + attrExpirationPeriod;
              isExists = true;
          } else {
              SmbComOpenAndXResponse response = new SmbComOpenAndXResponse();
              send( new SmbComOpenAndX( unc, access, flags, null ), response );
              f = response.fid;
          }
  
          return f;
      }
      void open( int flags, int access, int attrs, int options ) throws SmbException {
          if( isOpen() ) {
              return;
          }
          fid = open0( flags, access, attrs, options );
          opened = true;
          tree_num = tree.tree_num;
      }
      boolean isOpen() {
          boolean ans = opened && isConnected() && tree_num == tree.tree_num;
          return ans;
      }
      void close( int f, long lastWriteTime ) throws SmbException {
  
          if( log.level >= 3 )
              log.println( "close: " + f );
  
          /*
           * Close Request / Response
           */
  
          send( new SmbComClose( f, lastWriteTime ), blank_resp() );
      }
      void close( long lastWriteTime ) throws SmbException {
          if( isOpen() == false ) {
              return;
          }
          close( fid, lastWriteTime );
          opened = false;
      }
      void close() throws SmbException {
          close( 0L );
      }
  
  /**
   * 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).
   */
  
      public Principal getPrincipal() {
          return auth;
      }
  
  /**
   * 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.
   *
   * @return  The last component of the URL associated with this SMB
   *          resource or <code>smb://</code> if the resource is <code>smb://</code>
   *          itself.
   */
  
      public String getName() {
          getUncPath0();
          if( canon.length() > 1 ) {
              int i = canon.length() - 2;
              while( canon.charAt( i ) != '/' ) {
                  i--;
              }
              return canon.substring( i + 1 );
          } else if( share != null ) {
              return share + '/';
          } else if( url.getHost().length() > 0 ) {
              return url.getHost() + '/';
          } else {
              return "smb://";
          }
      }
  
  /**
   * 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.
   *
   * @return   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>.
   */
  
      public String getParent() {
          String str = url.getAuthority();
  
          if( str.length() > 0 ) {
              StringBuffer sb = new StringBuffer( "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.charAt( i ) != '/' ) {
                  i--;
              }
  
              return str.substring( 0, i + 1 );
          }
  
          return "smb://";
      }
  
  /**
   * 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.
   *
   * @return  The uncanonicalized full URL of this SMB resource.
   */
  
      public String getPath() {
          return url.toString();
      }
  
      String getUncPath0() {
          if( unc == null ) {
              char[] in = url.getPath().toCharArray();
              char[] out = new char[in.length];
              int length = in.length, i, o, state, s;
  
                                /* The canonicalization routine
                                 */
              state = 0;
              o = 0;
              for( i = 0; i < length; i++ ) {
                  switch( state ) {
                      case 0:
                          if( in[i] != '/' ) {
                              return null;
                          }
                          out[o++] = in[i];
                          state = 1;
                          break;
                      case 1:
                          if( in[i] == '/' ) {
                              break;
                          } else if( in[i] == '.' &&
                                      (( i + 1 ) >= length || in[i + 1] == '/' )) {
                              i++;
                              break;
                          } else if(( i + 1 ) < length &&
                                      in[i] == '.' &&
                                      in[i + 1] == '.' &&
                                      (( i + 2 ) >= length || in[i + 2] == '/' )) {
                              i += 2;
                              if( o == 1 ) break;
                              do {
                                  o--;
                              } while( o > 1 && out[o - 1] != '/' );
                              break;
                          }
                          state = 2;
                      case 2:
                          if( in[i] == '/' ) {
                              state = 1;
                          }
                          out[o++] = in[i];
                          break;
                  }
              }
  
              canon = new String( out, 0, o );
  
              if( o > 1 ) {
                  o--;
                  i = canon.indexOf( '/', 1 );
                  if( i < 0 ) {
                      share = canon.substring( 1 );
                      unc = "\\";
                  } else if( i == o ) {
                      share = canon.substring( 1, i );
                      unc = "\\";
                  } else {
                      share = canon.substring( 1, i );
                      unc = canon.substring( i, out[o] == '/' ? o : o + 1 );
                      unc = unc.replace( '/', '\\' );
                  }
              } else {
                  share = null;
                  unc = "\\";
              }
          }
          return unc;
      }
  /**
   * Retuns the Windows UNC style path with backslashs intead of forward slashes.
   *
   * @return  The UNC path.
   */
      public String getUncPath() {
          getUncPath0();
          if( share == null ) {
              return "\\\\" + url.getHost();
          }
          return "\\\\" + url.getHost() + canon.replace( '/', '\\' );
      }
  /**
   * 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.
   *
   * @return  The canonicalized URL of this SMB resource.
   */
  
      public String getCanonicalPath() {
          String str = url.getAuthority();
          getUncPath0();
          if( str.length() > 0 ) {
              return "smb://" + url.getAuthority() + canon;
          }
          return "smb://";
      }
  
  /**
   * 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.
   *
   * @return  The share component or <code>null</code> if there is no share
   */
  
      public String getShare() {
          return share;
      }
  
      String getServerWithDfs() {
          if (dfsReferral != null) {
              return dfsReferral.server;
          }
          return getServer();
      }
  /** 
   * 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.
   * 
   * @return  The server or workgroup name or <code>null</code> if this
   *          <code>SmbFile</code> refers to the root <code>smb://</code> resource.
   */ 
  
      public String getServer() {
          String str = url.getHost();
          if( str.length() == 0 ) {
              return null;
          }
          return str;
      }
  
  /**
   * Returns type of of object this <tt>SmbFile</tt> represents.
   * @return <tt>TYPE_FILESYSTEM, TYPE_WORKGROUP, TYPE_SERVER, TYPE_SHARE,
   * TYPE_PRINTER, TYPE_NAMED_PIPE</tt>, or <tt>TYPE_COMM</tt>.
   */
      public int getType() throws SmbException {
          if( type == 0 ) {
              if( getUncPath0().length() > 1 ) {
                  type = TYPE_FILESYSTEM;
              } else if( share != null ) {
                  // treeConnect good enough to test service type
                  connect0();
                  if( share.equals( "IPC$" )) {
                      type = TYPE_NAMED_PIPE;
                  } else if( tree.service.equals( "LPT1:" )) {
                      type = TYPE_PRINTER;
                  } else if( tree.service.equals( "COMM" )) {
                      type = TYPE_COMM;
                  } else {
                      type = TYPE_SHARE;
                  }
              } else if( url.getAuthority() == null || url.getAuthority().length() == 0 ) {
                  type = TYPE_WORKGROUP;
              } else {
                  UniAddress addr;
                  try {
                      addr = getAddress();
                  } catch( UnknownHostException uhe ) {
                      throw new SmbException( url.toString(), uhe );
                  }
                  if( addr.getAddress() instanceof NbtAddress ) {
                      int code = ((NbtAddress)addr.getAddress()).getNameType();
                      if( code == 0x1d || code == 0x1b ) {
                          type = TYPE_WORKGROUP;
                          return type;
                      }
                  }
                  type = TYPE_SERVER;
              }
          }
          return type;
      }
      boolean isWorkgroup0() throws UnknownHostException {
          if( type == TYPE_WORKGROUP || url.getHost().length() == 0 ) {
              type = TYPE_WORKGROUP;
              return true;
          } else {
              getUncPath0();
              if( share == null ) {
                  UniAddress addr = getAddress();
                  if( addr.getAddress() instanceof NbtAddress ) {
                      int code = ((NbtAddress)addr.getAddress()).getNameType();
                      if( code == 0x1d || code == 0x1b ) {
                          type = TYPE_WORKGROUP;
                          return true;
                      }
                  }
                  type = TYPE_SERVER;
              }
          }
          return false;
      }
  
      Info queryPath( String path, int infoLevel ) throws SmbException {
          connect0();
  
          if (log.level >= 3)
              log.println( "queryPath: " + path );
  
          /* normally we'd check the negotiatedCapabilities for CAP_NT_SMBS
           * however I can't seem to get a good last modified time from
           * SMB_COM_QUERY_INFORMATION so if NT_SMBs are requested
           * by the server than in this case that's what it will get
           * regardless of what jcifs.smb.client.useNTSmbs is set
           * to(overrides negotiatedCapabilities).
           */
  
          /* We really should do the referral before this in case
           * the redirected target has different capabilities. But
           * the way we have been doing that is to call exists() which
           * calls this method so another technique will be necessary
           * to support DFS referral _to_ Win95/98/ME.
           */
  
          if( tree.session.transport.hasCapability( ServerMessageBlock.CAP_NT_SMBS )) {
  
              /*
               * Trans2 Query Path Information Request / Response
               */
  
              Trans2QueryPathInformationResponse response =
                      new Trans2QueryPathInformationResponse( infoLevel );
              send( new Trans2QueryPathInformation( path, infoLevel ), response );
  
              return response.info;
          } else {
  
              /*
               * Query Information Request / Response
               */
  
              SmbComQueryInformationResponse response =
                      new SmbComQueryInformationResponse(
                      tree.session.transport.server.serverTimeZone * 1000 * 60L );
              send( new SmbComQueryInformation( path ), response );
              return response;
          }
      }
  
  /**
   * 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.
   *
   * @return <code>true</code> if the resource exists or is alive or
   *         <code>false</code> otherwise
   */
  
      public boolean exists() throws SmbException {
  
          if( attrExpiration > System.currentTimeMillis() ) {
              return isExists;
          }
  
          attributes = ATTR_READONLY | ATTR_DIRECTORY;
          createTime = 0L;
          lastModified = 0L;
          isExists = false;
  
          try {
              if( url.getHost().length() == 0 ) {
              } else if( share == null ) {
                  if( getType() == TYPE_WORKGROUP ) {
                      UniAddress.getByName( url.getHost(), true );
                  } else {
                      UniAddress.getByName( url.getHost() ).getHostName();
                  }
              } else if( getUncPath0().length() == 1 ||
                                          share.equalsIgnoreCase( "IPC$" )) {
                  connect0(); // treeConnect is good enough
              } else {
                  Info info = queryPath( getUncPath0(),
                      Trans2QueryPathInformationResponse.SMB_QUERY_FILE_BASIC_INFO );
                  attributes = info.getAttributes();
                  createTime = info.getCreateTime();
                  lastModified = info.getLastWriteTime();
              }
  
              /* If any of the above fail, isExists will not be set true
               */
  
              isExists = true;
  
          } catch( UnknownHostException uhe ) {
          } catch( SmbException se ) {
              switch (se.getNtStatus()) {
                  case NtStatus.NT_STATUS_NO_SUCH_FILE:
                  case NtStatus.NT_STATUS_OBJECT_NAME_INVALID:
                  case NtStatus.NT_STATUS_OBJECT_NAME_NOT_FOUND:
                  case NtStatus.NT_STATUS_OBJECT_PATH_NOT_FOUND:
                      break;
                  default:
                      throw se;
              }
          }
  
          attrExpiration = System.currentTimeMillis() + attrExpirationPeriod;
  
          return isExists;
      }
  
  /**
   * 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.
   *
   * @return <code>true</code> if the file is read-only
   */
  
      public boolean canRead() throws SmbException {
          if( getType() == TYPE_NAMED_PIPE ) { // try opening the pipe for reading?
              return true;
          }
          return exists(); // try opening and catch sharing violation?
      }
  
  /**
   * 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.
   *
   * @return  <code>true</code> if the resource exists is not marked
   *          read-only
   */
  
      public boolean canWrite() throws SmbException {
          if( getType() == TYPE_NAMED_PIPE ) { // try opening the pipe for writing?
              return true;
          }
          return exists() && ( attributes & ATTR_READONLY ) == 0;
      }
  
  /**
   * Tests to see if the file this <code>SmbFile</code> represents is a directory.
   *
   * @return <code>true</code> if this <code>SmbFile</code> is a directory
   */
  
      public boolean isDirectory() throws SmbException {
          if( getUncPath0().length() == 1 ) {
              return true;
          }
          if (!exists()) return false;
          return ( attributes & ATTR_DIRECTORY ) == ATTR_DIRECTORY;
      }
  
  /**
   * Tests to see if the file this <code>SmbFile</code> represents is not a directory.
   *
   * @return <code>true</code> if this <code>SmbFile</code> is not a directory
   */
  
      public boolean isFile() throws SmbException {
          if( getUncPath0().length() == 1 ) {
              return false;
          }
          exists();
          return ( attributes & ATTR_DIRECTORY ) == 0;
      }
  
  /**
   * 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>.
   *
   * @return <code>true</code> if the <code>SmbFile</code> is marked as being hidden
   */
  
      public boolean isHidden() throws SmbException {
          if( share == null ) {
              return false;
          } else if( getUncPath0().length() == 1 ) {
              if( share.endsWith( "$" )) {
                  return true;
              }
              return false;
          }
          exists();
          return ( attributes & ATTR_HIDDEN ) == ATTR_HIDDEN;
      }
  
  /**
   * 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.
   */
  
      public String getDfsPath() throws SmbException {
          resolveDfs(null);
          if( dfsReferral == null ) {
              return null;
          }
          String path = "smb:/" + dfsReferral.server + "/" + dfsReferral.share + unc;
          path = path.replace( '\\', '/' );
          if (isDirectory()) {
              path += '/';
          }
          return path;
      }
  
  /**
   * Retrieve the time this <code>SmbFile</code> was created. The value
   * returned is suitable for constructing a {@link java.util.Date} 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.
   *
   * @return The number of milliseconds since the 00:00:00 GMT, January 1,
   *         1970 as a <code>long</code> value
   */
      public long createTime() throws SmbException {
          if( getUncPath0().length() > 1 ) {
              exists();
              return createTime;
          }
          return 0L;
      }
  /**
   * Retrieve the last time the file represented by this
   * <code>SmbFile</code> was modified. The value returned is suitable for
   * constructing a {@link java.util.Date} object (i.e. seconds since Epoch
   * 1970). Times should be the same as those reported using the properties
   * dialog of the Windows Explorer program.
   *
   * @return The number of milliseconds since the 00:00:00 GMT, January 1,
   *         1970 as a <code>long</code> value
   */
      public long lastModified() throws SmbException {
          if( getUncPath0().length() > 1 ) {
              exists();
              return lastModified;
          }
          return 0L;
      }
  /**
   * 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>
   *
   * @return A <code>String[]</code> array of files and directories,
   * workgroups, servers, or shares depending on the context of the
   * resource URL
   */
      public String[] list() throws SmbException {
          return list( "*", ATTR_DIRECTORY | ATTR_HIDDEN | ATTR_SYSTEM, null, null );
      }
  
  /**
   * 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.
   *
   * @param filter a filename filter to exclude filenames from the results
   * @throws SmbException
   # @return An array of filenames
   */
      public String[] list( SmbFilenameFilter filter ) throws SmbException {
          return list( "*", ATTR_DIRECTORY | ATTR_HIDDEN | ATTR_SYSTEM, filter, null );
      }
  
  /**
   * 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>
   *
   * @return An array of <code>SmbFile</code> objects representing file
   * and directories, workgroups, servers, or shares depending on the context
   * of the resource URL
   */
      public SmbFile[] listFiles() throws SmbException {
          return listFiles( "*", ATTR_DIRECTORY | ATTR_HIDDEN | ATTR_SYSTEM, null, null );
      }
  
  /**
   * 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>
   *
   * @param wildcard a wildcard expression
   * @throws SmbException
   * @return An array of <code>SmbFile</code> objects representing file
   * and directories, workgroups, servers, or shares depending on the context
   * of the resource URL
   */
  
      public SmbFile[] listFiles( String wildcard ) throws SmbException {
          return listFiles( wildcard, ATTR_DIRECTORY | ATTR_HIDDEN | ATTR_SYSTEM, null, null );
      }
  /**
   * 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.
   *
   * @param filter a filter to exclude files from the results
   * @return An array of <tt>SmbFile</tt> objects
   * @throws SmbException
   */
      public SmbFile[] listFiles( SmbFilenameFilter filter ) throws SmbException {
          return listFiles( "*", ATTR_DIRECTORY | ATTR_HIDDEN | ATTR_SYSTEM, filter, null );
      }
  /**
   * 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.
   *
   * @param filter a file filter to exclude files from the results
   * @return An array of <tt>SmbFile</tt> objects
   */
      public SmbFile[] listFiles( SmbFileFilter filter ) throws SmbException {
          return listFiles( "*", ATTR_DIRECTORY | ATTR_HIDDEN | ATTR_SYSTEM, null, filter );
      }
      String[] list( String wildcard, int searchAttributes,
                  SmbFilenameFilter fnf, SmbFileFilter ff ) throws SmbException {
          ArrayList list = new ArrayList();
          doEnum(list, false, wildcard, searchAttributes, fnf, ff);
          return (String[])list.toArray(new String[list.size()]);
      }
      SmbFile[] listFiles( String wildcard, int searchAttributes,
                  SmbFilenameFilter fnf, SmbFileFilter ff ) throws SmbException {
          ArrayList list = new ArrayList();
          doEnum(list, true, wildcard, searchAttributes, fnf, ff);
          return (SmbFile[])list.toArray(new SmbFile[list.size()]);
      }
      void doEnum(ArrayList list,
                      boolean files,
                      String wildcard,
                      int searchAttributes,
                      SmbFilenameFilter fnf,
                      SmbFileFilter ff) throws SmbException {
          if (ff != null && ff instanceof DosFileFilter) {
              DosFileFilter dff = (DosFileFilter)ff;
              if (dff.wildcard != null)
                  wildcard = dff.wildcard;
              searchAttributes = dff.attributes;
          }
  
          try {
              int hostlen = url.getHost().length();
              if (hostlen == 0 || getType() == TYPE_WORKGROUP) {
                  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 (MalformedURLException mue) {
              throw new SmbException(url.toString(), mue);
          }
      }
      void doShareEnum(ArrayList list,
                  boolean files,
                  String wildcard,
                  int searchAttributes,
                  SmbFilenameFilter fnf,
                  SmbFileFilter ff) throws SmbException,
                                  UnknownHostException,
                                  MalformedURLException {
          String p = url.getPath();
          IOException last = null;
          FileEntry[] entries;
          UniAddress addr;
          FileEntry e;
          HashMap map;
  
          if (p.lastIndexOf('/') != (p.length() - 1))
              throw new SmbException(url.toString() + " directory must end with '/'");
          if (getType() != TYPE_SERVER)
              throw new SmbException("The requested list operations is invalid: " + url.toString());
  
          map = new HashMap();
  
          if (dfs.isTrustedDomain(getServer(), auth)) {
              /* The server name is actually the name of a trusted
               * domain. Add DFS roots to the list.
               */
              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)
                      ioe.printStackTrace(log);
              }
          }
  
          addr = getFirstAddress();
          while (addr != null) {
              try {
                  doConnect();
                  try {
                      entries = doMsrpcShareEnum();
                  } catch(IOException ioe) {
                      if (log.level >= 3)
                          ioe.printStackTrace(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)
                      ioe.printStackTrace(log);
                  last = ioe;
              }
              addr = getNextAddress();
          }
  
          if (last != null && map.isEmpty()) {
              if (last instanceof SmbException == false)
                  throw new SmbException(url.toString(), last);
              throw (SmbException)last;
          }
  
          Iterator iter = map.keySet().iterator();
          while (iter.hasNext()) {
  