KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > knowgate > 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 com.knowgate.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
30 import com.knowgate.debug.*;
31 import com.knowgate.jcifs.Config;
32 import com.knowgate.jcifs.UniAddress;
33 import com.knowgate.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 = 0x3F;
350     static final int ATTR_SET_MASK = 0x27;
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 long attrExpirationPeriod;
358
359     static {
360         try {
361             Class.forName( "jcifs.Config" );
362         } catch( ClassNotFoundException JavaDoc cnfe ) {
363             cnfe.printStackTrace();
364         }
365         attrExpirationPeriod = Config.getLong( "jcifs.smb.client.attrExpirationPeriod", DEFAULT_ATTR_EXPIRATION_PERIOD );
366     }
367
368     /**
369      * Returned by {@link #getType()} if the resource this <tt>SmbFile</tt>
370      * represents is a regular file or directory.
371      */

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

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

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

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

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

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

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

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

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

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

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

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

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

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

550     public SmbFile( String JavaDoc context, String JavaDoc name, NtlmPasswordAuthentication auth, int shareAccess )
551                     throws MalformedURLException JavaDoc {
552         this( new URL JavaDoc( new URL JavaDoc( null, context, Handler.SMB_HANDLER ), name, Handler.SMB_HANDLER ), auth );
553         if ((shareAccess & ~(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE)) != 0) {
554             throw new RuntimeException JavaDoc( "Illegal shareAccess parameter" );
555         }
556         this.shareAccess = shareAccess;
557     }
558 /**
559  * Constructs an SmbFile representing a resource on an SMB network such
560  * as a file or directory from a <tt>URL</tt> object.
561  *
562  * @param url The URL of the target resource
563  */

564     public SmbFile( URL JavaDoc url ) {
565         this( url, new NtlmPasswordAuthentication( url.getUserInfo() ));
566     }
567 /**
568  * Constructs an SmbFile representing a resource on an SMB network such
569  * as a file or directory from a <tt>URL</tt> object and an
570  * <tt>NtlmPasswordAuthentication</tt> object.
571  *
572  * @param url The URL of the target resource
573  * @param auth The credentials the client should use for authentication
574  */

575     public SmbFile( URL JavaDoc url, NtlmPasswordAuthentication auth ) {
576         super( url );
577         this.auth = auth == null ? new NtlmPasswordAuthentication( url.getUserInfo() ) : auth;
578
579         getUncPath0();
580     }
581     SmbFile( SmbFile context, String JavaDoc name, int type,
582                 int attributes, long createTime, long lastModified, long size )
583                 throws MalformedURLException JavaDoc, UnknownHostException JavaDoc {
584         this( context.isWorkgroup0() ?
585             new URL JavaDoc( null, "smb://" + name + "/", Handler.SMB_HANDLER ) :
586             new URL JavaDoc( context.url, name + (( attributes & ATTR_DIRECTORY ) > 0 ? "/" : "" )));
587
588         if( context.share != null ) {
589             this.tree = context.tree;
590         }
591         int last = name.length() - 1;
592         if( name.charAt( last ) == '/' ) {
593             name = name.substring( 0, last );
594         }
595         if( context.share == null ) {
596             this.unc = "\\";
597         } else if( context.unc.equals( "\\" )) {
598             this.unc = '\\' + name;
599         } else {
600             this.unc = context.unc + '\\' + name;
601         }
602     /* why? am I going around in circles?
603      * this.type = type == TYPE_WORKGROUP ? 0 : type;
604      */

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

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

818
819         if( tree.session.transport.hasCapability( ServerMessageBlock.CAP_NT_SMBS )) {
820             SmbComNTCreateAndXResponse response = new SmbComNTCreateAndXResponse();
821             send( new SmbComNTCreateAndX( unc, flags, shareAccess,
822                     attrs, options, null ), response );
823             f = response.fid;
824             attributes = response.extFileAttributes & ATTR_GET_MASK;
825             attrExpiration = System.currentTimeMillis() + attrExpirationPeriod;
826             isExists = true;
827         } else {
828             SmbComOpenAndXResponse response = new SmbComOpenAndXResponse();
829             send( new SmbComOpenAndX( unc, flags, null ), response );
830             f = response.fid;
831         }
832
833         return f;
834     }
835     void open( int flags, int attrs, int options ) throws SmbException {
836         if( isOpen() ) {
837             return;
838         }
839         fid = open0( flags, attrs, options );
840         opened = true;
841     }
842     boolean isOpen() {
843         return opened && isConnected();
844     }
845     void close( int f, long lastWriteTime ) throws SmbException {
846
847         if( DebugFile.trace )
848             DebugFile.writeln( "close: " + f );
849
850         /*
851          * Close Request / Response
852          */

853
854         send( new SmbComClose( f, lastWriteTime ), blank_resp() );
855     }
856     void close( long lastWriteTime ) throws SmbException {
857         if( isOpen() == false ) {
858             return;
859         }
860         close( fid, lastWriteTime );
861         opened = false;
862     }
863     void close() throws SmbException {
864         close( 0L );
865     }
866
867 /**
868  * Returns the last component of the target URL. This will
869  * effectively be the name of the file or directory represented by this
870  * <code>SmbFile</code> or in the case of URLs that only specify a server
871  * or workgroup, the server or workgroup will be returned. The name of
872  * the root URL <code>smb://</code> is also <code>smb://</code>. If this
873  * <tt>SmbFile</tt> refers to a workgroup, server, share, or directory,
874  * the name will include a trailing slash '/' so that composing new
875  * <tt>SmbFile</tt>s will maintain the trailing slash requirement.
876  *
877  * @return The last component of the URL associated with this SMB
878  * resource or <code>smb://</code> if the resource is <code>smb://</code>
879  * itself.
880  */

881
882     public String JavaDoc getName() {
883         getUncPath0();
884         if( canon.length() > 1 ) {
885             int i = canon.length() - 2;
886             while( canon.charAt( i ) != '/' ) {
887                 i--;
888             }
889             return canon.substring( i + 1 );
890         } else if( share != null ) {
891             return share + '/';
892         } else if( url.getHost().length() > 0 ) {
893             return url.getHost() + '/';
894         } else {
895             return "smb://";
896         }
897     }
898
899 /**
900  * Everything but the last component of the URL representing this SMB
901  * resource is effectivly it's parent. The root URL <code>smb://</code>
902  * does not have a parent. In this case <code>smb://</code> is returned.
903  *
904  * @return The parent directory of this SMB resource or
905  * <code>smb://</code> if the resource refers to the root of the URL
906  * hierarchy which incedentally is also <code>smb://</code>.
907  */

908
909     public String JavaDoc getParent() {
910         String JavaDoc str = url.getAuthority();
911
912         if( str.length() > 0 ) {
913             StringBuffer JavaDoc sb = new StringBuffer JavaDoc( "smb://" );
914
915             sb.append( str );
916
917             getUncPath0();
918             if( canon.length() > 1 ) {
919                 sb.append( canon );
920             } else {
921                 sb.append( '/' );
922             }
923
924             str = sb.toString();
925
926             int i = str.length() - 2;
927             while( str.charAt( i ) != '/' ) {
928                 i--;
929             }
930
931             return str.substring( 0, i + 1 );
932         }
933
934         return "smb://";
935     }
936
937 /**
938  * Returns the full uncanonicalized URL of this SMB resource. An
939  * <code>SmbFile</code> constructed with the result of this method will
940  * result in an <code>SmbFile</code> that is equal to the original.
941  *
942  * @return The uncanonicalized full URL of this SMB resource.
943  */

944
945     public String JavaDoc getPath() {
946         return url.toString();
947     }
948
949     String JavaDoc getUncPath0() {
950         if( unc == null ) {
951             char[] in = url.getPath().toCharArray();
952             char[] out = new char[in.length];
953             int length = in.length, i, o, state, s;
954
955                               /* The canonicalization routine
956                                */

957             state = 0;
958             o = 0;
959             for( i = 0; i < length; i++ ) {
960                 switch( state ) {
961                     case 0:
962                         if( in[i] != '/' ) {
963                             return null;
964                         }
965                         out[o++] = in[i];
966                         state = 1;
967                         break;
968                     case 1:
969                         if( in[i] == '/' ) {
970                             break;
971                         } else if( in[i] == '.' &&
972                                     (( i + 1 ) >= length || in[i + 1] == '/' )) {
973                             i++;
974                             break;
975                         } else if(( i + 1 ) < length &&
976                                     in[i] == '.' &&
977                                     in[i + 1] == '.' &&
978                                     (( i + 2 ) >= length || in[i + 2] == '/' )) {
979                             i += 2;
980                             if( o == 1 ) break;
981                             do {
982                                 o--;
983                             } while( o > 1 && out[o - 1] != '/' );
984                             break;
985                         }
986                         state = 2;
987                     case 2:
988                         if( in[i] == '/' ) {
989                             state = 1;
990                         }
991                         out[o++] = in[i];
992                         break;
993                 }
994             }
995
996             canon = new String JavaDoc( out, 0, o );
997
998             if( o > 1 ) {
999                 o--;
1000                i = canon.indexOf( '/', 1 );
1001                if( i < 0 ) {
1002                    share = canon.substring( 1 );
1003                    unc = "\\";
1004                } else if( i == o ) {
1005                    share = canon.substring( 1, i );
1006                    unc = "\\";
1007                } else {
1008                    share = canon.substring( 1, i );
1009                    unc = canon.substring( i, out[o] == '/' ? o : o + 1 );
1010                    unc = unc.replace( '/', '\\' );
1011                }
1012            } else {
1013                share = null;
1014                unc = "\\";
1015            }
1016        }
1017        return unc;
1018    }
1019/**
1020 * Retuns the Windows UNC style path with backslashs intead of forward slashes.
1021 *
1022 * @return The UNC path.
1023 */

1024    public String JavaDoc getUncPath() {
1025        getUncPath0();
1026        if( share == null ) {
1027            return "\\\\" + url.getHost();
1028        }
1029        return "\\\\" + url.getHost() + canon.replace( '/', '\\' );
1030    }
1031/**
1032 * Returns the full URL of this SMB resource with '.' and '..' components
1033 * factored out. An <code>SmbFile</code> constructed with the result of
1034 * this method will result in an <code>SmbFile</code> that is equal to
1035 * the original.
1036 *
1037 * @return The canonicalized URL of this SMB resource.
1038 */

1039
1040    public String JavaDoc getCanonicalPath() {
1041        String JavaDoc str = url.getAuthority();
1042        getUncPath0();
1043        if( str.length() > 0 ) {
1044            return "smb://" + url.getAuthority() + canon;
1045        }
1046        return "smb://";
1047    }
1048
1049/**
1050 * Retrieves the share associated with this SMB resource. In
1051 * the case of <code>smb://</code>, <code>smb://workgroup/</code>,
1052 * and <code>smb://server/</code> URLs which do not specify a share,
1053 * <code>null</code> will be returned.
1054 *
1055 * @return The share component or <code>null</code> if there is no share
1056 */

1057
1058    public String JavaDoc getShare() {
1059        return share;
1060    }
1061
1062/**
1063 * Retrieve the hostname of the server for this SMB resource. If this
1064 * <code>SmbFile</code> references a workgroup, the name of the workgroup
1065 * is returned. If this <code>SmbFile</code> refers to the root of this
1066 * SMB network hierarchy, <code>null</code> is returned.
1067 *
1068 * @return The server or workgroup name or <code>null</code> if this
1069 * <code>SmbFile</code> refers to the root <code>smb://</code> resource.
1070 */

1071
1072    public String JavaDoc getServer() {
1073        String JavaDoc str = url.getHost();
1074        if( str.length() == 0 ) {
1075            return null;
1076        }
1077        return str;
1078    }
1079
1080/**
1081 * Returns type of of object this <tt>SmbFile</tt> represents.
1082 * @return <tt>TYPE_FILESYSTEM, TYPE_WORKGROUP, TYPE_SERVER, TYPE_SHARE,
1083 * TYPE_PRINTER, TYPE_NAMED_PIPE</tt>, or <tt>TYPE_COMM</tt>.
1084 */

1085    public int getType() throws SmbException {
1086        if( type == 0 ) {
1087            if( getUncPath0().length() > 1 ) {
1088                type = TYPE_FILESYSTEM;
1089            } else if( share != null ) {
1090                // treeConnect good enough to test service type
1091
connect0();
1092                if( share.equals( "IPC$" )) {
1093                    type = TYPE_NAMED_PIPE;
1094                } else if( tree.service.equals( "LPT1:" )) {
1095                    type = TYPE_PRINTER;
1096                } else if( tree.service.equals( "COMM" )) {
1097                    type = TYPE_COMM;
1098                } else {
1099                    type = TYPE_SHARE;
1100                }
1101            } else if( url.getAuthority().length() == 0 ) {
1102                type = TYPE_WORKGROUP;
1103            } else {
1104                UniAddress addr;
1105                try {
1106                    addr = getAddress();
1107                } catch( UnknownHostException JavaDoc uhe ) {
1108                    throw new SmbException( url.toString(), uhe );
1109                }
1110                if( addr.getAddress() instanceof NbtAddress ) {
1111                    int code = ((NbtAddress)addr.getAddress()).getNameType();
1112                    if( code == 0x1d || code == 0x1b ) {
1113                        type = TYPE_WORKGROUP;
1114                        return type;
1115                    }
1116                }
1117                type = TYPE_SERVER;
1118            }
1119        }
1120        return type;
1121    }
1122    boolean isWorkgroup0() throws UnknownHostException JavaDoc {
1123        if( type == TYPE_WORKGROUP || url.getHost().length() == 0 ) {
1124            type = TYPE_WORKGROUP;
1125            return true;
1126        } else {
1127            getUncPath0();
1128            if( share == null ) {
1129                UniAddress addr = getAddress();
1130                if( addr.getAddress() instanceof NbtAddress ) {
1131                    int code = ((NbtAddress)addr.getAddress()).getNameType();
1132                    if( code == 0x1d || code == 0x1b ) {
1133                        type = TYPE_WORKGROUP;
1134                        return true;
1135                    }
1136                }
1137                type = TYPE_SERVER;
1138            }
1139        }
1140        return false;
1141    }
1142
1143    Info queryPath( String JavaDoc path, int infoLevel ) throws SmbException {
1144        connect0();
1145
1146        if( DebugFile.trace )
1147            DebugFile.writeln( "queryPath: " + path );
1148
1149        /* normally we'd check the negotiatedCapabilities for CAP_NT_SMBS
1150         * however I can't seem to get a good last modified time from
1151         * SMB_COM_QUERY_INFORMATION so if NT_SMBs are requested
1152         * by the server than in this case that's what it will get
1153         * regardless of what jcifs.smb.client.useNTSmbs is set
1154         * to(overrides negotiatedCapabilities).
1155         */

1156
1157        /* We really should do the referral before this in case
1158         * the redirected target has different capabilities. But
1159         * the way we have been doing that is to call exists() which
1160         * calls this method so another technique will be necessary
1161         * to support DFS referral _to_ Win95/98/ME.
1162         */

1163
1164        if( tree.session.transport.hasCapability( ServerMessageBlock.CAP_NT_SMBS )) {
1165
1166            /*
1167             * Trans2 Query Path Information Request / Response
1168             */

1169
1170            Trans2QueryPathInformationResponse response =
1171                    new Trans2QueryPathInformationResponse( infoLevel );
1172            sendTransaction( new Trans2QueryPathInformation( path,
1173                    infoLevel ), response );
1174
1175            return response.info;
1176        } else {
1177
1178            /*
1179             * Query Information Request / Response
1180             */

1181
1182            SmbComQueryInformationResponse response =
1183                    new SmbComQueryInformationResponse(
1184                    tree.session.transport.server.serverTimeZone * 1000 * 60L );
1185            send( new SmbComQueryInformation( path ), response );
1186            return response;
1187        }
1188    }
1189
1190/**
1191 * Tests to see if the SMB resource exists. If the resource refers
1192 * only to a server, this method determines if the server exists on the
1193 * network and is advertising SMB services. If this resource refers to
1194 * a workgroup, this method determines if the workgroup name is valid on
1195 * the local SMB network. If this <code>SmbFile</code> refers to the root
1196 * <code>smb://</code> resource <code>true</code> is always returned. If
1197 * this <code>SmbFile</code> is a traditional file or directory, it will
1198 * be queried for on the specified server as expected.
1199 *
1200 * @return <code>true</code> if the resource exists or is alive or
1201 * <code>false</code> otherwise
1202 */

1203
1204    public boolean exists() throws SmbException {
1205
1206        if( attrExpiration > System.currentTimeMillis() ) {
1207            return isExists;
1208        }
1209
1210        attributes = ATTR_READONLY | ATTR_DIRECTORY;
1211        createTime = 0L;
1212        lastModified = 0L;
1213        isExists = false;
1214
1215        try {
1216            if( url.getHost().length() == 0 ) {
1217            } else if( share == null ) {
1218                if( getType() == TYPE_WORKGROUP ) {
1219                    UniAddress.getByName( url.getHost(), true );
1220                } else {
1221                    UniAddress.getByName( url.getHost() ).getHostName();
1222                }
1223            } else if( getUncPath0().length() == 1 ||
1224                                        share.equalsIgnoreCase( "IPC$" )) {
1225                connect0(); // treeConnect is good enough
1226
} else {
1227                Info info = queryPath( getUncPath0(),
1228                    Trans2QueryPathInformationResponse.SMB_QUERY_FILE_BASIC_INFO );
1229                attributes = info.getAttributes();
1230                createTime = info.getCreateTime();
1231                lastModified = info.getLastWriteTime();
1232            }
1233
1234            /* If any of the above fail, isExists will not be set true
1235             */

1236
1237            isExists = true;
1238
1239        } catch( UnknownHostException JavaDoc uhe ) {
1240        } catch( SmbException se ) {
1241            switch (se.getNtStatus()) {
1242                case NtStatus.NT_STATUS_NO_SUCH_FILE:
1243                case NtStatus.NT_STATUS_OBJECT_NAME_INVALID:
1244                case NtStatus.NT_STATUS_OBJECT_NAME_NOT_FOUND:
1245                case NtStatus.NT_STATUS_OBJECT_PATH_NOT_FOUND:
1246                    break;
1247                default:
1248                    throw se;
1249            }
1250        }
1251
1252        attrExpiration = System.currentTimeMillis() + attrExpirationPeriod;
1253
1254        return isExists;
1255    }
1256
1257/**
1258 * Tests to see if the file this <code>SmbFile</code> represents can be
1259 * read. Because any file, directory, or other resource can be read if it
1260 * exists, this method simply calls the <code>exists</code> method.
1261 *
1262 * @return <code>true</code> if the file is read-only
1263 */

1264
1265    public boolean canRead() throws SmbException {
1266        if( getType() == TYPE_NAMED_PIPE ) { // try opening the pipe for reading?
1267
return true;
1268        }
1269        return exists(); // try opening and catch sharing violation?
1270
}
1271
1272/**
1273 * Tests to see if the file this <code>SmbFile</code> represents
1274 * exists and is not marked read-only. By default, resources are
1275 * considered to be read-only and therefore for <code>smb://</code>,
1276 * <code>smb://workgroup/</code>, and <code>smb://server/</code> resources
1277 * will be read-only.
1278 *
1279 * @return <code>true</code> if the resource exists is not marked
1280 * read-only
1281 */

1282
1283    public boolean canWrite() throws SmbException {
1284        if( getType() == TYPE_NAMED_PIPE ) { // try opening the pipe for writing?
1285
return true;
1286        }
1287        return exists() && ( attributes & ATTR_READONLY ) == 0;
1288    }
1289
1290/**
1291 * Tests to see if the file this <code>SmbFile</code> represents is a directory.
1292 *
1293 * @return <code>true</code> if this <code>SmbFile</code> is a directory
1294 */

1295
1296    public boolean isDirectory() throws SmbException {
1297        if( getUncPath0().length() == 1 ) {
1298            return true;
1299        }
1300        if (!exists()) return false;
1301        return ( attributes & ATTR_DIRECTORY ) == ATTR_DIRECTORY;
1302    }
1303
1304/**
1305 * Tests to see if the file this <code>SmbFile</code> represents is not a directory.
1306 *
1307 * @return <code>true</code> if this <code>SmbFile</code> is not a directory
1308 */

1309
1310    public boolean isFile() throws SmbException {
1311        if( getUncPath0().length() == 1 ) {
1312            return false;
1313        }
1314        exists();
1315        return ( attributes & ATTR_DIRECTORY ) == 0;
1316    }
1317
1318/**
1319 * Tests to see if the file this SmbFile represents is marked as
1320 * hidden. This method will also return true for shares with names that
1321 * end with '$' such as <code>IPC$</code> or <code>C$</code>.
1322 *
1323 * @return <code>true</code> if the <code>SmbFile</code> is marked as being hidden
1324 */

1325
1326    public boolean isHidden() throws SmbException {
1327        if( share == null ) {
1328            return false;
1329        } else if( getUncPath0().length() == 1 ) {
1330            if( share.endsWith( "$" )) {
1331                return true;
1332            }
1333            return false;
1334        }
1335        exists();
1336        return ( attributes & ATTR_HIDDEN ) == ATTR_HIDDEN;
1337    }
1338
1339/**
1340 * If the path of this <code>SmbFile</code> falls within a DFS volume,
1341 * this method will return the referral path to which it maps. Otherwise
1342 * <code>null</code> is returned.
1343 */

1344
1345    public String JavaDoc getDfsPath() throws SmbException {
1346        connect0();
1347        if( tree.inDfs ) {
1348            exists();
1349        }
1350        if( dfsReferral == null ) {
1351            return null;
1352        }
1353        return "smb:/" + (new String JavaDoc( dfsReferral.node + unc )).replace( '\\', '/' );
1354    }
1355
1356/**
1357 * Retrieve the time this <code>SmbFile</code> was created. The value
1358 * returned is suitable for constructing a {@link java.util.Date} object
1359 * (i.e. seconds since Epoch 1970). Times should be the same as those
1360 * reported using the properties dialog of the Windows Explorer program.
1361 *
1362 * For Win95/98/Me this is actually the last write time. It is currently
1363 * not possible to retrieve the create time from files on these systems.
1364 *
1365 * @return The number of milliseconds since the 00:00:00 GMT, January 1,
1366 * 1970 as a <code>long</code> value
1367 */

1368    public long createTime() throws SmbException {
1369        if( getUncPath0().length() > 1 ) {
1370            exists();
1371            return createTime;
1372        }
1373        return 0L;
1374    }
1375/**
1376 * Retrieve the last time the file represented by this
1377 * <code>SmbFile</code> was modified. The value returned is suitable for
1378 * constructing a {@link java.util.Date} object (i.e. seconds since Epoch
1379 * 1970). Times should be the same as those reported using the properties
1380 * dialog of the Windows Explorer program.
1381 *
1382 * @return The number of milliseconds since the 00:00:00 GMT, January 1,
1383 * 1970 as a <code>long</code> value
1384 */

1385    public long lastModified() throws SmbException {
1386        if( getUncPath0().length() > 1 ) {
1387            exists();
1388            return lastModified;
1389        }
1390        return 0L;
1391    }
1392/**
1393 * List the contents of this SMB resource. The list returned by this
1394 * method will be;
1395 *
1396 * <ul>
1397 * <li> files and directories contained within this resource if the
1398 * resource is a normal disk file directory,
1399 * <li> all available NetBIOS workgroups or domains if this resource is
1400 * the top level URL <code>smb://</code>,
1401 * <li> all servers registered as members of a NetBIOS workgroup if this
1402 * resource refers to a workgroup in a <code>smb://workgroup/</code> URL,
1403 * <li> all browseable shares of a server including printers, IPC
1404 * services, or disk volumes if this resource is a server URL in the form
1405 * <code>smb://server/</code>,
1406 * <li> or <code>null</code> if the resource cannot be resolved.
1407 * </ul>
1408 *
1409 * @return A <code>String[]</code> array of files and directories,
1410 * workgroups, servers, or shares depending on the context of the
1411 * resource URL
1412 */

1413    public String JavaDoc[] list() throws SmbException {
1414        return list( "*", ATTR_DIRECTORY | ATTR_HIDDEN | ATTR_SYSTEM, null, null );
1415    }
1416
1417/**
1418 * List the contents of this SMB resource. The list returned will be
1419 * identical to the list returned by the parameterless <code>list()</code>
1420 * method minus filenames filtered by the specified filter.
1421 *
1422 * @param filter a filename filter to exclude filenames from the results
1423 * @throws SmbException
1424 # @return An array of filenames
1425 */

1426    public String JavaDoc[] list( SmbFilenameFilter filter ) throws SmbException {
1427        return list( "*", ATTR_DIRECTORY | ATTR_HIDDEN | ATTR_SYSTEM, filter, null );
1428    }
1429
1430/**
1431 * List the contents of this SMB resource as an array of
1432 * <code>SmbFile</code> objects. This method is much more efficient than
1433 * the regular <code>list</code> method when querying attributes of each
1434 * file in the result set.
1435 * <p>
1436 * The list of <code>SmbFile</code>s returned by this method will be;
1437 *
1438 * <ul>
1439 * <li> files and directories contained within this resource if the
1440 * resource is a normal disk file directory,
1441 * <li> all available NetBIOS workgroups or domains if this resource is
1442 * the top level URL <code>smb://</code>,
1443 * <li> all servers registered as members of a NetBIOS workgroup if this
1444 * resource refers to a workgroup in a <code>smb://workgroup/</code> URL,
1445 * <li> all browseable shares of a server including printers, IPC
1446 * services, or disk volumes if this resource is a server URL in the form
1447 * <code>smb://server/</code>,
1448 * <li> or <code>null</code> if the resource cannot be resolved.
1449 * </ul>
1450 *
1451 * @return An array of <code>SmbFile</code> objects representing file
1452 * and directories, workgroups, servers, or shares depending on the context
1453 * of the resource URL
1454 */

1455    public SmbFile[] listFiles() throws SmbException {
1456        return listFiles( "*", ATTR_DIRECTORY | ATTR_HIDDEN | ATTR_SYSTEM, null, null );
1457    }
1458
1459/**
1460 * The CIFS protocol provides for DOS "wildcards" to be used as
1461 * a performance enhancement. The client does not have to filter
1462 * the names and the server does not have to return all directory
1463 * entries.
1464 * <p>
1465 * The wildcard expression may consist of two special meta
1466 * characters in addition to the normal filename characters. The '*'
1467 * character matches any number of characters in part of a name. If
1468 * the expression begins with one or more '?'s then exactly that
1469 * many characters will be matched whereas if it ends with '?'s
1470 * it will match that many characters <i>or less</i>.
1471 * <p>
1472 * Wildcard expressions will not filter workgroup names or server names.
1473 *
1474 * <blockquote><pre>
1475 * winnt> ls c?o*
1476 * clock.avi -rw-- 82944 Mon Oct 14 1996 1:38 AM
1477 * Cookies drw-- 0 Fri Nov 13 1998 9:42 PM
1478 * 2 items in 5ms
1479 * </pre></blockquote>
1480 *
1481 * @param wildcard a wildcard expression
1482 * @throws SmbException
1483 * @return An array of <code>SmbFile</code> objects representing file
1484 * and directories, workgroups, servers, or shares depending on the context
1485 * of the resource URL
1486 */

1487
1488    public SmbFile[] listFiles( String JavaDoc wildcard ) throws SmbException {
1489        return listFiles( wildcard, ATTR_DIRECTORY | ATTR_HIDDEN | ATTR_SYSTEM, null, null );
1490    }
1491/**
1492 * List the contents of this SMB resource. The list returned will be
1493 * identical to the list returned by the parameterless <code>listFiles()</code>
1494 * method minus files filtered by the specified filename filter.
1495 *
1496 * @param filter a filter to exclude files from the results
1497 * @return An array of <tt>SmbFile</tt> objects
1498 * @throws SmbException
1499 */

1500    public SmbFile[] listFiles( SmbFilenameFilter filter ) throws SmbException {
1501        return listFiles( "*", ATTR_DIRECTORY | ATTR_HIDDEN | ATTR_SYSTEM, filter, null );
1502    }
1503/**
1504 * List the contents of this SMB resource. The list returned will be
1505 * identical to the list returned by the parameterless <code>listFiles()</code>
1506 * method minus filenames filtered by the specified filter.
1507 *
1508 * @param filter a file filter to exclude files from the results
1509 * @return An array of <tt>SmbFile</tt> objects
1510 */

1511    public SmbFile[] listFiles( SmbFileFilter filter ) throws SmbException {
1512        return listFiles( "*", ATTR_DIRECTORY | ATTR_HIDDEN | ATTR_SYSTEM, null, filter );
1513    }
1514    String JavaDoc[] list( String JavaDoc wildcard, int searchAttributes,
1515                SmbFilenameFilter fnf, SmbFileFilter ff ) throws SmbException {
1516        ArrayList JavaDoc list = new ArrayList JavaDoc();
1517
1518        try {
1519            if( url.getHost().length() == 0 || share == null ) {
1520                doNetEnum( list, false, wildcard, searchAttributes, fnf, ff );
1521            } else {
1522                doFindFirstNext( list, false, wildcard, searchAttributes, fnf, ff );
1523            }
1524        } catch( UnknownHostException JavaDoc uhe ) {
1525            throw new SmbException( url.toString(), uhe );
1526        } catch( MalformedURLException JavaDoc mue ) {
1527            throw new SmbException( url.toString(), mue );
1528        }
1529
1530        return (String JavaDoc[])list.toArray(new String JavaDoc[list.size()]);
1531    }
1532    SmbFile[] listFiles( String JavaDoc wildcard, int searchAttributes,
1533                SmbFilenameFilter fnf, SmbFileFilter ff ) throws SmbException {
1534        ArrayList JavaDoc list = new ArrayList JavaDoc();
1535
1536        if( ff != null && ff instanceof DosFileFilter ) {
1537            DosFileFilter dff = (DosFileFilter)ff;
1538            if( dff.wildcard != null ) {
1539                wildcard = dff.wildcard;
1540            }
1541            searchAttributes = dff.attributes;
1542        }
1543
1544        try {
1545            if( url.getHost().length() == 0 || share == null ) {
1546                doNetEnum( list, true, wildcard, searchAttributes, fnf, ff );
1547            } else {
1548                doFindFirstNext( list, true, wildcard, searchAttributes, fnf, ff );
1549            }
1550        } catch( UnknownHostException JavaDoc uhe ) {
1551            throw new SmbException( url.toString(), uhe );
1552        } catch( MalformedURLException JavaDoc mue ) {
1553            throw new SmbException( url.toString(), mue );
1554        }
1555
1556        return (SmbFile[])list.toArray(new SmbFile[list.size()]);
1557    }
1558    void doNetEnum( ArrayList JavaDoc list,
1559                boolean files,
1560                String JavaDoc wildcard,
1561                int searchAttributes,
1562                SmbFilenameFilter fnf,
1563                SmbFileFilter ff ) throws SmbException,
1564                        UnknownHostException JavaDoc, MalformedURLException JavaDoc {
1565        SmbComTransaction req;
1566        SmbComTransactionResponse resp;
1567        int listType = url.getAuthority().length() == 0 ? 0 : getType();
1568        String JavaDoc p = url.getPath();
1569
1570        if( p.lastIndexOf( '/' ) != ( p.length() - 1 )) {
1571            throw new SmbException( url.toString() + " directory must end with '/'" );
1572        }
1573
1574        switch( listType ) {
1575            case 0:
1576                connect0();
1577                req = new NetServerEnum2( tree.session.transport.server.oemDomainName,
1578                        NetServerEnum2.SV_TYPE_DOMAIN_ENUM );
1579                resp = new NetServerEnum2Response();
1580                break;
1581            case TYPE_WORKGROUP:
1582                req = new NetServerEnum2( url.getHost(), NetServerEnum2.SV_TYPE_ALL );
1583                resp = new NetServerEnum2Response();
1584                break;
1585            case TYPE_SERVER:
1586                req = new NetShareEnum();
1587                resp = new NetShareEnumResponse();
1588                break;
1589            default:
1590                throw new SmbException( "The requested list operations is invalid: " + url.toString() );
1591        }
1592
1593        do {
1594            sendTransaction( req, resp );
1595
1596            if( resp.status != SmbException.ERROR_SUCCESS &&
1597                    resp.status != SmbException.ERROR_MORE_DATA ) {
1598                throw new SmbException( resp.status, true );
1599            }
1600
1601            for( int i = 0; i < resp.numEntries; i++ ) {
1602                FileEntry e = resp.results[i];
1603                String JavaDoc name = e.getName();
1604                if( fnf != null && fnf.accept( this, name ) == false ) {
1605                    continue;
1606                }
1607                if( name.length() > 0 ) {
1608                    SmbFile f = new SmbFile( this, name,
1609                                listType == 0 ? TYPE_WORKGROUP : (listType << 1),
1610                                ATTR_READONLY | ATTR_DIRECTORY, 0L, 0L, 0L );
1611                    if( ff != null && ff.accept( f ) == false ) {
1612                        continue;
1613                    }
1614                    if( files ) {
1615                        list.add( f );
1616                    } else {
1617                        list.add( name );
1618                    }
1619                }
1620            }
1621            if( listType != 0 || listType != TYPE_WORKGROUP ) {
1622                break;
1623            }
1624            req.subCommand = (byte)SmbComTransaction.NET_SERVER_ENUM3;
1625            req.reset( 0, ((NetServerEnum2Response)resp).lastName );
1626        } while( resp.status == SmbException.ERROR_MORE_DATA );
1627    }
1628    void doFindFirstNext( ArrayList JavaDoc list,
1629                boolean files,
1630                String JavaDoc wildcard,
1631                int searchAttributes,
1632                SmbFilenameFilter fnf,
1633                SmbFileFilter ff ) throws SmbException, UnknownHostException JavaDoc, MalformedURLException JavaDoc {
1634        SmbComTransaction req;
1635        Trans2FindFirst2Response resp;
1636        int sid;
1637        String JavaDoc path = getUncPath0();
1638        String JavaDoc p = url.getPath();
1639
1640        if( p.lastIndexOf( '/' ) != ( p.length() - 1 )) {
1641            throw new SmbException( url.toString() + " directory must end with '/'" );
1642        }
1643
1644        req = new Trans2FindFirst2( path, wildcard, searchAttributes );
1645        resp = new Trans2FindFirst2Response();
1646
1647        if( DebugFile.trace )
1648            DebugFile.writeln( "doFindFirstNext: " + req.path );
1649
1650        sendTransaction( req, resp );
1651
1652        sid = resp.sid;
1653        req = new Trans2FindNext2( sid, resp.resumeKey, resp.lastName );
1654
1655        /* The only difference between first2 and next2 responses is subCommand
1656         * so let's recycle the response object.
1657         */

1658        resp.subCommand = SmbComTransaction.TRANS2_FIND_NEXT2;
1659
1660        for( ;; ) {
1661            for( int i = 0; i < resp.numEntries; i++ ) {
1662                FileEntry e = resp.results[i];
1663                String JavaDoc name = e.getName();
1664                if( name.length() < 3 ) {
1665                    int h = name.hashCode();
1666                    if( h == HASH_DOT || h == HASH_DOT_DOT ) {
1667                        continue;
1668                    }
1669                }
1670                if( fnf != null && fnf.accept( this, name ) == false ) {
1671                    continue;
1672                }
1673                if( name.length() > 0 ) {
1674                    SmbFile f = new SmbFile( this, name, TYPE_FILESYSTEM,
1675                            e.getAttributes(), e.createTime(), e.lastModified(), e.length() );
1676                    if( ff != null && ff.accept( f ) == false ) {
1677                        continue;
1678                    }
1679                    if( files ) {
1680                        list.add( f );
1681                    } else {
1682                        list.add( name );
1683                    }
1684                }
1685            }
1686
1687            if( resp.isEndOfSearch || resp.numEntries == 0 ) {
1688                break;
1689            }
1690
1691            req.reset( resp.resumeKey, resp.lastName );
1692            resp.reset();
1693            sendTransaction( req, resp );
1694        }
1695
1696        send( new SmbComFindClose2( sid ), blank_resp() );
1697    }
1698
1699/**
1700 * Changes the name of the file this <code>SmbFile</code> represents to the name
1701 * designated by the <code>SmbFile</code> argument.
1702 * <p/>
1703 * <i>Remember: <code>SmbFile</code>s are immutible and therefore
1704 * the path associated with this <code>SmbFile</code> object will not
1705 * change). To access the renamed file it is necessary to construct a
1706 * new <tt>SmbFile</tt></i>.
1707 *
1708 * @param dest An <code>SmbFile</code> that represents the new pathname
1709 * @return <code>true</code> if the file or directory was successfully renamed
1710 * @throws NullPointerException
1711 * If the <code>dest</code> argument is <code>null</code>
1712 */

1713    public void renameTo( SmbFile dest ) throws SmbException {
1714        if( getUncPath0().length() == 1 || dest.getUncPath0().length() == 1 ) {
1715            throw new SmbException( "Invalid operation for workgroups, servers, or shares" );
1716        }
1717        connect0();
1718        dest.connect0();
1719
1720        if( tree.inDfs ) { /* This ensures that each path is
1721                * resolved independantly to deal with the case where files
1722                * have the same base path but ultimately turn out to be
1723                * on different servers because of DFS. It also eliminates
1724                * manipulating the SMB path which is problematic because
1725                * there are really two that would need to be prefixed
1726                * with host and share as DFS requires.
1727                */

1728            exists();
1729            dest.exists();
1730        }
1731        if( tree != dest.tree ) {
1732            throw new SmbException( "Invalid operation for workgroups, servers, or shares" );
1733        }
1734
1735        if( DebugFile.trace )
1736            DebugFile.writeln( "renameTo: " + unc + " -> " + dest.unc );
1737
1738        attrExpiration = sizeExpiration = 0;
1739        dest.attrExpiration = 0;
1740
1741        /*
1742         * Rename Request / Response
1743         */

1744
1745        send( new SmbComRename( unc, dest.unc ), blank_resp() );
1746    }
1747
1748    class WriterThread extends Thread JavaDoc {
1749        byte[] b;
1750        int n, off;
1751        boolean ready = true;
1752        SmbFile dest;
1753        SmbException e = null;
1754        boolean useNTSmbs;
1755        SmbComWriteAndX reqx;
1756        SmbComWrite req;
1757        ServerMessageBlock resp;
1758
1759        WriterThread() throws SmbException {
1760            super( "JCIFS-WriterThread" );
1761            useNTSmbs = tree.session.transport.hasCapability( ServerMessageBlock.CAP_NT_SMBS );
1762            if( useNTSmbs ) {
1763                reqx = new SmbComWriteAndX();
1764                resp = new SmbComWriteAndXResponse();
1765            } else {
1766                req = new SmbComWrite();
1767                resp = new SmbComWriteResponse();
1768            }
1769        }
1770
1771        synchronized void write( byte[] b, int n, SmbFile dest, int off ) {
1772            this.b = b;
1773            this.n = n;
1774            this.dest = dest;
1775            this.off = off;
1776            ready = false;
1777            notify();
1778        }
1779
1780        public void run() {
1781            synchronized( this ) {
1782                try {
1783                    for( ;; ) {
1784                        ready = true;
1785                        while( ready ) {
1786                            wait();
1787                        }
1788                        if( n == -1 ) {
1789                            return;
1790                        }
1791                        if( useNTSmbs ) {
1792                            reqx.setParam( dest.fid, off, n, b, 0, n );
1793                            dest.send( reqx, resp );
1794                        } else {
1795                            req.setParam( dest.fid, off, n, b, 0, n );
1796                            dest.send( req, resp );
1797                        }
1798                        notify();
1799                    }
1800                } catch( SmbException e ) {
1801                    this.e = e;
1802                } catch( Exception JavaDoc x ) {
1803                    this.e = new SmbException( "WriterThread", x );
1804                }
1805                notify();
1806            }
1807        }
1808    }
1809    void copyTo0( SmbFile dest, byte[][] b, int bsize, WriterThread w,
1810            SmbComReadAndX req, SmbComReadAndXResponse resp ) throws SmbException {
1811        int i;
1812
1813        if( attrExpiration < System.currentTimeMillis() ) {
1814            attributes = ATTR_READONLY | ATTR_DIRECTORY;
1815            createTime = 0L;
1816            lastModified = 0L;
1817            isExists = false;
1818
1819            Info info = queryPath( getUncPath0(),
1820                    Trans2QueryPathInformationResponse.SMB_QUERY_FILE_BASIC_INFO );
1821            attributes = info.getAttributes();
1822            createTime = info.getCreateTime();
1823            lastModified = info.getLastWriteTime();
1824
1825            /* If any of the above fails, isExists will not be set true
1826             */

1827
1828            isExists = true;
1829            attrExpiration = System.currentTimeMillis() + attrExpirationPeriod;
1830        }
1831
1832        if( isDirectory() ) {
1833            SmbFile[] files;
1834            SmbFile ndest;
1835
1836            try {
1837                dest.mkdir();
1838                dest.setPathInformation( attributes, createTime, lastModified );
1839            } catch( SmbException se ) {
1840                if( se.getNtStatus() != NtStatus.NT_STATUS_ACCESS_DENIED &&
1841                        se.getNtStatus() != NtStatus.NT_STATUS_OBJECT_NAME_COLLISION ) {
1842                    throw se;
1843                }
1844            }
1845
1846            files = listFiles( "*", ATTR_DIRECTORY | ATTR_HIDDEN | ATTR_SYSTEM, null, null );
1847            try {
1848                for( i = 0; i < files.length; i++ ) {
1849                    ndest = new SmbFile( dest,
1850                                    files[i].getName(),
1851                                    files[i].type,
1852                                    files[i].attributes,
1853                                    files[i].createTime,
1854                                    files[i].lastModified,
1855                                    files[i].size );
1856                    files[i].copyTo0( ndest, b, bsize, w, req, resp );
1857                }
1858            } catch( UnknownHostException JavaDoc uhe ) {
1859                throw new SmbException( url.toString(), uhe );
1860            } catch( MalformedURLException JavaDoc mue ) {
1861                throw new SmbException( url.toString(), mue );
1862            }
1863        } else {
1864            int off;
1865
1866            open( SmbFile.O_RDONLY, ATTR_NORMAL, 0 );
1867            try {
1868                dest.open( SmbFile.O_CREAT | SmbFile.O_WRONLY | SmbFile.O_TRUNC |
1869                        SmbComNTCreateAndX.FILE_WRITE_ATTRIBUTES << 16, attributes, 0 );
1870            } catch( SmbAuthException sae ) {
1871                if(( dest.attributes & ATTR_READONLY ) != 0 ) {
1872                                            /* Remove READONLY and try again
1873                                             */

1874                    dest.setPathInformation( dest.attributes & ~ATTR_READONLY, 0L, 0L );
1875                    dest.open( SmbFile.O_CREAT | SmbFile.O_WRONLY | SmbFile.O_TRUNC |
1876                            SmbComNTCreateAndX.FILE_WRITE_ATTRIBUTES << 16, attributes, 0 );
1877                } else {
1878                    throw sae;
1879                }
1880            }
1881
1882            i = off = 0;
1883            for( ;; ) {
1884                req.setParam( fid, off, bsize );
1885                resp.setParam( b[i], 0 );
1886                send( req, resp );
1887
1888                synchronized( w ) {
1889                    while( !w.ready ) {
1890                        try {
1891                            w.wait();
1892                        } catch( InterruptedException JavaDoc ie ) {
1893                            throw new SmbException( dest.url.toString(), ie );
1894                        }
1895                    }
1896                    if( w.e != null ) {
1897                        throw w.e;
1898                    }
1899                    if( resp.dataLength <= 0 ) {
1900                        break;
1901                    }
1902                    w.write( b[i], resp.dataLength, dest, off );
1903                }
1904
1905                i = i == 1 ? 0 : 1;
1906                off += resp.dataLength;
1907            }
1908
1909            dest.sendTransaction( new Trans2SetFileInformation(
1910                    dest.fid, attributes, createTime, lastModified ),
1911                    new Trans2SetFileInformationResponse() );
1912            dest.close( 0L );
1913            close();
1914        }
1915    }
1916/**
1917 * This method will copy the file or directory represented by this
1918 * <tt>SmbFile</tt> and it's sub-contents to the location specified by the
1919 * <tt>dest</tt> parameter. This file and the destination file do not
1920 * need to be on the same host. This operation does not copy extended
1921 * file attibutes such as ACLs but it does copy regular attributes as
1922 * well as create and last write times. This method is almost twice as
1923 * efficient as manually copying as it employs an additional write
1924 * thread to read and write data concurrently.
1925 * <p/>
1926 * It is not possible (nor meaningful) to copy entire workgroups or
1927 * servers.
1928 *
1929 * @param dest the destination file or directory
1930 * @throw SmbException
1931 */

1932    public void copyTo( SmbFile dest ) throws SmbException {
1933        SmbComReadAndX req;
1934        SmbComReadAndXResponse resp;
1935        WriterThread w;
1936        int bsize;
1937        byte[][] b;
1938
1939        /* Should be able to copy an entire share actually
1940         */

1941        if( share == null || dest.share == null) {
1942            throw new SmbException( "Invalid operation for workgroups or servers" );
1943        }
1944
1945        req = new SmbComReadAndX();
1946        resp = new SmbComReadAndXResponse();
1947
1948        connect0();
1949        dest.connect0();
1950
1951        if( tree.inDfs ) {
1952                /* At this point the maxBufferSize values are from the server
1953                 * exporting the volumes, not the one that we will actually
1954                 * end up performing IO with. If the server hosting the
1955                 * actual files has a smaller maxBufSize this could be
1956                 * incorrect. To handle this properly it is necessary
1957                 * to redirect the tree to the target server first before
1958                 * establishing buffer size. These exists() calls facilitate
1959                 * that.
1960                 */

1961            exists();
1962            dest.exists();
1963        }
1964
1965        w = new WriterThread();
1966        w.setDaemon( true );
1967        w.start();
1968
1969        bsize = Math.min( tree.session.transport.rcv_buf_size - 70,
1970                        tree.session.transport.snd_buf_size - 70 );
1971        b = new byte[2][bsize];
1972
1973        copyTo0( dest, b, bsize, w, req, resp );
1974        w.write( null, -1, null, 0 );
1975    }
1976
1977/**
1978 * This method will delete the file or directory specified by this
1979 * <code>SmbFile</code>. If the target is a directory, the contents of
1980 * the directory will be deleted as well. If a file within the directory or
1981 * it's sub-directories is marked read-only, the read-only status will
1982 * be removed and the file will be deleted.
1983 *
1984 * @throws SmbException
1985 */

1986    public void delete() throws SmbException {
1987        if( tree == null || tree.inDfs ) {
1988            exists(); /* This is necessary to ensure we
1989                            * pass a path adjusted for DFS */

1990        }
1991        getUncPath0();
1992        delete( unc );
1993    }
1994    void delete( String JavaDoc fileName ) throws SmbException {
1995        if( getUncPath0().length() == 1 ) {
1996            throw new SmbException( "Invalid operation for workgroups, servers, or shares" );
1997        }
1998
1999        if( System.currentTimeMillis() > attrExpiration ) {
2000            attributes = ATTR_READONLY | ATTR_DIRECTORY;
2001            createTime = 0L;
2002            lastModified = 0L;
2003            isExists = false;
2004
2005            Info info = queryPath( getUncPath0(),
2006                    Trans2QueryPathInformationResponse.SMB_QUERY_FILE_BASIC_INFO );
2007            attributes = info.getAttributes();
2008            createTime = info.getCreateTime();
2009            lastModified = info.getLastWriteTime();
2010
2011            attrExpiration = System.currentTimeMillis() + attrExpirationPeriod;
2012            isExists = true;
2013        }
2014
2015        if(( attributes & ATTR_READONLY ) != 0 ) {
2016            setReadWrite();
2017        }
2018
2019        /*
2020         * Delete or Delete Directory Request / Response
2021         */

2022
2023        if( DebugFile.trace )
2024            DebugFile.writeln( "delete: " + fileName );
2025
2026        if(( attributes & ATTR_DIRECTORY ) != 0 ) {
2027
2028            /* Recursively delete directory contents
2029             */

2030
2031            SmbFile[] l = listFiles( "*",
2032                    ATTR_DIRECTORY | ATTR_HIDDEN | ATTR_SYSTEM, null, null );
2033
2034            for( int i = 0; i < l.length; i++ ) {
2035                l[i].delete();
2036            }
2037
2038            send( new SmbComDeleteDirectory( fileName ), blank_resp() );
2039        } else {
2040            send( new SmbComDelete( fileName ), blank_resp() );
2041        }
2042
2043        attrExpiration = sizeExpiration = 0;
2044    }
2045
2046/**
2047 * Returns the length of this <tt>SmbFile</tt> in bytes. If this object
2048 * is a <tt>TYPE_SHARE</tt> the total capacity of the disk shared in
2049 * bytes is returned. If this object is a directory or a type other than
2050 * <tt>TYPE_SHARE</tt>, 0L is returned.
2051 *
2052 * @return The length of the file in bytes or 0 if this
2053 * <code>SmbFile</code> is not a file.
2054 * @throw SmbException
2055 */

2056
2057    public long length() throws SmbException {
2058        if( sizeExpiration > System.currentTimeMillis() ) {
2059            return size;
2060        }
2061
2062        if( getType() == TYPE_SHARE ) {
2063            Trans2QueryFSInformationResponse response;
2064            int level = Trans2QueryFSInformationResponse.SMB_INFO_ALLOCATION;
2065
2066            response = new Trans2QueryFSInformationResponse( level );
2067            sendTransaction( new Trans2QueryFSInformation( level ), response );
2068
2069            size = response.info.getCapacity();
2070        } else if( getUncPath0().length() > 1 && type != TYPE_NAMED_PIPE ) {
2071            Info info = queryPath( getUncPath0(),
2072                    Trans2QueryPathInformationResponse.SMB_QUERY_FILE_STANDARD_INFO );
2073            size = info.getSize();
2074        } else {
2075            size = 0L;
2076        }
2077        sizeExpiration = System.currentTimeMillis() + attrExpirationPeriod;
2078        return size;
2079    }
2080
2081/**
2082 * This method returns the free disk space in bytes of the drive this share
2083 * represents or the drive on which the directory or file resides. Objects
2084 * other than <tt>TYPE_SHARE</tt> or <tt>TYPE_FILESYSTEM</tt> will result
2085 * in 0L being returned.
2086 *
2087 * @return the free disk space in bytes of the drive on which this file or
2088 * directory resides
2089 */

2090    public long getDiskFreeSpace() throws SmbException {
2091        if( getType() == TYPE_SHARE || type == TYPE_FILESYSTEM ) {
2092            Trans2QueryFSInformationResponse response;
2093            int level = Trans2QueryFSInformationResponse.SMB_INFO_ALLOCATION;
2094
2095            response = new Trans2QueryFSInformationResponse( level );
2096            sendTransaction( new Trans2QueryFSInformation( level ), response );
2097
2098            if( type == TYPE_SHARE ) {
2099                size = response.info.getCapacity();
2100                sizeExpiration = System.currentTimeMillis() + attrExpirationPeriod;
2101            }
2102
2103            return response.info.getFree();
2104        }
2105        return 0L;
2106    }
2107
2108/**
2109 * Creates a directory with the path specified by this
2110 * <code>SmbFile</code>. For this method to be successful, the target
2111 * must not already exist. This method will fail when
2112 * used with <code>smb://</code>, <code>smb://workgroup/</code>,
2113 * <code>smb://server/</code>, or <code>smb://server/share/</code> URLs
2114 * because workgroups, servers, and shares cannot be dynamically created
2115 * (although in the future it may be possible to create shares).
2116 *
2117 * @throws SmbException
2118 */

2119    public void mkdir() throws SmbException {
2120        String JavaDoc path = getUncPath0();
2121
2122        if( path.length() == 1 ) {
2123            throw new SmbException( "Invalid operation for workgroups, servers, or shares" );
2124        }
2125
2126        /*
2127         * Create Directory Request / Response
2128         */

2129
2130        if( DebugFile.trace )
2131            DebugFile.writeln( "mkdir: " + path );
2132
2133        send( new SmbComCreateDirectory( path ), blank_resp() );
2134
2135        attrExpiration = sizeExpiration = 0;
2136    }
2137
2138/**
2139 * Creates a directory with the path specified by this <tt>SmbFile</tt>
2140 * and any parent directories that do not exist. This method will fail
2141 * when used with <code>smb://</code>, <code>smb://workgroup/</code>,
2142 * <code>smb://server/</code>, or <code>smb://server/share/</code> URLs
2143 * because workgroups, servers, and shares cannot be dynamically created
2144 * (although in the future it may be possible to create shares).
2145 *
2146 * @throws SmbException
2147 */

2148    public void mkdirs() throws SmbException {
2149        SmbFile parent;
2150
2151        try {
2152            parent = new SmbFile( new URL JavaDoc( null, getParent(), Handler.SMB_HANDLER ));
2153        } catch( IOException JavaDoc ioe ) {
2154            return;
2155        }
2156        if( parent.exists() == false ) {
2157            parent.mkdirs();
2158        }
2159        mkdir();
2160    }
2161
2162/**
2163 * Create a new file but fail if it already exists. The check for
2164 * existance of the file and it's creation are an atomic operation with
2165 * respect to other filesystem activities.
2166 */

2167    public void createNewFile() throws SmbException {
2168        if( getUncPath0().length() == 1 ) {
2169            throw new SmbException( "Invalid operation for workgroups, servers, or shares" );
2170        }
2171        close( open0( O_RDWR | O_CREAT | O_EXCL, ATTR_NORMAL, 0 ), 0L );
2172    }
2173
2174    void setPathInformation( int attrs, long ctime, long mtime ) throws SmbException {
2175        int f, options = 0;
2176
2177        if(( attrs & ATTR_DIRECTORY ) != 0 ) {
2178            options = 1;
2179        }
2180
2181        f = open0( O_RDONLY | SmbComNTCreateAndX.FILE_WRITE_ATTRIBUTES << 16, attrs, options );
2182        sendTransaction( new Trans2SetFileInformation( f, attrs, ctime, mtime ),
2183                new Trans2SetFileInformationResponse() );
2184        close( f, 0L );
2185
2186        attrExpiration = 0;
2187    }
2188
2189/**
2190 * Set the create time of the file. The time is specified as milliseconds
2191 * from Jan 1, 1970 which is the same as that which is returned by the
2192 * <tt>createTime()</tt> method.
2193 * <p/>
2194 * This method does not apply to workgroups, servers, or shares.
2195 *
2196 * @param time the create time as milliseconds since Jan 1, 1970
2197 */

2198    public void setCreateTime( long time ) throws SmbException {
2199        if( getUncPath0().length() == 1 ) {
2200            throw new SmbException( "Invalid operation for workgroups, servers, or shares" );
2201        }
2202
2203        setPathInformation( 0, time, 0L );
2204    }
2205/**
2206 * Set the last modified time of the file. The time is specified as milliseconds
2207 * from Jan 1, 1970 which is the same as that which is returned by the
2208 * <tt>lastModified()</tt>, <tt>getLastModified()</tt>, and <tt>getDate()</tt> methods.
2209 * <p/>
2210 * This method does not apply to workgroups, servers, or shares.
2211 *
2212 * @param time the last modified time as milliseconds since Jan 1, 1970
2213 */

2214    public void setLastModified( long time ) throws SmbException {
2215        if( getUncPath0().length() == 1 ) {
2216            throw new SmbException( "Invalid operation for workgroups, servers, or shares" );
2217        }
2218
2219        setPathInformation( 0, 0L, time );
2220    }
2221
2222/**
2223 * Return the attributes of this file. Attributes are represented as a
2224 * bitset that must be masked with <tt>ATTR_*</tt> constants to determine
2225 * if they are set or unset. The value returned is suitable for use with
2226 * the <tt>setAttributes()</tt> method.
2227 *
2228 * @return the <tt>ATTR_*</tt> attributes associated with this file
2229 * @throw SmbException
2230 */

2231    public int getAttributes() throws SmbException {
2232        if( getUncPath0().length() == 1 ) {
2233            return 0;
2234        }
2235        exists();
2236        return attributes & ATTR_GET_MASK;
2237    }
2238
2239/**
2240 * Set the attributes of this file. Attributes are composed into a
2241 * bitset by bitwise ORing the <tt>ATTR_*</tt> constants. Setting the
2242 * value returned by <tt>getAttributes</tt> will result in both files
2243 * having the same attributes.
2244 * @throw SmbException
2245 */

2246    public void setAttributes( int attrs ) throws SmbException {
2247        if( getUncPath0().length() == 1 ) {
2248            throw new SmbException( "Invalid operation for workgroups, servers, or shares" );
2249        }
2250
2251        setPathInformation( attrs & ATTR_SET_MASK, 0L, 0L );
2252    }
2253
2254/**
2255 * Make this file read-only. This is shorthand for <tt>setAttributes(
2256 * getAttributes() | ATTR_READ_ONLY )</tt>.
2257 *
2258 * @throw SmbException
2259 */

2260    public void setReadOnly() throws SmbException {
2261        setAttributes( getAttributes() | ATTR_READONLY );
2262    }
2263
2264/**
2265 * Turn off the read-only attribute of this file. This is shorthand for
2266 * <tt>setAttributes( getAttributes() & ~ATTR_READONLY )</tt>.
2267 *
2268 * @throw SmbException
2269 */

2270    public void setReadWrite() throws SmbException {
2271        setAttributes( getAttributes() & ~ATTR_READONLY );
2272    }
2273
2274/**
2275 * Returns a {@link java.net.URL} for this <code>SmbFile</code>. The
2276 * <code>URL</code> may be used as any other <code>URL</code> might to
2277 * access an SMB resource. Currently only retrieving data and information
2278 * is supported (i.e. no <tt>doOutput</tt>).
2279 *
2280 * @depricated Use getURL() instead
2281 * @return A new <code>{@link java.net.URL}</code> for this <code>SmbFile</code>
2282 * @throw MalformedURLException
2283 */

2284    public URL JavaDoc toURL() throws MalformedURLException JavaDoc {
2285        return url;
2286    }
2287
2288/**
2289 * Computes a hashCode for this file based on the URL string and IP
2290 * address if the server. The hashing function uses the hashcode of the
2291 * server address, the canonical representation of the URL, and does not
2292 * compare authentication information. In essance, two
2293 * <code>SmbFile</code> objects that refer to
2294 * the same file should generate the same hashcode provided it is possible
2295 * to make such a determination.
2296 *
2297 * @return A hashcode for this abstract file
2298 * @throw SmbException
2299 */

2300
2301    public int hashCode() {
2302        int hash;
2303        try {
2304            hash = getAddress().hashCode();
2305        } catch( UnknownHostException JavaDoc uhe ) {
2306            hash = getServer().toUpperCase().hashCode();
2307        }
2308        getUncPath0();
2309        return hash + canon.toUpperCase().hashCode();
2310    }
2311
2312/**
2313 * Tests to see if two <code>SmbFile</code> objects are equal. Two
2314 * SmbFile objects are equal when they reference the same SMB
2315 * resource. More specifically, two <code>SmbFile</code> objects are
2316 * equals if their server IP addresses are equal and the canonicalized
2317 * representation of their URLs, minus authentication parameters, are
2318 * case insensitivly and lexographically equal.
2319 * <p/>
2320 * For example, assuming the server <code>angus</code> resolves to the
2321 * <code>192.168.1.15</code> IP address, the below URLs would result in
2322 * <code>SmbFile</code>s that are equal.
2323 *
2324 * <p><blockquote><pre>
2325 * smb://192.168.1.15/share/DIR/foo.txt
2326 * smb://angus/share/data/../dir/foo.txt
2327 * </pre></blockquote>
2328 *
2329 * @param obj Another <code>SmbFile</code> object to compare for equality
2330 * @return <code>true</code> if the two objects refer to the same SMB resource
2331 * and <code>false</code> otherwise
2332 * @throw SmbException
2333 */

2334
2335    public boolean equals( Object JavaDoc obj ) {
2336        return obj instanceof SmbFile && obj.hashCode() == hashCode();
2337    }
2338
2339/**
2340 * Returns the string representation of this SmbFile object. This will
2341 * be the same as the URL used to construct this <code>SmbFile</code>.
2342 * This method will return the same value
2343 * as <code>getPath</code>.
2344 *
2345 * @return The original URL representation of this SMB resource
2346 * @throw SmbException
2347 */

2348
2349    public String JavaDoc toString() {
2350        return url.toString();
2351    }
2352
2353/* URLConnection implementation */
2354/**
2355 * This URLConnection method just returns the result of <tt>length()</tt>.
2356 *
2357 * @return the length of this file or 0 if it refers to a directory
2358 */

2359
2360    public int getContentLength() {
2361        try {
2362            return (int)(length() & 0xFFFFFFFFL);
2363        } catch( SmbException se ) {
2364        }
2365        return 0;
2366    }
2367
2368/**
2369 * This URLConnection method just returns the result of <tt>lastModified</tt>.
2370 *
2371 * @return the last modified data as milliseconds since Jan 1, 1970
2372 */

2373    public long getDate() {
2374        try {
2375            return lastModified();
2376        } catch( SmbException se ) {
2377        }
2378        return 0L;
2379    }
2380
2381/**
2382 * This URLConnection method just returns the result of <tt>lastModified</tt>.
2383 *
2384 * @return the last modified data as milliseconds since Jan 1, 1970
2385 */

2386    public long getLastModified() {
2387        try {
2388            return lastModified();
2389        } catch( SmbException se ) {
2390        }
2391        return 0L;
2392    }
2393
2394/**
2395 * This URLConnection method just returns a new <tt>SmbFileInputStream</tt> created with this file.
2396 *
2397 * @throw IOException thrown by <tt>SmbFileInputStream</tt> constructor
2398 */

2399    public InputStream JavaDoc getInputStream() throws IOException JavaDoc {
2400        return new SmbFileInputStream( this );
2401    }
2402}
2403
Popular Tags