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