Package com.knowgate.jcifs.smb

Source Code of com.knowgate.jcifs.smb.SmbFile$WriterThread

/* 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 com.knowgate.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.util.ArrayList;

import com.knowgate.debug.*;
import com.knowgate.jcifs.Config;
import com.knowgate.jcifs.UniAddress;
import com.knowgate.jcifs.netbios.NbtAddress;

/**
* 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> and <code>renameTo</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 {

    // these are shifted for use in flags
    static final int O_RDONLY = 0x010000;
    static final int O_WRONLY = 0x020000;
    static final int O_RDWR   = 0x030000;
    static final int O_APPEND = 0x040000;

    // 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;

    // 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   = 0x0001;
    // truncate if the file exists
    static final int O_TRUNC  = 0x0002;

    // 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 = 0x3F;
    static final int ATTR_SET_MASK = 0x27;

    static final int DEFAULT_ATTR_EXPIRATION_PERIOD = 5000;

    static final int HASH_DOT     = ".".hashCode();
    static final int HASH_DOT_DOT = "..".hashCode();

    static long attrExpirationPeriod;

    static {
        try {
            Class.forName( "jcifs.Config" );
        } catch( ClassNotFoundException cnfe ) {
            cnfe.printStackTrace();
        }
        attrExpirationPeriod = Config.getLong( "jcifs.smb.client.attrExpirationPeriod", DEFAULT_ATTR_EXPIRATION_PERIOD );
    }

    /**
     * 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 NtlmPasswordAuthentication auth; // Cannot be null
    private boolean isExists;
    private int shareAccess = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
    private SmbComBlankResponse blank_resp = null;
    private DfsReferral dfsReferral = null// Only used by getDfsPath()

    SmbTree tree = null;             // Initially null; may be !tree.treeConnected
    String unc;                      // Initially null; set by getUncPath; never ends with '/'
    int fid;                         // Initially 0; set by open()
    int type;
    boolean opened;

/**
* 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 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 ? "/" : "" )));

        if( context.share != null ) {
            this.tree = context.tree;
        }
        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 sendTransaction( SmbComTransaction request,
                    SmbComTransactionResponse response ) throws SmbException {
        for( ;; ) {
            connect0();
            if( tree.inDfs ) {
                DfsReferral dr = tree.session.transport.lookupReferral( unc );
                if( dr != null ) {
                    UniAddress addr;
                    SmbTransport trans;

                    try {
                        addr = UniAddress.getByName( dr.server );
                    } catch( UnknownHostException uhe ) {
                        throw new SmbException( dr.server, uhe );
                    }

                    trans = SmbTransport.getSmbTransport( addr, 0 );
                    tree = trans.getSmbSession( auth ).getSmbTree( dr.share, null );
                    unc = dr.nodepath + unc.substring( dr.path.length() );
                    if( request.path.charAt( request.path.length() - 1 ) == '\\' ) {
                        request.path = unc + '\\';
                    } else {
                        request.path = unc;
                    }
                    dfsReferral = dr; /* for getDfsPath */
                }
            }
            if( tree.inDfs ) {
                request.flags2 |= ServerMessageBlock.FLAGS2_RESOLVE_PATHS_IN_DFS;
            } else {
                request.flags2 &= ~ServerMessageBlock.FLAGS2_RESOLVE_PATHS_IN_DFS;
            }
            try {
                tree.sendTransaction( request, response );
                break;
            } catch( DfsReferral dr ) {
                if( dr.resolveHashes ) {
                    throw dr;
                }
                request.reset();
            }
        }
    }
    void send( ServerMessageBlock request,
                    ServerMessageBlock response ) throws SmbException {
        for( ;; ) {
            connect0();
            if( tree.inDfs ) {
                DfsReferral dr = tree.session.transport.lookupReferral( unc );
                if( dr != null ) {
                    UniAddress addr;
                    SmbTransport trans;

                    try {
                        addr = UniAddress.getByName( dr.server );
                    } catch( UnknownHostException uhe ) {
                        throw new SmbException( dr.server, uhe );
                    }

                    trans = SmbTransport.getSmbTransport( addr, 0 );
                    tree = trans.getSmbSession( auth ).getSmbTree( dr.share, null );
                    unc = request.path = dr.nodepath + unc.substring( dr.path.length() );
                    dfsReferral = dr; /* for getDfsPath */
                }
                request.flags2 |= ServerMessageBlock.FLAGS2_RESOLVE_PATHS_IN_DFS;
            } else {
                request.flags2 &= ~ServerMessageBlock.FLAGS2_RESOLVE_PATHS_IN_DFS;
            }
            try {
                tree.send( request, response );
                break;
            } catch( DfsReferral dr ) {
                if( dr.resolveHashes ) {
                    throw dr;
                }
            }
        }
    }

    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 getAddress() throws UnknownHostException {
        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 ) {
                return UniAddress.getByName( server );
            }
        }

        if( host.length() == 0 ) {
            return UniAddress.getByName( NbtAddress.getByName(
                    NbtAddress.MASTER_BROWSER_NAME, 0x01, null).getHostAddress() );
        } else if( path.length() == 0 || path.equals( "/" )) {
            return UniAddress.getByName( host, true );
        } else {
            return UniAddress.getByName( host );
        }
    }
    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 );
        }
    }
/**
* 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 {
        SmbTransport trans;
        SmbSession ssn;
        UniAddress addr;

        if( isConnected() ) {
            return;
        }

        getUncPath0();
        addr = getAddress();

        trans = SmbTransport.getSmbTransport( addr, url.getPort() );
        ssn = trans.getSmbSession( auth );
        tree = ssn.getSmbTree( share, null );

        try {
            tree.treeConnect( null, null );
        } catch( SmbAuthException sae ) {
            NtlmPasswordAuthentication a;

            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.treeConnect( null, null );
            } else {
                throw sae;
            }
        }
    }
    boolean isConnected() {
        return (connected = tree != null && tree.treeConnected);
    }
    int open0( int flags, int attrs, int options ) throws SmbException {
        int f;

        connect0();

        if( DebugFile.trace )
            DebugFile.writeln( "open0: " + unc );

        /*
         * NT Create AndX / Open AndX Request / Response
         */

        if( tree.session.transport.hasCapability( ServerMessageBlock.CAP_NT_SMBS )) {
            SmbComNTCreateAndXResponse response = new SmbComNTCreateAndXResponse();
            send( new SmbComNTCreateAndX( unc, flags, shareAccess,
                    attrs, options, null ), 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, flags, null ), response );
            f = response.fid;
        }

        return f;
    }
    void open( int flags, int attrs, int options ) throws SmbException {
        if( isOpen() ) {
            return;
        }
        fid = open0( flags, attrs, options );
        opened = true;
    }
    boolean isOpen() {
        return opened && isConnected();
    }
    void close( int f, long lastWriteTime ) throws SmbException {

        if( DebugFile.trace )
            DebugFile.writeln( "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 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;
    }

/**
* 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().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( DebugFile.trace )
            DebugFile.writeln( "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 );
            sendTransaction( 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 {
        connect0();
        if( tree.inDfs ) {
            exists();
        }
        if( dfsReferral == null ) {
            return null;
        }
        return "smb:/" + (new String( dfsReferral.node + unc )).replace( '\\', '/' );
    }

/**
* 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();

        try {
            if( url.getHost().length() == 0 || share == null ) {
                doNetEnum( list, false, wildcard, searchAttributes, fnf, ff );
            } else {
                doFindFirstNext( list, false, wildcard, searchAttributes, fnf, ff );
            }
        } catch( UnknownHostException uhe ) {
            throw new SmbException( url.toString(), uhe );
        } catch( MalformedURLException mue ) {
            throw new SmbException( url.toString(), mue );
        }

        return (String[])list.toArray(new String[list.size()]);
    }
    SmbFile[] listFiles( String wildcard, int searchAttributes,
                SmbFilenameFilter fnf, SmbFileFilter ff ) throws SmbException {
        ArrayList list = new ArrayList();

        if( ff != null && ff instanceof DosFileFilter ) {
            DosFileFilter dff = (DosFileFilter)ff;
            if( dff.wildcard != null ) {
                wildcard = dff.wildcard;
            }
            searchAttributes = dff.attributes;
        }

        try {
            if( url.getHost().length() == 0 || share == null ) {
                doNetEnum( list, true, wildcard, searchAttributes, fnf, ff );
            } else {
                doFindFirstNext( list, true, wildcard, searchAttributes, fnf, ff );
            }
        } catch( UnknownHostException uhe ) {
            throw new SmbException( url.toString(), uhe );
        } catch( MalformedURLException mue ) {
            throw new SmbException( url.toString(), mue );
        }

        return (SmbFile[])list.toArray(new SmbFile[list.size()]);
    }
    void doNetEnum( ArrayList list,
                boolean files,
                String wildcard,
                int searchAttributes,
                SmbFilenameFilter fnf,
                SmbFileFilter ff ) throws SmbException,
                        UnknownHostException, MalformedURLException {
        SmbComTransaction req;
        SmbComTransactionResponse resp;
        int listType = url.getAuthority().length() == 0 ? 0 : getType();
        String p = url.getPath();

        if( p.lastIndexOf( '/' ) != ( p.length() - 1 )) {
            throw new SmbException( url.toString() + " directory must end with '/'" );
        }

        switch( listType ) {
            case 0:
                connect0();
                req = new NetServerEnum2( tree.session.transport.server.oemDomainName,
                        NetServerEnum2.SV_TYPE_DOMAIN_ENUM );
                resp = new NetServerEnum2Response();
                break;
            case TYPE_WORKGROUP:
                req = new NetServerEnum2( url.getHost(), NetServerEnum2.SV_TYPE_ALL );
                resp = new NetServerEnum2Response();
                break;
            case TYPE_SERVER:
                req = new NetShareEnum();
                resp = new NetShareEnumResponse();
                break;
            default:
                throw new SmbException( "The requested list operations is invalid: " + url.toString() );
        }

        do {
            sendTransaction( req, resp );

            if( resp.status != SmbException.ERROR_SUCCESS &&
                    resp.status != SmbException.ERROR_MORE_DATA ) {
                throw new SmbException( resp.status, true );
            }

            for( int i = 0; i < resp.numEntries; i++ ) {
                FileEntry e = resp.results[i];
                String name = e.getName();
                if( fnf != null && fnf.accept( this, name ) == false ) {
                    continue;
                }
                if( name.length() > 0 ) {
                    SmbFile f = new SmbFile( this, name,
                                listType == 0 ? TYPE_WORKGROUP : (listType << 1),
                                ATTR_READONLY | ATTR_DIRECTORY, 0L, 0L, 0L );
                    if( ff != null && ff.accept( f ) == false ) {
                        continue;
                    }
                    if( files ) {
                        list.add( f );
                    } else {
                        list.add( name );
                    }
                }
            }
            if( listType != 0 || listType != TYPE_WORKGROUP ) {
                break;
            }
            req.subCommand = (byte)SmbComTransaction.NET_SERVER_ENUM3;
            req.reset( 0, ((NetServerEnum2Response)resp).lastName );
        } while( resp.status == SmbException.ERROR_MORE_DATA );
    }
    void doFindFirstNext( ArrayList list,
                boolean files,
                String wildcard,
                int searchAttributes,
                SmbFilenameFilter fnf,
                SmbFileFilter ff ) throws SmbException, UnknownHostException, MalformedURLException {
        SmbComTransaction req;
        Trans2FindFirst2Response resp;
        int sid;
        String path = getUncPath0();
        String p = url.getPath();

        if( p.lastIndexOf( '/' ) != ( p.length() - 1 )) {
            throw new SmbException( url.toString() + " directory must end with '/'" );
        }

        req = new Trans2FindFirst2( path, wildcard, searchAttributes );
        resp = new Trans2FindFirst2Response();

        if( DebugFile.trace )
            DebugFile.writeln( "doFindFirstNext: " + req.path );

        sendTransaction( req, resp );

        sid = resp.sid;
        req = new Trans2FindNext2( sid, resp.resumeKey, resp.lastName );

        /* The only difference between first2 and next2 responses is subCommand
         * so let's recycle the response object.
         */
        resp.subCommand = SmbComTransaction.TRANS2_FIND_NEXT2;

        for( ;; ) {
            for( int i = 0; i < resp.numEntries; i++ ) {
                FileEntry e = resp.results[i];
                String name = e.getName();
                if( name.length() < 3 ) {
                    int h = name.hashCode();
                    if( h == HASH_DOT || h == HASH_DOT_DOT ) {
                        continue;
                    }
                }
                if( fnf != null && fnf.accept( this, name ) == false ) {
                    continue;
                }
                if( name.length() > 0 ) {
                    SmbFile f = new SmbFile( this, name, TYPE_FILESYSTEM,
                            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();
            sendTransaction( req, resp );
        }

        send( new SmbComFindClose2( sid ), blank_resp() );
    }

/**
* 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>.
*
* @param  dest  An <code>SmbFile</code> that represents the new pathname
* @return <code>true</code> if the file or directory was successfully renamed
* @throws NullPointerException
*         If the <code>dest</code> argument is <code>null</code>
*/
    public void renameTo( SmbFile dest ) throws SmbException {
        if( getUncPath0().length() == 1 || dest.getUncPath0().length() == 1 ) {
            throw new SmbException( "Invalid operation for workgroups, servers, or shares" );
        }
        connect0();
        dest.connect0();

        if( tree.inDfs ) { /* This ensures that each path is
                * resolved independantly to deal with the case where files
                * have the same base path but ultimately turn out to be
                * on different servers because of DFS. It also eliminates
                * manipulating the SMB path which is problematic because
                * there are really two that would need to be prefixed
                * with host and share as DFS requires.
                */
            exists();
            dest.exists();
        }
        if( tree != dest.tree ) {
            throw new SmbException( "Invalid operation for workgroups, servers, or shares" );
        }

        if( DebugFile.trace )
            DebugFile.writeln( "renameTo: " + unc + " -> " + dest.unc );

        attrExpiration = sizeExpiration = 0;
        dest.attrExpiration = 0;

        /*
         * Rename Request / Response
         */

        send( new SmbComRename( unc, dest.unc ), blank_resp() );
    }

    class WriterThread extends Thread {
        byte[] b;
        int n, off;
        boolean ready = true;
        SmbFile dest;
        SmbException e = null;
        boolean useNTSmbs;
        SmbComWriteAndX reqx;
        SmbComWrite req;
        ServerMessageBlock resp;

        WriterThread() throws SmbException {
            super( "JCIFS-WriterThread" );
            useNTSmbs = tree.session.transport.hasCapability( ServerMessageBlock.CAP_NT_SMBS );
            if( useNTSmbs ) {
                reqx = new SmbComWriteAndX();
                resp = new SmbComWriteAndXResponse();
            } else {
                req = new SmbComWrite();
                resp = new SmbComWriteResponse();
            }
        }

        synchronized void write( byte[] b, int n, SmbFile dest, int off ) {
            this.b = b;
            this.n = n;
            this.dest = dest;
            this.off = off;
            ready = false;
            notify();
        }

        public void run() {
            synchronized( this ) {
                try {
                    for( ;; ) {
                        ready = true;
                        while( ready ) {
                            wait();
                        }
                        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 );
                        }
                        notify();
                    }
                } catch( SmbException e ) {
                    this.e = e;
                } catch( Exception x ) {
                    this.e = new SmbException( "WriterThread", x );
                }
                notify();
            }
        }
    }
    void copyTo0( SmbFile dest, byte[][] b, int bsize, WriterThread w,
            SmbComReadAndX req, SmbComReadAndXResponse resp ) throws SmbException {
        int i;

        if( attrExpiration < System.currentTimeMillis() ) {
            attributes = ATTR_READONLY | ATTR_DIRECTORY;
            createTime = 0L;
            lastModified = 0L;
            isExists = false;

            Info info = queryPath( getUncPath0(),
                    Trans2QueryPathInformationResponse.SMB_QUERY_FILE_BASIC_INFO );
            attributes = info.getAttributes();
            createTime = info.getCreateTime();
            lastModified = info.getLastWriteTime();

            /* If any of the above fails, isExists will not be set true
             */

            isExists = true;
            attrExpiration = System.currentTimeMillis() + attrExpirationPeriod;
        }

        if( isDirectory() ) {
            SmbFile[] files;
            SmbFile ndest;

            try {
                dest.mkdir();
                dest.setPathInformation( attributes, createTime, lastModified );
            } catch( SmbException se ) {
                if( se.getNtStatus() != NtStatus.NT_STATUS_ACCESS_DENIED &&
                        se.getNtStatus() != NtStatus.NT_STATUS_OBJECT_NAME_COLLISION ) {
                    throw se;
                }
            }

            files = listFiles( "*", ATTR_DIRECTORY | ATTR_HIDDEN | ATTR_SYSTEM, 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( MalformedURLException mue ) {
                throw new SmbException( url.toString(), mue );
            }
        } else {
            int off;

            open( SmbFile.O_RDONLY, ATTR_NORMAL, 0 );
            try {
                dest.open( SmbFile.O_CREAT | SmbFile.O_WRONLY | SmbFile.O_TRUNC |
                        SmbComNTCreateAndX.FILE_WRITE_ATTRIBUTES << 16, attributes, 0 );
            } catch( SmbAuthException sae ) {
                if(( dest.attributes & ATTR_READONLY ) != 0 ) {
                                            /* Remove READONLY and try again
                                             */
                    dest.setPathInformation( dest.attributes & ~ATTR_READONLY, 0L, 0L );
                    dest.open( SmbFile.O_CREAT | SmbFile.O_WRONLY | SmbFile.O_TRUNC |
                            SmbComNTCreateAndX.FILE_WRITE_ATTRIBUTES << 16, attributes, 0 );
                } else {
                    throw sae;
                }
            }

            i = off = 0;
            for( ;; ) {
                req.setParam( fid, off, bsize );
                resp.setParam( b[i], 0 );
                send( req, resp );

                synchronized( w ) {
                    while( !w.ready ) {
                        try {
                            w.wait();
                        } catch( InterruptedException 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.sendTransaction( new Trans2SetFileInformation(
                    dest.fid, attributes, createTime, lastModified ),
                    new Trans2SetFileInformationResponse() );
            dest.close( 0L );
            close();
        }
    }
/**
* 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.
*
* @param dest the destination file or directory
* @throw SmbException
*/
    public void copyTo( SmbFile dest ) throws SmbException {
        SmbComReadAndX req;
        SmbComReadAndXResponse resp;
        WriterThread w;
        int bsize;
        byte[][] b;

        /* Should be able to copy an entire share actually
         */
        if( share == null || dest.share == null) {
            throw new SmbException( "Invalid operation for workgroups or servers" );
        }

        req = new SmbComReadAndX();
        resp = new SmbComReadAndXResponse();

        connect0();
        dest.connect0();

        if( tree.inDfs ) {
                /* At this point the maxBufferSize values are from the server
                 * exporting the volumes, not the one that we will actually
                 * end up performing IO with. If the server hosting the
                 * actual files has a smaller maxBufSize this could be
                 * incorrect. To handle this properly it is necessary
                 * to redirect the tree to the target server first before
                 * establishing buffer size. These exists() calls facilitate
                 * that.
                 */
            exists();
            dest.exists();
        }

        w = new WriterThread();
        w.setDaemon( true );
        w.start();

        bsize = Math.min( tree.session.transport.rcv_buf_size - 70,
                        tree.session.transport.snd_buf_size - 70 );
        b = new byte[2][bsize];

        copyTo0( dest, b, bsize, w, req, resp );
        w.write( null, -1, null, 0 );
    }

/**
* 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.
*
* @throws SmbException
*/
    public void delete() throws SmbException {
        if( tree == null || tree.inDfs ) {
            exists();      /* This is necessary to ensure we
                            * pass a path adjusted for DFS */
        }
        getUncPath0();
        delete( unc );
    }
    void delete( String fileName ) throws SmbException {
        if( getUncPath0().length() == 1 ) {
            throw new SmbException( "Invalid operation for workgroups, servers, or shares" );
        }

        if( System.currentTimeMillis() > attrExpiration ) {
            attributes = ATTR_READONLY | ATTR_DIRECTORY;
            createTime = 0L;
            lastModified = 0L;
            isExists = false;

            Info info = queryPath( getUncPath0(),
                    Trans2QueryPathInformationResponse.SMB_QUERY_FILE_BASIC_INFO );
            attributes = info.getAttributes();
            createTime = info.getCreateTime();
            lastModified = info.getLastWriteTime();

            attrExpiration = System.currentTimeMillis() + attrExpirationPeriod;
            isExists = true;
        }

        if(( attributes & ATTR_READONLY ) != 0 ) {
            setReadWrite();
        }

        /*
         * Delete or Delete Directory Request / Response
         */

        if( DebugFile.trace )
            DebugFile.writeln( "delete: " + fileName );

        if(( attributes & ATTR_DIRECTORY ) != 0 ) {

            /* Recursively delete directory contents
             */

            SmbFile[] l = listFiles( "*",
                    ATTR_DIRECTORY | ATTR_HIDDEN | ATTR_SYSTEM, null, null );

            for( int i = 0; i < l.length; i++ ) {
                l[i].delete();
            }

            send( new SmbComDeleteDirectory( fileName ), blank_resp() );
        } else {
            send( new SmbComDelete( fileName ), blank_resp() );
        }

        attrExpiration = sizeExpiration = 0;
    }

/**
* 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.
*
* @return The length of the file in bytes or 0 if this
* <code>SmbFile</code> is not a file.
* @throw SmbException
*/

    public long length() throws SmbException {
        if( sizeExpiration > System.currentTimeMillis() ) {
            return size;
        }

        if( getType() == TYPE_SHARE ) {
            Trans2QueryFSInformationResponse response;
            int level = Trans2QueryFSInformationResponse.SMB_INFO_ALLOCATION;

            response = new Trans2QueryFSInformationResponse( level );
            sendTransaction( new Trans2QueryFSInformation( level ), response );

            size = response.info.getCapacity();
        } else if( getUncPath0().length() > 1 && type != TYPE_NAMED_PIPE ) {
            Info info = queryPath( getUncPath0(),
                    Trans2QueryPathInformationResponse.SMB_QUERY_FILE_STANDARD_INFO );
            size = info.getSize();
        } else {
            size = 0L;
        }
        sizeExpiration = System.currentTimeMillis() + attrExpirationPeriod;
        return size;
    }

/**
* 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.
*
* @return the free disk space in bytes of the drive on which this file or
* directory resides
*/
    public long getDiskFreeSpace() throws SmbException {
        if( getType() == TYPE_SHARE || type == TYPE_FILESYSTEM ) {
            Trans2QueryFSInformationResponse response;
            int level = Trans2QueryFSInformationResponse.SMB_INFO_ALLOCATION;

            response = new Trans2QueryFSInformationResponse( level );
            sendTransaction( new Trans2QueryFSInformation( level ), response );

            if( type == TYPE_SHARE ) {
                size = response.info.getCapacity();
                sizeExpiration = System.currentTimeMillis() + attrExpirationPeriod;
            }

            return response.info.getFree();
        }
        return 0L;
    }

/**
* 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).
*
* @throws SmbException
*/
    public void mkdir() throws SmbException {
        String path = getUncPath0();

        if( path.length() == 1 ) {
            throw new SmbException( "Invalid operation for workgroups, servers, or shares" );
        }

        /*
         * Create Directory Request / Response
         */

        if( DebugFile.trace )
            DebugFile.writeln( "mkdir: " + path );

        send( new SmbComCreateDirectory( path ), blank_resp() );

        attrExpiration = sizeExpiration = 0;
    }

/**
* 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).
*
* @throws SmbException
*/
    public void mkdirs() throws SmbException {
        SmbFile parent;

        try {
            parent = new SmbFile( new URL( null, getParent(), Handler.SMB_HANDLER ));
        } catch( IOException ioe ) {
            return;
        }
        if( parent.exists() == false ) {
            parent.mkdirs();
        }
        mkdir();
    }

/**
* 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.
*/
    public void createNewFile() throws SmbException {
        if( getUncPath0().length() == 1 ) {
            throw new SmbException( "Invalid operation for workgroups, servers, or shares" );
        }
        close( open0( O_RDWR | O_CREAT | O_EXCL, ATTR_NORMAL, 0 ), 0L );
    }

    void setPathInformation( int attrs, long ctime, long mtime ) throws SmbException {
        int f, options = 0;

        if(( attrs & ATTR_DIRECTORY ) != 0 ) {
            options = 1;
        }

        f = open0( O_RDONLY | SmbComNTCreateAndX.FILE_WRITE_ATTRIBUTES << 16, attrs, options );
        sendTransaction( new Trans2SetFileInformation( f, attrs, ctime, mtime ),
                new Trans2SetFileInformationResponse() );
        close( f, 0L );

        attrExpiration = 0;
    }

/**
* 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.
*
* @param time the create time as milliseconds since Jan 1, 1970
*/
    public void setCreateTime( long time ) throws SmbException {
        if( getUncPath0().length() == 1 ) {
            throw new SmbException( "Invalid operation for workgroups, servers, or shares" );
        }

        setPathInformation( 0, time, 0L );
    }
/**
* 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.
*
* @param time the last modified time as milliseconds since Jan 1, 1970
*/
    public void setLastModified( long time ) throws SmbException {
        if( getUncPath0().length() == 1 ) {
            throw new SmbException( "Invalid operation for workgroups, servers, or shares" );
        }

        setPathInformation( 0, 0L, time );
    }

/**
* 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.
*
* @return the <tt>ATTR_*</tt> attributes associated with this file
* @throw SmbException
*/
    public int getAttributes() throws SmbException {
        if( getUncPath0().length() == 1 ) {
            return 0;
        }
        exists();
        return attributes & ATTR_GET_MASK;
    }

/**
* 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.
* @throw SmbException
*/
    public void setAttributes( int attrs ) throws SmbException {
        if( getUncPath0().length() == 1 ) {
            throw new SmbException( "Invalid operation for workgroups, servers, or shares" );
        }

        setPathInformation( attrs & ATTR_SET_MASK, 0L, 0L );
    }

/**
* Make this file read-only. This is shorthand for <tt>setAttributes(
* getAttributes() | ATTR_READ_ONLY )</tt>.
*
* @throw SmbException
*/
    public void setReadOnly() throws SmbException {
        setAttributes( getAttributes() | ATTR_READONLY );
    }

/**
* Turn off the read-only attribute of this file. This is shorthand for
* <tt>setAttributes( getAttributes() & ~ATTR_READONLY )</tt>.
*
* @throw SmbException
*/
    public void setReadWrite() throws SmbException {
        setAttributes( getAttributes() & ~ATTR_READONLY );
    }

/**
* Returns a {@link java.net.URL} 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>).
*
* @depricated Use getURL() instead
* @return A new <code>{@link java.net.URL}</code> for this <code>SmbFile</code>
* @throw MalformedURLException
*/
    public URL toURL() throws MalformedURLException {
        return url;
    }

/**
* 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.
*
* @return  A hashcode for this abstract file
* @throw SmbException
*/

    public int hashCode() {
        int hash;
        try {
            hash = getAddress().hashCode();
        } catch( UnknownHostException uhe ) {
            hash = getServer().toUpperCase().hashCode();
        }
        getUncPath0();
        return hash + canon.toUpperCase().hashCode();
    }

/**
* 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>
*
* @param   obj Another <code>SmbFile</code> object to compare for equality
* @return  <code>true</code> if the two objects refer to the same SMB resource
*          and <code>false</code> otherwise
* @throw SmbException
*/

    public boolean equals( Object obj ) {
        return obj instanceof SmbFile && obj.hashCode() == hashCode();
    }

/**
* 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>.
*
* @return  The original URL representation of this SMB resource
* @throw SmbException
*/

    public String toString() {
        return url.toString();
    }

/* URLConnection implementation */
/**
* This URLConnection method just returns the result of <tt>length()</tt>.
*
* @return the length of this file or 0 if it refers to a directory
*/

    public int getContentLength() {
        try {
            return (int)(length() & 0xFFFFFFFFL);
        } catch( SmbException se ) {
        }
        return 0;
    }

/**
* This URLConnection method just returns the result of <tt>lastModified</tt>.
*
* @return the last modified data as milliseconds since Jan 1, 1970
*/
    public long getDate() {
        try {
            return lastModified();
        } catch( SmbException se ) {
        }
        return 0L;
    }

/**
* This URLConnection method just returns the result of <tt>lastModified</tt>.
*
* @return the last modified data as milliseconds since Jan 1, 1970
*/
    public long getLastModified() {
        try {
            return lastModified();
        } catch( SmbException se ) {
        }
        return 0L;
    }

/**
* This URLConnection method just returns a new <tt>SmbFileInputStream</tt> created with this file.
*
* @throw IOException thrown by <tt>SmbFileInputStream</tt> constructor
*/
    public InputStream getInputStream() throws IOException {
        return new SmbFileInputStream( this );
    }
}
TOP

Related Classes of com.knowgate.jcifs.smb.SmbFile$WriterThread

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.