jcifs.smb.SmbFile Maven / Gradle / Ivy
Show all versions of jcifs-krb5-jdk7 Show documentation
/* jcifs smb client library in Java
* Copyright (C) 2000 "Michael B. Allen"
*
* 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 jcifs.Config;
import jcifs.UniAddress;
import jcifs.dcerpc.DcerpcHandle;
import jcifs.dcerpc.msrpc.MsrpcDfsRootEnum;
import jcifs.dcerpc.msrpc.MsrpcShareEnum;
import jcifs.dcerpc.msrpc.MsrpcShareGetInfo;
import jcifs.netbios.NbtAddress;
import jcifs.util.LogStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.net.UnknownHostException;
import java.security.Principal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
/**
* This class represents a resource on an SMB network. Mainly these
* resources are files and directories however an SmbFile
* may also refer to servers and workgroups. If the resource is a file or
* directory the methods of SmbFile
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:
*
*
* smb://[[[domain;]username[:password]@]server[:port]/[[share/[dir/]file]]][?[param=value[param2=value2[...]]]
*
*
* This example:
*
*
* smb://storage15/public/foo.txt
*
*
* would reference the file foo.txt
in the share
* public
on the server storage15
. In addition
* to referencing files and directories, jCIFS can also address servers,
* and workgroups.
*
* Important: all SMB URLs that represent
* workgroups, servers, shares, or directories require a trailing slash '/'.
*
*
* When using the java.net.URL class with
* 'smb://' URLs it is necessary to first call the static
* jcifs.Config.registerSmbURLHandler(); method. This is required
* to register the SMB protocol handler.
*
* The userinfo component of the SMB URL (domain;user:pass) 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.
*
* 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 Setting Name
* Resolution Properties). 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 Setting
* JCIFS Properties). Here are some examples of SMB URLs with brief
* descriptions of what they do:
*
*
[1] This URL scheme is based largely on the SMB
* Filesharing URL Scheme IETF draft.
*
*
*
* SMB URL Examples
* URL Description
*
* smb://users-nyc;miallen:mypass@angus/tmp/
* This URL references a share called tmp
on the server
* angus
as user miallen
who's password is
* mypass
.
*
*
*
* smb://Administrator:P%40ss@msmith1/c/WINDOWS/Desktop/foo.txt
* A relativly sophisticated example that references a file
* msmith1
's desktop as user Administrator
. Notice the '@' is URL encoded with the '%40' hexcode escape.
*
*
* smb://angus/
* This references only a server. The behavior of some methods is different
* in this context(e.g. you cannot delete
a server) however
* as you might expect the list
method will list the available
* shares on this server.
*
*
* smb://myworkgroup/
* This syntactically is identical to the above example. However if
* myworkgroup
happends to be a workgroup(which is indeed
* suggested by the name) the list
method will return
* a list of servers that have registered themselves as members of
* myworkgroup
.
*
*
* smb://
* Just as smb://server/
lists shares and
* smb://workgroup/
lists servers, the smb://
* URL lists all available workgroups on a netbios LAN. Again,
* in this context many methods are not valid and return default
* values(e.g. isHidden
will always return false).
*
*
* smb://angus.foo.net/d/jcifs/pipes.doc
* The server name may also be a DNS name as it is in this example. See
* Setting Name Resolution Properties
* for details.
*
*
* smb://192.168.1.15/ADMIN$/
* The server name may also be an IP address. See Setting Name Resolution Properties
* for details.
*
*
*
* smb://domain;username:password@server/share/path/to/file.txt
* A prototypical example that uses all the fields.
*
*
* smb://myworkgroup/angus/ <-- ILLEGAL
* Despite the hierarchial relationship between workgroups, servers, and
* filesystems this example is not valid.
*
*
*
* smb://server/share/path/to/dir <-- ILLEGAL
* URLs that represent workgroups, servers, shares, or directories require a trailing slash '/'.
*
*
*
* smb://MYGROUP/?SERVER=192.168.10.15
* SMB URLs support some query string parameters. In this example
* the SERVER
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 MYGROUP
.
*
*
*
*
* 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
* java.io.File
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.
*
*
*
*
* Examples Of SMB URLs When Augmented With A Second Constructor Parameter
*
* First Parameter Second Parameter Result
*
*
* smb://host/share/a/b/
*
* c/d/
*
* smb://host/share/a/b/c/d/
*
*
*
* smb://host/share/foo/bar/
*
* /share2/zig/zag
*
* smb://host/share2/zig/zag
*
*
*
* smb://host/share/foo/bar/
*
* ../zip/
*
* smb://host/share/foo/zip/
*
*
*
* smb://host/share/zig/zag
*
* smb://foo/bar/
*
* smb://foo/bar/
*
*
*
* smb://host/share/foo/
*
* ../.././.././../foo/
*
* smb://host/foo/
*
*
*
* smb://host/share/zig/zag
*
* /
*
* smb://host/
*
*
*
* smb://server/
*
* ../
*
* smb://server/
*
*
*
* smb://
*
* myworkgroup/
*
* smb://myworkgroup/
*
*
*
* smb://myworkgroup/
*
* angus/
*
* smb://myworkgroup/angus/ <-- ILLEGAL
(But if you first create an SmbFile with 'smb://workgroup/' and use and use it as the first parameter to a constructor that accepts it with a second String parameter jCIFS will factor out the 'workgroup'.)
*
*
*
*
* Instances of the SmbFile
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 shareAccess 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 shareAccess 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 shareAccess 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 shareAccess 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 getAttributes() or set
* with setAttributes() will be read-only
*/
public static final int ATTR_READONLY = 0x01;
/**
* A file with this bit on as returned by getAttributes() or set
* with setAttributes() will be hidden
*/
public static final int ATTR_HIDDEN = 0x02;
/**
* A file with this bit on as returned by getAttributes() or set
* with setAttributes() will be a system file
*/
public static final int ATTR_SYSTEM = 0x04;
/**
* A file with this bit on as returned by getAttributes() is
* a volume
*/
public static final int ATTR_VOLUME = 0x08;
/**
* A file with this bit on as returned by getAttributes() is
* a directory
*/
public static final int ATTR_DIRECTORY = 0x10;
/**
* A file with this bit on as returned by getAttributes() or set
* with setAttributes() 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 SmbFile
* represents is a regular file or directory.
*/
public static final int TYPE_FILESYSTEM = 0x01;
/**
* Returned by {@link #getType()} if the resource this SmbFile
* represents is a workgroup.
*/
public static final int TYPE_WORKGROUP = 0x02;
/**
* Returned by {@link #getType()} if the resource this SmbFile
* represents is a server.
*/
public static final int TYPE_SERVER = 0x04;
/**
* Returned by {@link #getType()} if the resource this SmbFile
* represents is a share.
*/
public static final int TYPE_SHARE = 0x08;
/**
* Returned by {@link #getType()} if the resource this SmbFile
* represents is a named pipe.
*/
public static final int TYPE_NAMED_PIPE = 0x10;
/**
* Returned by {@link #getType()} if the resource this SmbFile
* represents is a printer.
*/
public static final int TYPE_PRINTER = 0x20;
/**
* Returned by {@link #getType()} if the resource this SmbFile
* 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;
/**
* 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 parent
and child
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 parent SmbFile
. See the description above for examples
* of using the second name
parameter.
*
* @param context A base SmbFile
* @param name A path string relative to the parent
paremeter
* @throws MalformedURLException
* If the parent
and child
parameters
* do not follow the prescribed syntax
* @throws UnknownHostException
* If the server or workgroup of the context 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 parent
. See the description above for examples of
* using the second chile
parameter.
*
* @param context A URL string
* @param name A path string relative to the context
paremeter
* @throws MalformedURLException
* If the context
and name
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 url
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
* shareAccess parameter controls what permissions other
* clients have when trying to access the same file while this instance
* is still open. This value is either FILE_NO_SHARE or any
* combination of FILE_SHARE_READ, FILE_SHARE_WRITE,
* and FILE_SHARE_DELETE 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 url
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 context
. See the description above for examples of
* using the second name
parameter.
*
* @param context A URL string
* @param name A path string relative to the context
paremeter
* @param auth The credentials the client should use for authentication
* @throws MalformedURLException
* If the context
and name
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 context
. See the description above for examples of
* using the second name
parameter. The shareAccess
* parameter controls what permissions other clients have when trying
* to access the same file while this instance is still open. This
* value is either FILE_NO_SHARE or any combination
* of FILE_SHARE_READ, FILE_SHARE_WRITE, and
* FILE_SHARE_DELETE logically OR'd together.
*
* @param context A URL string
* @param name A path string relative to the context
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 context
and name
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 context
. See the description above for examples of
* using the second name
parameter. The shareAccess
* parameter controls what permissions other clients have when trying
* to access the same file while this instance is still open. This
* value is either FILE_NO_SHARE or any combination
* of FILE_SHARE_READ, FILE_SHARE_WRITE, and
* FILE_SHARE_DELETE logically OR'd together.
*
* @param context A base SmbFile
* @param name A path string relative to the context
file path
* @param shareAccess Specifies what access other clients have while this file is open.
* @throws MalformedURLException
* If the context
and name
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 URL 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 URL object and an
* NtlmPasswordAuthentication 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();
// >>SmbAuthenticator Fixed trusted domain issue.
DfsReferral dr = dfs.resolve(
tree.session.transport.tconHostName,
tree.share,
unc,
authenticator,
auth);
// DfsReferral dr = dfs.resolve(
// tree.session.transport.tconHostName,
// tree.share,
// unc,
// auth);
// <= 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();
// >>SmbAuthenticator
// tree = trans.getSmbSession( auth ).getSmbTree( dr.share, service );
tree = trans.getSmbSession( authenticator, auth ).getSmbTree( dr.share, service );
// SmbAuthenticator<<
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 {
trans = SmbTransport.getSmbTransport(addr, url.getPort());
// >>SmbAuthenticator
// tree = trans.getSmbSession(auth).getSmbTree(share, null);
tree = trans.getSmbSession(authenticator, auth).getSmbTree(share, null);
// SmbAuthenticator<<
}
String hostName = getServerWithDfs();
tree.inDomainDfs = dfs.resolve(hostName, tree.share, null, authenticator,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
// >>SmbAuthenticator
// ssn = trans.getSmbSession(NtlmPasswordAuthentication.NULL);
ssn = trans.getSmbSession(authenticator, NtlmPasswordAuthentication.NULL);
// SmbAuthenticator<<
tree = ssn.getSmbTree(null, null);
tree.treeConnect(null, null);
} else if ((a = NtlmAuthenticator.requestNtlmPasswordAuthentication(
url.toString(), sae)) != null) {
auth = a;
// SmbAuthenticator<<
// ssn = trans.getSmbSession(auth);
ssn = trans.getSmbSession(authenticator, auth);
// SmbAuthenticator<<
tree = ssn.getSmbTree(share, null);
tree.inDomainDfs = dfs.resolve(hostName, tree.share, null, authenticator,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
* URLConnection implementation of connect().
*/
public void connect() throws IOException {
SmbTransport trans;
SmbSession ssn;
UniAddress addr;
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 NtlmPasswordAuthentication object used as
* credentials with this file or pipe. This can be used to retrieve the
* username for example:
*
* String username = f.getPrincipal().getName();
*
* The Principal object returned will never be null
* however the username can be null 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
* SmbFile
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 smb://
is also smb://
. If this
* SmbFile refers to a workgroup, server, share, or directory,
* the name will include a trailing slash '/' so that composing new
* SmbFiles will maintain the trailing slash requirement.
*
* @return The last component of the URL associated with this SMB
* resource or smb://
if the resource is smb://
* 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 smb://
* does not have a parent. In this case smb://
is returned.
*
* @return The parent directory of this SMB resource or
* smb://
if the resource refers to the root of the URL
* hierarchy which incedentally is also smb://
.
*/
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
* SmbFile
constructed with the result of this method will
* result in an SmbFile
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 SmbFile
constructed with the result of
* this method will result in an SmbFile
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 smb://
, smb://workgroup/
,
* and smb://server/
URLs which do not specify a share,
* null
will be returned.
*
* @return The share component or null
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
* SmbFile
references a workgroup, the name of the workgroup
* is returned. If this SmbFile
refers to the root of this
* SMB network hierarchy, null
is returned.
*
* @return The server or workgroup name or null
if this
* SmbFile
refers to the root smb://
resource.
*/
public String getServer() {
String str = url.getHost();
if( str.length() == 0 ) {
return null;
}
return str;
}
/**
* Returns type of of object this SmbFile represents.
* @return TYPE_FILESYSTEM, TYPE_WORKGROUP, TYPE_SERVER, TYPE_SHARE,
* TYPE_PRINTER, TYPE_NAMED_PIPE, or TYPE_COMM.
*/
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 SmbFile
refers to the root
* smb://
resource true
is always returned. If
* this SmbFile
is a traditional file or directory, it will
* be queried for on the specified server as expected.
*
* @return true
if the resource exists or is alive or
* false
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 SmbFile
represents can be
* read. Because any file, directory, or other resource can be read if it
* exists, this method simply calls the exists
method.
*
* @return true
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 SmbFile
represents
* exists and is not marked read-only. By default, resources are
* considered to be read-only and therefore for smb://
,
* smb://workgroup/
, and smb://server/
resources
* will be read-only.
*
* @return true
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 SmbFile
represents is a directory.
*
* @return true
if this SmbFile
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 SmbFile
represents is not a directory.
*
* @return true
if this SmbFile
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 IPC$
or C$
.
*
* @return true
if the SmbFile
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 SmbFile
falls within a DFS volume,
* this method will return the referral path to which it maps. Otherwise
* null
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 SmbFile
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 long
value
*/
public long createTime() throws SmbException {
if( getUncPath0().length() > 1 ) {
exists();
return createTime;
}
return 0L;
}
/**
* Retrieve the last time the file represented by this
* SmbFile
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 long
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;
*
*
* - files and directories contained within this resource if the
* resource is a normal disk file directory,
*
- all available NetBIOS workgroups or domains if this resource is
* the top level URL
smb://
,
* - all servers registered as members of a NetBIOS workgroup if this
* resource refers to a workgroup in a
smb://workgroup/
URL,
* - all browseable shares of a server including printers, IPC
* services, or disk volumes if this resource is a server URL in the form
*
smb://server/
,
* - or
null
if the resource cannot be resolved.
*
*
* @return A String[]
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 list()
* 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
* SmbFile
objects. This method is much more efficient than
* the regular list
method when querying attributes of each
* file in the result set.
*
* The list of SmbFile
s returned by this method will be;
*
*
* - files and directories contained within this resource if the
* resource is a normal disk file directory,
*
- all available NetBIOS workgroups or domains if this resource is
* the top level URL
smb://
,
* - all servers registered as members of a NetBIOS workgroup if this
* resource refers to a workgroup in a
smb://workgroup/
URL,
* - all browseable shares of a server including printers, IPC
* services, or disk volumes if this resource is a server URL in the form
*
smb://server/
,
* - or
null
if the resource cannot be resolved.
*
*
* @return An array of SmbFile
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.
*
* 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 or less.
*
* Wildcard expressions will not filter workgroup names or server names.
*
*
* 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
*
*
* @param wildcard a wildcard expression
* @throws SmbException
* @return An array of SmbFile
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 listFiles()
* method minus files filtered by the specified filename filter.
*
* @param filter a filter to exclude files from the results
* @return An array of SmbFile 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 listFiles()
* method minus filenames filtered by the specified filter.
*
* @param filter a file filter to exclude files from the results
* @return An array of SmbFile 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();
// >>SmbAuthenticator Fixed trusted domain issue.
if (dfs.isTrustedDomain(getServer(),authenticator, auth)) {
// if (dfs.isTrustedDomain(getServer(), auth)) {
// <= 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()) {
e = (FileEntry)iter.next();
String name = e.getName();
if (fnf != null && fnf.accept(this, name) == false)
continue;
if (name.length() > 0) {
// if !files we don't need to create SmbFiles here
SmbFile f = new SmbFile(this, name, e.getType(),
ATTR_READONLY | ATTR_DIRECTORY, 0L, 0L, 0L );
if (ff != null && ff.accept(f) == false)
continue;
if (files) {
list.add(f);
} else {
list.add(name);
}
}
}
}
FileEntry[] doDfsRootEnum() throws IOException {
MsrpcDfsRootEnum rpc;
DcerpcHandle handle = null;
FileEntry[] entries;
handle = DcerpcHandle.getHandle("ncacn_np:" +
getAddress().getHostAddress() +
"[\\PIPE\\netdfs]", auth);
try {
rpc = new MsrpcDfsRootEnum(getServer());
handle.sendrecv(rpc);
if (rpc.retval != 0)
throw new SmbException(rpc.retval, true);
return rpc.getEntries();
} finally {
try {
handle.close();
} catch(IOException ioe) {
if (log.level >= 4)
ioe.printStackTrace(log);
}
}
}
FileEntry[] doMsrpcShareEnum() throws IOException {
MsrpcShareEnum rpc;
DcerpcHandle handle;
rpc = new MsrpcShareEnum(url.getHost());
/* JCIFS will build a composite list of shares if the target host has
* multiple IP addresses such as when domain-based DFS is in play. Because
* of this, to ensure that we query each IP individually without re-resolving
* the hostname and getting a different IP, we must use the current addresses
* IP rather than just url.getHost() like we were using prior to 1.2.16.
*/
handle = DcerpcHandle.getHandle("ncacn_np:" +
getAddress().getHostAddress() +
"[\\PIPE\\srvsvc]", auth);
try {
handle.sendrecv(rpc);
if (rpc.retval != 0)
throw new SmbException(rpc.retval, true);
return rpc.getEntries();
} finally {
try {
handle.close();
} catch(IOException ioe) {
if (log.level >= 4)
ioe.printStackTrace(log);
}
}
}
FileEntry[] doNetShareEnum() throws SmbException {
SmbComTransaction req = new NetShareEnum();
SmbComTransactionResponse resp = new NetShareEnumResponse();
send(req, resp);
if (resp.status != SmbException.ERROR_SUCCESS)
throw new SmbException(resp.status, true);
return resp.results;
}
void doNetServerEnum(ArrayList list,
boolean files,
String wildcard,
int searchAttributes,
SmbFilenameFilter fnf,
SmbFileFilter ff) throws SmbException,
UnknownHostException,
MalformedURLException {
int listType = url.getHost().length() == 0 ? 0 : getType();
SmbComTransaction req;
SmbComTransactionResponse resp;
if (listType == 0) {
connect0();
req = new NetServerEnum2(tree.session.transport.server.oemDomainName,
NetServerEnum2.SV_TYPE_DOMAIN_ENUM );
resp = new NetServerEnum2Response();
} else if (listType == TYPE_WORKGROUP) {
req = new NetServerEnum2(url.getHost(), NetServerEnum2.SV_TYPE_ALL);
resp = new NetServerEnum2Response();
} else {
throw new SmbException( "The requested list operations is invalid: " + url.toString() );
}
boolean more;
do {
int n;
send(req, resp);
if (resp.status != SmbException.ERROR_SUCCESS &&
resp.status != SmbException.ERROR_MORE_DATA) {
throw new SmbException( resp.status, true );
}
more = resp.status == SmbException.ERROR_MORE_DATA;
n = more ? resp.numEntries - 1 : resp.numEntries;
for (int i = 0; i < n; i++) {
FileEntry e = resp.results[i];
String name = e.getName();
if (fnf != null && fnf.accept(this, name) == false)
continue;
if (name.length() > 0) {
// if !files we don't need to create SmbFiles here
SmbFile f = new SmbFile(this, name, e.getType(),
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 (getType() != TYPE_WORKGROUP) {
break;
}
req.subCommand = (byte)SmbComTransaction.NET_SERVER_ENUM3;
req.reset(0, ((NetServerEnum2Response)resp).lastName);
resp.reset();
} while(more);
}
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( log.level >= 3 )
log.println( "doFindFirstNext: " + req.path );
send( 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 ) {
if (name.equals(".") || name.equals(".."))
continue;
}
}
if( fnf != null && fnf.accept( this, name ) == false ) {
continue;
}
if( name.length() > 0 ) {
SmbFile f = new SmbFile( this, name, 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();
send( req, resp );
}
try {
send( new SmbComFindClose2( sid ), blank_resp() );
} catch (SmbException se) {
if( log.level >= 4 )
se.printStackTrace( log );
}
}
/**
* Changes the name of the file this SmbFile
represents to the name
* designated by the SmbFile
argument.
*
* Remember: SmbFile
s are immutible and therefore
* the path associated with this SmbFile
object will not
* change). To access the renamed file it is necessary to construct a
* new SmbFile.
*
* @param dest An SmbFile
that represents the new pathname
* @throws NullPointerException
* If the dest
argument is null
*/
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" );
}
resolveDfs(null);
dest.resolveDfs(null);
if (!tree.equals(dest.tree)) {
throw new SmbException( "Invalid operation for workgroups, servers, or shares" );
}
if( log.level >= 3 )
log.println( "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;
long off;
boolean ready;
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();
}
ready = false;
}
synchronized void write( byte[] b, int n, SmbFile dest, long off ) {
this.b = b;
this.n = n;
this.dest = dest;
this.off = off;
ready = false;
notify();
}
public void run() {
synchronized( this ) {
try {
for( ;; ) {
notify();
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 );
}
}
} 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;
String path = dest.getUncPath0();
if( path.length() > 1 ) {
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 {
long off;
try {
open( SmbFile.O_RDONLY, 0, ATTR_NORMAL, 0 );
try {
dest.open( SmbFile.O_CREAT | SmbFile.O_WRONLY | SmbFile.O_TRUNC,
FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES,
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,
FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES,
attributes, 0 );
} else {
throw sae;
}
}
i = 0;
off = 0L;
for( ;; ) {
req.setParam( fid, off, bsize );
resp.setParam( b[i], 0 );
send( req, resp );
synchronized( w ) {
if( w.e != null ) {
throw w.e;
}
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.send( new Trans2SetFileInformation(
dest.fid, attributes, createTime, lastModified ),
new Trans2SetFileInformationResponse() );
dest.close( 0L );
} catch( SmbException se ) {
if (ignoreCopyToException == false)
throw new SmbException("Failed to copy file from [" + this.toString() + "] to [" + dest.toString() + "]", se);
if( log.level > 1 )
se.printStackTrace( log );
} finally {
close();
}
}
}
/**
* This method will copy the file or directory represented by this
* SmbFile and it's sub-contents to the location specified by the
* dest 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.
*
* It is not possible (nor meaningful) to copy entire workgroups or
* servers.
*
* @param dest the destination file or directory
* @throws 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();
/* 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.
*/
resolveDfs(null);
/* It is invalid for the source path to be a child of the destination
* path or visa versa.
*/
try {
if (getAddress().equals( dest.getAddress() ) &&
canon.regionMatches( true, 0, dest.canon, 0,
Math.min( canon.length(), dest.canon.length() ))) {
throw new SmbException( "Source and destination paths overlap." );
}
} catch (UnknownHostException uhe) {
}
w = new WriterThread();
w.setDaemon( true );
w.start();
/* Downgrade one transport to the lower of the negotiated buffer sizes
* so we can just send whatever is received.
*/
SmbTransport t1 = tree.session.transport;
SmbTransport t2 = dest.tree.session.transport;
if( t1.snd_buf_size < t2.snd_buf_size ) {
t2.snd_buf_size = t1.snd_buf_size;
} else {
t1.snd_buf_size = t2.snd_buf_size;
}
bsize = Math.min( t1.rcv_buf_size - 70, t1.snd_buf_size - 70 );
b = new byte[2][bsize];
try {
copyTo0( dest, b, bsize, w, req, resp );
} finally {
w.write( null, -1, null, 0 );
}
}
/**
* This method will delete the file or directory specified by this
* SmbFile
. 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 {
exists();
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( log.level >= 3 )
log.println( "delete: " + fileName );
if(( attributes & ATTR_DIRECTORY ) != 0 ) {
/* Recursively delete directory contents
*/
try {
SmbFile[] l = listFiles( "*", ATTR_DIRECTORY | ATTR_HIDDEN | ATTR_SYSTEM, null, null );
for( int i = 0; i < l.length; i++ ) {
l[i].delete();
}
} catch( SmbException se ) {
/* Oracle FilesOnline version 9.0.4 doesn't send '.' and '..' so
* listFiles may generate undesireable "cannot find
* the file specified".
*/
if( se.getNtStatus() != SmbException.NT_STATUS_NO_SUCH_FILE ) {
throw se;
}
}
send( new SmbComDeleteDirectory( fileName ), blank_resp() );
} else {
send( new SmbComDelete( fileName ), blank_resp() );
}
attrExpiration = sizeExpiration = 0;
}
/**
* Returns the length of this SmbFile in bytes. If this object
* is a TYPE_SHARE the total capacity of the disk shared in
* bytes is returned. If this object is a directory or a type other than
* TYPE_SHARE, 0L is returned.
*
* @return The length of the file in bytes or 0 if this
* SmbFile
is not a file.
* @throws 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 );
send( 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 TYPE_SHARE or TYPE_FILESYSTEM 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 ) {
int level = Trans2QueryFSInformationResponse.SMB_FS_FULL_SIZE_INFORMATION;
try {
return queryFSInformation(level);
} catch( SmbException ex ) {
switch (ex.getNtStatus()) {
case NtStatus.NT_STATUS_INVALID_INFO_CLASS:
case NtStatus.NT_STATUS_UNSUCCESSFUL: // NetApp Filer
// SMB_FS_FULL_SIZE_INFORMATION not supported by the server.
level = Trans2QueryFSInformationResponse.SMB_INFO_ALLOCATION;
return queryFSInformation(level);
}
throw ex;
}
}
return 0L;
}
private long queryFSInformation( int level ) throws SmbException {
Trans2QueryFSInformationResponse response;
response = new Trans2QueryFSInformationResponse( level );
send( new Trans2QueryFSInformation( level ), response );
if( type == TYPE_SHARE ) {
size = response.info.getCapacity();
sizeExpiration = System.currentTimeMillis() + attrExpirationPeriod;
}
return response.info.getFree();
}
/**
* Creates a directory with the path specified by this
* SmbFile
. For this method to be successful, the target
* must not already exist. This method will fail when
* used with smb://
, smb://workgroup/
,
* smb://server/
, or smb://server/share/
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( log.level >= 3 )
log.println( "mkdir: " + path );
send( new SmbComCreateDirectory( path ), blank_resp() );
attrExpiration = sizeExpiration = 0;
}
/**
* Creates a directory with the path specified by this SmbFile
* and any parent directories that do not exist. This method will fail
* when used with smb://
, smb://workgroup/
,
* smb://server/
, or smb://server/share/
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( getParent(), auth );
} 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, 0, ATTR_NORMAL, 0 ), 0L );
}
void setPathInformation( int attrs, long ctime, long mtime ) throws SmbException {
int f, dir;
exists();
dir = attributes & ATTR_DIRECTORY;
f = open0( O_RDONLY, FILE_WRITE_ATTRIBUTES,
dir, dir != 0 ? 0x0001 : 0x0040 );
send( new Trans2SetFileInformation( f, attrs | dir, 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
* createTime() method.
*
* 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
* lastModified(), getLastModified(), and getDate() methods.
*
* 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 ATTR_* constants to determine
* if they are set or unset. The value returned is suitable for use with
* the setAttributes() method.
*
* @return the ATTR_* attributes associated with this file
* @throws 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 ATTR_* constants. Setting the
* value returned by getAttributes will result in both files
* having the same attributes.
* @throws 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 setAttributes(
* getAttributes() | ATTR_READ_ONLY ).
*
* @throws SmbException
*/
public void setReadOnly() throws SmbException {
setAttributes( getAttributes() | ATTR_READONLY );
}
/**
* Turn off the read-only attribute of this file. This is shorthand for
* setAttributes( getAttributes() & ~ATTR_READONLY ).
*
* @throws SmbException
*/
public void setReadWrite() throws SmbException {
setAttributes( getAttributes() & ~ATTR_READONLY );
}
/**
* Returns a {@link java.net.URL} for this SmbFile
. The
* URL
may be used as any other URL
might to
* access an SMB resource. Currently only retrieving data and information
* is supported (i.e. no doOutput).
*
* @deprecated Use getURL() instead
* @return A new {@link java.net.URL}
for this SmbFile
* @throws 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
* SmbFile
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
* @throws SmbException
*/
public int hashCode() {
int hash;
try {
hash = getAddress().hashCode();
} catch( UnknownHostException uhe ) {
hash = getServer().toUpperCase().hashCode();
}
getUncPath0();
return hash + canon.toUpperCase().hashCode();
}
protected boolean pathNamesPossiblyEqual(String path1, String path2) {
int p1, p2, l1, l2;
// if unsure return this method returns true
p1 = path1.lastIndexOf('/');
p2 = path2.lastIndexOf('/');
l1 = path1.length() - p1;
l2 = path2.length() - p2;
// anything with dots voids comparison
if (l1 > 1 && path1.charAt(p1 + 1) == '.')
return true;
if (l2 > 1 && path2.charAt(p2 + 1) == '.')
return true;
return l1 == l2 && path1.regionMatches(true, p1, path2, p2, l1);
}
/**
* Tests to see if two SmbFile
objects are equal. Two
* SmbFile objects are equal when they reference the same SMB
* resource. More specifically, two SmbFile
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.
*
* For example, assuming the server angus
resolves to the
* 192.168.1.15
IP address, the below URLs would result in
* SmbFile
s that are equal.
*
*
* smb://192.168.1.15/share/DIR/foo.txt
* smb://angus/share/data/../dir/foo.txt
*
*
* @param obj Another SmbFile
object to compare for equality
* @return true
if the two objects refer to the same SMB resource
* and false
otherwise
* @throws SmbException
*/
public boolean equals( Object obj ) {
if (obj instanceof SmbFile) {
SmbFile f = (SmbFile)obj;
boolean ret;
if (this == f)
return true;
/* If uncertain, pathNamesPossiblyEqual returns true.
* Comparing canonical paths is definitive.
*/
if (pathNamesPossiblyEqual(url.getPath(), f.url.getPath())) {
getUncPath0();
f.getUncPath0();
if (canon.equalsIgnoreCase(f.canon)) {
try {
ret = getAddress().equals(f.getAddress());
} catch( UnknownHostException uhe ) {
ret = getServer().equalsIgnoreCase(f.getServer());
}
return ret;
}
}
}
return false;
}
/*
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 SmbFile
.
* This method will return the same value
* as getPath
.
*
* @return The original URL representation of this SMB resource
* @throws SmbException
*/
public String toString() {
return url.toString();
}
/* URLConnection implementation */
/**
* This URLConnection method just returns the result of length().
*
* @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 lastModified.
*
* @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 lastModified.
*
* @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 SmbFileInputStream created with this file.
*
* @throws IOException thrown by SmbFileInputStream constructor
*/
public InputStream getInputStream() throws IOException {
return new SmbFileInputStream( this );
}
/**
* This URLConnection method just returns a new SmbFileOutputStream created with this file.
*
* @throws IOException thrown by SmbFileOutputStream constructor
*/
public OutputStream getOutputStream() throws IOException {
return new SmbFileOutputStream( this );
}
private void processAces(ACE[] aces, boolean resolveSids) throws IOException {
String server = getServerWithDfs();
int ai;
if (resolveSids) {
SID[] sids = new SID[aces.length];
String[] names = null;
for (ai = 0; ai < aces.length; ai++) {
sids[ai] = aces[ai].sid;
}
for (int off = 0; off < sids.length; off += 64) {
int len = sids.length - off;
if (len > 64)
len = 64;
SID.resolveSids(server, auth, sids, off, len);
}
} else {
for (ai = 0; ai < aces.length; ai++) {
aces[ai].sid.origin_server = server;
aces[ai].sid.origin_auth = auth;
}
}
}
/**
* Return an array of Access Control Entry (ACE) objects representing
* the security descriptor associated with this file or directory.
* If no DACL is present, null is returned. If the DACL is empty, an array with 0 elements is returned.
* @param resolveSids Attempt to resolve the SIDs within each ACE form
* their numeric representation to their corresponding account names.
*/
public ACE[] getSecurity(boolean resolveSids) throws IOException {
int f;
ACE[] aces;
f = open0( O_RDONLY, READ_CONTROL, 0, isDirectory() ? 1 : 0 );
/*
* NtTrans Query Security Desc Request / Response
*/
NtTransQuerySecurityDesc request = new NtTransQuerySecurityDesc( f, 0x04 );
NtTransQuerySecurityDescResponse response = new NtTransQuerySecurityDescResponse();
try {
send( request, response );
} finally {
close( f, 0L );
}
aces = response.securityDescriptor.aces;
if (aces != null)
processAces(aces, resolveSids);
return aces;
}
/**
* Return an array of Access Control Entry (ACE) objects representing
* the share permissions on the share exporting this file or directory.
* If no DACL is present, null is returned. If the DACL is empty, an array with 0 elements is returned.
*
* Note that this is different from calling getSecurity on a
* share. There are actually two different ACLs for shares - the ACL on
* the share and the ACL on the folder being shared.
* Go to Computer Management
* > System Tools > Shared Folders > Shares and
* look at the Properties for a share. You will see two tabs - one
* for "Share Permissions" and another for "Security". These correspond to
* the ACLs returned by getShareSecurity and getSecurity
* respectively.
* @param resolveSids Attempt to resolve the SIDs within each ACE form
* their numeric representation to their corresponding account names.
*/
public ACE[] getShareSecurity(boolean resolveSids) throws IOException {
String p = url.getPath();
MsrpcShareGetInfo rpc;
DcerpcHandle handle;
ACE[] aces;
resolveDfs(null);
String server = getServerWithDfs();
rpc = new MsrpcShareGetInfo(server, tree.share);
handle = DcerpcHandle.getHandle("ncacn_np:" + server + "[\\PIPE\\srvsvc]", auth);
try {
handle.sendrecv(rpc);
if (rpc.retval != 0)
throw new SmbException(rpc.retval, true);
aces = rpc.getSecurity();
if (aces != null)
processAces(aces, resolveSids);
} finally {
try {
handle.close();
} catch(IOException ioe) {
if (log.level >= 1)
ioe.printStackTrace(log);
}
}
return aces;
}
/**
* Return an array of Access Control Entry (ACE) objects representing
* the security descriptor associated with this file or directory.
*
* Initially, the SIDs within each ACE will not be resolved however when
* getType(), getDomainName(), getAccountName(),
* or toString() is called, the names will attempt to be
* resolved. If the names cannot be resolved (e.g. due to temporary
* network failure), the said methods will return default values (usually
* S-X-Y-Z strings of fragments of).
*
* Alternatively getSecurity(true) may be used to resolve all
* SIDs together and detect network failures.
*/
public ACE[] getSecurity() throws IOException {
return getSecurity(false);
}
// >>SmbAuthenticator
private SmbExtendedAuthenticator authenticator = null;
/**
* Instance a SmbFile object with Extended Security Authentication by
* providing specified SmbAuthenticator. The authentication information in
* URL will be ignored.
*
* @param url
* @param authenticator
* @param shareAccess
* @throws MalformedURLException
*/
public SmbFile(String url, SmbExtendedAuthenticator authenticator,
int shareAccess) throws MalformedURLException {
this(url, (NtlmPasswordAuthentication) null, shareAccess);
this.authenticator = authenticator;
}
/**
* Instance a SmbFile object with Extended Security Authentication by
* providing specified SmbAuthenticator. The authentication information in
* URL will be ignored.
*
* @param url
* @param authenticator
* @throws MalformedURLException
*/
public SmbFile(String url, SmbExtendedAuthenticator authenticator)
throws MalformedURLException {
this(url, (NtlmPasswordAuthentication) null);
this.authenticator = authenticator;
}
/**
* Instance a SmbFile object with Extended Security Authentication by
* providing specified SmbAuthenticator. The authentication information in
* URL will be ignored.
*
* @param url
* @param authenticator
*/
public SmbFile(URL url, SmbExtendedAuthenticator authenticator) {
this(url, (NtlmPasswordAuthentication) null);
this.authenticator = authenticator;
}
// SmbAuthenticator<<
}