From b34677b051641beec75ded27d8479cd2c741d3ff Mon Sep 17 00:00:00 2001 From: Felix Schumacher Date: Wed, 6 Aug 2008 16:14:00 +0200 Subject: [PATCH] jcifs-0.8.0 from tgz Tue Feb 10 23:44:45 EST 2004 jcifs-0.8.0 released This completes the 0.8 beta cycle. There is no technical difference between this release and 0.8.0b1. Mon Nov 24 04:06:55 EST 2003 jcifs-0.8.0b1 released Some work on NetworkExplorer and DFS referrals has been performed but it still doesn't work right. Currently if a DFS referral is encountered the DfsReferral will be thrown into NetworkExplorer which then sends an HTTP redirect. The problem is that when the SmbTransports close after being idle for jcifs.smb.client.soTimeout, attempting to access servers with NPAs in the session can cause a succession of authentication failures which on my network equates to your account being locked (like mine is right now :) An artifact of this work is that NetworkExplorer will store the NtlmPasswordAuthentication objects in the HttpSession as npa-servername where 'servername' is the name of the server that supplied the challenge. And all of the fixes to the 0.7 series since 0.7.15 are included: It was discovered that SmbComNTCreateAndX as well as SmbComReadAndx commands did not calculate MAC response signitures properly. In one case a field was not properly decoded and in the case of reading the payload, which is read directly from the stream into the supplied buffer as an optimization, was not being properly factored into the signature. These issues have been fixed. Only SMBs that follow authentication need to be actually signed if SMB signing is enabled. Because it was assumed that SMBs would follow authentication an Exception was coded to be thrown if password hashes are determined to be inadiquate to generate a MAC signing key. However because the NTLM HTTP Filter does not send additional SMBs, signing will never actually occur. The Exception is only generated if the password hashes are "externel" (meaning from the NTLM HTTP Filter) but this is precisely the case where signing will never occur. Therefore, the Exception coded to detect external password hashes has been removed so that additional SMBs will generate a signing error but the NTLM HTTP Filter will be permitted to proceed without error. The NtlmPasswordAuthencation class has also been made Serializable to permit certain containers to serialize the state of an HTTP session. It is not known however if the client will seamlessly re-authenticate when the NPA is de-serialized and discovered to be invalid. Regardless, the Filter will now work with these containers albeit possibly not to their greatest potential. JCIFS will now suppress the harmess "exception reading from socket input" message being written to the log. Specifically when using the NTLM HTTP Filter under load jCIFS would periodically write exceptions to the log like the following: Dec 19 10:45:14.474 - exception reading from socket input: IKOO635<1B>/172.81.13.154 java.net.SocketException: Connection reset at java.net.SocketInputStream.read(SocketInputStream.java:168) ... This was occuring because the domain controller is periodically closing the socket to the server. This is harmless. JCIFS will automatically reconnect and proceed as usual. If NT SMBs are not negotiated jCIFS will now use SMB_COM_WRITE as opposed to SMB_COM_WRITE_ANDX. This was done to support NetWare 6 which apparently does not implement this ANDX command. jcifs-0.8.0b released This is the first beta release of the 0.8 series. There have been significant additions to this package. The most noteable is Distributed FileSystem (DFS) support. This client fully supports DFS with deep paths, multiple redirects, you name it. This functionality required pervasive changes so DFS users should proceed with caution and report any problems to the mailing list. Support for setting file attributes has been added as is the long awaited RandomAccessFile. o DFS - DFS support is thorough. All DFS operations are completely transparent. It Just Works. The client can be redirected an infinate number of times. It will reuse mappings established by other callers. Components of a path that do not fall within the DFS space and deep paths are handled properly. Also, note the new getDfsPath method can be used to determine if a file or directory falls within a DFS space. This support required pervasive changes so proceed with caution and run your unit tests with files that span DFS volumes. Please report any problems to the jCIFS mailing list. o Random Access Files - The SmbRandomAccessFile class has been added. All features of the analygous java.io.RandomAccessFile class are supported and should function as a drop in replacement. Notice it is now possible to set the size of a file with the SmbRandomAccessFile.setLength() method. o File Filters - Support for SmbFilenameFilter and SmbFileFilter is complete. Because CIFS servers can accept attributes and a wildcard expression for filtering entries on the server side, a DosFileFilter is provided that will accept these parameters and pass them to the server. The DosFileFilter can also be extended to create highly sophisticated and efficient file filters. Because of this work the list() and listFiles methods have been refactored and actually reduced in size. o Complete copyTo - The SmbFile.copyTo operation will now copy all file attributes as well as create and last write times. Directories copied using this method should be indistinquishable from the original minus ACLs. This method can now copy entire shares whereas previously only sud-directories could be specified. o Setting Attributes and Times - It is now possible to set file attribites, the create time, and last write time in a variety of ways. The new methods are setCreateTime, setLastModified, getAttributes, setAttributes, setReadOnly, and setReadWrite. o Complete Delete - The SmbFile.delete() method will now completely delete directories regardless of wheather or not there are read-only files in them. o The createNewFile Method - An SmbFile.createFile method has been added. --- CHANGES.txt | 98 ++- README.txt | 365 +++++++++- build.xml | 18 +- examples/Append.java | 3 +- examples/AuthListFiles.java | 32 +- examples/Break.java | 29 - examples/CopyTo5.java | 34 - examples/CreateFile.java | 2 +- examples/Delete.java | 3 +- examples/Exists.java | 1 + examples/FileInfo.java | 119 +++ examples/FilterFiles.java | 37 + examples/GetDfsPath.java | 11 + examples/GetURL.java | 2 - examples/HttpURL.java | 11 + examples/Makefile | 14 + examples/SetAttrs.java | 18 + examples/SetTime.java | 12 + examples/SmbTableFile.java | 128 ++++ examples/SmbTableFileRecord.java | 25 + examples/TestRandomAccess.java | 121 ++++ examples/Torture2.java | 15 +- examples/pipes/callnp.exe | Bin 32768 -> 0 bytes examples/pipes/createf.exe | Bin 36864 -> 0 bytes examples/pipes/createnp.exe | Bin 32768 -> 0 bytes examples/runtests.sh | 57 ++ src/jcifs/http/NetworkExplorer.java | 216 ++++-- src/jcifs/smb/DfsReferral.java | 38 + src/jcifs/smb/DosFileFilter.java | 40 + src/jcifs/smb/FileEntry.java | 11 + src/jcifs/smb/Info.java | 1 + src/jcifs/smb/NetServerEnum2Response.java | 47 +- src/jcifs/smb/NetShareEnumResponse.java | 49 +- src/jcifs/smb/ServerMessageBlock.java | 30 +- src/jcifs/smb/SmbAuthException.java | 3 + src/jcifs/smb/SmbComClose.java | 2 +- src/jcifs/smb/SmbComCreateDirectory.java | 8 +- src/jcifs/smb/SmbComDelete.java | 2 +- src/jcifs/smb/SmbComDeleteDirectory.java | 8 +- src/jcifs/smb/SmbComNTCreateAndX.java | 19 +- src/jcifs/smb/SmbComNTCreateAndXResponse.java | 4 +- src/jcifs/smb/SmbComOpenAndX.java | 12 +- src/jcifs/smb/SmbComQueryInformation.java | 8 +- src/jcifs/smb/SmbComQueryInformationResponse.java | 6 +- src/jcifs/smb/SmbComTransaction.java | 8 + src/jcifs/smb/SmbComTransactionResponse.java | 5 + src/jcifs/smb/SmbComTreeConnectAndXResponse.java | 6 +- src/jcifs/smb/SmbException.java | 17 +- src/jcifs/smb/SmbFile.java | 804 +++++++++++++-------- src/jcifs/smb/SmbFileFilter.java | 23 + src/jcifs/smb/SmbFileInputStream.java | 6 +- src/jcifs/smb/SmbFileOutputStream.java | 4 +- src/jcifs/smb/SmbFilenameFilter.java | 23 + src/jcifs/smb/SmbRandomAccessFile.java | 337 +++++++++ src/jcifs/smb/SmbSession.java | 10 +- src/jcifs/smb/SmbTransport.java | 114 ++- src/jcifs/smb/SmbTree.java | 20 +- src/jcifs/smb/Trans2FindFirst2.java | 19 +- src/jcifs/smb/Trans2FindFirst2Response.java | 82 ++- src/jcifs/smb/Trans2FindNext2.java | 7 + src/jcifs/smb/Trans2GetDfsReferral.java | 66 ++ src/jcifs/smb/Trans2GetDfsReferralResponse.java | 139 ++++ src/jcifs/smb/Trans2QueryPathInformation.java | 7 +- .../smb/Trans2QueryPathInformationResponse.java | 14 +- src/jcifs/smb/Trans2SetFileInformation.java | 89 +++ .../smb/Trans2SetFileInformationResponse.java | 49 ++ src/jcifs/smb/TransactNamedPipeOutputStream.java | 2 +- src/jcifs/util/Encdec.java | 302 ++++++++ 68 files changed, 3154 insertions(+), 658 deletions(-) delete mode 100644 examples/Break.java delete mode 100644 examples/CopyTo5.java create mode 100644 examples/FileInfo.java create mode 100644 examples/FilterFiles.java create mode 100644 examples/GetDfsPath.java create mode 100644 examples/HttpURL.java create mode 100644 examples/Makefile create mode 100644 examples/SetAttrs.java create mode 100644 examples/SetTime.java create mode 100644 examples/SmbTableFile.java create mode 100644 examples/SmbTableFileRecord.java create mode 100644 examples/TestRandomAccess.java delete mode 100644 examples/pipes/callnp.exe delete mode 100644 examples/pipes/createf.exe delete mode 100644 examples/pipes/createnp.exe create mode 100644 examples/runtests.sh create mode 100644 src/jcifs/smb/DfsReferral.java create mode 100644 src/jcifs/smb/DosFileFilter.java create mode 100644 src/jcifs/smb/FileEntry.java create mode 100644 src/jcifs/smb/SmbFileFilter.java create mode 100644 src/jcifs/smb/SmbFilenameFilter.java create mode 100644 src/jcifs/smb/SmbRandomAccessFile.java create mode 100644 src/jcifs/smb/Trans2GetDfsReferral.java create mode 100644 src/jcifs/smb/Trans2GetDfsReferralResponse.java create mode 100644 src/jcifs/smb/Trans2SetFileInformation.java create mode 100644 src/jcifs/smb/Trans2SetFileInformationResponse.java create mode 100644 src/jcifs/util/Encdec.java diff --git a/CHANGES.txt b/CHANGES.txt index e3efec8..0e6aafb 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,5 +1,25 @@ -Mon Jan 19 22:06:54 EST 2004 -jcifs-0.7.19 released +Tue Feb 10 23:44:45 EST 2004 +jcifs-0.8.0 released + +This completes the 0.8 beta cycle. There is no technical difference between +this release and 0.8.0b1. + +Mon Nov 24 04:06:55 EST 2003 +jcifs-0.8.0b1 released + +Some work on NetworkExplorer and DFS referrals has been performed but it +still doesn't work right. Currently if a DFS referral is encountered the +DfsReferral will be thrown into NetworkExplorer which then sends an HTTP +redirect. The problem is that when the SmbTransports close after being idle +for jcifs.smb.client.soTimeout, attempting to access servers with NPAs in +the session can cause a succession of authentication failures which on my +network equates to your account being locked (like mine is right now :) + +An artifact of this work is that NetworkExplorer will store the +NtlmPasswordAuthentication objects in the HttpSession as npa-servername +where 'servername' is the name of the server that supplied the challenge. + +And all of the fixes to the 0.7 series since 0.7.15 are included: It was discovered that SmbComNTCreateAndX as well as SmbComReadAndx commands did not calculate MAC response signitures properly. In one case a @@ -8,9 +28,6 @@ which is read directly from the stream into the supplied buffer as an optimization, was not being properly factored into the signature. These issues have been fixed. -Wed Jan 7 19:24:59 EST 2004 -jcifs-0.7.18 released - Only SMBs that follow authentication need to be actually signed if SMB signing is enabled. Because it was assumed that SMBs would follow authentication an Exception was coded to be thrown if password hashes are @@ -30,10 +47,6 @@ NPA is de-serialized and discovered to be invalid. Regardless, the Filter will now work with these containers albeit possibly not to their greatest potential. - -Tue Dec 23 03:43:15 EST 2003 -jcifs-0.7.17 released - JCIFS will now suppress the harmess "exception reading from socket input" message being written to the log. Specifically when using the NTLM HTTP Filter under load jCIFS would periodically write exceptions to the log like @@ -48,13 +61,64 @@ This was occuring because the domain controller is periodically closing the socket to the server. This is harmless. JCIFS will automatically reconnect and proceed as usual. -Dec 9 18:13:25 EST 2003 -jcifs-0.7.16 released - If NT SMBs are not negotiated jCIFS will now use SMB_COM_WRITE as opposed to SMB_COM_WRITE_ANDX. This was done to support NetWare 6 which apparently does not implement this ANDX command. +jcifs-0.8.0b released + +This is the first beta release of the 0.8 series. There have been +significant additions to this package. The most noteable is Distributed +FileSystem (DFS) support. This client fully supports DFS with deep paths, +multiple redirects, you name it. This functionality required pervasive +changes so DFS users should proceed with caution and report any problems to +the mailing list. Support for setting file attributes has been added as is +the long awaited RandomAccessFile. + +o DFS - DFS support is thorough. All DFS operations are completely + transparent. It Just Works. The client can be redirected an infinate + number of times. It will reuse mappings established by other callers. + Components of a path that do not fall within the DFS space and deep paths + are handled properly. Also, note the new getDfsPath method can + be used to determine if a file or directory falls within a DFS space. + This support required pervasive changes so proceed with caution and run + your unit tests with files that span DFS volumes. Please report any + problems to the jCIFS mailing list. + +o Random Access Files - The SmbRandomAccessFile class has been + added. All features of the analygous java.io.RandomAccessFile + class are supported and should function as a drop in replacement. Notice + it is now possible to set the size of a file with the + SmbRandomAccessFile.setLength() method. + +o File Filters - Support for SmbFilenameFilter and + SmbFileFilter is complete. Because CIFS servers can accept + attributes and a wildcard expression for filtering entries on the server + side, a DosFileFilter is provided that will accept these + parameters and pass them to the server. The DosFileFilter can + also be extended to create highly sophisticated and efficient file + filters. Because of this work the list() and listFiles + methods have been refactored and actually reduced in size. + +o Complete copyTo - The SmbFile.copyTo operation will now copy all + file attributes as well as create and last write times. Directories + copied using this method should be indistinquishable from the original + minus ACLs. This method can now copy entire shares whereas previously + only sud-directories could be specified. + +o Setting Attributes and Times - It is now possible to set file attribites, + the create time, and last write time in a variety of ways. The new + methods are setCreateTime, setLastModified, + getAttributes, setAttributes, setReadOnly, and + setReadWrite. + +o Complete Delete - The SmbFile.delete() method will now + completely delete directories regardless of wheather or not there are + read-only files in them. + +o The createNewFile Method - An SmbFile.createFile method has been + added. + Thu Oct 23 01:18:29 EDT 2003 jcifs-0.7.15 released @@ -96,6 +160,8 @@ ArrayIndexOutOfBounds exceptions. These entries are now ignored. Wed Sep 17 21:21:31 EDT 2003 +jcifs-0.7.13 released + JCIFS now supports SMB signing. If a server requires SMB signing (Windows 2003 requires it by default) or if the server supports signing and the jcifs.smb.client.signingPreferred property is true signing will be @@ -107,6 +173,8 @@ comments for hashCode() and equals(). Mon Sep 1 19:04:11 EDT 2003 +jcifs-0.7.12 released + CIFS permits multiple users to be logged in over the same transport (socket). JCIFS takes advantage of this and reuses existing transports whenever possible. It was reported that a user using the NTLM Servlet @@ -117,6 +185,8 @@ jcifs.smb.client.ssnLimit propery is exceeded (default 100). Thu Jul 10 22:07:09 EDT 2003 +jcifs-0.7.11 released + Support for LMv2 authentication has been added. See the Overview page in the API documentation regarding the jcifs.smb.lmCompatibility property. Some additonal "cleanup" has also been performed. One side-effect that @@ -127,6 +197,8 @@ operation. Create an NtlmPasswordAuthentication object instead. Thu Jul 3 20:59:25 EDT 2003 +jcifs-0.7.10 released + There have been two small bug fixes in the SMB layer; the count field in the SMB_COM_WRITE_ANDX response was being ignored. Apparently the count specified in the request is always honored or we would have seen plenty of @@ -148,6 +220,8 @@ documentation detailing his analysis of the NTLM protocol: Thu Jun 12 00:18:05 EDT 2003 +jcifs-0.7.9 released + It was discovered that if a server responds to the NBT session setup but not to the SMB_COM_NEGOTIATE request the client will hang indefinately. This is not a likely thing to occur because servers usually respond or not diff --git a/README.txt b/README.txt index 99b8927..fd30bf5 100644 --- a/README.txt +++ b/README.txt @@ -1,27 +1,358 @@ -Mon Jan 19 22:06:54 EST 2004 -jcifs-0.7.19 released +Tue Feb 10 23:44:45 EST 2004 +jcifs-0.8.0 released -SMB signature varification was not working properly and has been fixed. +This completes the 0.8 beta cycle. There is no technical difference between +this release and 0.8.0b1. -Wed Jan 7 19:24:59 EST 2004 -jcifs-0.7.18 released +Tue Feb 3 00:54:04 EST 2004 +jcifs-0.8.0b1 released -The NTLM HTTP Authentication Filter should now work with domain controllers -that negotiate signing as well as without generating the benign "exception -reading from socket input" Exception. Also, the NtlmPasswordAuthentication -class is now Serializable to increase compatability with certain Servlet -containers. +There has been some work in trying to get NetworkExplorer to work with DFS +and all fixes from the 0.7 series since 0.7.15 have been incorporated. -Tue Dec 23 03:43:15 EST 2003 -jcifs-0.7.17 released +Mon Nov 24 04:06:55 EST 2003 -JCIFS will now suppress the harmess "exception reading from socket input" -message being written to the log. +jcifs-0.8.0b released -Tue Dec 9 18:13:25 EST 2003 -jcifs-0.7.16 released +This is the first beta release of the 0.8 series. There have been +significant additions to this package including DFS support, random access +files, filename and file filters, setting attributes and times, and copyTo +and delete improvements. -An issue regarding writing to NetWare 6 servers has been resolved. +Thu Oct 23 01:18:29 EDT 2003 + +jcifs-0.7.15 released + +The NTLM HTTP Authentication behavoir has been modified to permit load +balancing between many domain controllers. The jcifs.smb.client.ssnLimit +property default value has also been changed to 250 from 100. + +Mon Oct 6 23:53:28 EDT 2003 + +jcifs-0.7.14 released + +Eric's latest signing patch has been merged, a few adjustments have been +made to eliminate a concurrency issue, and a fix for browse servers that +return zero length server names has been incorporated. + +Wed Sep 17 21:21:31 EDT 2003 + +SMB signing is now supported and there have been some adjustments to +SmbFile.hashCode() and SmbFile.equals() + +Mon Sep 1 19:18:52 EDT 2003 + +Added code to logoff 1/10th of all sessions over a transport if +jcifs.smb.client.ssnLimit is reached (fix for ERRSVR/90 errors). + +Thu Jul 10 22:07:09 EDT 2003 + +Support for LMv2 authentication has been added. + +Thu Jul 3 20:59:25 EDT 2003 + +There have been minor bug fixes in the NTLM code and SMB layer as well as +documentation updates. + +Thu Jun 12 00:22:05 EDT 2003 + +jcifs-0.7.9 released + +A bug that could cause a connection to hang indefinately has been fixed and +some NTLM HTTP authentication tweeking has been applied. + +Wed May 28 19:09:17 EDT 2003 + +jcifs-0.7.8 released + +A bug in the new ntlm http client code that would cause it to hang for ~60 +seconds before retreiving the target document has been fixed. + +Tue May 27 21:40:58 EDT 2003 + +jcifs-0.7.7 released + +A deadlock has been identified and fixed, NTLM authentication for HTTP and +HTTPS clients has been added (the inverse of the NtlmHttpFilter), the +SmbFileInputStream.skip() method will skip without reading, and there have +two other small fixes. + +Wed Apr 16 22:46:07 EDT 2003 + +jcifs-0.7.6 released + +The isDirectory method has been changed to return false if the target does +not exist. Previously this would return true which is inconsistent with +java.io.File. + +Wed Apr 2 23:56:26 EST 2003 + +jcifs-0.7.5 released + +More refinement to the NTLM HTTP authentication code has been applied. +There is also a fix for listing hosts from Windows ME/98/95 local master +browsers. + +Wed Mar 26 19:17:24 EST 2003 + +jcifs-0.7.4 released + +Some NtlmHttpFilter issues were reported by several users of Win98 clients. +Eric has provided a new NtlmSsp.java that works correctly with these +clients. + +Wed Feb 12 01:23:02 EST 2003 + +A security issue regarding the SmbSession.logon() method used by NTLM HTTP +Authentication has been fixed and a modification has been made to trigger +MSIE to redisplay the Network Password dialog repeatedly until correct +credentials are supplied. The change was made in the core jcifs.smb package +however so test this in your dev environments. + +Wed Feb 5 00:41:32 EST 2003 + +The jcifs-0.7.0 and 0.7.1 releases will incorrectly throw an +ArrayIndexOutOfBounds exception if a write of 1501 to 1504 is performed. +This release fixes that bug. You may also set jcifs.smb.client.snd_buf_size += 1300 as a temporary work-around. It is also now possible to specify a +"ShareAccess" with SmbFileOutputStream and SmbFile to restrict other +processes from accessing files jCIFS opens. Also, SmbFileOutputStream now +supports writing files larger than 4GB. + +Wed Jan 15 22:34:14 EST 2003 + +jcifs-0.7.1 released + +Three bugs have been fixed regarding filenames with '#' signs in them, the +getInputStream(), getLastModified(), getDate(), and getContentLength() +methods of the URLConnection implementation, and isExists() incorrectly +returning false. + +Thu Jan 9 00:06:23 EST 2003 + +jcifs-0.7.0 released + +This is the 0.7.0 release of jCIFS, the Java SMB client. There is new +functionality specifically targeting Web applications as well as some +critical SMB URL fixes and feature improvements: + +o The new jcifs.http package is very handy when building Web applications + that need to integrate with MS corporate intranets. Adding a hyperlink to + a document on a network drive is easy with Network Explorer (pictured + right) and will transparently negotiate credentials with MSIE to browse + SMB resources (really, no password dialog necessary). This package also + boasts an NTLM HTTP Authentication servlet Filter for authenticating MSIE + clients. These are useful features because many of the tasks surrounding + user management now fall back to computer support and HR. It is not + necessary to add and remove users as they join and leave the company. + Perhaps most important from the user's perspective; they do not need to + enter a username or password if their workstation is a member of an NTLM + domain. The password hashes generated when they logged on to their + workstation will be negotiated during the initial request for a session, + passed through jCIFS, and validated against a PDC or BDC. This also makes + the user's domain, username, and password available for managing session + information, profiles, preferences, etc. + +o The functionality used to authenticate and manage arbitrary creadentials + has been exposed in the SmbSession and NtlmPasswordAuthentication + classes. This permits NTLM password authentication to be integrated into + applications without requiring Microsoft components or JNI. + +o With the introduction of the jcifs.encoding property the client is now + truely internationalized. It has been used successfully on OS/390 with + native EBCDIC character encoding. See the Sun JRE documentation for a + list of supported encodings. + +o The URL issues remaining in the 0.6 series have been fixed in 0.7 by + converting all SMB URL handling internally to use the java.net.URL class + (Note: directories must now have a trailing slash and Java 1.3 is + required). Also a deadlock in the name service code was identified and + fixed (repaired in 0.6.8 as well). + +o A copyTo() method has been added to SmbFile that is very fast at copying + large directories across hosts. + +o There have been numerous other changes including the mkdirs() and + getType() methods, plain text password support (disabled by default), the + available() method and close() fix for Named Pipes, reading files larger + than 4 GB, the NtlmAuthenticator class, and more. + +All documentation has been completely reviewed and updated. + +--8<-- + + jCIFS + Common Internet File System Client in 100% Java + http://jcifs.samba.org + + +This is the jCIFS SMB client library written in Java. In short, it will +enable Java applications to remotely access shared directories on SMB file +servers(i.e. a Microsoft Windows "share"). It is a fairly religious +implementation of the CIFS specification supporting Unicode, batching, +multiplexing, encrypted authentication, transactions, named pipes, +domain/workgroup/server/share/directory/file enumeration, RAPs and more. It +is licensed under LGPL which means commercial organizations can +legitimately use it with their propertietary code (you just can't modify +the library itself without providing the source for those changes to it's +users, see the LGPL for details). + +REQUIREMENTS: + +Java 1.3 or above - http://java.sun.com/products/ + +INSTALLATION: + +Just add the jar file to you classpath as you would with any other jar. +More specifically: + +UNIX: + +Go to http://jcifs.samba.org and download the latest jar. If you download +the tgz archive you also get the source code and javadoc API documentation +(see critical properties discussed on the Overview page). Put it someplace +reasonable and extract it. For example: + + $ gunzip jcifs-0.7.0.tgz + $ tar -xvf jcifs-0.7.0.tar + +Add the jar to your classpath. There are two ways to do this. One is to +explicitly set it on the command line when you run your application like: + + $ java -cp myjars/jcifs-0.7.0.jar MyApplication + +but a more robust solution is to export it in your ~/.profile or +~/.bash_profile like: + + CLASSPATH=$CLASSPATH:/home/produser/myapp/myjars/jcifs-0.7.0.jar + export CLASSPATH + +WINDOWS: + +Go to http://jcifs.samba.org and download the latest jar. If you download +the zip archive you also get the source code and javadoc API documentation +(see critical properties discussed on the Overview page). Put it someplace +reasonable and extract it with something like Winzip. + +Add the jar to your classpath. There are two ways to do this. One is to +explicitly set it on the command line when you run your application like: + + C:\> java -cp myjars\jcifs-0.7.0.jar MyApplication + +but a more robust solution would be to change your system environment but +I'm not confident I can tell you accurately how to do that. + +It is also common that the CLASSPATH be specified in a shell script or +batch file. See the build.bat batch file that runs the Ant build tool as an +example. + +USING JCIFS: + +In general the public API is extremely simple. The jcifs.smb.SmbFile, +jcifs.smb.SmbFileInputStream, and jcifs.smb.SmbFileOutputStream classes are +analogous to the java.io.File, java.io.FileInputStream, and +java.io.FileOutputStream classes so if you know how to use those it should +be obvious how to use jCIFS provided you set any necessary properties(such +as WINS) and understand the smb:// URL syntax. + +Here's an example to retrieve a file: + + import jcifs.smb.*; + + jcifs.Config.setProperty( "wins", "192.168.1.230" ); + SmbFileInputStream in = new SmbFileInputStream( + "smb://username:password@host/c/My Documents/report.txt" ); + byte[] b = new byte[8192]; + int n; + while(( n = in.read( b )) > 0 ) { + System.out.write( b, 0, n ); + } + +You can also write, rename, list contents of a directory, enumerate shares, +communicate with Win32 Named Pipe Servers, ...etc. + +The protocol handler for java.net.URL is also in place which means you +retrieve files using the URL class as you would with other protocols. For +example: + + jcifs.Config.registerSmbURLHandler(); //ensure protocol handler is loaded + URL url = new URL( "smb://user:pass@host/share/dir/file.doc" ); + InputStream in = url.openStream(); + +There are many example programs in the jcifs_0.7.0/examples/ directory. To +execute the Put example you might do: + + $ java -cp examples:jcifs-0.7.0.jar -Djcifs.properties=jcifs.prp \ + Put smb://usr:pass@host/share/dir/file.doc + ########## + 582K transfered + +This will also work with whatever else uses the URL class internally. For +example if you use RMI you can serve class files from an SMB share and use +the codebase property: + + -Djava.rmi.server.codebase=smb://mymachine/c/download/myapp.jar + +See the extensive API documentation included in the distribution. + +BUILDING JCIFS FROM SOURCE: + +If you wish to modify and/or build the jCIFS source simply download the +ant.tgz or ant.zip available at http://jcifs.samba.org and extract it in +the jcifs_0.7.0 directory. Edit the build.sh or build.bat file to +appropriately reflect the location of the JDK on your system and bootstrap +the Ant build tool. Now type: + + $ ./build.sh + +You will be presented with a list of target arguments. The build file is a +standard Ant build.xml config. You may also integrate the package into your +existing Ant installation or get the latest version of Ant here: + + http://jakarta.apache.org/ant/ + +WHAT IS SMB AND CIFS? + +Server Message Block (SMB) is an application layer networking protocol for +file and print sharing. It is the de-facto networking protocol for +Microsoft Windows platforms. The Common Internet File System (CIFS) is the +more generic name for all that encompasses the protocol and its many +layers. Collectively this is the networking protocol used when you "Map +Network Drive...", issue "net use * \\server\share" commands, use smbclient +on UNIX, smbfs on Linux, Sharity, OS2, server vendors such as Network +Appliance, EMC, and Novell NetWare. + +WHY DO YOU NEED JCIFS? + +This client is 100% Java and will work the same in a variety of +environments from Palm Pilots and applets to any UNIX that supports Java. +Naturally you can choose to run your applications on a platform that +supports mapping or mounting remote volumes into the local filesystem but +this assumes you know what shares you are accessing in advance and what +platform your application is running on(do you use /mnt/pnt or H:\dir). The +latency involed makes this approach unsatisfactory for certain applications +(e.g. Web Gateway). Using locally mapped filesystems is also not portable, +unstable due to unnecessary dependencies, and more difficult to manage. The +JCIFS infrastructure is also highly extensible. If there is a demand it +will include a great deal of additional functionality not available through +a filesystem API such as printing, RPC, NT file change notification, etc. + +ACKNOWLEDGEMENTS + +Special thanks to the Samba organization and Christopher R. Hertel for +starting the JCIFS project. + + +Mon Nov 24 04:06:55 EST 2003 + +jcifs-0.8.0b released + +This is the first beta release of the 0.8 series. There have been +significant additions to this package including DFS support, random access +files, filename and file filters, setting attributes and times, and copyTo +and delete improvements. + +Thu Oct 23 01:18:29 EDT 2003 jcifs-0.7.15 released diff --git a/build.xml b/build.xml index 872fbea..b0ed45e 100644 --- a/build.xml +++ b/build.xml @@ -66,38 +66,38 @@ - + - + - - - + + + - + - + - + windowtitle="jCIFS API"> diff --git a/examples/Append.java b/examples/Append.java index 7d073c9..8021d88 100644 --- a/examples/Append.java +++ b/examples/Append.java @@ -10,9 +10,10 @@ public class Append { byte[] msg; int i = 0; - while( i++ < 10 ) { + while( i++ < 5 ) { msg = new String( "this is msg #" + i ).getBytes(); out.write( msg ); + System.out.write( msg ); Thread.sleep( 17000 ); } diff --git a/examples/AuthListFiles.java b/examples/AuthListFiles.java index 918716f..eb3d2d1 100644 --- a/examples/AuthListFiles.java +++ b/examples/AuthListFiles.java @@ -4,7 +4,7 @@ import jcifs.smb.*; import jcifs.util.Log; import java.util.Date; -public class AuthListFiles implements AuthHandler { +public class AuthListFiles extends NtlmAuthenticator { public static String readLine() throws Exception { int c; @@ -17,7 +17,7 @@ public class AuthListFiles implements AuthHandler { } public AuthListFiles( String[] argv ) throws Exception { - SmbFile.setAuthHandler( this ); + NtlmAuthenticator.setDefault( this ); SmbFile file = new SmbFile( argv[0] ); @@ -26,28 +26,30 @@ public class AuthListFiles implements AuthHandler { for( int i = 0; i < files.length; i++ ) { System.out.print( " " + files[i].getName() ); } + System.out.println(); } - public boolean authenticate( AuthInfo authInfo ) { - System.out.println( authInfo.exception.getMessage() + " for " + authInfo.target ); + protected NtlmPasswordAuthentication getNtlmPasswordAuthentication() { + System.out.println( getRequestingException().getMessage() + " for " + getRequestingURL() ); System.out.print( "username: " ); - int i; try { - String user = readLine(); - if((i = user.indexOf( '\\' )) != -1 ) { - authInfo.domain = user.substring( 0, i ); - authInfo.username = user.substring( i + 1 ); - } else { - authInfo.username = user; + int i; + String username = readLine(); + String domain = null, password; + + if(( i = username.indexOf( '\\' )) != -1 ) { + domain = username.substring( 0, i ); + username = username.substring( i + 1 ); } System.out.print( "password: " ); - authInfo.password = readLine(); - if( authInfo.password.length() > 0 ) { - return true; + password = readLine(); + if( password.length() == 0 ) { + return null; } + return new NtlmPasswordAuthentication( domain, username, password ); } catch( Exception e ) { } - return false; + return null; } diff --git a/examples/Break.java b/examples/Break.java deleted file mode 100644 index f0ed65e..0000000 --- a/examples/Break.java +++ /dev/null @@ -1,29 +0,0 @@ -import jcifs.netbios.NbtAddress; -import jcifs.util.*; -import jcifs.smb.*; -import jcifs.util.Log; -import java.util.Date; - -public class Break implements AuthHandler { - - public Break( String[] argv ) throws Exception { - SmbFile.setAuthHandler( this ); - System.out.println( "listed " + (new SmbFile( argv[0] ).listFiles()).length + " files" ); - } - - public boolean authenticate( AuthInfo authInfo ) { - authInfo.domain = "mydom"; - authInfo.username = "miallen"; - authInfo.password = "wwcWww"; - System.out.println( authInfo.exception.getMessage() + ", trying new credentials" ); - return true; - } - - - public static void main( String[] argv ) throws Exception { - for( int i = 0; i < 10; i++ ) { - new Break( argv ); - Thread.sleep( 4000 ); - } - } -} diff --git a/examples/CopyTo5.java b/examples/CopyTo5.java deleted file mode 100644 index cd4f635..0000000 --- a/examples/CopyTo5.java +++ /dev/null @@ -1,34 +0,0 @@ -import jcifs.smb.SmbFile; - -public class CopyTo5 { - - public static void main( String argv[] ) throws Exception { - if( argv.length < 7 ) { - System.err.println( "CopyTo " ); - System.exit( 1 ); - } - SmbFile srcdir = new SmbFile( argv[0] ); - SmbFile dstdir = new SmbFile( argv[1] ); - - SmbFile sf1 = new SmbFile( srcdir, argv[2] ); - SmbFile df1 = new SmbFile( dstdir, argv[2] ); - sf1.copyTo( df1 ); - - SmbFile sf2 = new SmbFile( srcdir, argv[3] ); - SmbFile df2 = new SmbFile( dstdir, argv[3] ); - sf2.copyTo( df2 ); - - SmbFile sf3 = new SmbFile( srcdir, argv[4] ); - SmbFile df3 = new SmbFile( dstdir, argv[4] ); - sf3.copyTo( df3 ); - - SmbFile sf4 = new SmbFile( srcdir, argv[5] ); - SmbFile df4 = new SmbFile( dstdir, argv[5] ); - sf4.copyTo( df4 ); - - SmbFile sf5 = new SmbFile( srcdir, argv[6] ); - SmbFile df5 = new SmbFile( dstdir, argv[6] ); - sf5.copyTo( df5 ); - } -} - diff --git a/examples/CreateFile.java b/examples/CreateFile.java index a1394e4..a7d4685 100644 --- a/examples/CreateFile.java +++ b/examples/CreateFile.java @@ -4,7 +4,7 @@ public class CreateFile { public static void main( String argv[] ) throws Exception { - SmbFileOutputStream out = new SmbFileOutputStream( argv[0], true ); + SmbFileOutputStream out = new SmbFileOutputStream( argv[0], false ); out.close(); } } diff --git a/examples/Delete.java b/examples/Delete.java index 8124539..8c313de 100644 --- a/examples/Delete.java +++ b/examples/Delete.java @@ -1,5 +1,4 @@ -import jcifs.smb.SmbFile; -import jcifs.smb.SmbException; +import jcifs.smb.*; public class Delete { diff --git a/examples/Exists.java b/examples/Exists.java index 009e210..07985f7 100644 --- a/examples/Exists.java +++ b/examples/Exists.java @@ -5,6 +5,7 @@ public class Exists { public static void main( String argv[] ) throws Exception { SmbFile f = new SmbFile( argv[0] ); + System.out.println( f.length() + " bytes" ); if( f.exists() ) { System.out.println( argv[0] + " exists" ); } else { diff --git a/examples/FileInfo.java b/examples/FileInfo.java new file mode 100644 index 0000000..bb5f458 --- /dev/null +++ b/examples/FileInfo.java @@ -0,0 +1,119 @@ +import java.util.*; +import java.text.*; +import java.net.*; +import jcifs.smb.SmbFile; + +public class FileInfo { + + static final String TYPES[] = { + "TYPE_COMM", + "TYPE_FILESYSTEM", + "TYPE_NAMED_PIPE", + "TYPE_PRINTER", + "TYPE_SERVER", + "TYPE_SHARE", + "TYPE_WORKGROUP" + }; + + public static void main( String argv[] ) throws Exception { + int i, start, end;; + SimpleDateFormat sdf = new SimpleDateFormat( "MM/dd/yy hh:mm:ss a" ); + GregorianCalendar cal = new GregorianCalendar(); + SmbFile f; + + if( argv.length < 2 ) { + throw new IllegalArgumentException( "usage: FileInfo " ); + } + + if( argv.length == 3 ) { + SmbFile tmp = new SmbFile( argv[0] ); + f = new SmbFile( tmp.toString(), argv[1] ); + start = Integer.parseInt( argv[2] ); + } else { + f = new SmbFile( argv[0] ); + start = Integer.parseInt( argv[1] ); + } + + sdf.setCalendar( cal ); + + i = end = start; + do { + switch( i ) { + case 0: + System.out.println( " toString: " + f.toString() ); + break; + case 1: + System.out.println( " toURL: " + f.toURL() ); + break; + case 2: + System.out.println( " getName: " + f.getName() ); + break; + case 3: + System.out.println( " length: " + f.length() ); + break; + case 4: + System.out.println( " getLastModified: " + sdf.format( new Date( f.getLastModified() ))); + break; + case 5: + System.out.println( " isHidden: " + f.isHidden() ); + break; + case 6: + System.out.println( " isFile: " + f.isFile() ); + break; + case 7: + System.out.println( " isDirectory: " + f.isDirectory() ); + break; + case 8: + System.out.println( " hashCode: " + f.hashCode() ); + break; + case 9: + System.out.println( " getUncPath: " + f.getUncPath() ); + break; + case 10: + System.out.println( " getType: " + TYPES[f.getType()] ); + break; + case 11: + System.out.println( " getShare: " + f.getShare() ); + break; + case 12: + System.out.println( " getServer: " + f.getServer() ); + break; + case 13: + System.out.println( " getPath: " + f.getPath() ); + break; + case 14: + System.out.println( " getParent: " + f.getParent() ); + break; + case 15: + System.out.println( " lastModified: " + sdf.format( new Date( f.lastModified() ))); + break; + case 16: + System.out.println( "getDiskFreeSpace: " + f.getDiskFreeSpace() ); + break; + case 17: + System.out.println( " getDate: " + sdf.format( new Date( f.getDate() ))); + break; + case 18: + System.out.println( "getContentLength: " + f.getContentLength() ); + break; + case 19: + System.out.println( "getCanonicalPath: " + f.getCanonicalPath() ); + break; + case 20: + System.out.println( " exists: " + f.exists() ); + break; + case 21: + System.out.println( " canRead: " + f.canRead() ); + break; + case 22: + System.out.println( " canWrite: " + f.canWrite() ); + break; + } + i++; + if( i == 23 ) { + i = 0; + } + } while( i != end ); + } +} + diff --git a/examples/FilterFiles.java b/examples/FilterFiles.java new file mode 100644 index 0000000..018169e --- /dev/null +++ b/examples/FilterFiles.java @@ -0,0 +1,37 @@ +import jcifs.netbios.NbtAddress; +import jcifs.smb.*; +import jcifs.util.Log; +import java.util.Date; + +public class FilterFiles { + + static class ShortFilenameFilter implements SmbFilenameFilter { + public boolean accept( SmbFile dir, String name ) throws SmbException { + return name.length() < 9; + } + } + static class BigFileFilter implements SmbFileFilter { + public boolean accept( SmbFile file ) throws SmbException { + return file.length() > 0x1FFFFL; + } + } + + public static void main( String[] argv ) throws Exception { + + SmbFile file = new SmbFile( argv[0] ); + BigFileFilter filter = new BigFileFilter(); + DosFileFilter everything = new DosFileFilter( "*", + SmbFile.ATTR_DIRECTORY | SmbFile.ATTR_HIDDEN | SmbFile.ATTR_SYSTEM ); + + long t1 = System.currentTimeMillis(); + SmbFile[] files = file.listFiles( everything ); + long t2 = System.currentTimeMillis() - t1; + + for( int i = 0; i < files.length; i++ ) { + System.out.print( " " + files[i].getName() ); + } + System.out.println(); + System.out.println( files.length + " files in " + t2 + "ms" ); + } +} + diff --git a/examples/GetDfsPath.java b/examples/GetDfsPath.java new file mode 100644 index 0000000..fa4e12c --- /dev/null +++ b/examples/GetDfsPath.java @@ -0,0 +1,11 @@ +import jcifs.smb.SmbFile; + +public class GetDfsPath { + + public static void main( String argv[] ) throws Exception { + + SmbFile f = new SmbFile( argv[0] ); + System.out.println( f.getDfsPath() ); + } +} + diff --git a/examples/GetURL.java b/examples/GetURL.java index c93b2b4..7190fd1 100644 --- a/examples/GetURL.java +++ b/examples/GetURL.java @@ -5,8 +5,6 @@ public class GetURL { public static void main( String argv[] ) throws Exception { - System.in.read(); - jcifs.Config.registerSmbURLHandler(); URL url = new URL( argv[0] ); diff --git a/examples/HttpURL.java b/examples/HttpURL.java new file mode 100644 index 0000000..f74f5d2 --- /dev/null +++ b/examples/HttpURL.java @@ -0,0 +1,11 @@ +import java.net.*; + +public class HttpURL { + + public static void main( String[] args ) throws Exception { + jcifs.Config.registerSmbURLHandler(); + + URL u = new URL( new URL( args[0] ), args[1] ); + System.out.println( u ); + } +} diff --git a/examples/Makefile b/examples/Makefile new file mode 100644 index 0000000..1228d42 --- /dev/null +++ b/examples/Makefile @@ -0,0 +1,14 @@ +JAVA_HOME=/usr/local/java +CLASSPATH=../build:. + +.SUFFIXES: .java .class + +CLASSFILES=AllocInfo.class Append.class AuthListFiles.class CallNamedPipe.class CifsTime.class CopyTo.class CreateFile.class Delete.class Equals.class Exists.class FileInfo.class FileOps.class FilterFiles.class Format.class GetDate.class GetDfsPath.class Get.class GetType.class GetURL.class GrowWrite.class HttpURL.class Interleave.class IsDir.class Length.class ListFiles.class List.class ListTypes.class LogTest.class Mkdir.class NodeStatus.class OpenExclusive.class PeekNamedPipe.class PipeTalk.class Put.class Query.class RenameTo.class SetAttrs.class SetTime.class SlowRead.class SlowWrite.class SmbCrawler.class SmbShell.class SmbTableFile.class SmbTableFileRecord.class T2Crawler.class TestRandomAccess.class TestSmbURL.class TestUnicode.class ThreadedNbtQuery.class ThreadedSmbCrawler.class ThreadedUniQuery.class Torture1.class Torture2.class TortureTest5.class TransactNamedPipe.class URLTest.class VerifyGuest.class VerifyIO.class VerifyReads.class + +all: ${CLASSFILES} + +.java.class: + ${JAVA_HOME}/bin/javac -classpath ${CLASSPATH} $< + +clean: + ${RM} *.class diff --git a/examples/SetAttrs.java b/examples/SetAttrs.java new file mode 100644 index 0000000..c2d3af9 --- /dev/null +++ b/examples/SetAttrs.java @@ -0,0 +1,18 @@ +import jcifs.smb.*; + +public class SetAttrs { + + public static void main( String argv[] ) throws Exception { + if( argv.length < 2 ) { + System.err.println( "usage: SetAttrs " ); + return; + } + + SmbFile f = new SmbFile( argv[0] ); + SmbFileInputStream in = new SmbFileInputStream( f ); + int attrs = Integer.parseInt( argv[1], 16 ); + + f.setAttributes( attrs ); + } +} + diff --git a/examples/SetTime.java b/examples/SetTime.java new file mode 100644 index 0000000..6ec41a9 --- /dev/null +++ b/examples/SetTime.java @@ -0,0 +1,12 @@ +import jcifs.smb.SmbFile; +import jcifs.smb.SmbException; + +public class SetTime { + + public static void main( String argv[] ) throws Exception { + SmbFile f = new SmbFile( argv[0] ); + long time = f.getLastModified(); + f.setLastModified( time + 65000 ); /* add 1 minute and 5 seconds */ + } +} + diff --git a/examples/SmbTableFile.java b/examples/SmbTableFile.java new file mode 100644 index 0000000..43bc425 --- /dev/null +++ b/examples/SmbTableFile.java @@ -0,0 +1,128 @@ +/* jcifs smb client library in Java + * Copyright (C) 2003 "Michael B. Allen" + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +import jcifs.smb.*; +import java.io.*; + +public class SmbTableFile extends SmbRandomAccessFile { + + static final byte BYTE_FULL = (byte)0xFF; + + byte[] hdr = new byte[512]; + byte[] buf = new byte[1024]; + char[] cbuf = new char[512]; + int recordSize, row; + + public SmbTableFile( SmbFile file, String mode, int recordSize ) throws IOException { + super( file, mode ); + this.recordSize = recordSize; + read( hdr, 0, 512 ); + } + public SmbTableFile( String url, String mode, int shareAccess, int recordSize ) throws IOException { + super( url, mode, shareAccess ); + this.recordSize = recordSize; + read( hdr, 0, 512 ); + } + + public void insert( SmbTableFileRecord tfr ) throws IOException { + int i, b = 0; + + /* Find an unset bit it in the bitmap + */ + for( i = 128; i < 512; i++ ) { + if( hdr[i] != BYTE_FULL ) { + /* bitwise complement inverts each bit + * mask with 0xFF ensures we only use 8 bits of int b + */ + b = ~hdr[i] & 0xFF; + /* clever trick to isolate first bit on + */ + b = b & -b; + break; + } + } + if( i == 512 ) { + throw new IOException( "No more space in " + this ); + } + /* convert power of two to position + */ + switch( b ) { + case 1: b = 0; break; + case 2: b = 1; break; + case 4: b = 2; break; + case 8: b = 3; break; + case 16: b = 4; break; + case 32: b = 5; break; + case 64: b = 6; break; + case 128: b = 7; break; + } + tfr.rowid = (i - 128) * 8 + b; + update( tfr ); + } + public void update( SmbTableFileRecord tfr ) throws IOException { + int i; + + seek( 512L + tfr.rowid * recordSize ); + tfr.encode( this ); + + i = 128 + tfr.rowid / 8; + seek( i ); + hdr[i] |= 1 << (tfr.rowid % 8); + write( hdr[i] ); + } + public void get( SmbTableFileRecord tfr ) throws IOException { + seek( 512L + tfr.rowid * recordSize ); + tfr.decode( this ); + } + public void iterate() { + row = 0; + } + public boolean next( SmbTableFileRecord tfr ) throws IOException { + int i, b; + + i = 128 + row / 8; /* Search bitmap for next bit that is on */ + b = 1 << (row % 8); + for( ; i < 512; i++ ) { + if(( hdr[i] & -b ) != 0 ) { + b = hdr[i] & -b; + b = b & -b; + break; + } + b = 1; + } + if( i == 512 ) { /* Are no more on bits, return */ + return false; + } + switch( b ) { + case 1: b = 0; break; + case 2: b = 1; break; + case 4: b = 2; break; + case 8: b = 3; break; + case 16: b = 4; break; + case 32: b = 5; break; + case 64: b = 6; break; + case 128: b = 7; break; + } + tfr.rowid = (i - 128) * 8 + b; /* Set rowid and get */ + get( tfr ); + + row = tfr.rowid + 1; /* Iterate row for next caller */ + + return true; + } +} diff --git a/examples/SmbTableFileRecord.java b/examples/SmbTableFileRecord.java new file mode 100644 index 0000000..a92f9cb --- /dev/null +++ b/examples/SmbTableFileRecord.java @@ -0,0 +1,25 @@ +/* jcifs smb client library in Java + * Copyright (C) 2003 "Michael B. Allen" + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +import jcifs.smb.SmbException; + +public abstract class SmbTableFileRecord { + public int rowid; + public abstract void encode( SmbTableFile tf ) throws SmbException; + public abstract void decode( SmbTableFile tf ) throws SmbException; +} diff --git a/examples/TestRandomAccess.java b/examples/TestRandomAccess.java new file mode 100644 index 0000000..ec9305d --- /dev/null +++ b/examples/TestRandomAccess.java @@ -0,0 +1,121 @@ +import jcifs.smb.*; + +public class TestRandomAccess { + + public static class TestRecord extends SmbTableFileRecord { + + boolean f1; /* 1 byte */ + byte f2; /* 1 byte */ + int f3; /* 1 byte */ + short f4; /* 2 bytes */ + int f5; /* 2 bytes */ + char f6; /* 2 bytes */ + int f7; /* 4 bytes */ + long f8; /* 8 bytes */ + float f9; /* 4 bytes */ + double f10; /* 8 bytes */ + String f11; /* 95 bytes max */ + /* 128 bytes total */ + + public TestRecord() { + } + public TestRecord( boolean f1, byte f2, int f3, short f4, + int f5, char f6, int f7, long f8, + float f9, double f10, String f11 ) { + this.f1 = f1; + this.f2 = f2; + this.f3 = f3; + this.f4 = f4; + this.f5 = f5; + this.f6 = f6; + this.f7 = f7; + this.f8 = f8; + this.f9 = f9; + this.f10 = f10; + this.f11 = f11; + } + + public void encode( SmbTableFile tf ) throws SmbException { + tf.writeBoolean( f1 ); + tf.writeByte( f2 ); + tf.writeByte( f3 ); + tf.writeShort( f4 ); + tf.writeShort( f5 ); + tf.writeChar( f6 ); + tf.writeInt( f7 ); + tf.writeLong( f8 ); + tf.writeFloat( f9 ); + tf.writeDouble( f10 ); + tf.writeUTF( f11 ); + } + public void decode( SmbTableFile tf ) throws SmbException { + f1 = tf.readBoolean(); + f2 = tf.readByte(); + f3 = tf.readUnsignedByte(); + f4 = tf.readShort(); + f5 = tf.readUnsignedShort(); + f6 = tf.readChar(); + f7 = tf.readInt(); + f8 = tf.readLong(); + f9 = tf.readFloat(); + f10 = tf.readDouble(); + f11 = tf.readUTF(); + } + public boolean equals( Object obj ) { + if( obj instanceof TestRecord ) { + TestRecord r = (TestRecord)obj; + + return r.f1 == f1 && + r.f2 == f2 && + r.f3 == f3 && + r.f4 == f4 && + r.f5 == f5 && + r.f6 == f6 && + r.f7 == f7 && + r.f8 == f8 && + r.f9 == f9 && + r.f10 == f10 && + f11.equals( r.f11 ); + } + return false; + } + } + + public static void main( String[] argv ) throws Exception { + if( argv.length < 2 ) { + System.err.println( "usage: TestRandomAccess (1 for read or 2 for write with )" ); + return; + } + SmbTableFile stf; + int op = Integer.parseInt( argv[1] ); + + TestRecord r1 = new TestRecord( true, (byte)0x12, 0x34, (short)0x1122, + 0x3344, '\u04c1', 0x11112222, 0x1111111122222222L, + 0.1122f, 3344.1, "The surface is smooth like glass" ); + + if( op == 3 ) { + stf = new SmbTableFile( argv[0], "rw", 0, 128 ); + int newLength = Integer.parseInt( argv[2] ); + stf.setLength( newLength ); + System.out.println( "truncated to " + newLength ); + } else if( op == 1 ) { + SmbFile file = new SmbFile( argv[0], null, SmbFile.FILE_SHARE_READ ); + stf = new SmbTableFile( file, "rw", 128 ); + stf.insert( r1 ); + System.out.println( "rowid: " + r1.rowid ); + } else { + if( argv.length < 3 ) { + System.err.println( "usage: TestRandomAccess (1 for read or 2 for write with )" ); + return; + } + stf = new SmbTableFile( argv[0], "r", 0, 128 ); + TestRecord r2 = new TestRecord(); + r2.rowid = Integer.parseInt( argv[2] ); + stf.get( r2 ); + System.out.println( "r1.equals( r2 ) = " + r1.equals( r2 )); + } + + stf.close(); + } +} + diff --git a/examples/Torture2.java b/examples/Torture2.java index b92ac6e..f8737d0 100644 --- a/examples/Torture2.java +++ b/examples/Torture2.java @@ -32,10 +32,10 @@ try { } } if( f1.isDirectory() != f2.isDirectory() ) { - System.err.println( "directory comparison failed: " + f1.isDirectory() + " " + f2.isDirectory() ); + System.err.println( "directory comparison failed: " + f1.getName() + ": " + f1.isDirectory() + " " + f2.isDirectory() ); } if( f1.isFile() != f2.isFile() ) { - System.err.println( "file comparison failed: " + f1.isFile() + " " + f2.isFile() ); + System.err.println( "file comparison failed: " + f1.getName() + ": " + f1.isFile() + " " + f2.isFile() ); } if( f1.getType() != f2.getType() ) { System.err.println( "type comparison failed: " + f1.getName() + " " + f2.getName() ); @@ -44,7 +44,16 @@ try { System.err.println( "name comparison failed: " + f1.getName() + " " + f2.getName() ); } if( f1.length() != f2.length() ) { - System.err.println( "length comparison failed: " + f1.length() + " " + f2.length() ); + System.err.println( "length comparison failed: " + f1.getName() + ": " + f1.length() + " " + f2.length() ); + } + if( f1.getAttributes() != f2.getAttributes() ) { + System.err.println( "attribute comparison failed: " + f1.getName() + ": " + Log.getHexString( f1.getAttributes(), 4 ) + " " + Log.getHexString( f2.getAttributes(), 4 )); + } + if( Math.abs( f1.createTime() - f2.createTime() ) > 1000 ) { + System.err.println( "create time comparison failed: " + f1.getName() + ": " + f1.createTime() + " " + f2.createTime() ); + } + if( Math.abs( f1.lastModified() - f2.lastModified() ) > 1000 ) { + System.err.println( "last modified comparison failed: " + f1.getName() + ": " + f1.lastModified() + " " + f2.lastModified() ); } } catch( Exception x ) { System.err.println( "Exception comparing: " + f1 + " | " + f2 ); diff --git a/examples/pipes/callnp.exe b/examples/pipes/callnp.exe deleted file mode 100644 index 679e9cf918f14d6d18fb9beb20d11ea63b006125..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32768 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 WghH~I`b=7qim(-C(6m-){cUx% zVlBrt%>Z@-fM_Y4^l?EU_Zwu~T53!yz!47-Bt9;V>rKR`!YK_*X<$kNQyQ4kz?252 zG%%%sDGf|%U`hj18u;(2flBqPe0KSOhtBfcfai=6oJ9~G@MuPA)Xeh6!Yp`;+yrum zU=ru39;;f&+g@K|k}o}K>$(+(S6a)$^Loxr$48ezUOfpLTc379WWVCc zFo7|LUM5Un0%HmtCX8nseSz@l3C0|-6&P$|d=2$1DhF+lp zvyB2qo>Fpbz;l{ro>+=nd0IYbQYufYp3_c`V2iwsZdXfAtCd!j@3Flu$QPG@$suC$ z6ZrQq^$aB0WfuyAj*3}T>u#E-l**&vA@`aT&uO*fs8VuRtvoEh?^f{Lad@`va}P1j zhjV&+cK&p(i_Wt%+-F{+l_XI$R>gC6C<8^VB3Pp>N#trnmmtD5xw8_v zqeG{!&pn4(lyM##dYy97@f27ZISgaO!s88Eqo><9xH!dgO71wNON%BlLZ?X;2U$IW zT6v~iPq%^+UGYN*8*4}~HG?TfOkL25T4Ghxc7oJkdPeRzLrljrLSw{qQ=F;ily{tI zkeH^09NUm@D27K>Wx&e0tQ*8kHI*W3wD$;ki1&t;6S2c!2I9MbA4uTj&<=Se4 zO7WbRJI-^`qEJ3jf5bGytK(IxC1;?6c*_P!OX#Jsa@8~HMXW?yqgI|p>KG(7+B7J4 z4C>MnJzan{yrB?reUWh$)RI$iURUX>2V9|DPD+M4II+}-tDZro^)tY$C4)-InMN*B zIgGmA@~S20QST|}kw+z6y*qA0o@W^NVc^e$<_zSiKwj+4(DNk0ZSi*9Kmt@0SMz?T zgj0XJK6+2Oiwgo|z#hN|U^U?JTU;EMoNP)qCnsBy>B9q(^kpF#b}VKJ8u_iOs-p7t zOnz|7T|n{LJfOOItE5|2UD8$?{!>@f0^ATmu>l%?P zjize3-zNWV*x$(M%T~UCTGG?dtm>#*oXPJAKaJKO*`Dk?++kMx{I6@zf?#RaF|hJ$ z3sWJA+Fw;2Wg8HyPY#Z9{(#yc^xPTQVp60rId}JV7iZdGv+avE3SRBoGfDWEO-=0t zV<3BYw>&6x^@>S7cND~qg)iTSQs}0VG;=H|d^t->1|BHYWAEy>f*h8}S@U8M755Wc7>pzx< zvSSY<+BzfP*9In&)~kH$-2)(f%=UWJtNqGKT01NcJ>)F0w?C*#qiC(*l+L%MN!El6 z&Nnxc68Kn(?c0*w@}VM)s-7|3GuD$aRHtKrm89t>q#6x0nrc?$u9aP!Gz%Gmp6-CfVu;N{fPrDYO*1h`~mM=MkMWjs17+us0FJ9=k#nNwEhT*snyDV% z`5tz)oRXS}RNs4odw}4-}V!W1M#KHVz{{6WsQ? zpatyb}LrStG=Do$F%U&4q+GxmSqJ=1tLo*`EEk`-Y6qOfdBf`lGmr~GZ+PfIxKvE{Y zQ4_Z|x`#3%*6l$-QYY4VAH(GmZ8@C4$edy6x&m zS)Z}0pBj@7js^NYu&F;)pB`&C!kannaiwD{vdfeY$&~fym6BoAItSw$LsWXE?;_v$ zXi<$)Vs-v(vqkB!s>O47XYm|CD#VUJzUXM5N8>Ae@{f^X6My0D1>0ZCzmg(lq1FW} zVwrN^U_2Ytaf$=49YP5ZVQ~}|Y~#UEP`A(F6&B?Y6)fvVm2CoqQ7`xDe3`62y4lkB zXpLGjCcg){&f2%*{y(+We+v{g`?g45+yC(q^-I$@w$! zDYax&UBf%qOp^YGU=%+2K9lXlf{F?0XEEvH*$L@SEO`DZ>DTa#rJxq^%40^HSBnJY zG3={G<&LwQl(M43YU@-`3zqnZ^_NJDW1m5x}&?qcBQWy z@%Dw`4jU)U{1M5i{_JE7N=vzQJQc}*Ek7kAb}u8cN6H3SjzLw$Bc14-P9}HCaurJT z3@f)O>*kE836=_^K}ZmBKuOpwJ%PmIs~48~7^>BX1RSsmX@{(Q4J-W+=TC z+v&vkSPmhW(W~jqyUmBv!CJRFitj`733;(0;4>IIlYIE*ZW1%8Z{$GvsKR zbNv`H+J28v+NQqaJVj*bSCJ)wEcQF+DX9uX?XzSYG9>{xP{#g1h|6F33(q<4ODCX-Ib$vOXq1yj}+$5_zzV&FU?iI&YkSIl@@9KtSJ zeKSWlCYQv-QgU7`2H&4bsqtzW9jK%Rd%&dQ@2!qqz{XguxX7#1voQaBh`Fcl3STN; zd2|N`ezo(0tuqzVQ+$xrVt{mHjgJ9L0+H>@`3onN^=I#1*PX8buGl1)I`-{n7tDx# za`L!fw2@uRLNAtz$D@!vWqWsiY{?|T z)JSP!*rxn7CY74O@;!N=<}%U=01E z&g?o8*=AB6k#2L&CzF?EgO`&HZ)sY5tpP?6BCB{d5R4&^O6!e3kfS^Mr0ht(;>D1| znoNTzkQkRPj7#T$SSuTg6I;OIrHL+d$c^<&z$h{bM3(L}cK^-+rNAzJ0=_HGL30Y0 ztbm@rl5tZCtWui3**;zg4gQ?o*j%|((3MA&9lV}ILt8Dd%Yv@<*RbQk8n(U#)X&&e zw~+I0S1;^oFc0<=ZG5z$mzK9rMLM(D+J?QROHf?A+$%f=^;~Y``+je;Y_qa;JVcXR zTo&5IU_=jn3EGbQ9X)JuUfQxUvf3^ej_PvvSsRQ#(mjS?z%@3E04nSr`=hQah5SHR z1A>rAv}i{Phq2`zz&+HqU(_NY;SyHz0KLkFk{`lnk7fvkj3Tzv9BzlxS0d;$(5NnLQk_i${)#+#zxX?#O zInmZ*a(DKMP9rIOsG5GLU%9fwa!el9a}Hv-ew{O!c8Z$n9s}9kooB?`zK(Wr;!FhC zIhkWWiEPtjsgZ40VnSrw$1zJ}+ZZaa#`)jcGt1q1LaghayEr z&8yMaQ))ULz|?5ps9@W7cqIQZQ&^lC4o>H~(HHAe8qKkka4?1IUN2xL7bzKQG~onc zS>ug(1aF9grRmE^G%YIwVjOZ{^Z0d~DWzoFKF?Sz72hA*_F=y=D9q@?k%roW7DNh7 zJ1>%)TdeWJ>-TwgtXw_pSC^4EeHS6j@sWJLDO^p`T;aM)#=1Qp8`69X*&qj`na-r) zNSZNkqe=S{_Sy2Gd1#yYNYwUy&-A-J)+5G20%J_O6YC}IX}9dWLgxnwE}7VjomVu9 zZv;aMs;nttP2lUfI=(BqtSWUA)(?X}7`F z>r+3F>oYj{9!{mft_ayp>!Nr)DCOTNY9u^QRGzi%It_gIvSa(4@a3(~%*4sLC;Zk%7bg$eV&k?7iszW^bN_$>@bn#60-};(ER{!<%2P_^pi+4T%O?DeLt8-sMle0m zDQgz-K3NmQI|~cN*+|*Cs*o+0j0rM-T~@jD?4m85r}>V!~Yhgmibr zWA|!*LWch}sXS)bGL!>f%t*c)Bk9(N=d4$k;MRbz;u%o& z+=<=VYLuCrQBM^dvJNdoNGctD()Qdu^s+n}v_<}w#`ox2+jB8QY@J&PujSb~zl)1Z&*af&uu-mY8UNJ${N2C-r z^<@kKpx|Hzic|a4|0wM`Ve5Pgsd{-iDt;3&mn-)B0nbsQ2uB_0URUJb_QNfKLrBM{ zE%Gzq2Ry^jKR0w7Uz4RV>LoEmreU`%gBFL~^Ig3i58&R7*vIabwNHr; z$Xbf{?}fHb4Y(lz4b)EY7zy31=z!-uks5rk1s(c5bi*3@T@cYg&%s?Bb_|P0JRb|< zT-zJmaSd%mP4Q+9{VQoRc$!*{+@ZZl?s<_k0e|Vx=fPaEhA%%UO+%}wDmcz`=_r;` zg(Aah;iyuwUv-Tso&(gw@fJlq`*Awn5O#4Y{9uRDcK$AmvSOOtF^n@!oJdb^0znmV zpAFaH1iFr)nOg6YQqhs7$iMvnih_m%+HRb(tA@rPi>x)+I!Vd0mSpRE2@$BNlLB!K z4ZzNiQKk^M?=TvS$Rj8<;Cbt>kdCd)>N`F<^gUpV-e+ybcFO2>Q3x;RM2jo-VOHPJ zbFA1fIsH%?_YvHPXAC!?=jn7VuJ7@@)$e&fn_q*QQZq2!o>;6fPC;cvCz@c6Eo1$1 zJ9-d#jw&&w9=p_fMTjLKRD^i158VwGWL98Ljz{!cSj14lr0@siRK?s>T3mo{7c(WR zc9`(CH+ug*wop63;@|I$eKbxWX#d0t4r*VfFy{p2h_`p0cxLW)I2_w&T{tD5;MCy@ z?~ta|OEF#@RxeS#l12VK{M^G_2i%~n_hURx0vFW&83PhxXJBgIXRfc`uGTNOH z`&Tf`Lc%kO`pvLX@m6ec!z;2SNGhd*$c1#jDH~bjbhmY#hmO?sVP zg7fJC13MDM@EM8nlS*VW*B4629j7_bO_vfosO{?d(a2W4UbW>0*(x8hv@h#U&al^2>M-3-7eD_g`JI6VR6jv zGIokxhgt5+5L8Wn1>ctAe&?GV>5-*HS;r$w|1}#Z+E(>jACoRXxy!x^ z-}v{_w@9r;TPZI0TAlsv{0h7Dr--$vI%16w7H(6ZL~Qq>zMOYg%r7JZ3P?%X2iUo* z%hsW;9K9#`;ix!!Wv*yi?bq&}1p|tR$)=bV;r|UwD(BUD&tR|(S2@oq%gA>Pzjun( zF$C*TbBV+9JH-{10c{9tgQ`_|!jI;PcU5XC3OS=yN-`raCGvp&pqBFyB62@pW%+pd zozju@Iuk;Biu~Kkd}62EQBysTln+TwsGa%o*Q~i(`fN!5dKk1J9yETRo?Zw&?IE|j zvaAqz^~&-L(Tqz1Ea`Mi@c9B-3Aka}B`*Wt?bTwxp+@1hzyL_J8jPgnAGUVE?pFqhj2*5zAwD| zE-AmqYjTAz-6h_Ov*AM9z7r$QzSrgAc+skxx<_5KrIe21+}DvFQrIFJ5NZK!kBk8d#HsS1Z$W^MFTyrn#JV!^_u6ybEpz5@|6O9cf{XzM>;T zObu@}-4*egaKbzoga2HhvkDF(8MfySDwP?>W9rGraS=8%j|AyYURR)-9+%Gb*dga* zaz_RyO(#(vg(%{~cvi)U_k-}(Nli>P^ke7-*P-JIv_NaR$u+bV6D(^b6sDVo=2B7@ zE%INX+tV=B(zL#2&7RIq7w4-~?^_3xf%E6xoDn#HqEe3jb9LBxPQ>g$6x7<-B0YUA zsMJyCWn1U_z!-UE#D*}|*+pvCPDEfsR-?SF^B~goEACMH#hETw{fZ3fq7i(Q)vxAq zV0V1f=X3ddUcb+$-n zkHltFVtev2h98zRc!i3IVLu%ZYpT~jL03p;^L>^60DEGj?K6sE?P&E)IKYH@Nk3%gK3``UbtTMzrE zR}<)H&R<27t1A8?S^kFsMUC_{6VGk>kL+t+rZm z$`tQ+!al`Ck)@ke>vxr6+~pQpWu$tW{4OkBfs8?>Gy(*HPY7gEUI8Tb~`TjF~2K$V`{rq{cC|*P#dvs zmbX90bE3yz8F|XCS3JkBuLg%1y<*PRm7wxzU#H8~Lp*Z*KJAw*A)r{JNzH#w&}uQu zsQhzEo)u*U6hM}`^tsrc{nWPDJl2v}Ix%n5ecDS#`SlE)=#)zIilE-By61UScZOHn z7AHar_teQ4IHM*p6;r?`>Q=i5N+ zyhOFyIzPmBq-!07WXmt!7#Zs?3e1V)T~Fe6TU-4;=7%Z+74Sy>jf!XoF{EACZ?D{Z zuaSc{EkI$dW-MjR*=VthhE{?@KwHS>C3~c61JoRHFs>hBD`6l4UhVvNA~cWX{sstr z-%+&5pz&(QfeG0irZ^&WzMf=r%^^!%TobzoPR$4_(1;892erE(Ocf%H=Zz7Hfl7~* zs^gAm=8b0KEZ|oq+&SCvSO-Vw*Bb{qT(K(U%~c8%wtd!eNBx5uQglif|O+7((o*uw};2 zhKDVq2~Fs%!QlBK;5Fjy5vpsBM0ZnbC&!JJ$1#xcDs4a&3EGJGNnCra7>nyab3*^A zAD{c`80xh@idyswzkrr|AAyw=jrYezqd&5!KQfR73L@UU2=PKdE1;*qa`*A>)s|8) zE+ftDo5Ysnisw)C(FbPvk4*c!CQOTs2 z3ve2625=T|9x#nsmH+v9(DOWm83;2FrXfs2Xh&#AXhmp6C?FIN@(B5>`^C_Y1N;^N zmII23ccNbkENK0teqd>|c2XapZJ;r5SM?j_B)rJkfN^@m7di7VeJeo?+sWKrnAD_# z0G=s5;R~%pf!As6_G6jcj-h;kr_^3AZf6&-0i z;J8cECA;<#a;o5DPTNDdw8qZYrFpuPVdT@13r-NmXy}(TM)6Xv>m+01kc2(2qLAWo z1Of)R>d9oQ`JmwP>XJiG7a@R(TkbQ?U)efeL@~Nk=WmyLEtz9l70yR!o_YvxZsG3I zV{>rYr58OpaPu*Yy8g_Hj?-d2&dR5a8t`l@!557#L1S5#Atk9o9af}M&I{rLBc4>e zAYaij&(`@bsMsmZd*%+KY1bCP4y%hcuQ|Pu;9*=C)G2Hk#+rO2I^ZEUBb??B8Zv*? zX2^V6SwF~Ro;@ye&bx$AQTwmN@8Jk0;gu|;o)8TJzX*8(0f`R;V#$UueqYFePGPO0 zy)d@ixbglBEu#HHO%zmMDTGU;>x4h@n2u8goRWe|{jdmo&T~jqOG#PCj;aS~iGw+9 zy_q>>80NI+HFR3eyV&7B62a3dyw>zHi^Ar)69Pa=9{b(-l;<}r3CGDW%dvIR1G`4^ z+P%bstR++R%mb&SJ#_sw*=DdxP?@1GqX09S=Mh=4K4aWyGO&%ugEy=g9>*l3$uNut z_Z-fsxKUX@7Fmh;$WZ>GnTGOl(Q`WB^J|4jWAhZ7k>~>)U8oOmXt@`!!u$q*GMPA6 zuq4CxA_qoIt)+c^BzW|iUMGD!*#^4J0#5;6Ru10TcQHw|MrhmKli$b7gD&Gn4ZV=! z*We(bn<;n;5TX5d5^Of^%KrOI>`+Jt~ zMYHr!{nzp1nNO+(g8E3-XqUd@o`9Fi+Kpy92c(%Ge-$^AnIGfEJT^@(wL0V<>nI@O zWT7P2s8Q@AaRX>3acJX;qujfgKA6XAN68-YX{r2c=;GySA>Mh)Vm3}2c9UfDYWKs| zu-&f`=jX+4BP=K3DVSND>DA`Mvn2D3(E=x9w*9-Tpc;i!VSsk8Y#(i0Kc^Yj&#uJv za}uQC{`?*E1Gt0EH1441Ox!`c*{ya~Uo_&rLi}0mNfQockNFpyE%SQ3?_*cQ z(rkFvt@S%E>fDwUjizp4?fPh>M394UE721pdO{38h_%t=HHP0Zn#uHZ8iCLb-gM)< zooc*f{RE^(Z>~4)-Q(Mxqv%1rx17-{Wk3q7D4BLV^!-phg_srL$(UO`OfN?1Btf|2 zL&x7e{{Hb(Gmc2-FPwv`y)H8Y6#1EH7k+0s-Zs+-x-CN6G$&{`n@V#|sQuMX9JFIc zZ+v`28n2EqWYyuQg31o!rqj!+v!gmJXPLq^)g4B4XwK5GxKzlz zqRfP8f}S$upq0z~qt#shb*qV&IEzC3wt0q6`{EcouS(PW+BOW4D!O(SOaZYj zJ_>M0uFOX2b3f)31tg=jm)F0ZY8ut3m%`WZ_AxF$q}4U%lt(z;5}3b3S_eqJu+&I3c#eRU7aRIG9g~??MG2GLS`U8!Y9KAX|qVp$>=p`dA zt1PC%txnCPfoxhWUrF0Om(l1;Uo%ceGzC=2nXhq)fqa0NC+0FS)31FIGT{8=7pHr* z?@$~Q+tttVs%gl{XK6j5<8(JxNb-g*JA^T$;F`Cy5g4p6=WLyRik;Zs9fqSeTWphi zvsdMA-ASjdh)2UboZgKQ$JU2TP&T@9daK?Tgcnfm&`b(rc1S)I6@{ohE~_PKRZj4Un3g3|Xp z{u_~(UVublC#U{cT->w9WOyKP33ACfumw+N$9ldP27$1f*tnbnoB!l?hgWdYTX^v% zM+LcKjBo3Mc?=|-Mw=ly=iN)K*ZN7YeY;ThjbliE^{Z8TN%pVCD#2izj&o%pCR40e zhJ=DES4WAq5B=IMga+{h(^#@HpDM`dW!%2`&2Pq1SIqw&Fyc(@^C+Ov)&_QAfo-z= z3dTM!FC+?R^@?O4NTN6is4v{jRQ>VQ$XyIMCfV3tZi1ZMyxjK+&Sm0Vvc`*ZWi3^- z7K*eU?&skKkM5t%zIhxEqx)x3JkaQ>f~eGg=(V`eOfP$>6-*OoLyd73Ps6%^`z}8o zn33uxnZhk(G`(zx+ufOT7Kdb)v%l?pkLRQQE=oD@YUv?7NU1z$+czB-SI)jI^YJ9~ z03&tpci^M`0_TTw-&B8$we{RLR;}t;rFY_nt>y{*=BDfHCG^2E>KCJGTv-!aKRuq= zKZ5Qd8eAM3x&HBNzfnl@oR8CB^17BL{u+pwYx6-TA6kbUMue)YS7(hJ#GGk#y}jr8 zB&wu)_aIsyn>TXMEZU7M2W5eG>^I?;DU)-#-B7?Av7)$EVZJC*1cQ`!chz%x~fz|6cz00|D`2p8d>Ef6Xjoao@YE0e8x|`f9^aV=lwjYcoFal;9G$20S*8T z0p13nU;Y!$^HiCt5950jAe6xd0OS4g|8kyZmvZ|tVsWLoD)c=Z#PswRFu~G&iTI?t z?0LjPuL2#={{z4MAWG32dci=1Iw{M~V5G-fQGbF*b8bYvkWp#8iU-xl3>}g{8&*M0HKn@Fa<|si>{92TbF>r4NjvO~`m$ zy7WXf3gB5MdlyRjc2I8#NxXcPebpBKMl1SPeY}CWo|5E=dbA} z^@z z591l9rH=wTf;ZvKggV9KbKj(&%z@@H(6E-{8^>~d8Ar+XTx`*g;TS3Z`p)=CawPva zZxR=JwJ)G=;A2gH5e9;T3=XFPVstn~Z_nPFCewwI9pBgw(qSaNf@0C7H_*;hx(D(H zZ1OhD-tfJCXuM8{%ww+;;M7(vJ^9)Et9U~Y00rGo+VPS~=@@1|3LxC84iS%J18cOk zNZ?SdS}gt8Jcn(aeW(&Y%A+E9M%^LUKKpxMutHwOwzgy}3%?J-j}GrelzvN?623TH znx=b(`?*wCHyjm*BY#7bS#RR4E9BjNlpNa(gUjgia4{RHfLFEXOS28%G8&zSS2QNi zsB>UDei1m9<*_5KhWL|d%zD%=p9L9CG#v$PuWKD zKMAAod$sM<6WGYp`;0{2$I58LV>?bSX!P`#VI_D}b(#4yCYel%SNm&}u8KL-Y2mtT z{^JN*fosv5$rv>r{j%rq+OMOKmCn{(y?CoGrpiTDVZfb1!zVTi2|nUZLk3XL5r?<; zaxJ_MCgThdkbee;g!NSybAiMjepMEi`ZX)w3j5VjjYA1?X?UnW~&8w zt}M>*Y9jsSuYMkWxH2HuTQQ$8$AaIm@IZUzB;#H=ZrszDshY%x@GBR*mW#y zby&0)i9(Sy$J=;5CU_hF6tj36NA&bpNN>|*YqpNr@+L}|Jap$>;{qCi3&<4V`$UV% z;kSF&_d63C6ICi@?GNL`;}k>(E}K^6YR}T~N4_|xZN7Xlb4!NNxj6iCQI-FBCrI~F zZ%21tJOEtV?41|iL}*Z9ugz`7;~65rJ0e_8;wBn%dYUtrrU0A#Ss-yQd*S*Cfb7^L zHsA^gn^tx{Tq)}&^pb9tZh?i)1R7U(nwEq}mGj@L#CZYjBCMU}B`frWd1<;aNK_3o z(;os_Epk;$6&V=U^#?JfQcI??vGkiOb^Q~DnUIOd;`~xIiW z04mA!l)Qkijvmz)wxZ^#&y)tHG%%%sDGf|%U`hj18ko|+f4v6IH*nm`A^e~6fCGRx z0jB_G03QL8k$)RtG2mf9A;1r)1GECdfX@TI0q6z119%?grZ;ljU4WHgN>*DrFkyyp8!QoO~EH?n;f6s zCI%ht!Ouv+=8ZuR<~OtjH;OH7+Z^=w2ZEbg#cf=H)Vxt_XlZ7shE1(a!A-$tF<3`@ zOIsXGEgPR|chqjKZD^|fbQ72qG&BX9Yc~ZQVhiOqKMA_>V0(+ywlV0a3)a?A2^45) zsAEi!CpC!%b#2>|BnFq?vZ=PA*%54OYiXnOsJPg7u;U?WqfHZa}L^ z-pREWHP^P{4>7d1HPk{mMN7&Y8(W&iww5MGOKY&LmQ*nz15nMZ-u4^e=ubCXClA^v zkp`(JB!yNKb|gJ&}Cl{RWk`wzSo@H9*Z$ z^Q7KpHn1_&&{Ri%y<#Jn2hk@~7we6>VEe|lhE|vq=h+(E=%{aL+f<8s;vMU5dlF4) zkN3V|D_6JK(b6_9HT7w$Bra)@L<9*YMKH#;HmMaR>8MZG>c-lpCdi-Q6&Gzh&13`T zNvh|{61~d|9f?g1BAGdOB*f*CF~rFKZ|5w@T(a5Ddpw>b`Yg+4e=dS^EL!eeJ5Aso z%FYhZf9dCN2&6}0Ludm&82GJQ@ex45P2&Usfq;LM#NWJV<|M8O|L&zC+@fZwsR>d1 z2@tUX!-)yu;JDQ399L0X=JOdCN^-uU($$laTz*-JyQp*mQ~qG~1ItsV-$#9iK?-w+ zX*P%r7$8pv9id=tD`UeE-9tpjhW{jEppL^}-qJ9sAR7u37uzGhBmT9g7vvz}so}-DKd&yi8H^^WD#a$qM=SgjCFuDZt zO@?dPgjKq(xS=`d=J3)YDEfnKwBm5w|6(=d2ivi#ZsY!ur3?u;?hUiwm<0;jfhXe<1(rCl9oEDiD{-3n-e$3 z{R>BYJriTbC$_=z+uhuEZ;}(>PM8DOwd93r+bUX0Xxy*D28v52{?{|&gd5`n9Jk5r z3yOyJ^I9-`Fj|P#C)UNggiEJ1bza3b?0LLKZY}1=)}rS67B{!VTrM_MHgAEAb7xHB zvQ)Gr1mxZ@73Y=Iwi>NMdwVCRA%oB0No}S&K;gd{1DZEjPkO0J?gtci=Xp8qlSEhM zDWUiy6sL}N$F~;TK~phijQC^#uHWDanxys+wPlr5Uk@Xo2a;$%VZ1cWP#QLoOukycZ- zp>jB~w=T?HzSXhXQ|c)%%Bv~&xbu;79X?QARN*o9VB>lDo?=fNIgX?9HF;&F73F2c zllidnHTmwmYj_y=vH)tQoN+$Bs?xl|^0LyRPqA7j@D=XTe0O<%O?lDk!U`YcT~k?9 z?zu{4-*q%sx9Qsa8@26P8qZo!UZv64o66!YrY28ngV&Q+R6t5KWW6@uotNkF`D%Ps zz6wuCjSm$-$yH22<8_p{*A|sjmeinZS%5Wjf`6jVZX{bGFJ5Qe79aKuLF_^BKW5=G z&cC3j*i+*xbeDT-CUxfx^W&y*!@PtF$N7q(pm6}-IR2+3+ew1>KE7^?vERyXXm4$4 z$3d{A`RaD(p|%Q7O{phvL%h2<;I8tqtOCJ}rqsHeklfF}=AfD$0vi!7OUr<#~lgeq(^#jK))%S6=0< zxY|~);Zs9wprE*@ z^v}yr{a;pE%nbBqZ8U88&&v;E_m-8r%Zbs|d=p8xFOttM>!^vG7Ky>2yar453>tYSmZ9Q@#VR1*e|R;W9ZbB zmDc3JF>>uZ$@-7ct#ao*Ug^b9FL8rwo?*Z@kj-1>D_VOE*$wqT7GLi1y32i@8gE%q zX~m6o%0>p1Qtrt!CWP@i6SGc@yR@vds-&#)8kxrPi(r=(G@H3AiprQKuIEuy;`NmK z%1XycCS)GxL#kRmPH;UBT01;zD{3mrp{qpq-GGk=-J4f|F$b+1Hdz;}uf>rlK2EP$ z?Hufrd;K~$X=XHN=sVZJ`plRYC+58y%vwYnpQ|Qj7qZzK%^(TBlgH`gvD`o>#^8h) zES3u=NOO?&EBubv6HLngG&|n-$fY*%$#p!qY*`MRTr}^ePT$B*+Vzfg>)?vm9Bf<1 zrsZ|5wPMJz#OIMs*K67E%Y_ zAU`fdoM3`?bKHg9!j9wzh&m;r z#FF<0&^DZE>R(_=7gOxMP6t0bT)g14O`D zKt3QFFb^OA&U_lQfCGTn051b}13CdBpbk(0$OmKt?0~Ua7k3Ikw1*MCMsYwlU@M>r zuntfNco{T{<6#Cu9-!f!&s%^40LsteaDVi)ivcYDx_^t{Q8xZvpLac`5I*fc_pg(? z{@>N~JGqH}Chjl$H<`n;+{8aKp27Vk{}SaL(8d4sj|mF6A3))TI{bPMKp#iX@gbW&T90`h;mZh95Q+$? zoEIStlT3v8Zlf4c+kUwr3Z_GrsmKBeLhK zM!56KgH#3Q+7V{qtDx`O`_5j6k$v6w&MOAW6%YUQFQj+J>Gy&DXJ0wull{mE0m9J~mx^NxgM1_{km^ujkWC!tyv=Mhq$ zfGtOFhLZu*r_%sd04a?^!cj>5OnGFZi9F&MukWcO~#0cDaMeXiDV=W!XOyYQDjCEL8(9nN`S&S{z<_M z9?0czGL3C>dwcKAOJhuX?Vo#_dz)IDqzO1)(9|aW$!$%{OEI%ozWY7K?Ad#-z4qE`uf5maYp=a06g<3#vv3^8Bj`HE9YjnY7yJIpUq?_p z?bcVOaWBXHa_&LX>R-;?;IC?Q)Hl>U+EDhGV{2J$ZJp@&VuhnYs&!P=I&#()Iv%Sl zuUM3jFf}DYdd28FeX-wu<(2X9>MIM7FAse0l^De51ONWY6vS=v%dbpj`Ila~6Y=`0 zt$wN#<Rc_-o7fn0yxHMPkJc5d|;g;ENdYL}EnIj|073)KFd~ zmT_E%0O%G3!lih^#|42bGSIlCx1pdOf$%7v@Nscm=Y;HJa#8}55}1_0qy#1ls5bexQ1*^U*6a^ZHrdGfK|bJr zo*=Q~5SRZjK`!E;Djybr!*l*~1o?0bB5?i@f*c=%a3+7^mjQV_0$EC#e`guKn)ByT z_64J?n)jDe_8XBb=WnHKYb0y-zerhWtn>(FD?wV`|Yc`QsG|zMGFtv;CcwQ9hR5 z+0o)la&dY}+j2w@d;p}XE!7+*0#bo7s_XQm*;`zk;(lLleqWasj2HMf5kG_Q+F6 z!Tabgb>f=t{2VfqGnu@_1^$Cnfs0fa<4!H;k5r}pqe9;uqq>f6>uS;I^{V@{r`n_z zjI2|O29SFPeM&?#id&PLHC?(}PqHKH@t-E*`xx88=Wug3h(iEw4K%E%)Yd8G8r3sr znK@3(h|&^7(DHpgErUd4$lk@^s0F9jX;+Y7!WovEhdF7Pe<_i%Gcvqd^nPTNRd;`6 zT+{*$Jr}@=@Dw^FkC|RmarE<4T{%F8k&FM>Ke;utPIly6Q zsIbWEQ=3u}<;)kixj54l7_3!zXPA?q4a7 z-ld)wW8~3PiKl$}nJ5iwHSg654Fd6djmq_MFOSLzLH?hEn{tm+h49<*)w|S?WF9)b zxwPbw5bIHmuIt4iw%=F_<%(er>@^D=w+6SH6lo;d0@w->m*=epmj;Ufj&T{&aZ7Ia zc;L!is3pb91&QWxOyEkY6bm>&s@vYyX?u#4MIJWUT4k0*w`An5%5BhcHr9 zrXG>|?spd08}C!4Vf0pTN*5avC2N!i=bPI|2z)rs_9Mw|`9zV1RriSQ9_dK#D~HI{ zf<(Pw1Z)0ammt~OPKwYRL2-}J2!sm;yZD1ZdP15#*kzI)l+T*QxE5$A*6QtXZAYbr z8Sde2cMQ4{O~%%68%jj9H>`)JgL2@kdGOtvj}E~v3ntdOSxSG>`hw@@< z1VZ&}?Vl(uSB!Al%$smil#~W)`y7yhb|RVM_T`|4_V^7Pr@e;3(3W7A+Uo#pUkbiF z`jTWs`jSM#>i9DaHFnl>9_<0(VKG((HBKq$^{mq_&m^IRR4^>{Y70>;XRbyqvlyc# z%|XMoUb>O+>tIMvYRCFTtLYfh02Wh*}pI@H>B{X9&5{Gx_oJv zB6lW+H8jRn&O|ca3WP$hnq^o2l=@|>`tXQ+XvEib-lo2#K0Z=)ls9wSLrU{V@F`Oc ztxhEum4X4)IurU06_uXoy2Mv6&)=*RSe%fJZ)- z`Atj7hjB`JoaUUs;a({B} zGuLpx4mJQt3Ti&DtTNKPnlC7;V2i?bfwd8r(QLJ~DriM57zkP~MKLK2GzAn^@d_xc z;+?AmX=}&oDZH#Z+6!!>UF}FW&JQ%(IBD9e#Haf9sW5~VcjH(tnDbIjTypqMhGY+} z?PVnfRxyu!baYx7-*HP-2-Q8H+@x%pIid@;HcT6CFHJjagRQ?IHWLKo)Jzv9ZP!_w zCC~aQ7k~zFE{-c)r~le0XH_D?iSr078>#8C%P+Rm5F2z=mwhxZ+d#;(2XKj-{p*5xiR(Pt zk8c9!ZUHNWota^X8{0n$DyQkGCR+2&%X%7kVLy+J;^k1Hvt$GX4ZlY$?Nr}$o+h-UYtUkV7XHY2S}F!o<8=8e0w)Q8 z0Wk8ld`|c}2tXq>Hqq2lH~`f2B$nrCI9NT7eJa z0fllGua3^ki2t?Nr$R5^OxKOYCAzQ_TvS(s?psnqq?u|58rj@-v?=Dq*_GiT7~ATG zOT0QY73`I)FtW*tC=%I;E8K&)d|V zqacjhBusT2U;5|9hwquF7YsM}6q}(#r{>-`YC|%h3KfoTzH_uI&>YXT10uyURaDg+qkb_Q5gxo#<&Ieb+jKibw2KXEIVyS#du|^vU>UmSjH+O z4OmVD*O&-&?+C_6oz`|VxYMLODBa|oLsMRw0a{L`-qMuFUIU0C1Xu7(5eyYbp#8?{ zOV^!UQd%%a@j&HhPX^Q%)yvh;OJ|Q*CL5a*+rT2V3D18x3;UM>vDU)4U<0DK7y#nH8Ow* zB5WV|qpmA?d{00FfPgWyX$SKLU~>1s&JC`$#tdT)!^Q-fk|hLJS(Q9N$>Qbm=W;mZ zwdY9rI&!l+j*ND-c4FyiUz06a;&$+CnN@#r>f@o4b3Fnxg5S5wE(04qRQA1lxfVgE?MPpj4a|s2Jti3r5=ASF6q8xIo2KuH8KXvm{tB zwc14X@j6e?JpwO*_AQuc^5NN#g!yR5_EY!Nd2Z{`&@mNekGeX=9)ASiRw)j7e(3BW zJ6AY#Ry|FnL**%n79(}ma_SKj(ICm0t-D;Vfw zA%^C)T%q1HFNmIctkvd1%|?g8;&AkYv{CLggEL+Yg}YSM3)?ahyr!Uip6P65s<-iGtObuc%`e4VcD_hUd1}62x%(Jy#gm(!hOZv$$R?z`f zI-Y(qqp08Z)cb%3t~i=z2CnRQVj51K9f232sB*6@Y)62ULKyJxR2ZZ z?w?QrF23+WAQcQ?t2w3=ora%LDLRL3M=5&0ZwD|y2(v(R%9=&IOV$MO*1SA%26DEx zVid~-BSN3YNbr3tjfAQ1K_mhNBZ0mp2E^RAkRVsSAk7Q9?H=vV$dP#6y|&;C1BItk zW^W&l9Nc|4WMh>I=42fY_bEjKeSd%>g{t4esvn3}AFu^ap@jJFdlQKuuTV`?gUhe) zAgh!wE(%=ni;lpR8gWM8%61s31TVNM^!)>?J7Vwqs*!N?wNcUX`=rFc6Q;$|w3RD? z7l7RRYEVE9ROV}eaNx@r%*n!}zA;E{C<$%>ob{goR>!Tdc2=U!#DYp{;IOrCK4KE- z@T0b;XJeG*;R;*un@s06+MW(0VQbw%a4p-``fX$(?Rtb7S+m;O`rvk|9CDAO2@+2o zz}_N{>Qc<>boghIc^32u!!E$(fZf{GItsOg3m{HSco|9n5N-(o;?yqnS8Li%+FIX1 zu2No#hTlZWC!Q5MG7Sb)VyJ(;GpUnQQWW52uFGp zbRWd&vi@-wr^1hPII-oU0P2c~a`OPr1Q8@XsR;KCK0()Z)Go@FHtfwpJ3dti{+`X&r``S}79Okpi|}MV&mr{+;1KBafn1kNcfF zkPk0Q?K&~s_c9>H=(9e<95IGn6aq^*(c%h!lG@exG^;j1PBYZT9RfG#9tlA9SPVI_ zq{IDAxBKHXeqE3I9HfU8JRHu8U{D>=i7uGKOW3&Fj1fecqgafqgq2sR2;msSiVzv~ zzIikrm{uT9jysH7K!mDbQTPK&io;n{Tbzq;7n@2}?FhkbZ*=}+c)s=;OaHVp{HHMl zLHie0u~+*x#px%hM7+7}cIK6F|>$*BWF?~$a{%VAy}P%l%vl12VK{Llkj zGu()5^us(X=^?c{zDGjp9H!b&nd@snFBoX+d?J=gM5pS$_>b#7KCj~LREiF3cVonK z>9BfQ4yEZ*CwxBtdIS1+C4<`8WwDgJ1K*imFx;LT{s{=CBI6!L`)15i@kW@4fs8Z> zoJt8mav|SsN<)#{e44YhU4)F(l99oXOT2xs$0WvLDDDa;4u*_mYB+8%WESHAbzpcf zkeHFZsnWSr>Ed9>D$R&2Lf&eJHVdV2xDh1i{uoP^F2Q+ouK^tlL45{8{Dc~5%yons z5v2l*G_{O)K+`eqHo8; zNfB0b>7p(TYh*p46obW!6S@3yq*k-l*!i&MwavW598Am@uDZj;Ex+BxJ&f>Og#U@~ zJA|76|5pTTpzsRl`TUM)>hum68)n^PI{+(y7}JJ{)na)V{wli^1ADb3q;n-9=3_TF z-8C2ccCwdzbqI;t53#ml?UEnV#VM>SPy7}ZE;$^nYUm-audQ>}?Qqb~MlHMT^;lmn zYR&d&Ql#2)sup?(ljlQn^O0dDbG*u7Nr_|x_|3B5tcd1OSV^UOxv#_CuTE_1ES07l zB$Fr{>+EixqIU12l4{9H+}& zjukDJ7HmhAcS=LrrWx2MGRfu2i7|btXqQZwLX!0o%q;L_Yjq%@ZWP+i*jkflwq1#V z<9mw$lY3(y9UtW@@bC{}+|`6~5K;fR|?dtxXfgFv`GbI$}&3xP^9zcvBXN}yGl zIADxbY$MS9nZXI5-2_U>e0l=tNdisJ{5pZA8ZCAJq%%gU1^ra9!ji?MVS&274iIvA zP=9fx3CHP@RZsG4adD57gvfQMCL9FU8A&uY-h=clQg6{$O3R&AXLlo?VVC|KwC0xv ztwDmq?Ff(vb1zhwekNni0-8WRDMmYi{a0N=-raP)BldxiIAd9+Xj`^m`!qQvCj3S@*o*g8$Vs~J9rg&SC zcJ>@hgix^(%h0D0x=+7PdodC8bZI(5DrE8kSu; zrSt6`Eergq1Ms#xFMvZqtLX!WQG7ZD@=toaKIS1t4m*0D!o1y^OZSnx8%GSbt|Y7O z#6qt;7mKW&%<~?`H3qUx7X{^hxrc|{7g&0ml+)ocxdNAO6Yp$2DbBYYI61hx>j@Xf zi&ov#HYmn+*xPT&b+Pn+v2^$?mf-`pbGbwy*A>oYkQi3nVRzXMyf-LMBofn++Vj5XwA z$OhMmV*<25YP!kQw-F00>m>xHoBC!^Ru?Vu9T@gREVZ<(uUogb28YPPBK0mmrVN}X zXVM4ZKnY3d`hTed&I=+|2g0DH6UyTxfPS7k=BbjgVRl-jLkZUMOy!-a*e!a|R?uu#cKSqw+5<_wGX7HR)> z)=-(86uUUDu&{`(hf;v0{~9apG-T}BVp30KQ;HqCjoqs!Z6fwU;X--XEUk84p(P{A zvAAsMt!j@r)wMKPxb3Ntn)o#+w3_JSR`nyK`fv z?j;<#TZo~u@Q1kjvcp>5fwn4fTekwU@jp=&TVW14w|_?Iw4k3q$ibR)4g_Z}YBH+> z8B3^*=Jju!12Kc2re1a^7njOL`^uDHJ8QnHF*&dW%?oZVXRVLwQ4|Z|;;k|4fGk5LSm} zT^DCT@R4S$^L6xId5{J)Hn4_wg>CY%1%K7j0= z>QR3p2$btf0OcZgjwo?p(^l)PIAV&6oS2;Ag5ct9s`bapYFvcoS>+}i!lW5~6GmCh z`LUq6P+Q;dV<8+We>#RpVUm&2lipdbCQ!a*Cr=GYc6FUqU2o06g-;B7gb^xS_v#Vs zVBx8e1iXjJLxPgcD_^n}KXQoYjve!6-DzqgDNiKs_i6TDQqfPxGAU_7(-dWY;%5&6kvdLVTx~zGE4%3bpUi zP32)8rQSmA1C|j`4X<-@o))w+EHNtow32N_T>%wPq%M9sy!RmWEj*j`B%DOVtMx+d zIir3F14jp?2%{pXcdA*lJ!)36N81@eLJ#-S4JmwFVgmI43v%XDBGx5ou73o?wPbwl zt;b3QJDXjr)4b8l_+pPSTKZq4K`0Vn;hiQ+z%Ir^39>T%kHb#;#H)VN^^wK-O)9dr z%?4_;F`TGfF%W*ZVy~-Xy z7$Bb~_;aeF9e_T&U~DhjcBfI|(e?p^jhc~^HD|TOGVHg4f=`QMi;_LqwgqDL4{Rl> z6`*PWd>+j|mhtzqQXc^Nf%nlT1IMGe0rA@%rU<})nBk=)(o9^qIea^ui$PYQ8n@Z^ zvFCfmNK{`m)Dv2|5einyHhZ5}m2gRG$LkiHnNL`{ouk_X=df+}4v254hZIOk?CLYi zA6j6cHr#pPJ9^6Bf9nA#dCx0UDEILVR=L+=IRYo!OBvA5l;D08??Jqf;!eQ&5$wp! zJJAu_KBXhq)V|Wxk!uFo_LXM2+amWc;>X5_2lK~q8J0=zHp@L`V&S`ESbGe5xxiRS zB~Oi&p#IZnuL(g!s6i-4*o3eFVKsu9^VM@o&eP`*Ylt<({fPS!4Y0J=xKJ+f6-0Q^5$R8VzOU8Jl(s(RI5ikgOPae{Kggpoy zxt4h++E-fEfbd$9+<^&XiLXfc_!ylx%YS6je`*5eo(Xv19=(f<)p?8jM;cMv?uRkU zC)5D#b7;RG;UdBa0uSb_2#E-HAVAKuA?MkMlMyE)PDGrD*pAqa*oxSSSU@Zw<`MJP zj*B557wF|7Y(%Ib+R<^zwV?MC#)0M0+X-WUz5&O8T{CW2nDK-q4ch#fPgs(TCoFqK ztYYbBV1qpv#N!dXeDnGjq7KZPb|@c~Hy#$r94hnr7f=BV{#I6EYkdn7tlGSBAJL#a zGevdJ2Bnz2bXTC`nhLLUU=_gwVbtmSHfbUrkhML7L`F%nB zSjGMNIp=R|t7yFg*-~IKwle@`ZzxLDZ6S8O`sDl^8+1Iwpom z$Pv1jKn3V5E25cTh(aAuq|?qJ@!mmq0x!BUnrGWu{{sy>rP)v1Vs!2LDzHu4$n2*P zI&$3z;ZZ##gzJX;44!{zGkAVqDd}ZA&luww&o0pk6|Mh9d>M0DI(HpwVE9?^_Bsu!6hxFOvn*-A8XQVaGwkQ~0)%-ry=7=P|f*1(*6sKCJZ% z$dpPksVEMqhnSnm$t>mu3tR(uir9N=*QFTM8YDZtBd3d(dtJsA8Y;(mwXdOTxQg;> z^N=7XdkkC@r6Js9x$y1^w`~2>umzqjF1?u^UD0DVy6Q?l<5P33pnld8xMY^@ulyc< zYH*L5E2s~q4!7yMZufbp?i7d$TI1}IrU89FZUs}W;)*#uMP6fd$iL80LB>%+iQTM* zU_0W@lO|^|8LK$Eox!YUF0Ne#d&!BV@-IPtOVvC)jFiPR953u9$>z}(K}4|ci^VzF z;hPA`Nq9SD7N>c%nUNyN96y}vWW<_&%qprOcoBNYVr8~-7j5HWVuvZKwNrErv8?n7 zGi+7UEBff@F3WLwKu)D_?N*fov5aE zg@Re5W2viSsgX#E{0#6X;DWaV58~MMtTY3zbZceHC7s)zQEh4$HkO2f1%g}wKN7vk zp_jpMd|0bZ9%I%lp*2oVqKO0P#Igf+g7Nh83*ZN%SrR%n@q-b>1I74GDH&W~M@g~c zW$Y*H=Gcn(R5(jLLQgE|^ng<9iM|tOPJDdgbo^22;?M>7*2`0pfsvDvIP{U_M8h;E z@U{yLQ=GuvW?GYeQtd9KJLhPdg@hVujuB+d(KYoa>LFZhdRTLIG>7dhMYyiHBXpVl zHGJ74ZCTOQG_)mb6H>wZ%^Ok@5etZ|L+`=;{~i~)@J2sj)M1vE+V}6awO)WQ_PcCt zbnmv`B;G(1Z@=BvdIouQjgT^`OvBU!J{8EYMpOQ1HCKMmYN9t0w;5N=q7b=WjxW@{ zKEe*D(iE>&4;3ky}4*)ep8OU=gs4JxEX20p0!u=N$HKJ)uym zd=HDyaB-V1rF!AmobXs(g_cXV$(i&>o)d5MYMx&i=k>(!&+wGYr`GsaU!B6pjb|MiHj=oYox!? zwMd^v}#=>^(JGN4&0zI)8!qveSQ!w(dA4wN|hQ^CFV*zkR7l6O4 z`}3gtHyOG=ZCv+v>fwao&e5g2(1Jq}-Vn%E$Brme_q{|cQcgZPmN zY_?~x#xMgb1(?;Y^v>cpI^pQVFIP+oBY5El-&f`w4&dPVNq{;Vf(=5Zg42LGU*`R@QGJ@ z3bBDaPBa{=%%KL-I~lbf{NM-Sgp4`Q0wPY+zKRMO*&xsa3$&4$ei%Gno=+H%>ZoMz ziJ>$xsLy|viTdibz;7|&h-71S?KnEy_3wHSTbOv8tnuP3SxXSDc_LW=2YGnqB+bHM29(h0B+!S4~9hi!{IcL}QIe0bt8bi%G=)gyP7bm6* zKTv-GYvRK9SFGq*p||1^aq}bi2~b=AWsJcR8W*E!+{zPKFFlajet~{j1pw~GjZ*Je zvDc`iJ$o^NgXQ{qoXFcFBCd(RlMio#0T`qvE7j?PhpwSy>Rk8{zzjROI2Th8yMKhqot2X&ni4IojJ9rCae1t6?VI zU~QikC3hsy=Lq*kiGkGX@KMN_biV@!T=i9LAzCZ$i6H>Z6&j%bE&s*FhhS2DhX0~| z6=a--mtW|Yp@DgQ6|FCw&GL=!(mcp2d}gu@8O5Z*!fJp#t%Z{WY6 z##Egvz~YXOjIa=4Y+U|N{1@!j@ppKIEWgZ zJjg6Mc8tseAD%Tm?k!Z`#o;P(sGANWuu|;O6pV#b7oOV@o7+BRWxhBctLdFaxfEvW zrnFA}twl^Q5KwwIuHLY**dHm~95Os-;tZ;4YwZBhh%fEDQMCpI4@s9FDK*RpW{2d9 zKQc^_*r+LT1MYNQ-ijlUqKd-<%^g4JkB*6rX;Q(M<2m z7pJCoDpO$RJdBvwRBNrNAxDVu@H|Kd*KK48#&;t0uD2X@Xe1Mde&vKN5JsKHzc<7QTi_WYBMo- zV}I%q^>CX>J&UIqF5WZY1l^d4lgV+Mp5+g3jU1o{b58IkalS|U8iok&s z^I%7cTs#=`8T&?>=;DliDI5n;V8f4Wqu6=rt+X?t7sjmDXOnkg35U!16XO*|a5j5| z0sp;f>Bvdr_v5XEk2YA`!`ks;OlclqKOP|1eus#6xxRJUMr7!4V6k-L%n-1(cA-i9 zc#o>!xplK(d-C^yV4J)GYrbG46~80Gj}z}il74d-7q~Q4nxeZ0y14{bJKPFKf`3n# zsc+&@Gx%*fMm~H7!KM2}c+-uX&!bxO#c75c9i7g`Yax?+*xA#BUk;9>x~)#Py`du= zk8c38wW(ksqgfC)SI2D3a0}Avaq%!|^#y=nNKtVM;htc@MIT=AFc;r=BOrYdJ;KFL z_+Q46pC!w7nHeJz&QvXf5S&ZIWf3mtZ?d)i64rvz)@-z(WYW-tZ%Doj5$slytF8sy1w8G zF@K8JnBl(0q6w3G+1TWs!ltT8ydS@Y!K={7_1CTGovBAed%h^-OEW#y7sG<5`p;pD zr+QFNdWqyVMYg8tSTsLJC=*q8_H~}GL3qAQLB310s2qOd_vvwGY-7DjprV6&aO`mk zq67D8E7%YIn?8{*&1{$>UrO1YYz!{Ba6>BpU(Wz_AB}dX<*gHu4*9XC^HHHu*^aasNE@=>>r1u}Q4*Y4c&ZvSaZwSvO&nbhC5=X6Q74 zaa*ZrF-R0U|Fc+}?b9w{w$r+l40&N)nrbMCs$pgNgHJ0%scI>v3C6YkL5wTXVySL8 z>GOq(--5FT_cTRhan620yiwmKXiwtJd}T|eb?6FCfl4erna|}*L;CzCG(7p4l)$6} zCM7T_fk_EWN?=j~lMS{|9)#p z8vEbjbB+Z|JsYP8-2G{3KF<`6b2tQ|A#Cw)!3Y0;`}!UD2*BW`aDsqHz(303|LI@L zNn8#7-APrr1+`L54U+hi24dA?6^@FAhPnm^$0gu~a>MGig@p!$vRq;Qnw1l>T+Z5p zto$|OkoEVa-Mchl>Rk@0w(N^F6%Mfue86(C$!+gJxV zlvix4+ByL@BBP2O^%YyiigG5IaVc^npL>{mB(xa8c}%Jit3-c8MOir{wDn8Z8~$`P9m(V5LcHqidmR4+bTSD^-?`YFeC4) zsx7bE&J|QNHkLhFv7&BA7AG}UG^F3VsJy0z<6bLgq}f-KAJLZa{KV9M*R|_6W?w^< zSdm**Q^6&h*JFyZBv;DiR5d~gYB9~%K>X#Ns`?6U9+!PRh`Y%YkuMuYj?3b*G1JA0 zv07OicQcn=Q`cCLS5{k&b{0o4sHH6KA5D2!U9xJR8C)~xhJIomY{hVJ&zUzwrgudY z#0}BNWlIeWn3+-Fn+#U>*kd5Sx~dj5@y?<`^j0)9VvXRq|G`?wsb~}%>UMJf%yI^g z9QTIVYpf}`4HXsKw^){1&sjnE(HBlZbu zy`ZjKs$p^^K|luo!4X~exCR!A4bY>;EbhmjQ{v-JntfH}71{o>h7ENEw2G|QiOnpQ z=zp3JCww;C$8nFD3oArJ`q_0*M(7^l7K-JOA>ooJPXn@HC&t-hl$K$&tNMAWN87-?{m2|Qlp>xvO=n?#3Z2C zw&*`WJhW)h7&Q@OLjNCfuV3R{9o71Ynmw7Gl)$6}CM7T_fk_EWO5p#M1m3v|zah$Z zjWxG((RexvEZ@EsM>QB)k@K0uk+x%g+R_~kPrk>!IV(HcU0Ap|r^vI~y^$SGKC?W3 z&5E@}YjOzTGt1W&ZHOX_mFMQKc5lvIowahaZ+-p-_vQ_G>(>^o%)5rhIL=zHd;RJx zkH<}ojL|S^=453v)~<)I^{sKQzlL6WW>q}Af~L9M)5cwtbDyYE2m&>&ga%2EB^~Ld|CM$uInSCy=HB+q`acC z48|=xXgc6_AVbYTpG?Oxc4)3_sC&#&k3OQXUJ@S!IftW8VisKlZ#$i0$ZNsSP&S4) zK1)FFGiQdwF)qO<&Y093O-7}$u97IDz-SL>?tGCMn}y^q$#mSeWJx;Q7UYZB%1%SI z^^Q%O7HuN#HnHRE-TBd;-o19LFLxKPKT7Gi+vBLGzfS46d&LC0M2{V^9&kJ+HHz@N zRBWxP+zEF^!=n{+pvNatURApdo||$<UY$!L(h0!v?x_H8m!?DZQ_q?`YU!U8ZW=@Scv)+4a{@ls4T;Xm!lyG z*kgUT{;2u66(YPFRA>&sDO9%=N6?7uh-jg-mClq8Odx{h;j_ZgmbycQm2ju+1oAJF zjR&ZBWx@?x;ETj>m?>bCKlN_`ymH3BPnUffk_W%wU;5X|efr8B^8_e@CR*OO!TU!Rcv z#)LGMM%+=rmy19@93h`8WgIczxDna056$Oix8kic;(EmNax@1q)pH;wHM#gj7l&UW zbB7VnK>R%7nTSQi$%vOBCZdUm@&6d*-iO}ZjQB;w^!%|2@eaf_h{?gc3Gw5IS0f%l zoQIem?=D3g{cK(5;yx=x%S~qbSqa*(!0o$x=kB?Yg75yuNbLQN5x0E1mzv;Qy8q6cZwP+814u&@4;S{Gu@dS&kkH;?9eWDa{2d z0tt;`8fS`WoT-fFXta#zQeBE|2-F761A@^!pgBN+;3Ri~)Bo6o@>!95b|mjXOm)^r z;DwPG4n;=47%{b76M;X5*nv=wKy#-7fq12U5I+F~;<+Uf??z1JG6I#iA`m~&5q1;z sTqM62G4b~z0=4%q2!!`j1mf>41S" ); out.close(); } + String parseServerAndShare( String pathInfo ) { + char[] out = new char[256]; + char ch; + int len, p, i; + + if( pathInfo == null ) { + return null; + } + len = pathInfo.length(); + + p = i = 0; + while( p < len && pathInfo.charAt( p ) == '/' ) { + p++; + } + if( p == len ) { + return null; + } + do { /* collect server name */ + out[i++] = (ch = pathInfo.charAt( p++ )); + } while( p < len && ch != '/' ); + while( p < len && pathInfo.charAt( p ) == '/' ) { + p++; + } + if( p < len ) { /* then there must be a share */ + out[i++] = '/'; + do { /* collect the share name */ + out[i++] = (ch = pathInfo.charAt( p++ )); + } while( p < len && ch != '/' ); + } + return new String( out, 0, i ); + } public void doGet( HttpServletRequest req, - HttpServletResponse resp ) throws IOException, ServletException { - NtlmPasswordAuthentication ntlm; + HttpServletResponse resp ) throws IOException, ServletException { UniAddress dc; - byte[] challenge; - String pathInfo, s; - SmbFile file; + String msg, pathInfo, server = null; + boolean offerBasic, possibleWorkgroup = true; + NtlmPasswordAuthentication ntlm = null; + HttpSession ssn = req.getSession( false ); - s = null; if(( pathInfo = req.getPathInfo() ) != null ) { - s = parseServerAndShare( pathInfo ); + int i; + server = parseServerAndShare( pathInfo ); + if( server != null && ( i = server.indexOf( '/' )) > 0 ) { + server = server.substring( 0, i ).toLowerCase(); + possibleWorkgroup = false; + } } - if( useNtlmSsp ) { - String msg = req.getHeader( "Authorization" ); - if( msg == null || msg.startsWith( "NTLM " ) == false ) { - resp.setHeader( "WWW-Authenticate", "NTLM" ); - resp.setStatus( HttpServletResponse.SC_UNAUTHORIZED ); - resp.flushBuffer(); - return; - } + msg = req.getHeader( "Authorization" ); + offerBasic = enableBasic && (insecureBasic || req.isSecure()); - if( pathInfo == null || s == null ) { - s = NbtAddress.getByName( NbtAddress.MASTER_BROWSER_NAME, 0x01, null ).getHostAddress(); - dc = UniAddress.getByName( s ); - } else { - int i = s.indexOf( '/' ); - if( i > 0 ) { - s = s.substring( 0, i ); + if( msg != null && (msg.startsWith( "NTLM " ) || (offerBasic && msg.startsWith("Basic ")))) { + + if( msg.startsWith("NTLM ")) { + byte[] challenge; + + if( pathInfo == null || server == null ) { + String mb = NbtAddress.getByName( NbtAddress.MASTER_BROWSER_NAME, 0x01, null ).getHostAddress(); + dc = UniAddress.getByName( mb ); + } else { + dc = UniAddress.getByName( server, possibleWorkgroup ); } - dc = UniAddress.getByName( s, i == -1 ); + + challenge = SmbSession.getChallenge( dc ); + if(( ntlm = NtlmSsp.authenticate( req, resp, challenge )) == null ) { + return; + } + } else { + String auth = new String( Base64.decode( msg.substring(6) ), "US-ASCII" ); + int index = auth.indexOf( ':' ); + String user = (index != -1) ? auth.substring(0, index) : auth; + String password = (index != -1) ? auth.substring(index + 1) : ""; + index = user.indexOf('\\'); + if (index == -1) index = user.indexOf('/'); + String domain = (index != -1) ? user.substring(0, index) : defaultDomain; + user = (index != -1) ? user.substring(index + 1) : user; + ntlm = new NtlmPasswordAuthentication(domain, user, password); } - challenge = SmbSession.getChallenge( dc ); - /* Be carefull what you put before this. It's called 3 times in a row - * before doAuthentication returns a good NtlmPasswordAuthentication - * object. - */ + req.getSession().setAttribute( "npa-" + server, ntlm ); - if(( ntlm = ntlmSsp.doAuthentication( req, resp, challenge )) == null ) { + } else if( !credentialsSupplied ) { + if( ssn != null ) { + ntlm = (NtlmPasswordAuthentication)ssn.getAttribute( "npa-" + server ); + } + if( ntlm == null ) { + resp.setHeader( "WWW-Authenticate", "NTLM" ); + if (offerBasic) { + resp.addHeader( "WWW-Authenticate", "Basic realm=\"" + realm + "\""); + } + resp.setHeader( "Connection", "close" ); + resp.setStatus( HttpServletResponse.SC_UNAUTHORIZED ); + resp.flushBuffer(); return; } - file = new SmbFile( "smb:/" + pathInfo , ntlm ); - } else if( s == null ) { - file = new SmbFile( "smb://" ); - } else { - file = new SmbFile( "smb:/" + pathInfo ); } try { + SmbFile file; + + if( ntlm != null ) { + file = new SmbFile( "smb:/" + pathInfo , ntlm ); + } else if( server == null ) { + file = new SmbFile( "smb://" ); + } else { + file = new SmbFile( "smb:/" + pathInfo ); + } + if( file.isDirectory() ) { doDirectory( req, resp, file ); } else { doFile( req, resp, file ); } } catch( SmbAuthException sae ) { + if( ssn != null ) { + ssn.removeAttribute( "npa-" + server ); + } resp.setHeader( "WWW-Authenticate", "NTLM" ); + if (offerBasic) { + resp.addHeader( "WWW-Authenticate", "Basic realm=\"" + realm + "\""); + } resp.setHeader( "Connection", "close" ); resp.setStatus( HttpServletResponse.SC_UNAUTHORIZED ); resp.flushBuffer(); return; + } catch( DfsReferral dr ) { + StringBuffer redir = req.getRequestURL(); + String qs = req.getQueryString(); +if( true ) { + throw new RuntimeException( "DFS referrals with NetworkExplorer are currently disabled because they can lead to account lockout." ); +} + redir = new StringBuffer( redir.substring( 0, redir.length() - req.getPathInfo().length() )); + redir.append( dr.node.replace( '\\', '/' )); + redir.append( '/' ); + if( qs != null ) { + redir.append( req.getQueryString() ); + } + resp.sendRedirect( redir.toString() ); + resp.flushBuffer(); + return; } } } diff --git a/src/jcifs/smb/DfsReferral.java b/src/jcifs/smb/DfsReferral.java new file mode 100644 index 0000000..132a682 --- /dev/null +++ b/src/jcifs/smb/DfsReferral.java @@ -0,0 +1,38 @@ +/* jcifs smb client library in Java + * Copyright (C) 2003 "Michael B. Allen" + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package jcifs.smb; + +public class DfsReferral extends SmbException { + + public String path; // Path relative to tree from which this referral was thrown + public String node; // Server and share + public String server; // Server + public String share; // Share + public String nodepath; // Path relative to tree + public boolean resolveHashes; + + public String toString() { + return "DfsReferral[path=" + path + + ",node=" + node + + ",server=" + server + + ",share=" + share + + ",nodepath=" + nodepath + + ",resolveHashes=" + resolveHashes + "]"; + } +} diff --git a/src/jcifs/smb/DosFileFilter.java b/src/jcifs/smb/DosFileFilter.java new file mode 100644 index 0000000..bad58af --- /dev/null +++ b/src/jcifs/smb/DosFileFilter.java @@ -0,0 +1,40 @@ +/* jcifs smb client library in Java + * Copyright (C) 2003 "Michael B. Allen" + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package jcifs.smb; + +public class DosFileFilter implements SmbFileFilter { + + protected String wildcard; + protected int attributes; + + public DosFileFilter( String wildcard, int attributes ) { + this.wildcard = wildcard; + this.attributes = attributes; + } + + /** + * This always returns true as the wildcard and + * attributes members are passed to the server which uses them to + * filter on behalf of the client. Sub-classes might overload this + * method to further filter the list however. + */ + public boolean accept( SmbFile file ) throws SmbException { + return true; + } +} diff --git a/src/jcifs/smb/FileEntry.java b/src/jcifs/smb/FileEntry.java new file mode 100644 index 0000000..e16afb2 --- /dev/null +++ b/src/jcifs/smb/FileEntry.java @@ -0,0 +1,11 @@ +package jcifs.smb; + +interface FileEntry { + + String getName(); + int getType(); + int getAttributes(); + long createTime(); + long lastModified(); + long length(); +} diff --git a/src/jcifs/smb/Info.java b/src/jcifs/smb/Info.java index b9a3dbf..17b6129 100644 --- a/src/jcifs/smb/Info.java +++ b/src/jcifs/smb/Info.java @@ -20,6 +20,7 @@ package jcifs.smb; interface Info { int getAttributes(); + long getCreateTime(); long getLastWriteTime(); long getSize(); } diff --git a/src/jcifs/smb/NetServerEnum2Response.java b/src/jcifs/smb/NetServerEnum2Response.java index 747be59..de6571f 100644 --- a/src/jcifs/smb/NetServerEnum2Response.java +++ b/src/jcifs/smb/NetServerEnum2Response.java @@ -24,13 +24,32 @@ import java.io.IOException; class NetServerEnum2Response extends SmbComTransactionResponse { - class ServerInfo1 { + class ServerInfo1 implements FileEntry { String name; int versionMajor; int versionMinor; int type; String commentOrMasterBrowser; + public String getName() { + return name; + } + public int getType() { + return (type & 0x80000000) != 0 ? SmbFile.TYPE_WORKGROUP : SmbFile.TYPE_SERVER; + } + public int getAttributes() { + return SmbFile.ATTR_READONLY | SmbFile.ATTR_DIRECTORY; + } + public long createTime() { + return 0L; + } + public long lastModified() { + return 0L; + } + public long length() { + return 0L; + } + public String toString() { return new String( "ServerInfo1[" + "name=" + name + @@ -41,8 +60,7 @@ class NetServerEnum2Response extends SmbComTransactionResponse { } } - ServerInfo1[] results; - int status, converter, entriesReturned, totalAvailableEntries; + int converter, totalAvailableEntries; NetServerEnum2Response() { } @@ -66,7 +84,7 @@ class NetServerEnum2Response extends SmbComTransactionResponse { bufferIndex += 2; converter = readInt2( buffer, bufferIndex ); bufferIndex += 2; - entriesReturned = readInt2( buffer, bufferIndex ); + numEntries = readInt2( buffer, bufferIndex ); bufferIndex += 2; totalAvailableEntries = readInt2( buffer, bufferIndex ); bufferIndex += 2; @@ -75,22 +93,23 @@ class NetServerEnum2Response extends SmbComTransactionResponse { } int readDataWireFormat( byte[] buffer, int bufferIndex, int len ) { int start = bufferIndex; + ServerInfo1 e; - results = new ServerInfo1[entriesReturned]; - for( int i = 0; i < entriesReturned; i++ ) { - results[i] = new ServerInfo1(); - results[i].name = readString( buffer, bufferIndex, 16, false ); + results = new ServerInfo1[numEntries]; + for( int i = 0; i < numEntries; i++ ) { + results[i] = e = new ServerInfo1(); + e.name = readString( buffer, bufferIndex, 16, false ); bufferIndex += 16; - results[i].versionMajor = (int)( buffer[bufferIndex++] & 0xFF ); - results[i].versionMinor = (int)( buffer[bufferIndex++] & 0xFF ); - results[i].type = readInt4( buffer, bufferIndex ); + e.versionMajor = (int)( buffer[bufferIndex++] & 0xFF ); + e.versionMinor = (int)( buffer[bufferIndex++] & 0xFF ); + e.type = readInt4( buffer, bufferIndex ); bufferIndex += 4; int off = readInt4( buffer, bufferIndex ); bufferIndex += 4; off = ( off & 0xFFFF ) - converter; off = start + off; - results[i].commentOrMasterBrowser = readString( buffer, off, 48, false ); -Log.println( Log.WARNINGS, "net server enum response entry", results[i] ); + e.commentOrMasterBrowser = readString( buffer, off, 48, false ); +Log.println( Log.WARNINGS, "net server enum response entry", e ); } return bufferIndex - start; @@ -100,7 +119,7 @@ Log.println( Log.WARNINGS, "net server enum response entry", results[i] ); super.toString() + ",status=" + status + ",converter=" + converter + - ",entriesReturned=" + entriesReturned + + ",entriesReturned=" + numEntries + ",totalAvailableEntries=" + totalAvailableEntries + "]" ); } } diff --git a/src/jcifs/smb/NetShareEnumResponse.java b/src/jcifs/smb/NetShareEnumResponse.java index 662f6b9..3b7024a 100644 --- a/src/jcifs/smb/NetShareEnumResponse.java +++ b/src/jcifs/smb/NetShareEnumResponse.java @@ -24,11 +24,36 @@ import java.io.IOException; class NetShareEnumResponse extends SmbComTransactionResponse { - class ShareInfo1 { + class ShareInfo1 implements FileEntry { String netName; int type; String remark; + public String getName() { + return netName; + } + public int getType() { + switch( type ) { + case 1: + return SmbFile.TYPE_PRINTER; + case 3: + return SmbFile.TYPE_NAMED_PIPE; + } + return SmbFile.TYPE_SHARE; + } + public int getAttributes() { + return SmbFile.ATTR_READONLY | SmbFile.ATTR_DIRECTORY; + } + public long createTime() { + return 0L; + } + public long lastModified() { + return 0L; + } + public long length() { + return 0L; + } + public String toString() { return new String( "ShareInfo1[" + "netName=" + netName + @@ -37,8 +62,7 @@ class NetShareEnumResponse extends SmbComTransactionResponse { } } - ShareInfo1[] results; - int status, converter, entriesReturned, totalAvailableEntries; + int converter, totalAvailableEntries; NetShareEnumResponse() { } @@ -62,7 +86,7 @@ class NetShareEnumResponse extends SmbComTransactionResponse { bufferIndex += 2; converter = readInt2( buffer, bufferIndex ); bufferIndex += 2; - entriesReturned = readInt2( buffer, bufferIndex ); + numEntries = readInt2( buffer, bufferIndex ); bufferIndex += 2; totalAvailableEntries = readInt2( buffer, bufferIndex ); bufferIndex += 2; @@ -71,22 +95,23 @@ class NetShareEnumResponse extends SmbComTransactionResponse { } int readDataWireFormat( byte[] buffer, int bufferIndex, int len ) { int start = bufferIndex; + ShareInfo1 e; useUnicode = false; - results = new ShareInfo1[entriesReturned]; - for( int i = 0; i < entriesReturned; i++ ) { - results[i] = new ShareInfo1(); - results[i].netName = readString( buffer, bufferIndex, 13, false ); + results = new ShareInfo1[numEntries]; + for( int i = 0; i < numEntries; i++ ) { + results[i] = e = new ShareInfo1(); + e.netName = readString( buffer, bufferIndex, 13, false ); bufferIndex += 14; - results[i].type = readInt2( buffer, bufferIndex ); + e.type = readInt2( buffer, bufferIndex ); bufferIndex += 2; int off = readInt4( buffer, bufferIndex ); bufferIndex += 4; off = ( off & 0xFFFF ) - converter; off = start + off; - results[i].remark = readString( buffer, off, 128, false ); -Log.println( Log.WARNINGS, "smb warning", results[i] ); + e.remark = readString( buffer, off, 128, false ); +Log.println( Log.WARNINGS, "smb warning", e ); } return bufferIndex - start; @@ -96,7 +121,7 @@ Log.println( Log.WARNINGS, "smb warning", results[i] ); super.toString() + ",status=" + status + ",converter=" + converter + - ",entriesReturned=" + entriesReturned + + ",entriesReturned=" + numEntries + ",totalAvailableEntries=" + totalAvailableEntries + "]" ); } } diff --git a/src/jcifs/smb/ServerMessageBlock.java b/src/jcifs/smb/ServerMessageBlock.java index 980baee..ac92961 100644 --- a/src/jcifs/smb/ServerMessageBlock.java +++ b/src/jcifs/smb/ServerMessageBlock.java @@ -175,10 +175,36 @@ abstract class ServerMessageBlock { } } } + static void writeTime( long t, byte[] dst, int dstIndex ) { + + synchronized( TZ ) { + if( TZ.inDaylightTime( new Date() )) { + // in DST + if( TZ.inDaylightTime( new Date( t ))) { + // t also in DST so no correction + } else { + // t not in DST so subtract 1 hour + t -= 3600000; + } + } else { + // not in DST + if( TZ.inDaylightTime( new Date( t ))) { + // t is in DST so add 1 hour + t += 3600000; + } else { + // t isn't in DST either + } + } + } + + t = (t + MILLISECONDS_BETWEEN_1970_AND_1601) * 10000L; + + writeLong( t, dst, dstIndex ); + } static long readUTime( byte[] buffer, int bufferIndex ) { return readInt4( buffer, bufferIndex ) * 1000L; } - static void writeTime( long t, byte[] dst, int dstIndex ) { + static void writeUTime( long t, byte[] dst, int dstIndex ) { if( t == 0L || t == 0xFFFFFFFFFFFFFFFFL ) { writeInt4( 0xFFFFFFFF, dst, dstIndex ); return; @@ -265,6 +291,8 @@ abstract class ServerMessageBlock { long responseTimeout = 1; int verifySequence; boolean verifyFailed; + NtlmPasswordAuthentication auth = null; + String path; ServerMessageBlock() { flags = (byte)( FLAGS_PATH_NAMES_CASELESS | FLAGS_PATH_NAMES_CANONICALIZED ); diff --git a/src/jcifs/smb/SmbAuthException.java b/src/jcifs/smb/SmbAuthException.java index 65b073d..b98123d 100644 --- a/src/jcifs/smb/SmbAuthException.java +++ b/src/jcifs/smb/SmbAuthException.java @@ -30,4 +30,7 @@ public class SmbAuthException extends SmbException { public SmbAuthException( int code ) { super( code ); } + public SmbAuthException( int errorClass, int errorCode ) { + super( errorClass, errorCode ); + } } diff --git a/src/jcifs/smb/SmbComClose.java b/src/jcifs/smb/SmbComClose.java index 40642de..906fda1 100644 --- a/src/jcifs/smb/SmbComClose.java +++ b/src/jcifs/smb/SmbComClose.java @@ -34,7 +34,7 @@ class SmbComClose extends ServerMessageBlock { int writeParameterWordsWireFormat( byte[] dst, int dstIndex ) { writeInt2( fid, dst, dstIndex ); dstIndex += 2; - writeTime( lastWriteTime, dst, dstIndex ); + writeUTime( lastWriteTime, dst, dstIndex ); return 6; } int writeBytesWireFormat( byte[] dst, int dstIndex ) { diff --git a/src/jcifs/smb/SmbComCreateDirectory.java b/src/jcifs/smb/SmbComCreateDirectory.java index fc51f57..979a458 100644 --- a/src/jcifs/smb/SmbComCreateDirectory.java +++ b/src/jcifs/smb/SmbComCreateDirectory.java @@ -20,10 +20,8 @@ package jcifs.smb; class SmbComCreateDirectory extends ServerMessageBlock { - String directoryName; - SmbComCreateDirectory( String directoryName ) { - this.directoryName = directoryName; + this.path = directoryName; command = SMB_COM_CREATE_DIRECTORY; } @@ -34,7 +32,7 @@ class SmbComCreateDirectory extends ServerMessageBlock { int start = dstIndex; dst[dstIndex++] = (byte)0x04; - dstIndex += writeString( directoryName, dst, dstIndex ); + dstIndex += writeString( path, dst, dstIndex ); return dstIndex - start; } @@ -47,6 +45,6 @@ class SmbComCreateDirectory extends ServerMessageBlock { public String toString() { return new String( "SmbComCreateDirectory[" + super.toString() + - ",directoryName=" + directoryName + "]" ); + ",directoryName=" + path + "]" ); } } diff --git a/src/jcifs/smb/SmbComDelete.java b/src/jcifs/smb/SmbComDelete.java index fecdf0e..a1e9baf 100644 --- a/src/jcifs/smb/SmbComDelete.java +++ b/src/jcifs/smb/SmbComDelete.java @@ -26,7 +26,7 @@ class SmbComDelete extends ServerMessageBlock { SmbComDelete( String fileName ) { this.fileName = fileName; command = SMB_COM_DELETE; - searchAttributes = ATTR_HIDDEN | ATTR_SYSTEM | ATTR_DIRECTORY; + searchAttributes = ATTR_HIDDEN | ATTR_SYSTEM; } int writeParameterWordsWireFormat( byte[] dst, int dstIndex ) { diff --git a/src/jcifs/smb/SmbComDeleteDirectory.java b/src/jcifs/smb/SmbComDeleteDirectory.java index 6506fc0..ee6c6e7 100644 --- a/src/jcifs/smb/SmbComDeleteDirectory.java +++ b/src/jcifs/smb/SmbComDeleteDirectory.java @@ -20,10 +20,8 @@ package jcifs.smb; class SmbComDeleteDirectory extends ServerMessageBlock { - String directoryName; - SmbComDeleteDirectory( String directoryName ) { - this.directoryName = directoryName; + this.path = directoryName; command = SMB_COM_DELETE_DIRECTORY; } @@ -34,7 +32,7 @@ class SmbComDeleteDirectory extends ServerMessageBlock { int start = dstIndex; dst[dstIndex++] = (byte)0x04; - dstIndex += writeString( directoryName, dst, dstIndex ); + dstIndex += writeString( path, dst, dstIndex ); return dstIndex - start; } @@ -47,6 +45,6 @@ class SmbComDeleteDirectory extends ServerMessageBlock { public String toString() { return new String( "SmbComDeleteDirectory[" + super.toString() + - ",directoryName=" + directoryName + "]" ); + ",directoryName=" + path + "]" ); } } diff --git a/src/jcifs/smb/SmbComNTCreateAndX.java b/src/jcifs/smb/SmbComNTCreateAndX.java index bec6d11..beaa46a 100644 --- a/src/jcifs/smb/SmbComNTCreateAndX.java +++ b/src/jcifs/smb/SmbComNTCreateAndX.java @@ -93,7 +93,6 @@ class SmbComNTCreateAndX extends AndXServerMessageBlock { static final int SECURITY_CONTEXT_TRACKING = 0x01; static final int SECURITY_EFFECTIVE_ONLY = 0x02; - String name; int flags, rootDirectoryFid, desiredAccess, @@ -105,9 +104,13 @@ class SmbComNTCreateAndX extends AndXServerMessageBlock { long allocationSize; byte securityFlags; - SmbComNTCreateAndX( String name, int flags, ServerMessageBlock andx, int shareAccess ) { + SmbComNTCreateAndX( String name, int flags, + int shareAccess, + int extFileAttributes, + int createOptions, + ServerMessageBlock andx ) { super( andx ); - this.name = name; + this.path = name; command = SMB_COM_NT_CREATE_ANDX; // desiredAccess @@ -115,7 +118,7 @@ class SmbComNTCreateAndX extends AndXServerMessageBlock { desiredAccess |= FILE_READ_EA | FILE_READ_ATTRIBUTES; // extFileAttributes - extFileAttributes = ATTR_NORMAL; + this.extFileAttributes = extFileAttributes; // shareAccess this.shareAccess = shareAccess; @@ -144,7 +147,7 @@ class SmbComNTCreateAndX extends AndXServerMessageBlock { } } - createOptions = 0x0040; // see netmon + this.createOptions = createOptions | 0x0040; impersonationLevel = 0x02; // As seen on NT :~) securityFlags = (byte)0x03; // SECURITY_CONTEXT_TRACKING | SECURITY_EFFECTIVE_ONLY } @@ -154,7 +157,7 @@ class SmbComNTCreateAndX extends AndXServerMessageBlock { dst[dstIndex++] = (byte)0x00; // name length without counting null termination - writeInt2( ( useUnicode ? name.length() * 2 : name.length() ), dst, dstIndex ); + writeInt2( ( useUnicode ? path.length() * 2 : path.length() ), dst, dstIndex ); dstIndex += 2; writeInt4( flags, dst, dstIndex ); dstIndex += 4; @@ -179,7 +182,7 @@ class SmbComNTCreateAndX extends AndXServerMessageBlock { return dstIndex - start; } int writeBytesWireFormat( byte[] dst, int dstIndex ) { - return writeString( name, dst, dstIndex ); + return writeString( path, dst, dstIndex ); } int readParameterWordsWireFormat( byte[] buffer, int bufferIndex ) { return 0; @@ -203,6 +206,6 @@ class SmbComNTCreateAndX extends AndXServerMessageBlock { ",createOptions=0x" + Log.getHexString( createOptions, 8 ) + ",impersonationLevel=0x" + Log.getHexString( impersonationLevel, 4 ) + ",securityFlags=0x" + Log.getHexString( securityFlags, 2 ) + - ",name=" + name + "]" ); + ",name=" + path + "]" ); } } diff --git a/src/jcifs/smb/SmbComNTCreateAndXResponse.java b/src/jcifs/smb/SmbComNTCreateAndXResponse.java index 20482b4..5808eca 100644 --- a/src/jcifs/smb/SmbComNTCreateAndXResponse.java +++ b/src/jcifs/smb/SmbComNTCreateAndXResponse.java @@ -67,7 +67,9 @@ class SmbComNTCreateAndXResponse extends AndXServerMessageBlock { bufferIndex += 8; changeTime = readTime( buffer, bufferIndex ); bufferIndex += 8; -/* file attributes */ + extFileAttributes = readInt4( buffer, bufferIndex ); + bufferIndex += 4; +/* file attributes? */ bufferIndex += 4; allocationSize = readLong( buffer, bufferIndex ); bufferIndex += 8; diff --git a/src/jcifs/smb/SmbComOpenAndX.java b/src/jcifs/smb/SmbComOpenAndX.java index ea2eca4..c02df96 100644 --- a/src/jcifs/smb/SmbComOpenAndX.java +++ b/src/jcifs/smb/SmbComOpenAndX.java @@ -54,22 +54,18 @@ class SmbComOpenAndX extends AndXServerMessageBlock { creationTime, openFunction, allocationSize; - String fileName; // flags is NOT the same as flags member SmbComOpenAndX( String fileName, int flags, ServerMessageBlock andx ) { super( andx ); - this.fileName = fileName; + this.path = fileName; command = SMB_COM_OPEN_ANDX; - // flags -//why!! flags = 0; - // desiredAccess desiredAccess = ( flags >>> 16 ) & 0x3; if( desiredAccess == 0x3 ) { - desiredAccess = 0x4; + desiredAccess = 0x2; /* Mmm, I thought 0x03 was RDWR */ } desiredAccess |= SHARING_DENY_NONE; desiredAccess &= ~0x1; // Win98 doesn't like GENERIC_READ ?! -- get Access Denied. @@ -138,7 +134,7 @@ class SmbComOpenAndX extends AndXServerMessageBlock { if( useUnicode ) { dst[dstIndex++] = (byte)'\0'; } - dstIndex += writeString( fileName, dst, dstIndex ); + dstIndex += writeString( path, dst, dstIndex ); return dstIndex - start; } @@ -161,6 +157,6 @@ class SmbComOpenAndX extends AndXServerMessageBlock { ",creationTime=" + new Date( creationTime ) + ",openFunction=0x" + Log.getHexString( openFunction, 2 ) + ",allocationSize=" + allocationSize + - ",fileName=" + fileName + "]" ); + ",fileName=" + path + "]" ); } } diff --git a/src/jcifs/smb/SmbComQueryInformation.java b/src/jcifs/smb/SmbComQueryInformation.java index 3a23cf4..f6b0615 100644 --- a/src/jcifs/smb/SmbComQueryInformation.java +++ b/src/jcifs/smb/SmbComQueryInformation.java @@ -20,10 +20,8 @@ package jcifs.smb; class SmbComQueryInformation extends ServerMessageBlock { - String filename; - SmbComQueryInformation( String filename ) { - this.filename = filename; + path = filename; command = SMB_COM_QUERY_INFORMATION; } @@ -33,7 +31,7 @@ class SmbComQueryInformation extends ServerMessageBlock { int writeBytesWireFormat( byte[] dst, int dstIndex ) { int start = dstIndex; dst[dstIndex++] = (byte)0x04; - dstIndex += writeString( filename, dst, dstIndex ); + dstIndex += writeString( path, dst, dstIndex ); return dstIndex - start; } int readParameterWordsWireFormat( byte[] buffer, int bufferIndex ) { @@ -45,7 +43,7 @@ class SmbComQueryInformation extends ServerMessageBlock { public String toString() { return new String( "SmbComQueryInformation[" + super.toString() + - ",filename=" + filename + "]" ); + ",filename=" + path + "]" ); } } diff --git a/src/jcifs/smb/SmbComQueryInformationResponse.java b/src/jcifs/smb/SmbComQueryInformationResponse.java index 868945e..7a0a301 100644 --- a/src/jcifs/smb/SmbComQueryInformationResponse.java +++ b/src/jcifs/smb/SmbComQueryInformationResponse.java @@ -23,7 +23,8 @@ import java.util.Date; class SmbComQueryInformationResponse extends ServerMessageBlock implements Info { int fileAttributes = 0x0000; - long lastWriteTime = 0, serverTimeZoneOffset; + long lastWriteTime = 0L; + long serverTimeZoneOffset; int fileSize = 0; SmbComQueryInformationResponse( long serverTimeZoneOffset ) { @@ -34,6 +35,9 @@ class SmbComQueryInformationResponse extends ServerMessageBlock implements Info public int getAttributes() { return fileAttributes; } + public long getCreateTime() { + return lastWriteTime + serverTimeZoneOffset; + } public long getLastWriteTime() { return lastWriteTime + serverTimeZoneOffset; } diff --git a/src/jcifs/smb/SmbComTransaction.java b/src/jcifs/smb/SmbComTransaction.java index 6a02f17..5dd62d0 100644 --- a/src/jcifs/smb/SmbComTransaction.java +++ b/src/jcifs/smb/SmbComTransaction.java @@ -27,6 +27,8 @@ abstract class SmbComTransaction extends ServerMessageBlock implements Enumerati static final byte TRANS2_FIND_NEXT2 = (byte)0x02; static final byte TRANS2_QUERY_FS_INFORMATION = (byte)0x03; static final byte TRANS2_QUERY_PATH_INFORMATION = (byte)0x05; + static final byte TRANS2_GET_DFS_REFERRAL = (byte)0x10; + static final byte TRANS2_SET_FILE_INFORMATION = (byte)0x08; static final int NET_SHARE_ENUM = 0x0000; static final int NET_SERVER_ENUM2 = 0x0068; @@ -81,6 +83,12 @@ abstract class SmbComTransaction extends ServerMessageBlock implements Enumerati SmbComTransaction.TRANSACTION_BUF_SIZE ) - 512; } + public void reset() { + isPrimary = hasMore = true; + } + public void reset( int key, String lastName ) { + reset(); + } public boolean hasMoreElements() { return hasMore; } diff --git a/src/jcifs/smb/SmbComTransactionResponse.java b/src/jcifs/smb/SmbComTransactionResponse.java index 24a16d2..279824c 100644 --- a/src/jcifs/smb/SmbComTransactionResponse.java +++ b/src/jcifs/smb/SmbComTransactionResponse.java @@ -49,6 +49,11 @@ abstract class SmbComTransactionResponse extends ServerMessageBlock implements E int bufParameterStart; int bufDataStart; + /* for doNetEnum and doFindFirstNext */ + int status; + int numEntries; + FileEntry[] results; + SmbComTransactionResponse() { txn_buf = null; } diff --git a/src/jcifs/smb/SmbComTreeConnectAndXResponse.java b/src/jcifs/smb/SmbComTreeConnectAndXResponse.java index e583438..1c970ed 100644 --- a/src/jcifs/smb/SmbComTreeConnectAndXResponse.java +++ b/src/jcifs/smb/SmbComTreeConnectAndXResponse.java @@ -41,10 +41,8 @@ class SmbComTreeConnectAndXResponse extends AndXServerMessageBlock { return 0; } int readParameterWordsWireFormat( byte[] buffer, int bufferIndex ) { - supportSearchBits = ( buffer[bufferIndex] & SMB_SUPPORT_SEARCH_BITS ) == - SMB_SUPPORT_SEARCH_BITS ? true : false; - shareIsInDfs = ( buffer[bufferIndex] & SMB_SHARE_IS_IN_DFS ) == - SMB_SHARE_IS_IN_DFS ? true : false; + supportSearchBits = ( buffer[bufferIndex] & SMB_SUPPORT_SEARCH_BITS ) == SMB_SUPPORT_SEARCH_BITS; + shareIsInDfs = ( buffer[bufferIndex] & SMB_SHARE_IS_IN_DFS ) == SMB_SHARE_IS_IN_DFS; return 2; } int readBytesWireFormat( byte[] buffer, int bufferIndex ) { diff --git a/src/jcifs/smb/SmbException.java b/src/jcifs/smb/SmbException.java index 3b4a443..0024fd3 100644 --- a/src/jcifs/smb/SmbException.java +++ b/src/jcifs/smb/SmbException.java @@ -121,6 +121,10 @@ public class SmbException extends IOException { */ public static final int ERRbadpw = 2; /** + * Reserved (a.k.a. STATUS_PATH_NOT_COVERED) + */ + public static final int ERRreserved = 3; +/** * The client does not have the necessary access rights for the requested function */ public static final int ERRaccess = 4; @@ -177,7 +181,9 @@ public class SmbException extends IOException { public static final int ERRunknownHost = 5004; public static final int ERRinappro = 5005; public static final int ERRunknownType = 5006; - public static final int ERRimpossible = 5007; + public static final int ERRdfs = 5007; + public static final int ERRhashesExternal = 5008; + public static final int ERRimpossible = 5009; int errorClass; int errorCode; @@ -262,6 +268,9 @@ public class SmbException extends IOException { case ERRbadpw: result += "Bad password"; break; + case ERRreserved: + result += "Reserved"; + break; case ERRinvnid: result += "The Tid specified was invalid"; break; @@ -355,6 +364,12 @@ public class SmbException extends IOException { case ERRunknownType: result += "Unknown resource type"; break; + case ERRdfs: + result += "Invalid DFS operation"; + break; + case ERRhashesExternal: + result += "The password hashes are external."; + break; default: result += "No description available [ERRCLI/" + errorCode + "]"; } diff --git a/src/jcifs/smb/SmbFile.java b/src/jcifs/smb/SmbFile.java index 08a6d09..a4a0761 100644 --- a/src/jcifs/smb/SmbFile.java +++ b/src/jcifs/smb/SmbFile.java @@ -288,15 +288,26 @@ public class SmbFile extends URLConnection { static final int O_TRUNC = 0x0002; // file attribute encoding - static final int ATTR_READONLY = 0x01; - static final int ATTR_HIDDEN = 0x02; - static final int ATTR_SYSTEM = 0x04; - static final int ATTR_VOLUME = 0x08; - static final int ATTR_DIRECTORY = 0x10; - static final int ATTR_ARCHIVE = 0x20; + public static final int ATTR_READONLY = 0x01; + public static final int ATTR_HIDDEN = 0x02; + public static final int ATTR_SYSTEM = 0x04; + public static final int ATTR_VOLUME = 0x08; + public static final int ATTR_DIRECTORY = 0x10; + public static final int ATTR_ARCHIVE = 0x20; + + // extended file attribute encoding(others same as above) + static final int ATTR_COMPRESSED = 0x800; + static final int ATTR_NORMAL = 0x080; + static final int ATTR_TEMPORARY = 0x100; + + static final int ATTR_GET_MASK = 0x3F; + static final int ATTR_SET_MASK = 0x27; static final int DEFAULT_ATTR_EXPIRATION_PERIOD = 5000; + static final int HASH_DOT = ".".hashCode(); + static final int HASH_DOT_DOT = "..".hashCode(); + static long attrExpirationPeriod; static { @@ -351,6 +362,7 @@ public class SmbFile extends URLConnection { String share; // Can be null int fid; // Initially 0; set by open() int type; + long createTime; long lastModified; int attributes; long attrExpiration; @@ -361,6 +373,7 @@ public class SmbFile extends URLConnection { boolean isExists; int shareAccess = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; SmbComBlankResponse blank_resp = new SmbComBlankResponse(); + DfsReferral dfsReferral = null; // Only used by getDfsPath() /** @@ -430,6 +443,25 @@ The second parameter may be constructed explicitly or retreived with HttpSer this( new URL( null, url, Handler.SMB_HANDLER ), auth ); } /** + * Constructs an SmbFile representing a file on an SMB network. +The second parameter may be constructed explicitly or retreived with HttpServletRequest.getUserPrincipal() if NTLM HTTP authentication has been successfully negotiated. +The shareAccess parameter controls what permissions other clients have when trying to access the same file while this instance is still open. This value is either FILE_NO_SHARE or any combination of FILE_SHARE_READ, FILE_SHARE_WRITE, and FILE_SHARE_DELETE logically OR'd together. + * + * @param url A URL string + * @param auth The credentials the client should use for authentication + * @param shareAccess Specifies what access other clients have while this file is open. + * @throws MalformedURLException + * If the url parameter does not follow the prescribed syntax + */ + public SmbFile( String url, NtlmPasswordAuthentication auth, int shareAccess ) + throws MalformedURLException { + this( new URL( null, url, Handler.SMB_HANDLER ), auth ); + if ((shareAccess & ~(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE)) != 0) { + throw new RuntimeException( "Illegal shareAccess parameter" ); + } + this.shareAccess = shareAccess; + } +/** * Constructs an SmbFile representing a resource on an SMB network such * as a file or directory. The second parameter is a relative path from * the context. See the description above for examples of @@ -494,9 +526,9 @@ The shareAccess parameter controls what permissions other clients have getUncPath0(); } SmbFile( SmbFile context, String name, int type, - int attributes, long lastModified, long size ) + int attributes, long createTime, long lastModified, long size ) throws MalformedURLException, UnknownHostException { - this( context, name + (( attributes & ATTR_DIRECTORY ) > 0 ? "/" : "" )); + this( new URL( context.url, name + (( attributes & ATTR_DIRECTORY ) > 0 ? "/" : "" ))); if( context.share != null ) { this.tree = context.tree; } @@ -513,6 +545,7 @@ The shareAccess parameter controls what permissions other clients have } this.type = type; this.attributes = attributes; + this.createTime = createTime; this.lastModified = lastModified; this.size = size; isExists = true; @@ -520,16 +553,78 @@ The shareAccess parameter controls what permissions other clients have attrExpiration = sizeExpiration = System.currentTimeMillis() + attrExpirationPeriod; } - void sendTransaction( SmbComTransaction request, SmbComTransactionResponse response ) throws SmbException { - connect0(); - tree.sendTransaction( request, response ); + for( ;; ) { + connect0(); + if( tree.inDfs ) { + DfsReferral dr = tree.session.transport.lookupReferral( unc ); + if( dr != null ) { + UniAddress addr; + SmbTransport trans; + + try { + addr = UniAddress.getByName( dr.server ); + } catch( UnknownHostException uhe ) { + throw new SmbException( SmbException.ERRCLI, SmbException.ERRdfs, "unknown host: " + dr.server ); + } + + trans = SmbTransport.getSmbTransport( addr, 0 ); + tree = trans.getSmbSession( auth ).getSmbTree( dr.share, null ); + unc = request.path = dr.nodepath + unc.substring( dr.path.length() ); + dfsReferral = dr; /* for getDfsPath */ + } + } + if( tree.inDfs ) { + request.flags2 |= ServerMessageBlock.FLAGS2_RESOLVE_PATHS_IN_DFS; + } else { + request.flags2 &= ~ServerMessageBlock.FLAGS2_RESOLVE_PATHS_IN_DFS; + } + try { + tree.sendTransaction( request, response ); + break; + } catch( DfsReferral dr ) { + if( dr.resolveHashes ) { + throw dr; + } + request.reset(); + } + } } void send( ServerMessageBlock request, ServerMessageBlock response ) throws SmbException { - connect0(); - tree.send( request, response ); + for( ;; ) { + connect0(); + if( tree.inDfs ) { + DfsReferral dr = tree.session.transport.lookupReferral( unc ); + if( dr != null ) { + UniAddress addr; + SmbTransport trans; + + try { + addr = UniAddress.getByName( dr.server ); + } catch( UnknownHostException uhe ) { + throw new SmbException( SmbException.ERRCLI, SmbException.ERRdfs, "unknown host: " + dr.server ); + } + + trans = SmbTransport.getSmbTransport( addr, 0 ); + tree = trans.getSmbSession( auth ).getSmbTree( dr.share, null ); + unc = request.path = dr.nodepath + unc.substring( dr.path.length() ); + dfsReferral = dr; /* for getDfsPath */ + } + request.flags2 |= ServerMessageBlock.FLAGS2_RESOLVE_PATHS_IN_DFS; + } else { + request.flags2 &= ~ServerMessageBlock.FLAGS2_RESOLVE_PATHS_IN_DFS; + } + try { + tree.send( request, response ); + break; + } catch( DfsReferral dr ) { + if( dr.resolveHashes ) { + throw dr; + } + } + } } UniAddress getAddress() throws UnknownHostException { @@ -600,10 +695,9 @@ The shareAccess parameter controls what permissions other clients have boolean isConnected() { return (connected = tree != null && tree.treeConnected); } - void open( int flags ) throws SmbException { - if( isOpen() ) { - return; - } + int open0( int flags, int attrs, int options ) throws SmbException { + int f; + connect0(); Log.println( Log.WARNINGS, "smb open warning: ", unc ); @@ -611,39 +705,50 @@ The shareAccess parameter controls what permissions other clients have /* * Open AndX Request / Response */ - if( tree.session.transport.hasCapability( ServerMessageBlock.CAP_NT_SMBS )) { SmbComNTCreateAndXResponse response = new SmbComNTCreateAndXResponse(); - send( new SmbComNTCreateAndX( unc, flags, null, shareAccess ), response ); - fid = response.fid; + send( new SmbComNTCreateAndX( unc, flags, shareAccess, attrs, options, null ), response ); + f = response.fid; + attributes = response.extFileAttributes & 0x3F; + attrExpiration = System.currentTimeMillis() + attrExpirationPeriod; + isExists = true; } else { SmbComOpenAndXResponse response = new SmbComOpenAndXResponse(); send( new SmbComOpenAndX( unc, flags, null ), response ); - fid = response.fid; + f = response.fid; } + return f; + } + void open( int flags, int attrs, int options ) throws SmbException { + if( isOpen() ) { + return; + } + fid = open0( flags, attrs, options ); opened = true; } boolean isOpen() { return opened && isConnected(); } - void close() throws SmbException { - close( 0L ); - } - void close( long lastWriteTime ) throws SmbException { - if( isOpen() == false ) { - return; - } + void close( int f, long lastWriteTime ) throws SmbException { - opened = false; - - Log.println( Log.WARNINGS, "smb close warning", " fid=" + fid ); + Log.println( Log.WARNINGS, "smb close warning", " fid=" + f ); /* * Close Request / Response */ - send( new SmbComClose( fid, lastWriteTime ), blank_resp ); + send( new SmbComClose( f, lastWriteTime ), blank_resp ); + } + void close( long lastWriteTime ) throws SmbException { + if( isOpen() == false ) { + return; + } + close( fid, lastWriteTime ); + opened = false; + } + void close() throws SmbException { + close( 0L ); } /** @@ -795,13 +900,12 @@ The shareAccess parameter controls what permissions other clients have * * @return The UNC path. */ - public String getUncPath() { - String path = getUncPath0(); + getUncPath0(); if( share == null ) { return "\\\\" + url.getHost(); } - return "\\\\" + url.getHost() + "\\" + share + path; + return "\\\\" + url.getHost() + canon.replace( '/', '\\' ); } /** * Returns the full URL of this SMB resource with '.' and '..' components @@ -880,7 +984,7 @@ The shareAccess parameter controls what permissions other clients have try { addr = getAddress(); } catch( UnknownHostException uhe ) { - throw new SmbException( SmbException.ERRCLI | + throw new SmbException( SmbException.ERRCLI, SmbException.ERRunknownHost, "Unknown host: " + uhe.getMessage() ); } @@ -933,6 +1037,13 @@ The shareAccess parameter controls what permissions other clients have * to(overrides negotiatedCapabilities). */ + /* We really should do the referral before this in case + * the redirected target has different capabilities. But + * the way we have been doing that is to call exists() which + * calls this method so another technique will be necessary + * to support DFS referral _to_ Win95/98/ME. + */ + if( tree.session.transport.hasCapability( ServerMessageBlock.CAP_NT_SMBS )) { /* @@ -980,13 +1091,14 @@ The shareAccess parameter controls what permissions other clients have } attributes = ATTR_READONLY | ATTR_DIRECTORY; + createTime = 0L; lastModified = 0L; isExists = false; try { if( url.getHost().length() == 0 ) { } else if( share == null ) { - if( true || type == TYPE_WORKGROUP ) { + if( type == TYPE_WORKGROUP ) { UniAddress.getByName( url.getHost(), true ); } else { UniAddress.getByName( url.getHost() ).getHostName(); @@ -998,6 +1110,7 @@ The shareAccess parameter controls what permissions other clients have Info info = queryPath( getUncPath0(), Trans2QueryPathInformationResponse.SMB_QUERY_FILE_BASIC_INFO ); attributes = info.getAttributes(); + createTime = info.getCreateTime(); lastModified = info.getLastWriteTime(); } @@ -1103,6 +1216,41 @@ The shareAccess parameter controls what permissions other clients have } /** + * Retrieves the DFS path or null if the path specified does not fall within a DFS volume. + */ + + public String getDfsPath() throws SmbException { + connect0(); + if( tree.inDfs ) { + exists(); + } + if( dfsReferral == null ) { + return null; + } + return "smb:/" + (new String( dfsReferral.node + unc )).replace( '\\', '/' ); + } + +/** + * Retrieve the time this SmbFile was created. The value returned is suitable + * for constructing a {@link java.util.Date} object and is adjusted for + * the servers timezone differential. Times should be the same as those + * reported using the properties dialog of the Windows Explorer program. + * + * For Win95/98/Me this is actually the last write time. It is currently + * not possible to retrieve the create time from files on these systems. + * + * @return The number of milliseconds since the 00:00:00 GMT, January 1, + * 1970 as a long value + */ + + public long createTime() throws SmbException { + if( getUncPath0().length() > 1 ) { + exists(); + return createTime; + } + return 0L; + } +/** * Retrieve the last time the file represented by this * SmbFile was modified. The value returned is suitable * for constructing a {@link java.util.Date} object and is adjusted for @@ -1144,119 +1292,10 @@ The shareAccess parameter controls what permissions other clients have */ public String[] list() throws SmbException { - ArrayList list = new ArrayList(); - - connect0(); - - if( url.getHost().length() == 0 ) { - NetServerEnum2Response response = new NetServerEnum2Response(); - sendTransaction( new NetServerEnum2( - tree.session.transport.server.oemDomainName, - NetServerEnum2.SV_TYPE_DOMAIN_ENUM ), response ); - - if( response.status != SmbException.NERR_Success && - response.status != SmbException.ERROR_MORE_DATA ) { - throw new SmbException( SmbException.ERRRAP, - response.status, response.toString() ); - } - - for( int i = 0; i < response.entriesReturned; i++ ) { - if( response.results[i].name.length() > 0 ) { - list.add( response.results[i].name ); - } - } - return (String[])list.toArray( new String[list.size()] ); - } else if( share == null ) { - if( getType() == TYPE_WORKGROUP ) { - NetServerEnum2Response response = new NetServerEnum2Response(); - sendTransaction( new NetServerEnum2( url.getHost(), - NetServerEnum2.SV_TYPE_ALL ), response ); - - if( response.status != SmbException.NERR_Success && - response.status != SmbException.ERROR_MORE_DATA ) { - throw new SmbException( SmbException.ERRRAP, response.status ); - } - - for( int i = 0; i < response.entriesReturned; i++ ) { - if( response.results[i].name.length() > 0 ) { - list.add( response.results[i].name ); - } - } - - return (String[])list.toArray( new String[list.size()] ); - } else { - NetShareEnumResponse response = new NetShareEnumResponse(); - sendTransaction( new NetShareEnum(), response ); - - if( response.status != SmbException.NERR_Success && - response.status != SmbException.ERROR_MORE_DATA ) { - throw new SmbException( SmbException.ERRRAP, response.status ); - } - - for( int i = 0; i < response.entriesReturned; i++ ) { - list.add( response.results[i].netName ); - } - - return (String[])list.toArray( new String[list.size()] ); - } - } else { - return list( unc ); - } + return list( "*", ATTR_DIRECTORY | ATTR_SYSTEM, null, null ); } - String[] list( String dirPath ) throws SmbException { - int sid, count, i; - String filename; - ArrayList list = new ArrayList(); - - Log.println( Log.WARNINGS, "smb find warning", - " find with path=" + dirPath ); - - Trans2FindFirst2Response response = new Trans2FindFirst2Response(); - sendTransaction( new Trans2FindFirst2( dirPath + "\\*" ), response ); - - sid = response.sid; - count = response.searchCount; - - int h1 = new String( "." ).hashCode(); - int h2 = new String( ".." ).hashCode(); - - for( i = 0; i < count; i++ ) { - filename = response.results[i].filename; - if( filename.length() < 3 ) { - int h = filename.hashCode(); - if( h == h1 || h == h2 ) { - continue; - } - } - list.add( filename ); - } - - /* only difference between first2 and next2 - * responses is subCommand so let's recycle - */ - response.subCommand = SmbComTransaction.TRANS2_FIND_NEXT2; - - while( response.isEndOfSearch == false && response.searchCount > 0 ) { - response.reset(); - sendTransaction( new Trans2FindNext2( sid, response.resumeKey, - response.lastName ), response ); - count = response.searchCount; - - for( i = 0; i < count; i++ ) { - filename = response.results[i].filename; - if( filename.length() < 3 ) { - int h = filename.hashCode(); - if( h == h1 || h == h2 ) { - continue; - } - } - list.add( filename ); - } - } - - send( new SmbComFindClose2( sid ), blank_resp ); - - return (String[])list.toArray( new String[list.size()] ); + public String[] list( SmbFilenameFilter filter ) throws SmbException { + return list( "*", ATTR_DIRECTORY | ATTR_SYSTEM, filter, null ); } /** @@ -1286,7 +1325,7 @@ The shareAccess parameter controls what permissions other clients have */ public SmbFile[] listFiles() throws SmbException { - return listFiles( "*" ); + return listFiles( "*", ATTR_DIRECTORY | ATTR_SYSTEM, null, null ); } /** @@ -1319,96 +1358,51 @@ The shareAccess parameter controls what permissions other clients have */ public SmbFile[] listFiles( String wildcard ) throws SmbException { + return listFiles( wildcard, ATTR_DIRECTORY | ATTR_SYSTEM, null, null ); + } + public SmbFile[] listFiles( SmbFilenameFilter filter ) throws SmbException { + return listFiles( "*", ATTR_DIRECTORY | ATTR_SYSTEM, filter, null ); + } + public SmbFile[] listFiles( SmbFileFilter filter ) throws SmbException { + return listFiles( "*", ATTR_DIRECTORY | ATTR_SYSTEM, null, filter ); + } + String[] list( String wildcard, int searchAttributes, + SmbFilenameFilter fnf, SmbFileFilter ff ) throws SmbException { ArrayList list = new ArrayList(); - if( url.toString().lastIndexOf( '/' ) != ( url.toString().length() - 1 )) { + try { + if( url.getHost().length() == 0 || share == null ) { + doNetEnum( list, false, wildcard, searchAttributes, fnf, ff ); + } else { + doFindFirstNext( list, false, wildcard, searchAttributes, fnf, ff ); + } + } catch( UnknownHostException uhe ) { throw new SmbException( SmbException.ERRCLI, - SmbException.ERRlistFiles, url.toString() + " directory must end with '/'" ); + SmbException.ERRunknownHost, url.toString() ); + } catch( MalformedURLException mue ) { + throw new SmbException( SmbException.ERRCLI, + SmbException.ERRlistFiles, url.toString() ); } - try { - if( url.getHost().length() == 0 ) { - connect0(); - - NetServerEnum2Response response = new NetServerEnum2Response(); - sendTransaction( new NetServerEnum2( - tree.session.transport.server.oemDomainName, - NetServerEnum2.SV_TYPE_DOMAIN_ENUM ), response ); - - if( response.status != SmbException.NERR_Success && - response.status != SmbException.ERROR_MORE_DATA ) { - throw new SmbException( SmbException.ERRRAP, - response.status, response.toString() ); - } - for( int i = 0; i < response.entriesReturned; i++ ) { - if( response.results[i].name.length() > 0 ) { - list.add( new SmbFile( this, - response.results[i].name, - TYPE_WORKGROUP, - ATTR_READONLY | ATTR_DIRECTORY, - 0L, - 0L )); - } - } -//System.err.println( "ret=" + ret.length + ",ret[0]=" + ret[0] + ",name=" + response.results[0].name ); - - return (SmbFile[])list.toArray(new SmbFile[list.size()]); - } else if( share == null ) { - if( getType() == TYPE_WORKGROUP ) { - NetServerEnum2Response response = new NetServerEnum2Response(); - sendTransaction( new NetServerEnum2( url.getHost(), - NetServerEnum2.SV_TYPE_ALL ), response ); - - if( response.status != SmbException.NERR_Success && - response.status != SmbException.ERROR_MORE_DATA ) { - throw new SmbException( SmbException.ERRRAP, response.status ); - } - - for( int i = 0; i < response.entriesReturned; i++ ) { - if( response.results[i].name.length() > 0 ) { - list.add( new SmbFile( this, - response.results[i].name, - TYPE_SERVER, - ATTR_READONLY | ATTR_DIRECTORY, - 0L, - 0L )); - } - } + return (String[])list.toArray(new String[list.size()]); + } + SmbFile[] listFiles( String wildcard, int searchAttributes, + SmbFilenameFilter fnf, SmbFileFilter ff ) throws SmbException { + ArrayList list = new ArrayList(); - return (SmbFile[])list.toArray(new SmbFile[list.size()]); - } else { - NetShareEnumResponse response = new NetShareEnumResponse(); - sendTransaction( new NetShareEnum(), response ); - - if( response.status != SmbException.NERR_Success && - response.status != SmbException.ERROR_MORE_DATA ) { - throw new SmbException( SmbException.ERRRAP, response.status ); - } + if( ff != null && ff instanceof DosFileFilter ) { + DosFileFilter dff = (DosFileFilter)ff; + if( dff.wildcard != null ) { + wildcard = dff.wildcard; + } + searchAttributes = dff.attributes; + } - for( int i = 0; i < response.entriesReturned; i++ ) { - int shareType = response.results[i].type; - switch( shareType ) { - case 1: - shareType = TYPE_PRINTER; - break; - case 3: - shareType = TYPE_NAMED_PIPE; - break; - default: - shareType = TYPE_SHARE; - break; - } - list.add( new SmbFile( this, - response.results[i].netName, - shareType, - ATTR_READONLY | ATTR_DIRECTORY, - 0L, - 0L )); - } - return (SmbFile[])list.toArray(new SmbFile[list.size()]); - } + try { + if( url.getHost().length() == 0 || share == null ) { + doNetEnum( list, true, wildcard, searchAttributes, fnf, ff ); } else { - return listFiles( getUncPath0(), wildcard ); + doFindFirstNext( list, true, wildcard, searchAttributes, fnf, ff ); } } catch( UnknownHostException uhe ) { throw new SmbException( SmbException.ERRCLI, @@ -1417,84 +1411,143 @@ The shareAccess parameter controls what permissions other clients have throw new SmbException( SmbException.ERRCLI, SmbException.ERRlistFiles, url.toString() ); } + + return (SmbFile[])list.toArray(new SmbFile[list.size()]); } - SmbFile[] listFiles( String dirPath, String wildcard ) throws SmbException { - int sid, count, i; - String base, filename; - ArrayList list = new ArrayList(); + void doNetEnum( ArrayList list, + boolean files, + String wildcard, + int searchAttributes, + SmbFilenameFilter fnf, + SmbFileFilter ff ) throws SmbException, UnknownHostException, MalformedURLException { + SmbComTransaction req; + SmbComTransactionResponse resp; + int listType = url.getAuthority().length() == 0 ? 0 : getType(); - Log.println( Log.WARNINGS, "smb find warning", - " find with path=" + dirPath ); + if( url.toString().lastIndexOf( '/' ) != ( url.toString().length() - 1 )) { + throw new SmbException( SmbException.ERRCLI, + SmbException.ERRlistFiles, url.toString() + " directory must end with '/'" ); + } - Trans2FindFirst2Response response = new Trans2FindFirst2Response(); - if( dirPath.equals( "\\" )) { - filename = "\\" + wildcard; - } else { - filename = dirPath + "\\" + wildcard; + switch( listType ) { + case 0: + connect0(); + req = new NetServerEnum2( tree.session.transport.server.oemDomainName, + NetServerEnum2.SV_TYPE_DOMAIN_ENUM ); + resp = new NetServerEnum2Response(); + break; + case TYPE_WORKGROUP: + req = new NetServerEnum2( url.getHost(), NetServerEnum2.SV_TYPE_ALL ); + resp = new NetServerEnum2Response(); + break; + case TYPE_SERVER: + req = new NetShareEnum(); + resp = new NetShareEnumResponse(); + break; + default: + throw new SmbException( SmbException.ERRCLI, + SmbException.ERRlistFiles, "invalid list operation: " + url.toString() ); } - sendTransaction( new Trans2FindFirst2( filename ), response ); - sid = response.sid; - count = response.searchCount; + sendTransaction( req, resp ); - int h1 = new String( "." ).hashCode(); - int h2 = new String( ".." ).hashCode(); - try { - for( i = 0; i < count; i++ ) { - filename = response.results[i].filename; - if( filename.length() < 3 ) { - int h = filename.hashCode(); - if( h == h1 || h == h2 ) { - continue; - } + if( resp.status != SmbException.NERR_Success && + resp.status != SmbException.ERROR_MORE_DATA ) { + throw new SmbException( SmbException.ERRRAP, resp.status, resp.toString() ); + } + + for( int i = 0; i < resp.numEntries; i++ ) { + FileEntry e = resp.results[i]; + String name = e.getName(); + if( fnf != null && fnf.accept( this, name ) == false ) { + continue; + } + if( name.length() > 0 ) { + SmbFile f = new SmbFile( this, name, + listType == 0 ? TYPE_WORKGROUP : listType, + ATTR_READONLY | ATTR_DIRECTORY, 0L, 0L, 0L ); + if( ff != null && ff.accept( f ) == false ) { + continue; + } + if( files ) { + list.add( f ); + } else { + list.add( name ); } - list.add( new SmbFile( this, - filename, - TYPE_FILESYSTEM, - response.results[i].extFileAttributes, - response.results[i].lastWriteTime, - response.results[i].endOfFile )); } + } + } + void doFindFirstNext( ArrayList list, + boolean files, + String wildcard, + int searchAttributes, + SmbFilenameFilter fnf, + SmbFileFilter ff ) throws SmbException, UnknownHostException, MalformedURLException { + SmbComTransaction req; + Trans2FindFirst2Response resp; + int sid; + String path = getUncPath0(); - /* only difference between first2 and next2 - * responses is subCommand so let's recycle - */ - response.subCommand = SmbComTransaction.TRANS2_FIND_NEXT2; - - while( response.isEndOfSearch == false && response.searchCount > 0 ) { - response.reset(); - sendTransaction( new Trans2FindNext2( sid, response.resumeKey, - response.lastName ), response ); - count = response.searchCount; - - for( i = 0; i < count; i++ ) { - filename = response.results[i].filename; - if( filename.length() < 3 ) { - int h = filename.hashCode(); - if( h == h1 || h == h2 ) { - continue; - } + if( url.toString().lastIndexOf( '/' ) != ( url.toString().length() - 1 )) { + throw new SmbException( SmbException.ERRCLI, + SmbException.ERRlistFiles, url.toString() + " directory must end with '/'" ); + } + + req = new Trans2FindFirst2( path, wildcard, searchAttributes ); + resp = new Trans2FindFirst2Response(); + + Log.println( Log.WARNINGS, "smb find warning", + " find with path=" + req.path ); + + sendTransaction( req, resp ); + + sid = resp.sid; + req = new Trans2FindNext2( sid, resp.resumeKey, resp.lastName ); + + /* The only difference between first2 and next2 responses is subCommand + * so let's recycle the response object. + */ + resp.subCommand = SmbComTransaction.TRANS2_FIND_NEXT2; + + for( ;; ) { + for( int i = 0; i < resp.numEntries; i++ ) { + FileEntry e = resp.results[i]; + String name = e.getName(); + if( name.length() < 3 ) { + int h = name.hashCode(); + if( h == HASH_DOT || h == HASH_DOT_DOT ) { + continue; + } + } + if( fnf != null && fnf.accept( this, name ) == false ) { + continue; + } + if( name.length() > 0 ) { + SmbFile f = new SmbFile( this, name, TYPE_FILESYSTEM, + e.getAttributes(), e.createTime(), e.lastModified(), e.length() ); + if( ff != null && ff.accept( f ) == false ) { + continue; + } + if( files ) { + list.add( f ); + } else { + list.add( name ); } - list.add( new SmbFile( this, - filename, - TYPE_FILESYSTEM, - response.results[i].extFileAttributes, - response.results[i].lastWriteTime, - response.results[i].endOfFile )); } } - } catch( UnknownHostException uhe ) { - throw new SmbException( SmbException.ERRCLI, - SmbException.ERRunknownHost, url.toString() ); - } catch( MalformedURLException mue ) { - throw new SmbException( SmbException.ERRCLI, - SmbException.ERRlistFiles, "Malformed URL: " + url.toString() ); + + if( resp.isEndOfSearch || resp.numEntries == 0 ) { + break; + } + + req.reset( resp.resumeKey, resp.lastName ); + resp.reset(); + sendTransaction( req, resp ); } send( new SmbComFindClose2( sid ), blank_resp ); - - return (SmbFile[])list.toArray(new SmbFile[list.size()]); } + /** * Changes the name of the file this SmbFile represents to the name * designated by the SmbFile argument(Remember: @@ -1516,6 +1569,18 @@ The shareAccess parameter controls what permissions other clients have } connect0(); dest.connect0(); + + if( tree.inDfs ) { /* This ensures that each path is + * resolved independantly to deal with the case where files + * have the same base path but ultimately turn out to be + * on different servers because of DFS. It also eliminates + * manipulating the SMB path which is problematic because + * there are really two that would need to be prefixed + * with host and share as DFS requires. + */ + exists(); + dest.exists(); + } if( tree != dest.tree ) { throw new SmbException( SmbException.ERRDOS, SmbException.ERRnoaccess, @@ -1604,12 +1669,14 @@ The shareAccess parameter controls what permissions other clients have if( attrExpiration < System.currentTimeMillis() ) { attributes = ATTR_READONLY | ATTR_DIRECTORY; + createTime = 0L; lastModified = 0L; isExists = false; Info info = queryPath( getUncPath0(), Trans2QueryPathInformationResponse.SMB_QUERY_FILE_BASIC_INFO ); attributes = info.getAttributes(); + createTime = info.getCreateTime(); lastModified = info.getLastWriteTime(); /* If any of the above fails, isExists will not be set true @@ -1632,13 +1699,14 @@ The shareAccess parameter controls what permissions other clients have } } - files = listFiles(); + files = listFiles( "*", ATTR_DIRECTORY | ATTR_HIDDEN | ATTR_SYSTEM, null, null ); try { for( i = 0; i < files.length; i++ ) { ndest = new SmbFile( dest, files[i].getName(), files[i].type, files[i].attributes, + files[i].createTime, files[i].lastModified, files[i].size ); files[i].copyTo0( ndest, b, bsize, w, req, resp ); @@ -1652,9 +1720,23 @@ The shareAccess parameter controls what permissions other clients have } } else { int off; + long mtime; - open( SmbFile.O_RDONLY ); - dest.open( SmbFile.O_CREAT | SmbFile.O_WRONLY | SmbFile.O_TRUNC ); + open( SmbFile.O_RDONLY, ATTR_NORMAL, 0 ); + try { + dest.open( SmbFile.O_CREAT | SmbFile.O_WRONLY | SmbFile.O_TRUNC | + SmbComNTCreateAndX.FILE_WRITE_ATTRIBUTES << 16, attributes, 0 ); + } catch( SmbAuthException sae ) { + if(( dest.attributes & ATTR_READONLY ) != 0 ) { + /* Remove READONLY and try again + */ + dest.setPathInformation( dest.attributes & ~ATTR_READONLY, 0L, 0L ); + dest.open( SmbFile.O_CREAT | SmbFile.O_WRONLY | SmbFile.O_TRUNC | + SmbComNTCreateAndX.FILE_WRITE_ATTRIBUTES << 16, attributes, 0 ); + } else { + throw sae; + } + } i = off = 0; for( ;; ) { @@ -1683,13 +1765,24 @@ The shareAccess parameter controls what permissions other clients have i = i == 1 ? 0 : 1; off += resp.dataLength; } - dest.close( lastModified - tree.session.transport.server.serverTimeZone * 60 * 1000 ); + + mtime = lastModified - tree.session.transport.server.serverTimeZone * 60 * 1000; + /* It doesn't look like the createTime should be adjusted (shrug) + */ + dest.sendTransaction( new Trans2SetFileInformation( dest.fid, attributes, createTime, mtime ), + new Trans2SetFileInformationResponse() ); + dest.close( mtime ); close(); } } /** - * This method will copy the file or directory and it's subcontents represented by this SmbFile to the location specified by the dest parameter. This file and the destination file do not need to be on the same host. This operation does not copy extended file attibutes and files copied to NT 4.0 will not preserve file modification times. -*/ + * This method will copy the file or directory and it's subcontents + * represented by this SmbFile to the location specified by the + * dest parameter. This file and the destination file do not + * need to be on the same host. This operation does not copy extended + * file attibutes such as ACLs but regular attributes and create and + * last write times will be preserved. + */ public void copyTo( SmbFile dest ) throws SmbException { SmbComReadAndX req; SmbComReadAndXResponse resp; @@ -1699,7 +1792,7 @@ The shareAccess parameter controls what permissions other clients have /* Should be able to copy an entire share actually */ - if( getUncPath0().length() == 1 || dest.getUncPath0().length() == 1 ) { + if( share == null || dest.share == null) { throw new SmbException( SmbException.ERRDOS, SmbException.ERRnoaccess, "Cannot copyTo workgroups, servers, or shares" ); @@ -1711,6 +1804,20 @@ The shareAccess parameter controls what permissions other clients have connect0(); dest.connect0(); + if( tree.inDfs ) { + /* At this point the maxBufferSize values are from the server + * exporting the volumes, not the one that we will actually + * end up performing IO with. If the server hosting the + * actual files has a smaller maxBufSize this could be + * incorrect. To handle this properly it is necessary + * to redirect the tree to the target server first before + * establishing buffer size. These exists() calls facilitate + * that. + */ + exists(); + dest.exists(); + } + w = new WriterThread(); w.setDaemon( true ); w.start(); @@ -1728,14 +1835,19 @@ The shareAccess parameter controls what permissions other clients have /** * This method will delete the file or directory specified by this * SmbFile. If the target is a directory, the contents of - * the directory will be deleted as well. If the resource or sub-resource - * is marked read-only or is locked the operation will fail. There is currently no functionality to set the attributes of a file, break locks, or disconnect users. + * the directory will be deleted as well. If a file within the directory or + * it's sub-directories is marked read-only, the read-only status will be removed and the file will be deleted. * * @throws SmbException */ public void delete() throws SmbException { - delete( getUncPath0() ); + if( tree == null || tree.inDfs ) { + exists(); /* This is necessary to ensure we + * pass a path adjusted for DFS */ + } + getUncPath0(); + delete( unc ); } void delete( String fileName ) throws SmbException { if( getUncPath0().length() == 1 ) { @@ -1743,16 +1855,36 @@ The shareAccess parameter controls what permissions other clients have SmbException.ERRnoaccess ); } + if( System.currentTimeMillis() > attrExpiration ) { + attributes = ATTR_READONLY | ATTR_DIRECTORY; + createTime = 0L; + lastModified = 0L; + isExists = false; + + Info info = queryPath( getUncPath0(), + Trans2QueryPathInformationResponse.SMB_QUERY_FILE_BASIC_INFO ); + attributes = info.getAttributes(); + createTime = info.getCreateTime(); + lastModified = info.getLastWriteTime(); + + attrExpiration = System.currentTimeMillis() + attrExpirationPeriod; + isExists = true; + } + + if(( attributes & ATTR_READONLY ) != 0 ) { + setReadWrite(); + } + /* * Delete or Delete Directory Request / Response */ - if( isDirectory() ) { + if(( attributes & ATTR_DIRECTORY ) != 0 ) { /* Recursively delete directory contents */ - SmbFile[] l = listFiles( fileName, "*" ); + SmbFile[] l = listFiles( "*", ATTR_DIRECTORY | ATTR_HIDDEN | ATTR_SYSTEM, null, null ); for( int i = 0; i < l.length; i++ ) { l[i].delete(); @@ -1881,6 +2013,66 @@ The shareAccess parameter controls what permissions other clients have mkdir(); } + public void createNewFile() throws SmbException { + if( getUncPath0().length() == 1 ) { + throw new SmbException( SmbException.ERRDOS, + SmbException.ERRnoaccess ); + } + close( open0( O_RDWR | O_EXCL, ATTR_NORMAL, 0 ), 0L ); + } + + void setPathInformation( int attrs, long ctime, long mtime ) throws SmbException { + int f = open0( O_RDONLY | SmbComNTCreateAndX.FILE_WRITE_ATTRIBUTES << 16, attrs, 0 ); + + sendTransaction( new Trans2SetFileInformation( f, attrs, ctime, mtime ), new Trans2SetFileInformationResponse() ); + + close( f, 0L ); + + attrExpiration = 0; + } + + public void setCreateTime( long time ) throws SmbException { + if( getUncPath0().length() == 1 ) { + throw new SmbException( SmbException.ERRDOS, + SmbException.ERRnoaccess ); + } + + setPathInformation( 0, time, 0L ); + } + public void setLastModified( long time ) throws SmbException { + if( getUncPath0().length() == 1 ) { + throw new SmbException( SmbException.ERRDOS, + SmbException.ERRnoaccess ); + } + + setPathInformation( 0, 0L, time ); + } + + public int getAttributes() throws SmbException { + if( getUncPath0().length() == 1 ) { + return 0; + } + exists(); + return attributes & ATTR_GET_MASK; + } + + public void setAttributes( int attrs ) throws SmbException { + if( getUncPath0().length() == 1 ) { + throw new SmbException( SmbException.ERRDOS, + SmbException.ERRnoaccess ); + } + + setPathInformation( attrs & ATTR_SET_MASK, 0L, 0L ); + } + + public void setReadOnly() throws SmbException { + setAttributes( getAttributes() | ATTR_READONLY ); + } + + public void setReadWrite() throws SmbException { + setAttributes( getAttributes() & ~ATTR_READONLY ); + } + /** * Returns a {@link java.net.URL} for this SmbFile. The * URL may be used as any other URL might to diff --git a/src/jcifs/smb/SmbFileFilter.java b/src/jcifs/smb/SmbFileFilter.java new file mode 100644 index 0000000..9f4468c --- /dev/null +++ b/src/jcifs/smb/SmbFileFilter.java @@ -0,0 +1,23 @@ +/* jcifs smb client library in Java + * Copyright (C) 2003 "Michael B. Allen" + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package jcifs.smb; + +public interface SmbFileFilter { + public boolean accept( SmbFile file ) throws SmbException; +} diff --git a/src/jcifs/smb/SmbFileInputStream.java b/src/jcifs/smb/SmbFileInputStream.java index b0a04a4..8207313 100644 --- a/src/jcifs/smb/SmbFileInputStream.java +++ b/src/jcifs/smb/SmbFileInputStream.java @@ -66,7 +66,7 @@ public class SmbFileInputStream extends InputStream { SmbFileInputStream( SmbFile file, int openFlags ) throws SmbException, MalformedURLException, UnknownHostException { this.file = file; this.openFlags = openFlags; - file.open( openFlags ); + file.open( openFlags, SmbFile.ATTR_NORMAL, 0 ); readSize = Math.min( file.tree.session.transport.rcv_buf_size - 70, file.tree.session.transport.server.maxBufferSize - 70 ); } @@ -118,7 +118,7 @@ public class SmbFileInputStream extends InputStream { long start = fp; // ensure file is open - file.open( openFlags ); + file.open( openFlags, SmbFile.ATTR_NORMAL, 0 ); //Log.println( Log.WARNINGS, "smb read warning", " fid=" + file.fid + ",off=" + off + ",len=" + len ); @@ -166,7 +166,7 @@ public class SmbFileInputStream extends InputStream { } pipe = (SmbNamedPipe)file; - file.open(( pipe.pipeType & 0xFF0000 ) | SmbFile.O_EXCL ); + file.open(( pipe.pipeType & 0xFF0000 ) | SmbFile.O_EXCL, SmbFile.ATTR_NORMAL, 0 ); req = new TransPeekNamedPipe( file.unc, file.fid ); resp = new TransPeekNamedPipeResponse( pipe ); diff --git a/src/jcifs/smb/SmbFileOutputStream.java b/src/jcifs/smb/SmbFileOutputStream.java index 44674a0..fb2e6a6 100644 --- a/src/jcifs/smb/SmbFileOutputStream.java +++ b/src/jcifs/smb/SmbFileOutputStream.java @@ -132,7 +132,7 @@ write, and/or delete the file while the jCIFS user has the file open. fp = 0L; } } - file.open( openFlags ); + file.open( openFlags, SmbFile.ATTR_NORMAL, 0 ); writeSize = Math.min( file.tree.session.transport.snd_buf_size - 70, file.tree.session.transport.server.maxBufferSize - 70 ); @@ -194,7 +194,7 @@ write, and/or delete the file while the jCIFS user has the file open. // ensure file is open if( file.isOpen() == false ) { - file.open( openFlags ); + file.open( openFlags, SmbFile.ATTR_NORMAL, 0 ); if( append ) { fp = file.length(); } diff --git a/src/jcifs/smb/SmbFilenameFilter.java b/src/jcifs/smb/SmbFilenameFilter.java new file mode 100644 index 0000000..26d3ece --- /dev/null +++ b/src/jcifs/smb/SmbFilenameFilter.java @@ -0,0 +1,23 @@ +/* jcifs smb client library in Java + * Copyright (C) 2003 "Michael B. Allen" + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package jcifs.smb; + +public interface SmbFilenameFilter { + public boolean accept( SmbFile dir, String name ) throws SmbException; +} diff --git a/src/jcifs/smb/SmbRandomAccessFile.java b/src/jcifs/smb/SmbRandomAccessFile.java new file mode 100644 index 0000000..739fb88 --- /dev/null +++ b/src/jcifs/smb/SmbRandomAccessFile.java @@ -0,0 +1,337 @@ +/* jcifs smb client library in Java + * Copyright (C) 2003 "Michael B. Allen" + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package jcifs.smb; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.UnknownHostException; +import jcifs.util.Encdec; + +public class SmbRandomAccessFile implements DataOutput, DataInput { + + private static final int WRITE_OPTIONS = 0x0842; + + SmbFile file; + long fp; + int openFlags, readSize, writeSize, ch, options = 0; + byte[] tmp = new byte[8]; + SmbComWriteAndXResponse write_andx_resp = null; + + public SmbRandomAccessFile( String url, String mode, int shareAccess ) + throws SmbException, MalformedURLException, UnknownHostException { + this( new SmbFile( url, "", null, shareAccess ), mode ); + } + public SmbRandomAccessFile( SmbFile file, String mode ) + throws SmbException, MalformedURLException, UnknownHostException { + this.file = file; + if( mode.equals( "r" )) { + this.openFlags = SmbFile.O_CREAT | SmbFile.O_RDONLY; + } else if( mode.equals( "rw" )) { + this.openFlags = SmbFile.O_CREAT | SmbFile.O_RDWR | SmbFile.O_APPEND; + write_andx_resp = new SmbComWriteAndXResponse(); + options = WRITE_OPTIONS; + } else { + throw new IllegalArgumentException( "Invalid mode" ); + } + file.open( openFlags, SmbFile.ATTR_NORMAL, options ); + readSize = Math.min( file.tree.session.transport.rcv_buf_size - 70, + file.tree.session.transport.server.maxBufferSize - 70 ); + writeSize = Math.min( file.tree.session.transport.snd_buf_size - 70, + file.tree.session.transport.server.maxBufferSize - 70 ); + fp = 0L; + } + + public int read() throws SmbException { + if( read( tmp, 0, 1 ) == -1 ) { + return -1; + } + return tmp[0] & 0xFF; + } + public int read( byte b[] ) throws SmbException { + return read( b, 0, b.length ); + } + public int read( byte b[], int off, int len ) throws SmbException { + if( len <= 0 ) { + return 0; + } + long start = fp; + + // ensure file is open + if( file.isOpen() == false ) { + file.open( openFlags, SmbFile.ATTR_NORMAL, options ); + } + + int r, n; + SmbComReadAndXResponse response = new SmbComReadAndXResponse( b, off ); + do { + r = len > readSize ? readSize : len; + file.send( new SmbComReadAndX( file.fid, fp, r, null ), response ); + if(( n = response.dataLength ) <= 0 ) { + return (int)((fp - start) > 0L ? fp - start : -1); + } + fp += n; + len -= n; + response.off += n; + } while( len > 0 && n == r ); + + return (int)(fp - start); + } + public final void readFully( byte b[] ) throws SmbException { + readFully( b, 0, b.length ); + } + public final void readFully( byte b[], int off, int len ) throws SmbException { + int n = 0, count; + + do { + count = this.read( b, off + n, len - n ); + if( count < 0 ) throw new SmbException( SmbException.ERRCLI, SmbException.ERRioe, "EOF" ); + n += count; + fp += count; + } while( n < len ); + } + public int skipBytes( int n ) throws SmbException { + if (n > 0) { + fp += n; + return n; + } + return 0; + } + + public void write( int b ) throws SmbException { + tmp[0] = (byte)b; + write( tmp, 0, 1 ); + } + public void write( byte b[] ) throws SmbException { + write( b, 0, b.length ); + } + public void write( byte b[], int off, int len ) throws SmbException { + if( len <= 0 ) { + return; + } + + // ensure file is open + if( file.isOpen() == false ) { + file.open( openFlags, SmbFile.ATTR_NORMAL, options ); + } + + int w; + do { + w = len > writeSize ? writeSize : len; + file.send( new SmbComWriteAndX( file.fid, fp, len - w, b, off, w, null ), write_andx_resp ); + fp += write_andx_resp.count; + len -= write_andx_resp.count; + off += write_andx_resp.count; + } while( len > 0 ); + } + public long getFilePointer() throws SmbException { + return fp; + } + public void seek( long pos ) throws SmbException { + fp = pos; + } + public long length() throws SmbException { + return file.length(); + } + public void setLength( long newLength ) throws SmbException { + // ensure file is open + if( file.isOpen() == false ) { + file.open( openFlags, SmbFile.ATTR_NORMAL, options ); + } + SmbComWriteResponse rsp = new SmbComWriteResponse(); + file.send( new SmbComWrite( file.fid, (int)(newLength & 0xFFFFFFFFL), 0, tmp, 0, 0 ), rsp ); + } + public void close() throws SmbException { + file.close(); + } + + public final boolean readBoolean() throws SmbException { + if((read( tmp, 0, 1 )) < 0 ) { + throw new SmbException( SmbException.ERRCLI, SmbException.ERRioe, "EOF" ); + } + return tmp[0] != (byte)0x00; + } + public final byte readByte() throws SmbException { + if((read( tmp, 0, 1 )) < 0 ) { + throw new SmbException( SmbException.ERRCLI, SmbException.ERRioe, "EOF" ); + } + return tmp[0]; + } + public final int readUnsignedByte() throws SmbException { + if((read( tmp, 0, 1 )) < 0 ) { + throw new SmbException( SmbException.ERRCLI, SmbException.ERRioe, "EOF" ); + } + return tmp[0] & 0xFF; + } + public final short readShort() throws SmbException { + if((read( tmp, 0, 2 )) < 0 ) { + throw new SmbException( SmbException.ERRCLI, SmbException.ERRioe, "EOF" ); + } + return Encdec.dec_uint16be( tmp, 0 ); + } + public final int readUnsignedShort() throws SmbException { + if((read( tmp, 0, 2 )) < 0 ) { + throw new SmbException( SmbException.ERRCLI, SmbException.ERRioe, "EOF" ); + } + return Encdec.dec_uint16be( tmp, 0 ); + } + public final char readChar() throws SmbException { + if((read( tmp, 0, 2 )) < 0 ) { + throw new SmbException( SmbException.ERRCLI, SmbException.ERRioe, "EOF" ); + } + return (char)Encdec.dec_uint16be( tmp, 0 ); + } + public final int readInt() throws SmbException { + if((read( tmp, 0, 4 )) < 0 ) { + throw new SmbException( SmbException.ERRCLI, SmbException.ERRioe, "EOF" ); + } + return Encdec.dec_uint32be( tmp, 0 ); + } + public final long readLong() throws SmbException { + if((read( tmp, 0, 8 )) < 0 ) { + throw new SmbException( SmbException.ERRCLI, SmbException.ERRioe, "EOF" ); + } + return Encdec.dec_uint64be( tmp, 0 ); + } + public final float readFloat() throws SmbException { + if((read( tmp, 0, 4 )) < 0 ) { + throw new SmbException( SmbException.ERRCLI, SmbException.ERRioe, "EOF" ); + } + return Encdec.dec_floatbe( tmp, 0 ); + } + public final double readDouble() throws SmbException { + if((read( tmp, 0, 8 )) < 0 ) { + throw new SmbException( SmbException.ERRCLI, SmbException.ERRioe, "EOF" ); + } + return Encdec.dec_doublebe( tmp, 0 ); + } + public final String readLine() throws SmbException { + StringBuffer input = new StringBuffer(); + int c = -1; + boolean eol = false; + + while (!eol) { + switch( c = read() ) { + case -1: + case '\n': + eol = true; + break; + case '\r': + eol = true; + long cur = fp; + if( read() != '\n' ) { + fp = cur; + } + break; + default: + input.append( (char)c ); + break; + } + } + + if ((c == -1) && (input.length() == 0)) { + return null; + } + + return input.toString(); + } + + public final String readUTF() throws SmbException { + int size = readUnsignedShort(); + byte[] b = new byte[size]; + read( b, 0, size ); + try { + return Encdec.dec_utf8( b, 0, size ); + } catch( IOException ioe ) { + throw new SmbException( SmbException.ERRCLI, SmbException.ERRioe, ioe.getMessage() ); + } + } + public final void writeBoolean( boolean v ) throws SmbException { + tmp[0] = (byte)(v ? 1 : 0); + write( tmp, 0, 1 ); + } + public final void writeByte( int v ) throws SmbException { + tmp[0] = (byte)v; + write( tmp, 0, 1 ); + } + public final void writeShort( int v ) throws SmbException { + Encdec.enc_uint16be( (short)v, tmp, 0 ); + write( tmp, 0, 2 ); + } + public final void writeChar( int v ) throws SmbException { + Encdec.enc_uint16be( (short)v, tmp, 0 ); + write( tmp, 0, 2 ); + } + public final void writeInt( int v ) throws SmbException { + Encdec.enc_uint32be( v, tmp, 0 ); + write( tmp, 0, 4 ); + } + public final void writeLong( long v ) throws SmbException { + Encdec.enc_uint64be( v, tmp, 0 ); + write( tmp, 0, 8 ); + } + public final void writeFloat( float v ) throws SmbException { + Encdec.enc_floatbe( v, tmp, 0 ); + write( tmp, 0, 4 ); + } + public final void writeDouble( double v ) throws SmbException { + Encdec.enc_doublebe( v, tmp, 0 ); + write( tmp, 0, 8 ); + } + public final void writeBytes( String s ) throws SmbException { + int len = s.length(); + byte[] b = new byte[len]; + s.getBytes( 0, len, b, 0 ); + write( b, 0, len ); + } + public final void writeChars( String s ) throws SmbException { + int clen = s.length(); + int blen = 2 * clen; + byte[] b = new byte[blen]; + char[] c = new char[clen]; + s.getChars( 0, clen, c, 0 ); + for( int i = 0, j = 0; i < clen; i++ ) { + b[j++] = (byte)(c[i] >>> 8); + b[j++] = (byte)(c[i] >>> 0); + } + write( b, 0, blen ); + } + public final void writeUTF( String str ) throws SmbException { + int len = str.length(); + int ch, size = 0; + byte[] dst; + + for( int i = 0; i < len; i++ ) { + ch = str.charAt( i ); + size += ch > 0x07F ? (ch > 0x7FF ? 3 : 2) : 1; + } + dst = new byte[size]; + writeShort( size ); + try { + Encdec.enc_utf8( str, dst, 0, size ); + } catch( IOException ioe ) { + throw new SmbException( SmbException.ERRCLI, SmbException.ERRioe, ioe.getMessage() ); + } + write( dst, 0, size ); + } +} + diff --git a/src/jcifs/smb/SmbSession.java b/src/jcifs/smb/SmbSession.java index 71b6771..a14a8dc 100644 --- a/src/jcifs/smb/SmbSession.java +++ b/src/jcifs/smb/SmbSession.java @@ -92,6 +92,7 @@ public final class SmbSession { // transactions are not batchable sessionSetup( null, null ); request.uid = uid; + request.auth = auth; transport.sendTransaction( request, response ); } void send( ServerMessageBlock request, @@ -104,13 +105,12 @@ public final class SmbSession { return; } request.uid = uid; + request.auth = auth; transport.send( request, response ); } void sessionSetup( ServerMessageBlock andx, ServerMessageBlock andxResponse ) throws SmbException { synchronized( transport ) { - SmbComSessionSetupAndXResponse response = null; - if( sessionSetup ) { return; } @@ -134,8 +134,10 @@ synchronized( transport ) { * Session Setup And X Request / Response */ - response = new SmbComSessionSetupAndXResponse( andxResponse ); - transport.send( new SmbComSessionSetupAndX( this, andx ), response ); + SmbComSessionSetupAndX request = new SmbComSessionSetupAndX( this, andx ); + SmbComSessionSetupAndXResponse response = new SmbComSessionSetupAndXResponse( andxResponse ); + request.auth = auth; + transport.send( request, response ); uid = response.uid; sessionSetup = true; diff --git a/src/jcifs/smb/SmbTransport.java b/src/jcifs/smb/SmbTransport.java index cabd7da..8b75348 100644 --- a/src/jcifs/smb/SmbTransport.java +++ b/src/jcifs/smb/SmbTransport.java @@ -62,6 +62,9 @@ class SmbTransport implements Runnable { static final int FLAGS_OFFSET = 9; static final int FLAGS_RESPONSE = 0x80; + static final int ST_GROUND = 0; + static final int ST_NEGOTIATING = 1; + private NbtSocket socket; // should become UniSocket? private InputStream in; private OutputStream out; @@ -97,9 +100,13 @@ private static byte[] rcv_buf = new byte[0xFFFF]; int ssnLimit = Config.getInt( "jcifs.smb.client.ssnLimit", DEFAULT_SSN_LIMIT ); + int state; + ClientProperties client = new ClientProperties(); ServerProperties server = new ServerProperties(); + LinkedList referrals = new LinkedList(); + class ClientProperties { int maxMpxCount = Config.getInt( "jcifs.smb.client.maxMpxCount", DEFAULT_MAX_MPX_COUNT ); @@ -111,9 +118,12 @@ private static byte[] rcv_buf = new byte[0xFFFF]; * but we could do raw and mpx */ int flags2 = Config.getInt( "jcifs.smb.client.flags2", - ServerMessageBlock.FLAGS2_LONG_FILENAMES | ServerMessageBlock.FLAGS2_UNICODE ); + ServerMessageBlock.FLAGS2_LONG_FILENAMES | + ServerMessageBlock.FLAGS2_EXTENDED_ATTRIBUTES | + ServerMessageBlock.FLAGS2_UNICODE ); int capabilities = Config.getInt( "jcifs.smb.client.capabilities", - ServerMessageBlock.CAP_UNICODE | ServerMessageBlock.CAP_NT_SMBS ); + ServerMessageBlock.CAP_UNICODE | + ServerMessageBlock.CAP_NT_SMBS); String nativeOs = Config.getProperty( "jcifs.smb.client.nativeOs", System.getProperty( "os.name" )); String nativeLanMan = Config.getProperty( "jcifs.smb.client.nativeLanMan", "jCIFS" ); @@ -193,7 +203,7 @@ private static byte[] rcv_buf = new byte[0xFFFF]; sessions = new LinkedList(); responseTable = new Hashtable(); outLock = new Object(); - negotiated = false; + state = ST_GROUND; } synchronized SmbSession getSmbSession() { @@ -241,7 +251,9 @@ private static byte[] rcv_buf = new byte[0xFFFF]; localPort == this.localPort; } boolean hasCapability( int cap ) throws SmbException { - negotiate(); + if (state == ST_GROUND) { + negotiate(); + } return (negotiatedCapabilities & cap) == cap; } void ensureOpen() throws IOException { @@ -323,9 +335,9 @@ private static byte[] rcv_buf = new byte[0xFFFF]; in = null; out = null; socket = null; - negotiated = false; thread = null; responseTable.clear(); + state = ST_GROUND; } public void run() { int mid, l, i, n, m; @@ -422,7 +434,6 @@ synchronized( rcv_buf ) { } Log.printHexDump( "smb received", rcv_buf, 0, response.length ); if( useSigning ) { - int length = response.length; response.verifyFailed = verify(rcv_buf, 0, response); } @@ -579,15 +590,68 @@ synchronized( rcv_buf ) { } return false; - } + } + synchronized DfsReferral getDfsReferral( NtlmPasswordAuthentication auth, String path ) throws SmbException { + String subpath, node, host; + DfsReferral dr = new DfsReferral(); + int p, n, i, s; + UniAddress addr; + + SmbTree ipc = getSmbSession( auth ).getSmbTree( "IPC$", null ); + Trans2GetDfsReferralResponse resp = new Trans2GetDfsReferralResponse(); + ipc.sendTransaction( new Trans2GetDfsReferral( path ), resp ); + + subpath = path.substring( 0, resp.pathConsumed ); + node = resp.referral.node; + if( subpath.charAt( 0 ) != '\\' || + (i = subpath.indexOf( '\\', 1 )) < 2 || + (p = subpath.indexOf( '\\', i + 1 )) < (i + 2) || + node.charAt( 0 ) != '\\' || + (s = node.indexOf( '\\', 1 )) < 2) { + throw new SmbException( SmbException.ERRCLI, SmbException.ERRdfs, "Invalid DFS path: " + path ); + } + if ((n = node.indexOf( '\\', s + 1 )) == -1) { + n = node.length(); + } + + dr.path = subpath.substring( p ); + dr.node = node.substring( 0, n ); + dr.nodepath = node.substring( n ); + dr.server = node.substring( 1, s ); + dr.share = node.substring( s, n ); + dr.resolveHashes = auth.hashesExternal; /* NTLM HTTP Authentication must be re-negotiated + * with challenge from 'server' to access DFS vol. */ + return dr; + } + synchronized DfsReferral lookupReferral( String unc ) { + DfsReferral dr; + ListIterator iter = referrals.listIterator(); + int i, len; + + while( iter.hasNext() ) { + dr = (DfsReferral)iter.next(); + len = dr.path.length(); + for( i = 0; i < len && i < unc.length(); i++ ) { + if( dr.path.charAt( i ) != unc.charAt( i )) { + break; + } + } + if( i == len ) { + return dr; + } + } + return null; + } void send( ServerMessageBlock request, ServerMessageBlock response ) throws SmbException { Integer mid = null; - negotiate(); + if (state == ST_GROUND) { + negotiate(); + } - request.flags2 = client.flags2; + request.flags2 |= client.flags2; request.mid = aquireMid(); request.useUnicode = useUnicode; @@ -688,6 +752,13 @@ synchronized( snd_buf ) { throw new SmbException( response.errorCode ); case SmbException.ERRSRV: switch(( response.errorCode >> 16 ) & 0xFFFF ) { + case SmbException.ERRreserved: + if( request.auth == null ) { + throw new SmbException( response.errorCode ); + } + DfsReferral dr = getDfsReferral( request.auth, request.path ); + referrals.add( dr ); + throw dr; case SmbException.ERRbadpw: case SmbException.ERRaccess: case SmbException.ERRaccountExpired: @@ -707,7 +778,7 @@ synchronized( snd_buf ) { negotiate(); - request.flags2 = client.flags2; + request.flags2 |= client.flags2; request.mid = aquireMid(); mid = new Integer( request.mid ); request.useUnicode = useUnicode; @@ -762,6 +833,13 @@ synchronized(snd_buf) { throw new SmbException( response.errorCode ); case SmbException.ERRSRV: switch(( interimResponse.errorCode >> 16 ) & 0xFFFF ) { + case SmbException.ERRreserved: + if( request.auth == null ) { + throw new SmbException( response.errorCode ); + } + DfsReferral dr = getDfsReferral( request.auth, request.path ); + referrals.add( dr ); + throw dr; case SmbException.ERRbadpw: case SmbException.ERRaccess: case SmbException.ERRaccountExpired: @@ -837,6 +915,13 @@ synchronized( snd_buf ) { throw new SmbException( response.errorCode ); case SmbException.ERRSRV: switch(( response.errorCode >> 16 ) & 0xFFFF ) { + case SmbException.ERRreserved: + if( request.auth == null ) { + throw new SmbException( response.errorCode ); + } + DfsReferral dr = getDfsReferral( request.auth, request.path ); + referrals.add( dr ); + throw dr; case SmbException.ERRbadpw: case SmbException.ERRaccess: case SmbException.ERRaccountExpired: @@ -851,15 +936,10 @@ synchronized( snd_buf ) { } synchronized void negotiate() throws SmbException { - if( negotiated ) { + if( state >= ST_NEGOTIATING ) { return; } - /* we must set this here rather than later because this - * calls send() which calls negotiate so we don't want to - * end up in a loop. The alternative would be to inline - * the send routine here but this seems okay for now - */ - negotiated = true; + state = ST_NEGOTIATING; Log.println( Log.WARNINGS, "smb negotiation warning", " requesting negotiation with " + address ); diff --git a/src/jcifs/smb/SmbTree.java b/src/jcifs/smb/SmbTree.java index 879ecbf..7c5ea58 100644 --- a/src/jcifs/smb/SmbTree.java +++ b/src/jcifs/smb/SmbTree.java @@ -19,9 +19,9 @@ package jcifs.smb; import java.io.IOException; +import java.net.UnknownHostException; import jcifs.UniAddress; import jcifs.netbios.NbtAddress; -import java.net.UnknownHostException; class SmbTree { @@ -29,7 +29,7 @@ class SmbTree { String share; String service = jcifs.Config.getProperty( "jcifs.smb.client.serviceType", "?????" ); SmbSession session; - boolean treeConnected; + boolean treeConnected, inDfs; SmbTree( SmbSession session, String share, String service ) { this.session = session; @@ -56,6 +56,7 @@ class SmbTree { case SmbComTransaction.TRANS_WAIT_NAMED_PIPE: case SmbComTransaction.TRANS_CALL_NAMED_PIPE: case SmbComTransaction.TRANS_TRANSACT_NAMED_PIPE: + case SmbComTransaction.TRANS2_GET_DFS_REFERRAL: break; default: throw new SmbException( SmbException.ERRCLI, SmbException.ERRioe, @@ -63,6 +64,9 @@ class SmbTree { } } request.tid = tid; + if( inDfs && request.path != null && request.path.length() > 0 ) { + request.path = '\\' + session.transport.server.tconHostName + '\\' + share + request.path; + } session.sendTransaction( request, response ); } void send( ServerMessageBlock request, @@ -76,7 +80,6 @@ class SmbTree { } if( service.equals( "A:" ) == false ) { switch( request.command ) { - case ServerMessageBlock.SMB_COM_WRITE: case ServerMessageBlock.SMB_COM_OPEN_ANDX: case ServerMessageBlock.SMB_COM_NT_CREATE_ANDX: case ServerMessageBlock.SMB_COM_READ_ANDX: @@ -90,6 +93,10 @@ class SmbTree { } } request.tid = tid; + if( inDfs && request.path != null && request.path.length() > 0 ) { + request.flags2 = ServerMessageBlock.FLAGS2_RESOLVE_PATHS_IN_DFS; + request.path = '\\' + session.transport.server.tconHostName + '\\' + share + request.path; + } session.send( request, response ); } void treeConnect( ServerMessageBlock andx, @@ -108,7 +115,7 @@ synchronized( session.transport ) { session.transport.negotiate(); - unc = "\\\\" + session.transport.server.tconHostName + "\\" + share; + unc = "\\\\" + session.transport.server.tconHostName + '\\' + share; Log.println( Log.WARNINGS, "smb tree connect warning", " requesting tree connect with unc=" + unc + @@ -126,6 +133,7 @@ synchronized( session.transport ) { tid = response.tid; service = response.service; + inDfs = response.shareIsInDfs; treeConnected = true; } } @@ -147,6 +155,8 @@ synchronized( session.transport ) { public String toString() { return "SmbTree[share=" + share + ",service=" + service + - ",tid=" + tid + "]"; + ",tid=" + tid + + ",inDfs=" + inDfs + + ",treeConnected=" + treeConnected + "]"; } } diff --git a/src/jcifs/smb/Trans2FindFirst2.java b/src/jcifs/smb/Trans2FindFirst2.java index 24cd0ee..08ad662 100644 --- a/src/jcifs/smb/Trans2FindFirst2.java +++ b/src/jcifs/smb/Trans2FindFirst2.java @@ -50,14 +50,19 @@ class Trans2FindFirst2 extends SmbComTransaction { int flags; int informationLevel; int searchStorageType = 0; - String filename; - - Trans2FindFirst2( String filename ) { - this.filename = filename; + String wildcard; + + Trans2FindFirst2( String filename, String wildcard, int searchAttributes ) { + if( filename.equals( "\\" )) { + this.path = filename; + } else { + this.path = filename + "\\"; + } + this.wildcard = wildcard; + this.searchAttributes = searchAttributes & SmbFile.ATTR_GET_MASK; command = SMB_COM_TRANSACTION2; subCommand = TRANS2_FIND_FIRST2; - searchAttributes = ATTR_DIRECTORY | ATTR_HIDDEN | ATTR_SYSTEM; searchCount = Config.getInt( "jcifs.smb.client.listCount", DEFAULT_SEARCH_COUNT ); flags = 0x00; informationLevel = SMB_FILE_BOTH_DIRECTORY_INFO; @@ -86,7 +91,7 @@ class Trans2FindFirst2 extends SmbComTransaction { dstIndex += 2; writeInt4( searchStorageType, dst, dstIndex ); dstIndex += 4; - dstIndex += writeString( filename, dst, dstIndex ); + dstIndex += writeString( path + wildcard, dst, dstIndex ); return dstIndex - start; } @@ -109,6 +114,6 @@ class Trans2FindFirst2 extends SmbComTransaction { ",flags=0x" + Log.getHexString( flags, 2 ) + ",informationLevel=0x" + Log.getHexString( informationLevel, 3 ) + ",searchStorageType=" + searchStorageType + - ",filename=" + filename + "]" ); + ",filename=" + path + "]" ); } } diff --git a/src/jcifs/smb/Trans2FindFirst2Response.java b/src/jcifs/smb/Trans2FindFirst2Response.java index 1f3c819..b3d1546 100644 --- a/src/jcifs/smb/Trans2FindFirst2Response.java +++ b/src/jcifs/smb/Trans2FindFirst2Response.java @@ -33,7 +33,7 @@ class Trans2FindFirst2Response extends SmbComTransactionResponse { static final int SMB_FILE_NAMES_INFO = 0x103; static final int SMB_FILE_BOTH_DIRECTORY_INFO = 0x104; - class SmbFindFileBothDirectoryInfo { + class SmbFindFileBothDirectoryInfo implements FileEntry { int nextEntryOffset; int fileIndex; long creationTime; @@ -49,6 +49,25 @@ class Trans2FindFirst2Response extends SmbComTransactionResponse { String shortName; String filename; + public String getName() { + return filename; + } + public int getType() { + return SmbFile.TYPE_FILESYSTEM; + } + public int getAttributes() { + return extFileAttributes; + } + public long createTime() { + return creationTime; + } + public long lastModified() { + return lastWriteTime; + } + public long length() { + return endOfFile; + } + public String toString() { return new String( "SmbFindFileBothDirectoryInfo[" + "nextEntryOffset=" + nextEntryOffset + @@ -69,14 +88,12 @@ class Trans2FindFirst2Response extends SmbComTransactionResponse { } int sid; - int searchCount; boolean isEndOfSearch; int eaErrorOffset; int lastNameOffset, lastNameBufferIndex; String lastName; int resumeKey; - SmbFindFileBothDirectoryInfo[] results; Trans2FindFirst2Response() { command = SMB_COM_TRANSACTION2; @@ -132,7 +149,7 @@ class Trans2FindFirst2Response extends SmbComTransactionResponse { sid = readInt2( buffer, bufferIndex ); bufferIndex += 2; } - searchCount = readInt2( buffer, bufferIndex ); + numEntries = readInt2( buffer, bufferIndex ); bufferIndex += 2; isEndOfSearch = ( buffer[bufferIndex] & 0x01 ) == 0x01 ? true : false; bufferIndex += 2; @@ -145,36 +162,34 @@ class Trans2FindFirst2Response extends SmbComTransactionResponse { } int readDataWireFormat( byte[] buffer, int bufferIndex, int len ) { int start = bufferIndex; + SmbFindFileBothDirectoryInfo e; lastNameBufferIndex = bufferIndex + lastNameOffset; - results = new SmbFindFileBothDirectoryInfo[searchCount]; - - for( int i = 0; i < searchCount; i++ ) { - results[i] = new SmbFindFileBothDirectoryInfo(); - - results[i].nextEntryOffset = readInt4( buffer, bufferIndex ); - results[i].fileIndex = readInt4( buffer, bufferIndex + 4 ); - // results[i].creationTime = readTime( buffer, bufferIndex + 8 ); - // results[i].lastAccessTime = readTime( buffer, bufferIndex + 16 ); - results[i].lastWriteTime = readTime( buffer, bufferIndex + 24 ); - // results[i].changeTime = readTime( buffer, bufferIndex + 32 ); - results[i].endOfFile = readLong( buffer, bufferIndex + 40 ); - // results[i].allocationSize = readLong( buffer, bufferIndex + 48 ); - results[i].extFileAttributes = readInt4( buffer, bufferIndex + 56 ); - results[i].fileNameLength = readInt4( buffer, bufferIndex + 60 ); - // results[i].eaSize = readInt4( buffer, bufferIndex + 64 ); - // results[i].shortNameLength = buffer[bufferIndex + 68] & 0xFF; + results = new SmbFindFileBothDirectoryInfo[numEntries]; + for( int i = 0; i < numEntries; i++ ) { + results[i] = e = new SmbFindFileBothDirectoryInfo(); + + e.nextEntryOffset = readInt4( buffer, bufferIndex ); + e.fileIndex = readInt4( buffer, bufferIndex + 4 ); + e.creationTime = readTime( buffer, bufferIndex + 8 ); + // e.lastAccessTime = readTime( buffer, bufferIndex + 16 ); + e.lastWriteTime = readTime( buffer, bufferIndex + 24 ); + // e.changeTime = readTime( buffer, bufferIndex + 32 ); + e.endOfFile = readLong( buffer, bufferIndex + 40 ); + // e.allocationSize = readLong( buffer, bufferIndex + 48 ); + e.extFileAttributes = readInt4( buffer, bufferIndex + 56 ); + e.fileNameLength = readInt4( buffer, bufferIndex + 60 ); + // e.eaSize = readInt4( buffer, bufferIndex + 64 ); + // e.shortNameLength = buffer[bufferIndex + 68] & 0xFF; /* With NT, the shortName is in Unicode regardless of what is negotiated. */ - // results[i].shortName = readString( buffer, bufferIndex + 70, - // results[i].shortNameLength ); - results[i].filename = readString( buffer, bufferIndex + 94, - results[i].fileNameLength ); + // e.shortName = readString( buffer, bufferIndex + 70, e.shortNameLength ); + e.filename = readString( buffer, bufferIndex + 94, e.fileNameLength ); -//Log.println( Log.DEBUGGING, "Trans2FindFirst2/Next2Response debugging", "bufferIndex=" + bufferIndex + ",lastNameBufferIndex=" + lastNameBufferIndex + ",nextEntryOffet=" + ( bufferIndex + results[i].nextEntryOffset )); +//Log.println( Log.DEBUGGING, "Trans2FindFirst2/Next2Response debugging", "bufferIndex=" + bufferIndex + ",lastNameBufferIndex=" + lastNameBufferIndex + ",nextEntryOffet=" + ( bufferIndex + e.nextEntryOffset )); /* lastNameOffset ends up pointing to either to * the exact location of the filename(e.g. Win98) @@ -184,16 +199,15 @@ class Trans2FindFirst2Response extends SmbComTransactionResponse { * entry and the next entry. */ - if( lastNameBufferIndex >= bufferIndex && - ( results[i].nextEntryOffset == 0 || - lastNameBufferIndex < ( bufferIndex + results[i].nextEntryOffset ))) { - lastName = results[i].filename; - resumeKey = results[i].fileIndex; + if( lastNameBufferIndex >= bufferIndex && ( e.nextEntryOffset == 0 || + lastNameBufferIndex < ( bufferIndex + e.nextEntryOffset ))) { + lastName = e.filename; + resumeKey = e.fileIndex; } -//Log.println( Log.DEBUGGING, "info entry", results[i].toString() ); +//Log.println( Log.DEBUGGING, "info entry", e ); - bufferIndex += results[i].nextEntryOffset; + bufferIndex += e.nextEntryOffset; } @@ -219,7 +233,7 @@ class Trans2FindFirst2Response extends SmbComTransactionResponse { } return new String( c + super.toString() + ",sid=" + sid + - ",searchCount=" + searchCount + + ",searchCount=" + numEntries + ",isEndOfSearch=" + isEndOfSearch + ",eaErrorOffset=" + eaErrorOffset + ",lastNameOffset=" + lastNameOffset + diff --git a/src/jcifs/smb/Trans2FindNext2.java b/src/jcifs/smb/Trans2FindNext2.java index 27425a3..c02ae47 100644 --- a/src/jcifs/smb/Trans2FindNext2.java +++ b/src/jcifs/smb/Trans2FindNext2.java @@ -41,6 +41,13 @@ class Trans2FindNext2 extends SmbComTransaction { maxSetupCount = 0; } + public void reset( int resumeKey, String lastName ) { + super.reset(); + this.resumeKey = resumeKey; + this.filename = lastName; + flags2 = 0; + } + int writeSetupWireFormat( byte[] dst, int dstIndex ) { dst[dstIndex++] = subCommand; dst[dstIndex++] = (byte)0x00; diff --git a/src/jcifs/smb/Trans2GetDfsReferral.java b/src/jcifs/smb/Trans2GetDfsReferral.java new file mode 100644 index 0000000..e8952f3 --- /dev/null +++ b/src/jcifs/smb/Trans2GetDfsReferral.java @@ -0,0 +1,66 @@ +/* jcifs smb client library in Java + * Copyright (C) 2000 "Michael B. Allen" + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package jcifs.smb; + +class Trans2GetDfsReferral extends SmbComTransaction { + + int maxReferralLevel = 3; + + Trans2GetDfsReferral( String filename ) { + path = filename; + command = SMB_COM_TRANSACTION2; + subCommand = TRANS2_GET_DFS_REFERRAL; + totalDataCount = 0; + maxParameterCount = 0; + maxDataCount = 4096; + maxSetupCount = (byte)0x00; + } + + int writeSetupWireFormat( byte[] dst, int dstIndex ) { + dst[dstIndex++] = subCommand; + dst[dstIndex++] = (byte)0x00; + return 2; + } + int writeParametersWireFormat( byte[] dst, int dstIndex ) { + int start = dstIndex; + + writeInt2( maxReferralLevel, dst, dstIndex ); + dstIndex += 2; + dstIndex += writeString( path, dst, dstIndex ); + + return dstIndex - start; + } + int writeDataWireFormat( byte[] dst, int dstIndex ) { + return 0; + } + int readSetupWireFormat( byte[] buffer, int bufferIndex, int len ) { + return 0; + } + int readParametersWireFormat( byte[] buffer, int bufferIndex, int len ) { + return 0; + } + int readDataWireFormat( byte[] buffer, int bufferIndex, int len ) { + return 0; + } + public String toString() { + return new String( "Trans2GetDfsReferral[" + super.toString() + + ",maxReferralLevel=0x" + maxReferralLevel + + ",filename=" + path + "]" ); + } +} diff --git a/src/jcifs/smb/Trans2GetDfsReferralResponse.java b/src/jcifs/smb/Trans2GetDfsReferralResponse.java new file mode 100644 index 0000000..9c51f33 --- /dev/null +++ b/src/jcifs/smb/Trans2GetDfsReferralResponse.java @@ -0,0 +1,139 @@ +/* jcifs smb client library in Java + * Copyright (C) 2003 "Michael B. Allen" + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package jcifs.smb; + +import java.util.Date; + +class Trans2GetDfsReferralResponse extends SmbComTransactionResponse { + + class Referral { + int version; + int size; + int serverType; + int flags; + int proximity; + int ttl; + int pathOffset; + int altPathOffset; + int nodeOffset; + String path; + String altPath; + String node; + + int readWireFormat( byte[] buffer, int bufferIndex, int len ) { + int start = bufferIndex; + + version = readInt2( buffer, bufferIndex ); +if( version != 3 && version != 1 ) { + throw new RuntimeException( "Version " + version + " referral not supported. Please report this to jcifs at samba dot org." ); +} + bufferIndex += 2; + size = readInt2( buffer, bufferIndex ); + bufferIndex += 2; + serverType = readInt2( buffer, bufferIndex ); + bufferIndex += 2; + flags = readInt2( buffer, bufferIndex ); + bufferIndex += 2; + if( version == 3 ) { + proximity = readInt2( buffer, bufferIndex ); + bufferIndex += 2; + ttl = readInt2( buffer, bufferIndex ); + bufferIndex += 2; + pathOffset = readInt2( buffer, bufferIndex ); + bufferIndex += 2; + altPathOffset = readInt2( buffer, bufferIndex ); + bufferIndex += 2; + nodeOffset = readInt2( buffer, bufferIndex ); + bufferIndex += 2; + + path = readString( buffer, start + pathOffset, len, (flags2 & FLAGS2_UNICODE) != 0); + node = readString( buffer, start + nodeOffset, len, (flags2 & FLAGS2_UNICODE) != 0); + } else if( version == 1 ) { + node = readString( buffer, bufferIndex, len, (flags2 & FLAGS2_UNICODE) != 0); + } + + return size; + } + + public String toString() { + return new String( "Referral[" + + "version=" + version + ",size=" + size + + ",serverType=" + serverType + ",flags=" + flags + + ",proximity=" + proximity + ",ttl=" + ttl + + ",pathOffset=" + pathOffset + ",altPathOffset=" + altPathOffset + + ",nodeOffset=" + nodeOffset + ",path=" + path + ",altPath=" + altPath + + ",node=" + node + "]" ); + } + } + + int pathConsumed; + int numReferrals; + int flags; + Referral referral; + + Trans2GetDfsReferralResponse() { + subCommand = SmbComTransaction.TRANS2_GET_DFS_REFERRAL; + } + + int writeSetupWireFormat( byte[] dst, int dstIndex ) { + return 0; + } + int writeParametersWireFormat( byte[] dst, int dstIndex ) { + return 0; + } + int writeDataWireFormat( byte[] dst, int dstIndex ) { + return 0; + } + int readSetupWireFormat( byte[] buffer, int bufferIndex, int len ) { + return 0; + } + int readParametersWireFormat( byte[] buffer, int bufferIndex, int len ) { + return 0; + } + int readDataWireFormat( byte[] buffer, int bufferIndex, int len ) { + int start = bufferIndex; + + pathConsumed = readInt2( buffer, bufferIndex ); + bufferIndex += 2; + /* Samba 2.2.8a will reply with Unicode paths even though + * ASCII is negotiated so we must use flags2 (probably + * should anyway). + */ + if((flags2 & FLAGS2_UNICODE) != 0) { + pathConsumed /= 2; + } + numReferrals = readInt2( buffer, bufferIndex ); + bufferIndex += 2; + flags = readInt2( buffer, bufferIndex ); + bufferIndex += 4; + + referral = new Referral(); + while( numReferrals-- > 0 ) { + bufferIndex += referral.readWireFormat( buffer, bufferIndex, len ); + } + + return bufferIndex - start; + } + public String toString() { + return new String( "Trans2GetDfsReferralResponse[" + + super.toString() + ",pathConsumed=" + pathConsumed + + ",numReferrals=" + numReferrals + ",flags=" + flags + + "," + referral + "]" ); + } +} diff --git a/src/jcifs/smb/Trans2QueryPathInformation.java b/src/jcifs/smb/Trans2QueryPathInformation.java index ded6b14..1432e05 100644 --- a/src/jcifs/smb/Trans2QueryPathInformation.java +++ b/src/jcifs/smb/Trans2QueryPathInformation.java @@ -21,10 +21,9 @@ package jcifs.smb; class Trans2QueryPathInformation extends SmbComTransaction { int informationLevel; - String filename; Trans2QueryPathInformation( String filename, int informationLevel ) { - this.filename = filename; + path = filename; this.informationLevel = informationLevel; command = SMB_COM_TRANSACTION2; subCommand = TRANS2_QUERY_PATH_INFORMATION; @@ -48,7 +47,7 @@ class Trans2QueryPathInformation extends SmbComTransaction { dst[dstIndex++] = (byte)0x00; dst[dstIndex++] = (byte)0x00; dst[dstIndex++] = (byte)0x00; - dstIndex += writeString( filename, dst, dstIndex ); + dstIndex += writeString( path, dst, dstIndex ); return dstIndex - start; } @@ -67,6 +66,6 @@ class Trans2QueryPathInformation extends SmbComTransaction { public String toString() { return new String( "Trans2QueryPathInformation[" + super.toString() + ",informationLevel=0x" + Log.getHexString( informationLevel, 3 ) + - ",filename=" + filename + "]" ); + ",filename=" + path + "]" ); } } diff --git a/src/jcifs/smb/Trans2QueryPathInformationResponse.java b/src/jcifs/smb/Trans2QueryPathInformationResponse.java index 3d33ebb..a78e667 100644 --- a/src/jcifs/smb/Trans2QueryPathInformationResponse.java +++ b/src/jcifs/smb/Trans2QueryPathInformationResponse.java @@ -27,7 +27,7 @@ class Trans2QueryPathInformationResponse extends SmbComTransactionResponse { static final int SMB_QUERY_FILE_STANDARD_INFO = 0x102; class SmbQueryFileBasicInfo implements Info { - long creationTime; + long createTime; long lastAccessTime; long lastWriteTime; long changeTime; @@ -36,6 +36,9 @@ class Trans2QueryPathInformationResponse extends SmbComTransactionResponse { public int getAttributes() { return attributes; } + public long getCreateTime() { + return createTime; + } public long getLastWriteTime() { return lastWriteTime; } @@ -44,7 +47,7 @@ class Trans2QueryPathInformationResponse extends SmbComTransactionResponse { } public String toString() { return new String( "SmbQueryFileBasicInfo[" + - "creationTime=" + new Date( creationTime ) + + "createTime=" + new Date( createTime ) + ",lastAccessTime=" + new Date( lastAccessTime ) + ",lastWriteTime=" + new Date( lastWriteTime ) + ",changeTime=" + new Date( changeTime ) + @@ -61,8 +64,11 @@ class Trans2QueryPathInformationResponse extends SmbComTransactionResponse { public int getAttributes() { return 0; } + public long getCreateTime() { + return 0L; + } public long getLastWriteTime() { - return 0; + return 0L; } public long getSize() { return endOfFile; @@ -131,7 +137,7 @@ class Trans2QueryPathInformationResponse extends SmbComTransactionResponse { int start = bufferIndex; SmbQueryFileBasicInfo info = new SmbQueryFileBasicInfo(); - info.creationTime = readTime( buffer, bufferIndex ); + info.createTime = readTime( buffer, bufferIndex ); bufferIndex += 8; info.lastAccessTime = readTime( buffer, bufferIndex ); bufferIndex += 8; diff --git a/src/jcifs/smb/Trans2SetFileInformation.java b/src/jcifs/smb/Trans2SetFileInformation.java new file mode 100644 index 0000000..6dddf84 --- /dev/null +++ b/src/jcifs/smb/Trans2SetFileInformation.java @@ -0,0 +1,89 @@ +/* jcifs smb client library in Java + * Copyright (C) 2003 "Michael B. Allen" + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package jcifs.smb; + +class Trans2SetFileInformation extends SmbComTransaction { + + static final int SMB_FILE_BASIC_INFO = 0x101; + + int fid; + int attributes; + long createTime, lastWriteTime; + + Trans2SetFileInformation( int fid, int attributes, long createTime, long lastWriteTime ) { + this.fid = fid; + this.attributes = attributes; + this.createTime = createTime; + this.lastWriteTime = lastWriteTime; + command = SMB_COM_TRANSACTION2; + subCommand = TRANS2_SET_FILE_INFORMATION; + maxParameterCount = 6; + maxDataCount = 0; + maxSetupCount = (byte)0x00; + } + + int writeSetupWireFormat( byte[] dst, int dstIndex ) { + dst[dstIndex++] = subCommand; + dst[dstIndex++] = (byte)0x00; + return 2; + } + int writeParametersWireFormat( byte[] dst, int dstIndex ) { + int start = dstIndex; + + writeInt2( fid, dst, dstIndex ); + dstIndex += 2; + writeInt2( SMB_FILE_BASIC_INFO, dst, dstIndex ); + dstIndex += 2; + writeInt2( 0, dst, dstIndex ); + dstIndex += 2; + + return dstIndex - start; + } + int writeDataWireFormat( byte[] dst, int dstIndex ) { + int start = dstIndex; + + writeTime( createTime, dst, dstIndex ); dstIndex += 8; + writeLong( 0L, dst, dstIndex ); dstIndex += 8; + writeTime( lastWriteTime, dst, dstIndex ); dstIndex += 8; + writeLong( 0L, dst, dstIndex ); dstIndex += 8; +/* Samba 2.2.7 needs ATTR_NORMAL + */ + writeInt2( 0x80 | attributes, dst, dstIndex ); dstIndex += 2; + /* 6 zeros observed with NT */ + writeLong( 0L, dst, dstIndex ); dstIndex += 6; + + /* Also observed 4 byte alignment but we stick + * with the default for jCIFS which is 2 */ + + return dstIndex - start; + } + int readSetupWireFormat( byte[] buffer, int bufferIndex, int len ) { + return 0; + } + int readParametersWireFormat( byte[] buffer, int bufferIndex, int len ) { + return 0; + } + int readDataWireFormat( byte[] buffer, int bufferIndex, int len ) { + return 0; + } + public String toString() { + return new String( "Trans2SetFileInformation[" + super.toString() + + ",fid=" + fid + "]" ); + } +} diff --git a/src/jcifs/smb/Trans2SetFileInformationResponse.java b/src/jcifs/smb/Trans2SetFileInformationResponse.java new file mode 100644 index 0000000..503191a --- /dev/null +++ b/src/jcifs/smb/Trans2SetFileInformationResponse.java @@ -0,0 +1,49 @@ +/* jcifs smb client library in Java + * Copyright (C) 2003 "Michael B. Allen" + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package jcifs.smb; + +class Trans2SetFileInformationResponse extends SmbComTransactionResponse { + + Trans2SetFileInformationResponse() { + subCommand = SmbComTransaction.TRANS2_SET_FILE_INFORMATION; + } + + int writeSetupWireFormat( byte[] dst, int dstIndex ) { + return 0; + } + int writeParametersWireFormat( byte[] dst, int dstIndex ) { + return 0; + } + int writeDataWireFormat( byte[] dst, int dstIndex ) { + return 0; + } + int readSetupWireFormat( byte[] buffer, int bufferIndex, int len ) { + return 0; + } + int readParametersWireFormat( byte[] buffer, int bufferIndex, int len ) { + return 0; + } + int readDataWireFormat( byte[] buffer, int bufferIndex, int len ) { + return 0; + } + public String toString() { + return new String( "Trans2SetFileInformationResponse[" + + super.toString() + "]" ); + } +} diff --git a/src/jcifs/smb/TransactNamedPipeOutputStream.java b/src/jcifs/smb/TransactNamedPipeOutputStream.java index ef01ba9..c5f8fbc 100644 --- a/src/jcifs/smb/TransactNamedPipeOutputStream.java +++ b/src/jcifs/smb/TransactNamedPipeOutputStream.java @@ -55,7 +55,7 @@ class TransactNamedPipeOutputStream extends OutputStream { new TransCallNamedPipeResponse( pipe )); } else if(( pipe.pipeType & SmbNamedPipe.PIPE_TYPE_TRANSACT ) == SmbNamedPipe.PIPE_TYPE_TRANSACT ) { - pipe.open(( pipe.pipeType & 0xFF0000 ) | SmbFile.O_EXCL ); + pipe.open(( pipe.pipeType & 0xFF0000 ) | SmbFile.O_EXCL, SmbFile.ATTR_NORMAL, 0 ); pipe.sendTransaction( new TransTransactNamedPipe( pipe.fid, b, off, len ), new TransTransactNamedPipeResponse( pipe )); } diff --git a/src/jcifs/util/Encdec.java b/src/jcifs/util/Encdec.java new file mode 100644 index 0000000..f82cc48 --- /dev/null +++ b/src/jcifs/util/Encdec.java @@ -0,0 +1,302 @@ +/* encdec - encode and decode integers, times, and + * internationalized strings to and from popular binary formats + * http://www.ioplex.com/~miallen/encdec/ + * Copyright (c) 2003 Michael B. Allen + * + * The GNU Library General Public License + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA + */ + +package jcifs.util; + +import java.util.Date; +import java.io.IOException; + +public class Encdec { + + public static final long MILLISECONDS_BETWEEN_1970_AND_1601 = 11644473600000L; + public static final long SEC_BETWEEEN_1904_AND_1970 = 2082844800L; + public static final int TIME_1970_SEC_32BE = 1; + public static final int TIME_1970_SEC_32LE = 2; + public static final int TIME_1904_SEC_32BE = 3; + public static final int TIME_1904_SEC_32LE = 4; + public static final int TIME_1601_NANOS_64LE = 5; + public static final int TIME_1601_NANOS_64BE = 6; + public static final int TIME_1970_MILLIS_64BE = 7; + public static final int TIME_1970_MILLIS_64LE = 8; + + /* Encode integers + */ + + public static int enc_uint16be( short s, byte[] dst, int di ) { + dst[di++] = (byte)((s >> 8) & 0xFF); + dst[di] = (byte)(s & 0xFF); + return 2; + } + public static int enc_uint32be( int i, byte[] dst, int di ) { + dst[di++] = (byte)((i >> 24) & 0xFF); + dst[di++] = (byte)((i >> 16) & 0xFF); + dst[di++] = (byte)((i >> 8) & 0xFF); + dst[di] = (byte)(i & 0xFF); + return 4; + } + public static int enc_uint16le( short s, byte[] dst, int di ) + { + dst[di++] = (byte)(s & 0xFF); + dst[di] = (byte)((s >> 8) & 0xFF); + return 2; + } + public static int enc_uint32le( int i, byte[] dst, int di ) + { + dst[di++] = (byte)(i & 0xFF); + dst[di++] = (byte)((i >> 8) & 0xFF); + dst[di++] = (byte)((i >> 16) & 0xFF); + dst[di] = (byte)((i >> 24) & 0xFF); + return 4; + } + + /* Decode integers + */ + + public static short dec_uint16be( byte[] src, int si ) + { + return (short)(((src[si] & 0xFF) << 8) | (src[si + 1] & 0xFF)); + } + public static int dec_uint32be( byte[] src, int si ) + { + return ((src[si] & 0xFF) << 24) | ((src[si + 1] & 0xFF) << 16) | + ((src[si + 2] & 0xFF) << 8) | (src[si + 3] & 0xFF); + } + public static short dec_uint16le( byte[] src, int si ) + { + return (short)((src[si] & 0xFF) | ((src[si + 1] & 0xFF) << 8)); + } + public static int dec_uint32le( byte[] src, int si ) + { + return (src[si] & 0xFF) | ((src[si + 1] & 0xFF) << 8) | + ((src[si + 2] & 0xFF) << 16) | ((src[si + 3] & 0xFF) << 24); + } + + /* Encode and decode 64 bit integers + */ + + public static int enc_uint64be( long l, byte[] dst, int di ) + { + enc_uint32be( (int)(l & 0xFFFFFFFFL), dst, di + 4 ); + enc_uint32be( (int)(( l >> 32L ) & 0xFFFFFFFFL), dst, di ); + return 8; + } + public static int enc_uint64le( long l, byte[] dst, int di ) + { + enc_uint32le( (int)(l & 0xFFFFFFFFL), dst, di ); + enc_uint32le( (int)(( l >> 32L ) & 0xFFFFFFFFL), dst, di + 4 ); + return 8; + } + public static long dec_uint64be( byte[] src, int si ) + { + long l; + l = dec_uint32be( src, si ) & 0xFFFFFFFFL; + l <<= 32L; + l |= dec_uint32be( src, si + 4 ) & 0xFFFFFFFFL; + return l; + } + public static long dec_uint64le( byte[] src, int si ) + { + long l; + l = dec_uint32le( src, si + 4 ) & 0xFFFFFFFFL; + l <<= 32L; + l |= dec_uint32le( src, si ) & 0xFFFFFFFFL; + return l; + } + + /* Encode floats + */ + + public static int enc_floatle( float f, byte[] dst, int di ) + { + return enc_uint32le( Float.floatToIntBits( f ), dst, di ); + } + public static int enc_floatbe( float f, byte[] dst, int di ) + { + return enc_uint32be( Float.floatToIntBits( f ), dst, di ); + } + + /* Decode floating point numbers + */ + + public static float dec_floatle( byte[] src, int si ) + { + return Float.intBitsToFloat( dec_uint32le( src, si )); + } + public static float dec_floatbe( byte[] src, int si ) + { + return Float.intBitsToFloat( dec_uint32be( src, si )); + } + + /* Encode and decode doubles + */ + + public static int enc_doublele( double d, byte[] dst, int di ) + { + return enc_uint64le( Double.doubleToLongBits( d ), dst, di ); + } + public static int enc_doublebe( double d, byte[] dst, int di ) + { + return enc_uint64be( Double.doubleToLongBits( d ), dst, di ); + } + public static double dec_doublele( byte[] src, int si ) + { + return Double.longBitsToDouble( dec_uint64le( src, si )); + } + public static double dec_doublebe( byte[] src, int si ) + { + return Double.longBitsToDouble( dec_uint64be( src, si )); + } + + /* Encode times + */ + + public static int enc_time( Date date, byte[] dst, int di, int enc ) + { + long t; + + switch( enc ) { + case TIME_1970_SEC_32BE: + return enc_uint32be( (int)(date.getTime() / 1000L), dst, di ); + case TIME_1970_SEC_32LE: + return enc_uint32le( (int)(date.getTime() / 1000L), dst, di ); + case TIME_1904_SEC_32BE: + return enc_uint32be( (int)((date.getTime() / 1000L + + SEC_BETWEEEN_1904_AND_1970) & 0xFFFFFFFF), dst, di ); + case TIME_1904_SEC_32LE: + return enc_uint32le( (int)((date.getTime() / 1000L + + SEC_BETWEEEN_1904_AND_1970) & 0xFFFFFFFF), dst, di ); + case TIME_1601_NANOS_64BE: + t = (date.getTime() + MILLISECONDS_BETWEEN_1970_AND_1601) * 10000L; + return enc_uint64be( t, dst, di ); + case TIME_1601_NANOS_64LE: + t = (date.getTime() + MILLISECONDS_BETWEEN_1970_AND_1601) * 10000L; + return enc_uint64le( t, dst, di ); + case TIME_1970_MILLIS_64BE: + return enc_uint64be( date.getTime(), dst, di ); + case TIME_1970_MILLIS_64LE: + return enc_uint64le( date.getTime(), dst, di ); + default: + throw new IllegalArgumentException( "Unsupported time encoding" ); + } + } + + /* Decode times + */ + + public static Date dec_time( byte[] src, int si, int enc ) + { + long t; + + switch( enc ) { + case TIME_1970_SEC_32BE: + return new Date( dec_uint32be( src, si ) * 1000L ); + case TIME_1970_SEC_32LE: + return new Date( dec_uint32le( src, si ) * 1000L ); + case TIME_1904_SEC_32BE: + return new Date((( dec_uint32be( src, si ) & 0xFFFFFFFFL) - + SEC_BETWEEEN_1904_AND_1970 ) * 1000L ); + case TIME_1904_SEC_32LE: + return new Date((( dec_uint32le( src, si ) & 0xFFFFFFFFL) - + SEC_BETWEEEN_1904_AND_1970 ) * 1000L ); + case TIME_1601_NANOS_64BE: + t = dec_uint64be( src, si ); + return new Date( t / 10000L - MILLISECONDS_BETWEEN_1970_AND_1601); + case TIME_1601_NANOS_64LE: + t = dec_uint64le( src, si ); + return new Date( t / 10000L - MILLISECONDS_BETWEEN_1970_AND_1601); + case TIME_1970_MILLIS_64BE: + return new Date( dec_uint64be( src, si )); + case TIME_1970_MILLIS_64LE: + return new Date( dec_uint64le( src, si )); + default: + throw new IllegalArgumentException( "Unsupported time encoding" ); + } + } + + public static int enc_utf8( String str, byte[] dst, int di, int dlim ) throws IOException { + int start = di, ch; + int strlen = str.length(); + + for( int i = 0; di < dlim && i < strlen; i++ ) { + ch = str.charAt( i ); + if ((ch >= 0x0001) && (ch <= 0x007F)) { + dst[di++] = (byte)ch; + } else if (ch > 0x07FF) { + if((dlim - di) < 3 ) { + break; + } + dst[di++] = (byte)(0xE0 | ((ch >> 12) & 0x0F)); + dst[di++] = (byte)(0x80 | ((ch >> 6) & 0x3F)); + dst[di++] = (byte)(0x80 | ((ch >> 0) & 0x3F)); + } else { + if((dlim - di) < 2 ) { + break; + } + dst[di++] = (byte)(0xC0 | ((ch >> 6) & 0x1F)); + dst[di++] = (byte)(0x80 | ((ch >> 0) & 0x3F)); + } + } + + return di - start; + } + public static String dec_utf8( byte[] src, int si, int slim ) throws IOException { + char[] uni = new char[slim - si]; + int ui = 0, ch; + + for( ui = 0; si < slim && (ch = src[si++] & 0xFF) != 0; ui++ ) { + if( ch < 0x80 ) { + uni[ui] = (char)ch; + } else if((ch & 0xE0) == 0xC0 ) { + if((slim - si) < 2 ) { + break; + } + uni[ui] = (char)((ch & 0x1F) << 6); + ch = src[si++] & 0xFF; + uni[ui] |= ch & 0x3F; + if ((ch & 0xC0) != 0x80 || uni[ui] < 0x80 ) { + throw new IOException( "Invalid UTF-8 sequence" ); + } + } else if((ch & 0xF0) == 0xE0 ) { + if((slim - si) < 3 ) { + break; + } + uni[ui] = (char)((ch & 0x0F) << 12); + ch = src[si++] & 0xFF; + if ((ch & 0xC0) != 0x80 ) { + throw new IOException( "Invalid UTF-8 sequence" ); + } else { + uni[ui] |= (ch & 0x3F) << 6; + ch = src[si++] & 0xFF; + uni[ui] |= ch & 0x3F; + if ((ch & 0xC0) != 0x80 || uni[ui] < 0x800) { + throw new IOException( "Invalid UTF-8 sequence" ); + } + } + } else { + throw new IOException( "Unsupported UTF-8 sequence" ); + } + } + + return new String( uni, 0, ui ); + } +} -- 2.11.0