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/ <-- 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 <-- 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/ <-- 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 * > <i>System Tools</i> > <i>Shared Folders</i> > <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