KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > jcifs > smb > SmbFile


1 /* jcifs smb client library in Java
2  * Copyright (C) 2000 "Michael B. Allen" <jcifs at samba dot org>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17  */

18
19 package jcifs.smb;
20
21 import java.net.URLConnection JavaDoc;
22 import java.net.URL JavaDoc;
23 import java.net.MalformedURLException JavaDoc;
24 import java.net.UnknownHostException JavaDoc;
25 import java.io.IOException JavaDoc;
26 import java.io.InputStream JavaDoc;
27 import java.io.OutputStream JavaDoc;
28 import java.util.ArrayList JavaDoc;
29 import java.security.Principal JavaDoc;
30 import jcifs.Config;
31 import jcifs.util.LogStream;
32 import jcifs.UniAddress;
33 import jcifs.netbios.NbtAddress;
34
35 import java.util.Date JavaDoc;
36
37 /**
38  * This class represents a resource on an SMB network. Mainly these
39  * resources are files and directories however an <code>SmbFile</code>
40  * may also refer to servers and workgroups. If the resource is a file or
41  * directory the methods of <code>SmbFile</code> follow the behavior of
42  * the well known {@link java.io.File} class. One fundamental difference
43  * is the usage of a URL scheme [1] to specify the target file or
44  * directory. SmbFile URLs have the following syntax:
45  *
46  * <blockquote><pre>
47  * smb://[[[domain;]username[:password]@]server[:port]/[[share/[dir/]file]]][?[param=value[param2=value2[...]]]
48  * </pre></blockquote>
49  *
50  * This example:
51  *
52  * <blockquote><pre>
53  * smb://storage15/public/foo.txt
54  * </pre></blockquote>
55  *
56  * would reference the file <code>foo.txt</code> in the share
57  * <code>public</code> on the server <code>storage15</code>. In addition
58  * to referencing files and directories, jCIFS can also address servers,
59  * and workgroups.
60  * <p>
61  * <font color="#800000"><i>Important: all SMB URLs that represent
62  * workgroups, servers, shares, or directories require a trailing slash '/'.
63  * </i></font>
64  * <p>
65  * When using the <tt>java.net.URL</tt> class with
66  * 'smb://' URLs it is necessary to first call the static
67  * <tt>jcifs.Config.registerSmbURLHandler();</tt> method. This is required
68  * to register the SMB protocol handler.
69  * <p>
70  * The userinfo component of the SMB URL (<tt>domain;user:pass</tt>) must
71  * be URL encoded if it contains reserved characters. According to RFC 2396
72  * these characters are non US-ASCII characters and most meta characters
73  * however jCIFS will work correctly with anything but '@' which is used
74  * to delimit the userinfo component from the server and '%' which is the
75  * URL escape character itself.
76  * <p>
77  * The server
78  * component may a traditional NetBIOS name, a DNS name, or IP
79  * address. These name resolution mechanisms and their resolution order
80  * can be changed (See <a HREF="../../../resolver.html">Setting Name
81  * Resolution Properties</a>). The servername and path components are
82  * not case sensitive but the domain, username, and password components
83  * are. It is also likely that properties must be specified for jcifs
84  * to function (See <a HREF="../../overview-summary.html#scp">Setting
85  * JCIFS Properties</a>). Here are some examples of SMB URLs with brief
86  * descriptions of what they do:
87  *
88  * <p>[1] This URL scheme is based largely on the <i>SMB
89  * Filesharing URL Scheme</i> IETF draft.
90  *
91  * <p><table border="1" cellpadding="3" cellspacing="0" width="100%">
92  * <tr bgcolor="#ccccff">
93  * <td colspan="2"><b>SMB URL Examples</b></td>
94  * <tr><td width="20%"><b>URL</b></td><td><b>Description</b></td></tr>
95  *
96  * <tr><td width="20%"><code>smb://users-nyc;miallen:mypass@angus/tmp/</code></td><td>
97  * This URL references a share called <code>tmp</code> on the server
98  * <code>angus</code> as user <code>miallen</code> who's password is
99  * <code>mypass</code>.
100  * </td></tr>
101  *
102  * <tr><td width="20%">
103  * <code>smb://Administrator:P%40ss@msmith1/c/WINDOWS/Desktop/foo.txt</code></td><td>
104  * A relativly sophisticated example that references a file
105  * <code>msmith1</code>'s desktop as user <code>Administrator</code>. Notice the '@' is URL encoded with the '%40' hexcode escape.
106  * </td></tr>
107  *
108  * <tr><td width="20%"><code>smb://angus/</code></td><td>
109  * This references only a server. The behavior of some methods is different
110  * in this context(e.g. you cannot <code>delete</code> a server) however
111  * as you might expect the <code>list</code> method will list the available
112  * shares on this server.
113  * </td></tr>
114  *
115  * <tr><td width="20%"><code>smb://myworkgroup/</code></td><td>
116  * This syntactically is identical to the above example. However if
117  * <code>myworkgroup</code> happends to be a workgroup(which is indeed
118  * suggested by the name) the <code>list</code> method will return
119  * a list of servers that have registered themselves as members of
120  * <code>myworkgroup</code>.
121  * </td></tr>
122  *
123  * <tr><td width="20%"><code>smb://</code></td><td>
124  * Just as <code>smb://server/</code> lists shares and
125  * <code>smb://workgroup/</code> lists servers, the <code>smb://</code>
126  * URL lists all available workgroups on a netbios LAN. Again,
127  * in this context many methods are not valid and return default
128  * values(e.g. <code>isHidden</code> and <code>renameTo</code> will always
129  * return false).
130  * </td></tr>
131  *
132  * <tr><td width="20%"><code>smb://angus.foo.net/d/jcifs/pipes.doc</code></td><td>
133  * The server name may also be a DNS name as it is in this example. See
134  * <a HREF="../../../resolver.html">Setting Name Resolution Properties</a>
135  * for details.
136  * </td></tr>
137  *
138  * <tr><td width="20%"><code>smb://192.168.1.15/ADMIN$/</code></td><td>
139  * The server name may also be an IP address. See <a
140  * HREF="../../../resolver.html">Setting Name Resolution Properties</a>
141  * for details.
142  * </td></tr>
143  *
144  * <tr><td width="20%">
145  * <code>smb://domain;username:password@server/share/path/to/file.txt</code></td><td>
146  * A prototypical example that uses all the fields.
147  * </td></tr>
148  *
149  * <tr><td width="20%"><code>smb://myworkgroup/angus/ &lt;-- ILLEGAL </code></td><td>
150  * Despite the hierarchial relationship between workgroups, servers, and
151  * filesystems this example is not valid.
152  * </td></tr>
153  *
154  * <tr><td width="20%">
155  * <code>smb://server/share/path/to/dir &lt;-- ILLEGAL </code></td><td>
156  * URLs that represent workgroups, servers, shares, or directories require a trailing slash '/'.
157  * </td></tr>
158  *
159  * <tr><td width="20%">
160  * <code>smb://MYGROUP/?SERVER=192.168.10.15</code></td><td>
161  * SMB URLs support some query string parameters. In this example
162  * the <code>SERVER</code> parameter is used to override the
163  * server name service lookup to contact the server 192.168.10.15
164  * (presumably known to be a master
165  * browser) for the server list in workgroup <code>MYGROUP</code>.
166  * </td></tr>
167  *
168  * </table>
169  *
170  * <p>A second constructor argument may be specified to augment the URL
171  * for better programmatic control when processing many files under
172  * a common base. This is slightly different from the corresponding
173  * <code>java.io.File</code> usage; a '/' at the beginning of the second
174  * parameter will still use the server component of the first parameter. The
175  * examples below illustrate the resulting URLs when this second contructor
176  * argument is used.
177  *
178  * <p><table border="1" cellpadding="3" cellspacing="0" width="100%">
179  * <tr bgcolor="#ccccff">
180  * <td colspan="3">
181  * <b>Examples Of SMB URLs When Augmented With A Second Constructor Parameter</b></td>
182  * <tr><td width="20%">
183  * <b>First Parameter</b></td><td><b>Second Parameter</b></td><td><b>Result</b></td></tr>
184  *
185  * <tr><td width="20%"><code>
186  * smb://host/share/a/b/
187  * </code></td><td width="20%"><code>
188  * c/d/
189  * </code></td><td><code>
190  * smb://host/share/a/b/c/d/
191  * </code></td></tr>
192  *
193  * <tr><td width="20%"><code>
194  * smb://host/share/foo/bar/
195  * </code></td><td width="20%"><code>
196  * /share2/zig/zag
197  * </code></td><td><code>
198  * smb://host/share2/zig/zag
199  * </code></td></tr>
200  *
201  * <tr><td width="20%"><code>
202  * smb://host/share/foo/bar/
203  * </code></td><td width="20%"><code>
204  * ../zip/
205  * </code></td><td><code>
206  * smb://host/share/foo/zip/
207  * </code></td></tr>
208  *
209  * <tr><td width="20%"><code>
210  * smb://host/share/zig/zag
211  * </code></td><td width="20%"><code>
212  * smb://foo/bar/
213  * </code></td><td><code>
214  * smb://foo/bar/
215  * </code></td></tr>
216  *
217  * <tr><td width="20%"><code>
218  * smb://host/share/foo/
219  * </code></td><td width="20%"><code>
220  * ../.././.././../foo/
221  * </code></td><td><code>
222  * smb://host/foo/
223  * </code></td></tr>
224  *
225  * <tr><td width="20%"><code>
226  * smb://host/share/zig/zag
227  * </code></td><td width="20%"><code>
228  * /
229  * </code></td><td><code>
230  * smb://host/
231  * </code></td></tr>
232  *
233  * <tr><td width="20%"><code>
234  * smb://server/
235  * </code></td><td width="20%"><code>
236  * ../
237  * </code></td><td><code>
238  * smb://server/
239  * </code></td></tr>
240  *
241  * <tr><td width="20%"><code>
242  * smb://
243  * </code></td><td width="20%"><code>
244  * myworkgroup/
245  * </code></td><td><code>
246  * smb://myworkgroup/
247  * </code></td></tr>
248  *
249  * <tr><td width="20%"><code>
250  * smb://myworkgroup/
251  * </code></td><td width="20%"><code>
252  * angus/
253  * </code></td><td><code>
254  * smb://myworkgroup/angus/ &lt;-- ILLEGAL<br>(But if you first create an <tt>SmbFile</tt> with 'smb://workgroup/' and use and use it as the first parameter to a constructor that accepts it with a second <tt>String</tt> parameter jCIFS will factor out the 'workgroup'.)
255  * </code></td></tr>
256  *
257  * </table>
258  *
259  * <p>Instances of the <code>SmbFile</code> class are immutable; that is,
260  * once created, the abstract pathname represented by an SmbFile object
261  * will never change.
262  *
263  * @see java.io.File
264  */

265
266 public class SmbFile extends URLConnection JavaDoc {
267
268     // these are shifted for use in flags
269
static final int O_RDONLY = 0x010000;
270     static final int O_WRONLY = 0x020000;
271     static final int O_RDWR = 0x030000;
272     static final int O_APPEND = 0x040000;
273
274     // share access
275
/**
276  * When specified as the <tt>shareAccess</tt> constructor parameter,
277  * other SMB clients (including other threads making calls into jCIFS)
278  * will not be permitted to access the target file and will receive "The
279  * file is being accessed by another process" message.
280  */

281     public static final int FILE_NO_SHARE = 0x00;
282 /**
283  * When specified as the <tt>shareAccess</tt> constructor parameter,
284  * other SMB clients will be permitted to read from the target file while
285  * this file is open. This constant may be logically OR'd with other share
286  * access flags.
287  */

288     public static final int FILE_SHARE_READ = 0x01;
289 /**
290  * When specified as the <tt>shareAccess</tt> constructor parameter,
291  * other SMB clients will be permitted to write to the target file while
292  * this file is open. This constant may be logically OR'd with other share
293  * access flags.
294  */

295     public static final int FILE_SHARE_WRITE = 0x02;
296 /**
297  * When specified as the <tt>shareAccess</tt> constructor parameter,
298  * other SMB clients will be permitted to delete the target file while
299  * this file is open. This constant may be logically OR'd with other share
300  * access flags.
301  */

302     public static final int FILE_SHARE_DELETE = 0x04;
303
304     // Open Function Encoding
305
// create if the file does not exist
306
static final int O_CREAT = 0x0010;
307     // fail if the file exists
308
static final int O_EXCL = 0x0001;
309     // truncate if the file exists
310
static final int O_TRUNC = 0x0002;
311
312     // file attribute encoding
313
/**
314  * A file with this bit on as returned by <tt>getAttributes()</tt> or set
315  * with <tt>setAttributes()</tt> will be read-only
316  */

317     public static final int ATTR_READONLY = 0x01;
318 /**
319  * A file with this bit on as returned by <tt>getAttributes()</tt> or set
320  * with <tt>setAttributes()</tt> will be hidden
321  */

322     public static final int ATTR_HIDDEN = 0x02;
323 /**
324  * A file with this bit on as returned by <tt>getAttributes()</tt> or set
325  * with <tt>setAttributes()</tt> will be a system file
326  */

327     public static final int ATTR_SYSTEM = 0x04;
328 /**
329  * A file with this bit on as returned by <tt>getAttributes()</tt> is
330  * a volume
331  */

332     public static final int ATTR_VOLUME = 0x08;
333 /**
334  * A file with this bit on as returned by <tt>getAttributes()</tt> is
335  * a directory
336  */

337     public static final int ATTR_DIRECTORY = 0x10;
338 /**
339  * A file with this bit on as returned by <tt>getAttributes()</tt> or set
340  * with <tt>setAttributes()</tt> is an archived file
341  */

342     public static final int ATTR_ARCHIVE = 0x20;
343
344     // extended file attribute encoding(others same as above)
345
static final int ATTR_COMPRESSED = 0x800;
346     static final int ATTR_NORMAL = 0x080;
347     static final int ATTR_TEMPORARY = 0x100;
348
349     static final int ATTR_GET_MASK = 0x7FFF; /* orig 0x7fff */
350     static final int ATTR_SET_MASK = 0x30A7; /* orig 0x0027 */
351
352     static final int DEFAULT_ATTR_EXPIRATION_PERIOD = 5000;
353
354     static final int HASH_DOT = ".".hashCode();
355     static final int HASH_DOT_DOT = "..".hashCode();
356
357     static LogStream log = LogStream.getInstance();
358     static long attrExpirationPeriod;
359
360     static {
361         try {
362             Class.forName( "jcifs.Config" );
363         } catch( ClassNotFoundException JavaDoc cnfe ) {
364             cnfe.printStackTrace();
365         }
366         attrExpirationPeriod = Config.getLong( "jcifs.smb.client.attrExpirationPeriod", DEFAULT_ATTR_EXPIRATION_PERIOD );
367     }
368
369     /**
370      * Returned by {@link #getType()} if the resource this <tt>SmbFile</tt>
371      * represents is a regular file or directory.
372      */

373     public static final int TYPE_FILESYSTEM = 0x01;
374     /**
375      * Returned by {@link #getType()} if the resource this <tt>SmbFile</tt>
376      * represents is a workgroup.
377      */

378     public static final int TYPE_WORKGROUP = 0x02;
379     /**
380      * Returned by {@link #getType()} if the resource this <tt>SmbFile</tt>
381      * represents is a server.
382      */

383     public static final int TYPE_SERVER = 0x04;
384     /**
385      * Returned by {@link #getType()} if the resource this <tt>SmbFile</tt>
386      * represents is a share.
387      */

388     public static final int TYPE_SHARE = 0x08;
389     /**
390      * Returned by {@link #getType()} if the resource this <tt>SmbFile</tt>
391      * represents is a named pipe.
392      */

393     public static final int TYPE_NAMED_PIPE = 0x10;
394     /**
395      * Returned by {@link #getType()} if the resource this <tt>SmbFile</tt>
396      * represents is a printer.
397      */

398     public static final int TYPE_PRINTER = 0x20;
399     /**
400      * Returned by {@link #getType()} if the resource this <tt>SmbFile</tt>
401      * represents is a communications device.
402      */

403     public static final int TYPE_COMM = 0x40;
404
405
406     private String JavaDoc canon; // Initially null; set by getUncPath; dir must end with '/'
407
private String JavaDoc share; // Can be null
408
private long createTime;
409     private long lastModified;
410     private int attributes;
411     private long attrExpiration;
412     private long size;
413     private long sizeExpiration;
414     private NtlmPasswordAuthentication auth; // Cannot be null
415
private boolean isExists;
416     private int shareAccess = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
417     private SmbComBlankResponse blank_resp = null;
418     private DfsReferral dfsReferral = null; // Only used by getDfsPath()
419

420     SmbTree tree = null; // Initially null; may be !tree.treeConnected
421
String JavaDoc unc; // Initially null; set by getUncPath; never ends with '/'
422
int fid; // Initially 0; set by open()
423
int type;
424     boolean opened;
425     int tree_num;
426
427 /**
428  * Constructs an SmbFile representing a resource on an SMB network such as
429  * a file or directory. See the description and examples of smb URLs above.
430  *
431  * @param url A URL string
432  * @throws MalformedURLException
433  * If the <code>parent</code> and <code>child</code> parameters
434  * do not follow the prescribed syntax
435  */

436
437     public SmbFile( String JavaDoc url ) throws MalformedURLException JavaDoc {
438         this( new URL JavaDoc( null, url, Handler.SMB_HANDLER ));
439     }
440
441 /**
442  * Constructs an SmbFile representing a resource on an SMB network such
443  * as a file or directory. The second parameter is a relative path from
444  * the <code>parent SmbFile</code>. See the description above for examples
445  * of using the second <code>name</code> parameter.
446  *
447  * @param context A base <code>SmbFile</code>
448  * @param name A path string relative to the <code>parent</code> paremeter
449  * @throws MalformedURLException
450  * If the <code>parent</code> and <code>child</code> parameters
451  * do not follow the prescribed syntax
452  * @throws UnknownHostException
453  * If the server or workgroup of the <tt>context</tt> file cannot be determined
454  */

455
456     public SmbFile( SmbFile context, String JavaDoc name )
457                 throws MalformedURLException JavaDoc, UnknownHostException JavaDoc {
458         this( context.isWorkgroup0() ?
459             new URL JavaDoc( null, "smb://" + name, Handler.SMB_HANDLER ) :
460             new URL JavaDoc( context.url, name, Handler.SMB_HANDLER ), context.auth );
461     }
462
463 /**
464  * Constructs an SmbFile representing a resource on an SMB network such
465  * as a file or directory. The second parameter is a relative path from
466  * the <code>parent</code>. See the description above for examples of
467  * using the second <code>chile</code> parameter.
468  *
469  * @param context A URL string
470  * @param name A path string relative to the <code>context</code> paremeter
471  * @throws MalformedURLException
472  * If the <code>context</code> and <code>name</code> parameters
473  * do not follow the prescribed syntax
474  */

475
476     public SmbFile( String JavaDoc context, String JavaDoc name ) throws MalformedURLException JavaDoc {
477         this( new URL JavaDoc( new URL JavaDoc( null, context, Handler.SMB_HANDLER ),
478                 name, Handler.SMB_HANDLER ));
479     }
480
481 /**
482  * Constructs an SmbFile representing a resource on an SMB network such
483  * as a file or directory.
484  *
485  * @param url A URL string
486  * @param auth The credentials the client should use for authentication
487  * @throws MalformedURLException
488  * If the <code>url</code> parameter does not follow the prescribed syntax
489  */

490     public SmbFile( String JavaDoc url, NtlmPasswordAuthentication auth )
491                     throws MalformedURLException JavaDoc {
492         this( new URL JavaDoc( null, url, Handler.SMB_HANDLER ), auth );
493     }
494 /**
495  * Constructs an SmbFile representing a file on an SMB network. The
496  * <tt>shareAccess</tt> parameter controls what permissions other
497  * clients have when trying to access the same file while this instance
498  * is still open. This value is either <tt>FILE_NO_SHARE</tt> or any
499  * combination of <tt>FILE_SHARE_READ</tt>, <tt>FILE_SHARE_WRITE</tt>,
500  * and <tt>FILE_SHARE_DELETE</tt> logically OR'd together.
501  *
502  * @param url A URL string
503  * @param auth The credentials the client should use for authentication
504  * @param shareAccess Specifies what access other clients have while this file is open.
505  * @throws MalformedURLException
506  * If the <code>url</code> parameter does not follow the prescribed syntax
507  */

508     public SmbFile( String JavaDoc url, NtlmPasswordAuthentication auth, int shareAccess )
509                     throws MalformedURLException JavaDoc {
510         this( new URL JavaDoc( null, url, Handler.SMB_HANDLER ), auth );
511         if ((shareAccess & ~(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE)) != 0) {
512             throw new RuntimeException JavaDoc( "Illegal shareAccess parameter" );
513         }
514         this.shareAccess = shareAccess;
515     }
516 /**
517  * Constructs an SmbFile representing a resource on an SMB network such
518  * as a file or directory. The second parameter is a relative path from
519  * the <code>context</code>. See the description above for examples of
520  * using the second <code>name</code> parameter.
521  *
522  * @param context A URL string
523  * @param name A path string relative to the <code>context</code> paremeter
524  * @param auth The credentials the client should use for authentication
525  * @throws MalformedURLException
526  * If the <code>context</code> and <code>name</code> parameters
527  * do not follow the prescribed syntax
528  */

529     public SmbFile( String JavaDoc context, String JavaDoc name, NtlmPasswordAuthentication auth )
530                     throws MalformedURLException JavaDoc {
531         this( new URL JavaDoc( new URL JavaDoc( null, context, Handler.SMB_HANDLER ), name, Handler.SMB_HANDLER ), auth );
532     }
533 /**
534  * Constructs an SmbFile representing a resource on an SMB network such
535  * as a file or directory. The second parameter is a relative path from
536  * the <code>context</code>. See the description above for examples of
537  * using the second <code>name</code> parameter. The <tt>shareAccess</tt>
538  * parameter controls what permissions other clients have when trying
539  * to access the same file while this instance is still open. This
540  * value is either <tt>FILE_NO_SHARE</tt> or any combination
541  * of <tt>FILE_SHARE_READ</tt>, <tt>FILE_SHARE_WRITE</tt>, and
542  * <tt>FILE_SHARE_DELETE</tt> logically OR'd together.
543  *
544  * @param context A URL string
545  * @param name A path string relative to the <code>context</code> paremeter
546  * @param auth The credentials the client should use for authentication
547  * @param shareAccess Specifies what access other clients have while this file is open.
548  * @throws MalformedURLException
549  * If the <code>context</code> and <code>name</code> parameters
550  * do not follow the prescribed syntax
551  */

552     public SmbFile( String JavaDoc context, String JavaDoc name, NtlmPasswordAuthentication auth, int shareAccess )
553                     throws MalformedURLException JavaDoc {
554         this( new URL JavaDoc( new URL JavaDoc( null, context, Handler.SMB_HANDLER ), name, Handler.SMB_HANDLER ), auth );
555         if ((shareAccess & ~(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE)) != 0) {
556             throw new RuntimeException JavaDoc( "Illegal shareAccess parameter" );
557         }
558         this.shareAccess = shareAccess;
559     }
560 /**
561  * Constructs an SmbFile representing a resource on an SMB network such
562  * as a file or directory. The second parameter is a relative path from
563  * the <code>context</code>. See the description above for examples of
564  * using the second <code>name</code> parameter. The <tt>shareAccess</tt>
565  * parameter controls what permissions other clients have when trying
566  * to access the same file while this instance is still open. This
567  * value is either <tt>FILE_NO_SHARE</tt> or any combination
568  * of <tt>FILE_SHARE_READ</tt>, <tt>FILE_SHARE_WRITE</tt>, and
569  * <tt>FILE_SHARE_DELETE</tt> logically OR'd together.
570  *
571  * @param context A base <code>SmbFile</code>
572  * @param name A path string relative to the <code>context</code> file path
573  * @param shareAccess Specifies what access other clients have while this file is open.
574  * @throws MalformedURLException
575  * If the <code>context</code> and <code>name</code> parameters
576  * do not follow the prescribed syntax
577  */

578     public SmbFile( SmbFile context, String JavaDoc name, int shareAccess )
579                     throws MalformedURLException JavaDoc, UnknownHostException JavaDoc {
580         this( context.isWorkgroup0() ?
581             new URL JavaDoc( null, "smb://" + name, Handler.SMB_HANDLER ) :
582             new URL JavaDoc( context.url, name, Handler.SMB_HANDLER ), context.auth );
583         if ((shareAccess & ~(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE)) != 0) {
584             throw new RuntimeException JavaDoc( "Illegal shareAccess parameter" );
585         }
586         this.shareAccess = shareAccess;
587     }
588 /**
589  * Constructs an SmbFile representing a resource on an SMB network such
590  * as a file or directory from a <tt>URL</tt> object.
591  *
592  * @param url The URL of the target resource
593  */

594     public SmbFile( URL JavaDoc url ) {
595         this( url, new NtlmPasswordAuthentication( url.getUserInfo() ));
596     }
597 /**
598  * Constructs an SmbFile representing a resource on an SMB network such
599  * as a file or directory from a <tt>URL</tt> object and an
600  * <tt>NtlmPasswordAuthentication</tt> object.
601  *
602  * @param url The URL of the target resource
603  * @param auth The credentials the client should use for authentication
604  */

605     public SmbFile( URL JavaDoc url, NtlmPasswordAuthentication auth ) {
606         super( url );
607         this.auth = auth == null ? new NtlmPasswordAuthentication( url.getUserInfo() ) : auth;
608
609         getUncPath0();
610     }
611     SmbFile( SmbFile context, String JavaDoc name, int type,
612                 int attributes, long createTime, long lastModified, long size )
613                 throws MalformedURLException JavaDoc, UnknownHostException JavaDoc {
614         this( context.isWorkgroup0() ?
615             new URL JavaDoc( null, "smb://" + name + "/", Handler.SMB_HANDLER ) :
616             new URL JavaDoc( context.url, name + (( attributes & ATTR_DIRECTORY ) > 0 ? "/" : "" )));
617
618
619         /* why was this removed before? DFS? copyTo? Am I going around in circles? */
620         auth = context.auth;
621
622
623         if( context.share != null ) {
624             this.tree = context.tree;
625         }
626         int last = name.length() - 1;
627         if( name.charAt( last ) == '/' ) {
628             name = name.substring( 0, last );
629         }
630         if( context.share == null ) {
631             this.unc = "\\";
632         } else if( context.unc.equals( "\\" )) {
633             this.unc = '\\' + name;
634         } else {
635             this.unc = context.unc + '\\' + name;
636         }
637     /* why? am I going around in circles?
638      * this.type = type == TYPE_WORKGROUP ? 0 : type;
639      */

640         this.type = type;
641         this.attributes = attributes;
642         this.createTime = createTime;
643         this.lastModified = lastModified;
644         this.size = size;
645         isExists = true;
646
647         attrExpiration = sizeExpiration =
648                 System.currentTimeMillis() + attrExpirationPeriod;
649     }
650
651     private SmbComBlankResponse blank_resp() {
652         if( blank_resp == null ) {
653             blank_resp = new SmbComBlankResponse();
654         }
655         return blank_resp;
656     }
657     void send( ServerMessageBlock request,
658                     ServerMessageBlock response ) throws SmbException {
659         for( ;; ) {
660             connect0();
661             if( tree.inDfs ) {
662                 DfsReferral dr = tree.session.transport.lookupReferral( unc );
663                 if( dr != null ) {
664                     UniAddress addr;
665                     SmbTransport trans;
666
667                     try {
668                         addr = UniAddress.getByName( dr.server );
669                     } catch( UnknownHostException JavaDoc uhe ) {
670                         throw new SmbException( dr.server, uhe );
671                     }
672
673                     trans = SmbTransport.getSmbTransport( addr, url.getPort() );
674                     tree = trans.getSmbSession( auth ).getSmbTree( dr.share, null );
675                     unc = dr.nodepath + unc.substring( dr.path.length() );
676                     if( request.path.charAt( request.path.length() - 1 ) == '\\' ) {
677                         request.path = unc + '\\'; /* preserve trailing slash */
678                     } else {
679                         request.path = unc;
680                     }
681                     dfsReferral = dr; /* for getDfsPath */
682                 }
683                 request.flags2 |= ServerMessageBlock.FLAGS2_RESOLVE_PATHS_IN_DFS;
684             } else {
685                 request.flags2 &= ~ServerMessageBlock.FLAGS2_RESOLVE_PATHS_IN_DFS;
686             }
687             try {
688                 tree.send( request, response );
689                 break;
690             } catch( DfsReferral dr ) {
691                 if( dr.resolveHashes ) {
692                     throw dr;
693                 }
694                 request.reset();
695             }
696         }
697     }
698
699     static String JavaDoc queryLookup( String JavaDoc query, String JavaDoc param ) {
700         char in[] = query.toCharArray();
701         int i, ch, st, eq;
702
703         st = eq = 0;
704         for( i = 0; i < in.length; i++) {
705             ch = in[i];
706             if( ch == '&' ) {
707                 if( eq > st ) {
708                     String JavaDoc p = new String JavaDoc( in, st, eq - st );
709                     if( p.equalsIgnoreCase( param )) {
710                         eq++;
711                         return new String JavaDoc( in, eq, i - eq );
712                     }
713                 }
714                 st = i + 1;
715             } else if( ch == '=' ) {
716                 eq = i;
717             }
718         }
719         if( eq > st ) {
720             String JavaDoc p = new String JavaDoc( in, st, eq - st );
721             if( p.equalsIgnoreCase( param )) {
722                 eq++;
723                 return new String JavaDoc( in, eq, in.length - eq );
724             }
725         }
726
727         return null;
728     }
729
730     UniAddress getAddress() throws UnknownHostException JavaDoc {
731         String JavaDoc host = url.getHost();
732         String JavaDoc path = url.getPath();
733         String JavaDoc query = url.getQuery();
734
735         if( query != null ) {
736             String JavaDoc server = queryLookup( query, "server" );
737             if( server != null && server.length() > 0 ) {
738                 return UniAddress.getByName( server );
739             }
740         }
741
742         if( host.length() == 0 ) {
743             try {
744                 NbtAddress addr = NbtAddress.getByName(
745                         NbtAddress.MASTER_BROWSER_NAME, 0x01, null);
746                 return UniAddress.getByName( addr.getHostAddress() );
747             } catch( UnknownHostException JavaDoc uhe ) {
748                 NtlmPasswordAuthentication.initDefaults();
749                 if( NtlmPasswordAuthentication.DEFAULT_DOMAIN.equals( "?" )) {
750                     throw uhe;
751                 }
752                 return UniAddress.getByName( NtlmPasswordAuthentication.DEFAULT_DOMAIN, true );
753             }
754         } else if( path.length() == 0 || path.equals( "/" )) {
755             return UniAddress.getByName( host, true );
756         } else {
757             return UniAddress.getByName( host );
758         }
759     }
760     void connect0() throws SmbException {
761         try {
762             connect();
763         } catch( UnknownHostException JavaDoc uhe ) {
764             throw new SmbException( "Failed to connect to server", uhe );
765         } catch( SmbException se ) {
766             throw se;
767         } catch( IOException JavaDoc ioe ) {
768             throw new SmbException( "Failed to connect to server", ioe );
769         }
770     }
771 /**
772  * It is not necessary to call this method directly. This is the
773  * <tt>URLConnection</tt> implementation of <tt>connect()</tt>.
774  */

775     public void connect() throws IOException JavaDoc {
776         SmbTransport trans;
777         SmbSession ssn;
778         UniAddress addr;
779
780         if( isConnected() ) {
781             return;
782         }
783
784         getUncPath0();
785         addr = getAddress();
786
787         trans = SmbTransport.getSmbTransport( addr, url.getPort() );
788         ssn = trans.getSmbSession( auth );
789         tree = ssn.getSmbTree( share, null );
790
791         try {
792             tree.treeConnect( null, null );
793         } catch( SmbAuthException sae ) {
794             NtlmPasswordAuthentication a;
795
796             if( share == null ) { // IPC$ - try "anonymous" credentials
797
ssn = trans.getSmbSession( NtlmPasswordAuthentication.NULL );
798                 tree = ssn.getSmbTree( null, null );
799                 tree.treeConnect( null, null );
800             } else if(( a = NtlmAuthenticator.requestNtlmPasswordAuthentication(
801                         url.toString(), sae )) != null ) {
802                 auth = a;
803                 ssn = trans.getSmbSession( auth );
804                 tree = ssn.getSmbTree( share, null );
805                 tree.treeConnect( null, null );
806             } else {
807                 throw sae;
808             }
809         }
810     }
811     boolean isConnected() {
812         return (connected = tree != null && tree.treeConnected);
813     }
814     int open0( int flags, int attrs, int options ) throws SmbException {
815         int f;
816
817         connect0();
818
819         if( log.level > 2 )
820             log.println( "open0: " + unc );
821
822         /*
823          * NT Create AndX / Open AndX Request / Response
824          */

825
826         if( tree.session.transport.hasCapability( ServerMessageBlock.CAP_NT_SMBS )) {
827             SmbComNTCreateAndXResponse response = new SmbComNTCreateAndXResponse();
828             send( new SmbComNTCreateAndX( unc, flags, shareAccess,
829                     attrs, options, null ), response );
830             f = response.fid;
831             attributes = response.extFileAttributes & ATTR_GET_MASK;
832             attrExpiration = System.currentTimeMillis() + attrExpirationPeriod;
833             isExists = true;
834         } else {
835             SmbComOpenAndXResponse response = new SmbComOpenAndXResponse();
836             send( new SmbComOpenAndX( unc, flags, null ), response );
837             f = response.fid;
838         }
839
840         return f;
841     }
842     void open( int flags, int attrs, int options ) throws SmbException {
843         if( isOpen() ) {
844             return;
845         }
846         fid = open0( flags, attrs, options );
847         opened = true;
848         tree_num = tree.tree_num;
849     }
850     boolean isOpen() {
851         return opened && isConnected() && tree_num == tree.tree_num;
852     }
853     void close( int f, long lastWriteTime ) throws SmbException {
854
855         if( log.level > 2 )
856             log.println( "close: " + f );
857
858         /*
859          * Close Request / Response
860          */

861
862         send( new SmbComClose( f, lastWriteTime ), blank_resp() );
863     }
864     void close( long lastWriteTime ) throws SmbException {
865         if( isOpen() == false ) {
866             return;
867         }
868         close( fid, lastWriteTime );
869         opened = false;
870     }
871     void close() throws SmbException {
872         close( 0L );
873     }
874
875 /**
876  * Returns the <tt>NtlmPasswordAuthentication</tt> object used as
877  * credentials with this file or pipe. This can be used to retrieve the
878  * username for example:
879  * <tt>
880  * String username = f.getPrincipal().getName();
881  * </tt>
882  * The <tt>Principal</tt> object returned will never be <tt>null</tt>
883  * however the username can be <tt>null</tt> indication anonymous
884  * credentials were used (e.g. some IPC$ services).
885  */

886
887     public Principal JavaDoc getPrincipal() {
888         return auth;
889     }
890
891 /**
892  * Returns the last component of the target URL. This will
893  * effectively be the name of the file or directory represented by this
894  * <code>SmbFile</code> or in the case of URLs that only specify a server
895  * or workgroup, the server or workgroup will be returned. The name of
896  * the root URL <code>smb://</code> is also <code>smb://</code>. If this
897  * <tt>SmbFile</tt> refers to a workgroup, server, share, or directory,
898  * the name will include a trailing slash '/' so that composing new
899  * <tt>SmbFile</tt>s will maintain the trailing slash requirement.
900  *
901  * @return The last component of the URL associated with this SMB
902  * resource or <code>smb://</code> if the resource is <code>smb://</code>
903  * itself.
904  */

905
906     public String JavaDoc getName() {
907         getUncPath0();
908         if( canon.length() > 1 ) {
909             int i = canon.length() - 2;
910             while( canon.charAt( i ) != '/' ) {
911                 i--;
912             }
913             return canon.substring( i + 1 );
914         } else if( share != null ) {
915             return share + '/';
916         } else if( url.getHost().length() > 0 ) {
917             return url.getHost() + '/';
918         } else {
919             return "smb://";
920         }
921     }
922
923 /**
924  * Everything but the last component of the URL representing this SMB
925  * resource is effectivly it's parent. The root URL <code>smb://</code>
926  * does not have a parent. In this case <code>smb://</code> is returned.
927  *
928  * @return The parent directory of this SMB resource or
929  * <code>smb://</code> if the resource refers to the root of the URL
930  * hierarchy which incedentally is also <code>smb://</code>.
931  */

932
933     public String JavaDoc getParent() {
934         String JavaDoc str = url.getAuthority();
935
936         if( str.length() > 0 ) {
937             StringBuffer JavaDoc sb = new StringBuffer JavaDoc( "smb://" );
938
939             sb.append( str );
940
941             getUncPath0();
942             if( canon.length() > 1 ) {
943                 sb.append( canon );
944             } else {
945                 sb.append( '/' );
946             }
947
948             str = sb.toString();
949
950             int i = str.length() - 2;
951             while( str.charAt( i ) != '/' ) {
952                 i--;
953             }
954
955             return str.substring( 0, i + 1 );
956         }
957
958         return "smb://";
959     }
960
961 /**
962  * Returns the full uncanonicalized URL of this SMB resource. An
963  * <code>SmbFile</code> constructed with the result of this method will
964  * result in an <code>SmbFile</code> that is equal to the original.
965  *
966  * @return The uncanonicalized full URL of this SMB resource.
967  */

968
969     public String JavaDoc getPath() {
970         return url.toString();
971     }
972
973     String JavaDoc getUncPath0() {
974         if( unc == null ) {
975             char[] in = url.getPath().toCharArray();
976             char[] out = new char[in.length];
977             int length = in.length, i, o, state, s;
978
979                               /* The canonicalization routine
980                                */

981             state = 0;
982             o = 0;
983             for( i = 0; i < length; i++ ) {
984                 switch( state ) {
985                     case 0:
986                         if( in[i] != '/' ) {
987                             return null;
988                         }
989                         out[o++] = in[i];
990                         state = 1;
991                         break;
992                     case 1:
993                         if( in[i] == '/' ) {
994                             break;
995                         } else if( in[i] == '.' &&
996                                     (( i + 1 ) >= length || in[i + 1] == '/' )) {
997                             i++;
998                             break;
999                         } else if(( i + 1 ) < length &&
1000                                    in[i] == '.' &&
1001                                    in[i + 1] == '.' &&
1002                                    (( i + 2 ) >= length || in[i + 2] == '/' )) {
1003                            i += 2;
1004                            if( o == 1 ) break;
1005                            do {
1006                                o--;
1007                            } while( o > 1 && out[o - 1] != '/' );
1008                            break;
1009                        }
1010                        state = 2;
1011                    case 2:
1012                        if( in[i] == '/' ) {
1013                            state = 1;
1014                        }
1015                        out[o++] = in[i];
1016                        break;
1017                }
1018            }
1019
1020            canon = new String JavaDoc( out, 0, o );
1021
1022            if( o > 1 ) {
1023                o--;
1024                i = canon.indexOf( '/', 1 );
1025                if( i < 0 ) {
1026                    share = canon.substring( 1 );
1027                    unc = "\\";
1028                } else if( i == o ) {
1029                    share = canon.substring( 1, i );
1030                    unc = "\\";
1031                } else {
1032                    share = canon.substring( 1, i );
1033                    unc = canon.substring( i, out[o] == '/' ? o : o + 1 );
1034                    unc = unc.replace( '/', '\\' );
1035                }
1036            } else {
1037                share = null;
1038                unc = "\\";
1039            }
1040        }
1041        return unc;
1042    }
1043/**
1044 * Retuns the Windows UNC style path with backslashs intead of forward slashes.
1045 *
1046 * @return The UNC path.
1047 */

1048    public String JavaDoc getUncPath() {
1049        getUncPath0();
1050        if( share == null ) {
1051            return "\\\\" + url.getHost();
1052        }
1053        return "\\\\" + url.getHost() + canon.replace( '/', '\\' );
1054    }
1055/**
1056 * Returns the full URL of this SMB resource with '.' and '..' components
1057 * factored out. An <code>SmbFile</code> constructed with the result of
1058 * this method will result in an <code>SmbFile</code> that is equal to
1059 * the original.
1060 *
1061 * @return The canonicalized URL of this SMB resource.
1062 */

1063
1064    public String JavaDoc getCanonicalPath() {
1065        String JavaDoc str = url.getAuthority();
1066        getUncPath0();
1067        if( str.length() > 0 ) {
1068            return "smb://" + url.getAuthority() + canon;
1069        }
1070        return "smb://";
1071    }
1072
1073/**
1074 * Retrieves the share associated with this SMB resource. In
1075 * the case of <code>smb://</code>, <code>smb://workgroup/</code>,
1076 * and <code>smb://server/</code> URLs which do not specify a share,
1077 * <code>null</code> will be returned.
1078 *
1079 * @return The share component or <code>null</code> if there is no share
1080 */

1081
1082    public String JavaDoc getShare() {
1083        return share;
1084    }
1085
1086/**
1087 * Retrieve the hostname of the server for this SMB resource. If this
1088 * <code>SmbFile</code> references a workgroup, the name of the workgroup
1089 * is returned. If this <code>SmbFile</code> refers to the root of this
1090 * SMB network hierarchy, <code>null</code> is returned.
1091 *
1092 * @return The server or workgroup name or <code>null</code> if this
1093 * <code>SmbFile</code> refers to the root <code>smb://</code> resource.
1094 */

1095
1096    public String JavaDoc getServer() {
1097        String JavaDoc str = url.getHost();
1098        if( str.length() == 0 ) {
1099            return null;
1100        }
1101        return str;
1102    }
1103
1104/**
1105 * Returns type of of object this <tt>SmbFile</tt> represents.
1106 * @return <tt>TYPE_FILESYSTEM, TYPE_WORKGROUP, TYPE_SERVER, TYPE_SHARE,
1107 * TYPE_PRINTER, TYPE_NAMED_PIPE</tt>, or <tt>TYPE_COMM</tt>.
1108 */

1109    public int getType() throws SmbException {
1110        if( type == 0 ) {
1111            if( getUncPath0().length() > 1 ) {
1112                type = TYPE_FILESYSTEM;
1113            } else if( share != null ) {
1114                // treeConnect good enough to test service type
1115
connect0();
1116                if( share.equals( "IPC$" )) {
1117                    type = TYPE_NAMED_PIPE;
1118                } else if( tree.service.equals( "LPT1:" )) {
1119                    type = TYPE_PRINTER;
1120                } else if( tree.service.equals( "COMM" )) {
1121                    type = TYPE_COMM;
1122                } else {
1123                    type = TYPE_SHARE;
1124                }
1125            } else if( url.getAuthority().length() == 0 ) {
1126                type = TYPE_WORKGROUP;
1127            } else {
1128                UniAddress addr;
1129                try {
1130                    addr = getAddress();
1131                } catch( UnknownHostException JavaDoc uhe ) {
1132                    throw new SmbException( url.toString(), uhe );
1133                }
1134                if( addr.getAddress() instanceof NbtAddress ) {
1135                    int code = ((NbtAddress)addr.getAddress()).getNameType();
1136                    if( code == 0x1d || code == 0x1b ) {
1137                        type = TYPE_WORKGROUP;
1138                        return type;
1139                    }
1140                }
1141                type = TYPE_SERVER;
1142            }
1143        }
1144        return type;
1145    }
1146    boolean isWorkgroup0() throws UnknownHostException JavaDoc {
1147        if( type == TYPE_WORKGROUP || url.getHost().length() == 0 ) {
1148            type = TYPE_WORKGROUP;
1149            return true;
1150        } else {
1151            getUncPath0();
1152            if( share == null ) {
1153                UniAddress addr = getAddress();
1154                if( addr.getAddress() instanceof NbtAddress ) {
1155                    int code = ((NbtAddress)addr.getAddress()).getNameType();
1156                    if( code == 0x1d || code == 0x1b ) {
1157                        type = TYPE_WORKGROUP;
1158                        return true;
1159                    }
1160                }
1161                type = TYPE_SERVER;
1162            }
1163        }
1164        return false;
1165    }
1166
1167    Info queryPath( String JavaDoc path, int infoLevel ) throws SmbException {
1168        connect0();
1169
1170        if( log.level > 2 )
1171            log.println( "queryPath: " + path );
1172
1173        /* normally we'd check the negotiatedCapabilities for CAP_NT_SMBS
1174         * however I can't seem to get a good last modified time from
1175         * SMB_COM_QUERY_INFORMATION so if NT_SMBs are requested
1176         * by the server than in this case that's what it will get
1177         * regardless of what jcifs.smb.client.useNTSmbs is set
1178         * to(overrides negotiatedCapabilities).
1179         */

1180
1181        /* We really should do the referral before this in case
1182         * the redirected target has different capabilities. But
1183         * the way we have been doing that is to call exists() which
1184         * calls this method so another technique will be necessary
1185         * to support DFS referral _to_ Win95/98/ME.
1186         */

1187
1188        if( tree.session.transport.hasCapability( ServerMessageBlock.CAP_NT_SMBS )) {
1189
1190            /*
1191             * Trans2 Query Path Information Request / Response
1192             */

1193
1194            Trans2QueryPathInformationResponse response =
1195                    new Trans2QueryPathInformationResponse( infoLevel );
1196            send( new Trans2QueryPathInformation( path, infoLevel ), response );
1197
1198            return response.info;
1199        } else {
1200
1201            /*
1202             * Query Information Request / Response
1203             */

1204
1205            SmbComQueryInformationResponse response =
1206                    new SmbComQueryInformationResponse(
1207                    tree.session.transport.server.serverTimeZone * 1000 * 60L );
1208            send( new SmbComQueryInformation( path ), response );
1209            return response;
1210        }
1211    }
1212
1213/**
1214 * Tests to see if the SMB resource exists. If the resource refers
1215 * only to a server, this method determines if the server exists on the
1216 * network and is advertising SMB services. If this resource refers to
1217 * a workgroup, this method determines if the workgroup name is valid on
1218 * the local SMB network. If this <code>SmbFile</code> refers to the root
1219 * <code>smb://</code> resource <code>true</code> is always returned. If
1220 * this <code>SmbFile</code> is a traditional file or directory, it will
1221 * be queried for on the specified server as expected.
1222 *
1223 * @return <code>true</code> if the resource exists or is alive or
1224 * <code>false</code> otherwise
1225 */

1226
1227    public boolean exists() throws SmbException {
1228
1229        if( attrExpiration > System.currentTimeMillis() ) {
1230            return isExists;
1231        }
1232
1233        attributes = ATTR_READONLY | ATTR_DIRECTORY;
1234        createTime = 0L;
1235        lastModified = 0L;
1236        isExists = false;
1237
1238        try {
1239            if( url.getHost().length() == 0 ) {
1240            } else if( share == null ) {
1241                if( getType() == TYPE_WORKGROUP ) {
1242                    UniAddress.getByName( url.getHost(), true );
1243                } else {
1244                    UniAddress.getByName( url.getHost() ).getHostName();
1245                }
1246            } else if( getUncPath0().length() == 1 ||
1247                                        share.equalsIgnoreCase( "IPC$" )) {
1248                connect0(); // treeConnect is good enough
1249
} else {
1250                Info info = queryPath( getUncPath0(),
1251                    Trans2QueryPathInformationResponse.SMB_QUERY_FILE_BASIC_INFO );
1252                attributes = info.getAttributes();
1253                createTime = info.getCreateTime();
1254                lastModified = info.getLastWriteTime();
1255            }
1256
1257            /* If any of the above fail, isExists will not be set true
1258             */

1259
1260            isExists = true;
1261
1262        } catch( UnknownHostException JavaDoc uhe ) {
1263        } catch( SmbException se ) {
1264            switch (se.getNtStatus()) {
1265                case NtStatus.NT_STATUS_NO_SUCH_FILE:
1266                case NtStatus.NT_STATUS_OBJECT_NAME_INVALID:
1267                case NtStatus.NT_STATUS_OBJECT_NAME_NOT_FOUND:
1268                case NtStatus.NT_STATUS_OBJECT_PATH_NOT_FOUND:
1269                    break;
1270                default:
1271                    throw se;
1272            }
1273        }
1274
1275        attrExpiration = System.currentTimeMillis() + attrExpirationPeriod;
1276
1277        return isExists;
1278    }
1279
1280/**
1281 * Tests to see if the file this <code>SmbFile</code> represents can be
1282 * read. Because any file, directory, or other resource can be read if it
1283 * exists, this method simply calls the <code>exists</code> method.
1284 *
1285 * @return <code>true</code> if the file is read-only
1286 */

1287
1288    public boolean canRead() throws SmbException {
1289        if( getType() == TYPE_NAMED_PIPE ) { // try opening the pipe for reading?
1290
return true;
1291        }
1292        return exists(); // try opening and catch sharing violation?
1293
}
1294
1295/**
1296 * Tests to see if the file this <code>SmbFile</code> represents
1297 * exists and is not marked read-only. By default, resources are
1298 * considered to be read-only and therefore for <code>smb://</code>,
1299 * <code>smb://workgroup/</code>, and <code>smb://server/</code> resources
1300 * will be read-only.
1301 *
1302 * @return <code>true</code> if the resource exists is not marked
1303 * read-only
1304 */

1305
1306    public boolean canWrite() throws SmbException {
1307        if( getType() == TYPE_NAMED_PIPE ) { // try opening the pipe for writing?
1308
return true;
1309        }
1310        return exists() && ( attributes & ATTR_READONLY ) == 0;
1311    }
1312
1313/**
1314 * Tests to see if the file this <code>SmbFile</code> represents is a directory.
1315 *
1316 * @return <code>true</code> if this <code>SmbFile</code> is a directory
1317 */

1318
1319    public boolean isDirectory() throws SmbException {
1320        if( getUncPath0().length() == 1 ) {
1321            return true;
1322        }
1323        if (!exists()) return false;
1324        return ( attributes & ATTR_DIRECTORY ) == ATTR_DIRECTORY;
1325    }
1326
1327/**
1328 * Tests to see if the file this <code>SmbFile</code> represents is not a directory.
1329 *
1330 * @return <code>true</code> if this <code>SmbFile</code> is not a directory
1331 */

1332
1333    public boolean isFile() throws SmbException {
1334        if( getUncPath0().length() == 1 ) {
1335            return false;
1336        }
1337        exists();
1338        return ( attributes & ATTR_DIRECTORY ) == 0;
1339    }
1340
1341/**
1342 * Tests to see if the file this SmbFile represents is marked as
1343 * hidden. This method will also return true for shares with names that
1344 * end with '$' such as <code>IPC$</code> or <code>C$</code>.
1345 *
1346 * @return <code>true</code> if the <code>SmbFile</code> is marked as being hidden
1347 */

1348
1349    public boolean isHidden() throws SmbException {
1350        if( share == null ) {
1351            return false;
1352        } else if( getUncPath0().length() == 1 ) {
1353            if( share.endsWith( "$" )) {
1354                return true;
1355            }
1356            return false;
1357        }
1358        exists();
1359        return ( attributes & ATTR_HIDDEN ) == ATTR_HIDDEN;
1360    }
1361
1362/**
1363 * If the path of this <code>SmbFile</code> falls within a DFS volume,
1364 * this method will return the referral path to which it maps. Otherwise
1365 * <code>null</code> is returned.
1366 */

1367
1368    public String JavaDoc getDfsPath() throws SmbException {
1369        connect0();
1370        if( tree.inDfs ) {
1371            exists();
1372        }
1373        if( dfsReferral == null ) {
1374            return null;
1375        }
1376        String JavaDoc path = "smb:/" + (new String JavaDoc( dfsReferral.node + unc )).replace( '\\', '/' );
1377        if (isDirectory()) {
1378            path += '/';
1379        }
1380        return path;
1381    }
1382
1383/**
1384 * Retrieve the time this <code>SmbFile</code> was created. The value
1385 * returned is suitable for constructing a {@link java.util.Date} object
1386 * (i.e. seconds since Epoch 1970). Times should be the same as those
1387 * reported using the properties dialog of the Windows Explorer program.
1388 *
1389 * For Win95/98/Me this is actually the last write time. It is currently
1390 * not possible to retrieve the create time from files on these systems.
1391 *
1392 * @return The number of milliseconds since the 00:00:00 GMT, January 1,
1393 * 1970 as a <code>long</code> value
1394 */

1395    public long createTime() throws SmbException {
1396        if( getUncPath0().length() > 1 ) {
1397            exists();
1398            return createTime;
1399        }
1400        return 0L;
1401    }
1402/**
1403 * Retrieve the last time the file represented by this
1404 * <code>SmbFile</code> was modified. The value returned is suitable for
1405 * constructing a {@link java.util.Date} object (i.e. seconds since Epoch
1406 * 1970). Times should be the same as those reported using the properties
1407 * dialog of the Windows Explorer program.
1408 *
1409 * @return The number of milliseconds since the 00:00:00 GMT, January 1,
1410 * 1970 as a <code>long</code> value
1411 */

1412    public long lastModified() throws SmbException {
1413        if( getUncPath0().length() > 1 ) {
1414            exists();
1415            return lastModified;
1416        }
1417        return 0L;
1418    }
1419/**
1420 * List the contents of this SMB resource. The list returned by this
1421 * method will be;
1422 *
1423 * <ul>
1424 * <li> files and directories contained within this resource if the
1425 * resource is a normal disk file directory,
1426 * <li> all available NetBIOS workgroups or domains if this resource is
1427 * the top level URL <code>smb://</code>,
1428 * <li> all servers registered as members of a NetBIOS workgroup if this
1429 * resource refers to a workgroup in a <code>smb://workgroup/</code> URL,
1430 * <li> all browseable shares of a server including printers, IPC
1431 * services, or disk volumes if this resource is a server URL in the form
1432 * <code>smb://server/</code>,
1433 * <li> or <code>null</code> if the resource cannot be resolved.
1434 * </ul>
1435 *
1436 * @return A <code>String[]</code> array of files and directories,
1437 * workgroups, servers, or shares depending on the context of the
1438 * resource URL
1439 */

1440    public String JavaDoc[] list() throws SmbException {
1441        return list( "*", ATTR_DIRECTORY | ATTR_HIDDEN | ATTR_SYSTEM, null, null );
1442    }
1443
1444/**
1445 * List the contents of this SMB resource. The list returned will be
1446 * identical to the list returned by the parameterless <code>list()</code>
1447 * method minus filenames filtered by the specified filter.
1448 *
1449 * @param filter a filename filter to exclude filenames from the results
1450 * @throws SmbException
1451 # @return An array of filenames
1452 */

1453    public String JavaDoc[] list( SmbFilenameFilter filter ) throws SmbException {
1454        return list( "*", ATTR_DIRECTORY | ATTR_HIDDEN | ATTR_SYSTEM, filter, null );
1455    }
1456
1457/**
1458 * List the contents of this SMB resource as an array of
1459 * <code>SmbFile</code> objects. This method is much more efficient than
1460 * the regular <code>list</code> method when querying attributes of each
1461 * file in the result set.
1462 * <p>
1463 * The list of <code>SmbFile</code>s returned by this method will be;
1464 *
1465 * <ul>
1466 * <li> files and directories contained within this resource if the
1467 * resource is a normal disk file directory,
1468 * <li> all available NetBIOS workgroups or domains if this resource is
1469 * the top level URL <code>smb://</code>,
1470 * <li> all servers registered as members of a NetBIOS workgroup if this
1471 * resource refers to a workgroup in a <code>smb://workgroup/</code> URL,
1472 * <li> all browseable shares of a server including printers, IPC
1473 * services, or disk volumes if this resource is a server URL in the form
1474 * <code>smb://server/</code>,
1475 * <li> or <code>null</code> if the resource cannot be resolved.
1476 * </ul>
1477 *
1478 * @return An array of <code>SmbFile</code> objects representing file
1479 * and directories, workgroups, servers, or shares depending on the context
1480 * of the resource URL
1481 */

1482    public SmbFile[] listFiles() throws SmbException {
1483        return listFiles( "*", ATTR_DIRECTORY | ATTR_HIDDEN | ATTR_SYSTEM, null, null );
1484    }
1485
1486/**
1487 * The CIFS protocol provides for DOS "wildcards" to be used as
1488 * a performance enhancement. The client does not have to filter
1489 * the names and the server does not have to return all directory
1490 * entries.
1491 * <p>
1492 * The wildcard expression may consist of two special meta
1493 * characters in addition to the normal filename characters. The '*'
1494 * character matches any number of characters in part of a name. If
1495 * the expression begins with one or more '?'s then exactly that
1496 * many characters will be matched whereas if it ends with '?'s
1497 * it will match that many characters <i>or less</i>.
1498 * <p>
1499 * Wildcard expressions will not filter workgroup names or server names.
1500 *
1501 * <blockquote><pre>
1502 * winnt> ls c?o*
1503 * clock.avi -rw-- 82944 Mon Oct 14 1996 1:38 AM
1504 * Cookies drw-- 0 Fri Nov 13 1998 9:42 PM
1505 * 2 items in 5ms
1506 * </pre></blockquote>
1507 *
1508 * @param wildcard a wildcard expression
1509 * @throws SmbException
1510 * @return An array of <code>SmbFile</code> objects representing file
1511 * and directories, workgroups, servers, or shares depending on the context
1512 * of the resource URL
1513 */

1514
1515    public SmbFile[] listFiles( String JavaDoc wildcard ) throws SmbException {
1516        return listFiles( wildcard, ATTR_DIRECTORY | ATTR_HIDDEN | ATTR_SYSTEM, null, null );
1517    }
1518/**
1519 * List the contents of this SMB resource. The list returned will be
1520 * identical to the list returned by the parameterless <code>listFiles()</code>
1521 * method minus files filtered by the specified filename filter.
1522 *
1523 * @param filter a filter to exclude files from the results
1524 * @return An array of <tt>SmbFile</tt> objects
1525 * @throws SmbException
1526 */

1527    public SmbFile[] listFiles( SmbFilenameFilter filter ) throws SmbException {
1528        return listFiles( "*", ATTR_DIRECTORY | ATTR_HIDDEN | ATTR_SYSTEM, filter, null );
1529    }
1530/**
1531 * List the contents of this SMB resource. The list returned will be
1532 * identical to the list returned by the parameterless <code>listFiles()</code>
1533 * method minus filenames filtered by the specified filter.
1534 *
1535 * @param filter a file filter to exclude files from the results
1536 * @return An array of <tt>SmbFile</tt> objects
1537 */

1538    public SmbFile[] listFiles( SmbFileFilter filter ) throws SmbException {
1539        return listFiles( "*", ATTR_DIRECTORY | ATTR_HIDDEN | ATTR_SYSTEM, null, filter );
1540    }
1541    String JavaDoc[] list( String JavaDoc wildcard, int searchAttributes,
1542                SmbFilenameFilter fnf, SmbFileFilter ff ) throws SmbException {
1543        ArrayList JavaDoc list = new ArrayList JavaDoc();
1544
1545        try {
1546            if( url.getHost().length() == 0 || share == null ) {
1547                doNetEnum( list, false, wildcard, searchAttributes, fnf, ff );
1548            } else {
1549                doFindFirstNext( list, false, wildcard, searchAttributes, fnf, ff );
1550            }
1551        } catch( UnknownHostException JavaDoc uhe ) {
1552            throw new SmbException( url.toString(), uhe );
1553        } catch( MalformedURLException JavaDoc mue ) {
1554            throw new SmbException( url.toString(), mue );
1555        }
1556
1557        return (String JavaDoc[])list.toArray(new String JavaDoc[list.size()]);
1558    }
1559    SmbFile[] listFiles( String JavaDoc wildcard, int searchAttributes,
1560                SmbFilenameFilter fnf, SmbFileFilter ff ) throws SmbException {
1561        ArrayList JavaDoc list = new ArrayList JavaDoc();
1562
1563        if( ff != null && ff instanceof DosFileFilter ) {
1564            DosFileFilter dff = (DosFileFilter)ff;
1565            if( dff.wildcard != null ) {
1566                wildcard = dff.wildcard;
1567            }
1568            searchAttributes = dff.attributes;
1569        }
1570
1571        try {
1572            if( url.getHost().length() == 0 || share == null ) {
1573                doNetEnum( list, true, wildcard, searchAttributes, fnf, ff );
1574            } else {
1575                doFindFirstNext( list, true, wildcard, searchAttributes, fnf, ff );
1576            }
1577        } catch( UnknownHostException JavaDoc uhe ) {
1578            throw new SmbException( url.toString(), uhe );
1579        } catch( MalformedURLException JavaDoc mue ) {
1580            throw new SmbException( url.toString(), mue );
1581        }
1582
1583        return (SmbFile[])list.toArray(new SmbFile[list.size()]);
1584    }
1585    void doNetEnum( ArrayList JavaDoc list,
1586                boolean files,
1587                String JavaDoc wildcard,
1588                int searchAttributes,
1589                SmbFilenameFilter fnf,
1590                SmbFileFilter ff ) throws SmbException,
1591                        UnknownHostException JavaDoc, MalformedURLException JavaDoc {
1592        SmbComTransaction req;
1593        SmbComTransactionResponse resp;
1594        int listType = url.getHost().length() == 0 ? 0 : getType();
1595        String JavaDoc p = url.getPath();
1596
1597        if( p.lastIndexOf( '/' ) != ( p.length() - 1 )) {
1598            throw new SmbException( url.toString() + " directory must end with '/'" );
1599        }
1600
1601        switch( listType ) {
1602            case 0:
1603                connect0();
1604                req = new NetServerEnum2( tree.session.transport.server.oemDomainName,
1605                        NetServerEnum2.SV_TYPE_DOMAIN_ENUM );
1606                resp = new NetServerEnum2Response();
1607                break;
1608            case TYPE_WORKGROUP:
1609                req = new NetServerEnum2( url.getHost(), NetServerEnum2.SV_TYPE_ALL );
1610                resp = new NetServerEnum2Response();
1611                break;
1612            case TYPE_SERVER:
1613                req = new NetShareEnum();
1614                resp = new NetShareEnumResponse();
1615                break;
1616            default:
1617                throw new SmbException( "The requested list operations is invalid: " + url.toString() );
1618        }
1619
1620            boolean more;
1621        do {
1622            int n;
1623
1624            send( req, resp );
1625
1626            more = resp.status == SmbException.ERROR_MORE_DATA;
1627
1628            if( resp.status != SmbException.ERROR_SUCCESS &&
1629                    resp.status != SmbException.ERROR_MORE_DATA ) {
1630                throw new SmbException( resp.status, true );
1631            }
1632
1633            n = more ? resp.numEntries - 1 : resp.numEntries;
1634            for( int i = 0; i < n; i++ ) {
1635                FileEntry e = resp.results[i];
1636                String JavaDoc name = e.getName();
1637                if( fnf != null && fnf.accept( this, name ) == false ) {
1638                    continue;
1639                }
1640                if( name.length() > 0 ) {
1641                    SmbFile f = new SmbFile( this, name,
1642                                e.getType(),
1643                                ATTR_READONLY | ATTR_DIRECTORY, 0L, 0L, 0L );
1644                    if( ff != null && ff.accept( f ) == false ) {
1645                        continue;
1646                    }
1647                    if( files ) {
1648                        list.add( f );
1649                    } else {
1650                        list.add( name );
1651                    }
1652                }
1653            }
1654            if( listType != 0 && listType != TYPE_WORKGROUP ) {
1655                break;
1656            }
1657            req.subCommand = (byte)SmbComTransaction.NET_SERVER_ENUM3;
1658            req.reset( 0, ((NetServerEnum2Response)resp).lastName );
1659            resp.reset();
1660        } while( more );
1661    }
1662    void doFindFirstNext( ArrayList JavaDoc list,
1663                boolean files,
1664                String JavaDoc wildcard,
1665                int searchAttributes,
1666                SmbFilenameFilter fnf,
1667                SmbFileFilter ff ) throws SmbException, UnknownHostException JavaDoc, MalformedURLException JavaDoc {
1668        SmbComTransaction req;
1669        Trans2FindFirst2Response resp;
1670        int sid;
1671        String JavaDoc path = getUncPath0();
1672        String JavaDoc p = url.getPath();
1673
1674        if( p.lastIndexOf( '/' ) != ( p.length() - 1 )) {
1675            throw new SmbException( url.toString() + " directory must end with '/'" );
1676        }
1677
1678        req = new Trans2FindFirst2( path, wildcard, searchAttributes );
1679        resp = new Trans2FindFirst2Response();
1680
1681        if( log.level > 2 )
1682            log.println( "doFindFirstNext: " + req.path );
1683
1684        send( req, resp );
1685
1686        sid = resp.sid;
1687        req = new Trans2FindNext2( sid, resp.resumeKey, resp.lastName );
1688
1689        /* The only difference between first2 and next2 responses is subCommand
1690         * so let's recycle the response object.
1691         */

1692        resp.subCommand = SmbComTransaction.TRANS2_FIND_NEXT2;
1693
1694        for( ;; ) {
1695            for( int i = 0; i < resp.numEntries; i++ ) {
1696                FileEntry e = resp.results[i];
1697                String JavaDoc name = e.getName();
1698                if( name.length() < 3 ) {
1699                    int h = name.hashCode();
1700                    if( h == HASH_DOT || h == HASH_DOT_DOT ) {
1701                        continue;
1702                    }
1703                }
1704                if( fnf != null && fnf.accept( this, name ) == false ) {
1705                    continue;
1706                }
1707                if( name.length() > 0 ) {
1708                    SmbFile f = new SmbFile( this, name, TYPE_FILESYSTEM,
1709                            e.getAttributes(), e.createTime(), e.lastModified(), e.length() );
1710                    if( ff != null && ff.accept( f ) == false ) {
1711                        continue;
1712                    }
1713                    if( files ) {
1714                        list.add( f );
1715                    } else {
1716                        list.add( name );
1717                    }
1718                }
1719            }
1720
1721            if( resp.isEndOfSearch || resp.numEntries == 0 ) {
1722                break;
1723            }
1724
1725            req.reset( resp.resumeKey, resp.lastName );
1726            resp.reset();
1727            send( req, resp );
1728        }
1729
1730        send( new SmbComFindClose2( sid ), blank_resp() );
1731    }
1732
1733/**
1734 * Changes the name of the file this <code>SmbFile</code> represents to the name
1735 * designated by the <code>SmbFile</code> argument.
1736 * <p/>
1737 * <i>Remember: <code>SmbFile</code>s are immutible and therefore
1738 * the path associated with this <code>SmbFile</code> object will not
1739 * change). To access the renamed file it is necessary to construct a
1740 * new <tt>SmbFile</tt></i>.
1741 *
1742 * @param dest An <code>SmbFile</code> that represents the new pathname
1743 * @return <code>true</code> if the file or directory was successfully renamed
1744 * @throws NullPointerException
1745 * If the <code>dest</code> argument is <code>null</code>
1746 */

1747    public void renameTo( SmbFile dest ) throws SmbException {
1748        if( getUncPath0().length() == 1 || dest.getUncPath0().length() == 1 ) {
1749            throw new SmbException( "Invalid operation for workgroups, servers, or shares" );
1750        }
1751        connect0();
1752        dest.connect0();
1753
1754        if( tree.inDfs ) { /* This ensures that each path is
1755                * resolved independantly to deal with the case where files
1756                * have the same base path but ultimately turn out to be
1757                * on different servers because of DFS. It also eliminates
1758                * manipulating the SMB path which is problematic because
1759                * there are really two that would need to be prefixed
1760                * with host and share as DFS requires.
1761                */

1762            exists();
1763            dest.exists();
1764        }
1765        if( tree != dest.tree ) {
1766            throw new SmbException( "Invalid operation for workgroups, servers, or shares" );
1767        }
1768
1769        if( log.level > 2 )
1770            log.println( "renameTo: " + unc + " -> " + dest.unc );
1771
1772        attrExpiration = sizeExpiration = 0;
1773        dest.attrExpiration = 0;
1774
1775        /*
1776         * Rename Request / Response
1777         */

1778
1779        send( new SmbComRename( unc, dest.unc ), blank_resp() );
1780    }
1781
1782    class WriterThread extends Thread JavaDoc {
1783        byte[] b;
1784        int n, off;
1785        boolean ready;
1786        SmbFile dest;
1787        SmbException e = null;
1788        boolean useNTSmbs;
1789        SmbComWriteAndX reqx;
1790        SmbComWrite req;
1791        ServerMessageBlock resp;
1792
1793        WriterThread() throws SmbException {
1794            super( "JCIFS-WriterThread" );
1795            useNTSmbs = tree.session.transport.hasCapability( ServerMessageBlock.CAP_NT_SMBS );
1796            if( useNTSmbs ) {
1797                reqx = new SmbComWriteAndX();
1798                resp = new SmbComWriteAndXResponse();
1799            } else {
1800                req = new SmbComWrite();
1801                resp = new SmbComWriteResponse();
1802            }
1803            ready = false;
1804        }
1805
1806        synchronized void write( byte[] b, int n, SmbFile dest, int off ) {
1807            this.b = b;
1808            this.n = n;
1809            this.dest = dest;
1810            this.off = off;
1811            ready = false;
1812            notify();
1813        }
1814
1815        public void run() {
1816            synchronized( this ) {
1817                try {
1818                    for( ;; ) {
1819                        notify();
1820                        ready = true;
1821                        while( ready ) {
1822                            wait();
1823                        }
1824                        if( n == -1 ) {
1825                            return;
1826                        }
1827                        if( useNTSmbs ) {
1828                            reqx.setParam( dest.fid, off, n, b, 0, n );
1829                            dest.send( reqx, resp );
1830                        } else {
1831                            req.setParam( dest.fid, off, n, b, 0, n );
1832                            dest.send( req, resp );
1833                        }
1834                    }
1835                } catch( SmbException e ) {
1836                    this.e = e;
1837                } catch( Exception JavaDoc x ) {
1838                    this.e = new SmbException( "WriterThread", x );
1839                }
1840                notify();
1841            }
1842        }
1843    }
1844    void copyTo0( SmbFile dest, byte[][] b, int bsize, WriterThread w,
1845            SmbComReadAndX req, SmbComReadAndXResponse resp ) throws SmbException {
1846        int i;
1847
1848        if( attrExpiration < System.currentTimeMillis() ) {
1849            attributes = ATTR_READONLY | ATTR_DIRECTORY;
1850            createTime = 0L;
1851            lastModified = 0L;
1852            isExists = false;
1853
1854            Info info = queryPath( getUncPath0(),
1855                    Trans2QueryPathInformationResponse.SMB_QUERY_FILE_BASIC_INFO );
1856            attributes = info.getAttributes();
1857            createTime = info.getCreateTime();
1858            lastModified = info.getLastWriteTime();
1859
1860            /* If any of the above fails, isExists will not be set true
1861             */

1862
1863            isExists = true;
1864            attrExpiration = System.currentTimeMillis() + attrExpirationPeriod;
1865        }
1866
1867        if( isDirectory() ) {
1868            SmbFile[] files;
1869            SmbFile ndest;
1870
1871            String JavaDoc path = dest.getUncPath0();
1872            if( path.length() > 1 ) {
1873                try {
1874                    dest.mkdir();
1875                    dest.setPathInformation( attributes, createTime, lastModified );
1876                } catch( SmbException se ) {
1877                    if( se.getNtStatus() != NtStatus.NT_STATUS_ACCESS_DENIED &&
1878                            se.getNtStatus() != NtStatus.NT_STATUS_OBJECT_NAME_COLLISION ) {
1879                        throw se;
1880                    }
1881                }
1882            }
1883
1884            files = listFiles( "*", ATTR_DIRECTORY | ATTR_HIDDEN | ATTR_SYSTEM, null, null );
1885            try {
1886                for( i = 0; i < files.length; i++ ) {
1887                    ndest = new SmbFile( dest,
1888                                    files[i].getName(),
1889                                    files[i].type,
1890                                    files[i].attributes,
1891                                    files[i].createTime,
1892                                    files[i].lastModified,
1893                                    files[i].size );
1894                    files[i].copyTo0( ndest, b, bsize, w, req, resp );
1895                }
1896            } catch( UnknownHostException JavaDoc uhe ) {
1897                throw new SmbException( url.toString(), uhe );
1898            } catch( MalformedURLException JavaDoc mue ) {
1899                throw new SmbException( url.toString(), mue );
1900            }
1901        } else {
1902            int off;
1903
1904try {
1905            open( SmbFile.O_RDONLY, ATTR_NORMAL, 0 );
1906            try {
1907                dest.open( SmbFile.O_CREAT | SmbFile.O_WRONLY | SmbFile.O_TRUNC |
1908                        SmbComNTCreateAndX.FILE_WRITE_ATTRIBUTES << 16, attributes, 0 );
1909            } catch( SmbAuthException sae ) {
1910                if(( dest.attributes & ATTR_READONLY ) != 0 ) {
1911                                            /* Remove READONLY and try again
1912                                             */

1913                    dest.setPathInformation( dest.attributes & ~ATTR_READONLY, 0L, 0L );
1914                    dest.open( SmbFile.O_CREAT | SmbFile.O_WRONLY | SmbFile.O_TRUNC |
1915                            SmbComNTCreateAndX.FILE_WRITE_ATTRIBUTES << 16, attributes, 0 );
1916                } else {
1917                    throw sae;
1918                }
1919            }
1920
1921            i = off = 0;
1922            for( ;; ) {
1923                req.setParam( fid, off, bsize );
1924                resp.setParam( b[i], 0 );
1925                send( req, resp );
1926
1927                synchronized( w ) {
1928                    while( !w.ready ) {
1929                        try {
1930                            w.wait();
1931                        } catch( InterruptedException JavaDoc ie ) {
1932                            throw new SmbException( dest.url.toString(), ie );
1933                        }
1934                    }
1935                    if( w.e != null ) {
1936                        throw w.e;
1937                    }
1938                    if( resp.dataLength <= 0 ) {
1939                        break;
1940                    }
1941                    w.write( b[i], resp.dataLength, dest, off );
1942                }
1943
1944                i = i == 1 ? 0 : 1;
1945                off += resp.dataLength;
1946            }
1947
1948            dest.send( new Trans2SetFileInformation(
1949                    dest.fid, attributes, createTime, lastModified ),
1950                    new Trans2SetFileInformationResponse() );
1951            dest.close( 0L );
1952            close();
1953} catch( Exception JavaDoc ex ) {
1954    if( log.level > 1 )
1955        ex.printStackTrace( log );
1956}
1957        }
1958    }
1959/**
1960 * This method will copy the file or directory represented by this
1961 * <tt>SmbFile</tt> and it's sub-contents to the location specified by the
1962 * <tt>dest</tt> parameter. This file and the destination file do not
1963 * need to be on the same host. This operation does not copy extended
1964 * file attibutes such as ACLs but it does copy regular attributes as
1965 * well as create and last write times. This method is almost twice as
1966 * efficient as manually copying as it employs an additional write
1967 * thread to read and write data concurrently.
1968 * <p/>
1969 * It is not possible (nor meaningful) to copy entire workgroups or
1970 * servers.
1971 *
1972 * @param dest the destination file or directory
1973 * @throws SmbException
1974 */

1975    public void copyTo( SmbFile dest ) throws SmbException {
1976        SmbComReadAndX req;
1977        SmbComReadAndXResponse resp;
1978        WriterThread w;
1979        int bsize;
1980        byte[][] b;
1981
1982        /* Should be able to copy an entire share actually
1983         */

1984        if( share == null || dest.share == null) {
1985            throw new SmbException( "Invalid operation for workgroups or servers" );
1986        }
1987
1988        req = new SmbComReadAndX();
1989        resp = new SmbComReadAndXResponse();
1990
1991        connect0();
1992        dest.connect0();
1993
1994        if( tree.inDfs ) {
1995                /* At this point the maxBufferSize values are from the server
1996                 * exporting the volumes, not the one that we will actually
1997                 * end up performing IO with. If the server hosting the
1998                 * actual files has a smaller maxBufSize this could be
1999                 * incorrect. To handle this properly it is necessary
2000                 * to redirect the tree to the target server first before
2001                 * establishing buffer size. These exists() calls facilitate
2002                 * that.
2003                 */

2004            exists();
2005            dest.exists();
2006        }
2007
2008        /* It is invalid for the source path to be a child of the destination
2009         * path or visa versa.
2010         */

2011        try {
2012            if (getAddress().equals( dest.getAddress() ) &&
2013                        canon.regionMatches( true, 0, dest.canon, 0,
2014                                Math.min( canon.length(), dest.canon.length() ))) {
2015                throw new SmbException( "Source and destination paths overlap." );
2016            }
2017        } catch (UnknownHostException JavaDoc uhe) {
2018        }
2019
2020        w = new WriterThread();
2021        w.setDaemon( true );
2022        w.start();
2023
2024        /* Downgrade one transport to the lower of the negotiated buffer sizes
2025         * so we can just send whatever is received.
2026         */

2027
2028        SmbTransport t1 = tree.session.transport;
2029        SmbTransport t2 = dest.tree.session.transport;
2030
2031        if( t1.snd_buf_size < t2.snd_buf_size ) {
2032            t2.snd_buf_size = t1.snd_buf_size;
2033        } else {
2034            t1.snd_buf_size = t2.snd_buf_size;
2035        }
2036
2037        bsize = Math.min( t1.rcv_buf_size - 70, t1.snd_buf_size - 70 );
2038        b = new byte[2][bsize];
2039
2040        copyTo0( dest, b, bsize, w, req, resp );
2041        w.write( null, -1, null, 0 );
2042    }
2043
2044/**
2045 * This method will delete the file or directory specified by this
2046 * <code>SmbFile</code>. If the target is a directory, the contents of
2047 * the directory will be deleted as well. If a file within the directory or
2048 * it's sub-directories is marked read-only, the read-only status will
2049 * be removed and the file will be deleted.
2050 *
2051 * @throws SmbException
2052 */

2053    public void delete() throws SmbException {
2054        if( tree == null || tree.inDfs ) {
2055            exists(); /* This is necessary to ensure we
2056                            * pass a path adjusted for DFS */

2057        }
2058        getUncPath0();
2059        delete( unc );
2060    }
2061    void delete( String JavaDoc fileName ) throws SmbException {
2062        if( getUncPath0().length() == 1 ) {
2063            throw new SmbException( "Invalid operation for workgroups, servers, or shares" );
2064        }
2065
2066        if( System.currentTimeMillis() > attrExpiration ) {
2067            attributes = ATTR_READONLY | ATTR_DIRECTORY;
2068            createTime = 0L;
2069            lastModified = 0L;
2070            isExists = false;
2071
2072            Info info = queryPath( getUncPath0(),
2073                    Trans2QueryPathInformationResponse.SMB_QUERY_FILE_BASIC_INFO );
2074            attributes = info.getAttributes();
2075            createTime = info.getCreateTime();
2076            lastModified = info.getLastWriteTime();
2077
2078            attrExpiration = System.currentTimeMillis() + attrExpirationPeriod;
2079            isExists = true;
2080        }
2081
2082        if(( attributes & ATTR_READONLY ) != 0 ) {
2083            setReadWrite();
2084        }
2085
2086        /*
2087         * Delete or Delete Directory Request / Response
2088         */

2089
2090        if( log.level > 2 )
2091            log.println( "delete: " + fileName );
2092
2093        if(( attributes & ATTR_DIRECTORY ) != 0 ) {
2094
2095            /* Recursively delete directory contents
2096             */

2097
2098            try {
2099                SmbFile[] l = listFiles( "*", ATTR_DIRECTORY | ATTR_HIDDEN | ATTR_SYSTEM, null, null );
2100                for( int i = 0; i < l.length; i++ ) {
2101                    l[i].delete();
2102                }
2103            } catch( SmbException se ) {
2104                /* Oracle FilesOnline version 9.0.4 doesn't send '.' and '..' so
2105                 * listFiles may generate undesireable "cannot find
2106                 * the file specified".
2107                 */

2108                if( se.getNtStatus() != SmbException.NT_STATUS_NO_SUCH_FILE ) {
2109                    throw se;
2110                }
2111            }
2112
2113            send( new SmbComDeleteDirectory( fileName ), blank_resp() );
2114        } else {
2115            send( new SmbComDelete( fileName ), blank_resp() );
2116        }
2117
2118        attrExpiration = sizeExpiration = 0;
2119    }
2120
2121/**
2122 * Returns the length of this <tt>SmbFile</tt> in bytes. If this object
2123 * is a <tt>TYPE_SHARE</tt> the total capacity of the disk shared in
2124 * bytes is returned. If this object is a directory or a type other than
2125 * <tt>TYPE_SHARE</tt>, 0L is returned.
2126 *
2127 * @return The length of the file in bytes or 0 if this
2128 * <code>SmbFile</code> is not a file.
2129 * @throws SmbException
2130 */

2131
2132    public long length() throws SmbException {
2133        if( sizeExpiration > System.currentTimeMillis() ) {
2134            return size;
2135        }
2136
2137        if( getType() == TYPE_SHARE ) {
2138            Trans2QueryFSInformationResponse response;
2139            int level = Trans2QueryFSInformationResponse.SMB_INFO_ALLOCATION;
2140
2141            response = new Trans2QueryFSInformationResponse( level );
2142            send( new Trans2QueryFSInformation( level ), response );
2143
2144            size = response.info.getCapacity();
2145        } else if( getUncPath0().length() > 1 && type != TYPE_NAMED_PIPE ) {
2146            Info info = queryPath( getUncPath0(),
2147                    Trans2QueryPathInformationResponse.SMB_QUERY_FILE_STANDARD_INFO );
2148            size = info.getSize();
2149        } else {
2150            size = 0L;
2151        }
2152        sizeExpiration = System.currentTimeMillis() + attrExpirationPeriod;
2153        return size;
2154    }
2155
2156/**
2157 * This method returns the free disk space in bytes of the drive this share
2158 * represents or the drive on which the directory or file resides. Objects
2159 * other than <tt>TYPE_SHARE</tt> or <tt>TYPE_FILESYSTEM</tt> will result
2160 * in 0L being returned.
2161 *
2162 * @return the free disk space in bytes of the drive on which this file or
2163 * directory resides
2164 */

2165    public long getDiskFreeSpace() throws SmbException {
2166        if( getType() == TYPE_SHARE || type == TYPE_FILESYSTEM ) {
2167            int level = Trans2QueryFSInformationResponse.SMB_FS_FULL_SIZE_INFORMATION;
2168            try {
2169                return queryFSInformation(level);
2170            } catch( SmbException ex ) {
2171                switch (ex.getNtStatus()) {
2172                    case NtStatus.NT_STATUS_INVALID_INFO_CLASS:
2173                    case NtStatus.NT_STATUS_UNSUCCESSFUL: // NetApp Filer
2174
// SMB_FS_FULL_SIZE_INFORMATION not supported by the server.
2175
level = Trans2QueryFSInformationResponse.SMB_INFO_ALLOCATION;
2176                        return queryFSInformation(level);
2177                }
2178                throw ex;
2179            }
2180        }
2181        return 0L;
2182    }
2183
2184    private long queryFSInformation( int level ) throws SmbException {
2185        Trans2QueryFSInformationResponse response;
2186
2187        response = new Trans2QueryFSInformationResponse( level );
2188        send( new Trans2QueryFSInformation( level ), response );
2189
2190        if( type == TYPE_SHARE ) {
2191            size = response.info.getCapacity();
2192            sizeExpiration = System.currentTimeMillis() + attrExpirationPeriod;
2193        }
2194
2195        return response.info.getFree();
2196    }
2197
2198/**
2199 * Creates a directory with the path specified by this
2200 * <code>SmbFile</code>. For this method to be successful, the target
2201 * must not already exist. This method will fail when
2202 * used with <code>smb://</code>, <code>smb://workgroup/</code>,
2203 * <code>smb://server/</code>, or <code>smb://server/share/</code> URLs
2204 * because workgroups, servers, and shares cannot be dynamically created
2205 * (although in the future it may be possible to create shares).
2206 *
2207 * @throws SmbException
2208 */

2209    public void mkdir() throws SmbException {
2210        String JavaDoc path = getUncPath0();
2211
2212        if( path.length() == 1 ) {
2213            throw new SmbException( "Invalid operation for workgroups, servers, or shares" );
2214        }
2215
2216        /*
2217         * Create Directory Request / Response
2218         */

2219
2220        if( log.level > 2 )
2221            log.println( "mkdir: " + path );
2222
2223        send( new SmbComCreateDirectory( path ), blank_resp() );
2224
2225        attrExpiration = sizeExpiration = 0;
2226    }
2227
2228/**
2229 * Creates a directory with the path specified by this <tt>SmbFile</tt>
2230 * and any parent directories that do not exist. This method will fail
2231 * when used with <code>smb://</code>, <code>smb://workgroup/</code>,
2232 * <code>smb://server/</code>, or <code>smb://server/share/</code> URLs
2233 * because workgroups, servers, and shares cannot be dynamically created
2234 * (although in the future it may be possible to create shares).
2235 *
2236 * @throws SmbException
2237 */

2238    public void mkdirs() throws SmbException {
2239        SmbFile parent;
2240
2241        try {
2242            parent = new SmbFile( getParent(), auth );
2243        } catch( IOException JavaDoc ioe ) {
2244            return;
2245        }
2246        if( parent.exists() == false ) {
2247            parent.mkdirs();
2248        }
2249        mkdir();
2250    }
2251
2252/**
2253 * Create a new file but fail if it already exists. The check for
2254 * existance of the file and it's creation are an atomic operation with
2255 * respect to other filesystem activities.
2256 */

2257    public void createNewFile() throws SmbException {
2258        if( getUncPath0().length() == 1 ) {
2259            throw new SmbException( "Invalid operation for workgroups, servers, or shares" );
2260        }
2261        close( open0( O_RDWR | O_CREAT | O_EXCL, ATTR_NORMAL, 0 ), 0L );
2262    }
2263
2264    void setPathInformation( int attrs, long ctime, long mtime ) throws SmbException {
2265        int f, dir;
2266
2267        exists();
2268        dir = attributes & ATTR_DIRECTORY;
2269
2270        f = open0( O_RDONLY | SmbComNTCreateAndX.FILE_WRITE_ATTRIBUTES << 16,
2271                dir, dir != 0 ? 0x0001 : 0x0040 );
2272        send( new Trans2SetFileInformation( f, attrs | dir, ctime, mtime ),
2273                new Trans2SetFileInformationResponse() );
2274        close( f, 0L );
2275
2276        attrExpiration = 0;
2277    }
2278
2279/**
2280 * Set the create time of the file. The time is specified as milliseconds
2281 * from Jan 1, 1970 which is the same as that which is returned by the
2282 * <tt>createTime()</tt> method.
2283 * <p/>
2284 * This method does not apply to workgroups, servers, or shares.
2285 *
2286 * @param time the create time as milliseconds since Jan 1, 1970
2287 */

2288    public void setCreateTime( long time ) throws SmbException {
2289        if( getUncPath0().length() == 1 ) {
2290            throw new SmbException( "Invalid operation for workgroups, servers, or shares" );
2291        }
2292
2293        setPathInformation( 0, time, 0L );
2294    }
2295/**
2296 * Set the last modified time of the file. The time is specified as milliseconds
2297 * from Jan 1, 1970 which is the same as that which is returned by the
2298 * <tt>lastModified()</tt>, <tt>getLastModified()</tt>, and <tt>getDate()</tt> methods.
2299 * <p/>
2300 * This method does not apply to workgroups, servers, or shares.
2301 *
2302 * @param time the last modified time as milliseconds since Jan 1, 1970
2303 */

2304    public void setLastModified( long time ) throws SmbException {
2305        if( getUncPath0().length() == 1 ) {
2306            throw new SmbException( "Invalid operation for workgroups, servers, or shares" );
2307        }
2308
2309        setPathInformation( 0, 0L, time );
2310    }
2311
2312/**
2313 * Return the attributes of this file. Attributes are represented as a
2314 * bitset that must be masked with <tt>ATTR_*</tt> constants to determine
2315 * if they are set or unset. The value returned is suitable for use with
2316 * the <tt>setAttributes()</tt> method.
2317 *
2318 * @return the <tt>ATTR_*</tt> attributes associated with this file
2319 * @throws SmbException
2320 */

2321    public int getAttributes() throws SmbException {
2322        if( getUncPath0().length() == 1 ) {
2323            return 0;
2324        }
2325        exists();
2326        return attributes & ATTR_GET_MASK;
2327    }
2328
2329/**
2330 * Set the attributes of this file. Attributes are composed into a
2331 * bitset by bitwise ORing the <tt>ATTR_*</tt> constants. Setting the
2332 * value returned by <tt>getAttributes</tt> will result in both files
2333 * having the same attributes.
2334 * @throws SmbException
2335 */

2336    public void setAttributes( int attrs ) throws SmbException {
2337        if( getUncPath0().length() == 1 ) {
2338            throw new SmbException( "Invalid operation for workgroups, servers, or shares" );
2339        }
2340        setPathInformation( attrs & ATTR_SET_MASK, 0L, 0L );
2341    }
2342
2343/**
2344 * Make this file read-only. This is shorthand for <tt>setAttributes(
2345 * getAttributes() | ATTR_READ_ONLY )</tt>.
2346 *
2347 * @throws SmbException
2348 */

2349    public void setReadOnly() throws SmbException {
2350        setAttributes( getAttributes() | ATTR_READONLY );
2351    }
2352
2353/**
2354 * Turn off the read-only attribute of this file. This is shorthand for
2355 * <tt>setAttributes( getAttributes() & ~ATTR_READONLY )</tt>.
2356 *
2357 * @throws SmbException
2358 */

2359    public void setReadWrite() throws SmbException {
2360        setAttributes( getAttributes() & ~ATTR_READONLY );
2361    }
2362
2363/**
2364 * Returns a {@link java.net.URL} for this <code>SmbFile</code>. The
2365 * <code>URL</code> may be used as any other <code>URL</code> might to
2366 * access an SMB resource. Currently only retrieving data and information
2367 * is supported (i.e. no <tt>doOutput</tt>).
2368 *
2369 * @deprecated Use getURL() instead
2370 * @return A new <code>{@link java.net.URL}</code> for this <code>SmbFile</code>
2371 * @throws MalformedURLException
2372 */

2373    public URL JavaDoc toURL() throws MalformedURLException JavaDoc {
2374        return url;
2375    }
2376
2377/**
2378 * Computes a hashCode for this file based on the URL string and IP
2379 * address if the server. The hashing function uses the hashcode of the
2380 * server address, the canonical representation of the URL, and does not
2381 * compare authentication information. In essance, two
2382 * <code>SmbFile</code> objects that refer to
2383 * the same file should generate the same hashcode provided it is possible
2384 * to make such a determination.
2385 *
2386 * @return A hashcode for this abstract file
2387 * @throws SmbException
2388 */

2389
2390    public int hashCode() {
2391        int hash;
2392        try {
2393            hash = getAddress().hashCode();
2394        } catch( UnknownHostException JavaDoc uhe ) {
2395            hash = getServer().toUpperCase().hashCode();
2396        }
2397        getUncPath0();
2398        return hash + canon.toUpperCase().hashCode();
2399    }
2400
2401/**
2402 * Tests to see if two <code>SmbFile</code> objects are equal. Two
2403 * SmbFile objects are equal when they reference the same SMB
2404 * resource. More specifically, two <code>SmbFile</code> objects are
2405 * equals if their server IP addresses are equal and the canonicalized
2406 * representation of their URLs, minus authentication parameters, are
2407 * case insensitivly and lexographically equal.
2408 * <p/>
2409 * For example, assuming the server <code>angus</code> resolves to the
2410 * <code>192.168.1.15</code> IP address, the below URLs would result in
2411 * <code>SmbFile</code>s that are equal.
2412 *
2413 * <p><blockquote><pre>
2414 * smb://192.168.1.15/share/DIR/foo.txt
2415 * smb://angus/share/data/../dir/foo.txt
2416 * </pre></blockquote>
2417 *
2418 * @param obj Another <code>SmbFile</code> object to compare for equality
2419 * @return <code>true</code> if the two objects refer to the same SMB resource
2420 * and <code>false</code> otherwise
2421 * @throws SmbException
2422 */

2423
2424    public boolean equals( Object JavaDoc obj ) {
2425        return obj instanceof SmbFile && obj.hashCode() == hashCode();
2426    }
2427
2428/**
2429 * Returns the string representation of this SmbFile object. This will
2430 * be the same as the URL used to construct this <code>SmbFile</code>.
2431 * This method will return the same value
2432 * as <code>getPath</code>.
2433 *
2434 * @return The original URL representation of this SMB resource
2435 * @throws SmbException
2436 */

2437
2438    public String JavaDoc toString() {
2439        return url.toString();
2440    }
2441
2442/* URLConnection implementation */
2443/**
2444 * This URLConnection method just returns the result of <tt>length()</tt>.
2445 *
2446 * @return the length of this file or 0 if it refers to a directory
2447 */

2448
2449    public int getContentLength() {
2450        try {
2451            return (int)(length() & 0xFFFFFFFFL);
2452        } catch( SmbException se ) {
2453        }
2454        return 0;
2455    }
2456
2457/**
2458 * This URLConnection method just returns the result of <tt>lastModified</tt>.
2459 *
2460 * @return the last modified data as milliseconds since Jan 1, 1970
2461 */

2462    public long getDate() {
2463        try {
2464            return lastModified();
2465        } catch( SmbException se ) {
2466        }
2467        return 0L;
2468    }
2469
2470/**
2471 * This URLConnection method just returns the result of <tt>lastModified</tt>.
2472 *
2473 * @return the last modified data as milliseconds since Jan 1, 1970
2474 */

2475    public long getLastModified() {
2476        try {
2477            return lastModified();
2478        } catch( SmbException se ) {
2479        }
2480        return 0L;
2481    }
2482
2483/**
2484 * This URLConnection method just returns a new <tt>SmbFileInputStream</tt> created with this file.
2485 *
2486 * @throws IOException thrown by <tt>SmbFileInputStream</tt> constructor
2487 */

2488    public InputStream JavaDoc getInputStream() throws IOException JavaDoc {
2489        return new SmbFileInputStream( this );
2490    }
2491
2492/**
2493 * This URLConnection method just returns a new <tt>SmbFileOutputStream</tt> created with this file.
2494 *
2495 * @throws IOException thrown by <tt>SmbFileOutputStream</tt> constructor
2496 */

2497    public OutputStream JavaDoc getOutputStream() throws IOException JavaDoc {
2498        return new SmbFileOutputStream( this );
2499    }
2500}
2501
Popular Tags