0001: /* jcifs smb client library in Java
0002: * Copyright (C) 2000 "Michael B. Allen" <jcifs at samba dot org>
0003: *
0004: * This library is free software; you can redistribute it and/or
0005: * modify it under the terms of the GNU Lesser General Public
0006: * License as published by the Free Software Foundation; either
0007: * version 2.1 of the License, or (at your option) any later version.
0008: *
0009: * This library is distributed in the hope that it will be useful,
0010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0012: * Lesser General Public License for more details.
0013: *
0014: * You should have received a copy of the GNU Lesser General Public
0015: * License along with this library; if not, write to the Free Software
0016: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
0017: */
0018:
0019: package jcifs.smb;
0020:
0021: import java.net.URLConnection;
0022: import java.net.URL;
0023: import java.net.MalformedURLException;
0024: import java.net.UnknownHostException;
0025: import java.io.IOException;
0026: import java.io.InputStream;
0027: import java.io.OutputStream;
0028: import java.util.ArrayList;
0029: import java.security.Principal;
0030: import jcifs.Config;
0031: import jcifs.util.LogStream;
0032: import jcifs.UniAddress;
0033: import jcifs.netbios.NbtAddress;
0034:
0035: import java.util.Date;
0036:
0037: /**
0038: * This class represents a resource on an SMB network. Mainly these
0039: * resources are files and directories however an <code>SmbFile</code>
0040: * may also refer to servers and workgroups. If the resource is a file or
0041: * directory the methods of <code>SmbFile</code> follow the behavior of
0042: * the well known {@link java.io.File} class. One fundamental difference
0043: * is the usage of a URL scheme [1] to specify the target file or
0044: * directory. SmbFile URLs have the following syntax:
0045: *
0046: * <blockquote><pre>
0047: * smb://[[[domain;]username[:password]@]server[:port]/[[share/[dir/]file]]][?[param=value[param2=value2[...]]]
0048: * </pre></blockquote>
0049: *
0050: * This example:
0051: *
0052: * <blockquote><pre>
0053: * smb://storage15/public/foo.txt
0054: * </pre></blockquote>
0055: *
0056: * would reference the file <code>foo.txt</code> in the share
0057: * <code>public</code> on the server <code>storage15</code>. In addition
0058: * to referencing files and directories, jCIFS can also address servers,
0059: * and workgroups.
0060: * <p>
0061: * <font color="#800000"><i>Important: all SMB URLs that represent
0062: * workgroups, servers, shares, or directories require a trailing slash '/'.
0063: * </i></font>
0064: * <p>
0065: * When using the <tt>java.net.URL</tt> class with
0066: * 'smb://' URLs it is necessary to first call the static
0067: * <tt>jcifs.Config.registerSmbURLHandler();</tt> method. This is required
0068: * to register the SMB protocol handler.
0069: * <p>
0070: * The userinfo component of the SMB URL (<tt>domain;user:pass</tt>) must
0071: * be URL encoded if it contains reserved characters. According to RFC 2396
0072: * these characters are non US-ASCII characters and most meta characters
0073: * however jCIFS will work correctly with anything but '@' which is used
0074: * to delimit the userinfo component from the server and '%' which is the
0075: * URL escape character itself.
0076: * <p>
0077: * The server
0078: * component may a traditional NetBIOS name, a DNS name, or IP
0079: * address. These name resolution mechanisms and their resolution order
0080: * can be changed (See <a href="../../../resolver.html">Setting Name
0081: * Resolution Properties</a>). The servername and path components are
0082: * not case sensitive but the domain, username, and password components
0083: * are. It is also likely that properties must be specified for jcifs
0084: * to function (See <a href="../../overview-summary.html#scp">Setting
0085: * JCIFS Properties</a>). Here are some examples of SMB URLs with brief
0086: * descriptions of what they do:
0087: *
0088: * <p>[1] This URL scheme is based largely on the <i>SMB
0089: * Filesharing URL Scheme</i> IETF draft.
0090: *
0091: * <p><table border="1" cellpadding="3" cellspacing="0" width="100%">
0092: * <tr bgcolor="#ccccff">
0093: * <td colspan="2"><b>SMB URL Examples</b></td>
0094: * <tr><td width="20%"><b>URL</b></td><td><b>Description</b></td></tr>
0095: *
0096: * <tr><td width="20%"><code>smb://users-nyc;miallen:mypass@angus/tmp/</code></td><td>
0097: * This URL references a share called <code>tmp</code> on the server
0098: * <code>angus</code> as user <code>miallen</code> who's password is
0099: * <code>mypass</code>.
0100: * </td></tr>
0101: *
0102: * <tr><td width="20%">
0103: * <code>smb://Administrator:P%40ss@msmith1/c/WINDOWS/Desktop/foo.txt</code></td><td>
0104: * A relativly sophisticated example that references a file
0105: * <code>msmith1</code>'s desktop as user <code>Administrator</code>. Notice the '@' is URL encoded with the '%40' hexcode escape.
0106: * </td></tr>
0107: *
0108: * <tr><td width="20%"><code>smb://angus/</code></td><td>
0109: * This references only a server. The behavior of some methods is different
0110: * in this context(e.g. you cannot <code>delete</code> a server) however
0111: * as you might expect the <code>list</code> method will list the available
0112: * shares on this server.
0113: * </td></tr>
0114: *
0115: * <tr><td width="20%"><code>smb://myworkgroup/</code></td><td>
0116: * This syntactically is identical to the above example. However if
0117: * <code>myworkgroup</code> happends to be a workgroup(which is indeed
0118: * suggested by the name) the <code>list</code> method will return
0119: * a list of servers that have registered themselves as members of
0120: * <code>myworkgroup</code>.
0121: * </td></tr>
0122: *
0123: * <tr><td width="20%"><code>smb://</code></td><td>
0124: * Just as <code>smb://server/</code> lists shares and
0125: * <code>smb://workgroup/</code> lists servers, the <code>smb://</code>
0126: * URL lists all available workgroups on a netbios LAN. Again,
0127: * in this context many methods are not valid and return default
0128: * values(e.g. <code>isHidden</code> and <code>renameTo</code> will always
0129: * return false).
0130: * </td></tr>
0131: *
0132: * <tr><td width="20%"><code>smb://angus.foo.net/d/jcifs/pipes.doc</code></td><td>
0133: * The server name may also be a DNS name as it is in this example. See
0134: * <a href="../../../resolver.html">Setting Name Resolution Properties</a>
0135: * for details.
0136: * </td></tr>
0137: *
0138: * <tr><td width="20%"><code>smb://192.168.1.15/ADMIN$/</code></td><td>
0139: * The server name may also be an IP address. See <a
0140: * href="../../../resolver.html">Setting Name Resolution Properties</a>
0141: * for details.
0142: * </td></tr>
0143: *
0144: * <tr><td width="20%">
0145: * <code>smb://domain;username:password@server/share/path/to/file.txt</code></td><td>
0146: * A prototypical example that uses all the fields.
0147: * </td></tr>
0148: *
0149: * <tr><td width="20%"><code>smb://myworkgroup/angus/ <-- ILLEGAL </code></td><td>
0150: * Despite the hierarchial relationship between workgroups, servers, and
0151: * filesystems this example is not valid.
0152: * </td></tr>
0153: *
0154: * <tr><td width="20%">
0155: * <code>smb://server/share/path/to/dir <-- ILLEGAL </code></td><td>
0156: * URLs that represent workgroups, servers, shares, or directories require a trailing slash '/'.
0157: * </td></tr>
0158: *
0159: * <tr><td width="20%">
0160: * <code>smb://MYGROUP/?SERVER=192.168.10.15</code></td><td>
0161: * SMB URLs support some query string parameters. In this example
0162: * the <code>SERVER</code> parameter is used to override the
0163: * server name service lookup to contact the server 192.168.10.15
0164: * (presumably known to be a master
0165: * browser) for the server list in workgroup <code>MYGROUP</code>.
0166: * </td></tr>
0167: *
0168: * </table>
0169: *
0170: * <p>A second constructor argument may be specified to augment the URL
0171: * for better programmatic control when processing many files under
0172: * a common base. This is slightly different from the corresponding
0173: * <code>java.io.File</code> usage; a '/' at the beginning of the second
0174: * parameter will still use the server component of the first parameter. The
0175: * examples below illustrate the resulting URLs when this second contructor
0176: * argument is used.
0177: *
0178: * <p><table border="1" cellpadding="3" cellspacing="0" width="100%">
0179: * <tr bgcolor="#ccccff">
0180: * <td colspan="3">
0181: * <b>Examples Of SMB URLs When Augmented With A Second Constructor Parameter</b></td>
0182: * <tr><td width="20%">
0183: * <b>First Parameter</b></td><td><b>Second Parameter</b></td><td><b>Result</b></td></tr>
0184: *
0185: * <tr><td width="20%"><code>
0186: * smb://host/share/a/b/
0187: * </code></td><td width="20%"><code>
0188: * c/d/
0189: * </code></td><td><code>
0190: * smb://host/share/a/b/c/d/
0191: * </code></td></tr>
0192: *
0193: * <tr><td width="20%"><code>
0194: * smb://host/share/foo/bar/
0195: * </code></td><td width="20%"><code>
0196: * /share2/zig/zag
0197: * </code></td><td><code>
0198: * smb://host/share2/zig/zag
0199: * </code></td></tr>
0200: *
0201: * <tr><td width="20%"><code>
0202: * smb://host/share/foo/bar/
0203: * </code></td><td width="20%"><code>
0204: * ../zip/
0205: * </code></td><td><code>
0206: * smb://host/share/foo/zip/
0207: * </code></td></tr>
0208: *
0209: * <tr><td width="20%"><code>
0210: * smb://host/share/zig/zag
0211: * </code></td><td width="20%"><code>
0212: * smb://foo/bar/
0213: * </code></td><td><code>
0214: * smb://foo/bar/
0215: * </code></td></tr>
0216: *
0217: * <tr><td width="20%"><code>
0218: * smb://host/share/foo/
0219: * </code></td><td width="20%"><code>
0220: * ../.././.././../foo/
0221: * </code></td><td><code>
0222: * smb://host/foo/
0223: * </code></td></tr>
0224: *
0225: * <tr><td width="20%"><code>
0226: * smb://host/share/zig/zag
0227: * </code></td><td width="20%"><code>
0228: * /
0229: * </code></td><td><code>
0230: * smb://host/
0231: * </code></td></tr>
0232: *
0233: * <tr><td width="20%"><code>
0234: * smb://server/
0235: * </code></td><td width="20%"><code>
0236: * ../
0237: * </code></td><td><code>
0238: * smb://server/
0239: * </code></td></tr>
0240: *
0241: * <tr><td width="20%"><code>
0242: * smb://
0243: * </code></td><td width="20%"><code>
0244: * myworkgroup/
0245: * </code></td><td><code>
0246: * smb://myworkgroup/
0247: * </code></td></tr>
0248: *
0249: * <tr><td width="20%"><code>
0250: * smb://myworkgroup/
0251: * </code></td><td width="20%"><code>
0252: * angus/
0253: * </code></td><td><code>
0254: * smb://myworkgroup/angus/ <-- ILLEGAL<br>(But if you first create an <tt>SmbFile</tt> with 'smb://workgroup/' and use and use it as the first parameter to a constructor that accepts it with a second <tt>String</tt> parameter jCIFS will factor out the 'workgroup'.)
0255: * </code></td></tr>
0256: *
0257: * </table>
0258: *
0259: * <p>Instances of the <code>SmbFile</code> class are immutable; that is,
0260: * once created, the abstract pathname represented by an SmbFile object
0261: * will never change.
0262: *
0263: * @see java.io.File
0264: */
0265:
0266: public class SmbFile extends URLConnection {
0267:
0268: // these are shifted for use in flags
0269: static final int O_RDONLY = 0x010000;
0270: static final int O_WRONLY = 0x020000;
0271: static final int O_RDWR = 0x030000;
0272: static final int O_APPEND = 0x040000;
0273:
0274: // share access
0275: /**
0276: * When specified as the <tt>shareAccess</tt> constructor parameter,
0277: * other SMB clients (including other threads making calls into jCIFS)
0278: * will not be permitted to access the target file and will receive "The
0279: * file is being accessed by another process" message.
0280: */
0281: public static final int FILE_NO_SHARE = 0x00;
0282: /**
0283: * When specified as the <tt>shareAccess</tt> constructor parameter,
0284: * other SMB clients will be permitted to read from the target file while
0285: * this file is open. This constant may be logically OR'd with other share
0286: * access flags.
0287: */
0288: public static final int FILE_SHARE_READ = 0x01;
0289: /**
0290: * When specified as the <tt>shareAccess</tt> constructor parameter,
0291: * other SMB clients will be permitted to write to the target file while
0292: * this file is open. This constant may be logically OR'd with other share
0293: * access flags.
0294: */
0295: public static final int FILE_SHARE_WRITE = 0x02;
0296: /**
0297: * When specified as the <tt>shareAccess</tt> constructor parameter,
0298: * other SMB clients will be permitted to delete the target file while
0299: * this file is open. This constant may be logically OR'd with other share
0300: * access flags.
0301: */
0302: public static final int FILE_SHARE_DELETE = 0x04;
0303:
0304: // Open Function Encoding
0305: // create if the file does not exist
0306: static final int O_CREAT = 0x0010;
0307: // fail if the file exists
0308: static final int O_EXCL = 0x0001;
0309: // truncate if the file exists
0310: static final int O_TRUNC = 0x0002;
0311:
0312: // file attribute encoding
0313: /**
0314: * A file with this bit on as returned by <tt>getAttributes()</tt> or set
0315: * with <tt>setAttributes()</tt> will be read-only
0316: */
0317: public static final int ATTR_READONLY = 0x01;
0318: /**
0319: * A file with this bit on as returned by <tt>getAttributes()</tt> or set
0320: * with <tt>setAttributes()</tt> will be hidden
0321: */
0322: public static final int ATTR_HIDDEN = 0x02;
0323: /**
0324: * A file with this bit on as returned by <tt>getAttributes()</tt> or set
0325: * with <tt>setAttributes()</tt> will be a system file
0326: */
0327: public static final int ATTR_SYSTEM = 0x04;
0328: /**
0329: * A file with this bit on as returned by <tt>getAttributes()</tt> is
0330: * a volume
0331: */
0332: public static final int ATTR_VOLUME = 0x08;
0333: /**
0334: * A file with this bit on as returned by <tt>getAttributes()</tt> is
0335: * a directory
0336: */
0337: public static final int ATTR_DIRECTORY = 0x10;
0338: /**
0339: * A file with this bit on as returned by <tt>getAttributes()</tt> or set
0340: * with <tt>setAttributes()</tt> is an archived file
0341: */
0342: public static final int ATTR_ARCHIVE = 0x20;
0343:
0344: // extended file attribute encoding(others same as above)
0345: static final int ATTR_COMPRESSED = 0x800;
0346: static final int ATTR_NORMAL = 0x080;
0347: static final int ATTR_TEMPORARY = 0x100;
0348:
0349: static final int ATTR_GET_MASK = 0x7FFF;
0350: static final int ATTR_SET_MASK = 0x27;
0351:
0352: static final int DEFAULT_ATTR_EXPIRATION_PERIOD = 5000;
0353:
0354: static final int HASH_DOT = ".".hashCode();
0355: static final int HASH_DOT_DOT = "..".hashCode();
0356:
0357: static LogStream log = LogStream.getInstance();
0358: static long attrExpirationPeriod;
0359:
0360: static {
0361: try {
0362: Class.forName("jcifs.Config");
0363: } catch (ClassNotFoundException cnfe) {
0364: cnfe.printStackTrace();
0365: }
0366: attrExpirationPeriod = Config.getLong(
0367: "jcifs.smb.client.attrExpirationPeriod",
0368: DEFAULT_ATTR_EXPIRATION_PERIOD);
0369: }
0370:
0371: /**
0372: * Returned by {@link #getType()} if the resource this <tt>SmbFile</tt>
0373: * represents is a regular file or directory.
0374: */
0375: public static final int TYPE_FILESYSTEM = 0x01;
0376: /**
0377: * Returned by {@link #getType()} if the resource this <tt>SmbFile</tt>
0378: * represents is a workgroup.
0379: */
0380: public static final int TYPE_WORKGROUP = 0x02;
0381: /**
0382: * Returned by {@link #getType()} if the resource this <tt>SmbFile</tt>
0383: * represents is a server.
0384: */
0385: public static final int TYPE_SERVER = 0x04;
0386: /**
0387: * Returned by {@link #getType()} if the resource this <tt>SmbFile</tt>
0388: * represents is a share.
0389: */
0390: public static final int TYPE_SHARE = 0x08;
0391: /**
0392: * Returned by {@link #getType()} if the resource this <tt>SmbFile</tt>
0393: * represents is a named pipe.
0394: */
0395: public static final int TYPE_NAMED_PIPE = 0x10;
0396: /**
0397: * Returned by {@link #getType()} if the resource this <tt>SmbFile</tt>
0398: * represents is a printer.
0399: */
0400: public static final int TYPE_PRINTER = 0x20;
0401: /**
0402: * Returned by {@link #getType()} if the resource this <tt>SmbFile</tt>
0403: * represents is a communications device.
0404: */
0405: public static final int TYPE_COMM = 0x40;
0406:
0407: private String canon; // Initially null; set by getUncPath; dir must end with '/'
0408: private String share; // Can be null
0409: private long createTime;
0410: private long lastModified;
0411: private int attributes;
0412: private long attrExpiration;
0413: private long size;
0414: private long sizeExpiration;
0415: private NtlmPasswordAuthentication auth; // Cannot be null
0416: private boolean isExists;
0417: private int shareAccess = FILE_SHARE_READ | FILE_SHARE_WRITE
0418: | FILE_SHARE_DELETE;
0419: private SmbComBlankResponse blank_resp = null;
0420: private DfsReferral dfsReferral = null; // Only used by getDfsPath()
0421:
0422: SmbTree tree = null; // Initially null; may be !tree.treeConnected
0423: String unc; // Initially null; set by getUncPath; never ends with '/'
0424: int fid; // Initially 0; set by open()
0425: int type;
0426: boolean opened;
0427:
0428: /**
0429: * Constructs an SmbFile representing a resource on an SMB network such as
0430: * a file or directory. See the description and examples of smb URLs above.
0431: *
0432: * @param url A URL string
0433: * @throws MalformedURLException
0434: * If the <code>parent</code> and <code>child</code> parameters
0435: * do not follow the prescribed syntax
0436: */
0437:
0438: public SmbFile(String url) throws MalformedURLException {
0439: this (new URL(null, url, Handler.SMB_HANDLER));
0440: }
0441:
0442: /**
0443: * Constructs an SmbFile representing a resource on an SMB network such
0444: * as a file or directory. The second parameter is a relative path from
0445: * the <code>parent SmbFile</code>. See the description above for examples
0446: * of using the second <code>name</code> parameter.
0447: *
0448: * @param context A base <code>SmbFile</code>
0449: * @param name A path string relative to the <code>parent</code> paremeter
0450: * @throws MalformedURLException
0451: * If the <code>parent</code> and <code>child</code> parameters
0452: * do not follow the prescribed syntax
0453: * @throws UnknownHostException
0454: * If the server or workgroup of the <tt>context</tt> file cannot be determined
0455: */
0456:
0457: public SmbFile(SmbFile context, String name)
0458: throws MalformedURLException, UnknownHostException {
0459: this (context.isWorkgroup0() ? new URL(null, "smb://" + name,
0460: Handler.SMB_HANDLER) : new URL(context.url, name,
0461: Handler.SMB_HANDLER), context.auth);
0462: }
0463:
0464: /**
0465: * Constructs an SmbFile representing a resource on an SMB network such
0466: * as a file or directory. The second parameter is a relative path from
0467: * the <code>parent</code>. See the description above for examples of
0468: * using the second <code>chile</code> parameter.
0469: *
0470: * @param context A URL string
0471: * @param name A path string relative to the <code>context</code> paremeter
0472: * @throws MalformedURLException
0473: * If the <code>context</code> and <code>name</code> parameters
0474: * do not follow the prescribed syntax
0475: */
0476:
0477: public SmbFile(String context, String name)
0478: throws MalformedURLException {
0479: this (new URL(new URL(null, context, Handler.SMB_HANDLER), name,
0480: Handler.SMB_HANDLER));
0481: }
0482:
0483: /**
0484: * Constructs an SmbFile representing a resource on an SMB network such
0485: * as a file or directory.
0486: *
0487: * @param url A URL string
0488: * @param auth The credentials the client should use for authentication
0489: * @throws MalformedURLException
0490: * If the <code>url</code> parameter does not follow the prescribed syntax
0491: */
0492: public SmbFile(String url, NtlmPasswordAuthentication auth)
0493: throws MalformedURLException {
0494: this (new URL(null, url, Handler.SMB_HANDLER), auth);
0495: }
0496:
0497: /**
0498: * Constructs an SmbFile representing a file on an SMB network. The
0499: * <tt>shareAccess</tt> parameter controls what permissions other
0500: * clients have when trying to access the same file while this instance
0501: * is still open. This value is either <tt>FILE_NO_SHARE</tt> or any
0502: * combination of <tt>FILE_SHARE_READ</tt>, <tt>FILE_SHARE_WRITE</tt>,
0503: * and <tt>FILE_SHARE_DELETE</tt> logically OR'd together.
0504: *
0505: * @param url A URL string
0506: * @param auth The credentials the client should use for authentication
0507: * @param shareAccess Specifies what access other clients have while this file is open.
0508: * @throws MalformedURLException
0509: * If the <code>url</code> parameter does not follow the prescribed syntax
0510: */
0511: public SmbFile(String url, NtlmPasswordAuthentication auth,
0512: int shareAccess) throws MalformedURLException {
0513: this (new URL(null, url, Handler.SMB_HANDLER), auth);
0514: if ((shareAccess & ~(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE)) != 0) {
0515: throw new RuntimeException("Illegal shareAccess parameter");
0516: }
0517: this .shareAccess = shareAccess;
0518: }
0519:
0520: /**
0521: * Constructs an SmbFile representing a resource on an SMB network such
0522: * as a file or directory. The second parameter is a relative path from
0523: * the <code>context</code>. See the description above for examples of
0524: * using the second <code>name</code> parameter.
0525: *
0526: * @param context A URL string
0527: * @param name A path string relative to the <code>context</code> paremeter
0528: * @param auth The credentials the client should use for authentication
0529: * @throws MalformedURLException
0530: * If the <code>context</code> and <code>name</code> parameters
0531: * do not follow the prescribed syntax
0532: */
0533: public SmbFile(String context, String name,
0534: NtlmPasswordAuthentication auth)
0535: throws MalformedURLException {
0536: this (new URL(new URL(null, context, Handler.SMB_HANDLER), name,
0537: Handler.SMB_HANDLER), auth);
0538: }
0539:
0540: /**
0541: * Constructs an SmbFile representing a resource on an SMB network such
0542: * as a file or directory. The second parameter is a relative path from
0543: * the <code>context</code>. See the description above for examples of
0544: * using the second <code>name</code> parameter. The <tt>shareAccess</tt>
0545: * parameter controls what permissions other clients have when trying
0546: * to access the same file while this instance is still open. This
0547: * value is either <tt>FILE_NO_SHARE</tt> or any combination
0548: * of <tt>FILE_SHARE_READ</tt>, <tt>FILE_SHARE_WRITE</tt>, and
0549: * <tt>FILE_SHARE_DELETE</tt> logically OR'd together.
0550: *
0551: * @param context A URL string
0552: * @param name A path string relative to the <code>context</code> paremeter
0553: * @param auth The credentials the client should use for authentication
0554: * @param shareAccess Specifies what access other clients have while this file is open.
0555: * @throws MalformedURLException
0556: * If the <code>context</code> and <code>name</code> parameters
0557: * do not follow the prescribed syntax
0558: */
0559: public SmbFile(String context, String name,
0560: NtlmPasswordAuthentication auth, int shareAccess)
0561: throws MalformedURLException {
0562: this (new URL(new URL(null, context, Handler.SMB_HANDLER), name,
0563: Handler.SMB_HANDLER), auth);
0564: if ((shareAccess & ~(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE)) != 0) {
0565: throw new RuntimeException("Illegal shareAccess parameter");
0566: }
0567: this .shareAccess = shareAccess;
0568: }
0569:
0570: /**
0571: * Constructs an SmbFile representing a resource on an SMB network such
0572: * as a file or directory. The second parameter is a relative path from
0573: * the <code>context</code>. See the description above for examples of
0574: * using the second <code>name</code> parameter. The <tt>shareAccess</tt>
0575: * parameter controls what permissions other clients have when trying
0576: * to access the same file while this instance is still open. This
0577: * value is either <tt>FILE_NO_SHARE</tt> or any combination
0578: * of <tt>FILE_SHARE_READ</tt>, <tt>FILE_SHARE_WRITE</tt>, and
0579: * <tt>FILE_SHARE_DELETE</tt> logically OR'd together.
0580: *
0581: * @param context A base <code>SmbFile</code>
0582: * @param name A path string relative to the <code>context</code> file path
0583: * @param shareAccess Specifies what access other clients have while this file is open.
0584: * @throws MalformedURLException
0585: * If the <code>context</code> and <code>name</code> parameters
0586: * do not follow the prescribed syntax
0587: */
0588: public SmbFile(SmbFile context, String name, int shareAccess)
0589: throws MalformedURLException, UnknownHostException {
0590: this (context.isWorkgroup0() ? new URL(null, "smb://" + name,
0591: Handler.SMB_HANDLER) : new URL(context.url, name,
0592: Handler.SMB_HANDLER), context.auth);
0593: if ((shareAccess & ~(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE)) != 0) {
0594: throw new RuntimeException("Illegal shareAccess parameter");
0595: }
0596: this .shareAccess = shareAccess;
0597: }
0598:
0599: /**
0600: * Constructs an SmbFile representing a resource on an SMB network such
0601: * as a file or directory from a <tt>URL</tt> object.
0602: *
0603: * @param url The URL of the target resource
0604: */
0605: public SmbFile(URL url) {
0606: this (url, new NtlmPasswordAuthentication(url.getUserInfo()));
0607: }
0608:
0609: /**
0610: * Constructs an SmbFile representing a resource on an SMB network such
0611: * as a file or directory from a <tt>URL</tt> object and an
0612: * <tt>NtlmPasswordAuthentication</tt> object.
0613: *
0614: * @param url The URL of the target resource
0615: * @param auth The credentials the client should use for authentication
0616: */
0617: public SmbFile(URL url, NtlmPasswordAuthentication auth) {
0618: super (url);
0619: this .auth = auth == null ? new NtlmPasswordAuthentication(url
0620: .getUserInfo()) : auth;
0621:
0622: getUncPath0();
0623: }
0624:
0625: SmbFile(SmbFile context, String name, int type, int attributes,
0626: long createTime, long lastModified, long size)
0627: throws MalformedURLException, UnknownHostException {
0628: this (context.isWorkgroup0() ? new URL(null, "smb://" + name
0629: + "/", Handler.SMB_HANDLER) : new URL(context.url, name
0630: + ((attributes & ATTR_DIRECTORY) > 0 ? "/" : "")));
0631:
0632: /* why was this removed before? DFS? copyTo? Am I going around in circles? */
0633: auth = context.auth;
0634:
0635: if (context.share != null) {
0636: this .tree = context.tree;
0637: }
0638: int last = name.length() - 1;
0639: if (name.charAt(last) == '/') {
0640: name = name.substring(0, last);
0641: }
0642: if (context.share == null) {
0643: this .unc = "\\";
0644: } else if (context.unc.equals("\\")) {
0645: this .unc = '\\' + name;
0646: } else {
0647: this .unc = context.unc + '\\' + name;
0648: }
0649: /* why? am I going around in circles?
0650: * this.type = type == TYPE_WORKGROUP ? 0 : type;
0651: */
0652: this .type = type;
0653: this .attributes = attributes;
0654: this .createTime = createTime;
0655: this .lastModified = lastModified;
0656: this .size = size;
0657: isExists = true;
0658:
0659: attrExpiration = sizeExpiration = System.currentTimeMillis()
0660: + attrExpirationPeriod;
0661: }
0662:
0663: private SmbComBlankResponse blank_resp() {
0664: if (blank_resp == null) {
0665: blank_resp = new SmbComBlankResponse();
0666: }
0667: return blank_resp;
0668: }
0669:
0670: void sendTransaction(SmbComTransaction request,
0671: SmbComTransactionResponse response) throws SmbException {
0672: for (;;) {
0673: connect0();
0674: if (tree.inDfs) {
0675: DfsReferral dr = tree.session.transport
0676: .lookupReferral(unc);
0677: if (dr != null) {
0678: UniAddress addr;
0679: SmbTransport trans;
0680:
0681: try {
0682: addr = UniAddress.getByName(dr.server);
0683: } catch (UnknownHostException uhe) {
0684: throw new SmbException(dr.server, uhe);
0685: }
0686:
0687: trans = SmbTransport.getSmbTransport(addr, 0);
0688: tree = trans.getSmbSession(auth).getSmbTree(
0689: dr.share, null);
0690: unc = dr.nodepath + unc.substring(dr.path.length());
0691: if (request.path.charAt(request.path.length() - 1) == '\\') {
0692: request.path = unc + '\\';
0693: } else {
0694: request.path = unc;
0695: }
0696: dfsReferral = dr; /* for getDfsPath */
0697: }
0698: }
0699: if (tree.inDfs) {
0700: request.flags2 |= ServerMessageBlock.FLAGS2_RESOLVE_PATHS_IN_DFS;
0701: } else {
0702: request.flags2 &= ~ServerMessageBlock.FLAGS2_RESOLVE_PATHS_IN_DFS;
0703: }
0704: try {
0705: tree.sendTransaction(request, response);
0706: break;
0707: } catch (DfsReferral dr) {
0708: if (dr.resolveHashes) {
0709: throw dr;
0710: }
0711: request.reset();
0712: }
0713: }
0714: }
0715:
0716: void send(ServerMessageBlock request, ServerMessageBlock response)
0717: throws SmbException {
0718: for (;;) {
0719: connect0();
0720: if (tree.inDfs) {
0721: DfsReferral dr = tree.session.transport
0722: .lookupReferral(unc);
0723: if (dr != null) {
0724: UniAddress addr;
0725: SmbTransport trans;
0726:
0727: try {
0728: addr = UniAddress.getByName(dr.server);
0729: } catch (UnknownHostException uhe) {
0730: throw new SmbException(dr.server, uhe);
0731: }
0732:
0733: trans = SmbTransport.getSmbTransport(addr, url
0734: .getPort());
0735: tree = trans.getSmbSession(auth).getSmbTree(
0736: dr.share, null);
0737: unc = request.path = dr.nodepath
0738: + unc.substring(dr.path.length());
0739: dfsReferral = dr; /* for getDfsPath */
0740: }
0741: request.flags2 |= ServerMessageBlock.FLAGS2_RESOLVE_PATHS_IN_DFS;
0742: } else {
0743: request.flags2 &= ~ServerMessageBlock.FLAGS2_RESOLVE_PATHS_IN_DFS;
0744: }
0745: try {
0746: tree.send(request, response);
0747: break;
0748: } catch (DfsReferral dr) {
0749: if (dr.resolveHashes) {
0750: throw dr;
0751: }
0752: }
0753: }
0754: }
0755:
0756: static String queryLookup(String query, String param) {
0757: char in[] = query.toCharArray();
0758: int i, ch, st, eq;
0759:
0760: st = eq = 0;
0761: for (i = 0; i < in.length; i++) {
0762: ch = in[i];
0763: if (ch == '&') {
0764: if (eq > st) {
0765: String p = new String(in, st, eq - st);
0766: if (p.equalsIgnoreCase(param)) {
0767: eq++;
0768: return new String(in, eq, i - eq);
0769: }
0770: }
0771: st = i + 1;
0772: } else if (ch == '=') {
0773: eq = i;
0774: }
0775: }
0776: if (eq > st) {
0777: String p = new String(in, st, eq - st);
0778: if (p.equalsIgnoreCase(param)) {
0779: eq++;
0780: return new String(in, eq, in.length - eq);
0781: }
0782: }
0783:
0784: return null;
0785: }
0786:
0787: UniAddress getAddress() throws UnknownHostException {
0788: String host = url.getHost();
0789: String path = url.getPath();
0790: String query = url.getQuery();
0791:
0792: if (query != null) {
0793: String server = queryLookup(query, "server");
0794: if (server != null && server.length() > 0) {
0795: return UniAddress.getByName(server);
0796: }
0797: }
0798:
0799: if (host.length() == 0) {
0800: try {
0801: NbtAddress addr = NbtAddress.getByName(
0802: NbtAddress.MASTER_BROWSER_NAME, 0x01, null);
0803: return UniAddress.getByName(addr.getHostAddress());
0804: } catch (UnknownHostException uhe) {
0805: if (NtlmPasswordAuthentication.DEFAULT_DOMAIN
0806: .equals("?")) {
0807: throw uhe;
0808: }
0809: return UniAddress
0810: .getByName(
0811: NtlmPasswordAuthentication.DEFAULT_DOMAIN,
0812: true);
0813: }
0814: } else if (path.length() == 0 || path.equals("/")) {
0815: return UniAddress.getByName(host, true);
0816: } else {
0817: return UniAddress.getByName(host);
0818: }
0819: }
0820:
0821: void connect0() throws SmbException {
0822: try {
0823: connect();
0824: } catch (UnknownHostException uhe) {
0825: throw new SmbException("Failed to connect to server", uhe);
0826: } catch (SmbException se) {
0827: throw se;
0828: } catch (IOException ioe) {
0829: throw new SmbException("Failed to connect to server", ioe);
0830: }
0831: }
0832:
0833: /**
0834: * It is not necessary to call this method directly. This is the
0835: * <tt>URLConnection</tt> implementation of <tt>connect()</tt>.
0836: */
0837: public void connect() throws IOException {
0838: SmbTransport trans;
0839: SmbSession ssn;
0840: UniAddress addr;
0841:
0842: if (isConnected()) {
0843: return;
0844: }
0845:
0846: getUncPath0();
0847: addr = getAddress();
0848:
0849: trans = SmbTransport.getSmbTransport(addr, url.getPort());
0850: ssn = trans.getSmbSession(auth);
0851: tree = ssn.getSmbTree(share, null);
0852:
0853: try {
0854: tree.treeConnect(null, null);
0855: } catch (SmbAuthException sae) {
0856: NtlmPasswordAuthentication a;
0857:
0858: if (share == null) { // IPC$ - try "anonymous" credentials
0859: ssn = trans
0860: .getSmbSession(NtlmPasswordAuthentication.NULL);
0861: tree = ssn.getSmbTree(null, null);
0862: tree.treeConnect(null, null);
0863: } else if ((a = NtlmAuthenticator
0864: .requestNtlmPasswordAuthentication(url.toString(),
0865: sae)) != null) {
0866: auth = a;
0867: ssn = trans.getSmbSession(auth);
0868: tree = ssn.getSmbTree(share, null);
0869: tree.treeConnect(null, null);
0870: } else {
0871: throw sae;
0872: }
0873: }
0874: }
0875:
0876: boolean isConnected() {
0877: return (connected = tree != null && tree.treeConnected);
0878: }
0879:
0880: int open0(int flags, int attrs, int options) throws SmbException {
0881: int f;
0882:
0883: connect0();
0884:
0885: if (log.level > 2)
0886: log.println("open0: " + unc);
0887:
0888: /*
0889: * NT Create AndX / Open AndX Request / Response
0890: */
0891:
0892: if (tree.session.transport
0893: .hasCapability(ServerMessageBlock.CAP_NT_SMBS)) {
0894: SmbComNTCreateAndXResponse response = new SmbComNTCreateAndXResponse();
0895: send(new SmbComNTCreateAndX(unc, flags, shareAccess, attrs,
0896: options, null), response);
0897: f = response.fid;
0898: attributes = response.extFileAttributes & ATTR_GET_MASK;
0899: attrExpiration = System.currentTimeMillis()
0900: + attrExpirationPeriod;
0901: isExists = true;
0902: } else {
0903: SmbComOpenAndXResponse response = new SmbComOpenAndXResponse();
0904: send(new SmbComOpenAndX(unc, flags, null), response);
0905: f = response.fid;
0906: }
0907:
0908: return f;
0909: }
0910:
0911: void open(int flags, int attrs, int options) throws SmbException {
0912: if (isOpen()) {
0913: return;
0914: }
0915: fid = open0(flags, attrs, options);
0916: opened = true;
0917: }
0918:
0919: boolean isOpen() {
0920: return opened && isConnected();
0921: }
0922:
0923: void close(int f, long lastWriteTime) throws SmbException {
0924:
0925: if (log.level > 2)
0926: log.println("close: " + f);
0927:
0928: /*
0929: * Close Request / Response
0930: */
0931:
0932: send(new SmbComClose(f, lastWriteTime), blank_resp());
0933: }
0934:
0935: void close(long lastWriteTime) throws SmbException {
0936: if (isOpen() == false) {
0937: return;
0938: }
0939: close(fid, lastWriteTime);
0940: opened = false;
0941: }
0942:
0943: void close() throws SmbException {
0944: close(0L);
0945: }
0946:
0947: /**
0948: * Returns the <tt>NtlmPasswordAuthentication</tt> object used as
0949: * credentials with this file or pipe. This can be used to retrieve the
0950: * username for example:
0951: * <tt>
0952: * String username = f.getPrincipal().getName();
0953: * </tt>
0954: * The <tt>Principal</tt> object returned will never be <tt>null</tt>
0955: * however the username can be <tt>null</tt> indication anonymous
0956: * credentials were used (e.g. some IPC$ services).
0957: */
0958:
0959: public Principal getPrincipal() {
0960: return auth;
0961: }
0962:
0963: /**
0964: * Returns the last component of the target URL. This will
0965: * effectively be the name of the file or directory represented by this
0966: * <code>SmbFile</code> or in the case of URLs that only specify a server
0967: * or workgroup, the server or workgroup will be returned. The name of
0968: * the root URL <code>smb://</code> is also <code>smb://</code>. If this
0969: * <tt>SmbFile</tt> refers to a workgroup, server, share, or directory,
0970: * the name will include a trailing slash '/' so that composing new
0971: * <tt>SmbFile</tt>s will maintain the trailing slash requirement.
0972: *
0973: * @return The last component of the URL associated with this SMB
0974: * resource or <code>smb://</code> if the resource is <code>smb://</code>
0975: * itself.
0976: */
0977:
0978: public String getName() {
0979: getUncPath0();
0980: if (canon.length() > 1) {
0981: int i = canon.length() - 2;
0982: while (canon.charAt(i) != '/') {
0983: i--;
0984: }
0985: return canon.substring(i + 1);
0986: } else if (share != null) {
0987: return share + '/';
0988: } else if (url.getHost().length() > 0) {
0989: return url.getHost() + '/';
0990: } else {
0991: return "smb://";
0992: }
0993: }
0994:
0995: /**
0996: * Everything but the last component of the URL representing this SMB
0997: * resource is effectivly it's parent. The root URL <code>smb://</code>
0998: * does not have a parent. In this case <code>smb://</code> is returned.
0999: *
1000: * @return The parent directory of this SMB resource or
1001: * <code>smb://</code> if the resource refers to the root of the URL
1002: * hierarchy which incedentally is also <code>smb://</code>.
1003: */
1004:
1005: public String getParent() {
1006: String str = url.getAuthority();
1007:
1008: if (str.length() > 0) {
1009: StringBuffer sb = new StringBuffer("smb://");
1010:
1011: sb.append(str);
1012:
1013: getUncPath0();
1014: if (canon.length() > 1) {
1015: sb.append(canon);
1016: } else {
1017: sb.append('/');
1018: }
1019:
1020: str = sb.toString();
1021:
1022: int i = str.length() - 2;
1023: while (str.charAt(i) != '/') {
1024: i--;
1025: }
1026:
1027: return str.substring(0, i + 1);
1028: }
1029:
1030: return "smb://";
1031: }
1032:
1033: /**
1034: * Returns the full uncanonicalized URL of this SMB resource. An
1035: * <code>SmbFile</code> constructed with the result of this method will
1036: * result in an <code>SmbFile</code> that is equal to the original.
1037: *
1038: * @return The uncanonicalized full URL of this SMB resource.
1039: */
1040:
1041: public String getPath() {
1042: return url.toString();
1043: }
1044:
1045: String getUncPath0() {
1046: if (unc == null) {
1047: char[] in = url.getPath().toCharArray();
1048: char[] out = new char[in.length];
1049: int length = in.length, i, o, state, s;
1050:
1051: /* The canonicalization routine
1052: */
1053: state = 0;
1054: o = 0;
1055: for (i = 0; i < length; i++) {
1056: switch (state) {
1057: case 0:
1058: if (in[i] != '/') {
1059: return null;
1060: }
1061: out[o++] = in[i];
1062: state = 1;
1063: break;
1064: case 1:
1065: if (in[i] == '/') {
1066: break;
1067: } else if (in[i] == '.'
1068: && ((i + 1) >= length || in[i + 1] == '/')) {
1069: i++;
1070: break;
1071: } else if ((i + 1) < length && in[i] == '.'
1072: && in[i + 1] == '.'
1073: && ((i + 2) >= length || in[i + 2] == '/')) {
1074: i += 2;
1075: if (o == 1)
1076: break;
1077: do {
1078: o--;
1079: } while (o > 1 && out[o - 1] != '/');
1080: break;
1081: }
1082: state = 2;
1083: case 2:
1084: if (in[i] == '/') {
1085: state = 1;
1086: }
1087: out[o++] = in[i];
1088: break;
1089: }
1090: }
1091:
1092: canon = new String(out, 0, o);
1093:
1094: if (o > 1) {
1095: o--;
1096: i = canon.indexOf('/', 1);
1097: if (i < 0) {
1098: share = canon.substring(1);
1099: unc = "\\";
1100: } else if (i == o) {
1101: share = canon.substring(1, i);
1102: unc = "\\";
1103: } else {
1104: share = canon.substring(1, i);
1105: unc = canon.substring(i, out[o] == '/' ? o : o + 1);
1106: unc = unc.replace('/', '\\');
1107: }
1108: } else {
1109: share = null;
1110: unc = "\\";
1111: }
1112: }
1113: return unc;
1114: }
1115:
1116: /**
1117: * Retuns the Windows UNC style path with backslashs intead of forward slashes.
1118: *
1119: * @return The UNC path.
1120: */
1121: public String getUncPath() {
1122: getUncPath0();
1123: if (share == null) {
1124: return "\\\\" + url.getHost();
1125: }
1126: return "\\\\" + url.getHost() + canon.replace('/', '\\');
1127: }
1128:
1129: /**
1130: * Returns the full URL of this SMB resource with '.' and '..' components
1131: * factored out. An <code>SmbFile</code> constructed with the result of
1132: * this method will result in an <code>SmbFile</code> that is equal to
1133: * the original.
1134: *
1135: * @return The canonicalized URL of this SMB resource.
1136: */
1137:
1138: public String getCanonicalPath() {
1139: String str = url.getAuthority();
1140: getUncPath0();
1141: if (str.length() > 0) {
1142: return "smb://" + url.getAuthority() + canon;
1143: }
1144: return "smb://";
1145: }
1146:
1147: /**
1148: * Retrieves the share associated with this SMB resource. In
1149: * the case of <code>smb://</code>, <code>smb://workgroup/</code>,
1150: * and <code>smb://server/</code> URLs which do not specify a share,
1151: * <code>null</code> will be returned.
1152: *
1153: * @return The share component or <code>null</code> if there is no share
1154: */
1155:
1156: public String getShare() {
1157: return share;
1158: }
1159:
1160: /**
1161: * Retrieve the hostname of the server for this SMB resource. If this
1162: * <code>SmbFile</code> references a workgroup, the name of the workgroup
1163: * is returned. If this <code>SmbFile</code> refers to the root of this
1164: * SMB network hierarchy, <code>null</code> is returned.
1165: *
1166: * @return The server or workgroup name or <code>null</code> if this
1167: * <code>SmbFile</code> refers to the root <code>smb://</code> resource.
1168: */
1169:
1170: public String getServer() {
1171: String str = url.getHost();
1172: if (str.length() == 0) {
1173: return null;
1174: }
1175: return str;
1176: }
1177:
1178: /**
1179: * Returns type of of object this <tt>SmbFile</tt> represents.
1180: * @return <tt>TYPE_FILESYSTEM, TYPE_WORKGROUP, TYPE_SERVER, TYPE_SHARE,
1181: * TYPE_PRINTER, TYPE_NAMED_PIPE</tt>, or <tt>TYPE_COMM</tt>.
1182: */
1183: public int getType() throws SmbException {
1184: if (type == 0) {
1185: if (getUncPath0().length() > 1) {
1186: type = TYPE_FILESYSTEM;
1187: } else if (share != null) {
1188: // treeConnect good enough to test service type
1189: connect0();
1190: if (share.equals("IPC$")) {
1191: type = TYPE_NAMED_PIPE;
1192: } else if (tree.service.equals("LPT1:")) {
1193: type = TYPE_PRINTER;
1194: } else if (tree.service.equals("COMM")) {
1195: type = TYPE_COMM;
1196: } else {
1197: type = TYPE_SHARE;
1198: }
1199: } else if (url.getAuthority().length() == 0) {
1200: type = TYPE_WORKGROUP;
1201: } else {
1202: UniAddress addr;
1203: try {
1204: addr = getAddress();
1205: } catch (UnknownHostException uhe) {
1206: throw new SmbException(url.toString(), uhe);
1207: }
1208: if (addr.getAddress() instanceof NbtAddress) {
1209: int code = ((NbtAddress) addr.getAddress())
1210: .getNameType();
1211: if (code == 0x1d || code == 0x1b) {
1212: type = TYPE_WORKGROUP;
1213: return type;
1214: }
1215: }
1216: type = TYPE_SERVER;
1217: }
1218: }
1219: return type;
1220: }
1221:
1222: boolean isWorkgroup0() throws UnknownHostException {
1223: if (type == TYPE_WORKGROUP || url.getHost().length() == 0) {
1224: type = TYPE_WORKGROUP;
1225: return true;
1226: } else {
1227: getUncPath0();
1228: if (share == null) {
1229: UniAddress addr = getAddress();
1230: if (addr.getAddress() instanceof NbtAddress) {
1231: int code = ((NbtAddress) addr.getAddress())
1232: .getNameType();
1233: if (code == 0x1d || code == 0x1b) {
1234: type = TYPE_WORKGROUP;
1235: return true;
1236: }
1237: }
1238: type = TYPE_SERVER;
1239: }
1240: }
1241: return false;
1242: }
1243:
1244: Info queryPath(String path, int infoLevel) throws SmbException {
1245: connect0();
1246:
1247: if (log.level > 2)
1248: log.println("queryPath: " + path);
1249:
1250: /* normally we'd check the negotiatedCapabilities for CAP_NT_SMBS
1251: * however I can't seem to get a good last modified time from
1252: * SMB_COM_QUERY_INFORMATION so if NT_SMBs are requested
1253: * by the server than in this case that's what it will get
1254: * regardless of what jcifs.smb.client.useNTSmbs is set
1255: * to(overrides negotiatedCapabilities).
1256: */
1257:
1258: /* We really should do the referral before this in case
1259: * the redirected target has different capabilities. But
1260: * the way we have been doing that is to call exists() which
1261: * calls this method so another technique will be necessary
1262: * to support DFS referral _to_ Win95/98/ME.
1263: */
1264:
1265: if (tree.session.transport
1266: .hasCapability(ServerMessageBlock.CAP_NT_SMBS)) {
1267:
1268: /*
1269: * Trans2 Query Path Information Request / Response
1270: */
1271:
1272: Trans2QueryPathInformationResponse response = new Trans2QueryPathInformationResponse(
1273: infoLevel);
1274: sendTransaction(new Trans2QueryPathInformation(path,
1275: infoLevel), response);
1276:
1277: return response.info;
1278: } else {
1279:
1280: /*
1281: * Query Information Request / Response
1282: */
1283:
1284: SmbComQueryInformationResponse response = new SmbComQueryInformationResponse(
1285: tree.session.transport.server.serverTimeZone * 1000 * 60L);
1286: send(new SmbComQueryInformation(path), response);
1287: return response;
1288: }
1289: }
1290:
1291: /**
1292: * Tests to see if the SMB resource exists. If the resource refers
1293: * only to a server, this method determines if the server exists on the
1294: * network and is advertising SMB services. If this resource refers to
1295: * a workgroup, this method determines if the workgroup name is valid on
1296: * the local SMB network. If this <code>SmbFile</code> refers to the root
1297: * <code>smb://</code> resource <code>true</code> is always returned. If
1298: * this <code>SmbFile</code> is a traditional file or directory, it will
1299: * be queried for on the specified server as expected.
1300: *
1301: * @return <code>true</code> if the resource exists or is alive or
1302: * <code>false</code> otherwise
1303: */
1304:
1305: public boolean exists() throws SmbException {
1306:
1307: if (attrExpiration > System.currentTimeMillis()) {
1308: return isExists;
1309: }
1310:
1311: attributes = ATTR_READONLY | ATTR_DIRECTORY;
1312: createTime = 0L;
1313: lastModified = 0L;
1314: isExists = false;
1315:
1316: try {
1317: if (url.getHost().length() == 0) {
1318: } else if (share == null) {
1319: if (getType() == TYPE_WORKGROUP) {
1320: UniAddress.getByName(url.getHost(), true);
1321: } else {
1322: UniAddress.getByName(url.getHost()).getHostName();
1323: }
1324: } else if (getUncPath0().length() == 1
1325: || share.equalsIgnoreCase("IPC$")) {
1326: connect0(); // treeConnect is good enough
1327: } else {
1328: Info info = queryPath(
1329: getUncPath0(),
1330: Trans2QueryPathInformationResponse.SMB_QUERY_FILE_BASIC_INFO);
1331: attributes = info.getAttributes();
1332: createTime = info.getCreateTime();
1333: lastModified = info.getLastWriteTime();
1334: }
1335:
1336: /* If any of the above fail, isExists will not be set true
1337: */
1338:
1339: isExists = true;
1340:
1341: } catch (UnknownHostException uhe) {
1342: } catch (SmbException se) {
1343: switch (se.getNtStatus()) {
1344: case NtStatus.NT_STATUS_NO_SUCH_FILE:
1345: case NtStatus.NT_STATUS_OBJECT_NAME_INVALID:
1346: case NtStatus.NT_STATUS_OBJECT_NAME_NOT_FOUND:
1347: case NtStatus.NT_STATUS_OBJECT_PATH_NOT_FOUND:
1348: break;
1349: default:
1350: throw se;
1351: }
1352: }
1353:
1354: attrExpiration = System.currentTimeMillis()
1355: + attrExpirationPeriod;
1356:
1357: return isExists;
1358: }
1359:
1360: /**
1361: * Tests to see if the file this <code>SmbFile</code> represents can be
1362: * read. Because any file, directory, or other resource can be read if it
1363: * exists, this method simply calls the <code>exists</code> method.
1364: *
1365: * @return <code>true</code> if the file is read-only
1366: */
1367:
1368: public boolean canRead() throws SmbException {
1369: if (getType() == TYPE_NAMED_PIPE) { // try opening the pipe for reading?
1370: return true;
1371: }
1372: return exists(); // try opening and catch sharing violation?
1373: }
1374:
1375: /**
1376: * Tests to see if the file this <code>SmbFile</code> represents
1377: * exists and is not marked read-only. By default, resources are
1378: * considered to be read-only and therefore for <code>smb://</code>,
1379: * <code>smb://workgroup/</code>, and <code>smb://server/</code> resources
1380: * will be read-only.
1381: *
1382: * @return <code>true</code> if the resource exists is not marked
1383: * read-only
1384: */
1385:
1386: public boolean canWrite() throws SmbException {
1387: if (getType() == TYPE_NAMED_PIPE) { // try opening the pipe for writing?
1388: return true;
1389: }
1390: return exists() && (attributes & ATTR_READONLY) == 0;
1391: }
1392:
1393: /**
1394: * Tests to see if the file this <code>SmbFile</code> represents is a directory.
1395: *
1396: * @return <code>true</code> if this <code>SmbFile</code> is a directory
1397: */
1398:
1399: public boolean isDirectory() throws SmbException {
1400: if (getUncPath0().length() == 1) {
1401: return true;
1402: }
1403: if (!exists())
1404: return false;
1405: return (attributes & ATTR_DIRECTORY) == ATTR_DIRECTORY;
1406: }
1407:
1408: /**
1409: * Tests to see if the file this <code>SmbFile</code> represents is not a directory.
1410: *
1411: * @return <code>true</code> if this <code>SmbFile</code> is not a directory
1412: */
1413:
1414: public boolean isFile() throws SmbException {
1415: if (getUncPath0().length() == 1) {
1416: return false;
1417: }
1418: exists();
1419: return (attributes & ATTR_DIRECTORY) == 0;
1420: }
1421:
1422: /**
1423: * Tests to see if the file this SmbFile represents is marked as
1424: * hidden. This method will also return true for shares with names that
1425: * end with '$' such as <code>IPC$</code> or <code>C$</code>.
1426: *
1427: * @return <code>true</code> if the <code>SmbFile</code> is marked as being hidden
1428: */
1429:
1430: public boolean isHidden() throws SmbException {
1431: if (share == null) {
1432: return false;
1433: } else if (getUncPath0().length() == 1) {
1434: if (share.endsWith("$")) {
1435: return true;
1436: }
1437: return false;
1438: }
1439: exists();
1440: return (attributes & ATTR_HIDDEN) == ATTR_HIDDEN;
1441: }
1442:
1443: /**
1444: * If the path of this <code>SmbFile</code> falls within a DFS volume,
1445: * this method will return the referral path to which it maps. Otherwise
1446: * <code>null</code> is returned.
1447: */
1448:
1449: public String getDfsPath() throws SmbException {
1450: connect0();
1451: if (tree.inDfs) {
1452: exists();
1453: }
1454: if (dfsReferral == null) {
1455: return null;
1456: }
1457: return "smb:/"
1458: + (new String(dfsReferral.node + unc)).replace('\\',
1459: '/');
1460: }
1461:
1462: /**
1463: * Retrieve the time this <code>SmbFile</code> was created. The value
1464: * returned is suitable for constructing a {@link java.util.Date} object
1465: * (i.e. seconds since Epoch 1970). Times should be the same as those
1466: * reported using the properties dialog of the Windows Explorer program.
1467: *
1468: * For Win95/98/Me this is actually the last write time. It is currently
1469: * not possible to retrieve the create time from files on these systems.
1470: *
1471: * @return The number of milliseconds since the 00:00:00 GMT, January 1,
1472: * 1970 as a <code>long</code> value
1473: */
1474: public long createTime() throws SmbException {
1475: if (getUncPath0().length() > 1) {
1476: exists();
1477: return createTime;
1478: }
1479: return 0L;
1480: }
1481:
1482: /**
1483: * Retrieve the last time the file represented by this
1484: * <code>SmbFile</code> was modified. The value returned is suitable for
1485: * constructing a {@link java.util.Date} object (i.e. seconds since Epoch
1486: * 1970). Times should be the same as those reported using the properties
1487: * dialog of the Windows Explorer program.
1488: *
1489: * @return The number of milliseconds since the 00:00:00 GMT, January 1,
1490: * 1970 as a <code>long</code> value
1491: */
1492: public long lastModified() throws SmbException {
1493: if (getUncPath0().length() > 1) {
1494: exists();
1495: return lastModified;
1496: }
1497: return 0L;
1498: }
1499:
1500: /**
1501: * List the contents of this SMB resource. The list returned by this
1502: * method will be;
1503: *
1504: * <ul>
1505: * <li> files and directories contained within this resource if the
1506: * resource is a normal disk file directory,
1507: * <li> all available NetBIOS workgroups or domains if this resource is
1508: * the top level URL <code>smb://</code>,
1509: * <li> all servers registered as members of a NetBIOS workgroup if this
1510: * resource refers to a workgroup in a <code>smb://workgroup/</code> URL,
1511: * <li> all browseable shares of a server including printers, IPC
1512: * services, or disk volumes if this resource is a server URL in the form
1513: * <code>smb://server/</code>,
1514: * <li> or <code>null</code> if the resource cannot be resolved.
1515: * </ul>
1516: *
1517: * @return A <code>String[]</code> array of files and directories,
1518: * workgroups, servers, or shares depending on the context of the
1519: * resource URL
1520: */
1521: public String[] list() throws SmbException {
1522: return list("*", ATTR_DIRECTORY | ATTR_HIDDEN | ATTR_SYSTEM,
1523: null, null);
1524: }
1525:
1526: /**
1527: * List the contents of this SMB resource. The list returned will be
1528: * identical to the list returned by the parameterless <code>list()</code>
1529: * method minus filenames filtered by the specified filter.
1530: *
1531: * @param filter a filename filter to exclude filenames from the results
1532: * @throws SmbException
1533: # @return An array of filenames
1534: */
1535: public String[] list(SmbFilenameFilter filter) throws SmbException {
1536: return list("*", ATTR_DIRECTORY | ATTR_HIDDEN | ATTR_SYSTEM,
1537: filter, null);
1538: }
1539:
1540: /**
1541: * List the contents of this SMB resource as an array of
1542: * <code>SmbFile</code> objects. This method is much more efficient than
1543: * the regular <code>list</code> method when querying attributes of each
1544: * file in the result set.
1545: * <p>
1546: * The list of <code>SmbFile</code>s returned by this method will be;
1547: *
1548: * <ul>
1549: * <li> files and directories contained within this resource if the
1550: * resource is a normal disk file directory,
1551: * <li> all available NetBIOS workgroups or domains if this resource is
1552: * the top level URL <code>smb://</code>,
1553: * <li> all servers registered as members of a NetBIOS workgroup if this
1554: * resource refers to a workgroup in a <code>smb://workgroup/</code> URL,
1555: * <li> all browseable shares of a server including printers, IPC
1556: * services, or disk volumes if this resource is a server URL in the form
1557: * <code>smb://server/</code>,
1558: * <li> or <code>null</code> if the resource cannot be resolved.
1559: * </ul>
1560: *
1561: * @return An array of <code>SmbFile</code> objects representing file
1562: * and directories, workgroups, servers, or shares depending on the context
1563: * of the resource URL
1564: */
1565: public SmbFile[] listFiles() throws SmbException {
1566: return listFiles("*", ATTR_DIRECTORY | ATTR_HIDDEN
1567: | ATTR_SYSTEM, null, null);
1568: }
1569:
1570: /**
1571: * The CIFS protocol provides for DOS "wildcards" to be used as
1572: * a performance enhancement. The client does not have to filter
1573: * the names and the server does not have to return all directory
1574: * entries.
1575: * <p>
1576: * The wildcard expression may consist of two special meta
1577: * characters in addition to the normal filename characters. The '*'
1578: * character matches any number of characters in part of a name. If
1579: * the expression begins with one or more '?'s then exactly that
1580: * many characters will be matched whereas if it ends with '?'s
1581: * it will match that many characters <i>or less</i>.
1582: * <p>
1583: * Wildcard expressions will not filter workgroup names or server names.
1584: *
1585: * <blockquote><pre>
1586: * winnt> ls c?o*
1587: * clock.avi -rw-- 82944 Mon Oct 14 1996 1:38 AM
1588: * Cookies drw-- 0 Fri Nov 13 1998 9:42 PM
1589: * 2 items in 5ms
1590: * </pre></blockquote>
1591: *
1592: * @param wildcard a wildcard expression
1593: * @throws SmbException
1594: * @return An array of <code>SmbFile</code> objects representing file
1595: * and directories, workgroups, servers, or shares depending on the context
1596: * of the resource URL
1597: */
1598:
1599: public SmbFile[] listFiles(String wildcard) throws SmbException {
1600: return listFiles(wildcard, ATTR_DIRECTORY | ATTR_HIDDEN
1601: | ATTR_SYSTEM, null, null);
1602: }
1603:
1604: /**
1605: * List the contents of this SMB resource. The list returned will be
1606: * identical to the list returned by the parameterless <code>listFiles()</code>
1607: * method minus files filtered by the specified filename filter.
1608: *
1609: * @param filter a filter to exclude files from the results
1610: * @return An array of <tt>SmbFile</tt> objects
1611: * @throws SmbException
1612: */
1613: public SmbFile[] listFiles(SmbFilenameFilter filter)
1614: throws SmbException {
1615: return listFiles("*", ATTR_DIRECTORY | ATTR_HIDDEN
1616: | ATTR_SYSTEM, filter, null);
1617: }
1618:
1619: /**
1620: * List the contents of this SMB resource. The list returned will be
1621: * identical to the list returned by the parameterless <code>listFiles()</code>
1622: * method minus filenames filtered by the specified filter.
1623: *
1624: * @param filter a file filter to exclude files from the results
1625: * @return An array of <tt>SmbFile</tt> objects
1626: */
1627: public SmbFile[] listFiles(SmbFileFilter filter)
1628: throws SmbException {
1629: return listFiles("*", ATTR_DIRECTORY | ATTR_HIDDEN
1630: | ATTR_SYSTEM, null, filter);
1631: }
1632:
1633: String[] list(String wildcard, int searchAttributes,
1634: SmbFilenameFilter fnf, SmbFileFilter ff)
1635: throws SmbException {
1636: ArrayList list = new ArrayList();
1637:
1638: try {
1639: if (url.getHost().length() == 0 || share == null) {
1640: doNetEnum(list, false, wildcard, searchAttributes, fnf,
1641: ff);
1642: } else {
1643: doFindFirstNext(list, false, wildcard,
1644: searchAttributes, fnf, ff);
1645: }
1646: } catch (UnknownHostException uhe) {
1647: throw new SmbException(url.toString(), uhe);
1648: } catch (MalformedURLException mue) {
1649: throw new SmbException(url.toString(), mue);
1650: }
1651:
1652: return (String[]) list.toArray(new String[list.size()]);
1653: }
1654:
1655: SmbFile[] listFiles(String wildcard, int searchAttributes,
1656: SmbFilenameFilter fnf, SmbFileFilter ff)
1657: throws SmbException {
1658: ArrayList list = new ArrayList();
1659:
1660: if (ff != null && ff instanceof DosFileFilter) {
1661: DosFileFilter dff = (DosFileFilter) ff;
1662: if (dff.wildcard != null) {
1663: wildcard = dff.wildcard;
1664: }
1665: searchAttributes = dff.attributes;
1666: }
1667:
1668: try {
1669: if (url.getHost().length() == 0 || share == null) {
1670: doNetEnum(list, true, wildcard, searchAttributes, fnf,
1671: ff);
1672: } else {
1673: doFindFirstNext(list, true, wildcard, searchAttributes,
1674: fnf, ff);
1675: }
1676: } catch (UnknownHostException uhe) {
1677: throw new SmbException(url.toString(), uhe);
1678: } catch (MalformedURLException mue) {
1679: throw new SmbException(url.toString(), mue);
1680: }
1681:
1682: return (SmbFile[]) list.toArray(new SmbFile[list.size()]);
1683: }
1684:
1685: void doNetEnum(ArrayList list, boolean files, String wildcard,
1686: int searchAttributes, SmbFilenameFilter fnf,
1687: SmbFileFilter ff) throws SmbException,
1688: UnknownHostException, MalformedURLException {
1689: SmbComTransaction req;
1690: SmbComTransactionResponse resp;
1691: int listType = url.getHost().length() == 0 ? 0 : getType();
1692: String p = url.getPath();
1693:
1694: if (p.lastIndexOf('/') != (p.length() - 1)) {
1695: throw new SmbException(url.toString()
1696: + " directory must end with '/'");
1697: }
1698:
1699: switch (listType) {
1700: case 0:
1701: connect0();
1702: req = new NetServerEnum2(
1703: tree.session.transport.server.oemDomainName,
1704: NetServerEnum2.SV_TYPE_DOMAIN_ENUM);
1705: resp = new NetServerEnum2Response();
1706: break;
1707: case TYPE_WORKGROUP:
1708: req = new NetServerEnum2(url.getHost(),
1709: NetServerEnum2.SV_TYPE_ALL);
1710: resp = new NetServerEnum2Response();
1711: break;
1712: case TYPE_SERVER:
1713: req = new NetShareEnum();
1714: resp = new NetShareEnumResponse();
1715: break;
1716: default:
1717: throw new SmbException(
1718: "The requested list operations is invalid: "
1719: + url.toString());
1720: }
1721:
1722: boolean more;
1723: do {
1724: int n;
1725:
1726: sendTransaction(req, resp);
1727:
1728: more = resp.status == SmbException.ERROR_MORE_DATA;
1729:
1730: if (resp.status != SmbException.ERROR_SUCCESS
1731: && resp.status != SmbException.ERROR_MORE_DATA) {
1732: throw new SmbException(resp.status, true);
1733: }
1734:
1735: n = more ? resp.numEntries - 1 : resp.numEntries;
1736: for (int i = 0; i < n; i++) {
1737: FileEntry e = resp.results[i];
1738: String name = e.getName();
1739: if (fnf != null && fnf.accept(this , name) == false) {
1740: continue;
1741: }
1742: if (name.length() > 0) {
1743: SmbFile f = new SmbFile(this , name,
1744: listType == 0 ? TYPE_WORKGROUP
1745: : (listType << 1), ATTR_READONLY
1746: | ATTR_DIRECTORY, 0L, 0L, 0L);
1747: if (ff != null && ff.accept(f) == false) {
1748: continue;
1749: }
1750: if (files) {
1751: list.add(f);
1752: } else {
1753: list.add(name);
1754: }
1755: }
1756: }
1757: if (listType != 0 && listType != TYPE_WORKGROUP) {
1758: break;
1759: }
1760: req.subCommand = (byte) SmbComTransaction.NET_SERVER_ENUM3;
1761: req.reset(0, ((NetServerEnum2Response) resp).lastName);
1762: resp.reset();
1763: } while (more);
1764: }
1765:
1766: void doFindFirstNext(ArrayList list, boolean files,
1767: String wildcard, int searchAttributes,
1768: SmbFilenameFilter fnf, SmbFileFilter ff)
1769: throws SmbException, UnknownHostException,
1770: MalformedURLException {
1771: SmbComTransaction req;
1772: Trans2FindFirst2Response resp;
1773: int sid;
1774: String path = getUncPath0();
1775: String p = url.getPath();
1776:
1777: if (p.lastIndexOf('/') != (p.length() - 1)) {
1778: throw new SmbException(url.toString()
1779: + " directory must end with '/'");
1780: }
1781:
1782: req = new Trans2FindFirst2(path, wildcard, searchAttributes);
1783: resp = new Trans2FindFirst2Response();
1784:
1785: if (log.level > 2)
1786: log.println("doFindFirstNext: " + req.path);
1787:
1788: sendTransaction(req, resp);
1789:
1790: sid = resp.sid;
1791: req = new Trans2FindNext2(sid, resp.resumeKey, resp.lastName);
1792:
1793: /* The only difference between first2 and next2 responses is subCommand
1794: * so let's recycle the response object.
1795: */
1796: resp.subCommand = SmbComTransaction.TRANS2_FIND_NEXT2;
1797:
1798: for (;;) {
1799: for (int i = 0; i < resp.numEntries; i++) {
1800: FileEntry e = resp.results[i];
1801: String name = e.getName();
1802: if (name.length() < 3) {
1803: int h = name.hashCode();
1804: if (h == HASH_DOT || h == HASH_DOT_DOT) {
1805: continue;
1806: }
1807: }
1808: if (fnf != null && fnf.accept(this , name) == false) {
1809: continue;
1810: }
1811: if (name.length() > 0) {
1812: SmbFile f = new SmbFile(this , name,
1813: TYPE_FILESYSTEM, e.getAttributes(), e
1814: .createTime(), e.lastModified(), e
1815: .length());
1816: if (ff != null && ff.accept(f) == false) {
1817: continue;
1818: }
1819: if (files) {
1820: list.add(f);
1821: } else {
1822: list.add(name);
1823: }
1824: }
1825: }
1826:
1827: if (resp.isEndOfSearch || resp.numEntries == 0) {
1828: break;
1829: }
1830:
1831: req.reset(resp.resumeKey, resp.lastName);
1832: resp.reset();
1833: sendTransaction(req, resp);
1834: }
1835:
1836: send(new SmbComFindClose2(sid), blank_resp());
1837: }
1838:
1839: /**
1840: * Changes the name of the file this <code>SmbFile</code> represents to the name
1841: * designated by the <code>SmbFile</code> argument.
1842: * <p/>
1843: * <i>Remember: <code>SmbFile</code>s are immutible and therefore
1844: * the path associated with this <code>SmbFile</code> object will not
1845: * change). To access the renamed file it is necessary to construct a
1846: * new <tt>SmbFile</tt></i>.
1847: *
1848: * @param dest An <code>SmbFile</code> that represents the new pathname
1849: * @return <code>true</code> if the file or directory was successfully renamed
1850: * @throws NullPointerException
1851: * If the <code>dest</code> argument is <code>null</code>
1852: */
1853: public void renameTo(SmbFile dest) throws SmbException {
1854: if (getUncPath0().length() == 1
1855: || dest.getUncPath0().length() == 1) {
1856: throw new SmbException(
1857: "Invalid operation for workgroups, servers, or shares");
1858: }
1859: connect0();
1860: dest.connect0();
1861:
1862: if (tree.inDfs) { /* This ensures that each path is
1863: * resolved independantly to deal with the case where files
1864: * have the same base path but ultimately turn out to be
1865: * on different servers because of DFS. It also eliminates
1866: * manipulating the SMB path which is problematic because
1867: * there are really two that would need to be prefixed
1868: * with host and share as DFS requires.
1869: */
1870: exists();
1871: dest.exists();
1872: }
1873: if (tree != dest.tree) {
1874: throw new SmbException(
1875: "Invalid operation for workgroups, servers, or shares");
1876: }
1877:
1878: if (log.level > 2)
1879: log.println("renameTo: " + unc + " -> " + dest.unc);
1880:
1881: attrExpiration = sizeExpiration = 0;
1882: dest.attrExpiration = 0;
1883:
1884: /*
1885: * Rename Request / Response
1886: */
1887:
1888: send(new SmbComRename(unc, dest.unc), blank_resp());
1889: }
1890:
1891: class WriterThread extends Thread {
1892: byte[] b;
1893: int n, off;
1894: boolean ready;
1895: SmbFile dest;
1896: SmbException e = null;
1897: boolean useNTSmbs;
1898: SmbComWriteAndX reqx;
1899: SmbComWrite req;
1900: ServerMessageBlock resp;
1901:
1902: WriterThread() throws SmbException {
1903: super ("JCIFS-WriterThread");
1904: useNTSmbs = tree.session.transport
1905: .hasCapability(ServerMessageBlock.CAP_NT_SMBS);
1906: if (useNTSmbs) {
1907: reqx = new SmbComWriteAndX();
1908: resp = new SmbComWriteAndXResponse();
1909: } else {
1910: req = new SmbComWrite();
1911: resp = new SmbComWriteResponse();
1912: }
1913: ready = false;
1914: }
1915:
1916: synchronized void write(byte[] b, int n, SmbFile dest, int off) {
1917: this .b = b;
1918: this .n = n;
1919: this .dest = dest;
1920: this .off = off;
1921: ready = false;
1922: notify();
1923: }
1924:
1925: public void run() {
1926: synchronized (this ) {
1927: try {
1928: for (;;) {
1929: notify();
1930: ready = true;
1931: while (ready) {
1932: wait();
1933: }
1934: if (n == -1) {
1935: return;
1936: }
1937: if (useNTSmbs) {
1938: reqx.setParam(dest.fid, off, n, b, 0, n);
1939: dest.send(reqx, resp);
1940: } else {
1941: req.setParam(dest.fid, off, n, b, 0, n);
1942: dest.send(req, resp);
1943: }
1944: }
1945: } catch (SmbException e) {
1946: this .e = e;
1947: } catch (Exception x) {
1948: this .e = new SmbException("WriterThread", x);
1949: }
1950: notify();
1951: }
1952: }
1953: }
1954:
1955: void copyTo0(SmbFile dest, byte[][] b, int bsize, WriterThread w,
1956: SmbComReadAndX req, SmbComReadAndXResponse resp)
1957: throws SmbException {
1958: int i;
1959:
1960: if (attrExpiration < System.currentTimeMillis()) {
1961: attributes = ATTR_READONLY | ATTR_DIRECTORY;
1962: createTime = 0L;
1963: lastModified = 0L;
1964: isExists = false;
1965:
1966: Info info = queryPath(
1967: getUncPath0(),
1968: Trans2QueryPathInformationResponse.SMB_QUERY_FILE_BASIC_INFO);
1969: attributes = info.getAttributes();
1970: createTime = info.getCreateTime();
1971: lastModified = info.getLastWriteTime();
1972:
1973: /* If any of the above fails, isExists will not be set true
1974: */
1975:
1976: isExists = true;
1977: attrExpiration = System.currentTimeMillis()
1978: + attrExpirationPeriod;
1979: }
1980:
1981: if (isDirectory()) {
1982: SmbFile[] files;
1983: SmbFile ndest;
1984:
1985: String path = dest.getUncPath0();
1986: if (path.length() > 1) {
1987: try {
1988: dest.mkdir();
1989: dest.setPathInformation(attributes, createTime,
1990: lastModified);
1991: } catch (SmbException se) {
1992: if (se.getNtStatus() != NtStatus.NT_STATUS_ACCESS_DENIED
1993: && se.getNtStatus() != NtStatus.NT_STATUS_OBJECT_NAME_COLLISION) {
1994: throw se;
1995: }
1996: }
1997: }
1998:
1999: files = listFiles("*", ATTR_DIRECTORY | ATTR_HIDDEN
2000: | ATTR_SYSTEM, null, null);
2001: try {
2002: for (i = 0; i < files.length; i++) {
2003: ndest = new SmbFile(dest, files[i].getName(),
2004: files[i].type, files[i].attributes,
2005: files[i].createTime, files[i].lastModified,
2006: files[i].size);
2007: files[i].copyTo0(ndest, b, bsize, w, req, resp);
2008: }
2009: } catch (UnknownHostException uhe) {
2010: throw new SmbException(url.toString(), uhe);
2011: } catch (MalformedURLException mue) {
2012: throw new SmbException(url.toString(), mue);
2013: }
2014: } else {
2015: int off;
2016:
2017: try {
2018: open(SmbFile.O_RDONLY, ATTR_NORMAL, 0);
2019: try {
2020: dest
2021: .open(
2022: SmbFile.O_CREAT
2023: | SmbFile.O_WRONLY
2024: | SmbFile.O_TRUNC
2025: | SmbComNTCreateAndX.FILE_WRITE_ATTRIBUTES << 16,
2026: attributes, 0);
2027: } catch (SmbAuthException sae) {
2028: if ((dest.attributes & ATTR_READONLY) != 0) {
2029: /* Remove READONLY and try again
2030: */
2031: dest.setPathInformation(dest.attributes
2032: & ~ATTR_READONLY, 0L, 0L);
2033: dest
2034: .open(
2035: SmbFile.O_CREAT
2036: | SmbFile.O_WRONLY
2037: | SmbFile.O_TRUNC
2038: | SmbComNTCreateAndX.FILE_WRITE_ATTRIBUTES << 16,
2039: attributes, 0);
2040: } else {
2041: throw sae;
2042: }
2043: }
2044:
2045: i = off = 0;
2046: for (;;) {
2047: req.setParam(fid, off, bsize);
2048: resp.setParam(b[i], 0);
2049: send(req, resp);
2050:
2051: synchronized (w) {
2052: while (!w.ready) {
2053: try {
2054: w.wait();
2055: } catch (InterruptedException ie) {
2056: throw new SmbException(dest.url
2057: .toString(), ie);
2058: }
2059: }
2060: if (w.e != null) {
2061: throw w.e;
2062: }
2063: if (resp.dataLength <= 0) {
2064: break;
2065: }
2066: w.write(b[i], resp.dataLength, dest, off);
2067: }
2068:
2069: i = i == 1 ? 0 : 1;
2070: off += resp.dataLength;
2071: }
2072:
2073: dest.sendTransaction(
2074: new Trans2SetFileInformation(dest.fid,
2075: attributes, createTime, lastModified),
2076: new Trans2SetFileInformationResponse());
2077: dest.close(0L);
2078: close();
2079: } catch (Exception ex) {
2080: if (log.level > 1)
2081: ex.printStackTrace(log);
2082: }
2083: }
2084: }
2085:
2086: /**
2087: * This method will copy the file or directory represented by this
2088: * <tt>SmbFile</tt> and it's sub-contents to the location specified by the
2089: * <tt>dest</tt> parameter. This file and the destination file do not
2090: * need to be on the same host. This operation does not copy extended
2091: * file attibutes such as ACLs but it does copy regular attributes as
2092: * well as create and last write times. This method is almost twice as
2093: * efficient as manually copying as it employs an additional write
2094: * thread to read and write data concurrently.
2095: * <p/>
2096: * It is not possible (nor meaningful) to copy entire workgroups or
2097: * servers.
2098: *
2099: * @param dest the destination file or directory
2100: * @throws SmbException
2101: */
2102: public void copyTo(SmbFile dest) throws SmbException {
2103: SmbComReadAndX req;
2104: SmbComReadAndXResponse resp;
2105: WriterThread w;
2106: int bsize;
2107: byte[][] b;
2108:
2109: /* Should be able to copy an entire share actually
2110: */
2111: if (share == null || dest.share == null) {
2112: throw new SmbException(
2113: "Invalid operation for workgroups or servers");
2114: }
2115:
2116: req = new SmbComReadAndX();
2117: resp = new SmbComReadAndXResponse();
2118:
2119: connect0();
2120: dest.connect0();
2121:
2122: if (tree.inDfs) {
2123: /* At this point the maxBufferSize values are from the server
2124: * exporting the volumes, not the one that we will actually
2125: * end up performing IO with. If the server hosting the
2126: * actual files has a smaller maxBufSize this could be
2127: * incorrect. To handle this properly it is necessary
2128: * to redirect the tree to the target server first before
2129: * establishing buffer size. These exists() calls facilitate
2130: * that.
2131: */
2132: exists();
2133: dest.exists();
2134: }
2135:
2136: w = new WriterThread();
2137: w.setDaemon(true);
2138: w.start();
2139:
2140: /* Downgrade one transport to the lower of the negotiated buffer sizes
2141: * so we can just send whatever is received.
2142: */
2143:
2144: SmbTransport t1 = tree.session.transport;
2145: SmbTransport t2 = dest.tree.session.transport;
2146:
2147: if (t1.snd_buf_size < t2.snd_buf_size) {
2148: t2.snd_buf_size = t1.snd_buf_size;
2149: } else {
2150: t1.snd_buf_size = t2.snd_buf_size;
2151: }
2152:
2153: bsize = Math.min(t1.rcv_buf_size - 70, t1.snd_buf_size - 70);
2154: b = new byte[2][bsize];
2155:
2156: copyTo0(dest, b, bsize, w, req, resp);
2157: w.write(null, -1, null, 0);
2158: }
2159:
2160: /**
2161: * This method will delete the file or directory specified by this
2162: * <code>SmbFile</code>. If the target is a directory, the contents of
2163: * the directory will be deleted as well. If a file within the directory or
2164: * it's sub-directories is marked read-only, the read-only status will
2165: * be removed and the file will be deleted.
2166: *
2167: * @throws SmbException
2168: */
2169: public void delete() throws SmbException {
2170: if (tree == null || tree.inDfs) {
2171: exists(); /* This is necessary to ensure we
2172: * pass a path adjusted for DFS */
2173: }
2174: getUncPath0();
2175: delete(unc);
2176: }
2177:
2178: void delete(String fileName) throws SmbException {
2179: if (getUncPath0().length() == 1) {
2180: throw new SmbException(
2181: "Invalid operation for workgroups, servers, or shares");
2182: }
2183:
2184: if (System.currentTimeMillis() > attrExpiration) {
2185: attributes = ATTR_READONLY | ATTR_DIRECTORY;
2186: createTime = 0L;
2187: lastModified = 0L;
2188: isExists = false;
2189:
2190: Info info = queryPath(
2191: getUncPath0(),
2192: Trans2QueryPathInformationResponse.SMB_QUERY_FILE_BASIC_INFO);
2193: attributes = info.getAttributes();
2194: createTime = info.getCreateTime();
2195: lastModified = info.getLastWriteTime();
2196:
2197: attrExpiration = System.currentTimeMillis()
2198: + attrExpirationPeriod;
2199: isExists = true;
2200: }
2201:
2202: if ((attributes & ATTR_READONLY) != 0) {
2203: setReadWrite();
2204: }
2205:
2206: /*
2207: * Delete or Delete Directory Request / Response
2208: */
2209:
2210: if (log.level > 2)
2211: log.println("delete: " + fileName);
2212:
2213: if ((attributes & ATTR_DIRECTORY) != 0) {
2214:
2215: /* Recursively delete directory contents
2216: */
2217:
2218: try {
2219: SmbFile[] l = listFiles("*", ATTR_DIRECTORY
2220: | ATTR_HIDDEN | ATTR_SYSTEM, null, null);
2221: for (int i = 0; i < l.length; i++) {
2222: l[i].delete();
2223: }
2224: } catch (SmbException se) {
2225: /* Oracle FilesOnline version 9.0.4 doesn't send '.' and '..' so
2226: * listFiles may generate undesireable "cannot find
2227: * the file specified".
2228: */
2229: if (se.getNtStatus() != SmbException.NT_STATUS_NO_SUCH_FILE) {
2230: throw se;
2231: }
2232: }
2233:
2234: send(new SmbComDeleteDirectory(fileName), blank_resp());
2235: } else {
2236: send(new SmbComDelete(fileName), blank_resp());
2237: }
2238:
2239: attrExpiration = sizeExpiration = 0;
2240: }
2241:
2242: /**
2243: * Returns the length of this <tt>SmbFile</tt> in bytes. If this object
2244: * is a <tt>TYPE_SHARE</tt> the total capacity of the disk shared in
2245: * bytes is returned. If this object is a directory or a type other than
2246: * <tt>TYPE_SHARE</tt>, 0L is returned.
2247: *
2248: * @return The length of the file in bytes or 0 if this
2249: * <code>SmbFile</code> is not a file.
2250: * @throws SmbException
2251: */
2252:
2253: public long length() throws SmbException {
2254: if (sizeExpiration > System.currentTimeMillis()) {
2255: return size;
2256: }
2257:
2258: if (getType() == TYPE_SHARE) {
2259: Trans2QueryFSInformationResponse response;
2260: int level = Trans2QueryFSInformationResponse.SMB_INFO_ALLOCATION;
2261:
2262: response = new Trans2QueryFSInformationResponse(level);
2263: sendTransaction(new Trans2QueryFSInformation(level),
2264: response);
2265:
2266: size = response.info.getCapacity();
2267: } else if (getUncPath0().length() > 1
2268: && type != TYPE_NAMED_PIPE) {
2269: Info info = queryPath(
2270: getUncPath0(),
2271: Trans2QueryPathInformationResponse.SMB_QUERY_FILE_STANDARD_INFO);
2272: size = info.getSize();
2273: } else {
2274: size = 0L;
2275: }
2276: sizeExpiration = System.currentTimeMillis()
2277: + attrExpirationPeriod;
2278: return size;
2279: }
2280:
2281: /**
2282: * This method returns the free disk space in bytes of the drive this share
2283: * represents or the drive on which the directory or file resides. Objects
2284: * other than <tt>TYPE_SHARE</tt> or <tt>TYPE_FILESYSTEM</tt> will result
2285: * in 0L being returned.
2286: *
2287: * @return the free disk space in bytes of the drive on which this file or
2288: * directory resides
2289: */
2290: public long getDiskFreeSpace() throws SmbException {
2291: if (getType() == TYPE_SHARE || type == TYPE_FILESYSTEM) {
2292: Trans2QueryFSInformationResponse response;
2293: int level = Trans2QueryFSInformationResponse.SMB_INFO_ALLOCATION;
2294:
2295: response = new Trans2QueryFSInformationResponse(level);
2296: sendTransaction(new Trans2QueryFSInformation(level),
2297: response);
2298:
2299: if (type == TYPE_SHARE) {
2300: size = response.info.getCapacity();
2301: sizeExpiration = System.currentTimeMillis()
2302: + attrExpirationPeriod;
2303: }
2304:
2305: return response.info.getFree();
2306: }
2307: return 0L;
2308: }
2309:
2310: /**
2311: * Creates a directory with the path specified by this
2312: * <code>SmbFile</code>. For this method to be successful, the target
2313: * must not already exist. This method will fail when
2314: * used with <code>smb://</code>, <code>smb://workgroup/</code>,
2315: * <code>smb://server/</code>, or <code>smb://server/share/</code> URLs
2316: * because workgroups, servers, and shares cannot be dynamically created
2317: * (although in the future it may be possible to create shares).
2318: *
2319: * @throws SmbException
2320: */
2321: public void mkdir() throws SmbException {
2322: String path = getUncPath0();
2323:
2324: if (path.length() == 1) {
2325: throw new SmbException(
2326: "Invalid operation for workgroups, servers, or shares");
2327: }
2328:
2329: /*
2330: * Create Directory Request / Response
2331: */
2332:
2333: if (log.level > 2)
2334: log.println("mkdir: " + path);
2335:
2336: send(new SmbComCreateDirectory(path), blank_resp());
2337:
2338: attrExpiration = sizeExpiration = 0;
2339: }
2340:
2341: /**
2342: * Creates a directory with the path specified by this <tt>SmbFile</tt>
2343: * and any parent directories that do not exist. This method will fail
2344: * when used with <code>smb://</code>, <code>smb://workgroup/</code>,
2345: * <code>smb://server/</code>, or <code>smb://server/share/</code> URLs
2346: * because workgroups, servers, and shares cannot be dynamically created
2347: * (although in the future it may be possible to create shares).
2348: *
2349: * @throws SmbException
2350: */
2351: public void mkdirs() throws SmbException {
2352: SmbFile parent;
2353:
2354: try {
2355: parent = new SmbFile(getParent(), auth);
2356: } catch (IOException ioe) {
2357: return;
2358: }
2359: if (parent.exists() == false) {
2360: parent.mkdirs();
2361: }
2362: mkdir();
2363: }
2364:
2365: /**
2366: * Create a new file but fail if it already exists. The check for
2367: * existance of the file and it's creation are an atomic operation with
2368: * respect to other filesystem activities.
2369: */
2370: public void createNewFile() throws SmbException {
2371: if (getUncPath0().length() == 1) {
2372: throw new SmbException(
2373: "Invalid operation for workgroups, servers, or shares");
2374: }
2375: close(open0(O_RDWR | O_CREAT | O_EXCL, ATTR_NORMAL, 0), 0L);
2376: }
2377:
2378: void setPathInformation(int attrs, long ctime, long mtime)
2379: throws SmbException {
2380: int f, options = 0;
2381:
2382: if ((attrs & ATTR_DIRECTORY) != 0) {
2383: options = 1;
2384: }
2385:
2386: f = open0(O_RDONLY
2387: | SmbComNTCreateAndX.FILE_WRITE_ATTRIBUTES << 16,
2388: attrs, options);
2389: sendTransaction(new Trans2SetFileInformation(f, attrs, ctime,
2390: mtime), new Trans2SetFileInformationResponse());
2391: close(f, 0L);
2392:
2393: attrExpiration = 0;
2394: }
2395:
2396: /**
2397: * Set the create time of the file. The time is specified as milliseconds
2398: * from Jan 1, 1970 which is the same as that which is returned by the
2399: * <tt>createTime()</tt> method.
2400: * <p/>
2401: * This method does not apply to workgroups, servers, or shares.
2402: *
2403: * @param time the create time as milliseconds since Jan 1, 1970
2404: */
2405: public void setCreateTime(long time) throws SmbException {
2406: if (getUncPath0().length() == 1) {
2407: throw new SmbException(
2408: "Invalid operation for workgroups, servers, or shares");
2409: }
2410:
2411: setPathInformation(0, time, 0L);
2412: }
2413:
2414: /**
2415: * Set the last modified time of the file. The time is specified as milliseconds
2416: * from Jan 1, 1970 which is the same as that which is returned by the
2417: * <tt>lastModified()</tt>, <tt>getLastModified()</tt>, and <tt>getDate()</tt> methods.
2418: * <p/>
2419: * This method does not apply to workgroups, servers, or shares.
2420: *
2421: * @param time the last modified time as milliseconds since Jan 1, 1970
2422: */
2423: public void setLastModified(long time) throws SmbException {
2424: if (getUncPath0().length() == 1) {
2425: throw new SmbException(
2426: "Invalid operation for workgroups, servers, or shares");
2427: }
2428:
2429: setPathInformation(0, 0L, time);
2430: }
2431:
2432: /**
2433: * Return the attributes of this file. Attributes are represented as a
2434: * bitset that must be masked with <tt>ATTR_*</tt> constants to determine
2435: * if they are set or unset. The value returned is suitable for use with
2436: * the <tt>setAttributes()</tt> method.
2437: *
2438: * @return the <tt>ATTR_*</tt> attributes associated with this file
2439: * @throws SmbException
2440: */
2441: public int getAttributes() throws SmbException {
2442: if (getUncPath0().length() == 1) {
2443: return 0;
2444: }
2445: exists();
2446: return attributes & ATTR_GET_MASK;
2447: }
2448:
2449: /**
2450: * Set the attributes of this file. Attributes are composed into a
2451: * bitset by bitwise ORing the <tt>ATTR_*</tt> constants. Setting the
2452: * value returned by <tt>getAttributes</tt> will result in both files
2453: * having the same attributes.
2454: * @throws SmbException
2455: */
2456: public void setAttributes(int attrs) throws SmbException {
2457: if (getUncPath0().length() == 1) {
2458: throw new SmbException(
2459: "Invalid operation for workgroups, servers, or shares");
2460: }
2461:
2462: setPathInformation(attrs & ATTR_SET_MASK, 0L, 0L);
2463: }
2464:
2465: /**
2466: * Make this file read-only. This is shorthand for <tt>setAttributes(
2467: * getAttributes() | ATTR_READ_ONLY )</tt>.
2468: *
2469: * @throws SmbException
2470: */
2471: public void setReadOnly() throws SmbException {
2472: setAttributes(getAttributes() | ATTR_READONLY);
2473: }
2474:
2475: /**
2476: * Turn off the read-only attribute of this file. This is shorthand for
2477: * <tt>setAttributes( getAttributes() & ~ATTR_READONLY )</tt>.
2478: *
2479: * @throws SmbException
2480: */
2481: public void setReadWrite() throws SmbException {
2482: setAttributes(getAttributes() & ~ATTR_READONLY);
2483: }
2484:
2485: /**
2486: * Returns a {@link java.net.URL} for this <code>SmbFile</code>. The
2487: * <code>URL</code> may be used as any other <code>URL</code> might to
2488: * access an SMB resource. Currently only retrieving data and information
2489: * is supported (i.e. no <tt>doOutput</tt>).
2490: *
2491: * @deprecated Use getURL() instead
2492: * @return A new <code>{@link java.net.URL}</code> for this <code>SmbFile</code>
2493: * @throws MalformedURLException
2494: */
2495: public URL toURL() throws MalformedURLException {
2496: return url;
2497: }
2498:
2499: /**
2500: * Computes a hashCode for this file based on the URL string and IP
2501: * address if the server. The hashing function uses the hashcode of the
2502: * server address, the canonical representation of the URL, and does not
2503: * compare authentication information. In essance, two
2504: * <code>SmbFile</code> objects that refer to
2505: * the same file should generate the same hashcode provided it is possible
2506: * to make such a determination.
2507: *
2508: * @return A hashcode for this abstract file
2509: * @throws SmbException
2510: */
2511:
2512: public int hashCode() {
2513: int hash;
2514: try {
2515: hash = getAddress().hashCode();
2516: } catch (UnknownHostException uhe) {
2517: hash = getServer().toUpperCase().hashCode();
2518: }
2519: getUncPath0();
2520: return hash + canon.toUpperCase().hashCode();
2521: }
2522:
2523: /**
2524: * Tests to see if two <code>SmbFile</code> objects are equal. Two
2525: * SmbFile objects are equal when they reference the same SMB
2526: * resource. More specifically, two <code>SmbFile</code> objects are
2527: * equals if their server IP addresses are equal and the canonicalized
2528: * representation of their URLs, minus authentication parameters, are
2529: * case insensitivly and lexographically equal.
2530: * <p/>
2531: * For example, assuming the server <code>angus</code> resolves to the
2532: * <code>192.168.1.15</code> IP address, the below URLs would result in
2533: * <code>SmbFile</code>s that are equal.
2534: *
2535: * <p><blockquote><pre>
2536: * smb://192.168.1.15/share/DIR/foo.txt
2537: * smb://angus/share/data/../dir/foo.txt
2538: * </pre></blockquote>
2539: *
2540: * @param obj Another <code>SmbFile</code> object to compare for equality
2541: * @return <code>true</code> if the two objects refer to the same SMB resource
2542: * and <code>false</code> otherwise
2543: * @throws SmbException
2544: */
2545:
2546: public boolean equals(Object obj) {
2547: return obj instanceof SmbFile && obj.hashCode() == hashCode();
2548: }
2549:
2550: /**
2551: * Returns the string representation of this SmbFile object. This will
2552: * be the same as the URL used to construct this <code>SmbFile</code>.
2553: * This method will return the same value
2554: * as <code>getPath</code>.
2555: *
2556: * @return The original URL representation of this SMB resource
2557: * @throws SmbException
2558: */
2559:
2560: public String toString() {
2561: return url.toString();
2562: }
2563:
2564: /* URLConnection implementation */
2565: /**
2566: * This URLConnection method just returns the result of <tt>length()</tt>.
2567: *
2568: * @return the length of this file or 0 if it refers to a directory
2569: */
2570:
2571: public int getContentLength() {
2572: try {
2573: return (int) (length() & 0xFFFFFFFFL);
2574: } catch (SmbException se) {
2575: }
2576: return 0;
2577: }
2578:
2579: /**
2580: * This URLConnection method just returns the result of <tt>lastModified</tt>.
2581: *
2582: * @return the last modified data as milliseconds since Jan 1, 1970
2583: */
2584: public long getDate() {
2585: try {
2586: return lastModified();
2587: } catch (SmbException se) {
2588: }
2589: return 0L;
2590: }
2591:
2592: /**
2593: * This URLConnection method just returns the result of <tt>lastModified</tt>.
2594: *
2595: * @return the last modified data as milliseconds since Jan 1, 1970
2596: */
2597: public long getLastModified() {
2598: try {
2599: return lastModified();
2600: } catch (SmbException se) {
2601: }
2602: return 0L;
2603: }
2604:
2605: /**
2606: * This URLConnection method just returns a new <tt>SmbFileInputStream</tt> created with this file.
2607: *
2608: * @throws IOException thrown by <tt>SmbFileInputStream</tt> constructor
2609: */
2610: public InputStream getInputStream() throws IOException {
2611: return new SmbFileInputStream(this );
2612: }
2613:
2614: /**
2615: * This URLConnection method just returns a new <tt>SmbFileOutputStream</tt> created with this file.
2616: *
2617: * @throws IOException thrown by <tt>SmbFileOutputStream</tt> constructor
2618: */
2619: public OutputStream getOutputStream() throws IOException {
2620: return new SmbFileOutputStream(this);
2621: }
2622: }
|