1 /* jcifs smb client library in Java
2  * Copyright (C) 2000  "Michael B. Allen" <jcifs at samba dot org>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  */
18 
19 package jcifs.smb;
20 
21 import java.net.URLConnection;
22 import java.net.URL;
23 import java.net.MalformedURLException;
24 import java.net.UnknownHostException;
25 import java.io.IOException;
26 import java.io.InputStream;
27 import java.io.OutputStream;
28 import java.util.ArrayList;
29 import java.util.HashMap;
30 import java.util.Iterator;
31 import java.security.Principal;
32 import jcifs.Config;
33 import jcifs.util.LogStream;
34 import jcifs.UniAddress;
35 import jcifs.netbios.NbtAddress;
36 import jcifs.dcerpc.*;
37 import jcifs.dcerpc.msrpc.*;
38 
39 import java.util.Date;
40 
41 /**
42  * This class represents a resource on an SMB network. Mainly these
43  * resources are files and directories however an <code>SmbFile</code>
44  * may also refer to servers and workgroups. If the resource is a file or
45  * directory the methods of <code>SmbFile</code> follow the behavior of
46  * the well known {@link java.io.File} class. One fundamental difference
47  * is the usage of a URL scheme [1] to specify the target file or
48  * directory. SmbFile URLs have the following syntax:
49  *
50  * <blockquote><pre>
51  *     smb://[[[domain;]username[:password]@]server[:port]/[[share/[dir/]file]]][?param=value[param2=value2[...]]]
52  * </pre></blockquote>
53  *
54  * This example:
55  *
56  * <blockquote><pre>
57  *     smb://storage15/public/foo.txt
58  * </pre></blockquote>
59  *
60  * would reference the file <code>foo.txt</code> in the share
61  * <code>public</code> on the server <code>storage15</code>. In addition
62  * to referencing files and directories, jCIFS can also address servers,
63  * and workgroups.
64  * <p>
65  * <font color="#800000"><i>Important: all SMB URLs that represent
66  * workgroups, servers, shares, or directories require a trailing slash '/'.
67  * </i></font>
68  * <p>
69  * When using the <tt>java.net.URL</tt> class with
70  * 'smb://' URLs it is necessary to first call the static
71  * <tt>jcifs.Config.registerSmbURLHandler();</tt> method. This is required
72  * to register the SMB protocol handler.
73  * <p>
74  * The userinfo component of the SMB URL (<tt>domain;user:pass</tt>) must
75  * be URL encoded if it contains reserved characters. According to RFC 2396
76  * these characters are non US-ASCII characters and most meta characters
77  * however jCIFS will work correctly with anything but '@' which is used
78  * to delimit the userinfo component from the server and '%' which is the
79  * URL escape character itself.
80  * <p>
81  * The server
82  * component may a traditional NetBIOS name, a DNS name, or IP
83  * address. These name resolution mechanisms and their resolution order
84  * can be changed (See <a href="../../../resolver.html">Setting Name
85  * Resolution Properties</a>). The servername and path components are
86  * not case sensitive but the domain, username, and password components
87  * are. It is also likely that properties must be specified for jcifs
88  * to function (See <a href="../../overview-summary.html#scp">Setting
89  * JCIFS Properties</a>). Here are some examples of SMB URLs with brief
90  * descriptions of what they do:
91  *
92  * <p>[1] This URL scheme is based largely on the <i>SMB
93  * Filesharing URL Scheme</i> IETF draft.
94  *
95  * <p><table border="1" cellpadding="3" cellspacing="0" width="100%">
96  * <tr bgcolor="#ccccff">
97  * <td colspan="2"><b>SMB URL Examples</b></td>
98  * <tr><td width="20%"><b>URL</b></td><td><b>Description</b></td></tr>
99  *
100  * <tr><td width="20%"><code>smb://users-nyc;miallen:mypass@angus/tmp/</code></td><td>
101  * This URL references a share called <code>tmp</code> on the server
102  * <code>angus</code> as user <code>miallen</code> who's password is
103  * <code>mypass</code>.
104  * </td></tr>
105  *
106  * <tr><td width="20%">
107  * <code>smb://Administrator:P%40ss@msmith1/c/WINDOWS/Desktop/foo.txt</code></td><td>
108  * A relativly sophisticated example that references a file
109  * <code>msmith1</code>'s desktop as user <code>Administrator</code>. Notice the '@' is URL encoded with the '%40' hexcode escape.
110  * </td></tr>
111  *
112  * <tr><td width="20%"><code>smb://angus/</code></td><td>
113  * This references only a server. The behavior of some methods is different
114  * in this context(e.g. you cannot <code>delete</code> a server) however
115  * as you might expect the <code>list</code> method will list the available
116  * shares on this server.
117  * </td></tr>
118  *
119  * <tr><td width="20%"><code>smb://myworkgroup/</code></td><td>
120  * This syntactically is identical to the above example. However if
121  * <code>myworkgroup</code> happends to be a workgroup(which is indeed
122  * suggested by the name) the <code>list</code> method will return
123  * a list of servers that have registered themselves as members of
124  * <code>myworkgroup</code>.
125  * </td></tr>
126  *
127  * <tr><td width="20%"><code>smb://</code></td><td>
128  * Just as <code>smb://server/</code> lists shares and
129  * <code>smb://workgroup/</code> lists servers, the <code>smb://</code>
130  * URL lists all available workgroups on a netbios LAN. Again,
131  * in this context many methods are not valid and return default
132  * values(e.g. <code>isHidden</code> will always return false).
133  * </td></tr>
134  *
135  * <tr><td width="20%"><code>smb://angus.foo.net/d/jcifs/pipes.doc</code></td><td>
136  * The server name may also be a DNS name as it is in this example. See
137  * <a href="../../../resolver.html">Setting Name Resolution Properties</a>
138  * for details.
139  * </td></tr>
140  *
141  * <tr><td width="20%"><code>smb://192.168.1.15/ADMIN$/</code></td><td>
142  * The server name may also be an IP address. See <a
143  * href="../../../resolver.html">Setting Name Resolution Properties</a>
144  * for details.
145  * </td></tr>
146  *
147  * <tr><td width="20%">
148  * <code>smb://domain;username:password@server/share/path/to/file.txt</code></td><td>
149  * A prototypical example that uses all the fields.
150  * </td></tr>
151  *
152  * <tr><td width="20%"><code>smb://myworkgroup/angus/ &lt;-- ILLEGAL </code></td><td>
153  * Despite the hierarchial relationship between workgroups, servers, and
154  * filesystems this example is not valid.
155  * </td></tr>
156  *
157  * <tr><td width="20%">
158  * <code>smb://server/share/path/to/dir &lt;-- ILLEGAL </code></td><td>
159  * URLs that represent workgroups, servers, shares, or directories require a trailing slash '/'.
160  * </td></tr>
161  *
162  * <tr><td width="20%">
163  * <code>smb://MYGROUP/?SERVER=192.168.10.15</code></td><td>
164  * SMB URLs support some query string parameters. In this example
165  * the <code>SERVER</code> parameter is used to override the
166  * server name service lookup to contact the server 192.168.10.15
167  * (presumably known to be a master
168  * browser) for the server list in workgroup <code>MYGROUP</code>.
169  * </td></tr>
170  *
171  * </table>
172  *
173  * <p>A second constructor argument may be specified to augment the URL
174  * for better programmatic control when processing many files under
175  * a common base. This is slightly different from the corresponding
176  * <code>java.io.File</code> usage; a '/' at the beginning of the second
177  * parameter will still use the server component of the first parameter. The
178  * examples below illustrate the resulting URLs when this second contructor
179  * argument is used.
180  *
181  * <p><table border="1" cellpadding="3" cellspacing="0" width="100%">
182  * <tr bgcolor="#ccccff">
183  * <td colspan="3">
184  * <b>Examples Of SMB URLs When Augmented With A Second Constructor Parameter</b></td>
185  * <tr><td width="20%">
186  * <b>First Parameter</b></td><td><b>Second Parameter</b></td><td><b>Result</b></td></tr>
187  *
188  * <tr><td width="20%"><code>
189  *  smb://host/share/a/b/
190  * </code></td><td width="20%"><code>
191  *  c/d/
192  * </code></td><td><code>
193  *  smb://host/share/a/b/c/d/
194  * </code></td></tr>
195  *
196  * <tr><td width="20%"><code>
197  *  smb://host/share/foo/bar/
198  * </code></td><td width="20%"><code>
199  *  /share2/zig/zag
200  * </code></td><td><code>
201  *  smb://host/share2/zig/zag
202  * </code></td></tr>
203  *
204  * <tr><td width="20%"><code>
205  *  smb://host/share/foo/bar/
206  * </code></td><td width="20%"><code>
207  *  ../zip/
208  * </code></td><td><code>
209  *  smb://host/share/foo/zip/
210  * </code></td></tr>
211  *
212  * <tr><td width="20%"><code>
213  *  smb://host/share/zig/zag
214  * </code></td><td width="20%"><code>
215  *  smb://foo/bar/
216  * </code></td><td><code>
217  *  smb://foo/bar/
218  * </code></td></tr>
219  *
220  * <tr><td width="20%"><code>
221  *  smb://host/share/foo/
222  * </code></td><td width="20%"><code>
223  *  ../.././.././../foo/
224  * </code></td><td><code>
225  *  smb://host/foo/
226  * </code></td></tr>
227  *
228  * <tr><td width="20%"><code>
229  *  smb://host/share/zig/zag
230  * </code></td><td width="20%"><code>
231  *  /
232  * </code></td><td><code>
233  *  smb://host/
234  * </code></td></tr>
235  *
236  * <tr><td width="20%"><code>
237  *  smb://server/
238  * </code></td><td width="20%"><code>
239  *  ../
240  * </code></td><td><code>
241  *  smb://server/
242  * </code></td></tr>
243  *
244  * <tr><td width="20%"><code>
245  *  smb://
246  * </code></td><td width="20%"><code>
247  *  myworkgroup/
248  * </code></td><td><code>
249  *  smb://myworkgroup/
250  * </code></td></tr>
251  *
252  * <tr><td width="20%"><code>
253  *  smb://myworkgroup/
254  * </code></td><td width="20%"><code>
255  *  angus/
256  * </code></td><td><code>
257  *  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'.)
258  * </code></td></tr>
259  *
260  * </table>
261  *
262  * <p>Instances of the <code>SmbFile</code> class are immutable; that is,
263  * once created, the abstract pathname represented by an SmbFile object
264  * will never change.
265  *
266  * @see       java.io.File
267  */
268 
269 public class SmbFile extends URLConnection implements SmbConstants {
270 
271     static final int O_RDONLY = 0x01;
272     static final int O_WRONLY = 0x02;
273     static final int O_RDWR   = 0x03;
274     static final int O_APPEND = 0x04;
275 
276     // Open Function Encoding
277     // create if the file does not exist
278     static final int O_CREAT  = 0x0010;
279     // fail if the file exists
280     static final int O_EXCL   = 0x0020;
281     // truncate if the file exists
282     static final int O_TRUNC  = 0x0040;
283 
284     // share access
285 /**
286  * When specified as the <tt>shareAccess</tt> constructor parameter,
287  * other SMB clients (including other threads making calls into jCIFS)
288  * will not be permitted to access the target file and will receive "The
289  * file is being accessed by another process" message.
290  */
291     public static final int FILE_NO_SHARE     = 0x00;
292 /**
293  * When specified as the <tt>shareAccess</tt> constructor parameter,
294  * other SMB clients will be permitted to read from the target file while
295  * this file is open. This constant may be logically OR'd with other share
296  * access flags.
297  */
298     public static final int FILE_SHARE_READ   = 0x01;
299 /**
300  * When specified as the <tt>shareAccess</tt> constructor parameter,
301  * other SMB clients will be permitted to write to the target file while
302  * this file is open. This constant may be logically OR'd with other share
303  * access flags.
304  */
305     public static final int FILE_SHARE_WRITE  = 0x02;
306 /**
307  * When specified as the <tt>shareAccess</tt> constructor parameter,
308  * other SMB clients will be permitted to delete the target file while
309  * this file is open. This constant may be logically OR'd with other share
310  * access flags.
311  */
312     public static final int FILE_SHARE_DELETE = 0x04;
313 
314     // file attribute encoding
315 /**
316  * A file with this bit on as returned by <tt>getAttributes()</tt> or set
317  * with <tt>setAttributes()</tt> will be read-only
318  */
319     public static final int ATTR_READONLY   = 0x01;
320 /**
321  * A file with this bit on as returned by <tt>getAttributes()</tt> or set
322  * with <tt>setAttributes()</tt> will be hidden
323  */
324     public static final int ATTR_HIDDEN     = 0x02;
325 /**
326  * A file with this bit on as returned by <tt>getAttributes()</tt> or set
327  * with <tt>setAttributes()</tt> will be a system file
328  */
329     public static final int ATTR_SYSTEM     = 0x04;
330 /**
331  * A file with this bit on as returned by <tt>getAttributes()</tt> is
332  * a volume
333  */
334     public static final int ATTR_VOLUME     = 0x08;
335 /**
336  * A file with this bit on as returned by <tt>getAttributes()</tt> is
337  * a directory
338  */
339     public static final int ATTR_DIRECTORY  = 0x10;
340 /**
341  * A file with this bit on as returned by <tt>getAttributes()</tt> or set
342  * with <tt>setAttributes()</tt> is an archived file
343  */
344     public static final int ATTR_ARCHIVE    = 0x20;
345 
346     // extended file attribute encoding(others same as above)
347     static final int ATTR_COMPRESSED       = 0x800;
348     static final int ATTR_NORMAL           = 0x080;
349     static final int ATTR_TEMPORARY        = 0x100;
350 
351     static final int ATTR_GET_MASK = 0x7FFF; /* orig 0x7fff */
352     static final int ATTR_SET_MASK = 0x30A7; /* orig 0x0027 */
353 
354     static final int DEFAULT_ATTR_EXPIRATION_PERIOD = 5000;
355 
356     static final int HASH_DOT     = ".".hashCode();
357     static final int HASH_DOT_DOT = "..".hashCode();
358 
359     static LogStream log = LogStream.getInstance();
360     static long attrExpirationPeriod;
361     static boolean ignoreCopyToException;
362 
363     static {
364 
365         try {
366             Class.forName( "jcifs.Config" );
367         } catch( ClassNotFoundException cnfe ) {
368             cnfe.printStackTrace();
369         }
370         attrExpirationPeriod = Config.getLong( "jcifs.smb.client.attrExpirationPeriod", DEFAULT_ATTR_EXPIRATION_PERIOD );
371         ignoreCopyToException = Config.getBoolean( "jcifs.smb.client.ignoreCopyToException", true );
372         dfs = new Dfs();
373     }
374 
375     /**
376      * Returned by {@link #getType()} if the resource this <tt>SmbFile</tt>
377      * represents is a regular file or directory.
378      */
379     public static final int TYPE_FILESYSTEM = 0x01;
380     /**
381      * Returned by {@link #getType()} if the resource this <tt>SmbFile</tt>
382      * represents is a workgroup.
383      */
384     public static final int TYPE_WORKGROUP = 0x02;
385     /**
386      * Returned by {@link #getType()} if the resource this <tt>SmbFile</tt>
387      * represents is a server.
388      */
389     public static final int TYPE_SERVER = 0x04;
390     /**
391      * Returned by {@link #getType()} if the resource this <tt>SmbFile</tt>
392      * represents is a share.
393      */
394     public static final int TYPE_SHARE = 0x08;
395     /**
396      * Returned by {@link #getType()} if the resource this <tt>SmbFile</tt>
397      * represents is a named pipe.
398      */
399     public static final int TYPE_NAMED_PIPE = 0x10;
400     /**
401      * Returned by {@link #getType()} if the resource this <tt>SmbFile</tt>
402      * represents is a printer.
403      */
404     public static final int TYPE_PRINTER = 0x20;
405     /**
406      * Returned by {@link #getType()} if the resource this <tt>SmbFile</tt>
407      * represents is a communications device.
408      */
409     public static final int TYPE_COMM = 0x40;
410 
411 
412     private String canon;            // Initially null; set by getUncPath; dir must end with '/'
413     private String share;            // Can be null
414     private long createTime;
415     private long lastModified;
416     private int attributes;
417     private long attrExpiration;
418     private long size;
419     private long sizeExpiration;
420     private boolean isExists;
421     private int shareAccess = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
422     private SmbComBlankResponse blank_resp = null;
423     private DfsReferral dfsReferral = null;  // For getDfsPath() and getServerWithDfs()
424 
425     protected static Dfs dfs;
426 
427     NtlmPasswordAuthentication auth; // Cannot be null
428     SmbTree tree = null;             // Initially null
429     String unc;                      // Initially null; set by getUncPath; never ends with '/'
430     int fid;                         // Initially 0; set by open()
431     int type;
432     boolean opened;
433     int tree_num;
434 
435 /**
436  * Constructs an SmbFile representing a resource on an SMB network such as
437  * a file or directory. See the description and examples of smb URLs above.
438  *
439  * @param   url A URL string
440  * @throws  MalformedURLException
441  *          If the <code>parent</code> and <code>child</code> parameters
442  *          do not follow the prescribed syntax
443  */
444 
SmbFile( String url )445     public SmbFile( String url ) throws MalformedURLException {
446         this( new URL( null, url, Handler.SMB_HANDLER ));
447     }
448 
449 /**
450  * Constructs an SmbFile representing a resource on an SMB network such
451  * as a file or directory. The second parameter is a relative path from
452  * the <code>parent SmbFile</code>. See the description above for examples
453  * of using the second <code>name</code> parameter.
454  *
455  * @param   context A base <code>SmbFile</code>
456  * @param   name A path string relative to the <code>parent</code> paremeter
457  * @throws  MalformedURLException
458  *          If the <code>parent</code> and <code>child</code> parameters
459  *          do not follow the prescribed syntax
460  * @throws  UnknownHostException
461  *          If the server or workgroup of the <tt>context</tt> file cannot be determined
462  */
463 
SmbFile( SmbFile context, String name )464     public SmbFile( SmbFile context, String name )
465                 throws MalformedURLException, UnknownHostException {
466         this( context.isWorkgroup0() ?
467             new URL( null, "smb://" + name, Handler.SMB_HANDLER ) :
468             new URL( context.url, name, Handler.SMB_HANDLER ), context.auth );
469     }
470 
471 /**
472  * Constructs an SmbFile representing a resource on an SMB network such
473  * as a file or directory. The second parameter is a relative path from
474  * the <code>parent</code>. See the description above for examples of
475  * using the second <code>chile</code> parameter.
476  *
477  * @param   context A URL string
478  * @param   name A path string relative to the <code>context</code> paremeter
479  * @throws  MalformedURLException
480  *          If the <code>context</code> and <code>name</code> parameters
481  *          do not follow the prescribed syntax
482  */
483 
SmbFile( String context, String name )484     public SmbFile( String context, String name ) throws MalformedURLException {
485         this( new URL( new URL( null, context, Handler.SMB_HANDLER ),
486                 name, Handler.SMB_HANDLER ));
487     }
488 
489 /**
490  * Constructs an SmbFile representing a resource on an SMB network such
491  * as a file or directory.
492  *
493  * @param   url A URL string
494  * @param   auth The credentials the client should use for authentication
495  * @throws  MalformedURLException
496  *          If the <code>url</code> parameter does not follow the prescribed syntax
497  */
SmbFile( String url, NtlmPasswordAuthentication auth )498     public SmbFile( String url, NtlmPasswordAuthentication auth )
499                     throws MalformedURLException {
500         this( new URL( null, url, Handler.SMB_HANDLER ), auth );
501     }
502 /**
503  * Constructs an SmbFile representing a file on an SMB network. The
504  * <tt>shareAccess</tt> parameter controls what permissions other
505  * clients have when trying to access the same file while this instance
506  * is still open. This value is either <tt>FILE_NO_SHARE</tt> or any
507  * combination of <tt>FILE_SHARE_READ</tt>, <tt>FILE_SHARE_WRITE</tt>,
508  * and <tt>FILE_SHARE_DELETE</tt> logically OR'd together.
509  *
510  * @param   url A URL string
511  * @param   auth The credentials the client should use for authentication
512  * @param   shareAccess Specifies what access other clients have while this file is open.
513  * @throws  MalformedURLException
514  *          If the <code>url</code> parameter does not follow the prescribed syntax
515  */
SmbFile( String url, NtlmPasswordAuthentication auth, int shareAccess )516     public SmbFile( String url, NtlmPasswordAuthentication auth, int shareAccess )
517                     throws MalformedURLException {
518         this( new URL( null, url, Handler.SMB_HANDLER ), auth );
519         if ((shareAccess & ~(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE)) != 0) {
520             throw new RuntimeException( "Illegal shareAccess parameter" );
521         }
522         this.shareAccess = shareAccess;
523     }
524 /**
525  * Constructs an SmbFile representing a resource on an SMB network such
526  * as a file or directory. The second parameter is a relative path from
527  * the <code>context</code>. See the description above for examples of
528  * using the second <code>name</code> parameter.
529  *
530  * @param   context A URL string
531  * @param   name A path string relative to the <code>context</code> paremeter
532  * @param   auth The credentials the client should use for authentication
533  * @throws  MalformedURLException
534  *          If the <code>context</code> and <code>name</code> parameters
535  *          do not follow the prescribed syntax
536  */
SmbFile( String context, String name, NtlmPasswordAuthentication auth )537     public SmbFile( String context, String name, NtlmPasswordAuthentication auth )
538                     throws MalformedURLException {
539         this( new URL( new URL( null, context, Handler.SMB_HANDLER ), name, Handler.SMB_HANDLER ), auth );
540     }
541 /**
542  * Constructs an SmbFile representing a resource on an SMB network such
543  * as a file or directory. The second parameter is a relative path from
544  * the <code>context</code>. See the description above for examples of
545  * using the second <code>name</code> parameter. The <tt>shareAccess</tt>
546  * parameter controls what permissions other clients have when trying
547  * to access the same file while this instance is still open. This
548  * value is either <tt>FILE_NO_SHARE</tt> or any combination
549  * of <tt>FILE_SHARE_READ</tt>, <tt>FILE_SHARE_WRITE</tt>, and
550  * <tt>FILE_SHARE_DELETE</tt> logically OR'd together.
551  *
552  * @param   context A URL string
553  * @param   name A path string relative to the <code>context</code> paremeter
554  * @param   auth The credentials the client should use for authentication
555  * @param   shareAccess Specifies what access other clients have while this file is open.
556  * @throws  MalformedURLException
557  *          If the <code>context</code> and <code>name</code> parameters
558  *          do not follow the prescribed syntax
559  */
SmbFile( String context, String name, NtlmPasswordAuthentication auth, int shareAccess )560     public SmbFile( String context, String name, NtlmPasswordAuthentication auth, int shareAccess )
561                     throws MalformedURLException {
562         this( new URL( new URL( null, context, Handler.SMB_HANDLER ), name, Handler.SMB_HANDLER ), auth );
563         if ((shareAccess & ~(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE)) != 0) {
564             throw new RuntimeException( "Illegal shareAccess parameter" );
565         }
566         this.shareAccess = shareAccess;
567     }
568 /**
569  * Constructs an SmbFile representing a resource on an SMB network such
570  * as a file or directory. The second parameter is a relative path from
571  * the <code>context</code>. See the description above for examples of
572  * using the second <code>name</code> parameter. The <tt>shareAccess</tt>
573  * parameter controls what permissions other clients have when trying
574  * to access the same file while this instance is still open. This
575  * value is either <tt>FILE_NO_SHARE</tt> or any combination
576  * of <tt>FILE_SHARE_READ</tt>, <tt>FILE_SHARE_WRITE</tt>, and
577  * <tt>FILE_SHARE_DELETE</tt> logically OR'd together.
578  *
579  * @param   context A base <code>SmbFile</code>
580  * @param   name A path string relative to the <code>context</code> file path
581  * @param   shareAccess Specifies what access other clients have while this file is open.
582  * @throws  MalformedURLException
583  *          If the <code>context</code> and <code>name</code> parameters
584  *          do not follow the prescribed syntax
585  */
SmbFile( SmbFile context, String name, int shareAccess )586     public SmbFile( SmbFile context, String name, int shareAccess )
587                     throws MalformedURLException, UnknownHostException {
588         this( context.isWorkgroup0() ?
589             new URL( null, "smb://" + name, Handler.SMB_HANDLER ) :
590             new URL( context.url, name, Handler.SMB_HANDLER ), context.auth );
591         if ((shareAccess & ~(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE)) != 0) {
592             throw new RuntimeException( "Illegal shareAccess parameter" );
593         }
594         this.shareAccess = shareAccess;
595     }
596 /**
597  * Constructs an SmbFile representing a resource on an SMB network such
598  * as a file or directory from a <tt>URL</tt> object.
599  *
600  * @param   url The URL of the target resource
601  */
SmbFile( URL url )602     public SmbFile( URL url ) {
603         this( url, new NtlmPasswordAuthentication( url.getUserInfo() ));
604     }
605 /**
606  * Constructs an SmbFile representing a resource on an SMB network such
607  * as a file or directory from a <tt>URL</tt> object and an
608  * <tt>NtlmPasswordAuthentication</tt> object.
609  *
610  * @param   url The URL of the target resource
611  * @param   auth The credentials the client should use for authentication
612  */
SmbFile( URL url, NtlmPasswordAuthentication auth )613     public SmbFile( URL url, NtlmPasswordAuthentication auth ) {
614         super( url );
615         this.auth = auth == null ? new NtlmPasswordAuthentication( url.getUserInfo() ) : auth;
616 
617         getUncPath0();
618     }
SmbFile( SmbFile context, String name, int type, int attributes, long createTime, long lastModified, long size )619     SmbFile( SmbFile context, String name, int type,
620                 int attributes, long createTime, long lastModified, long size )
621                 throws MalformedURLException, UnknownHostException {
622         this( context.isWorkgroup0() ?
623             new URL( null, "smb://" + name + "/", Handler.SMB_HANDLER ) :
624             new URL( context.url, name + (( attributes & ATTR_DIRECTORY ) > 0 ? "/" : "" )));
625 
626         /* why was this removed before? DFS? copyTo? Am I going around in circles? */
627         auth = context.auth;
628 
629 
630         if( context.share != null ) {
631             this.tree = context.tree;
632             this.dfsReferral = context.dfsReferral;
633         }
634         int last = name.length() - 1;
635         if( name.charAt( last ) == '/' ) {
636             name = name.substring( 0, last );
637         }
638         if( context.share == null ) {
639             this.unc = "\\";
640         } else if( context.unc.equals( "\\" )) {
641             this.unc = '\\' + name;
642         } else {
643             this.unc = context.unc + '\\' + name;
644         }
645     /* why? am I going around in circles?
646      *  this.type = type == TYPE_WORKGROUP ? 0 : type;
647      */
648         this.type = type;
649         this.attributes = attributes;
650         this.createTime = createTime;
651         this.lastModified = lastModified;
652         this.size = size;
653         isExists = true;
654 
655         attrExpiration = sizeExpiration =
656                 System.currentTimeMillis() + attrExpirationPeriod;
657     }
658 
blank_resp()659     private SmbComBlankResponse blank_resp() {
660         if( blank_resp == null ) {
661             blank_resp = new SmbComBlankResponse();
662         }
663         return blank_resp;
664     }
resolveDfs(ServerMessageBlock request)665     void resolveDfs(ServerMessageBlock request) throws SmbException {
666         if (request instanceof SmbComClose)
667             return;
668 
669         connect0();
670 
671         DfsReferral dr = dfs.resolve(
672                     tree.session.transport.tconHostName,
673                     tree.share,
674                     unc,
675                     auth);
676         if (dr != null) {
677             String service = null;
678 
679             if (request != null) {
680                 switch( request.command ) {
681                     case ServerMessageBlock.SMB_COM_TRANSACTION:
682                     case ServerMessageBlock.SMB_COM_TRANSACTION2:
683                         switch( ((SmbComTransaction)request).subCommand & 0xFF ) {
684                             case SmbComTransaction.TRANS2_GET_DFS_REFERRAL:
685                                 break;
686                             default:
687                                 service = "A:";
688                         }
689                         break;
690                     default:
691                         service = "A:";
692                 }
693             }
694 
695             DfsReferral start = dr;
696             SmbException se = null;
697 
698             do {
699                 try {
700                     if (log.level >= 2)
701                         log.println("DFS redirect: " + dr);
702 
703                     UniAddress addr = UniAddress.getByName(dr.server);
704                     SmbTransport trans = SmbTransport.getSmbTransport(addr, url.getPort());
705                     /* This is a key point. This is where we set the "tree" of this file which
706                      * is like changing the rug out from underneath our feet.
707                      */
708 /* Technically we should also try to authenticate here but that means doing the session setup and tree connect separately. For now a simple connect will at least tell us if the host is alive. That should be sufficient for 99% of the cases. We can revisit this again for 2.0.
709  */
710                     trans.connect();
711                     tree = trans.getSmbSession( auth ).getSmbTree( dr.share, service );
712 
713                     if (dr != start && dr.key != null) {
714                         dr.map.put(dr.key, dr);
715                     }
716 
717                     se = null;
718 
719                     break;
720                 } catch (IOException ioe) {
721                     if (ioe instanceof SmbException) {
722                         se = (SmbException)ioe;
723                     } else {
724                         se = new SmbException(dr.server, ioe);
725                     }
726                 }
727 
728                 dr = dr.next;
729             } while (dr != start);
730 
731             if (se != null)
732                 throw se;
733 
734             if (log.level >= 3)
735                 log.println( dr );
736 
737             dfsReferral = dr;
738             if (dr.pathConsumed < 0) {
739                 dr.pathConsumed = 0;
740             } else if (dr.pathConsumed > unc.length()) {
741                 dr.pathConsumed = unc.length();
742             }
743             String dunc = unc.substring(dr.pathConsumed);
744             if (dunc.equals(""))
745                 dunc = "\\";
746             if (!dr.path.equals(""))
747                 dunc = "\\" + dr.path + dunc;
748 
749             unc = dunc;
750             if (request != null &&
751                         request.path != null &&
752                         request.path.endsWith("\\") &&
753                         dunc.endsWith("\\") == false) {
754                 dunc += "\\";
755             }
756             if (request != null) {
757                 request.path = dunc;
758                 request.flags2 |= ServerMessageBlock.FLAGS2_RESOLVE_PATHS_IN_DFS;
759             }
760         } else if (tree.inDomainDfs &&
761                         !(request instanceof NtTransQuerySecurityDesc) &&
762                         !(request instanceof SmbComClose) &&
763                         !(request instanceof SmbComFindClose2)) {
764             throw new SmbException(NtStatus.NT_STATUS_NOT_FOUND, false);
765         } else {
766             if (request != null)
767                 request.flags2 &= ~ServerMessageBlock.FLAGS2_RESOLVE_PATHS_IN_DFS;
768         }
769     }
send( ServerMessageBlock request, ServerMessageBlock response )770     void send( ServerMessageBlock request,
771                     ServerMessageBlock response ) throws SmbException {
772         for( ;; ) {
773             resolveDfs(request);
774             try {
775                 tree.send( request, response );
776                 break;
777             } catch( DfsReferral dre ) {
778                 if( dre.resolveHashes ) {
779                     throw dre;
780                 }
781                 request.reset();
782             }
783         }
784     }
785 
queryLookup( String query, String param )786     static String queryLookup( String query, String param ) {
787         char in[] = query.toCharArray();
788         int i, ch, st, eq;
789 
790         st = eq = 0;
791         for( i = 0; i < in.length; i++) {
792             ch = in[i];
793             if( ch == '&' ) {
794                 if( eq > st ) {
795                     String p = new String( in, st, eq - st );
796                     if( p.equalsIgnoreCase( param )) {
797                         eq++;
798                         return new String( in, eq, i - eq );
799                     }
800                 }
801                 st = i + 1;
802             } else if( ch == '=' ) {
803                 eq = i;
804             }
805         }
806         if( eq > st ) {
807             String p = new String( in, st, eq - st );
808             if( p.equalsIgnoreCase( param )) {
809                 eq++;
810                 return new String( in, eq, in.length - eq );
811             }
812         }
813 
814         return null;
815     }
816 
817 UniAddress[] addresses;
818 int addressIndex;
819 
getAddress()820     UniAddress getAddress() throws UnknownHostException {
821         if (addressIndex == 0)
822             return getFirstAddress();
823         return addresses[addressIndex - 1];
824     }
getFirstAddress()825     UniAddress getFirstAddress() throws UnknownHostException {
826         addressIndex = 0;
827 
828         String host = url.getHost();
829         String path = url.getPath();
830         String query = url.getQuery();
831 
832         if( query != null ) {
833             String server = queryLookup( query, "server" );
834             if( server != null && server.length() > 0 ) {
835                 addresses = new UniAddress[1];
836                 addresses[0] = UniAddress.getByName( server );
837                 return getNextAddress();
838             }
839             String address = queryLookup(query, "address");
840             if (address != null && address.length() > 0) {
841                 byte[] ip = java.net.InetAddress.getByName(address).getAddress();
842                 addresses = new UniAddress[1];
843                 addresses[0] = new UniAddress(java.net.InetAddress.getByAddress(host, ip));
844                 return getNextAddress();
845             }
846         }
847 
848         if (host.length() == 0) {
849             try {
850                 NbtAddress addr = NbtAddress.getByName(
851                         NbtAddress.MASTER_BROWSER_NAME, 0x01, null);
852                 addresses = new UniAddress[1];
853                 addresses[0] = UniAddress.getByName( addr.getHostAddress() );
854             } catch( UnknownHostException uhe ) {
855                 NtlmPasswordAuthentication.initDefaults();
856                 if( NtlmPasswordAuthentication.DEFAULT_DOMAIN.equals( "?" )) {
857                     throw uhe;
858                 }
859                 addresses = UniAddress.getAllByName( NtlmPasswordAuthentication.DEFAULT_DOMAIN, true );
860             }
861         } else if( path.length() == 0 || path.equals( "/" )) {
862             addresses = UniAddress.getAllByName( host, true );
863         } else {
864             addresses = UniAddress.getAllByName(host, false);
865         }
866 
867         return getNextAddress();
868     }
getNextAddress()869     UniAddress getNextAddress() {
870         UniAddress addr = null;
871         if (addressIndex < addresses.length)
872             addr = addresses[addressIndex++];
873         return addr;
874     }
hasNextAddress()875     boolean hasNextAddress() {
876         return addressIndex < addresses.length;
877     }
connect0()878     void connect0() throws SmbException {
879         try {
880             connect();
881         } catch( UnknownHostException uhe ) {
882             throw new SmbException( "Failed to connect to server", uhe );
883         } catch( SmbException se ) {
884             throw se;
885         } catch( IOException ioe ) {
886             throw new SmbException( "Failed to connect to server", ioe );
887         }
888     }
doConnect()889     void doConnect() throws IOException {
890         SmbTransport trans;
891         UniAddress addr;
892 
893         addr = getAddress();
894         if (tree != null) {
895             trans = tree.session.transport;
896         } else {
897             trans = SmbTransport.getSmbTransport(addr, url.getPort());
898             tree = trans.getSmbSession(auth).getSmbTree(share, null);
899         }
900 
901         String hostName = getServerWithDfs();
902         tree.inDomainDfs = dfs.resolve(hostName, tree.share, null, auth) != null;
903         if (tree.inDomainDfs) {
904             tree.connectionState = 2;
905         }
906 
907         try {
908             if( log.level >= 3 )
909                 log.println( "doConnect: " + addr );
910 
911             tree.treeConnect(null, null);
912         } catch (SmbAuthException sae) {
913             NtlmPasswordAuthentication a;
914             SmbSession ssn;
915 
916             if (share == null) { // IPC$ - try "anonymous" credentials
917                 ssn = trans.getSmbSession(NtlmPasswordAuthentication.NULL);
918                 tree = ssn.getSmbTree(null, null);
919                 tree.treeConnect(null, null);
920             } else if ((a = NtlmAuthenticator.requestNtlmPasswordAuthentication(
921                         url.toString(), sae)) != null) {
922                 auth = a;
923                 ssn = trans.getSmbSession(auth);
924                 tree = ssn.getSmbTree(share, null);
925                 tree.inDomainDfs = dfs.resolve(hostName, tree.share, null, auth) != null;
926                 if (tree.inDomainDfs) {
927                     tree.connectionState = 2;
928                 }
929                 tree.treeConnect(null, null);
930             } else {
931                 if (log.level >= 1 && hasNextAddress())
932                     sae.printStackTrace(log);
933                 throw sae;
934             }
935         }
936     }
937 /**
938  * It is not necessary to call this method directly. This is the
939  * <tt>URLConnection</tt> implementation of <tt>connect()</tt>.
940  */
connect()941     public void connect() throws IOException {
942         if (isConnected() && tree.session.transport.tconHostName == null) {
943             /* Tree thinks it is connected but transport disconnected
944              * under it, reset tree to reflect the truth.
945              */
946             tree.treeDisconnect(true);
947         }
948 
949         if( isConnected() ) {
950             return;
951         }
952 
953         getUncPath0();
954         getFirstAddress();
955         for ( ;; ) {
956             try {
957                 doConnect();
958                 return;
959             } catch(SmbAuthException sae) {
960                 throw sae; // Prevents account lockout on servers with multiple IPs
961             } catch(SmbException se) {
962                 if (getNextAddress() == null)
963                     throw se;
964                 if (log.level >= 3)
965                     se.printStackTrace(log);
966             }
967         }
968     }
isConnected()969     boolean isConnected() {
970         return tree != null && tree.connectionState == 2;
971     }
open0( int flags, int access, int attrs, int options )972     int open0( int flags, int access, int attrs, int options ) throws SmbException {
973         int f;
974 
975         connect0();
976 
977         if( log.level >= 3 )
978             log.println( "open0: " + unc );
979 
980         /*
981          * NT Create AndX / Open AndX Request / Response
982          */
983 
984         if( tree.session.transport.hasCapability( ServerMessageBlock.CAP_NT_SMBS )) {
985             SmbComNTCreateAndXResponse response = new SmbComNTCreateAndXResponse();
986 SmbComNTCreateAndX request = new SmbComNTCreateAndX( unc, flags, access, shareAccess, attrs, options, null );
987 if (this instanceof SmbNamedPipe) {
988     request.flags0 |= 0x16;
989     request.desiredAccess |= 0x20000;
990     response.isExtended = true;
991 }
992             send( request, response );
993             f = response.fid;
994             attributes = response.extFileAttributes & ATTR_GET_MASK;
995             attrExpiration = System.currentTimeMillis() + attrExpirationPeriod;
996             isExists = true;
997         } else {
998             SmbComOpenAndXResponse response = new SmbComOpenAndXResponse();
999             send( new SmbComOpenAndX( unc, access, flags, null ), response );
1000             f = response.fid;
1001         }
1002 
1003         return f;
1004     }
open( int flags, int access, int attrs, int options )1005     void open( int flags, int access, int attrs, int options ) throws SmbException {
1006         if( isOpen() ) {
1007             return;
1008         }
1009         fid = open0( flags, access, attrs, options );
1010         opened = true;
1011         tree_num = tree.tree_num;
1012     }
isOpen()1013     boolean isOpen() {
1014         boolean ans = opened && isConnected() && tree_num == tree.tree_num;
1015         return ans;
1016     }
close( int f, long lastWriteTime )1017     void close( int f, long lastWriteTime ) throws SmbException {
1018 
1019         if( log.level >= 3 )
1020             log.println( "close: " + f );
1021 
1022         /*
1023          * Close Request / Response
1024          */
1025 
1026         send( new SmbComClose( f, lastWriteTime ), blank_resp() );
1027     }
close( long lastWriteTime )1028     void close( long lastWriteTime ) throws SmbException {
1029         if( isOpen() == false ) {
1030             return;
1031         }
1032         close( fid, lastWriteTime );
1033         opened = false;
1034     }
close()1035     void close() throws SmbException {
1036         close( 0L );
1037     }
1038 
1039 /**
1040  * Returns the <tt>NtlmPasswordAuthentication</tt> object used as
1041  * credentials with this file or pipe. This can be used to retrieve the
1042  * username for example:
1043  * <tt>
1044  * String username = f.getPrincipal().getName();
1045  * </tt>
1046  * The <tt>Principal</tt> object returned will never be <tt>null</tt>
1047  * however the username can be <tt>null</tt> indication anonymous
1048  * credentials were used (e.g. some IPC$ services).
1049  */
1050 
getPrincipal()1051     public Principal getPrincipal() {
1052         return auth;
1053     }
1054 
1055 /**
1056  * Returns the last component of the target URL. This will
1057  * effectively be the name of the file or directory represented by this
1058  * <code>SmbFile</code> or in the case of URLs that only specify a server
1059  * or workgroup, the server or workgroup will be returned. The name of
1060  * the root URL <code>smb://</code> is also <code>smb://</code>. If this
1061  * <tt>SmbFile</tt> refers to a workgroup, server, share, or directory,
1062  * the name will include a trailing slash '/' so that composing new
1063  * <tt>SmbFile</tt>s will maintain the trailing slash requirement.
1064  *
1065  * @return  The last component of the URL associated with this SMB
1066  *          resource or <code>smb://</code> if the resource is <code>smb://</code>
1067  *          itself.
1068  */
1069 
getName()1070     public String getName() {
1071         getUncPath0();
1072         if( canon.length() > 1 ) {
1073             int i = canon.length() - 2;
1074             while( canon.charAt( i ) != '/' ) {
1075                 i--;
1076             }
1077             return canon.substring( i + 1 );
1078         } else if( share != null ) {
1079             return share + '/';
1080         } else if( url.getHost().length() > 0 ) {
1081             return url.getHost() + '/';
1082         } else {
1083             return "smb://";
1084         }
1085     }
1086 
1087 /**
1088  * Everything but the last component of the URL representing this SMB
1089  * resource is effectivly it's parent. The root URL <code>smb://</code>
1090  * does not have a parent. In this case <code>smb://</code> is returned.
1091  *
1092  * @return   The parent directory of this SMB resource or
1093  *           <code>smb://</code> if the resource refers to the root of the URL
1094  *           hierarchy which incedentally is also <code>smb://</code>.
1095  */
1096 
getParent()1097     public String getParent() {
1098         String str = url.getAuthority();
1099 
1100         if( str.length() > 0 ) {
1101             StringBuffer sb = new StringBuffer( "smb://" );
1102 
1103             sb.append( str );
1104 
1105             getUncPath0();
1106             if( canon.length() > 1 ) {
1107                 sb.append( canon );
1108             } else {
1109                 sb.append( '/' );
1110             }
1111 
1112             str = sb.toString();
1113 
1114             int i = str.length() - 2;
1115             while( str.charAt( i ) != '/' ) {
1116                 i--;
1117             }
1118 
1119             return str.substring( 0, i + 1 );
1120         }
1121 
1122         return "smb://";
1123     }
1124 
1125 /**
1126  * Returns the full uncanonicalized URL of this SMB resource. An
1127  * <code>SmbFile</code> constructed with the result of this method will
1128  * result in an <code>SmbFile</code> that is equal to the original.
1129  *
1130  * @return  The uncanonicalized full URL of this SMB resource.
1131  */
1132 
getPath()1133     public String getPath() {
1134         return url.toString();
1135     }
1136 
getUncPath0()1137     String getUncPath0() {
1138         if( unc == null ) {
1139             char[] in = url.getPath().toCharArray();
1140             char[] out = new char[in.length];
1141             int length = in.length, i, o, state, s;
1142 
1143                               /* The canonicalization routine
1144                                */
1145             state = 0;
1146             o = 0;
1147             for( i = 0; i < length; i++ ) {
1148                 switch( state ) {
1149                     case 0:
1150                         if( in[i] != '/' ) {
1151                             return null;
1152                         }
1153                         out[o++] = in[i];
1154                         state = 1;
1155                         break;
1156                     case 1:
1157                         if( in[i] == '/' ) {
1158                             break;
1159                         } else if( in[i] == '.' &&
1160                                     (( i + 1 ) >= length || in[i + 1] == '/' )) {
1161                             i++;
1162                             break;
1163                         } else if(( i + 1 ) < length &&
1164                                     in[i] == '.' &&
1165                                     in[i + 1] == '.' &&
1166                                     (( i + 2 ) >= length || in[i + 2] == '/' )) {
1167                             i += 2;
1168                             if( o == 1 ) break;
1169                             do {
1170                                 o--;
1171                             } while( o > 1 && out[o - 1] != '/' );
1172                             break;
1173                         }
1174                         state = 2;
1175                     case 2:
1176                         if( in[i] == '/' ) {
1177                             state = 1;
1178                         }
1179                         out[o++] = in[i];
1180                         break;
1181                 }
1182             }
1183 
1184             canon = new String( out, 0, o );
1185 
1186             if( o > 1 ) {
1187                 o--;
1188                 i = canon.indexOf( '/', 1 );
1189                 if( i < 0 ) {
1190                     share = canon.substring( 1 );
1191                     unc = "\\";
1192                 } else if( i == o ) {
1193                     share = canon.substring( 1, i );
1194                     unc = "\\";
1195                 } else {
1196                     share = canon.substring( 1, i );
1197                     unc = canon.substring( i, out[o] == '/' ? o : o + 1 );
1198                     unc = unc.replace( '/', '\\' );
1199                 }
1200             } else {
1201                 share = null;
1202                 unc = "\\";
1203             }
1204         }
1205         return unc;
1206     }
1207 /**
1208  * Retuns the Windows UNC style path with backslashs intead of forward slashes.
1209  *
1210  * @return  The UNC path.
1211  */
getUncPath()1212     public String getUncPath() {
1213         getUncPath0();
1214         if( share == null ) {
1215             return "\\\\" + url.getHost();
1216         }
1217         return "\\\\" + url.getHost() + canon.replace( '/', '\\' );
1218     }
1219 /**
1220  * Returns the full URL of this SMB resource with '.' and '..' components
1221  * factored out. An <code>SmbFile</code> constructed with the result of
1222  * this method will result in an <code>SmbFile</code> that is equal to
1223  * the original.
1224  *
1225  * @return  The canonicalized URL of this SMB resource.
1226  */
1227 
getCanonicalPath()1228     public String getCanonicalPath() {
1229         String str = url.getAuthority();
1230         getUncPath0();
1231         if( str.length() > 0 ) {
1232             return "smb://" + url.getAuthority() + canon;
1233         }
1234         return "smb://";
1235     }
1236 
1237 /**
1238  * Retrieves the share associated with this SMB resource. In
1239  * the case of <code>smb://</code>, <code>smb://workgroup/</code>,
1240  * and <code>smb://server/</code> URLs which do not specify a share,
1241  * <code>null</code> will be returned.
1242  *
1243  * @return  The share component or <code>null</code> if there is no share
1244  */
1245 
getShare()1246     public String getShare() {
1247         return share;
1248     }
1249 
getServerWithDfs()1250     String getServerWithDfs() {
1251         if (dfsReferral != null) {
1252             return dfsReferral.server;
1253         }
1254         return getServer();
1255     }
1256 /**
1257  * Retrieve the hostname of the server for this SMB resource. If this
1258  * <code>SmbFile</code> references a workgroup, the name of the workgroup
1259  * is returned. If this <code>SmbFile</code> refers to the root of this
1260  * SMB network hierarchy, <code>null</code> is returned.
1261  *
1262  * @return  The server or workgroup name or <code>null</code> if this
1263  *          <code>SmbFile</code> refers to the root <code>smb://</code> resource.
1264  */
1265 
getServer()1266     public String getServer() {
1267         String str = url.getHost();
1268         if( str.length() == 0 ) {
1269             return null;
1270         }
1271         return str;
1272     }
1273 
1274 /**
1275  * Returns type of of object this <tt>SmbFile</tt> represents.
1276  * @return <tt>TYPE_FILESYSTEM, TYPE_WORKGROUP, TYPE_SERVER, TYPE_SHARE,
1277  * TYPE_PRINTER, TYPE_NAMED_PIPE</tt>, or <tt>TYPE_COMM</tt>.
1278  */
getType()1279     public int getType() throws SmbException {
1280         if( type == 0 ) {
1281             if( getUncPath0().length() > 1 ) {
1282                 type = TYPE_FILESYSTEM;
1283             } else if( share != null ) {
1284                 // treeConnect good enough to test service type
1285                 connect0();
1286                 if( share.equals( "IPC$" )) {
1287                     type = TYPE_NAMED_PIPE;
1288                 } else if( tree.service.equals( "LPT1:" )) {
1289                     type = TYPE_PRINTER;
1290                 } else if( tree.service.equals( "COMM" )) {
1291                     type = TYPE_COMM;
1292                 } else {
1293                     type = TYPE_SHARE;
1294                 }
1295             } else if( url.getAuthority() == null || url.getAuthority().length() == 0 ) {
1296                 type = TYPE_WORKGROUP;
1297             } else {
1298                 UniAddress addr;
1299                 try {
1300                     addr = getAddress();
1301                 } catch( UnknownHostException uhe ) {
1302                     throw new SmbException( url.toString(), uhe );
1303                 }
1304                 if( addr.getAddress() instanceof NbtAddress ) {
1305                     int code = ((NbtAddress)addr.getAddress()).getNameType();
1306                     if( code == 0x1d || code == 0x1b ) {
1307                         type = TYPE_WORKGROUP;
1308                         return type;
1309                     }
1310                 }
1311                 type = TYPE_SERVER;
1312             }
1313         }
1314         return type;
1315     }
isWorkgroup0()1316     boolean isWorkgroup0() throws UnknownHostException {
1317         if( type == TYPE_WORKGROUP || url.getHost().length() == 0 ) {
1318             type = TYPE_WORKGROUP;
1319             return true;
1320         } else {
1321             getUncPath0();
1322             if( share == null ) {
1323                 UniAddress addr = getAddress();
1324                 if( addr.getAddress() instanceof NbtAddress ) {
1325                     int code = ((NbtAddress)addr.getAddress()).getNameType();
1326                     if( code == 0x1d || code == 0x1b ) {
1327                         type = TYPE_WORKGROUP;
1328                         return true;
1329                     }
1330                 }
1331                 type = TYPE_SERVER;
1332             }
1333         }
1334         return false;
1335     }
1336 
queryPath( String path, int infoLevel )1337     Info queryPath( String path, int infoLevel ) throws SmbException {
1338         connect0();
1339 
1340         if (log.level >= 3)
1341             log.println( "queryPath: " + path );
1342 
1343         /* normally we'd check the negotiatedCapabilities for CAP_NT_SMBS
1344          * however I can't seem to get a good last modified time from
1345          * SMB_COM_QUERY_INFORMATION so if NT_SMBs are requested
1346          * by the server than in this case that's what it will get
1347          * regardless of what jcifs.smb.client.useNTSmbs is set
1348          * to(overrides negotiatedCapabilities).
1349          */
1350 
1351         /* We really should do the referral before this in case
1352          * the redirected target has different capabilities. But
1353          * the way we have been doing that is to call exists() which
1354          * calls this method so another technique will be necessary
1355          * to support DFS referral _to_ Win95/98/ME.
1356          */
1357 
1358         if( tree.session.transport.hasCapability( ServerMessageBlock.CAP_NT_SMBS )) {
1359 
1360             /*
1361              * Trans2 Query Path Information Request / Response
1362              */
1363 
1364             Trans2QueryPathInformationResponse response =
1365                     new Trans2QueryPathInformationResponse( infoLevel );
1366             send( new Trans2QueryPathInformation( path, infoLevel ), response );
1367 
1368             return response.info;
1369         } else {
1370 
1371             /*
1372              * Query Information Request / Response
1373              */
1374 
1375             SmbComQueryInformationResponse response =
1376                     new SmbComQueryInformationResponse(
1377                     tree.session.transport.server.serverTimeZone * 1000 * 60L );
1378             send( new SmbComQueryInformation( path ), response );
1379             return response;
1380         }
1381     }
1382 
1383 /**
1384  * Tests to see if the SMB resource exists. If the resource refers
1385  * only to a server, this method determines if the server exists on the
1386  * network and is advertising SMB services. If this resource refers to
1387  * a workgroup, this method determines if the workgroup name is valid on
1388  * the local SMB network. If this <code>SmbFile</code> refers to the root
1389  * <code>smb://</code> resource <code>true</code> is always returned. If
1390  * this <code>SmbFile</code> is a traditional file or directory, it will
1391  * be queried for on the specified server as expected.
1392  *
1393  * @return <code>true</code> if the resource exists or is alive or
1394  *         <code>false</code> otherwise
1395  */
1396 
exists()1397     public boolean exists() throws SmbException {
1398 
1399         if( attrExpiration > System.currentTimeMillis() ) {
1400             return isExists;
1401         }
1402 
1403         attributes = ATTR_READONLY | ATTR_DIRECTORY;
1404         createTime = 0L;
1405         lastModified = 0L;
1406         isExists = false;
1407 
1408         try {
1409             if( url.getHost().length() == 0 ) {
1410             } else if( share == null ) {
1411                 if( getType() == TYPE_WORKGROUP ) {
1412                     UniAddress.getByName( url.getHost(), true );
1413                 } else {
1414                     UniAddress.getByName( url.getHost() ).getHostName();
1415                 }
1416             } else if( getUncPath0().length() == 1 ||
1417                                         share.equalsIgnoreCase( "IPC$" )) {
1418                 connect0(); // treeConnect is good enough
1419             } else {
1420                 Info info = queryPath( getUncPath0(),
1421                     Trans2QueryPathInformationResponse.SMB_QUERY_FILE_BASIC_INFO );
1422                 attributes = info.getAttributes();
1423                 createTime = info.getCreateTime();
1424                 lastModified = info.getLastWriteTime();
1425             }
1426 
1427             /* If any of the above fail, isExists will not be set true
1428              */
1429 
1430             isExists = true;
1431 
1432         } catch( UnknownHostException uhe ) {
1433         } catch( SmbException se ) {
1434             switch (se.getNtStatus()) {
1435                 case NtStatus.NT_STATUS_NO_SUCH_FILE:
1436                 case NtStatus.NT_STATUS_OBJECT_NAME_INVALID:
1437                 case NtStatus.NT_STATUS_OBJECT_NAME_NOT_FOUND:
1438                 case NtStatus.NT_STATUS_OBJECT_PATH_NOT_FOUND:
1439                     break;
1440                 default:
1441                     throw se;
1442             }
1443         }
1444 
1445         attrExpiration = System.currentTimeMillis() + attrExpirationPeriod;
1446 
1447         return isExists;
1448     }
1449 
1450 /**
1451  * Tests to see if the file this <code>SmbFile</code> represents can be
1452  * read. Because any file, directory, or other resource can be read if it
1453  * exists, this method simply calls the <code>exists</code> method.
1454  *
1455  * @return <code>true</code> if the file is read-only
1456  */
1457 
canRead()1458     public boolean canRead() throws SmbException {
1459         if( getType() == TYPE_NAMED_PIPE ) { // try opening the pipe for reading?
1460             return true;
1461         }
1462         return exists(); // try opening and catch sharing violation?
1463     }
1464 
1465 /**
1466  * Tests to see if the file this <code>SmbFile</code> represents
1467  * exists and is not marked read-only. By default, resources are
1468  * considered to be read-only and therefore for <code>smb://</code>,
1469  * <code>smb://workgroup/</code>, and <code>smb://server/</code> resources
1470  * will be read-only.
1471  *
1472  * @return  <code>true</code> if the resource exists is not marked
1473  *          read-only
1474  */
1475 
canWrite()1476     public boolean canWrite() throws SmbException {
1477         if( getType() == TYPE_NAMED_PIPE ) { // try opening the pipe for writing?
1478             return true;
1479         }
1480         return exists() && ( attributes & ATTR_READONLY ) == 0;
1481     }
1482 
1483 /**
1484  * Tests to see if the file this <code>SmbFile</code> represents is a directory.
1485  *
1486  * @return <code>true</code> if this <code>SmbFile</code> is a directory
1487  */
1488 
isDirectory()1489     public boolean isDirectory() throws SmbException {
1490         if( getUncPath0().length() == 1 ) {
1491             return true;
1492         }
1493         if (!exists()) return false;
1494         return ( attributes & ATTR_DIRECTORY ) == ATTR_DIRECTORY;
1495     }
1496 
1497 /**
1498  * Tests to see if the file this <code>SmbFile</code> represents is not a directory.
1499  *
1500  * @return <code>true</code> if this <code>SmbFile</code> is not a directory
1501  */
1502 
isFile()1503     public boolean isFile() throws SmbException {
1504         if( getUncPath0().length() == 1 ) {
1505             return false;
1506         }
1507         exists();
1508         return ( attributes & ATTR_DIRECTORY ) == 0;
1509     }
1510 
1511 /**
1512  * Tests to see if the file this SmbFile represents is marked as
1513  * hidden. This method will also return true for shares with names that
1514  * end with '$' such as <code>IPC$</code> or <code>C$</code>.
1515  *
1516  * @return <code>true</code> if the <code>SmbFile</code> is marked as being hidden
1517  */
1518 
isHidden()1519     public boolean isHidden() throws SmbException {
1520         if( share == null ) {
1521             return false;
1522         } else if( getUncPath0().length() == 1 ) {
1523             if( share.endsWith( "$" )) {
1524                 return true;
1525             }
1526             return false;
1527         }
1528         exists();
1529         return ( attributes & ATTR_HIDDEN ) == ATTR_HIDDEN;
1530     }
1531 
1532 /**
1533  * If the path of this <code>SmbFile</code> falls within a DFS volume,
1534  * this method will return the referral path to which it maps. Otherwise
1535  * <code>null</code> is returned.
1536  */
1537 
getDfsPath()1538     public String getDfsPath() throws SmbException {
1539         resolveDfs(null);
1540         if( dfsReferral == null ) {
1541             return null;
1542         }
1543         String path = "smb:/" + dfsReferral.server + "/" + dfsReferral.share + unc;
1544         path = path.replace( '\\', '/' );
1545         if (isDirectory()) {
1546             path += '/';
1547         }
1548         return path;
1549     }
1550 
1551 /**
1552  * Retrieve the time this <code>SmbFile</code> was created. The value
1553  * returned is suitable for constructing a {@link java.util.Date} object
1554  * (i.e. seconds since Epoch 1970). Times should be the same as those
1555  * reported using the properties dialog of the Windows Explorer program.
1556  *
1557  * For Win95/98/Me this is actually the last write time. It is currently
1558  * not possible to retrieve the create time from files on these systems.
1559  *
1560  * @return The number of milliseconds since the 00:00:00 GMT, January 1,
1561  *         1970 as a <code>long</code> value
1562  */
createTime()1563     public long createTime() throws SmbException {
1564         if( getUncPath0().length() > 1 ) {
1565             exists();
1566             return createTime;
1567         }
1568         return 0L;
1569     }
1570 /**
1571  * Retrieve the last time the file represented by this
1572  * <code>SmbFile</code> was modified. The value returned is suitable for
1573  * constructing a {@link java.util.Date} object (i.e. seconds since Epoch
1574  * 1970). Times should be the same as those reported using the properties
1575  * dialog of the Windows Explorer program.
1576  *
1577  * @return The number of milliseconds since the 00:00:00 GMT, January 1,
1578  *         1970 as a <code>long</code> value
1579  */
lastModified()1580     public long lastModified() throws SmbException {
1581         if( getUncPath0().length() > 1 ) {
1582             exists();
1583             return lastModified;
1584         }
1585         return 0L;
1586     }
1587 /**
1588  * List the contents of this SMB resource. The list returned by this
1589  * method will be;
1590  *
1591  * <ul>
1592  * <li> files and directories contained within this resource if the
1593  * resource is a normal disk file directory,
1594  * <li> all available NetBIOS workgroups or domains if this resource is
1595  * the top level URL <code>smb://</code>,
1596  * <li> all servers registered as members of a NetBIOS workgroup if this
1597  * resource refers to a workgroup in a <code>smb://workgroup/</code> URL,
1598  * <li> all browseable shares of a server including printers, IPC
1599  * services, or disk volumes if this resource is a server URL in the form
1600  * <code>smb://server/</code>,
1601  * <li> or <code>null</code> if the resource cannot be resolved.
1602  * </ul>
1603  *
1604  * @return A <code>String[]</code> array of files and directories,
1605  * workgroups, servers, or shares depending on the context of the
1606  * resource URL
1607  */
list()1608     public String[] list() throws SmbException {
1609         return list( "*", ATTR_DIRECTORY | ATTR_HIDDEN | ATTR_SYSTEM, null, null );
1610     }
1611 
1612 /**
1613  * List the contents of this SMB resource. The list returned will be
1614  * identical to the list returned by the parameterless <code>list()</code>
1615  * method minus filenames filtered by the specified filter.
1616  *
1617  * @param filter a filename filter to exclude filenames from the results
1618  * @throws SmbException
1619  # @return An array of filenames
1620  */
list( SmbFilenameFilter filter )1621     public String[] list( SmbFilenameFilter filter ) throws SmbException {
1622         return list( "*", ATTR_DIRECTORY | ATTR_HIDDEN | ATTR_SYSTEM, filter, null );
1623     }
1624 
1625 /**
1626  * List the contents of this SMB resource as an array of
1627  * <code>SmbFile</code> objects. This method is much more efficient than
1628  * the regular <code>list</code> method when querying attributes of each
1629  * file in the result set.
1630  * <p>
1631  * The list of <code>SmbFile</code>s returned by this method will be;
1632  *
1633  * <ul>
1634  * <li> files and directories contained within this resource if the
1635  * resource is a normal disk file directory,
1636  * <li> all available NetBIOS workgroups or domains if this resource is
1637  * the top level URL <code>smb://</code>,
1638  * <li> all servers registered as members of a NetBIOS workgroup if this
1639  * resource refers to a workgroup in a <code>smb://workgroup/</code> URL,
1640  * <li> all browseable shares of a server including printers, IPC
1641  * services, or disk volumes if this resource is a server URL in the form
1642  * <code>smb://server/</code>,
1643  * <li> or <code>null</code> if the resource cannot be resolved.
1644  * </ul>
1645  *
1646  * @return An array of <code>SmbFile</code> objects representing file
1647  * and directories, workgroups, servers, or shares depending on the context
1648  * of the resource URL
1649  */
listFiles()1650     public SmbFile[] listFiles() throws SmbException {
1651         return listFiles( "*", ATTR_DIRECTORY | ATTR_HIDDEN | ATTR_SYSTEM, null, null );
1652     }
1653 
1654 /**
1655  * The CIFS protocol provides for DOS "wildcards" to be used as
1656  * a performance enhancement. The client does not have to filter
1657  * the names and the server does not have to return all directory
1658  * entries.
1659  * <p>
1660  * The wildcard expression may consist of two special meta
1661  * characters in addition to the normal filename characters. The '*'
1662  * character matches any number of characters in part of a name. If
1663  * the expression begins with one or more '?'s then exactly that
1664  * many characters will be matched whereas if it ends with '?'s
1665  * it will match that many characters <i>or less</i>.
1666  * <p>
1667  * Wildcard expressions will not filter workgroup names or server names.
1668  *
1669  * <blockquote><pre>
1670  * winnt> ls c?o*
1671  * clock.avi                  -rw--      82944 Mon Oct 14 1996 1:38 AM
1672  * Cookies                    drw--          0 Fri Nov 13 1998 9:42 PM
1673  * 2 items in 5ms
1674  * </pre></blockquote>
1675  *
1676  * @param wildcard a wildcard expression
1677  * @throws SmbException
1678  * @return An array of <code>SmbFile</code> objects representing file
1679  * and directories, workgroups, servers, or shares depending on the context
1680  * of the resource URL
1681  */
1682 
listFiles( String wildcard )1683     public SmbFile[] listFiles( String wildcard ) throws SmbException {
1684         return listFiles( wildcard, ATTR_DIRECTORY | ATTR_HIDDEN | ATTR_SYSTEM, null, null );
1685     }
1686 /**
1687  * List the contents of this SMB resource. The list returned will be
1688  * identical to the list returned by the parameterless <code>listFiles()</code>
1689  * method minus files filtered by the specified filename filter.
1690  *
1691  * @param filter a filter to exclude files from the results
1692  * @return An array of <tt>SmbFile</tt> objects
1693  * @throws SmbException
1694  */
listFiles( SmbFilenameFilter filter )1695     public SmbFile[] listFiles( SmbFilenameFilter filter ) throws SmbException {
1696         return listFiles( "*", ATTR_DIRECTORY | ATTR_HIDDEN | ATTR_SYSTEM, filter, null );
1697     }
1698 /**
1699  * List the contents of this SMB resource. The list returned will be
1700  * identical to the list returned by the parameterless <code>listFiles()</code>
1701  * method minus filenames filtered by the specified filter.
1702  *
1703  * @param filter a file filter to exclude files from the results
1704  * @return An array of <tt>SmbFile</tt> objects
1705  */
listFiles( SmbFileFilter filter )1706     public SmbFile[] listFiles( SmbFileFilter filter ) throws SmbException {
1707         return listFiles( "*", ATTR_DIRECTORY | ATTR_HIDDEN | ATTR_SYSTEM, null, filter );
1708     }
list( String wildcard, int searchAttributes, SmbFilenameFilter fnf, SmbFileFilter ff )1709     String[] list( String wildcard, int searchAttributes,
1710                 SmbFilenameFilter fnf, SmbFileFilter ff ) throws SmbException {
1711         ArrayList list = new ArrayList();
1712         doEnum(list, false, wildcard, searchAttributes, fnf, ff);
1713         return (String[])list.toArray(new String[list.size()]);
1714     }
listFiles( String wildcard, int searchAttributes, SmbFilenameFilter fnf, SmbFileFilter ff )1715     SmbFile[] listFiles( String wildcard, int searchAttributes,
1716                 SmbFilenameFilter fnf, SmbFileFilter ff ) throws SmbException {
1717         ArrayList list = new ArrayList();
1718         doEnum(list, true, wildcard, searchAttributes, fnf, ff);
1719         return (SmbFile[])list.toArray(new SmbFile[list.size()]);
1720     }
doEnum(ArrayList list, boolean files, String wildcard, int searchAttributes, SmbFilenameFilter fnf, SmbFileFilter ff)1721     void doEnum(ArrayList list,
1722                     boolean files,
1723                     String wildcard,
1724                     int searchAttributes,
1725                     SmbFilenameFilter fnf,
1726                     SmbFileFilter ff) throws SmbException {
1727         if (ff != null && ff instanceof DosFileFilter) {
1728             DosFileFilter dff = (DosFileFilter)ff;
1729             if (dff.wildcard != null)
1730                 wildcard = dff.wildcard;
1731             searchAttributes = dff.attributes;
1732         }
1733 
1734         try {
1735             int hostlen = url.getHost().length();
1736             if (hostlen == 0 || getType() == TYPE_WORKGROUP) {
1737                 doNetServerEnum(list, files, wildcard, searchAttributes, fnf, ff);
1738             } else if (share == null) {
1739                 doShareEnum(list, files, wildcard, searchAttributes, fnf, ff);
1740             } else {
1741                 doFindFirstNext(list, files, wildcard, searchAttributes, fnf, ff);
1742             }
1743         } catch (UnknownHostException uhe) {
1744             throw new SmbException(url.toString(), uhe);
1745         } catch (MalformedURLException mue) {
1746             throw new SmbException(url.toString(), mue);
1747         }
1748     }
doShareEnum(ArrayList list, boolean files, String wildcard, int searchAttributes, SmbFilenameFilter fnf, SmbFileFilter ff)1749     void doShareEnum(ArrayList list,
1750                 boolean files,
1751                 String wildcard,
1752                 int searchAttributes,
1753                 SmbFilenameFilter fnf,
1754                 SmbFileFilter ff) throws SmbException,
1755                                 UnknownHostException,
1756                                 MalformedURLException {
1757         String p = url.getPath();
1758         IOException last = null;
1759         FileEntry[] entries;
1760         UniAddress addr;
1761         FileEntry e;
1762         HashMap map;
1763 
1764         if (p.lastIndexOf('/') != (p.length() - 1))
1765             throw new SmbException(url.toString() + " directory must end with '/'");
1766         if (getType() != TYPE_SERVER)
1767             throw new SmbException("The requested list operations is invalid: " + url.toString());
1768 
1769         map = new HashMap();
1770 
1771         if (dfs.isTrustedDomain(getServer(), auth)) {
1772             /* The server name is actually the name of a trusted
1773              * domain. Add DFS roots to the list.
1774              */
1775             try {
1776                 entries = doDfsRootEnum();
1777                 for (int ei = 0; ei < entries.length; ei++) {
1778                     e = entries[ei];
1779                     if (map.containsKey(e) == false)
1780                         map.put(e, e);
1781                 }
1782             } catch (IOException ioe) {
1783                 if (log.level >= 4)
1784                     ioe.printStackTrace(log);
1785             }
1786         }
1787 
1788         addr = getFirstAddress();
1789         while (addr != null) {
1790             try {
1791                 doConnect();
1792                 try {
1793                     entries = doMsrpcShareEnum();
1794                 } catch(IOException ioe) {
1795                     if (log.level >= 3)
1796                         ioe.printStackTrace(log);
1797                     entries = doNetShareEnum();
1798                 }
1799                 for (int ei = 0; ei < entries.length; ei++) {
1800                     e = entries[ei];
1801                     if (map.containsKey(e) == false)
1802                         map.put(e, e);
1803                 }
1804                 break;
1805             } catch(IOException ioe) {
1806                 if (log.level >= 3)
1807                     ioe.printStackTrace(log);
1808                 last = ioe;
1809             }
1810             addr = getNextAddress();
1811         }
1812 
1813         if (last != null && map.isEmpty()) {
1814             if (last instanceof SmbException == false)
1815                 throw new SmbException(url.toString(), last);
1816             throw (SmbException)last;
1817         }
1818 
1819         Iterator iter = map.keySet().iterator();
1820         while (iter.hasNext()) {
1821             e = (FileEntry)iter.next();
1822             String name = e.getName();
1823             if (fnf != null && fnf.accept(this, name) == false)
1824                 continue;
1825             if (name.length() > 0) {
1826                 // if !files we don't need to create SmbFiles here
1827                 SmbFile f = new SmbFile(this, name, e.getType(),
1828                             ATTR_READONLY | ATTR_DIRECTORY, 0L, 0L, 0L );
1829                 if (ff != null && ff.accept(f) == false)
1830                     continue;
1831                 if (files) {
1832                     list.add(f);
1833                 } else {
1834                     list.add(name);
1835                 }
1836             }
1837         }
1838     }
doDfsRootEnum()1839     FileEntry[] doDfsRootEnum() throws IOException {
1840         MsrpcDfsRootEnum rpc;
1841         DcerpcHandle handle = null;
1842         FileEntry[] entries;
1843 
1844         handle = DcerpcHandle.getHandle("ncacn_np:" +
1845                     getAddress().getHostAddress() +
1846                     "[\\PIPE\\netdfs]", auth);
1847         try {
1848             rpc = new MsrpcDfsRootEnum(getServer());
1849             handle.sendrecv(rpc);
1850             if (rpc.retval != 0)
1851                 throw new SmbException(rpc.retval, true);
1852             return rpc.getEntries();
1853         } finally {
1854             try {
1855                 handle.close();
1856             } catch(IOException ioe) {
1857                 if (log.level >= 4)
1858                     ioe.printStackTrace(log);
1859             }
1860         }
1861     }
doMsrpcShareEnum()1862     FileEntry[] doMsrpcShareEnum() throws IOException {
1863         MsrpcShareEnum rpc;
1864         DcerpcHandle handle;
1865 
1866         rpc = new MsrpcShareEnum(url.getHost());
1867 
1868         /* JCIFS will build a composite list of shares if the target host has
1869          * multiple IP addresses such as when domain-based DFS is in play. Because
1870          * of this, to ensure that we query each IP individually without re-resolving
1871          * the hostname and getting a different IP, we must use the current addresses
1872          * IP rather than just url.getHost() like we were using prior to 1.2.16.
1873          */
1874 
1875         handle = DcerpcHandle.getHandle("ncacn_np:" +
1876                     getAddress().getHostAddress() +
1877                     "[\\PIPE\\srvsvc]", auth);
1878 
1879         try {
1880             handle.sendrecv(rpc);
1881             if (rpc.retval != 0)
1882                 throw new SmbException(rpc.retval, true);
1883             return rpc.getEntries();
1884         } finally {
1885             try {
1886                 handle.close();
1887             } catch(IOException ioe) {
1888                 if (log.level >= 4)
1889                     ioe.printStackTrace(log);
1890             }
1891         }
1892     }
doNetShareEnum()1893     FileEntry[] doNetShareEnum() throws SmbException {
1894         SmbComTransaction req = new NetShareEnum();
1895         SmbComTransactionResponse resp = new NetShareEnumResponse();
1896 
1897         send(req, resp);
1898 
1899         if (resp.status != SmbException.ERROR_SUCCESS)
1900             throw new SmbException(resp.status, true);
1901 
1902         return resp.results;
1903     }
doNetServerEnum(ArrayList list, boolean files, String wildcard, int searchAttributes, SmbFilenameFilter fnf, SmbFileFilter ff)1904     void doNetServerEnum(ArrayList list,
1905                 boolean files,
1906                 String wildcard,
1907                 int searchAttributes,
1908                 SmbFilenameFilter fnf,
1909                 SmbFileFilter ff) throws SmbException,
1910                                 UnknownHostException,
1911                                 MalformedURLException {
1912         int listType = url.getHost().length() == 0 ? 0 : getType();
1913         SmbComTransaction req;
1914         SmbComTransactionResponse resp;
1915 
1916         if (listType == 0) {
1917             connect0();
1918             req = new NetServerEnum2(tree.session.transport.server.oemDomainName,
1919                         NetServerEnum2.SV_TYPE_DOMAIN_ENUM );
1920             resp = new NetServerEnum2Response();
1921         } else if (listType == TYPE_WORKGROUP) {
1922             req = new NetServerEnum2(url.getHost(), NetServerEnum2.SV_TYPE_ALL);
1923             resp = new NetServerEnum2Response();
1924         } else {
1925             throw new SmbException( "The requested list operations is invalid: " + url.toString() );
1926         }
1927 
1928         boolean more;
1929         do {
1930             int n;
1931 
1932             send(req, resp);
1933 
1934             if (resp.status != SmbException.ERROR_SUCCESS &&
1935                     resp.status != SmbException.ERROR_MORE_DATA) {
1936                 throw new SmbException( resp.status, true );
1937             }
1938             more = resp.status == SmbException.ERROR_MORE_DATA;
1939 
1940             n = more ? resp.numEntries - 1 : resp.numEntries;
1941             for (int i = 0; i < n; i++) {
1942                 FileEntry e = resp.results[i];
1943                 String name = e.getName();
1944                 if (fnf != null && fnf.accept(this, name) == false)
1945                     continue;
1946                 if (name.length() > 0) {
1947                     // if !files we don't need to create SmbFiles here
1948                     SmbFile f = new SmbFile(this, name, e.getType(),
1949                                 ATTR_READONLY | ATTR_DIRECTORY, 0L, 0L, 0L );
1950                     if (ff != null && ff.accept(f) == false)
1951                         continue;
1952                     if (files) {
1953                         list.add(f);
1954                     } else {
1955                         list.add(name);
1956                     }
1957                 }
1958             }
1959             if (getType() != TYPE_WORKGROUP) {
1960                 break;
1961             }
1962             req.subCommand = (byte)SmbComTransaction.NET_SERVER_ENUM3;
1963             req.reset(0, ((NetServerEnum2Response)resp).lastName);
1964             resp.reset();
1965         } while(more);
1966     }
doFindFirstNext( ArrayList list, boolean files, String wildcard, int searchAttributes, SmbFilenameFilter fnf, SmbFileFilter ff )1967     void doFindFirstNext( ArrayList list,
1968                 boolean files,
1969                 String wildcard,
1970                 int searchAttributes,
1971                 SmbFilenameFilter fnf,
1972                 SmbFileFilter ff ) throws SmbException, UnknownHostException, MalformedURLException {
1973         SmbComTransaction req;
1974         Trans2FindFirst2Response resp;
1975         int sid;
1976         String path = getUncPath0();
1977         String p = url.getPath();
1978 
1979         if( p.lastIndexOf( '/' ) != ( p.length() - 1 )) {
1980             throw new SmbException( url.toString() + " directory must end with '/'" );
1981         }
1982 
1983         req = new Trans2FindFirst2( path, wildcard, searchAttributes );
1984         resp = new Trans2FindFirst2Response();
1985 
1986         if( log.level >= 3 )
1987             log.println( "doFindFirstNext: " + req.path );
1988 
1989         send( req, resp );
1990 
1991         sid = resp.sid;
1992         req = new Trans2FindNext2( sid, resp.resumeKey, resp.lastName );
1993 
1994         /* The only difference between first2 and next2 responses is subCommand
1995          * so let's recycle the response object.
1996          */
1997         resp.subCommand = SmbComTransaction.TRANS2_FIND_NEXT2;
1998 
1999         for( ;; ) {
2000             for( int i = 0; i < resp.numEntries; i++ ) {
2001                 FileEntry e = resp.results[i];
2002                 String name = e.getName();
2003                 if( name.length() < 3 ) {
2004                     int h = name.hashCode();
2005                     if( h == HASH_DOT || h == HASH_DOT_DOT ) {
2006                         if (name.equals(".") || name.equals(".."))
2007                             continue;
2008                     }
2009                 }
2010                 if( fnf != null && fnf.accept( this, name ) == false ) {
2011                     continue;
2012                 }
2013                 if( name.length() > 0 ) {
2014                     SmbFile f = new SmbFile( this, name, TYPE_FILESYSTEM,
2015                             e.getAttributes(), e.createTime(), e.lastModified(), e.length() );
2016                     if( ff != null && ff.accept( f ) == false ) {
2017                         continue;
2018                     }
2019                     if( files ) {
2020                         list.add( f );
2021                     } else {
2022                         list.add( name );
2023                     }
2024                 }
2025             }
2026 
2027             if( resp.isEndOfSearch || resp.numEntries == 0 ) {
2028                 break;
2029             }
2030 
2031             req.reset( resp.resumeKey, resp.lastName );
2032             resp.reset();
2033             send( req, resp );
2034         }
2035 
2036         try {
2037             send( new SmbComFindClose2( sid ), blank_resp() );
2038         } catch (SmbException se) {
2039             if( log.level >= 4 )
2040                 se.printStackTrace( log );
2041         }
2042     }
2043 
2044 /**
2045  * Changes the name of the file this <code>SmbFile</code> represents to the name
2046  * designated by the <code>SmbFile</code> argument.
2047  * <p/>
2048  * <i>Remember: <code>SmbFile</code>s are immutible and therefore
2049  * the path associated with this <code>SmbFile</code> object will not
2050  * change). To access the renamed file it is necessary to construct a
2051  * new <tt>SmbFile</tt></i>.
2052  *
2053  * @param  dest  An <code>SmbFile</code> that represents the new pathname
2054  * @throws NullPointerException
2055  *         If the <code>dest</code> argument is <code>null</code>
2056  */
renameTo( SmbFile dest )2057     public void renameTo( SmbFile dest ) throws SmbException {
2058         if( getUncPath0().length() == 1 || dest.getUncPath0().length() == 1 ) {
2059             throw new SmbException( "Invalid operation for workgroups, servers, or shares" );
2060         }
2061 
2062         resolveDfs(null);
2063         dest.resolveDfs(null);
2064 
2065         if (!tree.equals(dest.tree)) {
2066             throw new SmbException( "Invalid operation for workgroups, servers, or shares" );
2067         }
2068 
2069         if( log.level >= 3 )
2070             log.println( "renameTo: " + unc + " -> " + dest.unc );
2071 
2072         attrExpiration = sizeExpiration = 0;
2073         dest.attrExpiration = 0;
2074 
2075         /*
2076          * Rename Request / Response
2077          */
2078 
2079         send( new SmbComRename( unc, dest.unc ), blank_resp() );
2080     }
2081 
2082     class WriterThread extends Thread {
2083         byte[] b;
2084         int n;
2085         long off;
2086         boolean ready;
2087         SmbFile dest;
2088         SmbException e = null;
2089         boolean useNTSmbs;
2090         SmbComWriteAndX reqx;
2091         SmbComWrite req;
2092         ServerMessageBlock resp;
2093 
WriterThread()2094         WriterThread() throws SmbException {
2095             super( "JCIFS-WriterThread" );
2096             useNTSmbs = tree.session.transport.hasCapability( ServerMessageBlock.CAP_NT_SMBS );
2097             if( useNTSmbs ) {
2098                 reqx = new SmbComWriteAndX();
2099                 resp = new SmbComWriteAndXResponse();
2100             } else {
2101                 req = new SmbComWrite();
2102                 resp = new SmbComWriteResponse();
2103             }
2104             ready = false;
2105         }
2106 
write( byte[] b, int n, SmbFile dest, long off )2107         synchronized void write( byte[] b, int n, SmbFile dest, long off ) {
2108             this.b = b;
2109             this.n = n;
2110             this.dest = dest;
2111             this.off = off;
2112             ready = false;
2113             notify();
2114         }
2115 
run()2116         public void run() {
2117             synchronized( this ) {
2118                 try {
2119                     for( ;; ) {
2120                         notify();
2121                         ready = true;
2122                         while( ready ) {
2123                             wait();
2124                         }
2125                         if( n == -1 ) {
2126                             return;
2127                         }
2128                         if( useNTSmbs ) {
2129                             reqx.setParam( dest.fid, off, n, b, 0, n );
2130                             dest.send( reqx, resp );
2131                         } else {
2132                             req.setParam( dest.fid, off, n, b, 0, n );
2133                             dest.send( req, resp );
2134                         }
2135                     }
2136                 } catch( SmbException e ) {
2137                     this.e = e;
2138                 } catch( Exception x ) {
2139                     this.e = new SmbException( "WriterThread", x );
2140                 }
2141                 notify();
2142             }
2143         }
2144     }
copyTo0( SmbFile dest, byte[][] b, int bsize, WriterThread w, SmbComReadAndX req, SmbComReadAndXResponse resp )2145     void copyTo0( SmbFile dest, byte[][] b, int bsize, WriterThread w,
2146             SmbComReadAndX req, SmbComReadAndXResponse resp ) throws SmbException {
2147         int i;
2148 
2149         if( attrExpiration < System.currentTimeMillis() ) {
2150             attributes = ATTR_READONLY | ATTR_DIRECTORY;
2151             createTime = 0L;
2152             lastModified = 0L;
2153             isExists = false;
2154 
2155             Info info = queryPath( getUncPath0(),
2156                     Trans2QueryPathInformationResponse.SMB_QUERY_FILE_BASIC_INFO );
2157             attributes = info.getAttributes();
2158             createTime = info.getCreateTime();
2159             lastModified = info.getLastWriteTime();
2160 
2161             /* If any of the above fails, isExists will not be set true
2162              */
2163 
2164             isExists = true;
2165             attrExpiration = System.currentTimeMillis() + attrExpirationPeriod;
2166         }
2167 
2168         if( isDirectory() ) {
2169             SmbFile[] files;
2170             SmbFile ndest;
2171 
2172             String path = dest.getUncPath0();
2173             if( path.length() > 1 ) {
2174                 try {
2175                     dest.mkdir();
2176                     dest.setPathInformation( attributes, createTime, lastModified );
2177                 } catch( SmbException se ) {
2178                     if( se.getNtStatus() != NtStatus.NT_STATUS_ACCESS_DENIED &&
2179                             se.getNtStatus() != NtStatus.NT_STATUS_OBJECT_NAME_COLLISION ) {
2180                         throw se;
2181                     }
2182                 }
2183             }
2184 
2185             files = listFiles( "*", ATTR_DIRECTORY | ATTR_HIDDEN | ATTR_SYSTEM, null, null );
2186             try {
2187                 for( i = 0; i < files.length; i++ ) {
2188                     ndest = new SmbFile( dest,
2189                                     files[i].getName(),
2190                                     files[i].type,
2191                                     files[i].attributes,
2192                                     files[i].createTime,
2193                                     files[i].lastModified,
2194                                     files[i].size );
2195                     files[i].copyTo0( ndest, b, bsize, w, req, resp );
2196                 }
2197             } catch( UnknownHostException uhe ) {
2198                 throw new SmbException( url.toString(), uhe );
2199             } catch( MalformedURLException mue ) {
2200                 throw new SmbException( url.toString(), mue );
2201             }
2202         } else {
2203             long off;
2204 
2205             try {
2206                 open( SmbFile.O_RDONLY, 0, ATTR_NORMAL, 0 );
2207                 try {
2208                     dest.open( SmbFile.O_CREAT | SmbFile.O_WRONLY | SmbFile.O_TRUNC,
2209                             FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES,
2210                             attributes, 0 );
2211                 } catch( SmbAuthException sae ) {
2212                     if(( dest.attributes & ATTR_READONLY ) != 0 ) {
2213                                                 /* Remove READONLY and try again
2214                                                  */
2215                         dest.setPathInformation( dest.attributes & ~ATTR_READONLY, 0L, 0L );
2216                         dest.open( SmbFile.O_CREAT | SmbFile.O_WRONLY | SmbFile.O_TRUNC,
2217                                 FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES,
2218                                 attributes, 0 );
2219                     } else {
2220                         throw sae;
2221                     }
2222                 }
2223 
2224                 i = 0;
2225                 off = 0L;
2226                 for( ;; ) {
2227                     req.setParam( fid, off, bsize );
2228                     resp.setParam( b[i], 0 );
2229                     send( req, resp );
2230 
2231                     synchronized( w ) {
2232                         if( w.e != null ) {
2233                             throw w.e;
2234                         }
2235                         while( !w.ready ) {
2236                             try {
2237                                 w.wait();
2238                             } catch( InterruptedException ie ) {
2239                                 throw new SmbException( dest.url.toString(), ie );
2240                             }
2241                         }
2242                         if( w.e != null ) {
2243                             throw w.e;
2244                         }
2245                         if( resp.dataLength <= 0 ) {
2246                             break;
2247                         }
2248                         w.write( b[i], resp.dataLength, dest, off );
2249                     }
2250 
2251                     i = i == 1 ? 0 : 1;
2252                     off += resp.dataLength;
2253                 }
2254 
2255                 dest.send( new Trans2SetFileInformation(
2256                         dest.fid, attributes, createTime, lastModified ),
2257                         new Trans2SetFileInformationResponse() );
2258                 dest.close( 0L );
2259             } catch( SmbException se ) {
2260 
2261                 if (ignoreCopyToException == false)
2262                     throw new SmbException("Failed to copy file from [" + this.toString() + "] to [" + dest.toString() + "]", se);
2263 
2264                 if( log.level > 1 )
2265                     se.printStackTrace( log );
2266             } finally {
2267                 close();
2268             }
2269         }
2270     }
2271 /**
2272  * This method will copy the file or directory represented by this
2273  * <tt>SmbFile</tt> and it's sub-contents to the location specified by the
2274  * <tt>dest</tt> parameter. This file and the destination file do not
2275  * need to be on the same host. This operation does not copy extended
2276  * file attibutes such as ACLs but it does copy regular attributes as
2277  * well as create and last write times. This method is almost twice as
2278  * efficient as manually copying as it employs an additional write
2279  * thread to read and write data concurrently.
2280  * <p/>
2281  * It is not possible (nor meaningful) to copy entire workgroups or
2282  * servers.
2283  *
2284  * @param dest the destination file or directory
2285  * @throws SmbException
2286  */
copyTo( SmbFile dest )2287     public void copyTo( SmbFile dest ) throws SmbException {
2288         SmbComReadAndX req;
2289         SmbComReadAndXResponse resp;
2290         WriterThread w;
2291         int bsize;
2292         byte[][] b;
2293 
2294         /* Should be able to copy an entire share actually
2295          */
2296         if( share == null || dest.share == null) {
2297             throw new SmbException( "Invalid operation for workgroups or servers" );
2298         }
2299 
2300         req = new SmbComReadAndX();
2301         resp = new SmbComReadAndXResponse();
2302 
2303         connect0();
2304         dest.connect0();
2305 
2306                 /* At this point the maxBufferSize values are from the server
2307                  * exporting the volumes, not the one that we will actually
2308                  * end up performing IO with. If the server hosting the
2309                  * actual files has a smaller maxBufSize this could be
2310                  * incorrect. To handle this properly it is necessary
2311                  * to redirect the tree to the target server first before
2312                  * establishing buffer size. These exists() calls facilitate
2313                  * that.
2314                  */
2315         resolveDfs(null);
2316 
2317         /* It is invalid for the source path to be a child of the destination
2318          * path or visa versa.
2319          */
2320         try {
2321             if (getAddress().equals( dest.getAddress() ) &&
2322                         canon.regionMatches( true, 0, dest.canon, 0,
2323                                 Math.min( canon.length(), dest.canon.length() ))) {
2324                 throw new SmbException( "Source and destination paths overlap." );
2325             }
2326         } catch (UnknownHostException uhe) {
2327         }
2328 
2329         w = new WriterThread();
2330         w.setDaemon( true );
2331         w.start();
2332 
2333         /* Downgrade one transport to the lower of the negotiated buffer sizes
2334          * so we can just send whatever is received.
2335          */
2336 
2337         SmbTransport t1 = tree.session.transport;
2338         SmbTransport t2 = dest.tree.session.transport;
2339 
2340         if( t1.snd_buf_size < t2.snd_buf_size ) {
2341             t2.snd_buf_size = t1.snd_buf_size;
2342         } else {
2343             t1.snd_buf_size = t2.snd_buf_size;
2344         }
2345 
2346         bsize = Math.min( t1.rcv_buf_size - 70, t1.snd_buf_size - 70 );
2347         b = new byte[2][bsize];
2348 
2349         try {
2350             copyTo0( dest, b, bsize, w, req, resp );
2351         } finally {
2352             w.write( null, -1, null, 0 );
2353         }
2354     }
2355 
2356 /**
2357  * This method will delete the file or directory specified by this
2358  * <code>SmbFile</code>. If the target is a directory, the contents of
2359  * the directory will be deleted as well. If a file within the directory or
2360  * it's sub-directories is marked read-only, the read-only status will
2361  * be removed and the file will be deleted.
2362  *
2363  * @throws SmbException
2364  */
delete()2365     public void delete() throws SmbException {
2366         exists();
2367         getUncPath0();
2368         delete( unc );
2369     }
delete( String fileName )2370     void delete( String fileName ) throws SmbException {
2371         if( getUncPath0().length() == 1 ) {
2372             throw new SmbException( "Invalid operation for workgroups, servers, or shares" );
2373         }
2374 
2375         if( System.currentTimeMillis() > attrExpiration ) {
2376             attributes = ATTR_READONLY | ATTR_DIRECTORY;
2377             createTime = 0L;
2378             lastModified = 0L;
2379             isExists = false;
2380 
2381             Info info = queryPath( getUncPath0(),
2382                     Trans2QueryPathInformationResponse.SMB_QUERY_FILE_BASIC_INFO );
2383             attributes = info.getAttributes();
2384             createTime = info.getCreateTime();
2385             lastModified = info.getLastWriteTime();
2386 
2387             attrExpiration = System.currentTimeMillis() + attrExpirationPeriod;
2388             isExists = true;
2389         }
2390 
2391         if(( attributes & ATTR_READONLY ) != 0 ) {
2392             setReadWrite();
2393         }
2394 
2395         /*
2396          * Delete or Delete Directory Request / Response
2397          */
2398 
2399         if( log.level >= 3 )
2400             log.println( "delete: " + fileName );
2401 
2402         if(( attributes & ATTR_DIRECTORY ) != 0 ) {
2403 
2404             /* Recursively delete directory contents
2405              */
2406 
2407             try {
2408                 SmbFile[] l = listFiles( "*", ATTR_DIRECTORY | ATTR_HIDDEN | ATTR_SYSTEM, null, null );
2409                 for( int i = 0; i < l.length; i++ ) {
2410                     l[i].delete();
2411                 }
2412             } catch( SmbException se ) {
2413                 /* Oracle FilesOnline version 9.0.4 doesn't send '.' and '..' so
2414                  * listFiles may generate undesireable "cannot find
2415                  * the file specified".
2416                  */
2417                 if( se.getNtStatus() != SmbException.NT_STATUS_NO_SUCH_FILE ) {
2418                     throw se;
2419                 }
2420             }
2421 
2422             send( new SmbComDeleteDirectory( fileName ), blank_resp() );
2423         } else {
2424             send( new SmbComDelete( fileName ), blank_resp() );
2425         }
2426 
2427         attrExpiration = sizeExpiration = 0;
2428     }
2429 
2430 /**
2431  * Returns the length of this <tt>SmbFile</tt> in bytes. If this object
2432  * is a <tt>TYPE_SHARE</tt> the total capacity of the disk shared in
2433  * bytes is returned. If this object is a directory or a type other than
2434  * <tt>TYPE_SHARE</tt>, 0L is returned.
2435  *
2436  * @return The length of the file in bytes or 0 if this
2437  * <code>SmbFile</code> is not a file.
2438  * @throws SmbException
2439  */
2440 
length()2441     public long length() throws SmbException {
2442         if( sizeExpiration > System.currentTimeMillis() ) {
2443             return size;
2444         }
2445 
2446         if( getType() == TYPE_SHARE ) {
2447             Trans2QueryFSInformationResponse response;
2448             int level = Trans2QueryFSInformationResponse.SMB_INFO_ALLOCATION;
2449 
2450             response = new Trans2QueryFSInformationResponse( level );
2451             send( new Trans2QueryFSInformation( level ), response );
2452 
2453             size = response.info.getCapacity();
2454         } else if( getUncPath0().length() > 1 && type != TYPE_NAMED_PIPE ) {
2455             Info info = queryPath( getUncPath0(),
2456                     Trans2QueryPathInformationResponse.SMB_QUERY_FILE_STANDARD_INFO );
2457             size = info.getSize();
2458         } else {
2459             size = 0L;
2460         }
2461         sizeExpiration = System.currentTimeMillis() + attrExpirationPeriod;
2462         return size;
2463     }
2464 
2465 /**
2466  * This method returns the free disk space in bytes of the drive this share
2467  * represents or the drive on which the directory or file resides. Objects
2468  * other than <tt>TYPE_SHARE</tt> or <tt>TYPE_FILESYSTEM</tt> will result
2469  * in 0L being returned.
2470  *
2471  * @return the free disk space in bytes of the drive on which this file or
2472  * directory resides
2473  */
getDiskFreeSpace()2474     public long getDiskFreeSpace() throws SmbException {
2475         if( getType() == TYPE_SHARE || type == TYPE_FILESYSTEM ) {
2476             int level = Trans2QueryFSInformationResponse.SMB_FS_FULL_SIZE_INFORMATION;
2477             try {
2478                 return queryFSInformation(level);
2479             } catch( SmbException ex ) {
2480                 switch (ex.getNtStatus()) {
2481                     case NtStatus.NT_STATUS_INVALID_INFO_CLASS:
2482                     case NtStatus.NT_STATUS_UNSUCCESSFUL: // NetApp Filer
2483                         // SMB_FS_FULL_SIZE_INFORMATION not supported by the server.
2484                         level = Trans2QueryFSInformationResponse.SMB_INFO_ALLOCATION;
2485                         return queryFSInformation(level);
2486                 }
2487                 throw ex;
2488             }
2489         }
2490         return 0L;
2491     }
2492 
queryFSInformation( int level )2493     private long queryFSInformation( int level ) throws SmbException {
2494         Trans2QueryFSInformationResponse response;
2495 
2496         response = new Trans2QueryFSInformationResponse( level );
2497         send( new Trans2QueryFSInformation( level ), response );
2498 
2499         if( type == TYPE_SHARE ) {
2500             size = response.info.getCapacity();
2501             sizeExpiration = System.currentTimeMillis() + attrExpirationPeriod;
2502         }
2503 
2504         return response.info.getFree();
2505     }
2506 
2507 /**
2508  * Creates a directory with the path specified by this
2509  * <code>SmbFile</code>. For this method to be successful, the target
2510  * must not already exist. This method will fail when
2511  * used with <code>smb://</code>, <code>smb://workgroup/</code>,
2512  * <code>smb://server/</code>, or <code>smb://server/share/</code> URLs
2513  * because workgroups, servers, and shares cannot be dynamically created
2514  * (although in the future it may be possible to create shares).
2515  *
2516  * @throws SmbException
2517  */
mkdir()2518     public void mkdir() throws SmbException {
2519         String path = getUncPath0();
2520 
2521         if( path.length() == 1 ) {
2522             throw new SmbException( "Invalid operation for workgroups, servers, or shares" );
2523         }
2524 
2525         /*
2526          * Create Directory Request / Response
2527          */
2528 
2529         if( log.level >= 3 )
2530             log.println( "mkdir: " + path );
2531 
2532         send( new SmbComCreateDirectory( path ), blank_resp() );
2533 
2534         attrExpiration = sizeExpiration = 0;
2535     }
2536 
2537 /**
2538  * Creates a directory with the path specified by this <tt>SmbFile</tt>
2539  * and any parent directories that do not exist. This method will fail
2540  * when used with <code>smb://</code>, <code>smb://workgroup/</code>,
2541  * <code>smb://server/</code>, or <code>smb://server/share/</code> URLs
2542  * because workgroups, servers, and shares cannot be dynamically created
2543  * (although in the future it may be possible to create shares).
2544  *
2545  * @throws SmbException
2546  */
mkdirs()2547     public void mkdirs() throws SmbException {
2548         SmbFile parent;
2549 
2550         try {
2551             parent = new SmbFile( getParent(), auth );
2552         } catch( IOException ioe ) {
2553             return;
2554         }
2555         if( parent.exists() == false ) {
2556             parent.mkdirs();
2557         }
2558         mkdir();
2559     }
2560 
2561 /**
2562  * Create a new file but fail if it already exists. The check for
2563  * existance of the file and it's creation are an atomic operation with
2564  * respect to other filesystem activities.
2565  */
createNewFile()2566     public void createNewFile() throws SmbException {
2567         if( getUncPath0().length() == 1 ) {
2568             throw new SmbException( "Invalid operation for workgroups, servers, or shares" );
2569         }
2570         close( open0( O_RDWR | O_CREAT | O_EXCL, 0, ATTR_NORMAL, 0 ), 0L );
2571     }
2572 
setPathInformation( int attrs, long ctime, long mtime )2573     void setPathInformation( int attrs, long ctime, long mtime ) throws SmbException {
2574         int f, dir;
2575 
2576         exists();
2577         dir = attributes & ATTR_DIRECTORY;
2578 
2579         f = open0( O_RDONLY, FILE_WRITE_ATTRIBUTES,
2580                 dir, dir != 0 ? 0x0001 : 0x0040 );
2581         send( new Trans2SetFileInformation( f, attrs | dir, ctime, mtime ),
2582                 new Trans2SetFileInformationResponse() );
2583         close( f, 0L );
2584 
2585         attrExpiration = 0;
2586     }
2587 
2588 /**
2589  * Set the create time of the file. The time is specified as milliseconds
2590  * from Jan 1, 1970 which is the same as that which is returned by the
2591  * <tt>createTime()</tt> method.
2592  * <p/>
2593  * This method does not apply to workgroups, servers, or shares.
2594  *
2595  * @param time the create time as milliseconds since Jan 1, 1970
2596  */
setCreateTime( long time )2597     public void setCreateTime( long time ) throws SmbException {
2598         if( getUncPath0().length() == 1 ) {
2599             throw new SmbException( "Invalid operation for workgroups, servers, or shares" );
2600         }
2601 
2602         setPathInformation( 0, time, 0L );
2603     }
2604 /**
2605  * Set the last modified time of the file. The time is specified as milliseconds
2606  * from Jan 1, 1970 which is the same as that which is returned by the
2607  * <tt>lastModified()</tt>, <tt>getLastModified()</tt>, and <tt>getDate()</tt> methods.
2608  * <p/>
2609  * This method does not apply to workgroups, servers, or shares.
2610  *
2611  * @param time the last modified time as milliseconds since Jan 1, 1970
2612  */
setLastModified( long time )2613     public void setLastModified( long time ) throws SmbException {
2614         if( getUncPath0().length() == 1 ) {
2615             throw new SmbException( "Invalid operation for workgroups, servers, or shares" );
2616         }
2617 
2618         setPathInformation( 0, 0L, time );
2619     }
2620 
2621 /**
2622  * Return the attributes of this file. Attributes are represented as a
2623  * bitset that must be masked with <tt>ATTR_*</tt> constants to determine
2624  * if they are set or unset. The value returned is suitable for use with
2625  * the <tt>setAttributes()</tt> method.
2626  *
2627  * @return the <tt>ATTR_*</tt> attributes associated with this file
2628  * @throws SmbException
2629  */
getAttributes()2630     public int getAttributes() throws SmbException {
2631         if( getUncPath0().length() == 1 ) {
2632             return 0;
2633         }
2634         exists();
2635         return attributes & ATTR_GET_MASK;
2636     }
2637 
2638 /**
2639  * Set the attributes of this file. Attributes are composed into a
2640  * bitset by bitwise ORing the <tt>ATTR_*</tt> constants. Setting the
2641  * value returned by <tt>getAttributes</tt> will result in both files
2642  * having the same attributes.
2643  * @throws SmbException
2644  */
setAttributes( int attrs )2645     public void setAttributes( int attrs ) throws SmbException {
2646         if( getUncPath0().length() == 1 ) {
2647             throw new SmbException( "Invalid operation for workgroups, servers, or shares" );
2648         }
2649         setPathInformation( attrs & ATTR_SET_MASK, 0L, 0L );
2650     }
2651 
2652 /**
2653  * Make this file read-only. This is shorthand for <tt>setAttributes(
2654  * getAttributes() | ATTR_READ_ONLY )</tt>.
2655  *
2656  * @throws SmbException
2657  */
setReadOnly()2658     public void setReadOnly() throws SmbException {
2659         setAttributes( getAttributes() | ATTR_READONLY );
2660     }
2661 
2662 /**
2663  * Turn off the read-only attribute of this file. This is shorthand for
2664  * <tt>setAttributes( getAttributes() & ~ATTR_READONLY )</tt>.
2665  *
2666  * @throws SmbException
2667  */
setReadWrite()2668     public void setReadWrite() throws SmbException {
2669         setAttributes( getAttributes() & ~ATTR_READONLY );
2670     }
2671 
2672 /**
2673  * Returns a {@link java.net.URL} for this <code>SmbFile</code>. The
2674  * <code>URL</code> may be used as any other <code>URL</code> might to
2675  * access an SMB resource. Currently only retrieving data and information
2676  * is supported (i.e. no <tt>doOutput</tt>).
2677  *
2678  * @deprecated Use getURL() instead
2679  * @return A new <code>{@link java.net.URL}</code> for this <code>SmbFile</code>
2680  * @throws MalformedURLException
2681  */
toURL()2682     public URL toURL() throws MalformedURLException {
2683         return url;
2684     }
2685 
2686 /**
2687  * Computes a hashCode for this file based on the URL string and IP
2688  * address if the server. The hashing function uses the hashcode of the
2689  * server address, the canonical representation of the URL, and does not
2690  * compare authentication information. In essance, two
2691  * <code>SmbFile</code> objects that refer to
2692  * the same file should generate the same hashcode provided it is possible
2693  * to make such a determination.
2694  *
2695  * @return  A hashcode for this abstract file
2696  * @throws SmbException
2697  */
2698 
hashCode()2699     public int hashCode() {
2700         int hash;
2701         try {
2702             hash = getAddress().hashCode();
2703         } catch( UnknownHostException uhe ) {
2704             hash = getServer().toUpperCase().hashCode();
2705         }
2706         getUncPath0();
2707         return hash + canon.toUpperCase().hashCode();
2708     }
2709 
pathNamesPossiblyEqual(String path1, String path2)2710     protected boolean pathNamesPossiblyEqual(String path1, String path2) {
2711         int p1, p2, l1, l2;
2712 
2713         // if unsure return this method returns true
2714 
2715         p1 = path1.lastIndexOf('/');
2716         p2 = path2.lastIndexOf('/');
2717         l1 = path1.length() - p1;
2718         l2 = path2.length() - p2;
2719 
2720         // anything with dots voids comparison
2721         if (l1 > 1 && path1.charAt(p1 + 1) == '.')
2722             return true;
2723         if (l2 > 1 && path2.charAt(p2 + 1) == '.')
2724             return true;
2725 
2726         return l1 == l2 && path1.regionMatches(true, p1, path2, p2, l1);
2727     }
2728 /**
2729  * Tests to see if two <code>SmbFile</code> objects are equal. Two
2730  * SmbFile objects are equal when they reference the same SMB
2731  * resource. More specifically, two <code>SmbFile</code> objects are
2732  * equals if their server IP addresses are equal and the canonicalized
2733  * representation of their URLs, minus authentication parameters, are
2734  * case insensitivly and lexographically equal.
2735  * <p/>
2736  * For example, assuming the server <code>angus</code> resolves to the
2737  * <code>192.168.1.15</code> IP address, the below URLs would result in
2738  * <code>SmbFile</code>s that are equal.
2739  *
2740  * <p><blockquote><pre>
2741  * smb://192.168.1.15/share/DIR/foo.txt
2742  * smb://angus/share/data/../dir/foo.txt
2743  * </pre></blockquote>
2744  *
2745  * @param   obj Another <code>SmbFile</code> object to compare for equality
2746  * @return  <code>true</code> if the two objects refer to the same SMB resource
2747  *          and <code>false</code> otherwise
2748  * @throws SmbException
2749  */
2750 
equals( Object obj )2751     public boolean equals( Object obj ) {
2752         if (obj instanceof SmbFile) {
2753             SmbFile f = (SmbFile)obj;
2754             boolean ret;
2755 
2756             if (this == f)
2757                 return true;
2758 
2759             /* If uncertain, pathNamesPossiblyEqual returns true.
2760              * Comparing canonical paths is definitive.
2761              */
2762             if (pathNamesPossiblyEqual(url.getPath(), f.url.getPath())) {
2763 
2764                 getUncPath0();
2765                 f.getUncPath0();
2766 
2767                 if (canon.equalsIgnoreCase(f.canon)) {
2768                     try {
2769                         ret = getAddress().equals(f.getAddress());
2770                     } catch( UnknownHostException uhe ) {
2771                         ret = getServer().equalsIgnoreCase(f.getServer());
2772                     }
2773                     return ret;
2774                 }
2775             }
2776         }
2777 
2778         return false;
2779     }
2780 /*
2781     public boolean equals( Object obj ) {
2782         return obj instanceof SmbFile && obj.hashCode() == hashCode();
2783     }
2784 */
2785 
2786 /**
2787  * Returns the string representation of this SmbFile object. This will
2788  * be the same as the URL used to construct this <code>SmbFile</code>.
2789  * This method will return the same value
2790  * as <code>getPath</code>.
2791  *
2792  * @return  The original URL representation of this SMB resource
2793  * @throws SmbException
2794  */
2795 
toString()2796     public String toString() {
2797         return url.toString();
2798     }
2799 
2800 /* URLConnection implementation */
2801 /**
2802  * This URLConnection method just returns the result of <tt>length()</tt>.
2803  *
2804  * @return the length of this file or 0 if it refers to a directory
2805  */
2806 
getContentLength()2807     public int getContentLength() {
2808         try {
2809             return (int)(length() & 0xFFFFFFFFL);
2810         } catch( SmbException se ) {
2811         }
2812         return 0;
2813     }
2814 
2815 /**
2816  * This URLConnection method just returns the result of <tt>lastModified</tt>.
2817  *
2818  * @return the last modified data as milliseconds since Jan 1, 1970
2819  */
getDate()2820     public long getDate() {
2821         try {
2822             return lastModified();
2823         } catch( SmbException se ) {
2824         }
2825         return 0L;
2826     }
2827 
2828 /**
2829  * This URLConnection method just returns the result of <tt>lastModified</tt>.
2830  *
2831  * @return the last modified data as milliseconds since Jan 1, 1970
2832  */
getLastModified()2833     public long getLastModified() {
2834         try {
2835             return lastModified();
2836         } catch( SmbException se ) {
2837         }
2838         return 0L;
2839     }
2840 
2841 /**
2842  * This URLConnection method just returns a new <tt>SmbFileInputStream</tt> created with this file.
2843  *
2844  * @throws IOException thrown by <tt>SmbFileInputStream</tt> constructor
2845  */
getInputStream()2846     public InputStream getInputStream() throws IOException {
2847         return new SmbFileInputStream( this );
2848     }
2849 
2850 /**
2851  * This URLConnection method just returns a new <tt>SmbFileOutputStream</tt> created with this file.
2852  *
2853  * @throws IOException thrown by <tt>SmbFileOutputStream</tt> constructor
2854  */
getOutputStream()2855     public OutputStream getOutputStream() throws IOException {
2856         return new SmbFileOutputStream( this );
2857     }
2858 
processAces(ACE[] aces, boolean resolveSids)2859     private void processAces(ACE[] aces, boolean resolveSids) throws IOException {
2860         String server = getServerWithDfs();
2861         int ai;
2862 
2863         if (resolveSids) {
2864             SID[] sids = new SID[aces.length];
2865             String[] names = null;
2866 
2867             for (ai = 0; ai < aces.length; ai++) {
2868                 sids[ai] = aces[ai].sid;
2869             }
2870 
2871             for (int off = 0; off < sids.length; off += 64) {
2872                 int len = sids.length - off;
2873                 if (len > 64)
2874                     len = 64;
2875                 SID.resolveSids(server, auth, sids, off, len);
2876             }
2877         } else {
2878             for (ai = 0; ai < aces.length; ai++) {
2879                 aces[ai].sid.origin_server = server;
2880                 aces[ai].sid.origin_auth = auth;
2881             }
2882         }
2883     }
2884 /**
2885  * Return an array of Access Control Entry (ACE) objects representing
2886  * the security descriptor associated with this file or directory.
2887  * If no DACL is present, null is returned. If the DACL is empty, an array with 0 elements is returned.
2888  * @param resolveSids Attempt to resolve the SIDs within each ACE form
2889  * their numeric representation to their corresponding account names.
2890  */
getSecurity(boolean resolveSids)2891     public ACE[] getSecurity(boolean resolveSids) throws IOException {
2892         int f;
2893         ACE[] aces;
2894 
2895         f = open0( O_RDONLY, READ_CONTROL, 0, isDirectory() ? 1 : 0 );
2896 
2897         /*
2898          * NtTrans Query Security Desc Request / Response
2899          */
2900 
2901         NtTransQuerySecurityDesc request = new NtTransQuerySecurityDesc( f, 0x04 );
2902         NtTransQuerySecurityDescResponse response = new NtTransQuerySecurityDescResponse();
2903 
2904         try {
2905             send( request, response );
2906         } finally {
2907             close( f, 0L );
2908         }
2909 
2910         aces = response.securityDescriptor.aces;
2911         if (aces != null)
2912             processAces(aces, resolveSids);
2913 
2914         return aces;
2915     }
2916 /**
2917  * Return an array of Access Control Entry (ACE) objects representing
2918  * the share permissions on the share exporting this file or directory.
2919  * If no DACL is present, null is returned. If the DACL is empty, an array with 0 elements is returned.
2920  * <p>
2921  * Note that this is different from calling <tt>getSecurity</tt> on a
2922  * share. There are actually two different ACLs for shares - the ACL on
2923  * the share and the ACL on the folder being shared.
2924  * Go to <i>Computer Management</i>
2925  * &gt; <i>System Tools</i> &gt; <i>Shared Folders</i> &gt <i>Shares</i> and
2926  * look at the <i>Properties</i> for a share. You will see two tabs - one
2927  * for "Share Permissions" and another for "Security". These correspond to
2928  * the ACLs returned by <tt>getShareSecurity</tt> and <tt>getSecurity</tt>
2929  * respectively.
2930  * @param resolveSids Attempt to resolve the SIDs within each ACE form
2931  * their numeric representation to their corresponding account names.
2932  */
getShareSecurity(boolean resolveSids)2933     public ACE[] getShareSecurity(boolean resolveSids) throws IOException {
2934         String p = url.getPath();
2935         MsrpcShareGetInfo rpc;
2936         DcerpcHandle handle;
2937         ACE[] aces;
2938 
2939         resolveDfs(null);
2940         String server = getServerWithDfs();
2941 
2942         rpc = new MsrpcShareGetInfo(server, tree.share);
2943         handle = DcerpcHandle.getHandle("ncacn_np:" + server + "[\\PIPE\\srvsvc]", auth);
2944 
2945         try {
2946             handle.sendrecv(rpc);
2947             if (rpc.retval != 0)
2948                 throw new SmbException(rpc.retval, true);
2949             aces = rpc.getSecurity();
2950             if (aces != null)
2951                 processAces(aces, resolveSids);
2952         } finally {
2953             try {
2954                 handle.close();
2955             } catch(IOException ioe) {
2956                 if (log.level >= 1)
2957                     ioe.printStackTrace(log);
2958             }
2959         }
2960 
2961         return aces;
2962     }
2963 /**
2964  * Return an array of Access Control Entry (ACE) objects representing
2965  * the security descriptor associated with this file or directory.
2966  * <p>
2967  * Initially, the SIDs within each ACE will not be resolved however when
2968  * <tt>getType()</tt>, <tt>getDomainName()</tt>, <tt>getAccountName()</tt>,
2969  * or <tt>toString()</tt> is called, the names will attempt to be
2970  * resolved. If the names cannot be resolved (e.g. due to temporary
2971  * network failure), the said methods will return default values (usually
2972  * <tt>S-X-Y-Z</tt> strings of fragments of).
2973  * <p>
2974  * Alternatively <tt>getSecurity(true)</tt> may be used to resolve all
2975  * SIDs together and detect network failures.
2976  */
getSecurity()2977     public ACE[] getSecurity() throws IOException {
2978         return getSecurity(false);
2979     }
2980 
2981 }
2982